From 92549f3c561e6c8f3678dac424b942eae94653c7 Mon Sep 17 00:00:00 2001 From: lethosor Date: Sat, 12 Aug 2023 03:09:18 -0400 Subject: [PATCH] Add _fields table to struct types --- library/LuaTypes.cpp | 106 ++++++++++++++++++++++++++++++++++++++--- library/LuaWrapper.cpp | 1 + 2 files changed, 100 insertions(+), 7 deletions(-) diff --git a/library/LuaTypes.cpp b/library/LuaTypes.cpp index ddc166abc..7e1ab67cb 100644 --- a/library/LuaTypes.cpp +++ b/library/LuaTypes.cpp @@ -1291,11 +1291,27 @@ void LuaWrapper::SetFunctionWrappers(lua_State *state, const FunctionReg *reg) /** * Add fields in the array to the UPVAL_FIELDTABLE candidates on the stack. + * + * flags: + * GLOBALS: if true, pstruct is a global_identity and fields with addresses of 0 are skipped + * RAW: if true, no fields are skipped (supersedes `GLOBALS` flag) and + * special-case fields like OBJ_METHODs are not added to the metatable + * + * Stack in & out: + * base+1: metatable + * base+2: fields table (to be populated, map of name -> struct_field_info*) + * base+3: field iter table (to be populated, bimap of name <-> integer index) */ -static void IndexFields(lua_State *state, int base, struct_identity *pstruct, bool globals) +namespace IndexFieldsFlags { + enum IndexFieldsFlags { + GLOBALS = 1 << 0, + RAW = 1 << 1, + }; +} +static void IndexFields(lua_State *state, int base, struct_identity *pstruct, int flags) { if (pstruct->getParent()) - IndexFields(state, base, pstruct->getParent(), globals); + IndexFields(state, base, pstruct->getParent(), flags); auto fields = pstruct->getFields(); if (!fields) @@ -1315,6 +1331,7 @@ static void IndexFields(lua_State *state, int base, struct_identity *pstruct, bo bool add_to_enum = true; + if (!(flags & IndexFieldsFlags::RAW)) // Handle the field switch (fields[i].mode) { @@ -1337,10 +1354,10 @@ static void IndexFields(lua_State *state, int base, struct_identity *pstruct, bo } // Do not add invalid globals to the enumeration order - if (globals && !*(void**)fields[i].offset) + if ((flags & IndexFieldsFlags::GLOBALS) && !*(void**)fields[i].offset) add_to_enum = false; - if (add_to_enum) + if (add_to_enum || (flags & IndexFieldsFlags::RAW)) AssociateId(state, base+3, ++cnt, name.c_str()); lua_pushlightuserdata(state, (void*)&fields[i]); @@ -1348,10 +1365,86 @@ static void IndexFields(lua_State *state, int base, struct_identity *pstruct, bo } } +static void AddFieldInfoTable(lua_State *state, int ftable_idx, struct_identity *pstruct) +{ + Lua::StackUnwinder base{state}; + + // metatable + lua_newtable(state); + int ix_meta = lua_gettop(state); + + // field info table (name -> struct_field_info*) + lua_newtable(state); + int ix_fieldinfo = lua_gettop(state); + + // field iter table (int <-> name) + lua_newtable(state); + int ix_fielditer = lua_gettop(state); + IndexFields(state, base, pstruct, IndexFieldsFlags::RAW); + + PushStructMethod(state, ix_meta, ix_fielditer, meta_struct_next); + SetPairsMethod(state, ix_meta, "__pairs"); + + // field table (name -> table representation of struct_field_info) + lua_newtable(state); + int ix_fields = lua_gettop(state); + + // wrapper table (empty, indexes into field table with metamethods) + lua_newtable(state); + int ix_wrapper = lua_gettop(state); + + // set up metatable for the wrapper + // use field table for __index + lua_pushstring(state, "__index"); + lua_pushvalue(state, ix_fields); + lua_settable(state, ix_meta); + + // use change_error() for __newindex + lua_pushstring(state, "__newindex"); + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_CHANGEERROR_NAME); + lua_settable(state, ix_meta); + + lua_pushvalue(state, ix_meta); + lua_setmetatable(state, ix_wrapper); + + // convert field info table (struct_field_info) to field table (lua tables) + lua_pushnil(state); // initial key for next() + while (lua_next(state, ix_fieldinfo)) { + auto field = static_cast(lua_touserdata(state, -1)); + lua_pushvalue(state, -2); // field name + lua_newtable(state); // new field info + Lua::TableInsert(state, "name", field->name); + Lua::TableInsert(state, "offset", field->offset); + + if (field->type) { + Lua::TableInsert(state, "type_name", field->type->getFullName()); + + lua_pushstring(state, "type"); + lua_rawgetp(state, LUA_REGISTRYINDEX, &DFHACK_TYPEID_TABLE_TOKEN); + lua_rawgetp(state, -1, field->type); + lua_remove(state, -2); // TYPEID_TABLE + lua_settable(state, -3); + } + + lua_settable(state, ix_fields); + + lua_pop(state, 1); // field name + } + + lua_pushvalue(state, ix_wrapper); + lua_setfield(state, ftable_idx, "_fields"); + lua_pushvalue(state, ix_fieldinfo); + lua_setfield(state, ftable_idx, "_fieldsinfo"); + lua_pushvalue(state, ix_meta); + lua_setfield(state, ftable_idx, "_fieldsmeta"); + lua_pushvalue(state, ix_fielditer); + lua_setfield(state, ftable_idx, "_fieldsiter"); +} + void LuaWrapper::IndexStatics(lua_State *state, int meta_idx, int ftable_idx, struct_identity *pstruct) { // stack: metatable fieldtable - + AddFieldInfoTable(state, ftable_idx, pstruct); for (struct_identity *p = pstruct; p; p = p->getParent()) { auto fields = p->getFields(); @@ -1387,8 +1480,7 @@ static void MakeFieldMetatable(lua_State *state, struct_identity *pstruct, // Index the fields lua_newtable(state); - - IndexFields(state, base, pstruct, globals); + IndexFields(state, base, pstruct, globals ? IndexFieldsFlags::GLOBALS : 0); // Add the iteration metamethods PushStructMethod(state, base+1, base+3, iterator); diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp index 1ea4865bd..9d2357c70 100644 --- a/library/LuaWrapper.cpp +++ b/library/LuaWrapper.cpp @@ -1670,6 +1670,7 @@ static void RenderType(lua_State *state, compound_identity *node) { RenderTypeChildren(state, node->getScopeChildren()); + IndexStatics(state, ix_meta, ftable, (struct_identity*)node); lua_pushlightuserdata(state, node); lua_setfield(state, ftable, "_identity");