diff --git a/library/DataDefs.cpp b/library/DataDefs.cpp index c7b0d29dd..e8ee14712 100644 --- a/library/DataDefs.cpp +++ b/library/DataDefs.cpp @@ -95,17 +95,20 @@ bitfield_identity::bitfield_identity(size_t size, TAllocateFn alloc, enum_identity::enum_identity(size_t size, TAllocateFn alloc, compound_identity *scope_parent, const char *dfhack_name, + type_identity *base_type, int64_t first_item_value, int64_t last_item_value, const char *const *keys) : compound_identity(size, alloc, scope_parent, dfhack_name), - first_item_value(first_item_value), last_item_value(last_item_value), keys(keys) + first_item_value(first_item_value), last_item_value(last_item_value), + keys(keys), base_type(base_type) { } struct_identity::struct_identity(size_t size, TAllocateFn alloc, compound_identity *scope_parent, const char *dfhack_name, struct_identity *parent, const struct_field_info *fields) - : compound_identity(size, alloc, scope_parent, dfhack_name), parent(parent), has_children(false) + : compound_identity(size, alloc, scope_parent, dfhack_name), + parent(parent), has_children(false), fields(fields) { } diff --git a/library/DataStatics.cpp b/library/DataStatics.cpp index 4a77b3b1d..1e4b21be9 100644 --- a/library/DataStatics.cpp +++ b/library/DataStatics.cpp @@ -7,6 +7,8 @@ #include "df/world_data.h" #include "df/ui.h" +#include "DataIdentity.h" + namespace { template inline T &_toref(T &r) { return r; } diff --git a/library/DataStaticsFields.cpp b/library/DataStaticsFields.cpp index a9d2f3121..22ce9b6df 100644 --- a/library/DataStaticsFields.cpp +++ b/library/DataStaticsFields.cpp @@ -14,30 +14,32 @@ #pragma GCC diagnostic ignored "-Winvalid-offsetof" namespace df { -#define ATOM_IDENTITY_TRAITS(type) \ - primitive_identity identity_traits::identity(sizeof(type)); - - ATOM_IDENTITY_TRAITS(char); - ATOM_IDENTITY_TRAITS(int8_t); - ATOM_IDENTITY_TRAITS(uint8_t); - ATOM_IDENTITY_TRAITS(int16_t); - ATOM_IDENTITY_TRAITS(uint16_t); - ATOM_IDENTITY_TRAITS(int32_t); - ATOM_IDENTITY_TRAITS(uint32_t); - ATOM_IDENTITY_TRAITS(int64_t); - ATOM_IDENTITY_TRAITS(uint64_t); - ATOM_IDENTITY_TRAITS(bool); - ATOM_IDENTITY_TRAITS(float); - ATOM_IDENTITY_TRAITS(std::string); - ATOM_IDENTITY_TRAITS(void*); - -#undef ATOM_IDENTITY_TRAITS +#define NUMBER_IDENTITY_TRAITS(type) \ + number_identity identity_traits::identity; + + NUMBER_IDENTITY_TRAITS(char); + NUMBER_IDENTITY_TRAITS(int8_t); + NUMBER_IDENTITY_TRAITS(uint8_t); + NUMBER_IDENTITY_TRAITS(int16_t); + NUMBER_IDENTITY_TRAITS(uint16_t); + NUMBER_IDENTITY_TRAITS(int32_t); + NUMBER_IDENTITY_TRAITS(uint32_t); + NUMBER_IDENTITY_TRAITS(int64_t); + NUMBER_IDENTITY_TRAITS(uint64_t); + NUMBER_IDENTITY_TRAITS(float); + + bool_identity identity_traits::identity; + stl_string_identity identity_traits::identity; + pointer_identity identity_traits::identity; + stl_ptr_vector_identity identity_traits >::identity; + +#undef NUMBER_IDENTITY_TRAITS } #define TID(type) (&identity_traits< type >::identity) #define FLD(mode, name) struct_field_info::mode, #name, offsetof(CUR_STRUCT, name) -#define GFLD(mode, name) struct_field_info::mode, #name, 0 +#define GFLD(mode, name) struct_field_info::mode, #name, (size_t)&df::global::name #define FLD_END struct_field_info::END // Field definitions diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp index 3ed4a35cd..27935ae50 100644 --- a/library/LuaWrapper.cpp +++ b/library/LuaWrapper.cpp @@ -48,6 +48,155 @@ static luaL_Reg no_functions[] = { { NULL, NULL } }; inline void lua_dup(lua_State *state) { lua_pushvalue(state, -1); } inline void lua_swap(lua_State *state) { lua_insert(state, -2); } +#define UPVAL_TYPETABLE lua_upvalueindex(1) +#define UPVAL_METATABLE lua_upvalueindex(2) +#define UPVAL_FIELDTABLE lua_upvalueindex(3) + +namespace { + struct DFRefHeader { + void *ptr; + }; + + inline bool is_self_contained(DFRefHeader *ptr) { + void **pp = &ptr->ptr; + return **(void****)pp == (pp + 1); + } +} + +static void field_error(lua_State *state, int index, const char *err, const char *mode = "read") +{ + lua_getfield(state, UPVAL_METATABLE, "__metatable"); + const char *cname = lua_tostring(state, -1); + const char *fname = lua_tostring(state, index); + luaL_error(state, "Cannot %s field %s.%s: %s.", + mode, (cname ? cname : "?"), (fname ? fname : "?"), err); +} + +static int push_object_internal(lua_State *state, type_identity *type, void *ptr, bool in_method = true); +static void *get_object_internal(lua_State *state, type_identity *type, int val_index, bool in_method = true); + +int DFHack::PushDFObject(lua_State *state, type_identity *type, void *ptr) +{ + return push_object_internal(state, type, ptr, false); +} + +void *DFHack::GetDFObject(lua_State *state, type_identity *type, int val_index) +{ + return get_object_internal(state, type, val_index, false); +} + +/* Primitive identity methods */ + +int constructed_identity::lua_read(lua_State *state, int fname_idx, void *ptr) +{ + return push_object_internal(state, this, ptr); +} + +void constructed_identity::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) +{ + field_error(state, fname_idx, "complex object", "write"); +} + +int enum_identity::lua_read(lua_State *state, int fname_idx, void *ptr) +{ + return base_type->lua_read(state, fname_idx, ptr); +} + +void enum_identity::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) +{ + base_type->lua_write(state, fname_idx, ptr, val_index); +} + +int df::number_identity_base::lua_read(lua_State *state, int fname_idx, void *ptr) +{ + lua_pushnumber(state, read(ptr)); + return 1; +} + +void df::number_identity_base::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) +{ + if (!lua_isnumber(state, val_index)) + field_error(state, fname_idx, "number expected", "write"); + + write(ptr, lua_tonumber(state, val_index)); +} + +int df::bool_identity::lua_read(lua_State *state, int fname_idx, void *ptr) +{ + lua_pushboolean(state, *(bool*)ptr); + return 1; +} + +void df::bool_identity::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) +{ + char *pb = (char*)ptr; + + if (lua_isboolean(state, val_index)) + *pb = lua_toboolean(state, val_index); + else if (lua_isnumber(state, val_index)) + *pb = lua_tonumber(state, val_index); + else + field_error(state, fname_idx, "boolean or number expected", "write"); +} + +int df::stl_string_identity::lua_read(lua_State *state, int fname_idx, void *ptr) +{ + auto pstr = (std::string*)ptr; + lua_pushlstring(state, pstr->data(), pstr->size()); + return 1; +} + +void df::stl_string_identity::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) +{ + size_t size; + const char *bytes = lua_tolstring(state, val_index, &size); + if (!bytes) + field_error(state, fname_idx, "string expected", "write"); + + *(std::string*)ptr = std::string(bytes, size); +} + +static int do_read_pointer(lua_State *state, int, void *ptr, type_identity *target) +{ + void *val = *(void**)ptr; + + if (val == NULL) + { + lua_pushnil(state); + return 1; + } + else + return push_object_internal(state, target, val); +} + +int df::pointer_identity::lua_read(lua_State *state, int fname_idx, void *ptr) +{ + return do_read_pointer(state, fname_idx, ptr, target); +} + +static void do_write_pointer(lua_State *state, int fname_idx, void *ptr, type_identity *target, int val_index) +{ + auto pptr = (void**)ptr; + + if (lua_isnil(state, val_index)) + *pptr = NULL; + else + { + void *nval = get_object_internal(state, target, val_index); + if (nval) + *pptr = nval; + else + field_error(state, fname_idx, "incompatible pointer type", "write"); + } +} + +void df::pointer_identity::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) +{ + do_write_pointer(state, fname_idx, ptr, target, val_index); +} + +/* */ + static int change_error(lua_State *state) { luaL_error(state, "Attempt to change a read-only table.\n"); @@ -111,6 +260,169 @@ static bool RegisterTypeInfo(lua_State *state, void *node) return added; } +static const struct_field_info *find_field(lua_State *state, int index, const char *mode = "read") +{ + lua_pushvalue(state, index); + lua_rawget(state, UPVAL_FIELDTABLE); + + if (!lua_islightuserdata(state, -1)) + field_error(state, index, "not found"); + + void *p = lua_touserdata(state, -1); + lua_pop(state, 1); + return (struct_field_info*)p; +} + +static int read_field(lua_State *state, const struct_field_info *field, void *ptr) +{ + switch (field->mode) + { + case struct_field_info::STATIC_STRING: + { + int len = strnlen((char*)ptr, field->count); + lua_pushlstring(state, (char*)ptr, len); + return 1; + } + + 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 do_read_pointer(state, 2, ptr, field->type); + + case struct_field_info::STATIC_ARRAY: + case struct_field_info::STL_VECTOR_PTR: + + case struct_field_info::END: + return 0; + } +} + +static void write_field(lua_State *state, const struct_field_info *field, void *ptr) +{ + switch (field->mode) + { + case struct_field_info::STATIC_STRING: + { + size_t size; + const char *str = lua_tolstring(state, -1, &size); + if (!str) + field_error(state, 2, "string expected", "write"); + memcpy(ptr, str, std::min(size+1, size_t(field->count))); + return; + } + + case struct_field_info::PRIMITIVE: + case struct_field_info::SUBSTRUCT: + case struct_field_info::CONTAINER: + field->type->lua_write(state, 2, ptr, 3); + return; + + case struct_field_info::POINTER: + do_write_pointer(state, 2, ptr, field->type, 3); + + case struct_field_info::STATIC_ARRAY: + case struct_field_info::STL_VECTOR_PTR: + field_error(state, 2, "complex object", "write"); + + case struct_field_info::END: + return; + } +} + +static int meta_global_index(lua_State *state) +{ + const struct_field_info *field = find_field(state, 2); + void *ptr = *(void**)field->offset; + if (!ptr) + field_error(state, 2, "global address not known"); + return read_field(state, field, ptr); +} + +static int meta_global_newindex(lua_State *state) +{ + const struct_field_info *field = find_field(state, 2); + void *ptr = *(void**)field->offset; + if (!ptr) + field_error(state, 2, "global address not known", "write"); + write_field(state, field, ptr); + return 0; +} + +static void IndexFields(lua_State *state, const struct_field_info *fields) +{ + int base = lua_gettop(state); + lua_newtable(state); // read + lua_newtable(state); // write + + for (; fields->mode != struct_field_info::END; ++fields) + { + switch (fields->mode) + { + case struct_field_info::END: + break; + + case struct_field_info::PRIMITIVE: + case struct_field_info::STATIC_STRING: + case struct_field_info::POINTER: + lua_pushstring(state,fields->name); + lua_pushlightuserdata(state,(void*)fields); + lua_settable(state,base+2); + // fallthrough + + case struct_field_info::STATIC_ARRAY: + case struct_field_info::SUBSTRUCT: + case struct_field_info::CONTAINER: + case struct_field_info::STL_VECTOR_PTR: + lua_pushstring(state,fields->name); + lua_pushlightuserdata(state,(void*)fields); + lua_settable(state,base+1); + break; + } + } +} + +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 + + lua_pushstring(state, pstruct->getName()); + lua_setfield(state, base+1, "__metatable"); + + lua_pushlightuserdata(state, pstruct); + lua_setfield(state, base+1, "_identity"); + + lua_getfield(state, LUA_REGISTRYINDEX, "DFHack::DFTypes"); + lua_pushvalue(state, base+1); + lua_pushvalue(state, base+2); + lua_pushcclosure(state, reader, 3); + lua_setfield(state, base+1, "__index"); + + lua_getfield(state, LUA_REGISTRYINDEX, "DFHack::DFTypes"); + lua_pushvalue(state, base+1); + lua_pushvalue(state, base+3); + lua_pushcclosure(state, writer, 3); + lua_setfield(state, base+1, "__newindex"); + + // returns: [metatable readfields writefields]; +} + +static int push_object_internal(lua_State *state, type_identity *type, void *ptr, bool in_method) +{ + return 0; +} + +static void *get_object_internal(lua_State *state, type_identity *type, int val_index, bool in_method) +{ + return NULL; +} + static void RenderTypeChildren(lua_State *state, const std::vector &children); static void RenderType(lua_State *state, compound_identity *node) @@ -150,13 +462,11 @@ static void RenderType(lua_State *state, compound_identity *node) if (eid->getFirstItem() <= eid->getLastItem()) { - lua_pushstring(state, "_first_item"); lua_pushinteger(state, eid->getFirstItem()); - lua_settable(state, base); + lua_setfield(state, base, "_first_item"); - lua_pushstring(state, "_last_item"); lua_pushinteger(state, eid->getLastItem()); - lua_settable(state, base); + lua_setfield(state, base, "_last_item"); } SaveTypeInfo(state, node); @@ -171,7 +481,26 @@ static void RenderType(lua_State *state, compound_identity *node) assert(base == lua_gettop(state)); - freeze_table(state, false, node->getName()); + if (node->type() == IDTYPE_GLOBAL) + { + auto gid = (global_identity*)node; + + MakeFieldMetatable(state, gid, meta_global_index, meta_global_newindex); + lua_pop(state, 2); + + lua_dup(state); + lua_setmetatable(state, base); + lua_swap(state); // -> meta curtable + + freeze_table(state, true, "global"); + lua_getfield(state, base, "__newindex"); + lua_setfield(state, -2, "__newindex"); + lua_pop(state, 1); + + lua_remove(state, base); + } + else + freeze_table(state, false, node->getName()); } static void RenderTypeChildren(lua_State *state, const std::vector &children) diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index 3a7679b65..df3af1bb4 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -50,11 +50,13 @@ namespace DFHack enum identity_type { IDTYPE_GLOBAL, IDTYPE_PRIMITIVE, + IDTYPE_POINTER, IDTYPE_CONTAINER, IDTYPE_BITFIELD, IDTYPE_ENUM, IDTYPE_STRUCT, - IDTYPE_CLASS + IDTYPE_CLASS, + IDTYPE_STL_PTR_VECTOR }; typedef void *(*TAllocateFn)(void*,const void*); @@ -80,6 +82,9 @@ namespace DFHack size_t byte_size() { return size; } virtual identity_type type() = 0; + + virtual int lua_read(lua_State *state, int fname_idx, void *ptr) = 0; + virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) = 0; }; class DFHACK_EXPORT constructed_identity : public type_identity { @@ -96,6 +101,9 @@ namespace DFHack if (allocator) allocator(tgt,src); else type_identity::do_copy(tgt, src); }; + + virtual int lua_read(lua_State *state, int fname_idx, void *ptr); + virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index); }; class DFHACK_EXPORT compound_identity : public constructed_identity { @@ -149,9 +157,12 @@ namespace DFHack int64_t first_item_value; int64_t last_item_value; + type_identity *base_type; + public: enum_identity(size_t size, TAllocateFn alloc, compound_identity *scope_parent, const char *dfhack_name, + type_identity *base_type, int64_t first_item_value, int64_t last_item_value, const char *const *keys); @@ -161,6 +172,11 @@ namespace DFHack int64_t getLastItem() { return last_item_value; } int getCount() { return int(last_item_value-first_item_value+1); } const char *const *getKeys() { return keys; } + + type_identity *getBaseType() { return base_type; } + + virtual int lua_read(lua_State *state, int fname_idx, void *ptr); + virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index); }; struct struct_field_info { @@ -187,6 +203,8 @@ namespace DFHack std::vector children; bool has_children; + const struct_field_info *fields; + protected: virtual void doInit(Core *core); @@ -201,6 +219,8 @@ namespace DFHack const std::vector &getChildren() { return children; } bool hasChildren() { return has_children; } + const struct_field_info *getFields() { return fields; } + bool is_subclass(struct_identity *subtype); }; @@ -286,6 +306,9 @@ namespace DFHack DFHACK_EXPORT void AttachDFGlobals(lua_State *state); + DFHACK_EXPORT int PushDFObject(lua_State *state, type_identity *type, void *ptr); + DFHACK_EXPORT void *GetDFObject(lua_State *state, type_identity *type, int val_index); + template T *ifnull(T *a, T *b) { return a ? a : b; } diff --git a/library/include/DataIdentity.h b/library/include/DataIdentity.h index d354beb03..4443518ab 100644 --- a/library/include/DataIdentity.h +++ b/library/include/DataIdentity.h @@ -44,6 +44,21 @@ namespace DFHack virtual identity_type type() { return IDTYPE_PRIMITIVE; } }; + class DFHACK_EXPORT pointer_identity : public primitive_identity { + type_identity *target; + + public: + pointer_identity(type_identity *target = NULL) + : primitive_identity(sizeof(void*)), target(target) {}; + + virtual identity_type type() { return IDTYPE_POINTER; } + + type_identity *getTarget() { return target; } + + virtual int lua_read(lua_State *state, int fname_idx, void *ptr); + virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index); + }; + class DFHACK_EXPORT container_identity : public constructed_identity { type_identity *item; enum_identity *ienum; @@ -53,35 +68,101 @@ namespace DFHack : constructed_identity(size, alloc), item(item), ienum(ienum) {}; virtual identity_type type() { return IDTYPE_CONTAINER; } + + type_identity *getItemType() { return item; } }; } namespace df { using DFHack::primitive_identity; + using DFHack::pointer_identity; using DFHack::container_identity; -#define ATOM_IDENTITY_TRAITS(type) \ + class number_identity_base : public primitive_identity { + public: + number_identity_base(size_t size) : primitive_identity(size) {}; + + virtual int lua_read(lua_State *state, int fname_idx, void *ptr); + virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index); + + protected: + virtual double read(void *ptr) = 0; + virtual void write(void *ptr, double val) = 0; + }; + + template + class number_identity : public number_identity_base { + public: + number_identity() : number_identity_base(sizeof(T)) {} + protected: + virtual double read(void *ptr) { return double(*(T*)ptr); } + virtual void write(void *ptr, double val) { *(T*)ptr = T(val); } + }; + + class bool_identity : public primitive_identity { + public: + bool_identity() : primitive_identity(sizeof(bool)) {}; + + virtual int lua_read(lua_State *state, int fname_idx, void *ptr); + virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index); + }; + + class stl_string_identity : public primitive_identity { + public: + stl_string_identity() : primitive_identity(sizeof(std::string)) {}; + + virtual int lua_read(lua_State *state, int fname_idx, void *ptr); + virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index); + }; + + class stl_ptr_vector_identity : public container_identity { + public: + stl_ptr_vector_identity(type_identity *item = NULL, enum_identity *ienum = NULL) + : container_identity(sizeof(std::vector),allocator_fn >,item, ienum) + {}; + + virtual DFHack::identity_type type() { return DFHack::IDTYPE_STL_PTR_VECTOR; } + }; + +#define NUMBER_IDENTITY_TRAITS(type) \ template<> struct identity_traits { \ - static primitive_identity identity; \ - static primitive_identity *get() { return &identity; } \ - }; - - ATOM_IDENTITY_TRAITS(char); - ATOM_IDENTITY_TRAITS(int8_t); - ATOM_IDENTITY_TRAITS(uint8_t); - ATOM_IDENTITY_TRAITS(int16_t); - ATOM_IDENTITY_TRAITS(uint16_t); - ATOM_IDENTITY_TRAITS(int32_t); - ATOM_IDENTITY_TRAITS(uint32_t); - ATOM_IDENTITY_TRAITS(int64_t); - ATOM_IDENTITY_TRAITS(uint64_t); - ATOM_IDENTITY_TRAITS(bool); - ATOM_IDENTITY_TRAITS(float); - ATOM_IDENTITY_TRAITS(std::string); - ATOM_IDENTITY_TRAITS(void*); - -#undef ATOM_IDENTITY_TRAITS + static number_identity identity; \ + static number_identity_base *get() { return &identity; } \ + }; + + NUMBER_IDENTITY_TRAITS(char); + NUMBER_IDENTITY_TRAITS(int8_t); + NUMBER_IDENTITY_TRAITS(uint8_t); + NUMBER_IDENTITY_TRAITS(int16_t); + NUMBER_IDENTITY_TRAITS(uint16_t); + NUMBER_IDENTITY_TRAITS(int32_t); + NUMBER_IDENTITY_TRAITS(uint32_t); + NUMBER_IDENTITY_TRAITS(int64_t); + NUMBER_IDENTITY_TRAITS(uint64_t); + NUMBER_IDENTITY_TRAITS(float); + + template<> struct identity_traits { + static bool_identity identity; + static bool_identity *get() { return &identity; } + }; + + template<> struct identity_traits { + static stl_string_identity identity; + static stl_string_identity *get() { return &identity; } + }; + + template<> struct identity_traits { + static pointer_identity identity; + static pointer_identity *get() { return &identity; } + }; + + template<> struct identity_traits > { + static stl_ptr_vector_identity identity; + static stl_ptr_vector_identity *get() { return &identity; } + }; + +#undef NUMBER_IDENTITY_TRAITS // Container declarations @@ -90,7 +171,7 @@ namespace df }; template struct identity_traits { - static container_identity *get(); + static pointer_identity *get(); }; template struct identity_traits { @@ -101,6 +182,10 @@ namespace df static container_identity *get(); }; + template struct identity_traits > { + static stl_ptr_vector_identity *get(); + }; + template struct identity_traits > { static container_identity *get(); }; @@ -117,15 +202,12 @@ namespace df template primitive_identity *identity_traits >::get() { - static primitive_identity identity(sizeof(FT)); - return &identity; + return identity_traits::get(); } template - container_identity *identity_traits::get() { - typedef T * container; - static container_identity identity(sizeof(container), &allocator_fn, - identity_traits::get()); + pointer_identity *identity_traits::get() { + static pointer_identity identity(identity_traits::get()); return &identity; } @@ -145,6 +227,12 @@ namespace df return &identity; } + template + stl_ptr_vector_identity *identity_traits >::get() { + static stl_ptr_vector_identity identity(identity_traits::get()); + return &identity; + } + template container_identity *identity_traits >::get() { typedef std::deque container; diff --git a/library/xml b/library/xml index 70eb6b5f3..c90a2d499 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 70eb6b5f35680655d04d9fda79ff7251e21b45ae +Subproject commit c90a2d499024319ea9aa4f98b3b61df7bba2fc62