青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

隨筆 - 17  文章 - 48  trackbacks - 0
<2012年9月>
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456

常用鏈接

留言簿(3)

隨筆檔案

搜索

  •  

最新評論

閱讀排行榜

評論排行榜

lua的VM執(zhí)行代碼是從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);
      )
    }
  }
}

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

debug hook

進入for循環(huán)開始執(zhí)行代碼,先取出當(dāng)前指令I(lǐng)nstruction,根據(jù)Lua State的hook mask來判斷是否需要hook代碼執(zhí)行,這個hook代碼執(zhí)行就是lua提供給外界調(diào)試代碼的庫,我們可以使用這個debug庫實現(xiàn)自己的調(diào)試器,兩年前我使用這個debug實現(xiàn)過一個簡單的lua調(diào)試器。(博客:http://m.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表示每次調(diào)用函數(shù)的時候hook;LUA_MASKRET表示每次函數(shù)返回的時候hook;LUA_MASKLINE表示每行執(zhí)行的時候hook;
LUA_MASKCOUNT表示每執(zhí)行count條lua指令hook一次,這里的count是debug.sethook ([thread,] hook, mask [, count])中傳遞的。
LUA_MASKLINE和LUA_MASKCOUNT類型的hook是在函數(shù)的開頭這段代碼里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。
如果設(shè)置了debug hook,那執(zhí)行指令的時候就會檢測一下是否需要調(diào)用hook函數(shù)。若需要LUA_MASKLINE或LUA_MASKCOUNT的hook則調(diào)用lvm.c中的traceexec函數(shù),而traceexec函數(shù)通過調(diào)用ldo.c中的luaD_hook函數(shù)完成;若需要LUA_MASKCALL或LUA_MASKRET的hook則ldo.c中的luaD_precall和luaD_poscall會對hook進行檢測,最終還是調(diào)用到ldo.c中的luaD_hook函數(shù)完成。

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;
  }
}

我們發(fā)現(xiàn)這個調(diào)用的hook函數(shù)是注冊在L->hook中的C函數(shù)指針,我們通過debug.sethook注冊的hook函數(shù)是lua的函數(shù),那這個注冊的C函數(shù)肯定是用來完成lua函數(shù)與C函數(shù)之間的轉(zhuǎn)換。
L->hook這個函數(shù)指針的注冊是通過ldebug.c中的lua_sethook函數(shù)完成:

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中調(diào)用了lua_sethook函數(shù),這個hook函數(shù)是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);
  }
}

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

寄存器結(jié)構(gòu)

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


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

函數(shù)調(diào)用的棧結(jié)構(gòu)

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

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的函數(shù)調(diào)用棧是通過一個CallInfo的鏈表來表示,每一個CallInfo鏈表元素表示一層函數(shù)調(diào)用,每個CallInfo通過prev和next指針分別指向前面的函數(shù)和后面的函數(shù)。CallInfo中的base和top分別指向這個調(diào)用棧幀的起始地址和結(jié)束地址,base到top這些??臻g在函數(shù)運行內(nèi)部就是可用的寄存器。func則指向這個被調(diào)用函數(shù)的closure所在lua棧中的地址。

函數(shù)CallInfo鏈表結(jié)構(gòu)與lua的棧的格式的關(guān)系有如下3種:

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



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


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



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

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對于遞歸會有尾遞歸優(yōu)化,如果一個函數(shù)調(diào)用是尾遞歸的話,那么函數(shù)的調(diào)用棧是不會增長的。lua通過OP_TAILCALL指令完成尾遞歸調(diào)用,這條指令的前面一段跟OP_CALL相似,通過luaD_precall增加函數(shù)調(diào)用棧信息CallInfo。當(dāng)luaD_precall返回時,調(diào)用的不是C函數(shù),則會將新增的CallInfo與上一個CallInfo棧幀合并,然后把新增的CallInfo移除掉,這樣的尾遞歸調(diào)用就不會導(dǎo)致棧幀增長了。

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

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

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

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

按照你說的意思訪問變參數(shù)的寄存器應(yīng)該是小于0的,但是看了一下源代碼沒發(fā)現(xiàn)lua是如何處理,估計是OP_VARARG這個沒理解透徹

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

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

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

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

