Some more error checking for class names and tangible IDs

This commit is contained in:
2024-02-28 14:19:59 -05:00
parent 32dae0df5a
commit 0c126e62d6
8 changed files with 77 additions and 21 deletions

View File

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

View File

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

View File

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

View File

@@ -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());
};

View File

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

View File

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

View File

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

View File

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