diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp index 59bd967328..4ded768a75 100644 --- a/library/LuaWrapper.cpp +++ b/library/LuaWrapper.cpp @@ -1534,6 +1534,25 @@ static void FillEnumKeys(lua_State *state, int ix_meta, int ftable, enum_identit lua_pushcclosure(state, meta_enum_attr_index, 3); freeze_table(state, false, (eid->getFullName()+".attrs").c_str()); + int ix_attrs = lua_gettop(state); + + // add metamethods to metatable of .attrs + if (lua_getmetatable(state, ix_attrs)) + { + int ix_attrs_meta = lua_gettop(state); + + if (eid->getFirstItem() <= eid->getLastItem()) + { + lua_pushvalue(state, ix_attrs); + lua_pushinteger(state, eid->getFirstItem() - 1); + lua_pushinteger(state, eid->getLastItem()); + lua_pushcclosure(state, wtype_ipairs, 3); + lua_setfield(state, ix_attrs_meta, "__ipairs"); + } + + lua_pop(state, 1); // pop metatable + } + lua_setfield(state, ftable, "attrs"); } diff --git a/test/structures/enum_attrs.lua b/test/structures/enum_attrs.lua new file mode 100644 index 0000000000..ff1e28aee1 --- /dev/null +++ b/test/structures/enum_attrs.lua @@ -0,0 +1,83 @@ +function test.getmetatable() + expect.eq(getmetatable(df.item_type.attrs), 'item_type.attrs') +end + +local function check_valid_attr_entry(enum_type, index) + local entry = enum_type.attrs[index] + local suffix = ('%s entry at index %s'):format(enum_type._attr_entry_type, index) + expect.ne(entry, nil, 'nil ' .. suffix) + expect.true_(enum_type._attr_entry_type:is_instance(entry), 'invalid ' .. suffix) +end + +function test.valid_items() + for i in ipairs(df.item_type) do + check_valid_attr_entry(df.item_type, i) + end + check_valid_attr_entry(df.item_type, df.item_type._first_item) + check_valid_attr_entry(df.item_type, df.item_type._last_item) +end + +function test.valid_items_name() + for i, name in ipairs(df.item_type) do + expect.eq(df.item_type.attrs[i], df.item_type.attrs[name]) + end +end + +function test.valid_items_unique() + -- check that every enum item has its own attrs entry + local addr_to_name = {} + for _, name in ipairs(df.item_type) do + local _, addr = df.sizeof(df.item_type.attrs[name]) + if addr_to_name[addr] then + expect.fail(('attrs shared between "%s" and "%s"'):format(name, addr_to_name[name])) + else + addr_to_name[addr] = name + end + end +end + +function test.invalid_items() + check_valid_attr_entry(df.item_type, df.item_type._first_item - 1) + check_valid_attr_entry(df.item_type, df.item_type._last_item + 1) +end + +function test.invalid_items_shared() + expect.eq(df.item_type.attrs[df.item_type._first_item - 1], df.item_type.attrs[df.item_type._last_item + 1]) +end + +function test.length() + expect.eq(#df.item_type.attrs, 0) +end + +local function max_attrs_length(enum_type) + return enum_type._last_item - enum_type._first_item + 1 +end + +function test.pairs() + local i = 0 + for _ in pairs(df.item_type.attrs) do + i = i + 1 + if i > max_attrs_length(df.item_type) then + expect.fail('pairs() returned too many items: ' .. tostring(i)) + break + end + end + expect.eq(i, 0, 'pairs() returned wrong number of items') +end + +function test.ipairs() + local i = 0 + for index, value in ipairs(df.item_type.attrs) do + if i == 0 then + expect.eq(index, df.item_type._first_item, 'ipairs() returned wrong start index') + end + i = i + 1 + if i > max_attrs_length(df.item_type) then + expect.fail('ipairs() returned too many items: ' .. tostring(i)) + break + end + expect.eq(value, df.item_type.attrs[i + df.item_type._first_item], + 'ipairs() returned incorrect attrs for item at index: ' .. tostring(index)) + end + expect.eq(i, max_attrs_length(df.item_type), 'ipairs() returned wrong number of items') +end