diff options
Diffstat (limited to 'apps/plugins/lua/lvm.c')
-rw-r--r-- | apps/plugins/lua/lvm.c | 914 |
1 files changed, 509 insertions, 405 deletions
diff --git a/apps/plugins/lua/lvm.c b/apps/plugins/lua/lvm.c index ee3256ab94..141b9fd19c 100644 --- a/apps/plugins/lua/lvm.c +++ b/apps/plugins/lua/lvm.c @@ -1,5 +1,5 @@ /* -** $Id: lvm.c,v 2.63.1.3 2007/12/28 15:32:23 roberto Exp $ +** $Id: lvm.c,v 2.155.1.1 2013/04/12 18:48:47 roberto Exp $ ** Lua virtual machine ** See Copyright Notice in lua.h */ @@ -35,7 +35,7 @@ const TValue *luaV_tonumber (const TValue *obj, TValue *n) { lua_Number num; if (ttisnumber(obj)) return obj; - if (ttisstring(obj) && luaO_str2d(svalue(obj), &num)) { + if (ttisstring(obj) && luaO_str2d(svalue(obj), tsvalue(obj)->len, &num)) { setnvalue(n, num); return n; } @@ -50,58 +50,60 @@ int luaV_tostring (lua_State *L, StkId obj) { else { char s[LUAI_MAXNUMBER2STR]; lua_Number n = nvalue(obj); - lua_number2str(s, n); - setsvalue2s(L, obj, luaS_new(L, s)); + int l = lua_number2str(s, n); + setsvalue2s(L, obj, luaS_newlstr(L, s, l)); return 1; } } -static void traceexec (lua_State *L, const Instruction *pc) { +static void traceexec (lua_State *L) { + CallInfo *ci = L->ci; lu_byte mask = L->hookmask; - const Instruction *oldpc = L->savedpc; - L->savedpc = pc; - if ((mask & LUA_MASKCOUNT) && L->hookcount == 0) { - resethookcount(L); - luaD_callhook(L, LUA_HOOKCOUNT, -1); + int counthook = ((mask & LUA_MASKCOUNT) && L->hookcount == 0); + if (counthook) + resethookcount(L); /* reset count */ + if (ci->callstatus & CIST_HOOKYIELD) { /* called hook last time? */ + ci->callstatus &= ~CIST_HOOKYIELD; /* erase mark */ + return; /* do not call hook again (VM yielded, so it did not move) */ } + if (counthook) + luaD_hook(L, LUA_HOOKCOUNT, -1); /* call count hook */ if (mask & LUA_MASKLINE) { - Proto *p = ci_func(L->ci)->l.p; - int npc = pcRel(pc, p); - int newline = getline(p, npc); - /* call linehook when enter a new function, when jump back (loop), - or when enter a new line */ - if (npc == 0 || pc <= oldpc || newline != getline(p, pcRel(oldpc, p))) - luaD_callhook(L, LUA_HOOKLINE, newline); + Proto *p = ci_func(ci)->p; + int npc = pcRel(ci->u.l.savedpc, p); + int newline = getfuncline(p, npc); + if (npc == 0 || /* call linehook when enter a new function, */ + ci->u.l.savedpc <= L->oldpc || /* when jump back (loop), or when */ + newline != getfuncline(p, pcRel(L->oldpc, p))) /* enter a new line */ + luaD_hook(L, LUA_HOOKLINE, newline); /* call line hook */ + } + L->oldpc = ci->u.l.savedpc; + if (L->status == LUA_YIELD) { /* did hook yield? */ + if (counthook) + L->hookcount = 1; /* undo decrement to zero */ + ci->u.l.savedpc--; /* undo increment (resume will increment it again) */ + ci->callstatus |= CIST_HOOKYIELD; /* mark that it yielded */ + ci->func = L->top - 1; /* protect stack below results */ + luaD_throw(L, LUA_YIELD); } } -static void callTMres (lua_State *L, StkId res, const TValue *f, - const TValue *p1, const TValue *p2) { - ptrdiff_t result = savestack(L, res); - setobj2s(L, L->top, f); /* push function */ - setobj2s(L, L->top+1, p1); /* 1st argument */ - setobj2s(L, L->top+2, p2); /* 2nd argument */ - luaD_checkstack(L, 3); - L->top += 3; - luaD_call(L, L->top - 3, 1); - res = restorestack(L, result); - L->top--; - setobjs2s(L, res, L->top); -} - - - static void callTM (lua_State *L, const TValue *f, const TValue *p1, - const TValue *p2, const TValue *p3) { - setobj2s(L, L->top, f); /* push function */ - setobj2s(L, L->top+1, p1); /* 1st argument */ - setobj2s(L, L->top+2, p2); /* 2nd argument */ - setobj2s(L, L->top+3, p3); /* 3th argument */ - luaD_checkstack(L, 4); - L->top += 4; - luaD_call(L, L->top - 4, 0); + const TValue *p2, TValue *p3, int hasres) { + ptrdiff_t result = savestack(L, p3); + setobj2s(L, L->top++, f); /* push function */ + setobj2s(L, L->top++, p1); /* 1st argument */ + setobj2s(L, L->top++, p2); /* 2nd argument */ + if (!hasres) /* no result? 'p3' is third argument */ + setobj2s(L, L->top++, p3); /* 3rd argument */ + /* metamethod may yield only when called from Lua code */ + luaD_call(L, L->top - (4 - hasres), hasres, isLua(L->ci)); + if (hasres) { /* if has result, move it to its place */ + p3 = restorestack(L, result); + setobjs2s(L, p3, --L->top); + } } @@ -112,7 +114,7 @@ void luaV_gettable (lua_State *L, const TValue *t, TValue *key, StkId val) { 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? */ + if (!ttisnil(res) || /* result is not nil? */ (tm = fasttm(L, h->metatable, TM_INDEX)) == NULL) { /* or no TM? */ setobj2s(L, val, res); return; @@ -122,10 +124,10 @@ void luaV_gettable (lua_State *L, const TValue *t, TValue *key, StkId val) { else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX))) luaG_typeerror(L, t, "index"); if (ttisfunction(tm)) { - callTMres(L, val, tm, t, key); + callTM(L, tm, t, key, val, 1); return; } - t = tm; /* else repeat with `tm' */ + t = tm; /* else repeat with 'tm' */ } luaG_runerror(L, "loop in gettable"); } @@ -137,22 +139,34 @@ void luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val) { const TValue *tm; if (ttistable(t)) { /* `t' is a table? */ Table *h = hvalue(t); - TValue *oldval = luaH_set(L, h, key); /* do a primitive set */ - if (!ttisnil(oldval) || /* result is no nil? */ - (tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL) { /* or no TM? */ - setobj2t(L, oldval, val); - luaC_barriert(L, h, val); + TValue *oldval = cast(TValue *, luaH_get(h, key)); + /* if previous value is not nil, there must be a previous entry + in the table; moreover, a metamethod has no relevance */ + if (!ttisnil(oldval) || + /* previous value is nil; must check the metamethod */ + ((tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL && + /* no metamethod; is there a previous entry in the table? */ + (oldval != luaO_nilobject || + /* no previous entry; must create one. (The next test is + always true; we only need the assignment.) */ + (oldval = luaH_newkey(L, h, key), 1)))) { + /* no metamethod and (now) there is an entry with given key */ + setobj2t(L, oldval, val); /* assign new value to that entry */ + invalidateTMcache(h); + luaC_barrierback(L, obj2gco(h), val); return; } - /* else will try the tag method */ + /* else will try the metamethod */ } - else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX))) - luaG_typeerror(L, t, "index"); + else /* not a table; check metamethod */ + if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX))) + luaG_typeerror(L, t, "index"); + /* there is a metamethod */ if (ttisfunction(tm)) { - callTM(L, tm, t, key, val); + callTM(L, tm, t, key, val, 0); return; } - t = tm; /* else repeat with `tm' */ + t = tm; /* else repeat with 'tm' */ } luaG_runerror(L, "loop in settable"); } @@ -164,12 +178,12 @@ static int call_binTM (lua_State *L, const TValue *p1, const TValue *p2, if (ttisnil(tm)) tm = luaT_gettmbyobj(L, p2, event); /* try second operand */ if (ttisnil(tm)) return 0; - callTMres(L, res, tm, p1, p2); + callTM(L, tm, p1, p2, res, 1); return 1; } -static const TValue *get_compTM (lua_State *L, Table *mt1, Table *mt2, +static const TValue *get_equalTM (lua_State *L, Table *mt1, Table *mt2, TMS event) { const TValue *tm1 = fasttm(L, mt1, event); const TValue *tm2; @@ -177,7 +191,7 @@ static const TValue *get_compTM (lua_State *L, Table *mt1, Table *mt2, if (mt1 == mt2) return tm1; /* same metatables => same metamethods */ tm2 = fasttm(L, mt2, event); if (tm2 == NULL) return NULL; /* no metamethod */ - if (luaO_rawequalObj(tm1, tm2)) /* same metamethods? */ + if (luaV_rawequalobj(tm1, tm2)) /* same metamethods? */ return tm1; return NULL; } @@ -185,14 +199,10 @@ static const TValue *get_compTM (lua_State *L, Table *mt1, Table *mt2, static int call_orderTM (lua_State *L, const TValue *p1, const TValue *p2, TMS event) { - const TValue *tm1 = luaT_gettmbyobj(L, p1, event); - const TValue *tm2; - if (ttisnil(tm1)) return -1; /* no metamethod? */ - tm2 = luaT_gettmbyobj(L, p2, event); - if (!luaO_rawequalObj(tm1, tm2)) /* different metamethods? */ - return -1; - callTMres(L, L->top, tm1, p1, p2); - return !l_isfalse(L->top); + if (!call_binTM(L, p1, p2, L->top, event)) + return -1; /* no metamethod */ + else + return !l_isfalse(L->top); } @@ -220,125 +230,261 @@ static int l_strcmp (const TString *ls, const TString *rs) { int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r) { int res; - if (ttype(l) != ttype(r)) - return luaG_ordererror(L, l, r); - else if (ttisnumber(l)) - return luai_numlt(nvalue(l), nvalue(r)); - else if (ttisstring(l)) + if (ttisnumber(l) && ttisnumber(r)) + return luai_numlt(L, nvalue(l), nvalue(r)); + else if (ttisstring(l) && ttisstring(r)) return l_strcmp(rawtsvalue(l), rawtsvalue(r)) < 0; - else if ((res = call_orderTM(L, l, r, TM_LT)) != -1) - return res; - return luaG_ordererror(L, l, r); + else if ((res = call_orderTM(L, l, r, TM_LT)) < 0) + luaG_ordererror(L, l, r); + return res; } -static int lessequal (lua_State *L, const TValue *l, const TValue *r) { +int luaV_lessequal (lua_State *L, const TValue *l, const TValue *r) { int res; - if (ttype(l) != ttype(r)) - return luaG_ordererror(L, l, r); - else if (ttisnumber(l)) - return luai_numle(nvalue(l), nvalue(r)); - else if (ttisstring(l)) + if (ttisnumber(l) && ttisnumber(r)) + return luai_numle(L, nvalue(l), nvalue(r)); + else if (ttisstring(l) && ttisstring(r)) return l_strcmp(rawtsvalue(l), rawtsvalue(r)) <= 0; - else if ((res = call_orderTM(L, l, r, TM_LE)) != -1) /* first try `le' */ + else if ((res = call_orderTM(L, l, r, TM_LE)) >= 0) /* first try `le' */ return res; - else if ((res = call_orderTM(L, r, l, TM_LT)) != -1) /* else try `lt' */ - return !res; - return luaG_ordererror(L, l, r); + else if ((res = call_orderTM(L, r, l, TM_LT)) < 0) /* else try `lt' */ + luaG_ordererror(L, l, r); + return !res; } -int luaV_equalval (lua_State *L, const TValue *t1, const TValue *t2) { +/* +** equality of Lua values. L == NULL means raw equality (no metamethods) +*/ +int luaV_equalobj_ (lua_State *L, const TValue *t1, const TValue *t2) { const TValue *tm; - lua_assert(ttype(t1) == ttype(t2)); + lua_assert(ttisequal(t1, t2)); switch (ttype(t1)) { case LUA_TNIL: return 1; case LUA_TNUMBER: return luai_numeq(nvalue(t1), nvalue(t2)); case LUA_TBOOLEAN: return bvalue(t1) == bvalue(t2); /* true must be 1 !! */ case LUA_TLIGHTUSERDATA: return pvalue(t1) == pvalue(t2); + case LUA_TLCF: return fvalue(t1) == fvalue(t2); + case LUA_TSHRSTR: return eqshrstr(rawtsvalue(t1), rawtsvalue(t2)); + case LUA_TLNGSTR: return luaS_eqlngstr(rawtsvalue(t1), rawtsvalue(t2)); case LUA_TUSERDATA: { if (uvalue(t1) == uvalue(t2)) return 1; - tm = get_compTM(L, uvalue(t1)->metatable, uvalue(t2)->metatable, - TM_EQ); + else if (L == NULL) return 0; + tm = get_equalTM(L, uvalue(t1)->metatable, uvalue(t2)->metatable, TM_EQ); break; /* will try TM */ } case LUA_TTABLE: { if (hvalue(t1) == hvalue(t2)) return 1; - tm = get_compTM(L, hvalue(t1)->metatable, hvalue(t2)->metatable, TM_EQ); + else if (L == NULL) return 0; + tm = get_equalTM(L, hvalue(t1)->metatable, hvalue(t2)->metatable, TM_EQ); break; /* will try TM */ } - default: return gcvalue(t1) == gcvalue(t2); + default: + lua_assert(iscollectable(t1)); + return gcvalue(t1) == gcvalue(t2); } if (tm == NULL) return 0; /* no TM? */ - callTMres(L, L->top, tm, t1, t2); /* call TM */ + callTM(L, tm, t1, t2, L->top, 1); /* call TM */ return !l_isfalse(L->top); } -void luaV_concat (lua_State *L, int total, int last) { +void luaV_concat (lua_State *L, int total) { + lua_assert(total >= 2); do { - StkId top = L->base + last + 1; + StkId top = L->top; int n = 2; /* number of elements handled in this pass (at least 2) */ if (!(ttisstring(top-2) || ttisnumber(top-2)) || !tostring(L, top-1)) { if (!call_binTM(L, top-2, top-1, top-2, TM_CONCAT)) luaG_concaterror(L, top-2, top-1); - } else if (tsvalue(top-1)->len == 0) /* second op is empty? */ - (void)tostring(L, top - 2); /* result is first op (as string) */ + } + else if (tsvalue(top-1)->len == 0) /* second operand is empty? */ + (void)tostring(L, top - 2); /* result is first operand */ + else if (ttisstring(top-2) && tsvalue(top-2)->len == 0) { + setobjs2s(L, top - 2, top - 1); /* result is second op. */ + } else { - /* at least two string values; get as many as possible */ + /* at least two non-empty string values; get as many as possible */ size_t tl = tsvalue(top-1)->len; char *buffer; int i; /* collect total length */ - for (n = 1; n < total && tostring(L, top-n-1); n++) { - size_t l = tsvalue(top-n-1)->len; - if (l >= MAX_SIZET - tl) luaG_runerror(L, "string length overflow"); + for (i = 1; i < total && tostring(L, top-i-1); i++) { + size_t l = tsvalue(top-i-1)->len; + if (l >= (MAX_SIZET/sizeof(char)) - tl) + luaG_runerror(L, "string length overflow"); tl += l; } buffer = luaZ_openspace(L, &G(L)->buff, tl); tl = 0; - for (i=n; i>0; i--) { /* concat all strings */ + n = i; + do { /* concat all strings */ size_t l = tsvalue(top-i)->len; - memcpy(buffer+tl, svalue(top-i), l); + memcpy(buffer+tl, svalue(top-i), l * sizeof(char)); tl += l; - } + } while (--i > 0); setsvalue2s(L, top-n, luaS_newlstr(L, buffer, tl)); } - total -= n-1; /* got `n' strings to create 1 new */ - last -= n-1; + total -= n-1; /* got 'n' strings to create 1 new */ + L->top -= n-1; /* popped 'n' strings and pushed one */ } while (total > 1); /* repeat until only 1 result left */ } -static void Arith (lua_State *L, StkId ra, const TValue *rb, - const TValue *rc, TMS op) { +void luaV_objlen (lua_State *L, StkId ra, const TValue *rb) { + const TValue *tm; + switch (ttypenv(rb)) { + case LUA_TTABLE: { + Table *h = hvalue(rb); + tm = fasttm(L, h->metatable, TM_LEN); + if (tm) break; /* metamethod? break switch to call it */ + setnvalue(ra, cast_num(luaH_getn(h))); /* else primitive len */ + return; + } + case LUA_TSTRING: { + setnvalue(ra, cast_num(tsvalue(rb)->len)); + return; + } + default: { /* try metamethod */ + tm = luaT_gettmbyobj(L, rb, TM_LEN); + if (ttisnil(tm)) /* no metamethod? */ + luaG_typeerror(L, rb, "get length of"); + break; + } + } + callTM(L, tm, rb, rb, ra, 1); +} + + +void luaV_arith (lua_State *L, StkId ra, const TValue *rb, + const TValue *rc, TMS op) { TValue tempb, tempc; const TValue *b, *c; if ((b = luaV_tonumber(rb, &tempb)) != NULL && (c = luaV_tonumber(rc, &tempc)) != NULL) { - lua_Number nb = nvalue(b), nc = nvalue(c); - 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; - } + lua_Number res = luaO_arith(op - TM_ADD + LUA_OPADD, nvalue(b), nvalue(c)); + setnvalue(ra, res); } else if (!call_binTM(L, rb, rc, ra, op)) luaG_aritherror(L, rb, rc); } +/* +** check whether cached closure in prototype 'p' may be reused, that is, +** whether there is a cached closure with the same upvalues needed by +** new closure to be created. +*/ +static Closure *getcached (Proto *p, UpVal **encup, StkId base) { + Closure *c = p->cache; + if (c != NULL) { /* is there a cached closure? */ + int nup = p->sizeupvalues; + Upvaldesc *uv = p->upvalues; + int i; + for (i = 0; i < nup; i++) { /* check whether it has right upvalues */ + TValue *v = uv[i].instack ? base + uv[i].idx : encup[uv[i].idx]->v; + if (c->l.upvals[i]->v != v) + return NULL; /* wrong upvalue; cannot reuse closure */ + } + } + return c; /* return cached closure (or NULL if no cached closure) */ +} + + +/* +** create a new Lua closure, push it in the stack, and initialize +** its upvalues. Note that the call to 'luaC_barrierproto' must come +** before the assignment to 'p->cache', as the function needs the +** original value of that field. +*/ +static void pushclosure (lua_State *L, Proto *p, UpVal **encup, StkId base, + StkId ra) { + int nup = p->sizeupvalues; + Upvaldesc *uv = p->upvalues; + int i; + Closure *ncl = luaF_newLclosure(L, nup); + ncl->l.p = p; + setclLvalue(L, ra, ncl); /* anchor new closure in stack */ + for (i = 0; i < nup; i++) { /* fill in its upvalues */ + if (uv[i].instack) /* upvalue refers to local variable? */ + ncl->l.upvals[i] = luaF_findupval(L, base + uv[i].idx); + else /* get upvalue from enclosing function */ + ncl->l.upvals[i] = encup[uv[i].idx]; + } + luaC_barrierproto(L, p, ncl); + p->cache = ncl; /* save it on cache for reuse */ +} + + +/* +** finish execution of an opcode interrupted by an yield +*/ +void luaV_finishOp (lua_State *L) { + CallInfo *ci = L->ci; + StkId base = ci->u.l.base; + Instruction inst = *(ci->u.l.savedpc - 1); /* interrupted instruction */ + OpCode op = GET_OPCODE(inst); + switch (op) { /* finish its execution */ + case OP_ADD: case OP_SUB: case OP_MUL: case OP_DIV: + case OP_MOD: case OP_POW: case OP_UNM: case OP_LEN: + case OP_GETTABUP: case OP_GETTABLE: case OP_SELF: { + setobjs2s(L, base + GETARG_A(inst), --L->top); + break; + } + case OP_LE: case OP_LT: case OP_EQ: { + int res = !l_isfalse(L->top - 1); + L->top--; + /* metamethod should not be called when operand is K */ + lua_assert(!ISK(GETARG_B(inst))); + if (op == OP_LE && /* "<=" using "<" instead? */ + ttisnil(luaT_gettmbyobj(L, base + GETARG_B(inst), TM_LE))) + res = !res; /* invert result */ + lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_JMP); + if (res != GETARG_A(inst)) /* condition failed? */ + ci->u.l.savedpc++; /* skip jump instruction */ + break; + } + case OP_CONCAT: { + StkId top = L->top - 1; /* top when 'call_binTM' was called */ + int b = GETARG_B(inst); /* first element to concatenate */ + int total = cast_int(top - 1 - (base + b)); /* yet to concatenate */ + setobj2s(L, top - 2, top); /* put TM result in proper position */ + if (total > 1) { /* are there elements to concat? */ + L->top = top - 1; /* top is one after last element (at top-2) */ + luaV_concat(L, total); /* concat them (may yield again) */ + } + /* move final result to final position */ + setobj2s(L, ci->u.l.base + GETARG_A(inst), L->top - 1); + L->top = ci->top; /* restore top */ + break; + } + case OP_TFORCALL: { + lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_TFORLOOP); + L->top = ci->top; /* correct top */ + break; + } + case OP_CALL: { + if (GETARG_C(inst) - 1 >= 0) /* nresults >= 0? */ + L->top = ci->top; /* adjust results */ + break; + } + case OP_TAILCALL: case OP_SETTABUP: case OP_SETTABLE: + break; + default: lua_assert(0); + } +} + + /* ** some macros for common tasks in `luaV_execute' */ -#define runtime_check(L, c) { if (!(c)) break; } +#if !defined luai_runtimecheck +#define luai_runtimecheck(L, c) /* void */ +#endif + #define RA(i) (base+GETARG_A(i)) /* to be used after possible stack reallocation */ @@ -348,13 +494,27 @@ static void Arith (lua_State *L, StkId ra, const TValue *rb, 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 KBx(i) \ + (k + (GETARG_Bx(i) != 0 ? GETARG_Bx(i) - 1 : GETARG_Ax(*ci->u.l.savedpc++))) + +/* execute a jump instruction */ +#define dojump(ci,i,e) \ + { int a = GETARG_A(i); \ + if (a > 0) luaF_close(L, ci->u.l.base + a - 1); \ + ci->u.l.savedpc += GETARG_sBx(i) + e; } -#define dojump(L,pc,i) {(pc) += (i); luai_threadyield(L);} +/* for test instructions, execute the jump instruction that follows it */ +#define donextjump(ci) { i = *ci->u.l.savedpc; dojump(ci, i, 1); } -#define Protect(x) { L->savedpc = pc; {x;}; base = L->base; } +#define Protect(x) { {x;}; base = ci->u.l.base; } + +#define checkGC(L,c) \ + Protect( luaC_condGC(L,{L->top = (c); /* limit of live values */ \ + luaC_step(L); \ + L->top = ci->top;}) /* restore top */ \ + luai_threadyield(L); ) #define arith_op(op,tm) { \ @@ -362,401 +522,345 @@ static void Arith (lua_State *L, StkId ra, const TValue *rb, TValue *rc = RKC(i); \ if (ttisnumber(rb) && ttisnumber(rc)) { \ lua_Number nb = nvalue(rb), nc = nvalue(rc); \ - setnvalue(ra, op(nb, nc)); \ + setnvalue(ra, op(L, nb, nc)); \ } \ - else \ - Protect(Arith(L, ra, rb, rc, tm)); \ - } + else { Protect(luaV_arith(L, ra, rb, rc, tm)); } } +#define vmdispatch(o) switch(o) +#define vmcase(l,b) case l: {b} break; +#define vmcasenb(l,b) case l: {b} /* nb = no break */ -void luaV_execute (lua_State *L, int nexeccalls) { +void luaV_execute (lua_State *L) { + CallInfo *ci = L->ci; 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; + StkId base; + newframe: /* reentry point when frame changes (call/return) */ + lua_assert(ci == L->ci); + cl = clLvalue(ci->func); k = cl->p->k; + base = ci->u.l.base; /* main loop of interpreter */ for (;;) { - const Instruction i = *pc++; + Instruction i = *(ci->u.l.savedpc++); 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; + Protect(traceexec(L)); } - /* warning!! several calls may realloc the stack and invalidate `ra' */ + /* 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: { + lua_assert(base == ci->u.l.base); + lua_assert(base <= L->top && L->top < L->stack + L->stacksize); + vmdispatch (GET_OPCODE(i)) { + vmcase(OP_MOVE, setobjs2s(L, ra, RB(i)); - continue; - } - case OP_LOADK: { - setobj2s(L, ra, KBx(i)); - continue; - } - case OP_LOADBOOL: { + ) + vmcase(OP_LOADK, + TValue *rb = k + GETARG_Bx(i); + setobj2s(L, ra, rb); + ) + vmcase(OP_LOADKX, + TValue *rb; + lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_EXTRAARG); + rb = k + GETARG_Ax(*ci->u.l.savedpc++); + setobj2s(L, ra, rb); + ) + vmcase(OP_LOADBOOL, setbvalue(ra, GETARG_B(i)); - if (GETARG_C(i)) pc++; /* skip next instruction (if C) */ - continue; - } - case OP_LOADNIL: { - TValue *rb = RB(i); + if (GETARG_C(i)) ci->u.l.savedpc++; /* skip next instruction (if C) */ + ) + vmcase(OP_LOADNIL, + int b = GETARG_B(i); do { - setnilvalue(rb--); - } while (rb >= ra); - continue; - } - case OP_GETUPVAL: { + setnilvalue(ra++); + } while (b--); + ) + vmcase(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: { + ) + vmcase(OP_GETTABUP, + int b = GETARG_B(i); + Protect(luaV_gettable(L, cl->upvals[b]->v, RKC(i), ra)); + ) + vmcase(OP_GETTABLE, Protect(luaV_gettable(L, RB(i), RKC(i), ra)); - 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: { + ) + vmcase(OP_SETTABUP, + int a = GETARG_A(i); + Protect(luaV_settable(L, cl->upvals[a]->v, RKB(i), RKC(i))); + ) + vmcase(OP_SETUPVAL, UpVal *uv = cl->upvals[GETARG_B(i)]; setobj(L, uv->v, ra); luaC_barrier(L, uv, ra); - continue; - } - case OP_SETTABLE: { + ) + vmcase(OP_SETTABLE, Protect(luaV_settable(L, ra, RKB(i), RKC(i))); - continue; - } - case OP_NEWTABLE: { + ) + vmcase(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: { + Table *t = luaH_new(L); + sethvalue(L, ra, t); + if (b != 0 || c != 0) + luaH_resize(L, t, luaO_fb2int(b), luaO_fb2int(c)); + checkGC(L, ra + 1); + ) + vmcase(OP_SELF, StkId rb = RB(i); setobjs2s(L, ra+1, rb); Protect(luaV_gettable(L, rb, RKC(i), ra)); - continue; - } - case OP_ADD: { + ) + vmcase(OP_ADD, arith_op(luai_numadd, TM_ADD); - continue; - } - case OP_SUB: { + ) + vmcase(OP_SUB, arith_op(luai_numsub, TM_SUB); - continue; - } - case OP_MUL: { + ) + vmcase(OP_MUL, arith_op(luai_nummul, TM_MUL); - continue; - } - case OP_DIV: { + ) + vmcase(OP_DIV, arith_op(luai_numdiv, TM_DIV); - continue; - } - case OP_MOD: { + ) + vmcase(OP_MOD, arith_op(luai_nummod, TM_MOD); - continue; - } - case OP_POW: { + ) + vmcase(OP_POW, arith_op(luai_numpow, TM_POW); - continue; - } - case OP_UNM: { + ) + vmcase(OP_UNM, TValue *rb = RB(i); if (ttisnumber(rb)) { lua_Number nb = nvalue(rb); - setnvalue(ra, luai_numunm(nb)); + setnvalue(ra, luai_numunm(L, nb)); } else { - Protect(Arith(L, ra, rb, rb, TM_UNM)); + 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 */ + ) + vmcase(OP_NOT, + TValue *rb = RB(i); + int res = l_isfalse(rb); /* 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: { + ) + vmcase(OP_LEN, + Protect(luaV_objlen(L, ra, RB(i))); + ) + vmcase(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: { + StkId rb; + L->top = base + c + 1; /* mark the end of concat operands */ + Protect(luaV_concat(L, c - b + 1)); + ra = RA(i); /* 'luav_concat' may invoke TMs and move the stack */ + rb = b + base; + setobjs2s(L, ra, rb); + checkGC(L, (ra >= rb ? ra + 1 : rb)); + L->top = ci->top; /* restore top */ + ) + vmcase(OP_JMP, + dojump(ci, i, 0); + ) + vmcase(OP_EQ, TValue *rb = RKB(i); TValue *rc = RKC(i); Protect( - if (equalobj(L, rb, rc) == GETARG_A(i)) - dojump(L, pc, GETARG_sBx(*pc)); + if (cast_int(equalobj(L, rb, rc)) != GETARG_A(i)) + ci->u.l.savedpc++; + else + donextjump(ci); ) - pc++; - continue; - } - case OP_LT: { + ) + vmcase(OP_LT, Protect( - if (luaV_lessthan(L, RKB(i), RKC(i)) == GETARG_A(i)) - dojump(L, pc, GETARG_sBx(*pc)); + if (luaV_lessthan(L, RKB(i), RKC(i)) != GETARG_A(i)) + ci->u.l.savedpc++; + else + donextjump(ci); ) - pc++; - continue; - } - case OP_LE: { + ) + vmcase(OP_LE, Protect( - if (lessequal(L, RKB(i), RKC(i)) == GETARG_A(i)) - dojump(L, pc, GETARG_sBx(*pc)); + if (luaV_lessequal(L, RKB(i), RKC(i)) != GETARG_A(i)) + ci->u.l.savedpc++; + else + donextjump(ci); ) - pc++; - continue; - } - case OP_TEST: { - if (l_isfalse(ra) != GETARG_C(i)) - dojump(L, pc, GETARG_sBx(*pc)); - pc++; - continue; - } - case OP_TESTSET: { + ) + vmcase(OP_TEST, + if (GETARG_C(i) ? l_isfalse(ra) : !l_isfalse(ra)) + ci->u.l.savedpc++; + else + donextjump(ci); + ) + vmcase(OP_TESTSET, TValue *rb = RB(i); - if (l_isfalse(rb) != GETARG_C(i)) { + if (GETARG_C(i) ? l_isfalse(rb) : !l_isfalse(rb)) + ci->u.l.savedpc++; + else { setobjs2s(L, ra, rb); - dojump(L, pc, GETARG_sBx(*pc)); + donextjump(ci); } - pc++; - continue; - } - case OP_CALL: { + ) + vmcase(OP_CALL, int b = GETARG_B(i); int nresults = GETARG_C(i) - 1; if (b != 0) L->top = ra+b; /* else previous instruction set top */ - L->savedpc = pc; - switch (luaD_precall(L, ra, nresults)) { - case PCRLUA: { - nexeccalls++; - goto reentry; /* restart luaV_execute over new Lua function */ - } - case PCRC: { - /* it was a C function (`precall' called it); adjust results */ - if (nresults >= 0) L->top = L->ci->top; - base = L->base; - continue; - } - default: { - return; /* yield */ - } + if (luaD_precall(L, ra, nresults)) { /* C function? */ + if (nresults >= 0) L->top = ci->top; /* adjust results */ + base = ci->u.l.base; } - } - case OP_TAILCALL: { + else { /* Lua function */ + ci = L->ci; + ci->callstatus |= CIST_REENTRY; + goto newframe; /* restart luaV_execute over new Lua function */ + } + ) + vmcase(OP_TAILCALL, int b = GETARG_B(i); if (b != 0) L->top = ra+b; /* else previous instruction set top */ - L->savedpc = pc; lua_assert(GETARG_C(i) - 1 == LUA_MULTRET); - switch (luaD_precall(L, ra, LUA_MULTRET)) { - case PCRLUA: { - /* tail call: put new frame in place of previous one */ - CallInfo *ci = L->ci - 1; /* previous frame */ - int aux; - StkId func = ci->func; - StkId pfunc = (ci+1)->func; /* previous function index */ - if (L->openupval) luaF_close(L, ci->base); - L->base = ci->base = ci->func + ((ci+1)->base - pfunc); - for (aux = 0; pfunc+aux < L->top; aux++) /* move frame down */ - setobjs2s(L, func+aux, pfunc+aux); - ci->top = L->top = func+aux; /* correct top */ - lua_assert(L->top == L->base + clvalue(func)->l.p->maxstacksize); - ci->savedpc = L->savedpc; - ci->tailcalls++; /* one more call lost */ - L->ci--; /* remove new frame */ - goto reentry; - } - case PCRC: { /* it was a C function (`precall' called it) */ - base = L->base; - continue; - } - default: { - return; /* yield */ - } + if (luaD_precall(L, ra, LUA_MULTRET)) /* C function? */ + base = ci->u.l.base; + else { + /* tail call: put called frame (n) in place of caller one (o) */ + CallInfo *nci = L->ci; /* called frame */ + CallInfo *oci = nci->previous; /* caller frame */ + StkId nfunc = nci->func; /* called function */ + StkId ofunc = oci->func; /* caller function */ + /* last stack slot filled by 'precall' */ + StkId lim = nci->u.l.base + getproto(nfunc)->numparams; + int aux; + /* close all upvalues from previous call */ + if (cl->p->sizep > 0) luaF_close(L, oci->u.l.base); + /* move new frame into old one */ + for (aux = 0; nfunc + aux < lim; aux++) + setobjs2s(L, ofunc + aux, nfunc + aux); + oci->u.l.base = ofunc + (nci->u.l.base - nfunc); /* correct base */ + oci->top = L->top = ofunc + (L->top - nfunc); /* correct top */ + oci->u.l.savedpc = nci->u.l.savedpc; + oci->callstatus |= CIST_TAIL; /* function was tail called */ + ci = L->ci = oci; /* remove new frame */ + lua_assert(L->top == oci->u.l.base + getproto(ofunc)->maxstacksize); + goto newframe; /* restart luaV_execute over new Lua function */ } - } - case OP_RETURN: { + ) + vmcasenb(OP_RETURN, int b = GETARG_B(i); if (b != 0) L->top = ra+b-1; - if (L->openupval) luaF_close(L, base); - L->savedpc = pc; + if (cl->p->sizep > 0) luaF_close(L, base); b = luaD_poscall(L, ra); - if (--nexeccalls == 0) /* was previous function running `here'? */ - return; /* no: return */ - else { /* yes: continue its execution */ - if (b) L->top = L->ci->top; - lua_assert(isLua(L->ci)); - lua_assert(GET_OPCODE(*((L->ci)->savedpc - 1)) == OP_CALL); - goto reentry; + if (!(ci->callstatus & CIST_REENTRY)) /* 'ci' still the called one */ + return; /* external invocation: return */ + else { /* invocation via reentry: continue execution */ + ci = L->ci; + if (b) L->top = ci->top; + lua_assert(isLua(ci)); + lua_assert(GET_OPCODE(*((ci)->u.l.savedpc - 1)) == OP_CALL); + goto newframe; /* restart luaV_execute over new Lua function */ } - } - case OP_FORLOOP: { + ) + vmcase(OP_FORLOOP, lua_Number step = nvalue(ra+2); - lua_Number idx = luai_numadd(nvalue(ra), step); /* increment index */ + lua_Number idx = luai_numadd(L, nvalue(ra), step); /* increment index */ lua_Number limit = nvalue(ra+1); - if (luai_numlt(0, step) ? luai_numle(idx, limit) - : luai_numle(limit, idx)) { - dojump(L, pc, GETARG_sBx(i)); /* jump back */ + if (luai_numlt(L, 0, step) ? luai_numle(L, idx, limit) + : luai_numle(L, limit, idx)) { + ci->u.l.savedpc += GETARG_sBx(i); /* jump back */ setnvalue(ra, idx); /* update internal index... */ setnvalue(ra+3, idx); /* ...and external index */ } - continue; - } - case OP_FORPREP: { + ) + vmcase(OP_FORPREP, const TValue *init = ra; const TValue *plimit = ra+1; const TValue *pstep = ra+2; - L->savedpc = pc; /* next steps may throw errors */ if (!tonumber(init, ra)) luaG_runerror(L, LUA_QL("for") " initial value must be a number"); else if (!tonumber(plimit, ra+1)) luaG_runerror(L, LUA_QL("for") " limit must be a number"); else if (!tonumber(pstep, ra+2)) luaG_runerror(L, LUA_QL("for") " step must be a number"); - setnvalue(ra, luai_numsub(nvalue(ra), nvalue(pstep))); - dojump(L, pc, GETARG_sBx(i)); - continue; - } - case OP_TFORLOOP: { + setnvalue(ra, luai_numsub(L, nvalue(ra), nvalue(pstep))); + ci->u.l.savedpc += GETARG_sBx(i); + ) + vmcasenb(OP_TFORCALL, StkId cb = ra + 3; /* call base */ setobjs2s(L, cb+2, ra+2); setobjs2s(L, cb+1, ra+1); setobjs2s(L, cb, ra); - L->top = cb+3; /* func. + 2 args (state and index) */ - Protect(luaD_call(L, cb, GETARG_C(i))); - L->top = L->ci->top; - cb = RA(i) + 3; /* previous call may change the stack */ - if (!ttisnil(cb)) { /* continue loop? */ - setobjs2s(L, cb-1, cb); /* save control variable */ - dojump(L, pc, GETARG_sBx(*pc)); /* jump back */ + L->top = cb + 3; /* func. + 2 args (state and index) */ + Protect(luaD_call(L, cb, GETARG_C(i), 1)); + L->top = ci->top; + i = *(ci->u.l.savedpc++); /* go to next instruction */ + ra = RA(i); + lua_assert(GET_OPCODE(i) == OP_TFORLOOP); + goto l_tforloop; + ) + vmcase(OP_TFORLOOP, + l_tforloop: + if (!ttisnil(ra + 1)) { /* continue loop? */ + setobjs2s(L, ra, ra + 1); /* save control variable */ + ci->u.l.savedpc += GETARG_sBx(i); /* jump back */ } - pc++; - continue; - } - case OP_SETLIST: { + ) + vmcase(OP_SETLIST, int n = GETARG_B(i); int c = GETARG_C(i); int last; Table *h; - if (n == 0) { - n = cast_int(L->top - ra) - 1; - L->top = L->ci->top; + if (n == 0) n = cast_int(L->top - ra) - 1; + if (c == 0) { + lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_EXTRAARG); + c = GETARG_Ax(*ci->u.l.savedpc++); } - if (c == 0) c = cast_int(*pc++); - runtime_check(L, ttistable(ra)); + luai_runtimecheck(L, ttistable(ra)); h = hvalue(ra); last = ((c-1)*LFIELDS_PER_FLUSH) + n; if (last > h->sizearray) /* needs more space? */ - luaH_resizearray(L, h, last); /* pre-alloc it at once */ + luaH_resizearray(L, h, last); /* pre-allocate it at once */ for (; n > 0; n--) { TValue *val = ra+n; - setobj2t(L, luaH_setnum(L, h, last--), val); - luaC_barriert(L, h, val); + luaH_setint(L, h, last--, val); + luaC_barrierback(L, obj2gco(h), val); } - continue; - } - case OP_CLOSE: { - luaF_close(L, ra); - continue; - } - 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->env); - 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(L, ra, ncl); - Protect(luaC_checkGC(L)); - continue; - } - case OP_VARARG: { + L->top = ci->top; /* correct top (in case of previous open call) */ + ) + vmcase(OP_CLOSURE, + Proto *p = cl->p->p[GETARG_Bx(i)]; + Closure *ncl = getcached(p, cl->upvals, base); /* cached closure */ + if (ncl == NULL) /* no match? */ + pushclosure(L, p, cl->upvals, base, ra); /* create a new one */ + else + setclLvalue(L, ra, ncl); /* push cashed closure */ + checkGC(L, ra + 1); + ) + vmcase(OP_VARARG, int b = GETARG_B(i) - 1; int j; - CallInfo *ci = L->ci; - int n = cast_int(ci->base - ci->func) - cl->p->numparams - 1; - if (b == LUA_MULTRET) { + int n = cast_int(base - ci->func) - cl->p->numparams - 1; + if (b < 0) { /* B == 0? */ + b = n; /* get all var. arguments */ Protect(luaD_checkstack(L, n)); ra = RA(i); /* previous call may change the stack */ - b = n; L->top = ra + n; } for (j = 0; j < b; j++) { if (j < n) { - setobjs2s(L, ra + j, ci->base - n + j); + setobjs2s(L, ra + j, base - n + j); } else { setnilvalue(ra + j); } } - continue; - } + ) + vmcase(OP_EXTRAARG, + lua_assert(0); + ) } } } |