LUA_API const char *lua_pushvfstring (lua_State *L, const char *fmt, va_list argp) { const char *ret; lua_lock(L); luaC_checkGC(L); ret = luaO_pushvfstring(L, fmt, argp); lua_unlock(L); return ret; }
LUA_API const char *lua_pushlstring (lua_State *L, const char *s, size_t len) { TString *ts; lua_lock(L); luaC_checkGC(L); ts = luaS_newlstr(L, s, len); setsvalue2s(L, L->top, ts); api_incr_top(L); lua_unlock(L); return getstr(ts); }
void lua_pushstring(const char *s) { if (!s) ttype(lua_state->stack.top) = LUA_T_NIL; else { tsvalue(lua_state->stack.top) = luaS_new(s); ttype(lua_state->stack.top) = LUA_T_STRING; } incr_top; luaC_checkGC(); }
LUA_API void lua_createtable (lua_State *L, int narray, int nrec) { Table *t; lua_lock(L); luaC_checkGC(L); t = luaH_new(L); sethvalue(L, L->top, t); api_incr_top(L); if (narray > 0 || nrec > 0) luaH_resize(L, t, narray, nrec); lua_unlock(L); }
void lua_pushcclosure (lua_CFunction fn, int n) { if (fn == NULL) lua_error("API error - attempt to push a NULL Cfunction"); checkCparams(n); ttype(L->stack.top) = LUA_T_CPROTO; fvalue(L->stack.top) = fn; incr_top; luaV_closure(n); luaC_checkGC(); }
LuaStackObject LuaState::PushFString(const char *fmt, ...) { lua_State* L = LuaState_to_lua_State(this); va_list argp; lua_lock(L); luaC_checkGC(L); va_start(argp, fmt); luaO_pushvfstring(L, fmt, argp); va_end(argp); lua_unlock(L); return LuaStackObject(this, lua_gettop(LuaState_to_lua_State(this))); }
LUA_API const char *lua_pushfstring (lua_State *L, const char *fmt, ...) { const char *ret; va_list argp; lua_lock(L); luaC_checkGC(L); va_start(argp, fmt); ret = luaO_pushvfstring(L, fmt, argp); va_end(argp); lua_unlock(L); return ret; }
/* weet: * 创建一个新的Lua协程 * */ LUA_API lua_State *lua_newthread (lua_State *L) { lua_State *L1; lua_lock(L); luaC_checkGC(L); L1 = luaE_newthread(L); setthvalue(L->top, L1); api_incr_top(L); lua_unlock(L); lua_userstateopen(L1); // weet: 一个空的宏 return L1; }
l_noret luaG_runerror (lua_State *L, const char *fmt, ...) { CallInfo *ci = L->ci; const char *msg; va_list argp; luaC_checkGC(L); /* error message uses memory */ va_start(argp, fmt); msg = luaO_pushvfstring(L, fmt, argp); /* format message */ va_end(argp); if (isLua(ci)) /* if Lua function, add source:line information */ luaG_addinfo(L, msg, ci_func(ci)->p->source, currentline(ci)); luaG_errormsg(L); }
LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) { Closure *cl; lua_lock(L); luaC_checkGC(L); api_checknelems(L, n); cl = luaF_newCclosure(L, n); cl->c.f = fn; L->top -= n; while (n--) setobj2n(&cl->c.upvalue[n], L->top+n); setclvalue(L->top, cl); api_incr_top(L); lua_unlock(L); }
LUA_API const char *lua_pushstring (lua_State *L, const char *s) { lua_lock(L); if (s == NULL) setnilvalue(L->top); else { TString *ts; luaC_checkGC(L); ts = luaS_new(L, s); setsvalue2s(L, L->top, ts); s = getstr(ts); /* internal copy's address */ } api_incr_top(L); lua_unlock(L); return s; }
LUA_API const char *lua_tostring (lua_State *L, int idx) { StkId o = luaA_indexAcceptable(L, idx); if (o == NULL) return NULL; else if (ttisstring(o)) return svalue(o); else { const char *s; lua_lock(L); /* `luaV_tostring' may create a new string */ s = (luaV_tostring(L, o) ? svalue(o) : NULL); luaC_checkGC(L); lua_unlock(L); return s; } }
LUA_API const char *lua_tolstring (lua_State *L, int idx, size_t *len) { StkId o = index2addr(L, idx); if (!ttisstring(o)) { lua_lock(L); /* `luaV_tostring' may create a new string */ if (!luaV_tostring(L, o)) { /* conversion failed? */ if (len != NULL) *len = 0; lua_unlock(L); return NULL; } luaC_checkGC(L); o = index2addr(L, idx); /* previous call may reallocate the stack */ lua_unlock(L); } if (len != NULL) *len = tsvalue(o)->len; return svalue(o); }
LUA_API const char *lua_pushstring (lua_State *L, const char *s) { if (s == NULL) { lua_pushnil(L); return NULL; } else { TString *ts; lua_lock(L); luaC_checkGC(L); ts = luaS_new(L, s); setsvalue2s(L, L->top, ts); api_incr_top(L); lua_unlock(L); return getstr(ts); } }
static int protectedparser (lua_State *L, ZIO *z, int bin) { struct ParserS p; unsigned long old_blocks; int status; p.z = z; p.bin = bin; luaC_checkGC(L); old_blocks = L->nblocks; status = luaD_runprotected(L, f_parser, &p); if (status == 0) { /* add new memory to threshold (as it probably will stay) */ L->GCthreshold += (L->nblocks - old_blocks); } else if (status == LUA_ERRRUN) /* an error occurred: correct error code */ status = LUA_ERRSYNTAX; return status; }
LUA_API lua_State *lua_newthread (lua_State *L) { lua_State *L1; lua_lock(L); luaC_checkGC(L); L1 = &luaC_newobj(L, LUA_TTHREAD, sizeof(LX), NULL, offsetof(LX, l))->th; setthvalue(L, L->top, L1); api_incr_top(L); preinit_state(L1, G(L)); L1->hookmask = L->hookmask; L1->basehookcount = L->basehookcount; L1->hook = L->hook; resethookcount(L1); luai_userstatethread(L, L1); stack_init(L1, L); /* init stack */ lua_unlock(L); return L1; }
LUA_API const char *lua_tolstring (lua_State *L, int idx, size_t *len) { StkId o = index2addr(L, idx); if (!ttisstring(o)) { if (!cvt2str(o)) { /* not convertible? */ if (len != NULL) *len = 0; return NULL; } lua_lock(L); /* 'luaO_tostring' may create a new string */ luaC_checkGC(L); o = index2addr(L, idx); /* previous call may reallocate the stack */ luaO_tostring(L, o); lua_unlock(L); } if (len != NULL) *len = vslen(o); return svalue(o); }
/* Note: upvalues are assumed to be after first two slots. */ static void push_LCL(lua_State *L, Proto *pt, Table *env) { Closure *cl; int i, nup = pt->nups; /* Adjust the number of stack slots to the number of upvalues. */ luaL_checkstack(L, nup, "too many upvalues"); lua_settop(L, 2+nup); /* Create a closure from the subroutine prototype. */ cl = luaF_newLclosure(L, nup, env); cl->l.p = pt; /* Allocate new upvalues and close them. */ for (i = 0; i < nup; i++) cl->l.upvals[i] = luaF_findupval(L, L->base + (2+i)); luaF_close(L, L->base + 2); lua_settop(L, 2); /* Remove upvalues. */ setclvalue(L, L->top++, cl); /* Return closure on top of stack. */ luaC_checkGC(L); }
static int32 do_main(ZIO *z, int32 bin) { int32 status; do { int32 old_blocks = (luaC_checkGC(), nblocks); status = protectedparser(z, bin); if (status == 1) return 1; // error else if (status == 2) return 0; // 'natural' end else { int32 newelems2 = 2 * (nblocks - old_blocks); GCthreshold += newelems2; status = luaD_protectedrun(MULT_RET); GCthreshold -= newelems2; } } while (bin && status == 0); return status; }
/* ** creates a new string and anchors it in scanner's table so that ** it will not be collected until the end of the compilation ** (by that time it should be anchored somewhere) */ TString *luaX_newstring (LexState *ls, const char *str, size_t l) { lua_State *L = ls->L; TValue *o; /* entry for 'str' */ TString *ts = luaS_newlstr(L, str, l); /* create new string */ setsvalue2s(L, L->top++, ts); /* temporarily anchor it in stack */ o = luaH_set(L, ls->h, L->top - 1); if (ttisnil(o)) { /* not in use yet? */ /* boolean value does not need GC barrier; table has no metatable, so it does not need to invalidate cache */ setbvalue(o, 1); /* t[string] = true */ luaC_checkGC(L); } else { /* string already present */ ts = tsvalue(keyfromval(o)); /* re-use value previously stored */ } L->top--; /* remove string from stack */ return ts; }
static int do_main (ZIO *z, int bin) { int status; int debug = L->debug; /* save debug status */ do { long old_blocks = (luaC_checkGC(), L->nblocks); status = protectedparser(z, bin); if (status == 1) return 1; /* error */ else if (status == 2) return 0; /* 'natural' end */ else { unsigned long newelems2 = 2*(L->nblocks-old_blocks); L->GCthreshold += newelems2; status = luaD_protectedrun(); L->GCthreshold -= newelems2; } } while (bin && status == 0); L->debug = debug; /* restore debug status */ return status; }
/* ** Call a function (C or Lua). The function to be called is at *func. ** The arguments are on the stack, right after the function. ** When returns, the results are on the stack, starting at the original ** function position. ** The number of results is nResults, unless nResults=LUA_MULTRET. */ void luaD_call (lua_State *L, StkId func, int nResults) { lua_Hook callhook; StkId firstResult; CallInfo ci; Closure *cl; if (ttype(func) != LUA_TFUNCTION) { /* `func' is not a function; check the `function' tag method */ Closure *tm = luaT_gettmbyObj(L, func, TM_FUNCTION); if (tm == NULL) luaG_typeerror(L, func, "call"); luaD_openstack(L, func); clvalue(func) = tm; /* tag method is the new function to be called */ ttype(func) = LUA_TFUNCTION; } cl = clvalue(func); ci.func = cl; infovalue(func) = &ci; ttype(func) = LUA_TMARK; callhook = L->callhook; if (callhook) luaD_callHook(L, func, callhook, "call"); firstResult = (cl->isC ? callCclosure(L, cl, func+1) : luaV_execute(L, cl, func+1)); if (callhook) /* same hook that was active at entry */ luaD_callHook(L, func, callhook, "return"); LUA_ASSERT(ttype(func) == LUA_TMARK, "invalid tag"); /* move results to `func' (to erase parameters and function) */ if (nResults == LUA_MULTRET) { while (firstResult < L->top) /* copy all results */ *func++ = *firstResult++; L->top = func; } else { /* copy at most `nResults' */ for (; nResults > 0 && firstResult < L->top; nResults--) *func++ = *firstResult++; L->top = func; for (; nResults > 0; nResults--) { /* if there are not enough results */ ttype(L->top) = LUA_TNIL; /* adjust the stack */ incr_top; /* must check stack space */ } } luaC_checkGC(L); }
LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) { lua_lock(L); if (n == 0) { setfvalue(L->top, fn); } else { Closure *cl; api_checknelems(L, n); api_check(L, n <= MAXUPVAL, "upvalue index too large"); luaC_checkGC(L); cl = luaF_newCclosure(L, n); cl->c.f = fn; L->top -= n; while (n--) setobj2n(L, &cl->c.upvalue[n], L->top + n); setclCvalue(L, L->top, cl); } api_incr_top(L); lua_unlock(L); }
LUALIB_API int load_compiled_protos(lua_State *L, jit_proto *p) { Closure *cl; Proto *tf; int i; // load compiled lua code. luaC_checkGC(L); set_block_gc(L); /* stop collector during jit function loading. */ tf = load_jit_proto(L, p); #if DUMP_PROTOS luaU_dump_proto(tf,2); #endif cl = luaF_newLclosure(L, tf->nups, hvalue(gt(L))); cl->l.p = tf; for (i = 0; i < tf->nups; i++) /* initialize eventual upvalues */ cl->l.upvals[i] = luaF_newupval(L); setclvalue(L, L->top, cl); incr_top(L); unset_block_gc(L); return 0; }
static void Arith (lua_State *L, StkId ra, const TValue *rb, const TValue *rc, TMS op) { TValue tempb, tempc; const TValue *b, *c; #if LUA_REFCOUNT luarc_newvalue(&tempb); luarc_newvalue(&tempc); if ((b = luaV_tonumber(L, rb, &tempb)) != NULL && (c = luaV_tonumber(L, rc, &tempc)) != NULL) { #else if ((b = luaV_tonumber(rb, &tempb)) != NULL && (c = luaV_tonumber(rc, &tempc)) != NULL) { #endif /* LUA_REFCOUNT */ lua_Number nb = nvalue(b), nc = nvalue(c); #if LUA_REFCOUNT luarc_cleanvalue(&tempb); luarc_cleanvalue(&tempc); #endif /* LUA_REFCOUNT */ switch (op) { case TM_ADD: setnvalue(ra, luai_numadd(nb, nc)); break; case TM_SUB: setnvalue(ra, luai_numsub(nb, nc)); break; case TM_MUL: setnvalue(ra, luai_nummul(nb, nc)); break; case TM_DIV: setnvalue(ra, luai_numdiv(nb, nc)); break; case TM_MOD: setnvalue(ra, luai_nummod(nb, nc)); break; case TM_POW: setnvalue(ra, luai_numpow(nb, nc)); break; case TM_UNM: setnvalue(ra, luai_numunm(nb)); break; default: lua_assert(0); break; } } #if LUA_REFCOUNT else if (!call_binTM(L, rb, rc, ra, op)) { luarc_cleanvalue(&tempb); luarc_cleanvalue(&tempc); luaG_aritherror(L, rb, rc); } #else else if (!call_binTM(L, rb, rc, ra, op)) luaG_aritherror(L, rb, rc); #endif /* LUA_REFCOUNT */ } /* ** some macros for common tasks in `luaV_execute' */ #define runtime_check(L, c) { if (!(c)) break; } #define RA(i) (base+GETARG_A(i)) /* to be used after possible stack reallocation */ #define RB(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgR, base+GETARG_B(i)) #define RC(i) check_exp(getCMode(GET_OPCODE(i)) == OpArgR, base+GETARG_C(i)) #define RKB(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgK, \ ISK(GETARG_B(i)) ? k+INDEXK(GETARG_B(i)) : base+GETARG_B(i)) #define RKC(i) check_exp(getCMode(GET_OPCODE(i)) == OpArgK, \ ISK(GETARG_C(i)) ? k+INDEXK(GETARG_C(i)) : base+GETARG_C(i)) #define KBx(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgK, k+GETARG_Bx(i)) #define dojump(L,pc,i) {(pc) += (i); luai_threadyield(L);} #define Protect(x) { L->savedpc = pc; {x;}; base = L->base; } #define arith_op(op,tm) { \ TValue *rb = RKB(i); \ TValue *rc = RKC(i); \ if (ttisnumber(rb) && ttisnumber(rc)) { \ lua_Number nb = nvalue(rb), nc = nvalue(rc); \ setnvalue(ra, op(nb, nc)); \ } \ else \ Protect(Arith(L, ra, rb, rc, tm)); \ } #if LUA_BITFIELD_OPS #define bit_op(op) { \ TValue *rb = RKB(i); \ TValue *rc = RKC(i); \ if (ttisnumber(rb) && ttisnumber(rc)) { \ unsigned int nb = (unsigned int)nvalue(rb), nc = (unsigned int)nvalue(rc); \ setnvalue(ra, nb op nc); \ } \ else \ luaG_aritherror(L, rb, rc); \ } #endif /* LUA_BITFIELD_OPS */ void luaV_execute (lua_State *L, int nexeccalls) { LClosure *cl; StkId base; TValue *k; const Instruction *pc; reentry: /* entry point */ lua_assert(isLua(L->ci)); pc = L->savedpc; cl = &clvalue(L->ci->func)->l; base = L->base; k = cl->p->k; /* main loop of interpreter */ for (;;) { const Instruction i = *pc++; StkId ra; if ((L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) && (--L->hookcount == 0 || L->hookmask & LUA_MASKLINE)) { traceexec(L, pc); if (L->status == LUA_YIELD) { /* did hook yield? */ L->savedpc = pc - 1; return; } base = L->base; } /* warning!! several calls may realloc the stack and invalidate `ra' */ ra = RA(i); lua_assert(base == L->base && L->base == L->ci->base); lua_assert(base <= L->top && L->top <= L->stack + L->stacksize); lua_assert(L->top == L->ci->top || luaG_checkopenop(i)); switch (GET_OPCODE(i)) { case OP_MOVE: { setobjs2s(L, ra, RB(i)); continue; } case OP_LOADK: { setobj2s(L, ra, KBx(i)); continue; } case OP_LOADBOOL: { setbvalue(ra, GETARG_B(i)); if (GETARG_C(i)) pc++; /* skip next instruction (if C) */ continue; } case OP_LOADNIL: { TValue *rb = RB(i); do { setnilvalue(rb--); } while (rb >= ra); continue; } case OP_GETUPVAL: { int b = GETARG_B(i); setobj2s(L, ra, cl->upvals[b]->v); continue; } case OP_GETGLOBAL: { TValue g; TValue *rb = KBx(i); #if LUA_REFCOUNT sethvalue2n(L, &g, cl->env); #else sethvalue(L, &g, cl->env); #endif /* LUA_REFCOUNT */ lua_assert(ttisstring(rb)); Protect(luaV_gettable(L, &g, rb, ra)); #if LUA_REFCOUNT setnilvalue(&g); #endif /* LUA_REFCOUNT */ continue; } case OP_GETTABLE: { Protect(luaV_gettable(L, RB(i), RKC(i), ra)); continue; } case OP_SETGLOBAL: { TValue g; #if LUA_REFCOUNT sethvalue2n(L, &g, cl->env); #else sethvalue(L, &g, cl->env); #endif /* LUA_REFCOUNT */ lua_assert(ttisstring(KBx(i))); Protect(luaV_settable(L, &g, KBx(i), ra)); #if LUA_REFCOUNT setnilvalue(&g); #endif /* LUA_REFCOUNT */ continue; } case OP_SETUPVAL: { UpVal *uv = cl->upvals[GETARG_B(i)]; setobj(L, uv->v, ra); luaC_barrier(L, uv, ra); continue; } case OP_SETTABLE: { Protect(luaV_settable(L, ra, RKB(i), RKC(i))); continue; } case OP_NEWTABLE: { int b = GETARG_B(i); int c = GETARG_C(i); sethvalue(L, ra, luaH_new(L, luaO_fb2int(b), luaO_fb2int(c))); Protect(luaC_checkGC(L)); continue; } case OP_SELF: { StkId rb = RB(i); setobjs2s(L, ra+1, rb); Protect(luaV_gettable(L, rb, RKC(i), ra)); continue; } case OP_ADD: { arith_op(luai_numadd, TM_ADD); continue; } case OP_SUB: { arith_op(luai_numsub, TM_SUB); continue; } case OP_MUL: { arith_op(luai_nummul, TM_MUL); continue; } case OP_DIV: { arith_op(luai_numdiv, TM_DIV); continue; } case OP_MOD: { arith_op(luai_nummod, TM_MOD); continue; } case OP_POW: { arith_op(luai_numpow, TM_POW); continue; } case OP_UNM: { TValue *rb = RB(i); if (ttisnumber(rb)) { lua_Number nb = nvalue(rb); setnvalue(ra, luai_numunm(nb)); } else { Protect(Arith(L, ra, rb, rb, TM_UNM)); } continue; } case OP_NOT: { int res = l_isfalse(RB(i)); /* next assignment may change this value */ setbvalue(ra, res); continue; } #if LUA_BITFIELD_OPS case OP_BAND: { bit_op(&); continue; } case OP_BOR: { bit_op(|); continue; } case OP_BXOR: { bit_op(^); continue; } case OP_BSHL: { bit_op(<<); continue; } case OP_BSHR: { bit_op(>>); continue; } #endif /* LUA_BITFIELD_OPS */ case OP_LEN: { const TValue *rb = RB(i); switch (ttype(rb)) { case LUA_TTABLE: { setnvalue(ra, cast_num(luaH_getn(hvalue(rb)))); break; } case LUA_TSTRING: { setnvalue(ra, cast_num(tsvalue(rb)->len)); break; } #if LUA_WIDESTRING case LUA_TWSTRING: { setnvalue(ra, cast_num(tsvalue(rb)->len)); break; } #endif /* LUA_WIDESTRING */ default: { /* try metamethod */ Protect( if (!call_binTM(L, rb, luaO_nilobject, ra, TM_LEN)) luaG_typeerror(L, rb, "get length of"); ) } } continue; } case OP_CONCAT: { int b = GETARG_B(i); int c = GETARG_C(i); Protect(luaV_concat(L, c-b+1, c); luaC_checkGC(L)); setobjs2s(L, RA(i), base+b); continue; } case OP_JMP: { dojump(L, pc, GETARG_sBx(i)); continue; } case OP_EQ: { TValue *rb = RKB(i); TValue *rc = RKC(i); Protect( if (equalobj(L, rb, rc) == GETARG_A(i)) dojump(L, pc, GETARG_sBx(*pc)); ) pc++; continue; } case OP_LT: { Protect( if (luaV_lessthan(L, RKB(i), RKC(i)) == GETARG_A(i)) dojump(L, pc, GETARG_sBx(*pc)); ) pc++; continue; } case OP_LE: { Protect( if (lessequal(L, RKB(i), RKC(i)) == GETARG_A(i)) dojump(L, pc, GETARG_sBx(*pc)); ) pc++; continue; }
// 已经准备好了lua函数的调用环境,开始逐句执行lua函数的指令 void luaV_execute (lua_State *L, int nexeccalls) { LClosure *cl; StkId base; TValue *k; const Instruction *pc; reentry: /* entry point */ pc = L->savedpc; //这时候已经保存了lua函数的第一个指令位置 cl = &clvalue(L->ci->func)->l; base = L->base; k = cl->p->k; /* main loop of interpreter */ for (;;) { const Instruction i = *pc++; StkId ra; if ((L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) && (--L->hookcount == 0 || L->hookmask & LUA_MASKLINE)) { traceexec(L, pc); if (L->status == LUA_YIELD) { /* did hook yield? */ L->savedpc = pc - 1; return; } base = L->base; } /* warning!! several calls may realloc the stack and invalidate `ra' */ ra = RA(i); lua_assert(base == L->base && L->base == L->ci->base); lua_assert(base <= L->top && L->top <= L->stack + L->stacksize); lua_assert(L->top == L->ci->top || luaG_checkopenop(i)); // 阅读说明: // 每lua的一个函数在编译器会生成这个函数的信息:函数引用到的upvalue,函数固定参数 // 数量,是否含有可变参数,指令序列等等,这些都记录在Proto结构体中。 // 可以通过 luac -o tmp <luafile> | luac -l tmp 来函数对应的字节码以及Proto信息 // 例如以下lua代码: // -- t.lua // local x, y, z // x = x*y + y*z + x*z - (x*x + y*y + z*z) // 得到以下输出: // main <t.lua:0,0> (12 instructions, 48 bytes at 0074B6A0) // 0 + params, 6 slots, 0 upvalues, 3 locals, 0 constants, 0 functions // 1[2] MUL 3 0 1 // 2[2] MUL 4 1 2 // 3[2] ADD 3 3 4 // 4[2] MUL 4 0 2 // 5[2] ADD 3 3 4 // 6[2] MUL 4 0 0 // 7[2] MUL 5 1 1 // 8[2] ADD 4 4 5 // 9[2] MUL 5 2 2 // 10[2] ADD 4 4 5 // 11[2] SUB 0 3 4 // 12[2] RETURN 0 1 // 从输出可以得到的信息包括: // 1 生成了多少个Proto // 2 Proto对应的lua源代码在哪里 (<t.lua:0,0>) // 3 Proto中的sizecode (12 instructions, 48 bytes at 0074B6A0) // 4 Proto中的固定参数数量numparams (0 + params,这里的0) // 5 Proto是否有可变参数is_vararg (0 + params,这里的+表示带有可变参数,没有可变参数就是 0 params) // 6 Proto中在栈上用到的临时变量总数maxstacksize (6 slots,表示local变量+计算中辅助用的临时变量=6个) // 7 Proto中用到的upvalue数量nups (0 upvalues,表示用到了0个upvalue) // 8 Proto中用到的local变量数量sizelocvars (3 locals,刚好t.lua用到了x,y,z三个local变量) // 9 Proto中用到的字面常量数量sizek (0 constants) // 10 Proto中用到的Closure数量sizep (0 functions) // 11 Proto中生成的字节码指令内容code,每条指令包括: // a 指令下标 // b 指令在源代码中对应的行号 // c 指令opcode // d 指令参数 // // PS:第6条和第8条,由于计算一条表达式需要用到的辅助临时变量数目是不定的,但是是可以通过 // 分析一条表达式的AST来确定最少需要几个临时变量(后续遍历+逆波兰式拟真)。 // PS:lua是会对表达式进行常量计算优化的!例如 x = x + 5*60*60*1000,只有一个常量18000000 // PS:函数执行的时候需要用到“一段”参数栈上的空间,也就是第6条所谓的临时变量。这一段空间的 // 范围由L->base开始,到L->top结束。通过相对L->base的下标来标识具体的变量是哪个。一般来说, // 固定参数的函数,L->base指向第一个固定参数,而L->base-1指向当前正在运行的函数;而可变参数 // 的函数,L->base和当前正在运行的函数中间,保存有全部的传入参数。 switch (GET_OPCODE(i)) { // 功能:用一个已有的变量创建一个新的变量 // 将一个 lua_TValue设置成另一个lua_TValue的样子 // iABC: A待创建变量在参数栈索引,B参数栈已有lua变量的索引。 case OP_MOVE: { // local x, y -----> 记录 index(x) = 0, index(y) = 1 // x = ... // ..... // y = x -----> OP_MOVE: 1, 0 setobjs2s(L, ra, RB(i)); continue; } // 功能:用一个常量来创建一个新的变量 // 从常量池(保存在Proto类型中)中保存的常量赋值给栈上的变量 // iABx: A待创建变量在参数栈索引,Bx常量在常量池的索引 case OP_LOADK: { // local x = 9 -----> 记录 index(x) = 0, index(constval(9)) = 1 // >----> OP_LOADK: 0, 1 setobj2s(L, ra, KBx(i)); continue; } // 功能:用一个布尔值来创建一个新的变量 // iABC: A待创建变量在参数栈索引,B布尔值,C通常是0 case OP_LOADBOOL: { // 注意,local c = true这种,true就不作为一个常量放到k里面 // 而是作为字面值放到参数B里面了!所以不需要KB(i)! // local a = false -----> 记录 index(a) = 0 // >----> OP_LOADBOOL 0 0 0 // local b = true -----> 记录 index(b) = 1 // >----> OP_LOADBOOL 1 1 0 setbvalue(ra, GETARG_B(i)); if (GETARG_C(i)) pc++; /* skip next instruction (if C) */ continue; } // 功能:用nil来初始化一个到多个变量 // 类似于bzero,这个指令会把一段内存中的变量置为nil // iABC: A第一个要置nil的变量参数栈索引,B最后一个要置nil的变量参数栈索引 case OP_LOADNIL: { // local a, b, c, d, e, f, g = 1, 2, 3, 4 -----> index(a~g) = 0~6 // >----> OP_LOADNIL 4 6 TValue *rb = RB(i); do { setnilvalue(rb--); } while (rb >= ra); continue; } // 功能:用upvalue来创建一个新的变量 // 所谓的“创建”操作,其实创建的不是副本而是引用 // iABC: A待创建变量在参数栈索引,B当前函数的upvalue表的索引 case OP_GETUPVAL: { // local x = {} // ... -- do something to x // function f() local a = x[1] end -----> 记录index(a) = 0, index(upval(x)) = 1 // >----> OP_GETUPVAL 0 1 int b = GETARG_B(i); setobj2s(L, ra, cl->upvals[b]->v); continue; } // 功能:从全局表中取某个key的值来创建一个新的变量 // iABx:A待创建变量在参数栈索引,Bxkey对应的常量在常量池的索引 case OP_GETGLOBAL: { // local a = dofile ------> 记录 index(a) = 0, index(constval("dofile")) = 1 // >-----> OP_GETGLOBAL 0 1 TValue g; TValue *rb = KBx(i); sethvalue(L, &g, cl->env); lua_assert(ttisstring(rb)); Protect(luaV_gettable(L, &g, rb, ra)); continue; } // 功能:从某个table中取某个key的值来创建一个新的变量 // iABC:A待创建变量在参数栈索引,B要取出key的table变量在参数栈的索引,Ckey对应的参数栈下标或者常量池下标 case OP_GETTABLE: { // local a = hello["world"] -----> 记录 index(a) = 0, index(hello) = 1 index(constval("world")) = 0 // >----> OP_GETTABLE 0 1 0|BITRK Protect(luaV_gettable(L, RB(i), RKC(i), ra)); continue; } // 功能:将参数栈上变量设置到全局表中 // iABx:A要写入全局表的变量在栈上的索引,Bx写入到全局表的key在常量池中的下标 case OP_SETGLOBAL: { // 假设我要替换 bit库 // local mybit = {} // mybit.band = ... // mybit.bor = ... // mybit.bxor = ... // ... // bit = mybit -----> 记录 index(mybit) = 0, index(constval("bit")) = 1 // >----> OP_SETGLOBAL 0 1 TValue g; sethvalue(L, &g, cl->env); lua_assert(ttisstring(KBx(i))); Protect(luaV_settable(L, &g, KBx(i), ra)); continue; } // 功能:修改upvalue的值 // iABC:A要写入upvalue的变量在参数栈上的索引,B待写入的upvalue在upvalue表的索引 case OP_SETUPVAL: { // local a = 5 // function p() // a = "hello" -----> 记录 index(upval(a)) = 0, index(constval("hello")) = 1 // >----> OP_SETUPVAL 0 1 // end UpVal *uv = cl->upvals[GETARG_B(i)]; setobj(L, uv->v, ra); luaC_barrier(L, uv, ra); continue; } // 功能:修改某个table对应的key // iABC:A要写入table变量在参数栈的索引,B要写入的key的变量的栈索引或者常量索引,C要写入的value的变量索引或者常量索引 case OP_SETTABLE: { // local a = {} // a[5] = 3 Protect(luaV_settable(L, ra, RKB(i), RKC(i))); continue; } // 功能:在栈上创建一个table变量 // iABC:A存放table变量的参数栈索引,B创建的table变量的数组容量,C创建的table变量的字典容量 case OP_NEWTABLE: { // local a = {} -----> index(a) = 0 // >----> OP_NEWTABLE 0 szArray szHash int b = GETARG_B(i); int c = GETARG_C(i); sethvalue(L, ra, luaH_new(L, luaO_fb2int(b), luaO_fb2int(c))); Protect(luaC_checkGC(L)); // 注意,创建table可能会引起GC continue; } // 功能:把self.method和self放到参数栈上相邻的两个位置。 // 为成员方法调用的语法糖提供支持 // iABC:A存放self.method的参数栈索引,B存放self的参数栈索引,C需要从self中调用的方法对应的变量索引或者常量索引 // 执行完成后,栈上内容为: ... -> self.method -> self -> ... // ^ // RA // 当然,OP_SELF之后能看到OP_CALL的身影 case OP_SELF: { // CCNode:create() -> index(constants("CCNode")) = 1, index(constants("create")) = 2 // -> OP_GETGLOBAL 0 1 // -> OP_SELF 0 0 2 // -> OP_CALL 0 2 1 StkId rb = RB(i); setobjs2s(L, ra+1, rb); Protect(luaV_gettable(L, rb, RKC(i), ra)); continue; } //---------------------------------------------------------------------------运算符指令 // 功能:实现二元运算符:+, -, *, /, %, ^ // iABC:A存放运算结果的参数栈索引,B存放第一操作数的参数栈索引,C存放第二操作数的参数栈索引 case OP_ADD: { // local a, b, c = ... -----> index(a) = 0, index(b) = 1, index(c) = 2 // a = b + c -----> OP_ADD 0 1|BITRK 2|BITRK // a = 1 + b -----> index(constval(1)) = 0 // >----> OP_ADD 0 0 1|BITRK // a = 1 + 100 -----> index(constval(1)) = 0, index(constval(100)) = 1 // >----> OP_ADD 0 0 1 arith_op(luai_numadd, TM_ADD); continue; } case OP_SUB: { // see OP_ADD arith_op(luai_numsub, TM_SUB); continue; } case OP_MUL: { // see OP_ADD arith_op(luai_nummul, TM_MUL); continue; } case OP_DIV: { // see OP_ADD arith_op(luai_numdiv, TM_DIV); continue; } case OP_MOD: { // 这个很特殊!由于lua没有整数,所以mod可不是%这个运算符! // 这里定义 mod(x, y) => (x - floor(x/y)*y) // see OP_ADD arith_op(luai_nummod, TM_MOD); continue; } case OP_POW: { // see OP_ADD arith_op(luai_numpow, TM_POW); continue; } // 功能:实现一元运算符 -, not, # // iABC:A存放运算结果的参数栈索引,B存放操作数的参数栈索引 case OP_UNM: { // local a = -b -----> index(a) = 1, index(b) = 2 // >----> OP_UNM 1 2 TValue *rb = RB(i); if (ttisnumber(rb)) { lua_Number nb = nvalue(rb); setnvalue(ra, luai_numunm(nb)); } else { Protect(Arith(L, ra, rb, rb, TM_UNM)); } continue; } case OP_NOT: { // local a = not b -----> index(a) = 1, index(b) = 2 // >----> OP_NOT 1 2 // 那local a = not true呢?人家编译期就给你处理好了 int res = l_isfalse(RB(i)); /* next assignment may change this value */ setbvalue(ra, res); continue; } case OP_LEN: { // local a = #b -----> index(a) = 1, index(b) = 2 // >----> OP_LEN 1 2 const TValue *rb = RB(i); switch (ttype(rb)) { case LUA_TTABLE: { setnvalue(ra, cast_num(luaH_getn(hvalue(rb)))); break; } case LUA_TSTRING: { setnvalue(ra, cast_num(tsvalue(rb)->len)); break; } default: { /* try metamethod */ Protect( if (!call_binTM(L, rb, luaO_nilobject, ra, TM_LEN)) luaG_typeerror(L, rb, "get length of"); ) } } continue; } // 功能:实现字符串拼接运算符 .. // iABC:A拼接后存放结果的参数栈索引,B第一个要拼接的变量的参数栈索引,C最后一个要拼接的变量的参数栈索引 // 要执行这个指令,对参数栈有特殊要求: // ... -> string1 -> string2 ... -> stringN -> ... // ^ ^ // RB RC case OP_CONCAT: { // 类似OP_LOADNIL,只不过,这次范围是[rb,rc],loadnil是[ra,rb] // local b, c, d, a = "hello", "world", "!" // a = b .. c .. d -----> index(a) = 4, index(b~d) = 1~3 // >----> OP_CONCAT 4 1 3 // 问题是如果b~d不能保证是连续的怎么办?答案是一个个MOVE上去在OP_CONCAT... int b = GETARG_B(i); int c = GETARG_C(i); Protect(luaV_concat(L, c-b+1, c); luaC_checkGC(L)); setobjs2s(L, RA(i), base+b); continue; } //---------------------------------------------------------------------------跳转指令 // 功能:无条件跳转 // iAsBx:A不使用,sBx跳转偏移 // 一般这个语句不单独出现,都是在一些条件控制中和其他的条件跳转指令配合使用的。 case OP_JMP: { // 无条件跳转指令。由于跳转偏移总是有正向和反向之分的,所以需要用到 // 负数。那就只能用iAsBx类型的指令了。而sBx是有长度限制的! // 所以,如果生成的指令很多,超过了sBx的长度限制,可能就会编译失败 dojump(L, pc, GETARG_sBx(i)); continue; } // 功能:检查两个变量是否相等,满足预期则跳转。配合OP_JMP使用。 // iABC:A纯数字,对比较结果的预期,满足预期则跳转,B参数1的索引,C参数2的索引 case OP_EQ: { // if a == b then -----> index(a) = 1, index(b) = 2 // >----> 这里将生成两行指令: // >----> OP_EQ 0 1 2 // 用0,因为成立的话跳过,不成立才执行 // >----> OP_JMP N // N 表示then ... end中间的指令数量 // >----> ... // Instructions between "then" and "end" TValue *rb = RKB(i); TValue *rc = RKC(i); Protect( if (equalobj(L, rb, rc) == GETARG_A(i)) dojump(L, pc, GETARG_sBx(*pc)); ) pc++; continue; } // 功能:检查两个变量是否小于,满足预期则跳转。配合OP_JMP使用。 // iABC:A纯数字,对比较结果的预期,满足预期则跳转,B参数1的索引,C参数2的索引 case OP_LT: { // if a < b then -----> index(a) = 1, index(b) = 2 // >----> 这里将生成两行指令: // >----> OP_LT 0 1 2 // 用0,因为成立的话跳过,不成立才执行 // >----> OP_JMP N // N 表示then ... end中间的指令数量 // >----> ... // Instructions between "then" and "end" Protect( if (luaV_lessthan(L, RKB(i), RKC(i)) == GETARG_A(i)) dojump(L, pc, GETARG_sBx(*pc)); ) pc++; continue; } // 功能:检查两个变量是否小于等于,满足预期则跳转。配合OP_JMP使用。 // iABC:A纯数字,对比较结果的预期,满足预期则跳转,B参数1的索引,C参数2的索引 case OP_LE: { // if a <= b then -----> index(a) = 1, index(b) = 2 // >----> 这里将生成两行指令: // >----> OP_LE 0 1 2 // 用0,因为成立的话跳过,不成立才执行 // >----> OP_JMP N // N 表示then ... end中间的指令数量 // >----> ... // Instructions between "then" and "end" Protect( if (lessequal(L, RKB(i), RKC(i)) == GETARG_A(i)) dojump(L, pc, GETARG_sBx(*pc)); ) pc++; continue; }
StkId luaV_execute (lua_State *L) { LClosure *cl; TObject *k; const Instruction *pc; callentry: /* entry point when calling new functions */ L->ci->u.l.pc = &pc; if (L->hookmask & LUA_MASKCALL) luaD_callhook(L, LUA_HOOKCALL, -1); retentry: /* entry point when returning to old functions */ lua_assert(L->ci->state == CI_SAVEDPC || L->ci->state == (CI_SAVEDPC | CI_CALLING)); L->ci->state = CI_HASFRAME; /* activate frame */ pc = L->ci->u.l.savedpc; cl = &clvalue(L->base - 1)->l; k = cl->p->k; /* main loop of interpreter */ for (;;) { const Instruction i = *pc++; StkId base, ra; if ((L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) && (--L->hookcount == 0 || L->hookmask & LUA_MASKLINE)) { traceexec(L); if (L->ci->state & CI_YIELD) { /* did hook yield? */ L->ci->u.l.savedpc = pc - 1; L->ci->state = CI_YIELD | CI_SAVEDPC; return NULL; } } /* warning!! several calls may realloc the stack and invalidate `ra' */ base = L->base; ra = RA(i); lua_assert(L->ci->state & CI_HASFRAME); lua_assert(base == L->ci->base); lua_assert(L->top <= L->stack + L->stacksize && L->top >= base); lua_assert(L->top == L->ci->top || GET_OPCODE(i) == OP_CALL || GET_OPCODE(i) == OP_TAILCALL || GET_OPCODE(i) == OP_RETURN || GET_OPCODE(i) == OP_SETLISTO); switch (GET_OPCODE(i)) { case OP_MOVE: { setobjs2s(ra, RB(i)); break; } case OP_LOADK: { setobj2s(ra, KBx(i)); break; } case OP_LOADBOOL: { setbvalue(ra, GETARG_B(i)); if (GETARG_C(i)) pc++; /* skip next instruction (if C) */ break; } case OP_LOADNIL: { TObject *rb = RB(i); do { setnilvalue(rb--); } while (rb >= ra); break; } case OP_GETUPVAL: { int b = GETARG_B(i); setobj2s(ra, cl->upvals[b]->v); break; } case OP_GETGLOBAL: { TObject *rb = KBx(i); const TObject *v; lua_assert(ttisstring(rb) && ttistable(&cl->g)); v = luaH_getstr(hvalue(&cl->g), tsvalue(rb)); if (!ttisnil(v)) { setobj2s(ra, v); } else setobj2s(XRA(i), luaV_index(L, &cl->g, rb, 0)); break; } case OP_GETTABLE: { StkId rb = RB(i); TObject *rc = RKC(i); if (ttistable(rb)) { const TObject *v = luaH_get(hvalue(rb), rc); if (!ttisnil(v)) { setobj2s(ra, v); } else setobj2s(XRA(i), luaV_index(L, rb, rc, 0)); } else setobj2s(XRA(i), luaV_getnotable(L, rb, rc, 0)); break; } case OP_SETGLOBAL: { lua_assert(ttisstring(KBx(i)) && ttistable(&cl->g)); luaV_settable(L, &cl->g, KBx(i), ra); break; } case OP_SETUPVAL: { int b = GETARG_B(i); setobj(cl->upvals[b]->v, ra); /* write barrier */ break; } case OP_SETTABLE: { luaV_settable(L, ra, RKB(i), RKC(i)); break; } case OP_NEWTABLE: { int b = GETARG_B(i); b = fb2int(b); sethvalue(ra, luaH_new(L, b, GETARG_C(i))); luaC_checkGC(L); break; } case OP_SELF: { StkId rb = RB(i); TObject *rc = RKC(i); runtime_check(L, ttisstring(rc)); setobjs2s(ra+1, rb); if (ttistable(rb)) { const TObject *v = luaH_getstr(hvalue(rb), tsvalue(rc)); if (!ttisnil(v)) { setobj2s(ra, v); } else setobj2s(XRA(i), luaV_index(L, rb, rc, 0)); } else setobj2s(XRA(i), luaV_getnotable(L, rb, rc, 0)); break; } case OP_ADD: { TObject *rb = RKB(i); TObject *rc = RKC(i); if (ttisnumber(rb) && ttisnumber(rc)) { setnvalue(ra, nvalue(rb) + nvalue(rc)); } else Arith(L, ra, rb, rc, TM_ADD); break; } case OP_SUB: { TObject *rb = RKB(i); TObject *rc = RKC(i); if (ttisnumber(rb) && ttisnumber(rc)) { setnvalue(ra, nvalue(rb) - nvalue(rc)); } else Arith(L, ra, rb, rc, TM_SUB); break; } case OP_MUL: { TObject *rb = RKB(i); TObject *rc = RKC(i); if (ttisnumber(rb) && ttisnumber(rc)) { setnvalue(ra, nvalue(rb) * nvalue(rc)); } else Arith(L, ra, rb, rc, TM_MUL); break; } case OP_DIV: { TObject *rb = RKB(i); TObject *rc = RKC(i); if (ttisnumber(rb) && ttisnumber(rc)) { setnvalue(ra, nvalue(rb) / nvalue(rc)); } else Arith(L, ra, rb, rc, TM_DIV); break; } case OP_POW: { Arith(L, ra, RKB(i), RKC(i), TM_POW); break; } case OP_UNM: { const TObject *rb = RB(i); TObject temp; if (tonumber(rb, &temp)) { setnvalue(ra, -nvalue(rb)); } else { setnilvalue(&temp); if (!call_binTM(L, RB(i), &temp, ra, TM_UNM)) luaG_aritherror(L, RB(i), &temp); } break; } case OP_NOT: { int res = l_isfalse(RB(i)); /* next assignment may change this value */ setbvalue(ra, res); break; } case OP_CONCAT: { int b = GETARG_B(i); int c = GETARG_C(i); luaV_concat(L, c-b+1, c); /* may change `base' (and `ra') */ base = L->base; setobjs2s(RA(i), base+b); luaC_checkGC(L); break; } case OP_JMP: { dojump(pc, GETARG_sBx(i)); break; } case OP_EQ: { if (equalobj(L, RKB(i), RKC(i)) != GETARG_A(i)) pc++; else dojump(pc, GETARG_sBx(*pc) + 1); break; } case OP_LT: { if (luaV_lessthan(L, RKB(i), RKC(i)) != GETARG_A(i)) pc++; else dojump(pc, GETARG_sBx(*pc) + 1); break; } case OP_LE: { if (luaV_lessequal(L, RKB(i), RKC(i)) != GETARG_A(i)) pc++; else dojump(pc, GETARG_sBx(*pc) + 1); break; } case OP_TEST: { TObject *rb = RB(i); if (l_isfalse(rb) == GETARG_C(i)) pc++; else { setobjs2s(ra, rb); dojump(pc, GETARG_sBx(*pc) + 1); } break; } case OP_CALL: case OP_TAILCALL: { StkId firstResult; int b = GETARG_B(i); int nresults; if (b != 0) L->top = ra+b; /* else previous instruction set top */ nresults = GETARG_C(i) - 1; firstResult = luaD_precall(L, ra); if (firstResult) { if (firstResult > L->top) { /* yield? */ lua_assert(L->ci->state == (CI_C | CI_YIELD)); (L->ci - 1)->u.l.savedpc = pc; (L->ci - 1)->state = CI_SAVEDPC; return NULL; } /* it was a C function (`precall' called it); adjust results */ luaD_poscall(L, nresults, firstResult); if (nresults >= 0) L->top = L->ci->top; } else { /* it is a Lua function */ if (GET_OPCODE(i) == OP_CALL) { /* regular call? */ (L->ci-1)->u.l.savedpc = pc; /* save `pc' to return later */ (L->ci-1)->state = (CI_SAVEDPC | CI_CALLING); } else { /* tail call: put new frame in place of previous one */ int aux; base = (L->ci - 1)->base; /* `luaD_precall' may change the stack */ ra = RA(i); if (L->openupval) luaF_close(L, base); for (aux = 0; ra+aux < L->top; aux++) /* move frame down */ setobjs2s(base+aux-1, ra+aux); (L->ci - 1)->top = L->top = base+aux; /* correct top */ lua_assert(L->ci->state & CI_SAVEDPC); (L->ci - 1)->u.l.savedpc = L->ci->u.l.savedpc; (L->ci - 1)->u.l.tailcalls++; /* one more call lost */ (L->ci - 1)->state = CI_SAVEDPC; L->ci--; /* remove new frame */ L->base = L->ci->base; } goto callentry; } break; } case OP_RETURN: { CallInfo *ci = L->ci - 1; /* previous function frame */ int b = GETARG_B(i); if (b != 0) L->top = ra+b-1; lua_assert(L->ci->state & CI_HASFRAME); if (L->openupval) luaF_close(L, base); L->ci->state = CI_SAVEDPC; /* deactivate current function */ L->ci->u.l.savedpc = pc; /* previous function was running `here'? */ if (!(ci->state & CI_CALLING)) { lua_assert((ci->state & CI_C) || ci->u.l.pc != &pc); return ra; /* no: return */ } else { /* yes: continue its execution */ int nresults; lua_assert(ci->u.l.pc == &pc && ttisfunction(ci->base - 1) && (ci->state & CI_SAVEDPC)); lua_assert(GET_OPCODE(*(ci->u.l.savedpc - 1)) == OP_CALL); nresults = GETARG_C(*(ci->u.l.savedpc - 1)) - 1; luaD_poscall(L, nresults, ra); if (nresults >= 0) L->top = L->ci->top; goto retentry; } } case OP_FORLOOP: { lua_Number step, idx, limit; const TObject *plimit = ra+1; const TObject *pstep = ra+2; if (!ttisnumber(ra)) luaG_runerror(L, "`for' initial value must be a number"); if (!tonumber(plimit, ra+1)) luaG_runerror(L, "`for' limit must be a number"); if (!tonumber(pstep, ra+2)) luaG_runerror(L, "`for' step must be a number"); step = nvalue(pstep); idx = nvalue(ra) + step; /* increment index */ limit = nvalue(plimit); if (step > 0 ? idx <= limit : idx >= limit) { dojump(pc, GETARG_sBx(i)); /* jump back */ chgnvalue(ra, idx); /* update index */ } break; } case OP_TFORLOOP: { int nvar = GETARG_C(i) + 1; StkId cb = ra + nvar + 2; /* call base */ setobjs2s(cb, ra); setobjs2s(cb+1, ra+1); setobjs2s(cb+2, ra+2); L->top = cb+3; /* func. + 2 args (state and index) */ luaD_call(L, cb, nvar); L->top = L->ci->top; ra = XRA(i) + 2; /* final position of first result */ cb = ra + nvar; do { /* move results to proper positions */ nvar--; setobjs2s(ra+nvar, cb+nvar); } while (nvar > 0); if (ttisnil(ra)) /* break loop? */ pc++; /* skip jump (break loop) */ else dojump(pc, GETARG_sBx(*pc) + 1); /* jump back */ break; } case OP_TFORPREP: { /* for compatibility only */ if (ttistable(ra)) { setobjs2s(ra+1, ra); setobj2s(ra, luaH_getstr(hvalue(gt(L)), luaS_new(L, "next"))); } dojump(pc, GETARG_sBx(i)); break; } case OP_SETLIST: case OP_SETLISTO: { int bc; int n; Table *h; runtime_check(L, ttistable(ra)); h = hvalue(ra); bc = GETARG_Bx(i); if (GET_OPCODE(i) == OP_SETLIST) n = (bc&(LFIELDS_PER_FLUSH-1)) + 1; else { n = L->top - ra - 1; L->top = L->ci->top; } bc &= ~(LFIELDS_PER_FLUSH-1); /* bc = bc - bc%FPF */ for (; n > 0; n--) setobj2t(luaH_setnum(L, h, bc+n), ra+n); /* write barrier */ break; } case OP_CLOSE: { luaF_close(L, ra); break; } case OP_CLOSURE: { Proto *p; Closure *ncl; int nup, j; p = cl->p->p[GETARG_Bx(i)]; nup = p->nups; ncl = luaF_newLclosure(L, nup, &cl->g); ncl->l.p = p; for (j=0; j<nup; j++, pc++) { if (GET_OPCODE(*pc) == OP_GETUPVAL) ncl->l.upvals[j] = cl->upvals[GETARG_B(*pc)]; else { lua_assert(GET_OPCODE(*pc) == OP_MOVE); ncl->l.upvals[j] = luaF_findupval(L, base + GETARG_B(*pc)); } } setclvalue(ra, ncl); luaC_checkGC(L); break; } } } }
void luaV_execute (lua_State *L, int nexeccalls) { LClosure *cl; StkId base; TValue *k; const Instruction *pc; reentry: /* entry point */ lua_assert(isLua(L->ci)); pc = L->savedpc; cl = &clvalue(L->ci->func)->l; base = L->base; k = cl->p->k; /* main loop of interpreter */ for (;;) { const Instruction i = *pc++; StkId ra; if ((L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) && (--L->hookcount == 0 || L->hookmask & LUA_MASKLINE)) { traceexec(L, pc); if (L->status == LUA_YIELD) { /* did hook yield? */ L->savedpc = pc - 1; return; } base = L->base; } /* warning!! several calls may realloc the stack and invalidate `ra' */ ra = RA(i); lua_assert(base == L->base && L->base == L->ci->base); lua_assert(base <= L->top && L->top <= L->stack + L->stacksize); lua_assert(L->top == L->ci->top || luaG_checkopenop(i)); switch (GET_OPCODE(i)) { case OP_MOVE: { setobjs2s(L, ra, RB(i)); continue; } case OP_LOADK: { setobj2s(L, ra, KBx(i)); continue; } case OP_LOADBOOL: { setbvalue(ra, GETARG_B(i)); if (GETARG_C(i)) pc++; /* skip next instruction (if C) */ continue; } case OP_LOADNIL: { TValue *rb = RB(i); do { setnilvalue(rb--); } while (rb >= ra); continue; } case OP_GETUPVAL: { int b = GETARG_B(i); setobj2s(L, ra, cl->upvals[b]->v); continue; } case OP_GETGLOBAL: { TValue g; TValue *rb = KBx(i); sethvalue(L, &g, cl->env); lua_assert(ttisstring(rb)); Protect(luaV_gettable(L, &g, rb, ra)); continue; } case OP_GETTABLE: { Protect(luaV_gettable(L, RB(i), RKC(i), ra)); continue; } case OP_SETGLOBAL: { TValue g; sethvalue(L, &g, cl->env); lua_assert(ttisstring(KBx(i))); Protect(luaV_settable(L, &g, KBx(i), ra)); continue; } case OP_SETUPVAL: { UpVal *uv = cl->upvals[GETARG_B(i)]; setobj(L, uv->v, ra); luaC_barrier(L, uv, ra); continue; } case OP_SETTABLE: { Protect(luaV_settable(L, ra, RKB(i), RKC(i))); continue; } case OP_NEWTABLE: { int b = GETARG_B(i); int c = GETARG_C(i); sethvalue(L, ra, luaH_new(L, luaO_fb2int(b), luaO_fb2int(c))); Protect(luaC_checkGC(L)); continue; } case OP_SELF: { StkId rb = RB(i); setobjs2s(L, ra+1, rb); Protect(luaV_gettable(L, rb, RKC(i), ra)); continue; } case OP_ADD: { arith_op(luai_numadd, TM_ADD); continue; } case OP_SUB: { arith_op(luai_numsub, TM_SUB); continue; } case OP_MUL: { arith_op(luai_nummul, TM_MUL); continue; } case OP_DIV: { arith_op(luai_numdiv, TM_DIV); continue; } case OP_MOD: { arith_op(luai_nummod, TM_MOD); continue; } case OP_POW: { arith_op(luai_numpow, TM_POW); continue; } case OP_UNM: { TValue *rb = RB(i); if (ttisnumber(rb)) { lua_Number nb = nvalue(rb); setnvalue(ra, luai_numunm(nb)); } else { Protect(luaV_arith(L, ra, rb, rb, TM_UNM)); } continue; } case OP_NOT: { int res = l_isfalse(RB(i)); /* next assignment may change this value */ setbvalue(ra, res); continue; } case OP_LEN: { const TValue *rb = RB(i); switch (ttype(rb)) { case LUA_TTABLE: { setnvalue(ra, cast_num(luaH_getn(hvalue(rb)))); break; } case LUA_TSTRING: { setnvalue(ra, cast_num(tsvalue(rb)->len)); break; } default: { /* try metamethod */ Protect( if (!call_binTM(L, rb, luaO_nilobject, ra, TM_LEN)) luaG_typeerror(L, rb, "get length of"); ) } } continue; } case OP_CONCAT: { int b = GETARG_B(i); int c = GETARG_C(i); Protect(luaV_concat(L, c-b+1, c); luaC_checkGC(L)); setobjs2s(L, RA(i), base+b); continue; } case OP_JMP: { dojump(L, pc, GETARG_sBx(i)); continue; } case OP_EQ: { TValue *rb = RKB(i); TValue *rc = RKC(i); Protect( if (equalobj(L, rb, rc) == GETARG_A(i)) dojump(L, pc, GETARG_sBx(*pc)); ) pc++; continue; } case OP_LT: { Protect( if (luaV_lessthan(L, RKB(i), RKC(i)) == GETARG_A(i)) dojump(L, pc, GETARG_sBx(*pc)); ) pc++; continue; } case OP_LE: { Protect( if (luaV_lessequal(L, RKB(i), RKC(i)) == GETARG_A(i)) dojump(L, pc, GETARG_sBx(*pc)); ) pc++; continue; }
/* ** Executes the given Lua function. Parameters are between [base,top). ** Returns n such that the the results are between [n,top). */ StkId luaV_execute (lua_State *L, const Closure *cl, StkId base) { const Proto *const tf = cl->f.l; StkId top; /* keep top local, for performance */ const Instruction *pc = tf->code; TString **const kstr = tf->kstr; const lua_Hook linehook = L->linehook; infovalue(base-1)->pc = &pc; luaD_checkstack(L, tf->maxstacksize+EXTRA_STACK); if (tf->is_vararg) /* varargs? */ adjust_varargs(L, base, tf->numparams); else luaD_adjusttop(L, base, tf->numparams); top = L->top; /* main loop of interpreter */ for (;;) { const Instruction i = *pc++; if (linehook) traceexec(L, base, top, linehook); switch (GET_OPCODE(i)) { case OP_END: { L->top = top; return top; } case OP_RETURN: { L->top = top; return base+GETARG_U(i); } case OP_CALL: { int nres = GETARG_B(i); if (nres == MULT_RET) nres = LUA_MULTRET; L->top = top; luaD_call(L, base+GETARG_A(i), nres); top = L->top; break; } case OP_TAILCALL: { L->top = top; luaD_call(L, base+GETARG_A(i), LUA_MULTRET); return base+GETARG_B(i); } case OP_PUSHNIL: { int n = GETARG_U(i); LUA_ASSERT(n>0, "invalid argument"); do { ttype(top++) = LUA_TNIL; } while (--n > 0); break; } case OP_POP: { top -= GETARG_U(i); break; } case OP_PUSHINT: { ttype(top) = LUA_TNUMBER; nvalue(top) = (Number)GETARG_S(i); top++; break; } case OP_PUSHSTRING: { ttype(top) = LUA_TSTRING; tsvalue(top) = kstr[GETARG_U(i)]; top++; break; } case OP_PUSHNUM: { ttype(top) = LUA_TNUMBER; nvalue(top) = tf->knum[GETARG_U(i)]; top++; break; } case OP_PUSHNEGNUM: { ttype(top) = LUA_TNUMBER; nvalue(top) = -tf->knum[GETARG_U(i)]; top++; break; } case OP_PUSHUPVALUE: { *top++ = cl->upvalue[GETARG_U(i)]; break; } case OP_GETLOCAL: { *top++ = *(base+GETARG_U(i)); break; } case OP_GETGLOBAL: { L->top = top; *top = *luaV_getglobal(L, kstr[GETARG_U(i)]); top++; break; } case OP_GETTABLE: { L->top = top; top--; *(top-1) = *luaV_gettable(L, top-1); break; } case OP_GETDOTTED: { ttype(top) = LUA_TSTRING; tsvalue(top) = kstr[GETARG_U(i)]; L->top = top+1; *(top-1) = *luaV_gettable(L, top-1); break; } case OP_GETINDEXED: { *top = *(base+GETARG_U(i)); L->top = top+1; *(top-1) = *luaV_gettable(L, top-1); break; } case OP_PUSHSELF: { TObject receiver; receiver = *(top-1); ttype(top) = LUA_TSTRING; tsvalue(top++) = kstr[GETARG_U(i)]; L->top = top; *(top-2) = *luaV_gettable(L, top-2); *(top-1) = receiver; break; } case OP_CREATETABLE: { L->top = top; luaC_checkGC(L); hvalue(top) = luaH_new(L, GETARG_U(i)); ttype(top) = LUA_TTABLE; top++; break; } case OP_SETLOCAL: { *(base+GETARG_U(i)) = *(--top); break; } case OP_SETGLOBAL: { L->top = top; luaV_setglobal(L, kstr[GETARG_U(i)]); top--; break; } case OP_SETTABLE: { StkId t = top-GETARG_A(i); L->top = top; luaV_settable(L, t, t+1); top -= GETARG_B(i); /* pop values */ break; } case OP_SETLIST: { int aux = GETARG_A(i) * LFIELDS_PER_FLUSH; int n = GETARG_B(i); Hash *arr = hvalue(top-n-1); L->top = top-n; /* final value of `top' (in case of errors) */ for (; n; n--) *luaH_setint(L, arr, n+aux) = *(--top); break; } case OP_SETMAP: { int n = GETARG_U(i); StkId finaltop = top-2*n; Hash *arr = hvalue(finaltop-1); L->top = finaltop; /* final value of `top' (in case of errors) */ for (; n; n--) { top-=2; *luaH_set(L, arr, top) = *(top+1); } break; } case OP_ADD: { if (tonumber(top-2) || tonumber(top-1)) call_arith(L, top, TM_ADD); else nvalue(top-2) += nvalue(top-1); top--; break; } case OP_ADDI: { if (tonumber(top-1)) { ttype(top) = LUA_TNUMBER; nvalue(top) = (Number)GETARG_S(i); call_arith(L, top+1, TM_ADD); } else nvalue(top-1) += (Number)GETARG_S(i); break; } case OP_SUB: { if (tonumber(top-2) || tonumber(top-1)) call_arith(L, top, TM_SUB); else nvalue(top-2) -= nvalue(top-1); top--; break; } case OP_MULT: { if (tonumber(top-2) || tonumber(top-1)) call_arith(L, top, TM_MUL); else nvalue(top-2) *= nvalue(top-1); top--; break; } case OP_DIV: { if (tonumber(top-2) || tonumber(top-1)) call_arith(L, top, TM_DIV); else nvalue(top-2) /= nvalue(top-1); top--; break; } case OP_POW: { if (!call_binTM(L, top, TM_POW)) lua_error(L, "undefined operation"); top--; break; } case OP_CONCAT: { int n = GETARG_U(i); luaV_strconc(L, n, top); top -= n-1; L->top = top; luaC_checkGC(L); break; } case OP_MINUS: { if (tonumber(top-1)) { ttype(top) = LUA_TNIL; call_arith(L, top+1, TM_UNM); } else nvalue(top-1) = -nvalue(top-1); break; } case OP_NOT: { ttype(top-1) = (ttype(top-1) == LUA_TNIL) ? LUA_TNUMBER : LUA_TNIL; nvalue(top-1) = 1; break; } case OP_JMPNE: { top -= 2; if (!luaO_equalObj(top, top+1)) dojump(pc, i); break; } case OP_JMPEQ: { top -= 2; if (luaO_equalObj(top, top+1)) dojump(pc, i); break; } case OP_JMPLT: { top -= 2; if (luaV_lessthan(L, top, top+1, top+2)) dojump(pc, i); break; } case OP_JMPLE: { /* a <= b === !(b<a) */ top -= 2; if (!luaV_lessthan(L, top+1, top, top+2)) dojump(pc, i); break; } case OP_JMPGT: { /* a > b === (b<a) */ top -= 2; if (luaV_lessthan(L, top+1, top, top+2)) dojump(pc, i); break; } case OP_JMPGE: { /* a >= b === !(a<b) */ top -= 2; if (!luaV_lessthan(L, top, top+1, top+2)) dojump(pc, i); break; } case OP_JMPT: { if (ttype(--top) != LUA_TNIL) dojump(pc, i); break; } case OP_JMPF: { if (ttype(--top) == LUA_TNIL) dojump(pc, i); break; } case OP_JMPONT: { if (ttype(top-1) == LUA_TNIL) top--; else dojump(pc, i); break; } case OP_JMPONF: { if (ttype(top-1) != LUA_TNIL) top--; else dojump(pc, i); break; } case OP_JMP: { dojump(pc, i); break; } case OP_PUSHNILJMP: { ttype(top++) = LUA_TNIL; pc++; break; } case OP_FORPREP: { if (tonumber(top-1)) lua_error(L, "`for' step must be a number"); if (tonumber(top-2)) lua_error(L, "`for' limit must be a number"); if (tonumber(top-3)) lua_error(L, "`for' initial value must be a number"); if (nvalue(top-1) > 0 ? nvalue(top-3) > nvalue(top-2) : nvalue(top-3) < nvalue(top-2)) { /* `empty' loop? */ top -= 3; /* remove control variables */ dojump(pc, i); /* jump to loop end */ } break; } case OP_FORLOOP: { LUA_ASSERT(ttype(top-1) == LUA_TNUMBER, "invalid step"); LUA_ASSERT(ttype(top-2) == LUA_TNUMBER, "invalid limit"); if (ttype(top-3) != LUA_TNUMBER) lua_error(L, "`for' index must be a number"); nvalue(top-3) += nvalue(top-1); /* increment index */ if (nvalue(top-1) > 0 ? nvalue(top-3) > nvalue(top-2) : nvalue(top-3) < nvalue(top-2)) top -= 3; /* end loop: remove control variables */ else dojump(pc, i); /* repeat loop */ break; } case OP_LFORPREP: { Node *node; if (ttype(top-1) != LUA_TTABLE) lua_error(L, "`for' table must be a table"); node = luaH_next(L, hvalue(top-1), &luaO_nilobject); if (node == NULL) { /* `empty' loop? */ top--; /* remove table */ dojump(pc, i); /* jump to loop end */ } else { top += 2; /* index,value */ *(top-2) = *key(node); *(top-1) = *val(node); } break; } case OP_LFORLOOP: { Node *node; LUA_ASSERT(ttype(top-3) == LUA_TTABLE, "invalid table"); node = luaH_next(L, hvalue(top-3), top-2); if (node == NULL) /* end loop? */ top -= 3; /* remove table, key, and value */ else { *(top-2) = *key(node); *(top-1) = *val(node); dojump(pc, i); /* repeat loop */ } break; } case OP_CLOSURE: { L->top = top; luaV_Lclosure(L, tf->kproto[GETARG_A(i)], GETARG_B(i)); top = L->top; luaC_checkGC(L); break; } } } }
LUA_API void lua_concat (lua_State *L, int n) { StkId top = L->top; luaV_strconc(L, n, top); L->top = top-(n-1); luaC_checkGC(L); }