// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details // This code is based on Lua 5.x implementation licensed under MIT License; see lua_LICENSE.txt for details #include "lvm.h" #include "lstate.h" #include "lstring.h" #include "ltable.h" #include "lgc.h" #include "ldo.h" #include "lnumutils.h" #include // limit for table tag-method chains (to avoid loops) #define MAXTAGLOOP 100 const TValue* luaV_tonumber(const TValue* obj, TValue* n) { double num; if (ttisnumber(obj)) return obj; if (ttisstring(obj) && luaO_str2d(svalue(obj), &num)) { setnvalue(n, num); return n; } else return NULL; } int luaV_tostring(lua_State* L, StkId obj) { if (!ttisnumber(obj)) return 0; else { char s[LUAI_MAXNUM2STR]; double n = nvalue(obj); char* e = luai_num2str(s, n); LUAU_ASSERT(e < s + sizeof(s)); setsvalue(L, obj, luaS_newlstr(L, s, e - s)); return 1; } } const float* luaV_tovector(const TValue* obj) { if (ttisvector(obj)) return obj->value.v; return nullptr; } static StkId callTMres(lua_State* L, StkId res, const TValue* f, const TValue* p1, const TValue* p2) { ptrdiff_t result = savestack(L, res); // using stack room beyond top is technically safe here, but for very complicated reasons: // * The stack guarantees EXTRA_STACK room beyond stack_last (see luaD_reallocstack) will be allocated // * we cannot move luaD_checkstack above because the arguments are *sometimes* pointers to the lua // stack and checkstack may invalidate those pointers // * we cannot use savestack/restorestack because the arguments are sometimes on the C++ stack // * during stack reallocation all of the allocated stack is copied (even beyond stack_last) so these // values will be preserved even if they go past stack_last LUAU_ASSERT((L->top + 3) < (L->stack + L->stacksize)); 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--; setobj2s(L, res, L->top); return res; } static void callTM(lua_State* L, const TValue* f, const TValue* p1, const TValue* p2, const TValue* p3) { // using stack room beyond top is technically safe here, but for very complicated reasons: // * The stack guarantees EXTRA_STACK room beyond stack_last (see luaD_reallocstack) will be allocated // * we cannot move luaD_checkstack above because the arguments are *sometimes* pointers to the lua // stack and checkstack may invalidate those pointers // * we cannot use savestack/restorestack because the arguments are sometimes on the C++ stack // * during stack reallocation all of the allocated stack is copied (even beyond stack_last) so these // values will be preserved even if they go past stack_last LUAU_ASSERT((L->top + 4) < (L->stack + L->stacksize)); 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); } void luaV_gettable(lua_State* L, const TValue* t, TValue* key, StkId val) { int loop; for (loop = 0; loop < MAXTAGLOOP; loop++) { const TValue* tm; if (ttistable(t)) { // `t' is a table? Table* h = hvalue(t); const TValue* res = luaH_get(h, key); // do a primitive get if (res != luaO_nilobject) L->cachedslot = gval2slot(h, res); // remember slot to accelerate future lookups if (!ttisnil(res) // result is no nil? || (tm = fasttm(L, h->metatable, TM_INDEX)) == NULL) { // or no TM? setobj2s(L, val, res); return; } // t isn't a table, so see if it has an INDEX meta-method to look up the key with } else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX))) luaG_indexerror(L, t, key); if (ttisfunction(tm)) { callTMres(L, val, tm, t, key); return; } t = tm; // else repeat with `tm' } luaG_runerror(L, "'__index' chain too long; possible loop"); } void luaV_settable(lua_State* L, const TValue* t, TValue* key, StkId val) { int loop; TValue temp; for (loop = 0; loop < MAXTAGLOOP; loop++) { const TValue* tm; if (ttistable(t)) { // `t' is a table? Table* h = hvalue(t); const TValue* oldval = luaH_get(h, key); // should we assign the key? (if key is valid or __newindex is not set) if (!ttisnil(oldval) || (tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL) { if (h->readonly) luaG_readonlyerror(L); // luaH_set would work but would repeat the lookup so we use luaH_setslot that can reuse oldval if it's safe TValue* newval = luaH_setslot(L, h, oldval, key); L->cachedslot = gval2slot(h, newval); // remember slot to accelerate future lookups setobj2t(L, newval, val); luaC_barriert(L, h, val); return; } // fallthrough to metamethod } else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX))) luaG_indexerror(L, t, key); if (ttisfunction(tm)) { callTM(L, tm, t, key, val); return; } // else repeat with `tm' setobj(L, &temp, tm); // avoid pointing inside table (may rehash) t = &temp; } luaG_runerror(L, "'__newindex' chain too long; possible loop"); } static int call_binTM(lua_State* L, const TValue* p1, const TValue* p2, StkId res, TMS event) { const TValue* tm = luaT_gettmbyobj(L, p1, event); // try first operand if (ttisnil(tm)) tm = luaT_gettmbyobj(L, p2, event); // try second operand if (ttisnil(tm)) return 0; callTMres(L, res, tm, p1, p2); return 1; } static const TValue* get_compTM(lua_State* L, Table* mt1, Table* mt2, TMS event) { const TValue* tm1 = fasttm(L, mt1, event); const TValue* tm2; if (tm1 == NULL) return NULL; // no metamethod 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? return tm1; return NULL; } 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); } int luaV_strcmp(const TString* ls, const TString* rs) { if (ls == rs) return 0; const char* l = getstr(ls); const char* r = getstr(rs); // always safe to read one character because even empty strings are nul terminated if (*l != *r) return uint8_t(*l) - uint8_t(*r); size_t ll = ls->len; size_t lr = rs->len; size_t lmin = ll < lr ? ll : lr; int res = memcmp(l, r, lmin); if (res != 0) return res; return ll == lr ? 0 : ll < lr ? -1 : 1; } int luaV_lessthan(lua_State* L, const TValue* l, const TValue* r) { int res; if (ttype(l) != ttype(r)) luaG_ordererror(L, l, r, TM_LT); else if (ttisnumber(l)) return luai_numlt(nvalue(l), nvalue(r)); else if (ttisstring(l)) return luaV_strcmp(tsvalue(l), tsvalue(r)) < 0; else if ((res = call_orderTM(L, l, r, TM_LT)) == -1) luaG_ordererror(L, l, r, TM_LT); return res; } int luaV_lessequal(lua_State* L, const TValue* l, const TValue* r) { int res; if (ttype(l) != ttype(r)) luaG_ordererror(L, l, r, TM_LE); else if (ttisnumber(l)) return luai_numle(nvalue(l), nvalue(r)); else if (ttisstring(l)) return luaV_strcmp(tsvalue(l), tsvalue(r)) <= 0; else if ((res = call_orderTM(L, l, r, TM_LE)) != -1) // first try `le' return res; else if ((res = call_orderTM(L, r, l, TM_LT)) == -1) // error if not `lt' luaG_ordererror(L, l, r, TM_LE); return !res; } int luaV_equalval(lua_State* L, const TValue* t1, const TValue* t2) { const TValue* tm; LUAU_ASSERT(ttype(t1) == ttype(t2)); switch (ttype(t1)) { case LUA_TNIL: return 1; case LUA_TNUMBER: return luai_numeq(nvalue(t1), nvalue(t2)); case LUA_TVECTOR: return luai_veceq(vvalue(t1), vvalue(t2)); case LUA_TBOOLEAN: return bvalue(t1) == bvalue(t2); // true must be 1 !! case LUA_TLIGHTUSERDATA: return pvalue(t1) == pvalue(t2); case LUA_TUSERDATA: { tm = get_compTM(L, uvalue(t1)->metatable, uvalue(t2)->metatable, TM_EQ); if (!tm) return uvalue(t1) == uvalue(t2); break; // will try TM } case LUA_TTABLE: { tm = get_compTM(L, hvalue(t1)->metatable, hvalue(t2)->metatable, TM_EQ); if (!tm) return hvalue(t1) == hvalue(t2); break; // will try TM } default: return gcvalue(t1) == gcvalue(t2); } callTMres(L, L->top, tm, t1, t2); // call TM return !l_isfalse(L->top); } void luaV_concat(lua_State* L, int total, int last) { do { StkId top = L->base + last + 1; 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 { // at least two 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 > MAXSSIZE - tl) luaG_runerror(L, "string length overflow"); tl += l; } char buf[LUA_BUFFERSIZE]; TString* ts = nullptr; if (tl < LUA_BUFFERSIZE) { buffer = buf; } else { ts = luaS_bufstart(L, tl); buffer = ts->data; } tl = 0; for (i = n; i > 0; i--) { // concat all strings size_t l = tsvalue(top - i)->len; memcpy(buffer + tl, svalue(top - i), l); tl += l; } if (tl < LUA_BUFFERSIZE) { setsvalue(L, top - n, luaS_newlstr(L, buffer, tl)); } else { setsvalue(L, top - n, luaS_buffinish(L, ts)); } } total -= n - 1; // got `n' strings to create 1 new last -= n - 1; } while (total > 1); // repeat until only 1 result left } void luaV_doarith(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) { double 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: LUAU_ASSERT(0); break; } } else { // vector operations that we support: v + v, v - v, v * v, s * v, v * s, v / v, s / v, v / s, -v const float* vb = luaV_tovector(rb); const float* vc = luaV_tovector(rc); if (vb && vc) { switch (op) { case TM_ADD: setvvalue(ra, vb[0] + vc[0], vb[1] + vc[1], vb[2] + vc[2], vb[3] + vc[3]); return; case TM_SUB: setvvalue(ra, vb[0] - vc[0], vb[1] - vc[1], vb[2] - vc[2], vb[3] - vc[3]); return; case TM_MUL: setvvalue(ra, vb[0] * vc[0], vb[1] * vc[1], vb[2] * vc[2], vb[3] * vc[3]); return; case TM_DIV: setvvalue(ra, vb[0] / vc[0], vb[1] / vc[1], vb[2] / vc[2], vb[3] / vc[3]); return; case TM_UNM: setvvalue(ra, -vb[0], -vb[1], -vb[2], -vb[3]); return; default: break; } } else if (vb) { c = luaV_tonumber(rc, &tempc); if (c) { float nc = cast_to(float, nvalue(c)); switch (op) { case TM_MUL: setvvalue(ra, vb[0] * nc, vb[1] * nc, vb[2] * nc, vb[3] * nc); return; case TM_DIV: setvvalue(ra, vb[0] / nc, vb[1] / nc, vb[2] / nc, vb[3] / nc); return; default: break; } } } else if (vc) { b = luaV_tonumber(rb, &tempb); if (b) { float nb = cast_to(float, nvalue(b)); switch (op) { case TM_MUL: setvvalue(ra, nb * vc[0], nb * vc[1], nb * vc[2], nb * vc[3]); return; case TM_DIV: setvvalue(ra, nb / vc[0], nb / vc[1], nb / vc[2], nb / vc[3]); return; default: break; } } } if (!call_binTM(L, rb, rc, ra, op)) { luaG_aritherror(L, rb, rc, op); } } } void luaV_dolen(lua_State* L, StkId ra, const TValue* rb) { const TValue* tm = NULL; switch (ttype(rb)) { case LUA_TTABLE: { Table* h = hvalue(rb); if ((tm = fasttm(L, h->metatable, TM_LEN)) == NULL) { setnvalue(ra, cast_num(luaH_getn(h))); return; } break; } case LUA_TSTRING: { TString* ts = tsvalue(rb); setnvalue(ra, cast_num(ts->len)); return; } default: tm = luaT_gettmbyobj(L, rb, TM_LEN); } if (ttisnil(tm)) luaG_typeerror(L, rb, "get length of"); StkId res = callTMres(L, ra, tm, rb, luaO_nilobject); if (!ttisnumber(res)) luaG_runerror(L, "'__len' must return a number"); // note, we can't access rb since stack may have been reallocated } LUAU_NOINLINE void luaV_prepareFORN(lua_State* L, StkId plimit, StkId pstep, StkId pinit) { if (!ttisnumber(pinit) && !luaV_tonumber(pinit, pinit)) luaG_forerror(L, pinit, "initial value"); if (!ttisnumber(plimit) && !luaV_tonumber(plimit, plimit)) luaG_forerror(L, plimit, "limit"); if (!ttisnumber(pstep) && !luaV_tonumber(pstep, pstep)) luaG_forerror(L, pstep, "step"); } // calls a C function f with no yielding support; optionally save one resulting value to the res register // the function and arguments have to already be pushed to L->top LUAU_NOINLINE void luaV_callTM(lua_State* L, int nparams, int res) { ++L->nCcalls; if (L->nCcalls >= LUAI_MAXCCALLS) luaD_checkCstack(L); luaD_checkstack(L, LUA_MINSTACK); StkId top = L->top; StkId fun = top - nparams - 1; CallInfo* ci = incr_ci(L); ci->func = fun; ci->base = fun + 1; ci->top = top + LUA_MINSTACK; ci->savedpc = NULL; ci->flags = 0; ci->nresults = (res >= 0); LUAU_ASSERT(ci->top <= L->stack_last); LUAU_ASSERT(ttisfunction(ci->func)); LUAU_ASSERT(clvalue(ci->func)->isC); L->base = fun + 1; LUAU_ASSERT(L->top == L->base + nparams); lua_CFunction func = clvalue(fun)->c.f; int n = func(L); LUAU_ASSERT(n >= 0); // yields should have been blocked by nCcalls // ci is our callinfo, cip is our parent // note that we read L->ci again since it may have been reallocated by the call CallInfo* cip = L->ci - 1; // copy return value into parent stack if (res >= 0) { if (n > 0) { setobj2s(L, &cip->base[res], L->top - n); } else { setnilvalue(&cip->base[res]); } } L->ci = cip; L->base = cip->base; L->top = cip->top; --L->nCcalls; } LUAU_NOINLINE void luaV_tryfuncTM(lua_State* L, StkId func) { const TValue* tm = luaT_gettmbyobj(L, func, TM_CALL); if (!ttisfunction(tm)) luaG_typeerror(L, func, "call"); for (StkId p = L->top; p > func; p--) // open space for metamethod setobj2s(L, p, p - 1); L->top++; // stack space pre-allocated by the caller setobj2s(L, func, tm); // tag method is the new function to be called }