lua虚拟机中有3种函数,lua闭包,c函数,c闭包
#define LUA_TLCL (LUA_TFUNCTION | (0 << 4)) /* Lua closure */
#define LUA_TLCF (LUA_TFUNCTION | (1 << 4)) /* light C function */
#define LUA_TCCL (LUA_TFUNCTION | (2 << 4)) /* C closure */
c函数和c闭包的实现和lua闭包的实现略有不同
vmcase(OP_CALL) {
int b = GETARG_B(i);
int nresults = GETARG_C(i) - 1;
if (b != 0) L->top = ra+b; /* else previous instruction set top */
if (luaD_precall(L, ra, nresults)) { /* C function? */
if (nresults >= 0)
L->top = ci->top; /* adjust results */
Protect((void)0); /* update 'base' */
}
else { /* Lua function */
ci = L->ci;
goto newframe; /* restart luaV_execute over new Lua function */
}
vmbreak;
}
因为lua闭包的实现需要return指令,指令OP_RETURN完成了后半部的实现。
先看看c函数和c闭包的实现,lua中的函数可以有多个返回值,所有这里先计算出返回值的个数,然后调用luaD_precall实现函数调用,调用结束后需要调整L->top的值,因为ci->top是为c函数调用保留的栈空间标记位,所有这里L->top调整到ci->top处,这样就不会侵占为c函数预留的占空间。
int luaD_precall (lua_State *L, StkId func, int nresults) {
lua_CFunction f;
CallInfo *ci;
switch (ttype(func)) {
case LUA_TCCL: /* C closure */
f = clCvalue(func)->f;
goto Cfunc;
case LUA_TLCF: /* light C function */
f = fvalue(func);
Cfunc: {
int n; /* number of returns */
checkstackp(L, LUA_MINSTACK, func); /* ensure minimum stack size */
ci = next_ci(L); /* now 'enter' new function */
ci->nresults = nresults;
ci->func = func;
ci->top = L->top + LUA_MINSTACK;
lua_assert(ci->top <= L->stack_last);
ci->callstatus = 0;
if (L->hookmask & LUA_MASKCALL)
luaD_hook(L, LUA_HOOKCALL, -1);
lua_unlock(L);
n = (*f)(L); /* do the actual call */
lua_lock(L);
api_checknelems(L, n);
luaD_poscall(L, ci, L->top - n, n);
return 1;
}
。。。
}
c函数和c闭包大部分处理一样,只是函数的来源对象不一样,(c闭包需要存储更多的数据)。首先要为调用栈预留足够的栈空间LUA_MINSTACK,然后记录函数调用栈ci = next_ci(L);再然后存储一些必要的值后调用c函数n = (*f)(L);调用结束后使用luaD_poscall调整返回值和函数调用栈。
lua闭包的调用分两步实现
第一步是OP_CALL
vmcase(OP_CALL) {
int b = GETARG_B(i);
int nresults = GETARG_C(i) - 1;
if (b != 0) L->top = ra+b; /* else previous instruction set top */
if (luaD_precall(L, ra, nresults)) { /* C function? */
。。。
}
else { /* Lua function */
ci = L->ci;
goto newframe; /* restart luaV_execute over new Lua function */
}
vmbreak;
和c函数调用实现一样,调用luaD_precall
int luaD_precall (lua_State *L, StkId func, int nresults) {
lua_CFunction f;
CallInfo *ci;
switch (ttype(func)) {
。。。
case LUA_TLCL: { /* Lua function: prepare its call */
StkId base;
Proto *p = clLvalue(func)->p;
int n = cast_int(L->top - func) - 1; /* number of real arguments */
int fsize = p->maxstacksize; /* frame size */
checkstackp(L, fsize, func);
if (p->is_vararg != 1) { /* do not use vararg? */
for (; n < p->numparams; n++)
setnilvalue(L->top++); /* complete missing arguments */
base = func + 1;
}
else
base = adjust_varargs(L, p, n);
ci = next_ci(L); /* now 'enter' new function */
ci->nresults = nresults;
ci->func = func;
ci->u.l.base = base;
L->top = ci->top = base + fsize;
lua_assert(ci->top <= L->stack_last);
ci->u.l.savedpc = p->code; /* starting point */
ci->callstatus = CIST_LUA;
if (L->hookmask & LUA_MASKCALL)
callhook(L, ci);
return 0;
}
。。。
}
除了记录一些基本数据和函数调用栈外,这里最重要的是改变了lua虚拟机的程序指令指针的值:ci->u.l.savedpc = p->code; /* starting point */
这个函数返回后,下一步就从ci->u.l.savedpc处开始执行。直到执行到OP_RETURN,这个函数才会返回:
vmcase(OP_RETURN) {
int b = GETARG_B(i);
if (cl->p->sizep > 0) luaF_close(L, base);
b = luaD_poscall(L, ci, ra, (b != 0 ? b - 1 : cast_int(L->top - ra)));
if (ci->callstatus & CIST_FRESH) /* local 'ci' still from callee */
return; /* external invocation: return */
else { /* invocation via reentry: continue execution */
ci = L->ci;
if (b) L->top = ci->top;
lua_assert(isLua(ci));
lua_assert(GET_OPCODE(*((ci)->u.l.savedpc - 1)) == OP_CALL);
goto newframe; /* restart luaV_execute over new Lua function */
}
}
这里主要的操作时调整返回值,恢复函数调用栈,恢复指令指针计数器,至此lua闭包执行结束。