Rewrite 'getclass' and 'classname' operators. These were flaky and inconsistent.

This commit is contained in:
2026-02-21 23:37:55 -05:00
parent afa8c698be
commit b1a132e252
3 changed files with 92 additions and 91 deletions

View File

@@ -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

View File

@@ -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");
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);
} 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);
} else {
return "";
LS.set(classtab, LuaNil);
}
} else if (xt == LUA_TT_CLASS) {
LS.set(classtab, input);
} else if (xt == LUA_TT_GENERAL) {
LS.getmetatable(classtab, input);
} else {
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;
}
}
static eng::string nonexistentclass(eng::string name) {
eng::string err;
if (!sv::is_lua_classname(name)) {
err = "invalid class name: " + name;
// 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 {
err = "not a class: " + name;
error = "class does not exist: " + s;
}
return err;
} 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";
}
}
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 {

View File

@@ -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