謝謝,受教了  回復(fù)  更多評論
  
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
              久久久av毛片精品| 免费亚洲电影在线观看| 激情欧美丁香| 国产视频精品va久久久久久| 国产偷国产偷亚洲高清97cao| 欧美亚日韩国产aⅴ精品中极品| 久久精品国产99国产精品| 亚洲欧美久久久久一区二区三区| 亚洲欧美另类在线观看| 欧美中文字幕| 欧美电影在线观看| 99香蕉国产精品偷在线观看| 亚洲一区二区在线播放| 欧美一区二区三区婷婷月色| 麻豆久久精品| 国产精品日韩精品欧美在线| 伊人精品成人久久综合软件| 一区二区欧美在线| 久久久国产成人精品| 欧美国产一区二区三区激情无套| 99国产精品国产精品久久| 性色av一区二区三区| 欧美福利电影网| 午夜视频一区在线观看| 亚洲欧美日韩精品久久| 亚洲香蕉视频| 久久视频在线看| 欧美天天在线| 最新日韩中文字幕| 久久成人18免费网站| 亚洲国产一二三| 午夜伦理片一区| 欧美精品在线观看91| 伊人精品久久久久7777| 香蕉尹人综合在线观看| 亚洲老司机av| 欧美成人激情视频| 极品少妇一区二区三区| 午夜在线电影亚洲一区| 日韩午夜免费视频| 欧美福利网址| 亚洲黄网站黄| 欧美96在线丨欧| 久久精品国产亚洲精品 | 国产麻豆综合| 99精品免费视频| 女女同性精品视频| 久久精品电影| 国产美女精品视频免费观看| 亚洲视频中文字幕| 日韩一级在线| 欧美日韩精品一二三区| 日韩亚洲欧美成人一区| 亚洲国产精品免费| 欧美成人福利视频| 亚洲精品国产精品国自产观看| 欧美.日韩.国产.一区.二区| 久久久免费av| 亚洲国产免费| 欧美激情小视频| 欧美国产日产韩国视频| 99riav1国产精品视频| 亚洲美洲欧洲综合国产一区| 欧美色欧美亚洲另类二区| 亚洲一区二区在线免费观看视频 | 久久久高清一区二区三区| 国产亚洲欧美色| 久久人人爽人人| 久久国产日本精品| 亚洲黑丝一区二区| 99国产精品久久久久久久| 国产精品另类一区| 久久乐国产精品| 美女主播精品视频一二三四| 亚洲美女在线国产| 亚洲自拍偷拍网址| 精品999久久久| 亚洲福利一区| 在线一区免费观看| 午夜久久影院| 一区二区视频免费在线观看| 欧美激情偷拍| 欧美视频网址| 久久亚洲一区二区| 欧美暴力喷水在线| 午夜精品福利一区二区三区av| 欧美一区成人| 日韩小视频在线观看专区| 亚洲免费伊人电影在线观看av| 加勒比av一区二区| 9i看片成人免费高清| 狠狠狠色丁香婷婷综合久久五月| 欧美国产一区视频在线观看 | 亚洲国产欧美一区二区三区久久| 亚洲国产另类精品专区 | 欧美激情综合色综合啪啪| 国产小视频国产精品| 久久阴道视频| 欧美性片在线观看| 嫩模写真一区二区三区三州| 欧美日韩高清一区| 麻豆精品视频在线观看| 欧美视频中文在线看| 欧美成人午夜激情视频| 国产精品欧美日韩一区二区| 亚洲国产mv| 亚洲大片免费看| 亚洲一级网站| 一区二区激情视频| 久久婷婷麻豆| 久久精品一区二区三区四区 | 亚洲乱码国产乱码精品精可以看| 中文无字幕一区二区三区| 亚洲二区三区四区| 午夜精品一区二区三区电影天堂 | 免费毛片一区二区三区久久久| 欧美亚韩一区| 亚洲人体影院| 亚洲第一精品福利| 欧美一区二区黄色| 午夜免费在线观看精品视频| 欧美日韩精品久久| 亚洲精品中文字幕女同| 亚洲国产精品成人va在线观看| 久久精品国产亚洲aⅴ| 久久精品官网| 国产日本欧美在线观看| 亚洲一区二区毛片| 午夜精品久久久久影视| 欧美日韩一区二区视频在线| 亚洲人成啪啪网站| 99国产麻豆精品| 欧美精品一区二区精品网 | 国产精品亚洲成人| 亚洲激情午夜| 亚洲伦理自拍| 欧美精品福利| 夜夜爽99久久国产综合精品女不卡| 日韩午夜在线电影| 欧美三级电影网| 亚洲女人小视频在线观看| 欧美伊人久久久久久久久影院| 国产精品制服诱惑| 欧美亚洲一级片| 久久性色av| 亚洲精品综合久久中文字幕| 欧美精品一区三区| 中文一区在线| 久久久久国内| 亚洲国产欧美精品| 欧美日韩一区二区高清| 亚洲天堂成人| 久久成人免费网| 影音先锋中文字幕一区二区| 久久久精品一区| 亚洲精品日韩一| 亚洲欧美另类久久久精品2019| 国产精品永久入口久久久| 性欧美超级视频| 欧美国产另类| 亚洲专区在线| 狠狠色综合一区二区| 欧美日韩xxxxx| 欧美一区二区视频在线| 亚洲成色精品| 国产欧美日韩不卡| 久久久精品五月天| 日韩视频在线播放| 久久久久综合一区二区三区| 亚洲国产一区二区三区a毛片| 欧美日韩精品一区二区天天拍小说 | 午夜在线观看免费一区| 影音先锋日韩精品| 欧美午夜片在线观看| 久久精品亚洲热| 99综合电影在线视频| 免费看成人av| 欧美在线亚洲| 99视频国产精品免费观看| 国产亚洲精品成人av久久ww| 欧美全黄视频| 麻豆国产精品777777在线| 亚洲免费中文| 亚洲日产国产精品| 蜜臀99久久精品久久久久久软件 | 亚洲欧美亚洲| 亚洲美女淫视频| 在线观看视频欧美| 国产精品一区二区久久精品| 欧美国产先锋| 久久久久国产免费免费| 亚洲一二三区在线| 日韩一级精品| 亚洲人体偷拍| 亚洲国产欧美日韩精品| 久久一二三四| 久久九九免费| 久久激情网站| 性色av一区二区三区在线观看 | 亚洲乱码国产乱码精品精98午夜|