lua虚拟机函数调用指令OP_CALL、OP_RETURN

来源:互联网 发布:ubuntu 安装svnserver 编辑:程序博客网 时间:2024/06/06 12:43

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闭包执行结束。

0 0