diff options
| author | Alexander Gavrilov | 2012-03-21 13:26:53 +0400 |
|---|---|---|
| committer | Alexander Gavrilov | 2012-03-21 13:26:53 +0400 |
| commit | 9b78fffe9255e0fd14b6e446652598a36372b934 (patch) | |
| tree | 8c1e02483868cd3c22219152b32f544b877b19cf /library/LuaWrapper.cpp | |
| parent | 73e138c9fdf64cab23330fc13b73383d8648324d (diff) | |
| download | dfhack-9b78fffe9255e0fd14b6e446652598a36372b934.tar.gz dfhack-9b78fffe9255e0fd14b6e446652598a36372b934.tar.bz2 dfhack-9b78fffe9255e0fd14b6e446652598a36372b934.tar.xz | |
Support containers in the lua wrapper.
Diffstat (limited to 'library/LuaWrapper.cpp')
| -rw-r--r-- | library/LuaWrapper.cpp | 444 |
1 files changed, 371 insertions, 73 deletions
diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp index 7081cd25..d4755bc0 100644 --- a/library/LuaWrapper.cpp +++ b/library/LuaWrapper.cpp @@ -50,6 +50,7 @@ inline void lua_swap(lua_State *state) { lua_insert(state, -2); } #define DFHACK_TYPETABLE_NAME "DFHack::DFTypes" #define DFHACK_TYPEID_TABLE_NAME "DFHack::DFTypeIds" +#define DFHACK_ENUM_TABLE_NAME "DFHack::DFEnums" #define DFHACK_CHANGEERROR_NAME "DFHack::ChangeError" #define DFHACK_COMPARE_NAME "DFHack::ComparePtrs" #define DFHACK_TYPE_TOSTRING_NAME "DFHack::TypeToString" @@ -58,6 +59,10 @@ inline void lua_swap(lua_State *state) { lua_insert(state, -2); } #define UPVAL_METATABLE lua_upvalueindex(2) #define UPVAL_FIELDTABLE lua_upvalueindex(3) +#define UPVAL_CONTAINER_ID lua_upvalueindex(4) +#define UPVAL_ITEM_ID lua_upvalueindex(5) +#define UPVAL_ITEM_COUNT lua_upvalueindex(6) + namespace { struct DFRefHeader { void *ptr; @@ -138,10 +143,10 @@ void df::bool_identity::lua_write(lua_State *state, int fname_idx, void *ptr, in { char *pb = (char*)ptr; - if (lua_isboolean(state, val_index)) + if (lua_isboolean(state, val_index) || lua_isnil(state, val_index)) *pb = lua_toboolean(state, val_index); else if (lua_isnumber(state, val_index)) - *pb = lua_tonumber(state, val_index); + *pb = lua_tointeger(state, val_index); else field_error(state, fname_idx, "boolean or number expected", "write"); } @@ -196,6 +201,72 @@ void df::pointer_identity::lua_write(lua_State *state, int fname_idx, void *ptr, lua_write(state, fname_idx, ptr, target, val_index); } +int container_identity::lua_item_count(lua_State *state, void *ptr) +{ + if (lua_isnumber(state, UPVAL_ITEM_COUNT)) + return lua_tointeger(state, UPVAL_ITEM_COUNT); + else + return item_count(ptr); +} + +int container_identity::lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx) +{ + void *pitem = item_pointer(ptr, idx); + auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID); + return id->lua_read(state, fname_idx, pitem); +} + +void container_identity::lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index) +{ + void *pitem = item_pointer(ptr, idx); + auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID); + id->lua_write(state, fname_idx, pitem, val_index); +} + +int ptr_container_identity::lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx) +{ + void *pitem = item_pointer(ptr, idx); + auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID); + return df::pointer_identity::lua_read(state, fname_idx, pitem, id); +} + +void ptr_container_identity::lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index) +{ + void *pitem = item_pointer(ptr, idx); + auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID); + df::pointer_identity::lua_write(state, fname_idx, pitem, id, val_index); +} + +int bit_container_identity::lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx) +{ + lua_pushboolean(state, get_item(ptr, idx)); + return 1; +} + +void bit_container_identity::lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index) +{ + if (lua_isboolean(state, val_index) || lua_isnil(state, val_index)) + set_item(ptr, idx, lua_toboolean(state, val_index)); + else if (lua_isnumber(state, val_index)) + set_item(ptr, idx, lua_tointeger(state, val_index) != 0); + else + field_error(state, fname_idx, "boolean or number expected", "write"); +} + +int df::buffer_container_identity::lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx) +{ + auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID); + void *pitem = ((uint8_t*)ptr) + idx * id->byte_size(); + return id->lua_read(state, fname_idx, pitem); +} + +void df::buffer_container_identity::lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index) +{ + auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID); + void *pitem = ((uint8_t*)ptr) + idx * id->byte_size(); + id->lua_write(state, fname_idx, pitem, val_index); +} + /* */ static int change_error(lua_State *state) @@ -253,6 +324,14 @@ static bool LookupTypeInfo(lua_State *state, bool in_method) return true; } +static void LookupInTable(lua_State *state, void *id, const char *tname) +{ + lua_getfield(state, LUA_REGISTRYINDEX, tname); + lua_pushlightuserdata(state, id); + lua_rawget(state, -2); + lua_remove(state, -2); +} + static void SaveInTable(lua_State *state, void *node, const char *tname) { // stack: [info] @@ -311,10 +390,7 @@ static void push_object_internal(lua_State *state, type_identity *type, void *pt lua_pushlightuserdata(state, type); // () -> type if (!LookupTypeInfo(state, in_method)) // type -> metatable? - { BuildTypeMetatable(state, type); // () -> metatable - SaveTypeInfo(state, type); - } push_object_ref(state, ptr); // metatable -> userdata } @@ -371,17 +447,22 @@ static int meta_ptr_compare(lua_State *state) return 1; } -static const struct_field_info *find_field(lua_State *state, int index, const char *mode) +static void lookup_field(lua_State *state, int index, const char *mode) { lua_pushvalue(state, index); - lua_rawget(state, UPVAL_FIELDTABLE); + lua_gettable(state, UPVAL_FIELDTABLE); // uses metatable with enum keys - if (!lua_islightuserdata(state, -1)) + if (lua_isnil(state, -1)) field_error(state, index, "not found", mode); +} + +static void *find_field(lua_State *state, int index, const char *mode) +{ + lookup_field(state, index, mode); void *p = lua_touserdata(state, -1); lua_pop(state, 1); - return (struct_field_info*)p; + return p; } static uint8_t *get_object_addr(lua_State *state, int obj, int field, const char *mode) @@ -399,6 +480,8 @@ static uint8_t *get_object_addr(lua_State *state, int obj, int field, const char return (uint8_t*)ref->ptr; } +static void GetAdHocMetatable(lua_State *state, const struct_field_info *field); + static int read_field(lua_State *state, const struct_field_info *field, void *ptr) { switch (field->mode) @@ -412,14 +495,21 @@ static int read_field(lua_State *state, const struct_field_info *field, void *pt case struct_field_info::PRIMITIVE: case struct_field_info::SUBSTRUCT: - case struct_field_info::CONTAINER: return field->type->lua_read(state, 2, ptr); case struct_field_info::POINTER: return df::pointer_identity::lua_read(state, 2, ptr, field->type); + case struct_field_info::CONTAINER: + if (!field->eid || !field->type->isContainer() || + field->eid == ((container_identity*)field->type)->getIndexEnumType()) + return field->type->lua_read(state, 2, ptr); + case struct_field_info::STATIC_ARRAY: case struct_field_info::STL_VECTOR_PTR: + GetAdHocMetatable(state, field); + push_object_ref(state, ptr); + return 1; case struct_field_info::END: return 0; @@ -490,7 +580,7 @@ static int get_metafield(lua_State *state) static int meta_struct_index(lua_State *state) { uint8_t *ptr = get_object_addr(state, 1, 2, "read"); - const struct_field_info *field = find_field(state, 2, "read"); + auto field = (struct_field_info*)find_field(state, 2, "read"); if (!field) return get_metafield(state); return read_field(state, field, ptr + field->offset); @@ -499,14 +589,88 @@ static int meta_struct_index(lua_State *state) static int meta_struct_newindex(lua_State *state) { uint8_t *ptr = get_object_addr(state, 1, 2, "write"); - const struct_field_info *field = find_field(state, 2, "write"); + auto field = (struct_field_info*)find_field(state, 2, "write"); write_field(state, field, ptr + field->offset, 3); return 0; } +static int meta_primitive_index(lua_State *state) +{ + uint8_t *ptr = get_object_addr(state, 1, 2, "read"); + auto type = (type_identity*)find_field(state, 2, "read"); + if (!type) + return get_metafield(state); + return type->lua_read(state, 2, ptr); +} + +static int meta_primitive_newindex(lua_State *state) +{ + uint8_t *ptr = get_object_addr(state, 1, 2, "write"); + auto type = (type_identity*)find_field(state, 2, "write"); + type->lua_write(state, 2, ptr, 3); + return 0; +} + +static int meta_container_len(lua_State *state) +{ + uint8_t *ptr = get_object_addr(state, 1, 0, "get length"); + auto id = (container_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID); + int len = id->lua_item_count(state, ptr); + lua_pushinteger(state, len); + return 1; +} + +static int lookup_container_field(lua_State *state, int field, const char *mode) +{ + if (lua_type(state, field) == LUA_TNUMBER) + return field; + + lookup_field(state, field, mode); + return -1; +} + +static int check_container_index(lua_State *state, container_identity *container, void *ptr, + int fidx, int iidx, const char *mode) +{ + if (!lua_isnumber(state, iidx)) + field_error(state, fidx, "invalid index", mode); + + int idx = lua_tointeger(state, iidx); + int len = container->lua_item_count(state, ptr); + if (idx < 0 || idx >= len) + field_error(state, fidx, "index out of bounds", mode); + + return idx; +} + +static int meta_container_index(lua_State *state) +{ + uint8_t *ptr = get_object_addr(state, 1, 2, "read"); + int iidx = lookup_container_field(state, 2, "read"); + if (lua_isuserdata(state, iidx)) + { + lua_pop(state, 1); + return get_metafield(state); + } + + auto id = (container_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID); + int idx = check_container_index(state, id, ptr, 2, iidx, "read"); + return id->lua_item_read(state, 2, ptr, idx); +} + +static int meta_container_newindex(lua_State *state) +{ + uint8_t *ptr = get_object_addr(state, 1, 2, "write"); + int iidx = lookup_container_field(state, 2, "write"); + auto id = (container_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID); + int idx = check_container_index(state, id, ptr, 2, iidx, "write"); + id->lua_item_write(state, 2, ptr, idx, 3); + return 0; +} + static int meta_global_index(lua_State *state) { - const struct_field_info *field = find_field(state, 2, "read"); + auto field = (struct_field_info*)find_field(state, 2, "read"); if (!field) return get_metafield(state); void *ptr = *(void**)field->offset; @@ -517,7 +681,7 @@ static int meta_global_index(lua_State *state) static int meta_global_newindex(lua_State *state) { - const struct_field_info *field = find_field(state, 2, "write"); + auto field = (struct_field_info*)find_field(state, 2, "write"); void *ptr = *(void**)field->offset; if (!ptr) field_error(state, 2, "global address not known", "write"); @@ -527,16 +691,16 @@ static int meta_global_newindex(lua_State *state) static void IndexFields(lua_State *state, const struct_field_info *fields) { - int base = lua_gettop(state); - lua_newtable(state); // read - lua_newtable(state); // write + // stack: read write - for (; fields->mode != struct_field_info::END; ++fields) + int base = lua_gettop(state) - 2; + + for (; fields; ++fields) { switch (fields->mode) { case struct_field_info::END: - break; + return; case struct_field_info::PRIMITIVE: case struct_field_info::STATIC_STRING: @@ -558,7 +722,13 @@ static void IndexFields(lua_State *state, const struct_field_info *fields) } } -static void SetPtrMethods(lua_State *state, int meta_idx, void *node) +static void EnableMetaField(lua_State *state, int ftable_idx, const char *name, void *id = NULL) +{ + lua_pushlightuserdata(state, id); + lua_setfield(state, ftable_idx, name); +} + +static void SetPtrMethods(lua_State *state, int meta_idx, int read_idx) { lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_COMPARE_NAME); lua_setfield(state, meta_idx, "__eq"); @@ -568,12 +738,7 @@ static void SetPtrMethods(lua_State *state, int meta_idx, void *node) lua_pushcclosure(state, meta_ptr_tostring, 2); lua_setfield(state, meta_idx, "__tostring"); - // type field - lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPEID_TABLE_NAME); - lua_pushlightuserdata(state, node); - lua_rawget(state, -2); - lua_setfield(state, meta_idx, "_type"); - lua_pop(state, 1); + EnableMetaField(state, read_idx, "_type"); } static void SetStructMethod(lua_State *state, int meta_idx, int ftable_idx, @@ -586,62 +751,199 @@ static void SetStructMethod(lua_State *state, int meta_idx, int ftable_idx, lua_setfield(state, meta_idx, name); } +static void MakeMetatable(lua_State *state, type_identity *type) +{ + int base = lua_gettop(state); + lua_newtable(state); // metatable + + lua_pushstring(state, type->getFullName().c_str()); + lua_setfield(state, base+1, "__metatable"); + + lua_pushlightuserdata(state, type); + lua_setfield(state, base+1, "_identity"); + + LookupInTable(state, type, DFHACK_TYPEID_TABLE_NAME); + if (lua_isnil(state, -1)) + { + lua_pop(state, 1); + lua_getfield(state, base+1, "__metatable"); + } + lua_setfield(state, base+1, "_type"); + + lua_newtable(state); // read + lua_newtable(state); // write +} + static void MakeFieldMetatable(lua_State *state, struct_identity *pstruct, lua_CFunction reader, lua_CFunction writer) { int base = lua_gettop(state); - lua_newtable(state); // metatable - IndexFields(state, pstruct->getFields()); // read, write + MakeMetatable(state, pstruct); // meta, read, write - lua_pushstring(state, pstruct->getName()); - lua_setfield(state, base+1, "__metatable"); + for (struct_identity *p = pstruct; p; p = p->getParent()) + { + IndexFields(state, p->getFields()); + } SetStructMethod(state, base+1, base+2, reader, "__index"); SetStructMethod(state, base+1, base+3, writer, "__newindex"); - // Custom fields + // returns: [metatable readfields writefields]; +} - lua_pushlightuserdata(state, pstruct); - lua_setfield(state, base+1, "_identity"); +static void MakePrimitiveMetatable(lua_State *state, type_identity *type) +{ + int base = lua_gettop(state); - // returns: [metatable readfields writefields]; + MakeMetatable(state, type); + SetPtrMethods(state, base+1, base+2); + + EnableMetaField(state, base+2, "value", type); + EnableMetaField(state, base+3, "value", type); + + SetStructMethod(state, base+1, base+2, meta_primitive_index, "__index"); + SetStructMethod(state, base+1, base+3, meta_primitive_newindex, "__newindex"); } -static void EnableMetaField(lua_State *state, int ftable_idx, const char *name) +static void SetContainerMethod(lua_State *state, int meta_idx, int ftable_idx, + lua_CFunction function, const char *name, + type_identity *container, type_identity *item, int count) { - lua_pushlightuserdata(state, NULL); - lua_setfield(state, ftable_idx, name); + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPETABLE_NAME); + lua_pushvalue(state, meta_idx); + lua_pushvalue(state, ftable_idx); + + lua_pushlightuserdata(state, container); + lua_pushlightuserdata(state, item); + if (count < 0) + lua_pushnil(state); + else + lua_pushinteger(state, count); + + lua_pushcclosure(state, function, 6); + lua_setfield(state, meta_idx, name); } -static void BuildTypeMetatable(lua_State *state, type_identity *type) +static void AttachEnumKeys(lua_State *state, int base, type_identity *ienum) +{ + LookupInTable(state, ienum, DFHACK_ENUM_TABLE_NAME); + + if (!lua_isnil(state, -1)) + { + lua_newtable(state); + lua_swap(state); + lua_setfield(state, -2, "__index"); + lua_dup(state); + lua_setmetatable(state, base+2); + lua_setmetatable(state, base+3); + } + else + lua_pop(state, 1); +} + +static void MakeContainerMetatable(lua_State *state, container_identity *type, + type_identity *item, int count, type_identity *ienum) { int base = lua_gettop(state); - switch (type->type()) + MakeMetatable(state, type); + SetPtrMethods(state, base+1, base+2); + + lua_pushstring(state, type->getFullName(item).c_str()); + lua_dup(state); + lua_setfield(state, base+1, "__metatable"); + lua_setfield(state, base+1, "_type"); + + lua_pushlightuserdata(state, item); + lua_setfield(state, base+1, "_field_identity"); + + if (count >= 0) { - case IDTYPE_GLOBAL: - assert(false); - - case IDTYPE_STRUCT: - case IDTYPE_CLASS: - MakeFieldMetatable(state, (struct_identity*)type, meta_struct_index, meta_struct_newindex); - SetPtrMethods(state, base+1, type); - EnableMetaField(state, base+2, "_type"); - lua_pop(state, 2); - return; + lua_pushinteger(state, count); + lua_setfield(state, base+1, "_count"); + } - case IDTYPE_PRIMITIVE: - case IDTYPE_ENUM: - case IDTYPE_POINTER: - luaL_error(state, "primitive not implemented"); + SetContainerMethod(state, base+1, base+2, meta_container_len, "__len", type, item, count); + SetContainerMethod(state, base+1, base+2, meta_container_index, "__index", type, item, count); + SetContainerMethod(state, base+1, base+3, meta_container_newindex, "__newindex", type, item, count); + + AttachEnumKeys(state, base, ienum); +} + +void type_identity::build_metatable(lua_State *state) +{ + MakePrimitiveMetatable(state, this); +} + +void container_identity::build_metatable(lua_State *state) +{ + MakeContainerMetatable(state, this, getItemType(), -1, getIndexEnumType()); +} + +void pointer_identity::build_metatable(lua_State *state) +{ + int base = lua_gettop(state); + + primitive_identity::build_metatable(state); + + EnableMetaField(state, base+2, "target", this); + EnableMetaField(state, base+3, "target", this); +} + +void struct_identity::build_metatable(lua_State *state) +{ + int base = lua_gettop(state); + MakeFieldMetatable(state, this, meta_struct_index, meta_struct_newindex); + SetPtrMethods(state, base+1, base+2); +} + +void global_identity::build_metatable(lua_State *state) +{ + MakeFieldMetatable(state, this, meta_global_index, meta_global_newindex); +} + +static void BuildTypeMetatable(lua_State *state, type_identity *type) +{ + type->build_metatable(state); + + lua_pop(state, 2); + + SaveTypeInfo(state, type); +} - case IDTYPE_BITFIELD: - luaL_error(state, "bitfield not implemented"); +static void GetAdHocMetatable(lua_State *state, const struct_field_info *field) +{ + lua_pushlightuserdata(state, (void*)field); + + if (!LookupTypeInfo(state, true)) + { + switch (field->mode) + { + case struct_field_info::CONTAINER: + { + auto ctype = (container_identity*)field->type; + MakeContainerMetatable(state, ctype, ctype->getItemType(), -1, field->eid); + break; + } + + case struct_field_info::STATIC_ARRAY: + MakeContainerMetatable(state, &df::buffer_container_identity::base_instance, + field->type, field->count, field->eid); + break; + + case struct_field_info::STL_VECTOR_PTR: + MakeContainerMetatable(state, &df::identity_traits<std::vector<void*> >::identity, + field->type, -1, field->eid); + break; + + default: + luaL_error(state, "Invalid ad-hoc field: %d", field->mode); + } - case IDTYPE_CONTAINER: - case IDTYPE_STL_PTR_VECTOR: - luaL_error(state, "container not implemented"); + lua_pop(state, 2); + + SaveTypeInfo(state, (void*)field); } } @@ -650,6 +952,7 @@ static void RenderTypeChildren(lua_State *state, const std::vector<compound_iden static void RenderType(lua_State *state, compound_identity *node) { assert(node->getName()); + std::string name = node->getFullName(); lua_newtable(state); if (!lua_checkstack(state, 20)) @@ -691,7 +994,7 @@ static void RenderType(lua_State *state, compound_identity *node) lua_setfield(state, base, "_last_item"); } - SaveTypeInfo(state, node); + SaveInTable(state, node, DFHACK_ENUM_TABLE_NAME); } break; @@ -705,10 +1008,7 @@ static void RenderType(lua_State *state, compound_identity *node) if (node->type() == IDTYPE_GLOBAL) { - auto gid = (global_identity*)node; - - MakeFieldMetatable(state, gid, meta_global_index, meta_global_newindex); - lua_pop(state, 2); + BuildTypeMetatable(state, node); lua_dup(state); lua_setmetatable(state, base); @@ -719,22 +1019,17 @@ static void RenderType(lua_State *state, compound_identity *node) lua_getfield(state, base, "__newindex"); lua_setfield(state, -2, "__newindex"); - lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPE_TOSTRING_NAME); - lua_setfield(state, -2, "__tostring"); - - lua_pop(state, 1); - lua_remove(state, base); } else { - freeze_table(state, true, node->getName()); + freeze_table(state, true, name.c_str()); + } - lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPE_TOSTRING_NAME); - lua_setfield(state, -2, "__tostring"); + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPE_TOSTRING_NAME); + lua_setfield(state, -2, "__tostring"); - lua_pop(state, 1); - } + lua_pop(state, 1); SaveInTable(state, node, DFHACK_TYPEID_TABLE_NAME); } @@ -757,6 +1052,9 @@ static void DoAttach(lua_State *state) lua_newtable(state); lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_TYPEID_TABLE_NAME); + lua_newtable(state); + lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_ENUM_TABLE_NAME); + lua_pushcfunction(state, change_error); lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_CHANGEERROR_NAME); |
