From b1a132e252a39c405492382a643fc1eb6e53bc7d Mon Sep 17 00:00:00 2001 From: jyelon Date: Sat, 21 Feb 2026 23:37:55 -0500 Subject: [PATCH] Rewrite 'getclass' and 'classname' operators. These were flaky and inconsistent. --- luprex/Makefile | 2 +- luprex/cpp/core/luastack.cpp | 140 ++++++++++++++++------------------- luprex/cpp/core/luastack.hpp | 41 +++++++--- 3 files changed, 92 insertions(+), 91 deletions(-) diff --git a/luprex/Makefile b/luprex/Makefile index edaa110a..79713c7a 100644 --- a/luprex/Makefile +++ b/luprex/Makefile @@ -81,7 +81,7 @@ BASE_ERIS := \ BASE_CORE := \ invocation spookyv2 eng-malloc debugcollector drivenengine util luastack \ - traceback planemap pprint luavector idalloc globaldb sched http \ + traceback planemap pprint luavector idalloc sched http \ json table luasnap animqueue streambuffer source world-core world-accessor \ world-difftab world-diffxmit world-pairtab world-testing lpxserver lpxclient \ eng-tests printbuffer serializelua diff --git a/luprex/cpp/core/luastack.cpp b/luprex/cpp/core/luastack.cpp index c30b199f..06f8eab0 100644 --- a/luprex/cpp/core/luastack.cpp +++ b/luprex/cpp/core/luastack.cpp @@ -381,103 +381,87 @@ lua_State *LuaCoreStack::newthread(LuaSlot target) const { return result; } -eng::string LuaCoreStack::classname(LuaSlot input) const { - LuaVar lookup, classtab, classname, metatable; - LuaExtStack LS(L_, lookup, classtab, classname, metatable); +void LuaCoreStack::getclassinfo(LuaSlot classtab, + eng::string &classname, eng::string &error, LuaSlot input) const { + LuaVar lookup, cname; + LuaExtStack LS(L_, lookup, cname); + // Step 1: Resolve input to a class table. int xt = xtype(input); if (xt == LUA_TSTRING) { LS.rawget(lookup, LuaRegistry, "classes"); LS.rawget(classtab, lookup, input); - if (xtype(classtab) != LUA_TT_CLASS) { - return ""; - } - return LS.ckstring(input); } else if (xt == LUA_TT_TANGIBLE) { - if (!LS.getmetatable(lookup, input)) { - return ""; + if (LS.getmetatable(lookup, input)) { + LS.rawget(classtab, lookup, "__index"); + } else { + LS.set(classtab, LuaNil); } - LS.rawget(classtab, lookup, "__index"); - if (xtype(classtab) != LUA_TT_CLASS) { - return ""; - } - LS.rawget(lookup, LuaRegistry, "classnames"); - LS.rawget(classname, lookup, classtab); - if (!LS.isstring(classname)) { - return ""; - } - return LS.ckstring(classname); } else if (xt == LUA_TT_CLASS) { - LS.rawget(lookup, LuaRegistry, "classnames"); - LS.rawget(classname, lookup, input); - if (!LS.isstring(classname)) { - return ""; - } - return LS.ckstring(classname); + LS.set(classtab, input); } else if (xt == LUA_TT_GENERAL) { - LS.getmetatable(metatable, input); - LS.rawget(lookup, LuaRegistry, "classnames"); - LS.rawget(classname, lookup, metatable); - if (!LS.isstring(classname)) { - return ""; - } - return LS.ckstring(classname); + LS.getmetatable(classtab, input); } else { - return ""; + LS.set(classtab, LuaNil); + } + + // Step 2: Validate classtab and get classname, or generate error. + // A class table is only valid if it's registered in classnames. + if (xtype(classtab) == LUA_TT_CLASS) { + LS.rawget(lookup, LuaRegistry, "classnames"); + LS.rawget(cname, lookup, classtab); + auto name = LS.trystring(cname); + if (name) { + classname = *name; + error = ""; + return; + } + } + + // Step 3: We didn't find a valid classtab and classname. + // Clear the classtab and classname return-values, then + // figure out what went wrong and generate an error message. + LS.set(classtab, LuaNil); + classname = ""; + if (xt == LUA_TSTRING) { + eng::string s = LS.ckstring(input); + if (!sv::is_lua_classname(s)) { + error = "invalid class name: " + s; + } else { + error = "class does not exist: " + s; + } + } else if (xt == LUA_TT_TANGIBLE) { + error = "tangible has no valid class"; + } else if (xt == LUA_TT_GENERAL) { + error = "table has no valid class"; + } else if (xt == LUA_TT_CLASS) { + error = "class is no longer valid"; + } else { + error = "expected a string, class, tangible, or table"; } } -static eng::string nonexistentclass(eng::string name) { - eng::string err; - if (!sv::is_lua_classname(name)) { - err = "invalid class name: " + name; - } else { - err = "not a class: " + name; - } - return err; +eng::string LuaCoreStack::classname(LuaSlot input) const { + LuaVar tab; + LuaExtStack LS(L_, tab); + eng::string name, error; + getclassinfo(tab, name, error, input); + return name; } eng::string LuaCoreStack::getclass(LuaSlot classtab, LuaSlot input) const { - LuaVar lookup; - LuaExtStack LS(L_, lookup); - - int xt = xtype(input); - if (xt == LUA_TSTRING) { - LS.rawget(lookup, LuaRegistry, "classes"); - LS.rawget(classtab, lookup, input); - if (xtype(classtab) != LUA_TT_CLASS) { - eng::string classname = LS.ckstring(input); - return nonexistentclass(LS.ckstring(input)); - } - return ""; - } else if (xt == LUA_TT_TANGIBLE) { - if (!LS.getmetatable(lookup, input)) { - eng::string err = "inactive tangible has no class"; - return err; - } - LS.rawget(classtab, lookup, "__index"); - if (xtype(classtab) != LUA_TT_CLASS) { - eng::string err = "tangible has invalid class"; - } - return ""; - } else if (xt == LUA_TT_CLASS) { - LS.set(classtab, input); - return ""; - } else { - eng::string err = "getclass must be passed a string, class, or tangible"; - return err; - } + eng::string name, error; + getclassinfo(classtab, name, error, input); + return error; } eng::string LuaCoreStack::getclass(LuaSlot classtab, std::string_view classname) const { - LuaVar lookup; - LuaExtStack LS(L_, lookup); - LS.rawget(lookup, LuaRegistry, "classes"); - LS.rawget(classtab, lookup, classname); - if (xtype(classtab) != LUA_TT_CLASS) { - return nonexistentclass(eng::string(classname)); - } - return ""; + LuaVar input; + LuaExtStack LS(L_, input); + LS.set(input, classname); + eng::string name, error; + getclassinfo(classtab, name, error, input); + return error; } void LuaCoreStack::makeclass(LuaSlot classtab, LuaSlot classname) const { diff --git a/luprex/cpp/core/luastack.hpp b/luprex/cpp/core/luastack.hpp index 5b2a6db3..d8512d17 100644 --- a/luprex/cpp/core/luastack.hpp +++ b/luprex/cpp/core/luastack.hpp @@ -833,28 +833,42 @@ public: // static bool validpositiveinteger(int64_t value) { return (value <= MAXINT) && (value >= 1); } - // Get the class name given a class table. + // Get the class name of X. // - // Return the class name if x is a valid class table. Otherwise, returns - // empty string. If nonempty, the result is guaranteed to be a - // validclassname. This can also function as an "isclass" operator. + // The object passed in can be: + // + // * A valid class table. + // * A valid, existing class name. + // * A tangible that has a class. + // * A normal table with a class metatable. + // + // If the object is none of these, returns empty string. // eng::string classname(LuaSlot x) const; - // Look for a class by class-name. + // Get the class table of X. // - // If there is a problem, returns an error message. There are lots - // of error conditions, including such things as no such class, corrupted - // class, classname invalid, etc. + // The object passed in can be: // - eng::string getclass(LuaSlot tab, LuaSlot name) const; + // * A valid class table. + // * A valid, existing class name. + // * A tangible that has a class. + // * + // + // If there is a problem, returns an error message. + // There are lots of error conditions, including such + // things as no such class, corrupted class, classname + // invalid, etc. + // + eng::string getclass(LuaSlot tab, LuaSlot obj) const; eng::string getclass(LuaSlot tab, std::string_view name) const; // Create a class, or look up an existing class. // - // Creates a new class, unless the class already exists. Stores the - // class in the global environment table. This routine assert-fails if the - // parameter is not a valid classname. + // Creates a new class, unless the class already exists. + // Stores the class in the global environment table, and + // in the class database. This routine assert-fails if + // the parameter is not a valid classname. // void makeclass(LuaSlot tab, LuaSlot name) const; void makeclass(LuaSlot tab, std::string_view name) const; @@ -1018,6 +1032,9 @@ public: lua_rawseti(L_, tab, key); } +private: + void getclassinfo(LuaSlot classtab, eng::string &classname, eng::string &error, LuaSlot input) const; + protected: // Assign slots: this is used by the LuaDefStack and LuaExtStack