• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            隨筆 - 17  文章 - 48  trackbacks - 0
            <2014年10月>
            2829301234
            567891011
            12131415161718
            19202122232425
            2627282930311
            2345678

            常用鏈接

            留言簿(3)

            隨筆檔案

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            lua的VM執行代碼是從lvm.c中的void luaV_execute(lua_State *L)開始:

            void luaV_execute (lua_State *L) {
              CallInfo *ci = L->ci;
              LClosure *cl;
              TValue *k;
              StkId base;
             newframe:  /* reentry point when frame changes (call/return) */
              lua_assert(ci == L->ci);
              cl = clLvalue(ci->func);
              k = cl->p->k;
              base = ci->u.l.base;
              /* main loop of interpreter */
              for (;;) {
                Instruction i = *(ci->u.l.savedpc++);
                StkId ra;
                if ((L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) &&
                    (--L->hookcount == 0 || L->hookmask & LUA_MASKLINE)) {
                  Protect(traceexec(L));
                }
                /* WARNING: several calls may realloc the stack and invalidate `ra' */
                ra = RA(i);
                lua_assert(base == ci->u.l.base);
                lua_assert(base <= L->top && L->top < L->stack + L->stacksize);
                vmdispatch (GET_OPCODE(i)) {
                  vmcase(OP_MOVE,
                    setobjs2s(L, ra, RB(i));
                  )
                  vmcase(OP_LOADK,
                    TValue *rb = k + GETARG_Bx(i);
                    setobj2s(L, ra, rb);
                  )
                  vmcase(OP_LOADKX,
                    TValue *rb;
                    lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_EXTRAARG);
                    rb = k + GETARG_Ax(*ci->u.l.savedpc++);
                    setobj2s(L, ra, rb);
                  )
                  vmcase(OP_LOADBOOL,
                    setbvalue(ra, GETARG_B(i));
                    if (GETARG_C(i)) ci->u.l.savedpc++;  /* skip next instruction (if C) */
                  )
                  vmcase(OP_LOADNIL,
                    int b = GETARG_B(i);
                    do {
                      setnilvalue(ra++);
                    } while (b--);
                  )
                  vmcase(OP_GETUPVAL,
                    int b = GETARG_B(i);
                    setobj2s(L, ra, cl->upvals[b]->v);
                  )
                  vmcase(OP_GETTABUP,
                    int b = GETARG_B(i);
                    Protect(luaV_gettable(L, cl->upvals[b]->v, RKC(i), ra));
                  )
                  vmcase(OP_GETTABLE,
                    Protect(luaV_gettable(L, RB(i), RKC(i), ra));
                  )
                  vmcase(OP_SETTABUP,
                    int a = GETARG_A(i);
                    Protect(luaV_settable(L, cl->upvals[a]->v, RKB(i), RKC(i)));
                  )
                  vmcase(OP_SETUPVAL,
                    UpVal *uv = cl->upvals[GETARG_B(i)];
                    setobj(L, uv->v, ra);
                    luaC_barrier(L, uv, ra);
                  )
                  vmcase(OP_SETTABLE,
                    Protect(luaV_settable(L, ra, RKB(i), RKC(i)));
                  )
                  vmcase(OP_NEWTABLE,
                    int b = GETARG_B(i);
                    int c = GETARG_C(i);
                    Table *t = luaH_new(L);
                    sethvalue(L, ra, t);
                    if (b != 0 || c != 0)
                      luaH_resize(L, t, luaO_fb2int(b), luaO_fb2int(c));
                    checkGC(L, ra + 1);
                  )
                  vmcase(OP_SELF,
                    StkId rb = RB(i);
                    setobjs2s(L, ra+1, rb);
                    Protect(luaV_gettable(L, rb, RKC(i), ra));
                  )
                  vmcase(OP_ADD,
                    arith_op(luai_numadd, TM_ADD);
                  )
                  vmcase(OP_SUB,
                    arith_op(luai_numsub, TM_SUB);
                  )
                  vmcase(OP_MUL,
                    arith_op(luai_nummul, TM_MUL);
                  )
                  vmcase(OP_DIV,
                    arith_op(luai_numdiv, TM_DIV);
                  )
                  vmcase(OP_MOD,
                    arith_op(luai_nummod, TM_MOD);
                  )
                  vmcase(OP_POW,
                    arith_op(luai_numpow, TM_POW);
                  )
                  vmcase(OP_UNM,
                    TValue *rb = RB(i);
                    if (ttisnumber(rb)) {
                      lua_Number nb = nvalue(rb);
                      setnvalue(ra, luai_numunm(L, nb));
                    }
                    else {
                      Protect(luaV_arith(L, ra, rb, rb, TM_UNM));
                    }
                  )
                  vmcase(OP_NOT,
                    TValue *rb = RB(i);
                    int res = l_isfalse(rb);  /* next assignment may change this value */
                    setbvalue(ra, res);
                  )
                  vmcase(OP_LEN,
                    Protect(luaV_objlen(L, ra, RB(i)));
                  )
                  vmcase(OP_CONCAT,
                    int b = GETARG_B(i);
                    int c = GETARG_C(i);
                    StkId rb;
                    L->top = base + c + 1;  /* mark the end of concat operands */
                    Protect(luaV_concat(L, c - b + 1));
                    ra = RA(i);  /* 'luav_concat' may invoke TMs and move the stack */
                    rb = b + base;
                    setobjs2s(L, ra, rb);
                    checkGC(L, (ra >= rb ? ra + 1 : rb));
                    L->top = ci->top;  /* restore top */
                  )
                  vmcase(OP_JMP,
                    dojump(ci, i, 0);
                  )
                  vmcase(OP_EQ,
                    TValue *rb = RKB(i);
                    TValue *rc = RKC(i);
                    Protect(
                      if (cast_int(equalobj(L, rb, rc)) != GETARG_A(i))
                        ci->u.l.savedpc++;
                      else
                        donextjump(ci);
                    )
                  )
                  vmcase(OP_LT,
                    Protect(
                      if (luaV_lessthan(L, RKB(i), RKC(i)) != GETARG_A(i))
                        ci->u.l.savedpc++;
                      else
                        donextjump(ci);
                    )
                  )
                  vmcase(OP_LE,
                    Protect(
                      if (luaV_lessequal(L, RKB(i), RKC(i)) != GETARG_A(i))
                        ci->u.l.savedpc++;
                      else
                        donextjump(ci);
                    )
                  )
                  vmcase(OP_TEST,
                    if (GETARG_C(i) ? l_isfalse(ra) : !l_isfalse(ra))
                        ci->u.l.savedpc++;
                      else
                      donextjump(ci);
                  )
                  vmcase(OP_TESTSET,
                    TValue *rb = RB(i);
                    if (GETARG_C(i) ? l_isfalse(rb) : !l_isfalse(rb))
                      ci->u.l.savedpc++;
                    else {
                      setobjs2s(L, ra, rb);
                      donextjump(ci);
                    }
                  )
                  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 */
                      base = ci->u.l.base;
                    }
                    else {  /* Lua function */
                      ci = L->ci;
                      ci->callstatus |= CIST_REENTRY;
                      goto newframe;  /* restart luaV_execute over new Lua function */
                    }
                  )
                  vmcase(OP_TAILCALL,
                    int b = GETARG_B(i);
                    if (b != 0) L->top = ra+b;  /* else previous instruction set top */
                    lua_assert(GETARG_C(i) - 1 == LUA_MULTRET);
                    if (luaD_precall(L, ra, LUA_MULTRET))  /* C function? */
                      base = ci->u.l.base;
                    else {
                      /* tail call: put called frame (n) in place of caller one (o) */
                      CallInfo *nci = L->ci;  /* called frame */
                      CallInfo *oci = nci->previous;  /* caller frame */
                      StkId nfunc = nci->func;  /* called function */
                      StkId ofunc = oci->func;  /* caller function */
                      /* last stack slot filled by 'precall' */
                      StkId lim = nci->u.l.base + getproto(nfunc)->numparams;
                      int aux;
                      /* close all upvalues from previous call */
                      if (cl->p->sizep > 0) luaF_close(L, oci->u.l.base);
                      /* move new frame into old one */
                      for (aux = 0; nfunc + aux < lim; aux++)
                        setobjs2s(L, ofunc + aux, nfunc + aux);
                      oci->u.l.base = ofunc + (nci->u.l.base - nfunc);  /* correct base */
                      oci->top = L->top = ofunc + (L->top - nfunc);  /* correct top */
                      oci->u.l.savedpc = nci->u.l.savedpc;
                      oci->callstatus |= CIST_TAIL;  /* function was tail called */
                      ci = L->ci = oci;  /* remove new frame */
                      lua_assert(L->top == oci->u.l.base + getproto(ofunc)->maxstacksize);
                      goto newframe;  /* restart luaV_execute over new Lua function */
                    }
                  )
                  vmcasenb(OP_RETURN,
                    int b = GETARG_B(i);
                    if (b != 0) L->top = ra+b-1;
                    if (cl->p->sizep > 0) luaF_close(L, base);
                    b = luaD_poscall(L, ra);
                    if (!(ci->callstatus & CIST_REENTRY))  /* 'ci' still the called one */
                      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 */
                    }
                  )
                  vmcase(OP_FORLOOP,
                    lua_Number step = nvalue(ra+2);
                    lua_Number idx = luai_numadd(L, nvalue(ra), step); /* increment index */
                    lua_Number limit = nvalue(ra+1);
                    if (luai_numlt(L, 0, step) ? luai_numle(L, idx, limit)
                                               : luai_numle(L, limit, idx)) {
                      ci->u.l.savedpc += GETARG_sBx(i);  /* jump back */
                      setnvalue(ra, idx);  /* update internal index */
                      setnvalue(ra+3, idx);  /* and external index */
                    }
                  )
                  vmcase(OP_FORPREP,
                    const TValue *init = ra;
                    const TValue *plimit = ra+1;
                    const TValue *pstep = ra+2;
                    if (!tonumber(init, ra))
                      luaG_runerror(L, LUA_QL("for") " initial value must be a number");
                    else if (!tonumber(plimit, ra+1))
                      luaG_runerror(L, LUA_QL("for") " limit must be a number");
                    else if (!tonumber(pstep, ra+2))
                      luaG_runerror(L, LUA_QL("for") " step must be a number");
                    setnvalue(ra, luai_numsub(L, nvalue(ra), nvalue(pstep)));
                    ci->u.l.savedpc += GETARG_sBx(i);
                  )
                  vmcasenb(OP_TFORCALL,
                    StkId cb = ra + 3;  /* call base */
                    setobjs2s(L, cb+2, ra+2);
                    setobjs2s(L, cb+1, ra+1);
                    setobjs2s(L, cb, ra);
                    L->top = cb + 3;  /* func. + 2 args (state and index) */
                    Protect(luaD_call(L, cb, GETARG_C(i), 1));
                    L->top = ci->top;
                    i = *(ci->u.l.savedpc++);  /* go to next instruction */
                    ra = RA(i);
                    lua_assert(GET_OPCODE(i) == OP_TFORLOOP);
                    goto l_tforloop;
                  )
                  vmcase(OP_TFORLOOP,
                    l_tforloop:
                    if (!ttisnil(ra + 1)) {  /* continue loop? */
                      setobjs2s(L, ra, ra + 1);  /* save control variable */
                       ci->u.l.savedpc += GETARG_sBx(i);  /* jump back */
                    }
                  )
                  vmcase(OP_SETLIST,
                    int n = GETARG_B(i);
                    int c = GETARG_C(i);
                    int last;
                    Table *h;
                    if (n == 0) n = cast_int(L->top - ra) - 1;
                    if (c == 0) {
                      lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_EXTRAARG);
                      c = GETARG_Ax(*ci->u.l.savedpc++);
                    }
                    luai_runtimecheck(L, ttistable(ra));
                    h = hvalue(ra);
                    last = ((c-1)*LFIELDS_PER_FLUSH) + n;
                    if (last > h->sizearray)  /* needs more space? */
                      luaH_resizearray(L, h, last);  /* pre-allocate it at once */
                    for (; n > 0; n--) {
                      TValue *val = ra+n;
                      luaH_setint(L, h, last--, val);
                      luaC_barrierback(L, obj2gco(h), val);
                    }
                    L->top = ci->top;  /* correct top (in case of previous open call) */
                  )
                  vmcase(OP_CLOSURE,
                    Proto *p = cl->p->p[GETARG_Bx(i)];
                    Closure *ncl = getcached(p, cl->upvals, base);  /* cached closure */
                    if (ncl == NULL)  /* no match? */
                      pushclosure(L, p, cl->upvals, base, ra);  /* create a new one */
                    else
                      setclLvalue(L, ra, ncl);  /* push cashed closure */
                    checkGC(L, ra + 1);
                  )
                  vmcase(OP_VARARG,
                    int b = GETARG_B(i) - 1;
                    int j;
                    int n = cast_int(base - ci->func) - cl->p->numparams - 1;
                    if (b < 0) {  /* B == 0? */
                      b = n;  /* get all var. arguments */
                      Protect(luaD_checkstack(L, n));
                      ra = RA(i);  /* previous call may change the stack */
                      L->top = ra + n;
                    }
                    for (j = 0; j < b; j++) {
                      if (j < n) {
                        setobjs2s(L, ra + j, base - n + j);
                      }
                      else {
                        setnilvalue(ra + j);
                      }
                    }
                  )
                  vmcase(OP_EXTRAARG,
                    lua_assert(0);
                  )
                }
              }
            }

            此函數先從CallInfo中取出運行的lua closure,取出這個closure的寄存器的base指針和closure的函數Proto的常量列表k。

            debug hook

            進入for循環開始執行代碼,先取出當前指令Instruction,根據Lua State的hook mask來判斷是否需要hook代碼執行,這個hook代碼執行就是lua提供給外界調試代碼的庫,我們可以使用這個debug庫實現自己的調試器,兩年前我使用這個debug實現過一個簡單的lua調試器。(博客:http://www.shnenglu.com/airtrack/archive/2011/01/01/137825.html 代碼放在github上:https://github.com/airtrack/lua-debugger

            lua提供了四種hookmask,分別是:

            #define LUA_MASKCALL     (1 << LUA_HOOKCALL)
            #define LUA_MASKRET     (1 << LUA_HOOKRET)
            #define LUA_MASKLINE     (1 << LUA_HOOKLINE)
            #define LUA_MASKCOUNT     (1 << LUA_HOOKCOUNT)

            LUA_MASKCALL表示每次調用函數的時候hook;LUA_MASKRET表示每次函數返回的時候hook;LUA_MASKLINE表示每行執行的時候hook;
            LUA_MASKCOUNT表示每執行count條lua指令hook一次,這里的count是debug.sethook ([thread,] hook, mask [, count])中傳遞的。
            LUA_MASKLINE和LUA_MASKCOUNT類型的hook是在函數的開頭這段代碼里hook:

                if ((L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) &&
                    (--L->hookcount == 0 || L->hookmask & LUA_MASKLINE)) {
                  Protect(traceexec(L));
                }

            而LUA_MASKCALL和LUA_MASKRET類型的hook則分別在call和return的時候hook,具體是在ldo.c中的luaD_precall和luaD_poscall中hook。
            如果設置了debug hook,那執行指令的時候就會檢測一下是否需要調用hook函數。若需要LUA_MASKLINE或LUA_MASKCOUNT的hook則調用lvm.c中的traceexec函數,而traceexec函數通過調用ldo.c中的luaD_hook函數完成;若需要LUA_MASKCALL或LUA_MASKRET的hook則ldo.c中的luaD_precall和luaD_poscall會對hook進行檢測,最終還是調用到ldo.c中的luaD_hook函數完成。

            void luaD_hook (lua_State *L, int eventint line) {
              lua_Hook hook = L->hook;
              if (hook && L->allowhook) {
                CallInfo *ci = L->ci;
                ptrdiff_t top = savestack(L, L->top);
                ptrdiff_t ci_top = savestack(L, ci->top);
                lua_Debug ar;
                ar.event = event;
                ar.currentline = line;
                ar.i_ci = ci;
                luaD_checkstack(L, LUA_MINSTACK);  /* ensure minimum stack size */
                ci->top = L->top + LUA_MINSTACK;
                lua_assert(ci->top <= L->stack_last);
                L->allowhook = 0;  /* cannot call hooks inside a hook */
                ci->callstatus |= CIST_HOOKED;
                lua_unlock(L);
                (*hook)(L, &ar);
                lua_lock(L);
                lua_assert(!L->allowhook);
                L->allowhook = 1;
                ci->top = restorestack(L, ci_top);
                L->top = restorestack(L, top);
                ci->callstatus &= ~CIST_HOOKED;
              }
            }

            我們發現這個調用的hook函數是注冊在L->hook中的C函數指針,我們通過debug.sethook注冊的hook函數是lua的函數,那這個注冊的C函數肯定是用來完成lua函數與C函數之間的轉換。
            L->hook這個函數指針的注冊是通過ldebug.c中的lua_sethook函數完成:

            LUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count) {
              if (func == NULL || mask == 0) {  /* turn off hooks? */
                mask = 0;
                func = NULL;
              }
              if (isLua(L->ci))
                L->oldpc = L->ci->u.l.savedpc;
              L->hook = func;
              L->basehookcount = count;
              resethookcount(L);
              L->hookmask = cast_byte(mask);
              return 1;
            }

            在ldblib.c中的db_sethook中調用了lua_sethook函數,這個hook函數是ldblib.c中的hookf:

            static void hookf (lua_State *L, lua_Debug *ar) {
              static const char *const hooknames[] =
                {"call", "return", "line", "count", "tail call"};
              gethooktable(L);
              lua_pushthread(L);
              lua_rawget(L, -2);
              if (lua_isfunction(L, -1)) {
                lua_pushstring(L, hooknames[(int)ar->event]);
                if (ar->currentline >= 0)
                  lua_pushinteger(L, ar->currentline);
                else lua_pushnil(L);
                lua_assert(lua_getinfo(L, "lS", ar));
                lua_call(L, 2, 0);
              }
            }

            這個函數就把注冊的lua hook函數取出來然后調用,傳遞的hook類型作為hook函數的第一參數,分別是{"call", "return", "line", "count", "tail call"}。

            寄存器結構

            lua是寄存器虛擬機,它為每個函數在運行時期最多分配250個寄存器。函數運行時都是通過這些寄存器來操作數據,指令操作寄存器的參數都是記錄著相應寄存器的下標。在for循環中,通過RA(i)獲取到指令i的參數A的寄存器,lua指令格式在上一篇中有介紹(http://www.shnenglu.com/airtrack/archive/2012/08/12/186998.html),RA宏獲得A參數的寄存器下標,再加上當前運行函數的base指針,就可以得出相應的寄存器。再之后通過GET_OPCODE(i)獲得opcode并進入switch-case,分別針對每條指令類型取出相應的其它指令參數并執行。
            lua寄存器結構如圖:


            對每條指令分別根據指令類型操作A、B、C、Ax、Bx、sBx參數,參數可以是寄存器的下標,也可以是Proto的常量列表k的下標。case的第一條指令OP_MOVE就是最簡單的指令,從指令i中取出參數B,然后把B指向的TValue賦值給A指向的TValue。從常量列表中把TValue load到寄存器中的指令有兩種,分別是OP_LOADK和OP_LOADKX。在OP_LOADK中,參數Bx就是Proto的常量列表的下標,然后簡單的將這個TValue load到寄存器RA(i)中,如果一個函數的常量很多,個數超過了,參數Bx(14~31bits,共18位)的表示范圍,這時候就要使用OP_LOADKX指令表示。在OP_LOADKX指令中,會繼續讀取下一條指令,下一條指令的類型是OP_EXTRAARG,它的參數是Ax(6~31bits,共26位)來表示Proto的常量列表的下標,這樣常量的個數就擴大到了26位的表示范圍。

            函數調用的棧結構

            lua的函數調用指令是OP_CALL和OP_TAILCALL,實際上函數調用是通過luaD_precall完成,這個函數判斷被調用的函數是否是C函數,如果是C函數的話那就將函數執行完返回,如果不是則準備好一些基本數據,并把指令切換到被調用的lua函數的指令地址上,然后執行被調用函數的指令。

            int luaD_precall (lua_State *L, StkId func, int nresults) {
              lua_CFunction f;
              CallInfo *ci;
              int n;  /* number of arguments (Lua) or returns (C) */
              ptrdiff_t funcr = savestack(L, func);
              switch (ttype(func)) {
                case LUA_TLCF:  /* light C function */
                  f = fvalue(func);
                  goto Cfunc;
                case LUA_TCCL: {  /* C closure */
                  f = clCvalue(func)->f;
                 Cfunc:
                  luaD_checkstack(L, LUA_MINSTACK);  /* ensure minimum stack size */
                  ci = next_ci(L);  /* now 'enter' new function */
                  ci->nresults = nresults;
                  ci->func = restorestack(L, funcr);
                  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, L->top - n);
                  return 1;
                }
                case LUA_TLCL: {  /* Lua function: prepare its call */
                  StkId base;
                  Proto *p = clLvalue(func)->p;
                  luaD_checkstack(L, p->maxstacksize);
                  func = restorestack(L, funcr);
                  n = cast_int(L->top - func) - 1;  /* number of real arguments */
                  for (; n < p->numparams; n++)
                    setnilvalue(L->top++);  /* complete missing arguments */
                  base = (!p->is_vararg) ? func + 1 : adjust_varargs(L, p, n);
                  ci = next_ci(L);  /* now 'enter' new function */
                  ci->nresults = nresults;
                  ci->func = func;
                  ci->u.l.base = base;
                  ci->top = base + p->maxstacksize;
                  lua_assert(ci->top <= L->stack_last);
                  ci->u.l.savedpc = p->code;  /* starting point */
                  ci->callstatus = CIST_LUA;
                  L->top = ci->top;
                  if (L->hookmask & LUA_MASKCALL)
                    callhook(L, ci);
                  return 0;
                }
                default: {  /* not a function */
                  func = tryfuncTM(L, func);  /* retry with 'function' tag method */
                  return luaD_precall(L, func, nresults);  /* now it must be a function */
                }
              }
            }

            lua的函數調用棧是通過一個CallInfo的鏈表來表示,每一個CallInfo鏈表元素表示一層函數調用,每個CallInfo通過prev和next指針分別指向前面的函數和后面的函數。CallInfo中的base和top分別指向這個調用棧幀的起始地址和結束地址,base到top這些棧空間在函數運行內部就是可用的寄存器。func則指向這個被調用函數的closure所在lua棧中的地址。

            函數CallInfo鏈表結構與lua的棧的格式的關系有如下3種:

            1.被調用函數為普通lua函數時,調用者把被調用函數的closure放到棧中,然后把傳入函數的參數依次放入棧中。被調用者的CallInfo中的func指針指向它所屬的closure,并把這個運行時期的base指針指向傳進來的第一參數,如下圖:



            2.被調用函數是vararg(變參)的lua函數時,被調用者的CallInfo的func還是指向相應的closure,固定參數則會復制一份,并把原來的設置為nil,而多出的參數則保留在原始位置,并將base指針指向復制的第一個實參。這樣,base指針前面的就是多出的參數,即固定的參數是從base指針指向的地方開始,而變參數則在base指針前面,這樣可以保證后續的指令訪問固定的參數跟非可變參數函數(第一種情況)時一致。
            例如:一個變參函數function f(a, b, ...) end,這樣調用f(1, 2, 3, 4),那么會把1和2復制一份,分別作為a和b的實參,3和4則保留在原始位置,也就是在base指針之前。


            3.被調用的函數是C函數的時候,CallIInfo的top指向L->top + LUA_MINSTACK(20),為C函數操作lua棧預留的最小stack空間,在被調用的C函數中若使用的lua棧空間比較多時,需要調用lua_checkstack來向lua申請保證有足夠的棧空間使用,不然就會出現lua stack overflow的錯誤。



            函數調用完成后,lua通過指令OP_RETURN返回,這時候,最后一個CallIInfo就回收了。在回收之前,通過luaD_poscall來將函數的返回值復制到相應的位置,函數返回值復制到的位置的起點就是closure的位置,把closure覆蓋掉。若調用的CallInfo表示的是C函數時,也是通過luaD_poscall完成返回值的復制。

            int luaD_poscall (lua_State *L, StkId firstResult) {
              StkId res;
              int wanted, i;
              CallInfo *ci = L->ci;
              if (L->hookmask & (LUA_MASKRET | LUA_MASKLINE)) {
                if (L->hookmask & LUA_MASKRET) {
                  ptrdiff_t fr = savestack(L, firstResult);  /* hook may change stack */
                  luaD_hook(L, LUA_HOOKRET, -1);
                  firstResult = restorestack(L, fr);
                }
                L->oldpc = ci->previous->u.l.savedpc;  /* 'oldpc' for caller function */
              }
              res = ci->func;  /* res == final position of 1st result */
              wanted = ci->nresults;
              L->ci = ci = ci->previous;  /* back to caller */
              /* move results to correct place */
              for (i = wanted; i != 0 && firstResult < L->top; i--)
                setobjs2s(L, res++, firstResult++);
              while (i-- > 0)
                setnilvalue(res++);
              L->top = res;
              return (wanted - LUA_MULTRET);  /* 0 iff wanted == LUA_MULTRET */
            }

            尾遞歸

            lua對于遞歸會有尾遞歸優化,如果一個函數調用是尾遞歸的話,那么函數的調用棧是不會增長的。lua通過OP_TAILCALL指令完成尾遞歸調用,這條指令的前面一段跟OP_CALL相似,通過luaD_precall增加函數調用棧信息CallInfo。當luaD_precall返回時,調用的不是C函數,則會將新增的CallInfo與上一個CallInfo棧幀合并,然后把新增的CallInfo移除掉,這樣的尾遞歸調用就不會導致棧幀增長了。

            lua的其它指令就是很明確的操作一些寄存器和常量來完成代碼執行。
            posted on 2012-09-19 12:34 airtrack 閱讀(7158) 評論(5)  編輯 收藏 引用

            FeedBack:
            # re: lua源碼剖析(三):VM 2012-09-20 13:42 zenk
            lz的分析很贊

            請問一下:變參數的函數被調用時,參數為啥需要復制一份,base指針則指向復制的那些實參的第一個地址。  回復  更多評論
              
            # re: lua源碼剖析(三):VM 2012-09-20 15:56 airtrack
            @zenk
            我對這段描述不夠清楚,現在已經補上了。

            復制的只是固定的那些參數,"..."所代表的變參并沒有復制,保留在base指針前面,這樣可以保證后續指令訪問固定的那些參數和非可變參數函數是一致。  回復  更多評論
              
            # re: lua源碼剖析(三):VM 2012-09-23 08:06 zenk
            @airtrack

            按照你說的意思訪問變參數的寄存器應該是小于0的,但是看了一下源代碼沒發現lua是如何處理,估計是OP_VARARG這個沒理解透徹

            想請教一下,lua如何處理訪問變參數的,比如說可以通過arg這個本地變量訪問,那lua什么時候創建這個本地變量(看了一下午源碼沒明白)

            謝了  回復  更多評論
              
            # re: lua源碼剖析(三):VM 2012-09-23 09:13 airtrack
            @zenk

            OP_VARARG這條指令就是訪問...參數的,可以看到用
            int n = cast_int(base - ci->func) - cl->p->numparams - 1;
            來計算可變參數變參的個數(cl->p->numparams是這個函數的固定參數的個數),接下來在判斷B參數是否為0,如果為0,那就有多少個變參就復制多少個,不然就復制B - 1個變參。在lua中如下代碼會生成OP_VARARG指令:
            function f(...)
            print(...)
            end

            至于arg變量,在lua 5.2中已經不會自動打包可變參數...為一個arg變量,在lua 5.2中要使用arg變量可以這樣寫:
            function f(...)
            local arg = table.pack(...)
            end  回復  更多評論
              
            # re: lua源碼剖析(三):VM 2012-09-23 13:45 zenk
            @airtrack

            謝謝,受教了  回復  更多評論
              
            久久精品国产99国产精品亚洲| 狠色狠色狠狠色综合久久| 久久99国产精品久久99果冻传媒 | 精品国产婷婷久久久| 国产999精品久久久久久| 99精品国产免费久久久久久下载| 亚洲中文字幕久久精品无码APP| 久久丫精品国产亚洲av| 精品无码久久久久久久动漫| 久久久久久久波多野结衣高潮 | 久久91精品国产91久久户| 国产福利电影一区二区三区,免费久久久久久久精 | 久久久久国产精品熟女影院| 久久91综合国产91久久精品| 久久久久人妻一区二区三区| 亚洲国产精品久久| 亚洲精品乱码久久久久久久久久久久 | 久久五月精品中文字幕| 久久99久久无码毛片一区二区| 综合网日日天干夜夜久久| 91精品国产色综久久| 亚洲综合熟女久久久30p| 国产亚洲精久久久久久无码AV| 久久久久99精品成人片欧美| 久久天天躁狠狠躁夜夜2020| 久久免费视频网站| 亚洲αv久久久噜噜噜噜噜| 久久一本综合| 欧美精品丝袜久久久中文字幕 | 国产精品对白刺激久久久| 久久久亚洲AV波多野结衣| 香蕉久久AⅤ一区二区三区| 精品久久综合1区2区3区激情| 国产一久久香蕉国产线看观看| 99精品久久精品一区二区| 久久夜色精品国产噜噜亚洲a| 久久AAAA片一区二区| 激情五月综合综合久久69| 久久久噜噜噜久久中文字幕色伊伊| 7国产欧美日韩综合天堂中文久久久久| 国产亚洲精品美女久久久|