diff --git a/luprex/cpp/core/luastack.cpp b/luprex/cpp/core/luastack.cpp index ccd09706..47e8007d 100644 --- a/luprex/cpp/core/luastack.cpp +++ b/luprex/cpp/core/luastack.cpp @@ -75,7 +75,20 @@ static void *l_alloc(void *ud, void *ptr, size_t osize, size_t 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); + assert(L != nullptr); + lua_atpanic(L, &panicf); + + // We want all states to have a classes table and + // a tangibles table so that LS.makeclass and LS.maketan + // work out-of-the-box. + + lua_pushstring(L, "classes"); + lua_newtable(L); + lua_rawset(L, LUA_REGISTRYINDEX); + lua_pushstring(L, "tangibles"); + lua_newtable(L); + lua_rawset(L, LUA_REGISTRYINDEX); + return L; } @@ -256,27 +269,35 @@ bool LuaStack::validclassname(LuaSlot slot) const { 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 = ""; - } + if ((istable(tab)) && (gettabletype(tab) == LUA_TT_CLASS)) { + LuaVar classes, name, dup; + LuaStack LS(L_, classes, name, dup); + // Get the classes table from the registry. + LS.rawget(classes, LuaRegistry, "classes"); + + // Try the efficient approach: get the class name from + // the class, and confirm it by checking the classes table. + LS.rawget(name, tab, "__class"); + if (LS.isstring(name)) { + LS.rawget(dup, classes, name); + if (LS.rawequal(dup, tab)) { + result = LS.ckstring(name); + LS.result(); + return result; + } + } + + // Do it the brute force way: scan the classes table. + LS.set(name, LuaNil); + while (LS.next(classes, name, dup)) { + if (LS.rawequal(dup, tab)) { + result = LS.ckstring(name); + LS.result(); + return result; } - lua_pop(L_, 3); - } else { - lua_pop(L_, 1); } } - return result; + return ""; } eng::string LuaStack::getclass(LuaSlot classtab, LuaSlot classname) const { @@ -342,24 +363,31 @@ eng::string LuaStack::getclass(LuaSlot tab, std::string_view name) const { void LuaStack::makeclass(LuaSlot classtab, LuaSlot classname) const { lua_checkstack(L_, 20); - LuaVar globtab, cname; - LuaStack LS(L_, globtab, cname); + LuaVar classes, globtab, cname; + LuaStack LS(L_, classes, globtab, cname); // Validate the class name. assert(LS.validclassname(classname)); + // Fetch the classes table from the registry. + LS.rawget(classes, LuaRegistry, "classes"); + assert(LS.istable(classes)); + // Fetch the global environment from the registry. LS.getglobaltable(globtab); - // Get the classtab from the global environment. - LS.rawget(classtab, globtab, classname); + // Get the classtab from the classes table. + LS.rawget(classtab, classes, classname); // Make a new table if necessary. if (!LS.istable(classtab)) { LS.newtable(classtab); - LS.rawset(globtab, classname, classtab); + LS.rawset(classes, classname, classtab); } + // Put the table into the global environment. + LS.rawset(globtab, classname, classtab); + // Repair the special fields. LS.settabletype(classtab, LUA_TT_CLASS); LS.rawset(classtab, "__class", classname); diff --git a/luprex/cpp/core/luastack.hpp b/luprex/cpp/core/luastack.hpp index b68a66e5..a5a4daeb 100644 --- a/luprex/cpp/core/luastack.hpp +++ b/luprex/cpp/core/luastack.hpp @@ -458,9 +458,11 @@ public: void makeclass(LuaSlot tab, std::string_view name) const; // Create a tangible, or look up an existing tangible. - // This doesn't do the entire process of tangible creation. It - // just creates an empty table, marks it as a tangible, creates - // the metatable, stores the tangible ID, and stores it in the tangible table. + // If the tangible doesn't exist yet, this creates a tangible stub. + // It is possible to use World::tangible_make to transform a tangible + // stub into a full blown tangible, and World::tangible_delete to turn + // a full-blown tangible back into a stub. A stub doesn't have a + // class or a thread table. void maketan(LuaSlot tab, int64_t id) const; // Return true if a tangible is empty (deleted or not yet created). diff --git a/luprex/cpp/core/source.cpp b/luprex/cpp/core/source.cpp index ccb9142e..8aa824b6 100644 --- a/luprex/cpp/core/source.cpp +++ b/luprex/cpp/core/source.cpp @@ -245,26 +245,24 @@ void SourceDB::update(const util::LuaSourceVec &source) { LS.result(); } -// Delete everything from the global environment except -// the class tables. +// Delete everything from the global environment. +// Clear all the classes in the registry classes table. // static void source_clear_globals(lua_State *L) { - LuaVar classname, classtab, key, globtab; - LuaStack LS(L, classname, classtab, key, globtab); + LuaVar classname, classtab, key, globtab, classes; + LuaStack LS(L, classname, classtab, key, globtab, classes); LS.getglobaltable(globtab); - LS.rawset(globtab, "_G", LuaNil); - LS.set(classname, LuaNil); - while (LS.next(globtab, classname, classtab) != 0) { - if (LS.rawequal(globtab, classtab)) { - LS.rawset(globtab, classname, LuaNil); - } else if (LS.istable(classtab)) { - LS.cleartable(classtab, true); - } else { - LS.rawset(globtab, classname, LuaNil); - } - } + LS.cleartable(globtab, true); LS.rawset(globtab, "_G", globtab); + + LS.rawget(classes, LuaRegistry, "classes"); + assert(LS.istable(classes)); + LS.set(classname, LuaNil); + while (LS.next(classes, classname, classtab) != 0) { + assert(LS.istable(classtab)); + LS.cleartable(classtab, true); + } LS.result(); } diff --git a/luprex/cpp/core/world-core.cpp b/luprex/cpp/core/world-core.cpp index 62c5dd1b..11866fc1 100644 --- a/luprex/cpp/core/world-core.cpp +++ b/luprex/cpp/core/world-core.cpp @@ -60,9 +60,6 @@ World::World(WorldType wt) { LS.getglobaltable(globtab); LS.settabletype(globtab, LUA_TT_GLOBALENV); - // Create the tangibles table in the registry. - LS.rawset(LuaRegistry, "tangibles", LuaNewTable); - // Store the world type in the registry. LS.rawset(LuaRegistry, "worldtype", wt); @@ -592,7 +589,7 @@ void World::invoke_flush_prints(int64_t actor_id, int64_t place_id, const eng::s void World::invoke_lua(int64_t actor_id, int64_t place_id, const eng::string &action, const InvocationData &data) { assert(stack_is_clear()); - // Make sure that actor and place exist. + // Make sure that actor and place exist and are not stubs. Tangible *tactor = tangible_get(actor_id); Tangible *tplace = tangible_get(place_id); if ((tactor == nullptr) || (tplace == nullptr)) {