diff --git a/luprex/cpp/core/luastack.cpp b/luprex/cpp/core/luastack.cpp index 210f7af6..a0142a0d 100644 --- a/luprex/cpp/core/luastack.cpp +++ b/luprex/cpp/core/luastack.cpp @@ -10,6 +10,10 @@ LuaSpecial LuaRegistry(LUA_REGISTRYINDEX); LuaNilMarker LuaNil; LuaNewTableMarker LuaNewTable; +inline bool ascii_islower(char c) { return (c >= 'a') && (c <= 'z'); } +inline bool ascii_isupper(char c) { return (c >= 'A') && (c <= 'Z'); } +inline bool ascii_isdigit(char c) { return (c >= '0') && (c <= '9'); } + LuaFunctionReg::LuaFunctionReg(const char *n, const char *a, const char *d, bool s, lua_CFunction f) { name_ = n; args_ = a; @@ -220,12 +224,30 @@ lua_State *LuaCoreStack::newthread(LuaSlot target) const { return result; } -bool LuaCoreStack::validclassname(std::string_view cname) { - if (cname.empty()) return false; - if (cname == "_G") return false; +bool LuaCoreStack::valididentifier(std::string_view str) { + if (str.size() == 0) return false; + char c=str[0]; + if ((!ascii_islower(c)) && (!ascii_isupper(c)) && (c!='_')) return false; + for (int i = 1; i < int(str.size()); i++) { + char c = str[i]; + if ((!ascii_islower(c)) && (!ascii_isupper(c)) && (!ascii_isdigit(c)) && (c!='_')) return false; + } return true; } +bool LuaCoreStack::validint64(int64_t value) { + return (value <= MAXINT) && (value >= -MAXINT); +} + +bool LuaCoreStack::validpositiveint64(int64_t value) { + return (value <= MAXINT) && (value >= 1); +} + +bool LuaCoreStack::validclassname(std::string_view cname) { + if (cname == "_G") return false; + return valididentifier(cname); +} + bool LuaCoreStack::validclassname(LuaSlot slot) const { if (!isstring(slot)) return false; return validclassname(ckstring(slot)); @@ -352,6 +374,8 @@ void LuaCoreStack::makeclass(LuaSlot tab, std::string_view name) const { } void LuaCoreStack::maketan(LuaSlot tab, int64_t id) const { + assert(validpositiveint64(id)); + LuaVar tangibles, metatab; LuaExtStack LS(L_, tangibles, metatab); @@ -412,6 +436,20 @@ int64_t LuaCoreStack::tanid(LuaSlot tab) const { return result; } +bool LuaCoreStack::tangetclass(LuaSlot classobj, LuaSlot tab) { + if (istable(tab) && (gettabletype(tab) == LUA_TT_TANGIBLE) && lua_getmetatable(L_, tab.index())) { + lua_pushstring(L_, "__index"); + lua_rawget(L_, -2); + lua_replace(L_, classobj); + lua_pop(L_, 1); + if (istable(classobj) && (gettabletype(classobj) == LUA_TT_CLASS)) { + return true; + } + } + set(classobj, LuaNil); + return false; +} + bool LuaCoreStack::issortablekey(LuaSlot s) const { int type = lua_type(L_, s); return (type == LUA_TBOOLEAN) || (type == LUA_TNUMBER) || (type == LUA_TSTRING); diff --git a/luprex/cpp/core/luastack.hpp b/luprex/cpp/core/luastack.hpp index 89fe48b9..1e6d29c5 100644 --- a/luprex/cpp/core/luastack.hpp +++ b/luprex/cpp/core/luastack.hpp @@ -417,6 +417,15 @@ public: int next(LuaSlot tab, LuaSlot key, LuaSlot value) const; + // Return true if the string is a valid lua identifier. + static bool valididentifier(std::string_view id); + + // Return true if the int64 can be stored losslessly in a lua_Number. + static bool validint64(int64_t value); + + // Return true if the int64 is storable in lua and is positive. + static bool validpositiveint64(int64_t value); + // Return true if the classname is legal. bool validclassname(LuaSlot value) const; static bool validclassname(std::string_view cname); @@ -446,6 +455,9 @@ public: // 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. + // + // Assert-fails if the tangible ID is not a validpositiveint64. + // void maketan(LuaSlot tab, int64_t id) const; // Return true if a tangible is empty (deleted or not yet created). @@ -454,6 +466,9 @@ public: // Get the ID of a tangible. int64_t tanid(LuaSlot tab) const; + // Get the class of a tangible. + bool tangetclass(LuaSlot classobj, LuaSlot tan); + // Return true if the value is a sortable key (string, number, or boolean). bool issortablekey(LuaSlot s) const; diff --git a/luprex/cpp/core/serializelua.cpp b/luprex/cpp/core/serializelua.cpp index 4f5a61cf..116af233 100644 --- a/luprex/cpp/core/serializelua.cpp +++ b/luprex/cpp/core/serializelua.cpp @@ -80,11 +80,19 @@ class Deserializer { return; } case LUA_TT_TANGIBLE: { - LS_.maketan(target, sb_->read_int64()); + int64_t tanid = sb_->read_int64(); + if (!LS_.validpositiveint64(tanid)) { + throw DeserializeError(); + } + LS_.maketan(target, tanid); return; } case LUA_TT_CLASS: { - LS_.makeclass(target, sb_->read_string()); + eng::string name = sb_->read_string(); + if (!LS_.validclassname(name)) { + throw DeserializeError(); + } + LS_.makeclass(target, name); return; } case LUA_PK_REFERENCE: { diff --git a/luprex/cpp/core/source.cpp b/luprex/cpp/core/source.cpp index 636f6b2d..16b6a45d 100644 --- a/luprex/cpp/core/source.cpp +++ b/luprex/cpp/core/source.cpp @@ -21,9 +21,6 @@ LuaDefine(makeclass, "classname", "create a class if it doesn't already exist") LuaArg classname; LuaRet classtab; LuaDefStack LS(L, classname, classtab); - if (!LS.isstring(classname)) { - luaL_error(L, "class name must be a string"); - } if (!LS.validclassname(classname)) { luaL_error(L, "invalid class name: %s", LS.ckstring(classname).c_str()); }; diff --git a/luprex/cpp/core/util.cpp b/luprex/cpp/core/util.cpp index 7213ca1e..34ea008f 100644 --- a/luprex/cpp/core/util.cpp +++ b/luprex/cpp/core/util.cpp @@ -178,14 +178,7 @@ int common_prefix_length(string_view a, string_view b) { } bool is_lua_id(string_view str) { - if (str.size() == 0) return false; - char c=str[0]; - if ((!ascii_isalpha(c)) && (c!='_')) return false; - for (int i = 1; i < int(str.size()); i++) { - char c = str[i]; - if ((!ascii_isalpha(c)) && (!ascii_isdigit(c)) && (c!='_')) return false; - } - return true; + return LuaCoreStack::valididentifier(str); } bool is_lua_comment(string_view s) { diff --git a/luprex/cpp/core/world-accessor.cpp b/luprex/cpp/core/world-accessor.cpp index c43b4d52..b144461e 100644 --- a/luprex/cpp/core/world-accessor.cpp +++ b/luprex/cpp/core/world-accessor.cpp @@ -227,13 +227,12 @@ LuaDefine(tangible_getclass, "tan", "|The return value is a string, the class name, not" "|the class table.") { LuaArg tanobj; - LuaVar mt, classtab; + LuaVar classtab; LuaRet classname; - LuaDefStack LS(L, tanobj, mt, classtab, classname); + LuaDefStack LS(L, tanobj, classtab, classname); World *w = World::fetch_global_pointer(L); w->tangible_get(LS, tanobj, false); - LS.getmetatable(mt, tanobj); - LS.rawget(classtab, mt, "__index"); + LS.tangetclass(classtab, tanobj); eng::string name = LS.classname(classtab); if (name == "") { LS.set(classname, LuaNil); diff --git a/luprex/cpp/core/world-core.cpp b/luprex/cpp/core/world-core.cpp index 50960a71..b40fe682 100644 --- a/luprex/cpp/core/world-core.cpp +++ b/luprex/cpp/core/world-core.cpp @@ -141,7 +141,7 @@ Tangible *World::tangible_get(const LuaCoreStack &LS, LuaSlot tab, bool allowdel } Tangible *World::tangible_make(const LuaCoreStack &LS0, LuaSlot database, int64_t id) { - assert(id != 0); + assert(LS0.validpositiveint64(id)); LuaVar metatab; LuaExtStack LS(LS0.state(), metatab); diff --git a/luprex/cpp/core/world-testing.cpp b/luprex/cpp/core/world-testing.cpp index e9a5dc16..c5304bbd 100644 --- a/luprex/cpp/core/world-testing.cpp +++ b/luprex/cpp/core/world-testing.cpp @@ -58,11 +58,17 @@ eng::string World::tangible_ids_debug_string() const { } eng::string World::tangibles_near_debug_string(int64_t actor, int64_t distance) { + assert(stack_is_clear()); + lua_State *L = state(); + LuaVar tangibles, database, mt; + LuaExtStack LS(L, tangibles, database); + LS.rawget(tangibles, LuaRegistry, "tangibles"); eng::ostringstream result; util::IdVector tans; get_near(actor, distance, true, false, true, &tans); for (int64_t id : tans) { const Tangible *tan = tangible_get(id); + LS.rawget(database, tangibles, id); AnimState state = tan->anim_queue_.get_final_everything(); result << id << ": " << state.debug_string() << std::endl; }