Dynamically generate field info in __index

Constructing the complete tables when the types were initialized made it
impossible to populate the "type" field, because not all types had been added to
the global type tables yet.
develop
lethosor 2023-08-12 20:18:22 -04:00
parent 4a9a83daa5
commit fc6d4caa8e
No known key found for this signature in database
GPG Key ID: 76A269552F4F58C1
2 changed files with 68 additions and 2 deletions

@ -1367,6 +1367,11 @@ static void IndexFields(lua_State *state, int base, struct_identity *pstruct, in
static void PushFieldInfoSubTable(lua_State *state, const struct_field_info *field)
{
if (!field) {
lua_pushnil(state);
return;
}
lua_newtable(state); // new field info
Lua::TableInsert(state, "mode", field->mode);
Lua::TableInsert(state, "name", field->name);
@ -1401,6 +1406,51 @@ static void PushFieldInfoSubTable(lua_State *state, const struct_field_info *fie
// freeze_table(state); // TODO: make pairs() work
}
/**
* Metamethod: __index for struct._fields
*
* upvalue 1: name -> struct_field_info* table
*/
static int meta_fieldinfo_index(lua_State *state)
{
luaL_checktype(state, -1, LUA_TSTRING);
lua_gettable(state, lua_upvalueindex(1));
auto field = static_cast<const struct_field_info*>(lua_touserdata(state, -1));
lua_pop(state, 1);
PushFieldInfoSubTable(state, field);
return 1;
}
/**
* Metamethod: iterator for struct._fields
*
* upvalue 1: name -> struct_field_info* table
* upvalue 3: field table (int <-> name)
*/
static int meta_fieldinfo_next(lua_State *state)
{
if (lua_gettop(state) < 2) lua_pushnil(state);
int len = lua_rawlen(state, UPVAL_FIELDTABLE);
int idx = cur_iter_index(state, len+1, 2, 0);
if (idx == len)
return 0;
lua_rawgeti(state, UPVAL_FIELDTABLE, idx+1);
// modified from meta_struct_next:
// retrieve the struct_field_info* from the table and convert it
lua_dup(state);
lua_gettable(state, lua_upvalueindex(1));
auto field = static_cast<const struct_field_info*>(lua_touserdata(state, -1));
lua_pop(state, 1);
PushFieldInfoSubTable(state, field);
return 2;
}
static void AddFieldInfoTable(lua_State *state, int ftable_idx, struct_identity *pstruct)
{
Lua::StackUnwinder base{state};
@ -1418,7 +1468,10 @@ static void AddFieldInfoTable(lua_State *state, int ftable_idx, struct_identity
int ix_fielditer = lua_gettop(state);
IndexFields(state, base, pstruct, IndexFieldsFlags::RAW);
PushStructMethod(state, ix_meta, ix_fielditer, meta_struct_next);
PushStructMethod(state, ix_meta, ix_fielditer, meta_fieldinfo_next);
// change upvalue 1 to the field info table since we don't need the original
lua_pushvalue(state, ix_fieldinfo);
lua_setupvalue(state, -2, 1);
SetPairsMethod(state, ix_meta, "__pairs");
// field table (name -> table representation of struct_field_info)
@ -1432,7 +1485,8 @@ static void AddFieldInfoTable(lua_State *state, int ftable_idx, struct_identity
// set up metatable for the wrapper
// use field table for __index
lua_pushstring(state, "__index");
lua_pushvalue(state, ix_fields);
lua_pushvalue(state, ix_fieldinfo);
lua_pushcclosure(state, meta_fieldinfo_index, 1);
lua_settable(state, ix_meta);
// use change_error() for __newindex

@ -60,3 +60,15 @@ function test.readonly()
expect.eq(df.coord._fields.x.name, 'x')
expect.nil_(df.coord._fields.nonexistent)
end
function test.circular_refs_init()
expect.eq(df.job._fields.list_link.type, df.job_list_link)
expect.eq(df.job_list_link._fields.item.type, df.job)
end
function test.subclass_match()
for f, parent in pairs(df.viewscreen._fields) do
local child = df.viewscreen_titlest._fields[f]
expect.table_eq(parent, child, f)
end
end