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

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

你可能感兴趣的:(lua)