void luaG_concaterror (lua_State *L, StkId p1, StkId p2) { if (ttisstring(p1) || ttisnumber(p1)) p1 = p2; lua_assert(!ttisstring(p1) && !ttisnumber(p1)); luaG_typeerror(L, p1, "concatenate"); }
static Instruction luaG_symbexec (const Proto *pt, int lastpc, int reg) { int pc; int last; /* stores position of last instruction that changed `reg' */ last = pt->sizecode-1; /* points to final return (a `neutral' instruction) */ check(precheck(pt)); for (pc = 0; pc < lastpc; pc++) { const Instruction i = pt->code[pc]; OpCode op = GET_OPCODE(i); int a = GETARG_A(i); int b = 0; int c = 0; checkreg(pt, a); switch (getOpMode(op)) { case iABC: { b = GETARG_B(i); c = GETARG_C(i); if (testOpMode(op, OpModeBreg)) { checkreg(pt, b); } else if (testOpMode(op, OpModeBrk)) check(checkRK(pt, b)); if (testOpMode(op, OpModeCrk)) check(checkRK(pt, c)); break; } case iABx: { b = GETARG_Bx(i); if (testOpMode(op, OpModeK)) check(b < pt->sizek); break; } case iAsBx: { b = GETARG_sBx(i); break; } } if (testOpMode(op, OpModesetA)) { if (a == reg) last = pc; /* change register `a' */ } if (testOpMode(op, OpModeT)) { check(pc+2 < pt->sizecode); /* check skip */ check(GET_OPCODE(pt->code[pc+1]) == OP_JMP); } switch (op) { case OP_LOADBOOL: { check(c == 0 || pc+2 < pt->sizecode); /* check its jump */ break; } case OP_LOADNIL: { if (a <= reg && reg <= b) last = pc; /* set registers from `a' to `b' */ break; } case OP_GETUPVAL: case OP_SETUPVAL: { check(b < pt->nups); break; } case OP_GETGLOBAL: case OP_SETGLOBAL: { check(ttisstring(&pt->k[b])); break; } case OP_SELF: { checkreg(pt, a+1); if (reg == a+1) last = pc; break; } case OP_CONCAT: { /* `c' is a register, and at least two operands */ check(c < MAXSTACK && b < c); break; } case OP_TFORLOOP: checkreg(pt, a+c+5); if (reg >= a) last = pc; /* affect all registers above base */ /* go through */ case OP_FORLOOP: checkreg(pt, a+2); /* go through */ case OP_JMP: { int dest = pc+1+b; check(0 <= dest && dest < pt->sizecode); /* not full check and jump is forward and do not skip `lastpc'? */ if (reg != NO_REG && pc < dest && dest <= lastpc) pc += b; /* do the jump */ break; } case OP_CALL: case OP_TAILCALL: { if (b != 0) { checkreg(pt, a+b-1); } c--; /* c = num. returns */ if (c == LUA_MULTRET) { check(checkopenop(pt, pc)); } else if (c != 0) checkreg(pt, a+c-1); if (reg >= a) last = pc; /* affect all registers above base */ break; } case OP_RETURN: { b--; /* b = num. returns */ if (b > 0) checkreg(pt, a+b-1); break; } case OP_SETLIST: { checkreg(pt, a + (b&(LFIELDS_PER_FLUSH-1)) + 1); break; } case OP_CLOSURE: { int nup; check(b < pt->sizep); nup = pt->p[b]->nups; check(pc + nup < pt->sizecode); for (; nup>0; nup--) { OpCode op1 = GET_OPCODE(pt->code[pc+nup]); check(op1 == OP_GETUPVAL || op1 == OP_MOVE); } break; } default: break; } } return pt->code[last]; }
static Instruction symbexec (const Proto *pt, int lastpc, int reg) { int pc; int last; /* stores position of last instruction that changed `reg' */ last = pt->sizecode-1; /* points to final return (a `neutral' instruction) */ check(precheck(pt)); for (pc = 0; pc < lastpc; pc++) { Instruction i = pt->code[pc]; OpCode op = GET_OPCODE(i); int a = GETARG_A(i); int b = 0; int c = 0; check(op < NUM_OPCODES); checkreg(pt, a); switch (getOpMode(op)) { case iABC: { b = GETARG_B(i); c = GETARG_C(i); check(checkArgMode(pt, b, getBMode(op))); check(checkArgMode(pt, c, getCMode(op))); break; } case iABx: { b = GETARG_Bx(i); if (getBMode(op) == OpArgK) check(b < pt->sizek); break; } case iAsBx: { b = GETARG_sBx(i); if (getBMode(op) == OpArgR) { int dest = pc+1+b; check(0 <= dest && dest < pt->sizecode); if (dest > 0) { /* cannot jump to a setlist count */ Instruction d = pt->code[dest-1]; check(!(GET_OPCODE(d) == OP_SETLIST && GETARG_C(d) == 0)); } } break; } } if (testAMode(op)) { if (a == reg) last = pc; /* change register `a' */ } if (testTMode(op)) { check(pc+2 < pt->sizecode); /* check skip */ check(GET_OPCODE(pt->code[pc+1]) == OP_JMP); } switch (op) { case OP_LOADBOOL: { check(c == 0 || pc+2 < pt->sizecode); /* check its jump */ break; } case OP_LOADNIL: { if (a <= reg && reg <= b) last = pc; /* set registers from `a' to `b' */ break; } case OP_GETUPVAL: case OP_SETUPVAL: { check(b < pt->nups); break; } case OP_GETGLOBAL: case OP_SETGLOBAL: { check(ttisstring(&pt->k[b])); break; } case OP_SELF: { checkreg(pt, a+1); if (reg == a+1) last = pc; break; } case OP_CONCAT: { check(b < c); /* at least two operands */ break; } case OP_TFORLOOP: { check(c >= 1); /* at least one result (control variable) */ checkreg(pt, a+2+c); /* space for results */ if (reg >= a+2) last = pc; /* affect all regs above its base */ break; } case OP_FORLOOP: case OP_FORPREP: checkreg(pt, a+3); /* go through */ case OP_JMP: { int dest = pc+1+b; /* not full check and jump is forward and do not skip `lastpc'? */ if (reg != NO_REG && pc < dest && dest <= lastpc) pc += b; /* do the jump */ break; } case OP_CALL: case OP_TAILCALL: { if (b != 0) { checkreg(pt, a+b-1); } c--; /* c = num. returns */ if (c == LUA_MULTRET) { check(checkopenop(pt, pc)); } else if (c != 0) checkreg(pt, a+c-1); if (reg >= a) last = pc; /* affect all registers above base */ break; } case OP_RETURN: { b--; /* b = num. returns */ if (b > 0) checkreg(pt, a+b-1); break; } case OP_SETLIST: { if (b > 0) checkreg(pt, a + b); if (c == 0) pc++; break; } case OP_CLOSURE: { int nup, j; check(b < pt->sizep); nup = pt->p[b]->nups; check(pc + nup < pt->sizecode); for (j = 1; j <= nup; j++) { OpCode op1 = GET_OPCODE(pt->code[pc + j]); check(op1 == OP_GETUPVAL || op1 == OP_MOVE); } if (reg != NO_REG) /* tracing? */ pc += nup; /* do not 'execute' these pseudo-instructions */ break; } case OP_VARARG: { check((pt->is_vararg & VARARG_ISVARARG) && !(pt->is_vararg & VARARG_NEEDSARG)); b--; if (b == LUA_MULTRET) check(checkopenop(pt, pc)); checkreg(pt, a+b-1); break; } default: break; } } return pt->code[last]; }
static const char *kname (Proto *p, int c) { if (ISK(c) && ttisstring(&p->k[INDEXK(c)])) return svalue(&p->k[INDEXK(c)]); else return "?"; }
l_noret luaG_concaterror (lua_State *L, const TValue *p1, const TValue *p2) { if (ttisstring(p1) || cvt2str(p1)) p1 = p2; luaG_typeerror(L, p1, "concatenate"); }
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(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 (lessequal(L, RKB(i), RKC(i)) == GETARG_A(i)) dojump(L, pc, GETARG_sBx(*pc)); ) pc++; continue; }
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; }
static int valismarked (const TObject *o) { if (ttisstring(o) || ttiswstring(o)) stringmark(tsvalue(o)); /* strings are `values', so are never weak */ return !iscollectable(o) || testbit(o->value.gc->gch.marked, 0); }
LUA_API int lua_isstring (lua_State *L, int idx) { const TValue *o = index2addr(L, idx); return (ttisstring(o) || cvt2str(o)); }
// 已经准备好了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 */ if (L->hookmask & LUA_MASKCALL) luaD_callhook(L, LUA_HOOKCALL, -1); retentry: /* entry point when returning to old functions */ L->ci->u.l.pc = &pc; 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(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 BijouBlock_dump2(VM, BijouBlock* b, int level) { char * str; size_t x; INDENT; printf("; block at: %p, %s (level %d)\n", (void *)b, b->funcname != NULL ? b->funcname : "", level); INDENT; printf("; %zu registers\n", b->regc); INDENT; printf("; constants (%zu)\n", kv_size(b->k)); for (x = 0; x < kv_size(b->k); ++x) { str = TValue_to_string(kv_A(b->k, x)); int s = ttisstring(&kv_A(b->k, x)); INDENT; printf("\t%zu: (%s) %s%s%s\n", x, TValue_type_to_string(kv_A(b->k, x)), s ? "\"" : "", str, s ? "\"" : ""); if (ttisnumber(&kv_A(b->k, x))) B_FREE(str); } INDENT; printf("; locals (%zu)\n", kv_size(b->locals)); for (x = 0; x < kv_size(b->locals); ++x) { str = TValue_to_string(kv_A(b->locals, x)); INDENT; printf("\t%zu: (%s) %s\n", x, TValue_type_to_string(kv_A(b->locals, x)), str); if (ttisnumber(&kv_A(b->locals, x))) B_FREE(str); } INDENT; printf("; upvals (%zu)\n", kv_size(b->upvals)); for (x = 0; x < kv_size(b->upvals); ++x) { str = TValue_to_string(kv_A(b->upvals, x)); INDENT; printf("\t%zu: (%s) %s\n", x, TValue_type_to_string(kv_A(b->upvals, x)), str); if (ttisnumber(&kv_A(b->upvals, x))) B_FREE(str); } INDENT; printf("; code section (%zu instructions)\n", kv_size(b->code)); for (x = 0; x < kv_size(b->code); ++x) { bInst i = kv_A(b->code, x); INDENT; print_op(i); printf("\t"); switch (GET_OPCODE(i)) { case OP_MOVE: printf("; R[%d] = R[%d]", GETARG_A(i), GETARG_B(i)); break; case OP_LOADK: printf("; R[%d] = K[%d]", GETARG_A(i), GETARG_Bx(i)); break; case OP_LOADBOOL: printf("; R[%d] = %s", GETARG_A(i), GETARG_B(i) == 0 ? "false" : "true" ); break; case OP_LOADNULL: printf("; R[%d] = null", GETARG_A(i)); break; case OP_GETGLOBAL: printf("; R[%d] = globals[K[%d]]", GETARG_A(i), GETARG_Bx(i)); break; case OP_SETGLOBAL: printf("; globals[K[%d]] = R[%d]", GETARG_Bx(i), GETARG_A(i)); break; case OP_GETLOCAL: printf("; R[%d] = locals[K[%d]]", GETARG_A(i), GETARG_Bx(i)); break; case OP_SETLOCAL: printf("; locals[K[%d]] = R[%d]", GETARG_Bx(i), GETARG_A(i)); break; case OP_ADD: printf("; R[%d] = RK[%d] + RK[%d]", GETARG_A(i), GETARG_B(i), GETARG_C(i)); break; case OP_SUB: printf("; R[%d] = RK[%d] - RK[%d]", GETARG_A(i), GETARG_B(i), GETARG_C(i)); break; case OP_MUL: printf("; R[%d] = RK[%d] * RK[%d]", GETARG_A(i), GETARG_B(i), GETARG_C(i)); break; case OP_DIV: printf("; R[%d] = RK[%d] / RK[%d]", GETARG_A(i), GETARG_B(i), GETARG_C(i)); break; case OP_POW: printf("; R[%d] = RK[%d] ** RK[%d]", GETARG_A(i), GETARG_B(i), GETARG_C(i)); break; case OP_REM: printf("; R[%d] = RK[%d] %% RK[%d]", GETARG_A(i), GETARG_B(i), GETARG_C(i)); break; case OP_UNM: printf("; R[%d] = -RK[%d]", GETARG_A(i), GETARG_B(i)); break; case OP_NOT: printf("; R[%d] = !RK[%d]", GETARG_A(i), GETARG_B(i)); break; case OP_CLOSURE: printf("; R[%d] = ", GETARG_A(i)); if (ISK(GETARG_Bx(i))) { BijouFunction *func = kv_A(vm->functions, GETARG_Bx(i) & ~0x100); printf("%s\n", func->name); } else printf("closure[%d] (%s)\n", GETARG_Bx(i), b->children[GETARG_Bx(i)]->funcname); break; case OP_CALL: printf("; R[%d] = R[%d](", GETARG_A(i), GETARG_B(i)); size_t x; for (x = 0; x < GETARG_C(i); ++x) { printf("R[%zu], ", x); } if (x > 0) { printf("\b\b"); } printf(")"); break; case OP_GETEXTERNAL: printf("; R[%d] = closure(K[%d])", GETARG_A(i), GETARG_B(i)); break; } printf("\n"); } INDENT; printf("; functions (%zu definitions)\n", b->numchildren); for (x = 0; x < b->numchildren; ++x) { BijouBlock_dump2(vm, b->children[x], level + 1); } }
/* histogram: key should be number or string, value must be number */ void kp_table_histogram(ktap_State *ks, Table *t) { struct table_hist_record *thr; char dist_str[40]; int i, ratio, total = 0, count = 0; thr = kp_malloc(ks, sizeof(*thr) * (t->sizearray + sizenode(t))); for (i = 0; i < t->sizearray; i++) { Tvalue *v = &t->array[i]; if (isnil(v)) continue; if (!ttisnumber(v)) goto error; setnvalue(&thr[count++].key, i + 1); total += nvalue(v); } for (i = 0; i < sizenode(t); i++) { Node *n = &t->node[i]; int num; if (isnil(gkey(n))) continue; if (!ttisnumber(gval(n))) goto error; num = nvalue(gval(n)); setobj(ks, &thr[count].key, gkey(n)); setobj(ks, &thr[count].val, gval(n)); count++; total += nvalue(gval(n)); } sort(thr, count, sizeof(struct table_hist_record), hist_record_cmp, NULL); kp_printf(ks, "%32s%s%s\n", "value ", DISTRIBUTION_STR, " count"); dist_str[sizeof(dist_str) - 1] = '\0'; for (i = 0; i < count; i++) { Tvalue *key = &thr[i].key; Tvalue *val = &thr[i].val; memset(dist_str, ' ', sizeof(dist_str) - 1); ratio = (nvalue(val) * (sizeof(dist_str) - 1)) / total; memset(dist_str, '@', ratio); if (ttisstring(key)) { char buf[32 + 1] = {0}; char *keystr; if (strlen(svalue(key)) > 32) { strncpy(buf, svalue(key), 32-4); memset(buf + 32-4, '.', 3); keystr = buf; } else keystr = svalue(key); kp_printf(ks, "%32s |%s%-10d\n", keystr, dist_str, nvalue(val)); } else kp_printf(ks, "%32d | %s%-10d\n", nvalue(key), dist_str, nvalue(val)); } goto out; error: kp_printf(ks, "error: table histogram only handle " " (key: string/number val: number)\n"); out: kp_free(ks, thr); }
void luaV_gettable (lua_State *L, const TValue *t, TValue *key, StkId val) { int loop; for (loop = 0; loop < MAXTAGLOOP; loop++) { const TValue *tm = 0; /* LUA-VEC -- compiler gives a warning of uninitialized value on this so we set it to 0 */ if (ttistable(t)) { /* `t' is a table? */ Table *h = hvalue(t); const TValue *res = luaH_get(h, key); /* do a primitive get */ if (!ttisnil(res) || /* result is no nil? */ (tm = fasttm(L, h->metatable, TM_INDEX)) == NULL) { /* or no TM? */ setobj2s(L, val, res); return; } /* else will try the tag method */ } else if (ttisvec(t)) { /* LUA-VEC -- vec[idx] operator */ /* issue: "index" may not be the correct arg for luaG_typeerror in here */ if (ttisnumber(key) && /* acessing vec by a number? */ (nvalue(key) >= 1 && nvalue(key) <= 4)) { /* index is between 1-4? */ TValue res; setnvalue(&res, vecvalue(t)[(int)nvalue(key)-1]); setobj2s(L, val, &res); return; } else if (ttisstring(key)) { /* acessing vec by a string? */ if (tsvalue(key)->len == 1) { /* accessing by a single component, such as vec.x */ TValue res; switch (*getstr(tsvalue(key))) { case 'x': setnvalue(&res, vecvalue(t)[0]); break; case 'y': setnvalue(&res, vecvalue(t)[1]); break; case 'z': setnvalue(&res, vecvalue(t)[2]); break; case 'w': setnvalue(&res, vecvalue(t)[3]); break; default: luaG_typeerror(L, t, "index"); } setobj2s(L, val, &res); return; } else if (tsvalue(key)->len <= 4) { /* accessing by swizzling, such as vec.xy, vec.xxyz etc. */ TValue res; float v[4] = {0.0f,0.0f,0.0f,0.0f}; int i; for (i = 0; i < (int)tsvalue(key)->len; ++i) { switch (getstr(tsvalue(key))[i]) { case 'x': v[i] = vecvalue(t)[0]; break; case 'y': v[i] = vecvalue(t)[1]; break; case 'z': v[i] = vecvalue(t)[2]; break; case 'w': v[i] = vecvalue(t)[3]; break; default: luaG_typeerror(L, t, "index"); } } setvecvalue(&res, v[0], v[1], v[2], v[3]); setobj2s(L, val, &res); return; } } luaG_typeerror(L, t, "index"); } else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX))) luaG_typeerror(L, t, "index"); if (ttisfunction(tm)) { callTMres(L, val, tm, t, key); return; } t = tm; /* else repeat with `tm' */ } luaG_runerror(L, "loop in gettable"); }
l_noret luaG_concaterror (lua_State *L, const TValue *p1, const TValue *p2) { if (ttisstring(p1) || ttisnumber(p1)) p1 = p2; lua_assert(!ttisstring(p1) && !ttisnumber(p1)); luaG_typeerror(L, p1, "concatenate"); }