Category Archives: Lua

Lua 源代码学习 – (1)

像一切的C程序一样,Lua解释器的main函数入口定义在Lua.c文件里,

int main (int argc, char **argv) {
  int status, result;
  lua_State *L = luaL_newstate();  /* create state */
  if (L == NULL) {
    l_message(argv[0], "cannot create state: not enough memory");
    return EXIT_FAILURE;
  }
  /* call 'pmain' in protected mode */
  lua_pushcfunction(L, &pmain);
  lua_pushinteger(L, argc);  /* 1st argument */
  lua_pushlightuserdata(L, argv); /* 2nd argument */
  status = lua_pcall(L, 2, 1, 0);
  result = lua_toboolean(L, -1);  /* get result */
  finalreport(L, status);
  lua_close(L);
  return (result && status == LUA_OK) ? EXIT_SUCCESS : EXIT_FAILURE;
}

虽然我抑制不住地想讲lua_State这个核心的数据结构,但是还是决定放在后面虚拟机的部分讲。让我们先有个直观的概念,当你在shell敲下lua的时候发生了什么。

这段启动代码有趣的地方在于main函数马上把控制权交给了pmain函数,虽然你可以很容易找到pmain的定义就在同一个文件里,但是Lua调用它的方式相当不直接,先push各种参数进lua_State,然后调用lua_pcall,这种调用方式就是注释里所说的protected mode。参数push的顺序只要pmain函数自己能够理解就好,但是第一个一定要是要调用的函数指针。lua_pcall只是lua_pcallk函数的一个宏定义,lua_pcallk定义在Lapi.c里,如果你想用lua_pcallk让lua虚拟机调用你的C函数,首先函数一定要具有int (*)(lua_State *L)的签名,然后可以仿照这里调用pmain的方法去调用。

下面我们看看真正的lua_pcallk做了什么事情,

LUA_API int lua_pcallk (lua_State *L, int nargs, int nresults, int errfunc,
                        int ctx, lua_CFunction k) {
  struct CallS c;
  ...
  ...
  c.func = L->top - (nargs+1);  /* function to be called */
  if (k == NULL || L->nny > 0) {  /* no continuation or no yieldable? */
    c.nresults = nresults;  /* do a 'conventional' protected call */
    status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func);
  }
  else {  /* prepare continuation (call is already protected by 'resume') */
    ...
    ...
  }
  adjustresults(L, nresults);
  lua_unlock(L);
  return status;
}

我们先看主要的几点,
1. 声明了一个struct CallS c,并且把我们调用之前push进state的pmain函数指针设置进去c.func = L->top – (nargs+1),看看这个表达式,这就是刚开始强调的要把函数指针第一个push进state的原因。
2. 调用luaD_pcall(L, f_call, &c, savestack(L, c.func), func),这个时候pmain还是没有被真正的调用到,这个函数里是要调用f_call函数,如果你要在你的代码里直接使用luaD_pcall函数的话,第二个参数一定得具有void (*)(lua_State *L, void *ud)的签名。
3. 我们接着往下继续看,luaD_pcall里也不会直接调用f_call(L,c),它又包装了一层去调用luaD_rawrunprotected(L, f_call, c)。
4. 在luaD_rawrunprotected里,终于调用到了f_call(L,c),等等pmain在哪里呢?让我们检查f_call的定义,

static void f_call (lua_State *L, void *ud) {
  struct CallS *c = cast(struct CallS *, ud);
  luaD_call(L, c->func, c->nresults, 0);
}

上帝啊,luaD_call又是怎么回事,不过这次至少我们把pmain的值(c->func)传给它了,要是它不用,我们干吗传给它,相信直觉,胜利就在眼前了,

void luaD_call (lua_State *L, StkId func, int nResults, int allowyield) {
  ...
  ...
  if (!luaD_precall(L, func, nResults))  /* is a Lua function? */
    luaV_execute(L);  /* call it */
  if (!allowyield) L->nny--;
  L->nCcalls--;
}

崩溃,胖子,你说的胜利呢?好吧,答案在luaD_precall(L, func, nResults)

int luaD_precall (lua_State *L, StkId func, int nresults) {
  lua_CFunction f;
  ...
  ...
  ...
     Cfunc:
      ...
      ...
      n = (*f)(L);  /* do the actual call */
      lua_lock(L);
      ...
      ...
}

泪流满面啊,n = (*f)(L),pmain终于在这被调用了,我们的Lua之旅终于要正式开始了……