#include "luastack.hpp" #include #include #include LuaSpecial LuaRegistry(LUA_REGISTRYINDEX); LuaNilMarker LuaNil; LuaNewTableMarker LuaNewTable; LuaFunctionReg::LuaFunctionReg(const char *n, const char *a, const char *d, bool s, lua_CFunction f) { name_ = n; args_ = a; docs_ = d; func_ = f; sandbox_ = s; next_ = All; All = this; } const LuaFunctionReg *LuaFunctionReg::lookup(lua_CFunction fn) { for (const LuaFunctionReg *r = All; r != 0; r = r->next_) { if (r->func_ == fn) { return r; } } return nullptr; } LuaFunctionReg *LuaFunctionReg::All; static int panicf(lua_State *L) { const char *p = lua_tostring(L, -1); fprintf(stderr, "PANIC: unprotected error in call to Lua API (%s)\n", p); fflush(stderr); exit(1); } // An allocator for lua states that uses system malloc and system free. static void *l_alloc(void *ud, void *ptr, size_t osize, size_t nsize) { if (nsize == 0) { free(ptr); return NULL; } else { return realloc(ptr, nsize); } } lua_State *LuaStack::newstate (lua_Alloc allocf) { if (allocf == nullptr) allocf = l_alloc; lua_State *L = lua_newstate(allocf, NULL); if (L) lua_atpanic(L, &panicf); return L; } bool LuaStack::isinteger(LuaSlot s) const { if (lua_type(L_, s) == LUA_TNUMBER) { lua_Number result = lua_tonumber(L_, s); if (lua_Integer(result) == result) return true; } return false; } bool LuaStack::isint(LuaSlot s) const { if (lua_type(L_, s) == LUA_TNUMBER) { lua_Number result = lua_tonumber(L_, s); if (int(result) == result) return true; } return false; } bool LuaStack::ckboolean(LuaSlot s) const { luaL_checktype(L_, s, LUA_TBOOLEAN); return lua_toboolean(L_, s) ? true:false; } lua_Integer LuaStack::ckinteger(LuaSlot s) const { luaL_checktype(L_, s, LUA_TNUMBER); lua_Number result = lua_tonumber(L_, s); lua_Integer iresult(result); if (iresult != result) { luaL_error(L_, "not a valid integer"); return 0; } return iresult; } int LuaStack::ckint(LuaSlot s) const { luaL_checktype(L_, s, LUA_TNUMBER); lua_Number result = lua_tonumber(L_, s); int iresult(result); if (iresult != result) { luaL_error(L_, "not a valid int"); return 0; } return iresult; } lua_Number LuaStack::cknumber(LuaSlot s) const { luaL_checktype(L_, s, LUA_TNUMBER); return lua_tonumber(L_, s); } eng::string LuaStack::ckstring(LuaSlot s) const { luaL_checktype(L_, s, LUA_TSTRING); size_t len; const char *str = lua_tolstring(L_, s, &len); return eng::string(str, len); } lua_State *LuaStack::ckthread(LuaSlot s) const { luaL_checktype(L_, s, LUA_TTHREAD); return lua_tothread(L_, s); } void LuaStack::count_slots_finalize(int narg, int nvar, int nret) { narg_ = narg; nret_ = nret; nvar_ = nvar; ngap_ = nret - nvar - narg; if (ngap_ < 0) ngap_ = 0; int argtop = lua_gettop(L_); argpos_ = argtop + 1 - narg_; gappos_ = argpos_ + narg_; varpos_ = gappos_ + ngap_; retpos_ = varpos_ + nvar_; rettop_ = retpos_ + nret_ - 1; finaltop_ = argpos_ + nret_ - 1; } void LuaStack::clear_frame() { lua_settop(L_, varpos_ - 1); for (int i = 0; i < nvar_ + nret_; i++) { lua_pushnil(L_); } } int LuaStack::result() { lua_settop(L_, rettop_); int i = finaltop_; for (int j = 0; j < nret_; j++) { lua_replace(L_, i); i -= 1; } lua_settop(L_, finaltop_); return nret_; } void LuaStack::clearmetatable(LuaSlot tab) const { lua_pushnil(L_); lua_setmetatable(L_, tab); } void LuaStack::setmetatable(LuaSlot tab, LuaSlot mt) const { lua_pushvalue(L_, mt); lua_setmetatable(L_, tab); } bool LuaStack::getmetatable(LuaSlot mt, LuaSlot tab) const { if (lua_getmetatable(L_, tab)) { lua_replace(L_, mt); return true; } else { lua_pushnil(L_); lua_replace(L_, mt); return false; } } int LuaStack::next(LuaSlot tab, LuaSlot key, LuaSlot value) const { lua_pushvalue(L_, key); int ret = lua_next(L_, tab); if (ret != 0) { lua_replace(L_, value); lua_replace(L_, key); } return ret; } void LuaStack::getglobaltable(LuaSlot target) const { lua_pushglobaltable(L_); lua_replace(L_, target); } void LuaStack::newtable(LuaSlot target) const { lua_newtable(L_); lua_replace(L_, target); } void LuaStack::createtable(LuaSlot target, int narr, int nrec) const { lua_createtable(L_, narr, nrec); lua_replace(L_, target); } lua_State *LuaStack::newthread(LuaSlot target) const { lua_State *result = lua_newthread(L_); lua_replace(L_, target); return result; } bool LuaStack::validclassname(std::string_view cname) { if (cname.empty()) return false; if (cname == "_G") return false; return true; } bool LuaStack::validclassname(LuaSlot slot) const { if (!isstring(slot)) return false; return validclassname(ckstring(slot)); } eng::string LuaStack::classname(LuaSlot tab) const { eng::string result; if (istable(tab)) { lua_pushstring(L_, "__class"); lua_rawget(L_, tab); if (lua_type(L_, -1) == LUA_TSTRING) { lua_pushglobaltable(L_); // cname table lua_pushvalue(L_, -2); // cname table cname lua_rawget(L_, -2); // cname table ctab if (lua_rawequal(L_, -1, tab)) { size_t len; const char *s = lua_tolstring(L_, -3, &len); result = eng::string(s, len); if (!validclassname(result)) { result = ""; } } lua_pop(L_, 3); } else { lua_pop(L_, 1); } } return result; } eng::string LuaStack::getclass(LuaSlot classtab, LuaSlot classname) const { lua_checkstack(L_, 20); LuaVar globtab, cname; LuaStack LS(L_, globtab, cname); LS.getglobaltable(globtab); if (LS.isstring(classname)) { if (!validclassname(LS.ckstring(classname))) { eng::string err = "invalid class name: " + LS.ckstring(classname); LS.result(); return err; } LS.rawget(classtab, globtab, classname); if (!LS.istable(classtab)) { eng::string err = "not a class: " + LS.ckstring(classname); LS.result(); return err; } LS.rawget(cname, classtab, "__class"); if (!LS.rawequal(cname, classname)) { eng::string err = "not a valid class: " + LS.ckstring(classname); LS.result(); return err; } LS.result(); return ""; } else if (LS.istable(classname)) { LS.rawget(cname, classname, "__class"); if (!LS.isstring(cname)) { eng::string err = "table is not a class."; LS.result(); return err; } if (!validclassname(LS.ckstring(cname))) { eng::string err = "invalid class name: " + LS.ckstring(cname); LS.result(); return err; } LS.rawget(classtab, globtab, cname); if (!LS.rawequal(classtab, classname)) { eng::string err = "not a valid class: " + LS.ckstring(cname); LS.result(); return err; } LS.result(); return ""; } else { eng::string err = "getclass expects a string or a classtab"; LS.result(); return err; } } eng::string LuaStack::getclass(LuaSlot tab, std::string_view name) const { push_any_value(name); LuaSpecial classname(lua_gettop(L_)); eng::string err = getclass(tab, classname); lua_pop(L_, 1); return err; } void LuaStack::makeclass(LuaSlot classtab, LuaSlot classname) const { lua_checkstack(L_, 20); LuaVar globtab, cname; LuaStack LS(L_, globtab, cname); // Validate the class name. assert(LS.validclassname(classname)); // Fetch the global environment from the registry. LS.getglobaltable(globtab); // Get the classtab from the global environment. LS.rawget(classtab, globtab, classname); // Make a new table if necessary. if (!LS.istable(classtab)) { LS.newtable(classtab); LS.rawset(globtab, classname, classtab); } // Repair the special fields. LS.settabletype(classtab, LUA_TT_CLASS); LS.rawset(classtab, "__class", classname); LS.rawset(classtab, "__index", classtab); // Put the stack back. LS.result(); } void LuaStack::makeclass(LuaSlot tab, std::string_view name) const { push_any_value(name); LuaSpecial classname(lua_gettop(L_)); makeclass(tab, classname); lua_pop(L_, 1); } int64_t LuaStack::tanid(LuaSlot tab) const { int64_t result = 0; if (istable(tab) && gettabletype(tab) == LUA_TT_TANGIBLE) { if (lua_getmetatable(L_, tab.index())) { lua_pushstring(L_, "id"); lua_rawget(L_, -2); if (lua_type(L_, -1) == LUA_TNUMBER) { result = int64_t(lua_tonumber(L_, -1)); } lua_pop(L_, 2); } } return result; } bool LuaStack::issortablekey(LuaSlot s) const { int type = lua_type(L_, s); return (type == LUA_TBOOLEAN) || (type == LUA_TNUMBER) || (type == LUA_TSTRING); } void LuaStack::movesortablekey(LuaSlot key, LuaStack &otherstack, LuaSlot otherslot) { int type = lua_type(L_, key); switch (type) { case LUA_TBOOLEAN: lua_pushboolean(otherstack.L_, lua_toboolean(L_, key)); lua_replace(otherstack.L_, otherslot); break; case LUA_TNUMBER: lua_pushnumber(otherstack.L_, lua_tonumber(L_, key)); lua_replace(otherstack.L_, otherslot); break; case LUA_TSTRING: { size_t len; const char *str = lua_tolstring(L_, key, &len); lua_pushlstring(otherstack.L_, str, len); lua_replace(otherstack.L_, otherslot); break; } default: assert(false && "movesortablekey: not a sortable key"); } } void LuaStack::cleartable(LuaSlot tab, bool clearmeta) const { assert(istable(tab)); lua_pushnil(L_); while (lua_next(L_, tab.index()) != 0) { lua_pop(L_, 1); // Pop the old value. lua_pushvalue(L_, -1); // Clone the key lua_pushnil(L_); // Push the new value. lua_rawset(L_, tab.index()); } if (clearmeta) { lua_pushnil(L_); lua_setmetatable(L_, tab.index()); } } int LuaStack::rawlen(LuaSlot obj) const { return lua_rawlen(L_, obj.index()); } int LuaStack::gettabletype(LuaSlot tab) const { uint16_t bits = lua_getflagbits(L_, tab.index()); return LUA_TT_GENERAL + (bits & 0x000F); } void LuaStack::settabletype(LuaSlot tab, int t) const { assert((t >= LUA_TT_GENERAL) && (t <= LUA_TT_CLASS)); int offset = (t - LUA_TT_GENERAL); lua_modflagbits(L_, tab.index(), 0x000F, offset); } int LuaStack::xtype(LuaSlot slot) const { int t = lua_type(L_, slot); if (t != LUA_TTABLE) return t; uint16_t bits = lua_getflagbits(L_, slot); return LUA_TT_GENERAL + (bits & 0x000F); } bool LuaStack::getvisited(LuaSlot tab) const { uint16_t bits = lua_getflagbits(L_, tab.index()); return (bits & 0x0010); } void LuaStack::setvisited(LuaSlot tab, bool visited) const { lua_modflagbits(L_, tab.index(), 0x0010, visited ? 0x0010 : 0); }