/* ** $Id: lvm.c,v 1.61 1999/09/06 13:55:09 roberto Exp roberto $ ** Lua virtual machine ** See Copyright Notice in lua.h */ #include #include #include #include "lauxlib.h" #include "ldo.h" #include "lfunc.h" #include "lgc.h" #include "lobject.h" #include "lopcodes.h" #include "lstate.h" #include "lstring.h" #include "ltable.h" #include "ltm.h" #include "lvm.h" #ifdef OLD_ANSI #define strcoll(a,b) strcmp(a,b) #endif #define highbyte(x) ((x)<<8) /* Extra stack size to run a function: LUA_T_LINE(1), TM calls(2), ... */ #define EXTRA_STACK 5 static TaggedString *strconc (TaggedString *l, TaggedString *r) { long nl = l->u.s.len; long nr = r->u.s.len; char *buffer = luaL_openspace(nl+nr); memcpy(buffer, l->str, nl); memcpy(buffer+nl, r->str, nr); return luaS_newlstr(buffer, nl+nr); } int luaV_tonumber (TObject *obj) { /* LUA_NUMBER */ if (ttype(obj) != LUA_T_STRING) return 1; else { if (!luaO_str2d(svalue(obj), &nvalue(obj))) return 2; ttype(obj) = LUA_T_NUMBER; return 0; } } int luaV_tostring (TObject *obj) { /* LUA_NUMBER */ if (ttype(obj) != LUA_T_NUMBER) return 1; else { char s[32]; /* 16 digits, signal, point and \0 (+ some extra...) */ sprintf(s, "%.16g", (double)nvalue(obj)); tsvalue(obj) = luaS_new(s); ttype(obj) = LUA_T_STRING; return 0; } } void luaV_setn (Hash *t, int val) { TObject index, value; ttype(&index) = LUA_T_STRING; tsvalue(&index) = luaS_new("n"); ttype(&value) = LUA_T_NUMBER; nvalue(&value) = val; luaH_set(t, &index, &value); } void luaV_closure (int nelems) { if (nelems > 0) { struct Stack *S = &L->stack; Closure *c = luaF_newclosure(nelems); c->consts[0] = *(S->top-1); memcpy(&c->consts[1], S->top-(nelems+1), nelems*sizeof(TObject)); S->top -= nelems; ttype(S->top-1) = LUA_T_CLOSURE; (S->top-1)->value.cl = c; } } /* ** Function to index a table. ** Receives the table at top-2 and the index at top-1. */ void luaV_gettable (void) { TObject *table = L->stack.top-2; const TObject *im; if (ttype(table) != LUA_T_ARRAY) { /* not a table, get gettable method */ im = luaT_getimbyObj(table, IM_GETTABLE); if (ttype(im) == LUA_T_NIL) lua_error("indexed expression not a table"); } else { /* object is a table... */ int tg = table->value.a->htag; im = luaT_getim(tg, IM_GETTABLE); if (ttype(im) == LUA_T_NIL) { /* and does not have a "gettable" method */ TObject *h = luaH_get(avalue(table), table+1); if (ttype(h) == LUA_T_NIL && (ttype(im=luaT_getim(tg, IM_INDEX)) != LUA_T_NIL)) { /* result is nil and there is an "index" tag method */ luaD_callTM(im, 2, 1); /* calls it */ } else { L->stack.top--; *table = *h; /* "push" result into table position */ } return; } /* else it has a "gettable" method, go through to next command */ } /* object is not a table, or it has a "gettable" method */ luaD_callTM(im, 2, 1); } /* ** Receives table at *t, index at *(t+1) and value at top. */ void luaV_settable (const TObject *t) { struct Stack *S = &L->stack; const TObject *im; if (ttype(t) != LUA_T_ARRAY) { /* not a table, get "settable" method */ im = luaT_getimbyObj(t, IM_SETTABLE); if (ttype(im) == LUA_T_NIL) lua_error("indexed expression not a table"); } else { /* object is a table... */ im = luaT_getim(avalue(t)->htag, IM_SETTABLE); if (ttype(im) == LUA_T_NIL) { /* and does not have a "settable" method */ luaH_set(avalue(t), t+1, S->top-1); S->top--; /* pop value */ return; } /* else it has a "settable" method, go through to next command */ } /* object is not a table, or it has a "settable" method */ /* prepare arguments and call the tag method */ *(S->top+1) = *(L->stack.top-1); *(S->top) = *(t+1); *(S->top-1) = *t; S->top += 2; /* WARNING: caller must assure stack space */ luaD_callTM(im, 3, 0); } void luaV_rawsettable (const TObject *t) { if (ttype(t) != LUA_T_ARRAY) lua_error("indexed expression not a table"); else { struct Stack *S = &L->stack; luaH_set(avalue(t), t+1, S->top-1); S->top -= 3; } } void luaV_getglobal (TaggedString *ts) { /* WARNING: caller must assure stack space */ const TObject *value = &ts->u.s.globalval; switch (ttype(value)) { /* only userdata, tables and nil can have getglobal tag methods */ case LUA_T_USERDATA: case LUA_T_ARRAY: case LUA_T_NIL: { TObject *im = luaT_getimbyObj(value, IM_GETGLOBAL); if (ttype(im) != LUA_T_NIL) { /* is there a tag method? */ struct Stack *S = &L->stack; ttype(S->top) = LUA_T_STRING; tsvalue(S->top) = ts; S->top++; *S->top++ = *value; luaD_callTM(im, 2, 1); return; } /* else no tag method: go through to default behavior */ } default: *L->stack.top++ = *value; /* default behavior */ } } void luaV_setglobal (TaggedString *ts) { const TObject *oldvalue = &ts->u.s.globalval; const TObject *im = luaT_getimbyObj(oldvalue, IM_SETGLOBAL); if (ttype(im) == LUA_T_NIL) /* is there a tag method? */ luaS_rawsetglobal(ts, --L->stack.top); else { /* WARNING: caller must assure stack space */ struct Stack *S = &L->stack; TObject newvalue; newvalue = *(S->top-1); ttype(S->top-1) = LUA_T_STRING; tsvalue(S->top-1) = ts; *S->top++ = *oldvalue; *S->top++ = newvalue; luaD_callTM(im, 3, 0); } } static void call_binTM (IMS event, const char *msg) { /* try first operand */ const TObject *im = luaT_getimbyObj(L->stack.top-2, event); if (ttype(im) == LUA_T_NIL) { im = luaT_getimbyObj(L->stack.top-1, event); /* try second operand */ if (ttype(im) == LUA_T_NIL) { im = luaT_getim(0, event); /* try a 'global' i.m. */ if (ttype(im) == LUA_T_NIL) lua_error(msg); } } lua_pushstring(luaT_eventname[event]); luaD_callTM(im, 3, 1); } static void call_arith (IMS event) { call_binTM(event, "unexpected type in arithmetic operation"); } static int luaV_strcomp (const char *l, long ll, const char *r, long lr) { for (;;) { long temp = strcoll(l, r); if (temp != 0) return temp; /* strings are equal up to a '\0' */ temp = strlen(l); /* index of first '\0' in both strings */ if (temp == ll) /* l is finished? */ return (temp == lr) ? 0 : -1; /* l is equal or smaller than r */ else if (temp == lr) /* r is finished? */ return 1; /* l is greater than r (because l is not finished) */ /* both strings longer than temp; go on comparing (after the '\0') */ temp++; l += temp; ll -= temp; r += temp; lr -= temp; } } void luaV_comparison (lua_Type ttype_less, lua_Type ttype_equal, lua_Type ttype_great, IMS op) { struct Stack *S = &L->stack; const TObject *l = S->top-2; const TObject *r = S->top-1; real result; if (ttype(l) == LUA_T_NUMBER && ttype(r) == LUA_T_NUMBER) result = nvalue(l)-nvalue(r); else if (ttype(l) == LUA_T_STRING && ttype(r) == LUA_T_STRING) result = luaV_strcomp(svalue(l), tsvalue(l)->u.s.len, svalue(r), tsvalue(r)->u.s.len); else { call_binTM(op, "unexpected type in comparison"); return; } S->top--; nvalue(S->top-1) = 1; ttype(S->top-1) = (result < 0) ? ttype_less : (result == 0) ? ttype_equal : ttype_great; } void luaV_pack (StkId firstel, int nvararg, TObject *tab) { TObject *firstelem = L->stack.stack+firstel; int i; Hash *htab; if (nvararg < 0) nvararg = 0; htab = avalue(tab) = luaH_new(nvararg+1); /* +1 for field 'n' */ ttype(tab) = LUA_T_ARRAY; for (i=0; istack.top-L->stack.stack)-first_extra_arg, &arg); luaD_adjusttop(first_extra_arg); *L->stack.top++ = arg; } /* ** Execute the given opcode, until a RET. Parameters are between ** [stack+base,top). Returns n such that the the results are between ** [stack+n,top). */ StkId luaV_execute (Closure *cl, TProtoFunc *tf, StkId base) { struct Stack *S = &L->stack; /* to optimize */ register const Byte *pc = tf->code; const TObject *consts = tf->consts; if (L->callhook) luaD_callHook(base, tf, 0); luaD_checkstack((*pc++)+EXTRA_STACK); if (*pc < ZEROVARARG) luaD_adjusttop(base+*(pc++)); else { /* varargs */ luaC_checkGC(); adjust_varargs(base+(*pc++)-ZEROVARARG); } for (;;) { register int aux = 0; switchentry: switch ((OpCode)*pc++) { case ENDCODE: S->top = S->stack + base; goto ret; case RETCODE: base += *pc++; goto ret; case CALL: aux = *pc++; luaD_calln(*pc++, aux); break; case TAILCALL: aux = *pc++; luaD_calln(*pc++, MULT_RET); base += aux; goto ret; case PUSHNIL: aux = *pc++; do { ttype(S->top++) = LUA_T_NIL; } while (aux--); break; case POP: aux = *pc++; S->top -= aux; break; case PUSHNUMBERW: aux += highbyte(*pc++); case PUSHNUMBER: aux += *pc++; ttype(S->top) = LUA_T_NUMBER; nvalue(S->top) = aux; S->top++; break; case PUSHNUMBERNEGW: aux += highbyte(*pc++); case PUSHNUMBERNEG: aux += *pc++; ttype(S->top) = LUA_T_NUMBER; nvalue(S->top) = -aux; S->top++; break; case PUSHCONSTANTW: aux += highbyte(*pc++); case PUSHCONSTANT: aux += *pc++; *S->top++ = consts[aux]; break; case PUSHUPVALUE: aux = *pc++; *S->top++ = cl->consts[aux+1]; break; case PUSHLOCAL: aux = *pc++; *S->top++ = *((S->stack+base) + aux); break; case GETGLOBALW: aux += highbyte(*pc++); case GETGLOBAL: aux += *pc++; luaV_getglobal(tsvalue(&consts[aux])); break; case GETTABLE: luaV_gettable(); break; case GETDOTTEDW: aux += highbyte(*pc++); case GETDOTTED: aux += *pc++; *S->top++ = consts[aux]; luaV_gettable(); break; case PUSHSELFW: aux += highbyte(*pc++); case PUSHSELF: aux += *pc++; { TObject receiver; receiver = *(S->top-1); *S->top++ = consts[aux]; luaV_gettable(); *S->top++ = receiver; break; } case CREATEARRAYW: aux += highbyte(*pc++); case CREATEARRAY: aux += *pc++; luaC_checkGC(); avalue(S->top) = luaH_new(aux); ttype(S->top) = LUA_T_ARRAY; S->top++; break; case SETLOCAL: aux = *pc++; *((S->stack+base) + aux) = *(--S->top); break; case SETGLOBALW: aux += highbyte(*pc++); case SETGLOBAL: aux += *pc++; luaV_setglobal(tsvalue(&consts[aux])); break; case SETTABLEPOP: luaV_settable(S->top-3); S->top -= 2; /* pop table and index */ break; case SETTABLE: luaV_settable(S->top-3-(*pc++)); break; case SETLISTW: aux += highbyte(*pc++); case SETLIST: aux += *pc++; { int n = *(pc++); Hash *arr = avalue(S->top-n-1); aux *= LFIELDS_PER_FLUSH; for (; n; n--) luaH_setint(arr, n+aux, --S->top); break; } case SETMAP: aux = *pc++; { Hash *arr = avalue(S->top-(2*aux)-3); do { luaH_set(arr, S->top-2, S->top-1); S->top-=2; } while (aux--); break; } case NEQOP: aux = 1; case EQOP: { int res = luaO_equalObj(S->top-2, S->top-1); if (aux) res = !res; S->top--; ttype(S->top-1) = res ? LUA_T_NUMBER : LUA_T_NIL; nvalue(S->top-1) = 1; break; } case LTOP: luaV_comparison(LUA_T_NUMBER, LUA_T_NIL, LUA_T_NIL, IM_LT); break; case LEOP: luaV_comparison(LUA_T_NUMBER, LUA_T_NUMBER, LUA_T_NIL, IM_LE); break; case GTOP: luaV_comparison(LUA_T_NIL, LUA_T_NIL, LUA_T_NUMBER, IM_GT); break; case GEOP: luaV_comparison(LUA_T_NIL, LUA_T_NUMBER, LUA_T_NUMBER, IM_GE); break; case ADDOP: { TObject *l = S->top-2; TObject *r = S->top-1; if (tonumber(r) || tonumber(l)) call_arith(IM_ADD); else { nvalue(l) += nvalue(r); --S->top; } break; } case SUBOP: { TObject *l = S->top-2; TObject *r = S->top-1; if (tonumber(r) || tonumber(l)) call_arith(IM_SUB); else { nvalue(l) -= nvalue(r); --S->top; } break; } case MULTOP: { TObject *l = S->top-2; TObject *r = S->top-1; if (tonumber(r) || tonumber(l)) call_arith(IM_MUL); else { nvalue(l) *= nvalue(r); --S->top; } break; } case DIVOP: { TObject *l = S->top-2; TObject *r = S->top-1; if (tonumber(r) || tonumber(l)) call_arith(IM_DIV); else { nvalue(l) /= nvalue(r); --S->top; } break; } case POWOP: call_binTM(IM_POW, "undefined operation"); break; case CONCOP: { TObject *l = S->top-2; TObject *r = S->top-1; if (tostring(l) || tostring(r)) call_binTM(IM_CONCAT, "unexpected type for concatenation"); else { tsvalue(l) = strconc(tsvalue(l), tsvalue(r)); --S->top; } luaC_checkGC(); break; } case MINUSOP: if (tonumber(S->top-1)) { ttype(S->top) = LUA_T_NIL; S->top++; call_arith(IM_UNM); } else nvalue(S->top-1) = - nvalue(S->top-1); break; case NOTOP: ttype(S->top-1) = (ttype(S->top-1) == LUA_T_NIL) ? LUA_T_NUMBER : LUA_T_NIL; nvalue(S->top-1) = 1; break; case ONTJMPW: aux += highbyte(*pc++); case ONTJMP: aux += *pc++; if (ttype(S->top-1) != LUA_T_NIL) pc += aux; else S->top--; break; case ONFJMPW: aux += highbyte(*pc++); case ONFJMP: aux += *pc++; if (ttype(S->top-1) == LUA_T_NIL) pc += aux; else S->top--; break; case JMPW: aux += highbyte(*pc++); case JMP: aux += *pc++; pc += aux; break; case IFFJMPW: aux += highbyte(*pc++); case IFFJMP: aux += *pc++; if (ttype(--S->top) == LUA_T_NIL) pc += aux; break; case IFTUPJMPW: aux += highbyte(*pc++); case IFTUPJMP: aux += *pc++; if (ttype(--S->top) != LUA_T_NIL) pc -= aux; break; case IFFUPJMPW: aux += highbyte(*pc++); case IFFUPJMP: aux += *pc++; if (ttype(--S->top) == LUA_T_NIL) pc -= aux; break; case CLOSUREW: aux += highbyte(*pc++); case CLOSURE: aux += *pc++; *S->top++ = consts[aux]; luaV_closure(*pc++); luaC_checkGC(); break; case SETLINEW: aux += highbyte(*pc++); case SETLINE: aux += *pc++; if ((S->stack+base-1)->ttype != LUA_T_LINE) { /* open space for LINE value */ luaD_openstack((S->top-S->stack)-base); base++; (S->stack+base-1)->ttype = LUA_T_LINE; } (S->stack+base-1)->value.i = aux; if (L->linehook) luaD_lineHook(aux); break; case LONGARGW: aux += highbyte(*pc++); case LONGARG: aux += *pc++; aux = highbyte(highbyte(aux)); goto switchentry; /* do not reset "aux" */ case CHECKSTACK: aux = *pc++; LUA_ASSERT((S->top-S->stack)-base == aux && S->last >= S->top, "wrong stack size"); break; } } ret: if (L->callhook) luaD_callHook(0, NULL, 1); return base; }