Add _fields table to struct types

develop
lethosor 2023-08-12 03:09:18 -04:00
parent 9cb764b847
commit 92549f3c56
No known key found for this signature in database
GPG Key ID: 76A269552F4F58C1
2 changed files with 100 additions and 7 deletions

@ -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<const struct_field_info*>(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);

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