From bfb226b92ea9021c199d81e189c35d01c6f1f1a0 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Mon, 19 Mar 2012 11:33:30 +0400 Subject: [PATCH 01/16] Add a few comments. --- library/proto/BasicApi.proto | 8 ++++++++ library/proto/CoreProtocol.proto | 6 +++++- plugins/proto/rename.proto | 2 ++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/library/proto/BasicApi.proto b/library/proto/BasicApi.proto index 7b105ee88..137f25726 100644 --- a/library/proto/BasicApi.proto +++ b/library/proto/BasicApi.proto @@ -4,6 +4,10 @@ option optimize_for = LITE_RUNTIME; import "Basic.proto"; +// RPC GetVersion : EmptyMessage -> StringMessage +// RPC GetDFVersion : EmptyMessage -> StringMessage + +// RPC GetWorldInfo : EmptyMessage -> GetWorldInfoOut message GetWorldInfoOut { enum Mode { MODE_DWARF = 1; @@ -27,6 +31,7 @@ message GetWorldInfoOut { repeated int32 companion_histfig_ids = 10; }; +// RPC ListEnums : EmptyMessage -> ListEnumsOut message ListEnumsOut { repeated EnumItemName material_flags = 1; repeated EnumItemName inorganic_flags = 2; @@ -46,6 +51,7 @@ message ListEnumsOut { repeated EnumItemName profession = 11; }; +// RPC ListMaterials : ListMaterialsIn -> ListMaterialsOut message ListMaterialsIn { optional BasicMaterialInfoMask mask = 1; @@ -62,6 +68,7 @@ message ListMaterialsOut { repeated BasicMaterialInfo value = 1; }; +// RPC ListUnits : ListUnitsIn -> ListUnitsOut message ListUnitsIn { optional BasicUnitInfoMask mask = 1; @@ -81,6 +88,7 @@ message ListUnitsOut { repeated BasicUnitInfo value = 1; }; +// RPC ListSquads : ListSquadsIn -> ListSquadsOut message ListSquadsIn {} message ListSquadsOut { repeated BasicSquadInfo value = 1; diff --git a/library/proto/CoreProtocol.proto b/library/proto/CoreProtocol.proto index 6f3b84510..92d7c48d9 100644 --- a/library/proto/CoreProtocol.proto +++ b/library/proto/CoreProtocol.proto @@ -62,18 +62,22 @@ message StringListMessage { repeated string value = 1; } +// RPC BindMethod : CoreBindRequest -> CoreBindReply message CoreBindRequest { required string method = 1; required string input_msg = 2; required string output_msg = 3; optional string plugin = 4; } - message CoreBindReply { required int32 assigned_id = 1; } +// RPC RunCommand : CoreRunCommandRequest -> EmptyMessage message CoreRunCommandRequest { required string command = 1; repeated string arguments = 2; } + +// RPC CoreSuspend : EmptyMessage -> IntMessage +// RPC CoreResume : EmptyMessage -> IntMessage diff --git a/plugins/proto/rename.proto b/plugins/proto/rename.proto index a6c3b055a..aa1e95f48 100644 --- a/plugins/proto/rename.proto +++ b/plugins/proto/rename.proto @@ -2,6 +2,7 @@ package dfproto; option optimize_for = LITE_RUNTIME; +// RPC RenameSquad : RenameSquadIn -> EmptyMessage message RenameSquadIn { required int32 squad_id = 1; @@ -9,6 +10,7 @@ message RenameSquadIn { optional string alias = 3; } +// RPC RenameUnit : RenameUnitIn -> EmptyMessage message RenameUnitIn { required int32 unit_id = 1; From 296d3a0af306a5247048ed4f38b2bc72eb582d89 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Mon, 19 Mar 2012 16:59:11 +0400 Subject: [PATCH 02/16] Skeleton type metadata for future use in lua bindings. --- library/CMakeLists.txt | 9 ++ library/DataDefs.cpp | 145 +++++++++++++------- library/DataStatics.cpp | 2 + library/DataStaticsFields.cpp | 23 ++++ library/include/DataDefs.h | 235 +++++++++++++++++++++++++++------ library/include/DataIdentity.h | 175 ++++++++++++++++++++++++ library/xml | 2 +- 7 files changed, 502 insertions(+), 89 deletions(-) create mode 100644 library/DataStaticsFields.cpp create mode 100644 library/include/DataIdentity.h diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 63107456a..7d3c4d6bb 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -57,6 +57,7 @@ ColorText.cpp DataDefs.cpp DataStatics.cpp DataStaticsCtor.cpp +DataStaticsFields.cpp MiscUtils.cpp PluginManager.cpp TileTypes.cpp @@ -195,6 +196,14 @@ ADD_CUSTOM_COMMAND( ADD_CUSTOM_TARGET(generate_headers DEPENDS ${dfapi_SOURCE_DIR}/include/df/static.inc) +IF(UNIX) + # Don't produce debug info for generated stubs + SET_SOURCE_FILES_PROPERTIES(DataStatics.cpp DataStaticsCtor.cpp DataStaticsFields.cpp + PROPERTIES COMPILE_FLAGS "-g0") +ELSE(WIN32) +ENDIF() + + # Compilation ADD_DEFINITIONS(-DBUILD_DFHACK_LIB) diff --git a/library/DataDefs.cpp b/library/DataDefs.cpp index 30a4da271..c7b0d29dd 100644 --- a/library/DataDefs.cpp +++ b/library/DataDefs.cpp @@ -44,18 +44,107 @@ using namespace DFHack; * this list has to be plain data, so that it gets * initialized by the loader in the initial mmap. */ -virtual_identity *virtual_identity::list = NULL; +compound_identity *compound_identity::list = NULL; +std::vector compound_identity::top_scope; -virtual_identity::virtual_identity(const char *dfhack_name, const char *original_name, virtual_identity *parent) - : dfhack_name(dfhack_name), original_name(original_name), parent(parent), - prev(NULL), vtable_ptr(NULL), has_children(true) +compound_identity::compound_identity(size_t size, TAllocateFn alloc, + compound_identity *scope_parent, const char *dfhack_name) + : constructed_identity(size, alloc), scope_parent(scope_parent), dfhack_name(dfhack_name) { - // Link into the static list. Nothing else can be safely done at this point. - next = list; list = this; + next = list; list = this; +} + +void compound_identity::doInit(Core *) +{ + if (scope_parent) + scope_parent->scope_children.push_back(this); + else + top_scope.push_back(this); } -/* Vtable to identity lookup. */ static tthread::mutex *known_mutex = NULL; + +void compound_identity::Init(Core *core) +{ + if (!known_mutex) + known_mutex = new tthread::mutex(); + + // This cannot be done in the constructors, because + // they are called in an undefined order. + for (compound_identity *p = list; p; p = p->next) + p->doInit(core); + + //FIXME: ... nuked. the group was empty... +/* + // Read pre-filled vtable ptrs + OffsetGroup *ptr_table = core->vinfo->getGroup("vtable"); + for (virtual_identity *p = list; p; p = p->next) { + void * tmp; + if (ptr_table->getSafeAddress(p->getName(),tmp)) + p->vtable_ptr = tmp; + } + */ +} + +bitfield_identity::bitfield_identity(size_t size, TAllocateFn alloc, + compound_identity *scope_parent, const char *dfhack_name, + int num_bits, const bitfield_item_info *bits) + : compound_identity(size, alloc, scope_parent, dfhack_name), bits(bits), num_bits(num_bits) +{ +} + +enum_identity::enum_identity(size_t size, TAllocateFn alloc, + compound_identity *scope_parent, const char *dfhack_name, + 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) +{ +} + +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) +{ +} + +void struct_identity::doInit(Core *core) +{ + compound_identity::doInit(core); + + if (parent) { + parent->children.push_back(this); + parent->has_children = true; + } +} + +bool struct_identity::is_subclass(struct_identity *actual) +{ + for (; actual; actual = actual->getParent()) + if (actual == this) return true; + + return false; +} + +virtual_identity::virtual_identity(size_t size, TAllocateFn alloc, + const char *dfhack_name, const char *original_name, + virtual_identity *parent, const struct_field_info *fields) + : struct_identity(size, alloc, NULL, dfhack_name, parent, fields), original_name(original_name), + vtable_ptr(NULL) +{ +} + +static std::map name_lookup; + +void virtual_identity::doInit(Core *core) +{ + struct_identity::doInit(core); + + name_lookup[getOriginalName()] = this; +} + +/* Vtable to identity lookup. */ std::map virtual_identity::known; virtual_identity *virtual_identity::get(virtual_ptr instance_ptr) @@ -78,8 +167,9 @@ virtual_identity *virtual_identity::get(virtual_ptr instance_ptr) virtual_identity *actual = NULL; - for (virtual_identity *p = list; p; p = p->next) { - if (strcmp(name.c_str(), p->getOriginalName()) != 0) continue; + auto name_it = name_lookup.find(name); + if (name_it != name_lookup.end()) { + virtual_identity *p = name_it->second; if (p->vtable_ptr && p->vtable_ptr != vtable) { std::cerr << "Conflicting vtable ptr for class '" << p->getName() @@ -103,14 +193,6 @@ virtual_identity *virtual_identity::get(virtual_ptr instance_ptr) return NULL; } -bool virtual_identity::is_subclass(virtual_identity *actual) -{ - for (; actual; actual = actual->parent) - if (actual == this) return true; - - return false; -} - void virtual_identity::adjust_vtable(virtual_ptr obj, virtual_identity *main) { if (vtable_ptr) { @@ -135,35 +217,6 @@ virtual_ptr virtual_identity::clone(virtual_ptr obj) return copy; } -void virtual_identity::Init(Core *core) -{ - if (!known_mutex) - known_mutex = new tthread::mutex(); - - // This cannot be done in the constructors, because - // they are called in an undefined order. - for (virtual_identity *p = list; p; p = p->next) { - p->has_children = false; - p->children.clear(); - } - for (virtual_identity *p = list; p; p = p->next) { - if (p->parent) { - p->parent->children.push_back(p); - p->parent->has_children = true; - } - } - //FIXME: ... nuked. the group was empty... -/* - // Read pre-filled vtable ptrs - OffsetGroup *ptr_table = core->vinfo->getGroup("vtable"); - for (virtual_identity *p = list; p; p = p->next) { - void * tmp; - if (ptr_table->getSafeAddress(p->getName(),tmp)) - p->vtable_ptr = tmp; - } - */ -} - bool DFHack::findBitfieldField(unsigned *idx, const std::string &name, unsigned size, const bitfield_item_info *items) { diff --git a/library/DataStatics.cpp b/library/DataStatics.cpp index 5ccabb7e1..4a77b3b1d 100644 --- a/library/DataStatics.cpp +++ b/library/DataStatics.cpp @@ -21,6 +21,8 @@ namespace { #define INIT_GLOBAL_FUNCTION_ITEM(type,name) \ if (global_table_->getAddress(#name,tmp_)) name = (type*)tmp_; +#define TID(type) (&identity_traits< type >::identity) + // Instantiate all the static objects #include "df/static.inc" #include "df/static.enums.inc" diff --git a/library/DataStaticsFields.cpp b/library/DataStaticsFields.cpp new file mode 100644 index 000000000..0e6e9957e --- /dev/null +++ b/library/DataStaticsFields.cpp @@ -0,0 +1,23 @@ +#include "Internal.h" +#include "DataDefs.h" +#include "MiscUtils.h" +#include "VersionInfo.h" + +#include "df/world.h" +#include "df/world_data.h" +#include "df/ui.h" + +#include "DataIdentity.h" + +#include + +#pragma GCC diagnostic ignored "-Winvalid-offsetof" + +#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 FLD_END struct_field_info::END + +// Field definitions +#include "df/static.fields.inc" diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index 99694d949..2a9567182 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -45,47 +45,205 @@ namespace DFHack { class virtual_class {}; + enum identity_type { + IDTYPE_GLOBAL, + IDTYPE_PRIMITIVE, + IDTYPE_CONTAINER, + IDTYPE_BITFIELD, + IDTYPE_ENUM, + IDTYPE_STRUCT, + IDTYPE_CLASS + }; + + typedef void *(*TAllocateFn)(void*,const void*); + + class DFHACK_EXPORT type_identity { + size_t size; + + protected: + type_identity(size_t size) : size(size) {}; + + virtual void *do_instantiate() { + void *p = malloc(size); + memset(p, 0, size); + return p; + } + virtual void do_copy(void *tgt, const void *src) { + memmove(tgt, src, size); + }; + + public: + virtual ~type_identity() {} + + size_t byte_size() { return size; } + + virtual identity_type type() = 0; + }; + + class DFHACK_EXPORT constructed_identity : public type_identity { + TAllocateFn allocator; + + protected: + constructed_identity(size_t size, TAllocateFn alloc) + : type_identity(size), allocator(alloc) {}; + + virtual void *do_instantiate() { + return allocator ? allocator(NULL,NULL) : type_identity::do_instantiate(); + } + virtual void do_copy(void *tgt, const void *src) { + if (allocator) allocator(tgt,src); + else type_identity::do_copy(tgt, src); + }; + }; + + class DFHACK_EXPORT compound_identity : public constructed_identity { + static compound_identity *list; + compound_identity *next; + + const char *dfhack_name; + compound_identity *scope_parent; + std::vector scope_children; + static std::vector top_scope; + + protected: + compound_identity(size_t size, TAllocateFn alloc, + compound_identity *scope_parent, const char *dfhack_name); + + virtual void doInit(Core *core); + + public: + const char *getName() { return dfhack_name; } + + compound_identity *getScopeParent() { return scope_parent; } + const std::vector &getScopeChildren() { return scope_children; } + static const std::vector &getTopScope() { return top_scope; } + + static void Init(Core *core); + }; + + // Bitfields + struct bitfield_item_info { + const char *name; + int size; + }; + + class DFHACK_EXPORT bitfield_identity : public compound_identity { + const bitfield_item_info *bits; + int num_bits; + + public: + bitfield_identity(size_t size, TAllocateFn alloc, + compound_identity *scope_parent, const char *dfhack_name, + int num_bits, const bitfield_item_info *bits); + + virtual identity_type type() { return IDTYPE_BITFIELD; } + + int getNumBits() { return num_bits; } + const bitfield_item_info *getBits() { return bits; } + }; + + class DFHACK_EXPORT enum_identity : public compound_identity { + const char *const *keys; + int64_t first_item_value; + int64_t last_item_value; + + public: + enum_identity(size_t size, TAllocateFn alloc, + compound_identity *scope_parent, const char *dfhack_name, + int64_t first_item_value, int64_t last_item_value, + const char *const *keys); + + virtual identity_type type() { return IDTYPE_ENUM; } + + int getCount() { return int(last_item_value-first_item_value+1); } + const char *const *getKeys() { return keys; } + }; + + struct struct_field_info { + enum Mode { + END, + PRIMITIVE, + STATIC_STRING, + POINTER, + STATIC_ARRAY, + SUBSTRUCT, + CONTAINER, + STL_VECTOR_PTR + }; + Mode mode; + const char *name; + size_t offset; + type_identity *type; + size_t count; + enum_identity *eid; + }; + + class DFHACK_EXPORT struct_identity : public compound_identity { + struct_identity *parent; + std::vector children; + bool has_children; + + protected: + virtual void doInit(Core *core); + + public: + struct_identity(size_t size, TAllocateFn alloc, + compound_identity *scope_parent, const char *dfhack_name, + struct_identity *parent, const struct_field_info *fields); + + virtual identity_type type() { return IDTYPE_STRUCT; } + + struct_identity *getParent() { return parent; } + const std::vector &getChildren() { return children; } + bool hasChildren() { return has_children; } + + bool is_subclass(struct_identity *subtype); + }; + + class DFHACK_EXPORT global_identity : public struct_identity { + public: + global_identity(const struct_field_info *fields) + : struct_identity(0,NULL,NULL,"global",NULL,fields) {} + + virtual identity_type type() { return IDTYPE_GLOBAL; } + }; + #ifdef _MSC_VER typedef void *virtual_ptr; #else typedef virtual_class *virtual_ptr; #endif - class DFHACK_EXPORT virtual_identity { - static virtual_identity *list; + class DFHACK_EXPORT virtual_identity : public struct_identity { static std::map known; - - virtual_identity *prev, *next; - const char *dfhack_name; + const char *original_name; - virtual_identity *parent; - std::vector children; - + void *vtable_ptr; - bool has_children; protected: - virtual_identity(const char *dfhack_name, const char *original_name, virtual_identity *parent); + virtual void doInit(Core *core); static void *get_vtable(virtual_ptr instance_ptr) { return *(void**)instance_ptr; } public: - const char *getName() { return dfhack_name; } - const char *getOriginalName() { return original_name ? original_name : dfhack_name; } + virtual_identity(size_t size, TAllocateFn alloc, + const char *dfhack_name, const char *original_name, + virtual_identity *parent, const struct_field_info *fields); + + virtual identity_type type() { return IDTYPE_CLASS; } - virtual_identity *getParent() { return parent; } - const std::vector &getChildren() { return children; } + const char *getOriginalName() { return original_name ? original_name : getName(); } public: static virtual_identity *get(virtual_ptr instance_ptr); - - bool is_subclass(virtual_identity *subtype); + bool is_instance(virtual_ptr instance_ptr) { if (!instance_ptr) return false; if (vtable_ptr) { void *vtable = get_vtable(instance_ptr); if (vtable == vtable_ptr) return true; - if (!has_children) return false; + if (!hasChildren()) return false; } return is_subclass(get(instance_ptr)); } @@ -98,15 +256,10 @@ namespace DFHack public: bool can_instantiate() { return (vtable_ptr != NULL); } - virtual_ptr instantiate() { return can_instantiate() ? do_instantiate() : NULL; } + virtual_ptr instantiate() { return can_instantiate() ? (virtual_ptr)do_instantiate() : NULL; } static virtual_ptr clone(virtual_ptr obj); - protected: - virtual virtual_ptr do_instantiate() = 0; - virtual void do_copy(virtual_ptr tgt, virtual_ptr src) = 0; public: - static void Init(Core *core); - // Strictly for use in virtual class constructors void adjust_vtable(virtual_ptr obj, virtual_identity *main); }; @@ -135,12 +288,6 @@ namespace DFHack size_t size; const T *items; }; - - // Bitfields - struct bitfield_item_info { - const char *name; - int size; - }; } template @@ -164,33 +311,37 @@ inline int linear_index(const DFHack::enum_list_attr &lst, const st namespace df { + using DFHack::type_identity; + using DFHack::compound_identity; using DFHack::virtual_ptr; using DFHack::virtual_identity; using DFHack::virtual_class; + using DFHack::global_identity; + using DFHack::struct_identity; + using DFHack::struct_field_info; using DFHack::bitfield_item_info; + using DFHack::bitfield_identity; + using DFHack::enum_identity; using DFHack::enum_list_attr; using DFHack::BitArray; using DFHack::DfArray; template - struct enum_traits {}; + void *allocator_fn(void *out, const void *in) { + if (out) { *(T*)out = *(const T*)in; return out; } + else return new T(); + } template - struct bitfield_traits {}; + struct identity_traits { + static compound_identity *get() { return &T::_identity; } + }; template - class class_virtual_identity : public virtual_identity { - public: - class_virtual_identity(const char *dfhack_name, const char *original_name, virtual_identity *parent) - : virtual_identity(dfhack_name, original_name, parent) {}; - - T *instantiate() { return static_cast(virtual_identity::instantiate()); } - T *clone(T* obj) { return static_cast(virtual_identity::clone(obj)); } + struct enum_traits {}; - protected: - virtual virtual_ptr do_instantiate() { return new T(); } - virtual void do_copy(virtual_ptr tgt, virtual_ptr src) { *static_cast(tgt) = *static_cast(src); } - }; + template + struct bitfield_traits {}; template struct enum_field { diff --git a/library/include/DataIdentity.h b/library/include/DataIdentity.h new file mode 100644 index 000000000..679023fcf --- /dev/null +++ b/library/include/DataIdentity.h @@ -0,0 +1,175 @@ +/* +https://github.com/peterix/dfhack +Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#pragma once + +#include +#include +#include +#include + +#include "DataDefs.h" + +/* + * Definitions of DFHack namespace structs used by generated headers. + */ + +namespace DFHack +{ + class DFHACK_EXPORT primitive_identity : public type_identity { + public: + primitive_identity(size_t size) : type_identity(size) {}; + + virtual identity_type type() { return IDTYPE_PRIMITIVE; } + }; + + class DFHACK_EXPORT container_identity : public constructed_identity { + type_identity *item; + enum_identity *ienum; + + public: + container_identity(size_t size, TAllocateFn alloc, type_identity *item, enum_identity *ienum = NULL) + : constructed_identity(size, alloc), item(item), ienum(ienum) {}; + + virtual identity_type type() { return IDTYPE_CONTAINER; } + }; +} + +namespace df +{ + using DFHack::primitive_identity; + using DFHack::container_identity; + +#define ATOM_IDENTITY_TRAITS(type) \ + template<> struct identity_traits { \ + static primitive_identity identity; \ + static primitive_identity *get() { return &identity; } \ + }; \ + 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 + + // Container declarations + + template struct identity_traits > { + static primitive_identity *get(); + }; + + template struct identity_traits { + static container_identity *get(); + }; + + template struct identity_traits { + static container_identity *get(); + }; + + template struct identity_traits > { + static container_identity *get(); + }; + + template struct identity_traits > { + static container_identity *get(); + }; + + template struct identity_traits > { + static container_identity *get(); + }; + + template struct identity_traits > { + static container_identity *get(); + }; + + // Container definitions + + template + primitive_identity *identity_traits >::get() { + static primitive_identity identity(sizeof(FT)); + return &identity; + } + + template + container_identity *identity_traits::get() { + typedef T * container; + static container_identity identity(sizeof(container), &allocator_fn, + identity_traits::get()); + return &identity; + } + + template + container_identity *identity_traits::get() { + typedef T container[sz]; + static container_identity identity(sizeof(container), NULL, + identity_traits::get()); + return &identity; + } + + template + container_identity *identity_traits >::get() { + typedef std::vector container; + static container_identity identity(sizeof(container), &allocator_fn, + identity_traits::get()); + return &identity; + } + + template + container_identity *identity_traits >::get() { + typedef std::deque container; + static container_identity identity(sizeof(container), &allocator_fn, + identity_traits::get()); + return &identity; + } + + template + container_identity *identity_traits >::get() { + typedef BitArray container; + static type_identity *eid = identity_traits::get(); + static enum_identity *reid = eid->type() == DFHack::IDTYPE_ENUM ? (enum_identity*)eid : NULL; + static container_identity identity(sizeof(container), &allocator_fn, + &identity_traits::identity, reid); + return &identity; + } + + template + container_identity *identity_traits >::get() { + typedef DfArray container; + static container_identity identity(sizeof(container), &allocator_fn, + identity_traits::get()); + return &identity; + } +} + diff --git a/library/xml b/library/xml index 08e1f71e8..70eb6b5f3 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 08e1f71e89c1af6b3bef940914ed7f3d8fed89b0 +Subproject commit 70eb6b5f35680655d04d9fda79ff7251e21b45ae From dbbd9acfad8d5994f321840e3d695fe8a67ac315 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Mon, 19 Mar 2012 20:12:27 +0400 Subject: [PATCH 03/16] Export the type tree with enum keys to lua. --- CMakeLists.txt | 1 + depends/lua/CMakeLists.txt | 7 +- depends/lua/include/luaconf.h | 15 ++- library/CMakeLists.txt | 3 +- library/DataStaticsFields.cpp | 21 ++++ library/LuaWrapper.cpp | 218 +++++++++++++++++++++++++++++++++ library/include/DataDefs.h | 6 + library/include/DataIdentity.h | 3 +- 8 files changed, 267 insertions(+), 7 deletions(-) create mode 100644 library/LuaWrapper.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d39d940e2..1c538e246 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,6 +103,7 @@ ENDIF() # use shared libraries for protobuf ADD_DEFINITIONS(-DPROTOBUF_USE_DLLS) +ADD_DEFINITIONS(-DLUA_BUILD_AS_DLL) if(UNIX) add_definitions(-D_LINUX) diff --git a/depends/lua/CMakeLists.txt b/depends/lua/CMakeLists.txt index b135f221d..6a97bd434 100644 --- a/depends/lua/CMakeLists.txt +++ b/depends/lua/CMakeLists.txt @@ -76,8 +76,13 @@ src/lzio.c ) LIST(APPEND SRC_LIBLUA ${HDR_LIBLUA}) -ADD_LIBRARY ( lua STATIC EXCLUDE_FROM_ALL ${SRC_LIBLUA} ) +ADD_LIBRARY ( lua SHARED EXCLUDE_FROM_ALL ${SRC_LIBLUA} ) TARGET_LINK_LIBRARIES ( lua ${LIBS}) + +install(TARGETS lua + LIBRARY DESTINATION ${DFHACK_LIBRARY_DESTINATION} + RUNTIME DESTINATION ${DFHACK_LIBRARY_DESTINATION}) + IDE_FOLDER(lua "Depends") #SET ( SRC_LUA src/lua.c ) diff --git a/depends/lua/include/luaconf.h b/depends/lua/include/luaconf.h index 660793356..b202967b3 100644 --- a/depends/lua/include/luaconf.h +++ b/depends/lua/include/luaconf.h @@ -151,11 +151,20 @@ ** the libraries, you may want to use the following definition (define ** LUA_BUILD_AS_DLL to get it). */ +#ifdef __cplusplus + #define LUA_API_EXTERN extern "C" +#else + #define LUA_API_EXTERN extern +#endif #if defined(LUA_BUILD_AS_DLL) - #if defined(LUA_CORE) || defined(LUA_LIB) - #define LUA_API __declspec(dllexport) + #if defined(_MSC_VER) + #if defined(LUA_CORE) || defined(LUA_LIB) + #define LUA_API __declspec(dllexport) LUA_API_EXTERN + #else + #define LUA_API __declspec(dllimport) LUA_API_EXTERN + #endif #else - #define LUA_API __declspec(dllimport) + #define LUA_API LUA_API_EXTERN __attribute__ ((visibility("default"))) #endif #else #ifdef __cplusplus diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 7d3c4d6bb..3b1c6e7bf 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -55,6 +55,7 @@ SET(MAIN_SOURCES Core.cpp ColorText.cpp DataDefs.cpp +LuaWrapper.cpp DataStatics.cpp DataStaticsCtor.cpp DataStaticsFields.cpp @@ -251,7 +252,7 @@ ENDIF() #effectively disables debug builds... SET_TARGET_PROPERTIES(dfhack PROPERTIES DEBUG_POSTFIX "-debug" ) -TARGET_LINK_LIBRARIES(dfhack protobuf-lite clsocket ${PROJECT_LIBS}) +TARGET_LINK_LIBRARIES(dfhack protobuf-lite clsocket lua ${PROJECT_LIBS}) SET_TARGET_PROPERTIES(dfhack PROPERTIES LINK_INTERFACE_LIBRARIES "") TARGET_LINK_LIBRARIES(dfhack-client protobuf-lite clsocket) diff --git a/library/DataStaticsFields.cpp b/library/DataStaticsFields.cpp index 0e6e9957e..a9d2f3121 100644 --- a/library/DataStaticsFields.cpp +++ b/library/DataStaticsFields.cpp @@ -13,6 +13,27 @@ #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 TID(type) (&identity_traits< type >::identity) #define FLD(mode, name) struct_field_info::mode, #name, offsetof(CUR_STRUCT, name) diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp new file mode 100644 index 000000000..3ed4a35cd --- /dev/null +++ b/library/LuaWrapper.cpp @@ -0,0 +1,218 @@ +/* +https://github.com/peterix/dfhack +Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include "Internal.h" + +#include +#include +#include + +#include "MemAccess.h" +#include "Core.h" +#include "VersionInfo.h" +#include "tinythread.h" +// must be last due to MS stupidity +#include "DataDefs.h" +#include "DataIdentity.h" + +#include "MiscUtils.h" + +#include +#include + +using namespace DFHack; + +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); } + +static int change_error(lua_State *state) +{ + luaL_error(state, "Attempt to change a read-only table.\n"); +} + +static void freeze_table(lua_State *state, bool leave_metatable = false, const char *name = NULL) +{ + // rv = {}; setmetatable(rv, { __index = in, __newindex = change_error, __metatable = name }) + int base = lua_gettop(state); + lua_newtable(state); + lua_swap(state); + lua_setfield(state, base, "__index"); + lua_getfield(state, LUA_REGISTRYINDEX, "DFHack.ChangeError"); + lua_setfield(state, base, "__newindex"); + lua_newtable(state); + lua_swap(state); + lua_dup(state); + lua_setmetatable(state, base); + if (name) + { + lua_pushstring(state, name); + lua_setfield(state, -2, "__metatable"); + } + // result: [frozen table] [metatable] + if (!leave_metatable) + lua_pop(state, 1); +} + +static void SaveTypeInfo(lua_State *state, void *node) +{ + lua_getfield(state, LUA_REGISTRYINDEX, "DFHack.DFTypes"); + lua_pushlightuserdata(state, node); + lua_pushvalue(state, -3); + lua_settable(state, -3); + lua_pop(state, 1); +} + +static bool RegisterTypeInfo(lua_State *state, void *node) +{ + lua_getfield(state, LUA_REGISTRYINDEX, "DFHack.DFTypes"); + int base = lua_gettop(state); + + lua_pushlightuserdata(state, node); + lua_rawget(state, base); + + bool added = false; + + if (lua_isnil(state, -1)) + { + lua_pop(state, 1); + + lua_newtable(state); + lua_pushlightuserdata(state, node); + lua_pushvalue(state, -2); + lua_rawset(state, base); + + added = true; + } + + lua_remove(state, -2); + return added; +} + +static void RenderTypeChildren(lua_State *state, const std::vector &children); + +static void RenderType(lua_State *state, compound_identity *node) +{ + assert(node->getName()); + + lua_newtable(state); + if (!lua_checkstack(state, 20)) + return; + + int base = lua_gettop(state); + + lua_pushlightuserdata(state, node); + lua_setfield(state, base, "_identity"); + + switch (node->type()) + { + case IDTYPE_ENUM: + { + enum_identity *eid = (enum_identity*)node; + const char *const *keys = eid->getKeys(); + + // For enums, set mapping between keys and values + for (int64_t i = eid->getFirstItem(), j = 0; i <= eid->getLastItem(); i++, j++) + { + if (!keys[j]) + continue; + + lua_pushinteger(state, i); + lua_pushstring(state, keys[j]); + lua_dup(state); + lua_pushinteger(state, i); + + lua_settable(state, base); + lua_settable(state, base); + } + + if (eid->getFirstItem() <= eid->getLastItem()) + { + lua_pushstring(state, "_first_item"); + lua_pushinteger(state, eid->getFirstItem()); + lua_settable(state, base); + + lua_pushstring(state, "_last_item"); + lua_pushinteger(state, eid->getLastItem()); + lua_settable(state, base); + } + + SaveTypeInfo(state, node); + } + break; + + default: + break; + } + + RenderTypeChildren(state, node->getScopeChildren()); + + assert(base == lua_gettop(state)); + + freeze_table(state, false, node->getName()); +} + +static void RenderTypeChildren(lua_State *state, const std::vector &children) +{ + for (size_t i = 0; i < children.size(); i++) + { + RenderType(state, children[i]); + lua_pushstring(state, children[i]->getName()); + lua_swap(state); + lua_settable(state, -3); + } +} + +static void DoAttach(lua_State *state) +{ + int base = lua_gettop(state); + + lua_pushcfunction(state, change_error); + lua_setfield(state, LUA_REGISTRYINDEX, "DFHack.ChangeError"); + + luaL_register(state, "df", no_functions); + + { + // Assign df a metatable with read-only contents + lua_newtable(state); + + // Render the type structure + RenderTypeChildren(state, compound_identity::getTopScope()); + + freeze_table(state, true, "df"); + lua_remove(state, -2); + lua_setmetatable(state, -2); + } + + lua_pop(state, 1); +} + +void DFHack::AttachDFGlobals(lua_State *state) +{ + if (luaL_newmetatable(state, "DFHack.DFTypes")) + DoAttach(state); + + lua_pop(state, 1); +} diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index 2a9567182..3a7679b65 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -37,6 +37,8 @@ distribution. #undef interface #endif +typedef struct lua_State lua_State; + /* * Definitions of DFHack namespace structs used by generated headers. */ @@ -155,6 +157,8 @@ namespace DFHack virtual identity_type type() { return IDTYPE_ENUM; } + int64_t getFirstItem() { return first_item_value; } + int64_t getLastItem() { return last_item_value; } int getCount() { return int(last_item_value-first_item_value+1); } const char *const *getKeys() { return keys; } }; @@ -280,6 +284,8 @@ namespace DFHack void InitDataDefGlobals(Core *core); + DFHACK_EXPORT void AttachDFGlobals(lua_State *state); + template T *ifnull(T *a, T *b) { return a ? a : b; } diff --git a/library/include/DataIdentity.h b/library/include/DataIdentity.h index 679023fcf..d354beb03 100644 --- a/library/include/DataIdentity.h +++ b/library/include/DataIdentity.h @@ -65,8 +65,7 @@ namespace df template<> struct identity_traits { \ static primitive_identity identity; \ static primitive_identity *get() { return &identity; } \ - }; \ - primitive_identity identity_traits::identity(sizeof(type)); + }; ATOM_IDENTITY_TRAITS(char); ATOM_IDENTITY_TRAITS(int8_t); From 6c661bcaa907952f3ffdc4ff546ec88cfcc36d72 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Tue, 20 Mar 2012 13:56:29 +0400 Subject: [PATCH 04/16] Add support for primitive type fields in lua wrapper. --- library/DataDefs.cpp | 7 +- library/DataStatics.cpp | 2 + library/DataStaticsFields.cpp | 40 ++-- library/LuaWrapper.cpp | 339 ++++++++++++++++++++++++++++++++- library/include/DataDefs.h | 25 ++- library/include/DataIdentity.h | 142 +++++++++++--- library/xml | 2 +- 7 files changed, 502 insertions(+), 55 deletions(-) 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 From 73e138c9fdf64cab23330fc13b73383d8648324d Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Tue, 20 Mar 2012 21:34:27 +0400 Subject: [PATCH 05/16] Support ordinary struct and class fields. --- library/CMakeLists.txt | 2 +- library/LuaWrapper.cpp | 389 +++++++++++++++++++++++++++------ library/include/DataDefs.h | 2 +- library/include/DataIdentity.h | 3 + 4 files changed, 322 insertions(+), 74 deletions(-) diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 3b1c6e7bf..72bf91e62 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -200,7 +200,7 @@ ADD_CUSTOM_TARGET(generate_headers DEPENDS ${dfapi_SOURCE_DIR}/include/df/static IF(UNIX) # Don't produce debug info for generated stubs SET_SOURCE_FILES_PROPERTIES(DataStatics.cpp DataStaticsCtor.cpp DataStaticsFields.cpp - PROPERTIES COMPILE_FLAGS "-g0") + PROPERTIES COMPILE_FLAGS "-g0 -O1") ELSE(WIN32) ENDIF() diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp index 27935ae50..7081cd259 100644 --- a/library/LuaWrapper.cpp +++ b/library/LuaWrapper.cpp @@ -48,6 +48,12 @@ 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 DFHACK_TYPETABLE_NAME "DFHack::DFTypes" +#define DFHACK_TYPEID_TABLE_NAME "DFHack::DFTypeIds" +#define DFHACK_CHANGEERROR_NAME "DFHack::ChangeError" +#define DFHACK_COMPARE_NAME "DFHack::ComparePtrs" +#define DFHACK_TYPE_TOSTRING_NAME "DFHack::TypeToString" + #define UPVAL_TYPETABLE lua_upvalueindex(1) #define UPVAL_METATABLE lua_upvalueindex(2) #define UPVAL_FIELDTABLE lua_upvalueindex(3) @@ -63,7 +69,7 @@ namespace { } } -static void field_error(lua_State *state, int index, const char *err, const char *mode = "read") +static void field_error(lua_State *state, int index, const char *err, const char *mode) { lua_getfield(state, UPVAL_METATABLE, "__metatable"); const char *cname = lua_tostring(state, -1); @@ -72,12 +78,12 @@ static void field_error(lua_State *state, int index, const char *err, const char 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 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) +void DFHack::PushDFObject(lua_State *state, type_identity *type, void *ptr) { - return push_object_internal(state, type, ptr, false); + push_object_internal(state, type, ptr, false); } void *DFHack::GetDFObject(lua_State *state, type_identity *type, int val_index) @@ -89,7 +95,8 @@ void *DFHack::GetDFObject(lua_State *state, type_identity *type, int val_index) int constructed_identity::lua_read(lua_State *state, int fname_idx, void *ptr) { - return push_object_internal(state, this, ptr); + push_object_internal(state, this, ptr); + return 1; } void constructed_identity::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) @@ -156,25 +163,19 @@ void df::stl_string_identity::lua_write(lua_State *state, int fname_idx, void *p *(std::string*)ptr = std::string(bytes, size); } -static int do_read_pointer(lua_State *state, int, void *ptr, type_identity *target) +int df::pointer_identity::lua_read(lua_State *state, int fname_idx, 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); + push_object_internal(state, target, *(void**)ptr); + return 1; } int df::pointer_identity::lua_read(lua_State *state, int fname_idx, void *ptr) { - return do_read_pointer(state, fname_idx, ptr, target); + return lua_read(state, fname_idx, ptr, target); } -static void do_write_pointer(lua_State *state, int fname_idx, void *ptr, type_identity *target, int val_index) +void df::pointer_identity::lua_write(lua_State *state, int fname_idx, void *ptr, + type_identity *target, int val_index) { auto pptr = (void**)ptr; @@ -192,7 +193,7 @@ static void do_write_pointer(lua_State *state, int fname_idx, void *ptr, type_id 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); + lua_write(state, fname_idx, ptr, target, val_index); } /* */ @@ -209,7 +210,7 @@ static void freeze_table(lua_State *state, bool leave_metatable = false, const c lua_newtable(state); lua_swap(state); lua_setfield(state, base, "__index"); - lua_getfield(state, LUA_REGISTRYINDEX, "DFHack.ChangeError"); + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_CHANGEERROR_NAME); lua_setfield(state, base, "__newindex"); lua_newtable(state); lua_swap(state); @@ -225,54 +226,179 @@ static void freeze_table(lua_State *state, bool leave_metatable = false, const c lua_pop(state, 1); } -static void SaveTypeInfo(lua_State *state, void *node) +static bool LookupTypeInfo(lua_State *state, bool in_method) +{ + // stack: [lookup key] + + if (in_method) + { + lua_rawget(state, UPVAL_TYPETABLE); + } + else + { + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPETABLE_NAME); + lua_swap(state); + lua_rawget(state, -2); + lua_remove(state, -2); + } + + // stack: [info] + + if (lua_isnil(state, -1)) + { + lua_pop(state, 1); + return false; + } + else + return true; +} + +static void SaveInTable(lua_State *state, void *node, const char *tname) { - lua_getfield(state, LUA_REGISTRYINDEX, "DFHack.DFTypes"); + // stack: [info] + lua_getfield(state, LUA_REGISTRYINDEX, tname); + lua_pushlightuserdata(state, node); lua_pushvalue(state, -3); - lua_settable(state, -3); + lua_rawset(state, -3); + + lua_pushvalue(state, -2); + lua_pushlightuserdata(state, node); + lua_rawset(state, -3); + lua_pop(state, 1); + // stack: [info] } -static bool RegisterTypeInfo(lua_State *state, void *node) +static void SaveTypeInfo(lua_State *state, void *node) { - lua_getfield(state, LUA_REGISTRYINDEX, "DFHack.DFTypes"); - int base = lua_gettop(state); + SaveInTable(state, node, DFHACK_TYPETABLE_NAME); +} - lua_pushlightuserdata(state, node); - lua_rawget(state, base); +static void BuildTypeMetatable(lua_State *state, type_identity *type); - bool added = false; +static void push_object_ref(lua_State *state, void *ptr) +{ + // stack: [metatable] + auto ref = (DFRefHeader*)lua_newuserdata(state, sizeof(DFRefHeader)); + ref->ptr = ptr; - if (lua_isnil(state, -1)) + lua_swap(state); + lua_setmetatable(state, -2); + // stack: [userdata] +} + +static void push_object_internal(lua_State *state, type_identity *type, void *ptr, bool in_method) +{ + if (!ptr || !type) { - lua_pop(state, 1); + if (!ptr) + lua_pushnil(state); + else + lua_pushlightuserdata(state, ptr); - lua_newtable(state); - lua_pushlightuserdata(state, node); - lua_pushvalue(state, -2); - lua_rawset(state, base); + return; + } - added = true; + // Resolve actual class using vtable + if (type->type() == IDTYPE_CLASS) + { + virtual_identity *class_vid = virtual_identity::get(virtual_ptr(ptr)); + if (class_vid) + type = class_vid; } - lua_remove(state, -2); - return added; + 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 } -static const struct_field_info *find_field(lua_State *state, int index, const char *mode = "read") +static void *get_object_internal(lua_State *state, type_identity *type, int val_index, bool in_method) +{ + if (!lua_isuserdata(state, val_index)) + return NULL; + + if (!lua_getmetatable(state, val_index)) // () -> metatable? + { + if (!type && lua_islightuserdata(state, val_index)) + return lua_touserdata(state, val_index); + + return NULL; + } + + // Verify known metatable and correct type + if (!LookupTypeInfo(state, in_method)) // metatable -> type? + return NULL; + + bool ok = lua_islightuserdata(state, -1) && + (!type || lua_touserdata(state, -1) == type); + + lua_pop(state, 1); // type -> () + + if (!ok) + return NULL; + + auto ref = (DFRefHeader*)lua_touserdata(state, val_index); + return ref->ptr; +} + + +static int meta_ptr_compare(lua_State *state) +{ + if (!lua_isuserdata(state, 1) || !lua_isuserdata(state, 2) || + !lua_getmetatable(state, 1) || !lua_getmetatable(state, 2)) + { + lua_pushboolean(state, false); + return 1; + } + + if (!lua_equal(state, -1, -2)) + { + // todo: nonidentical type comparison + lua_pushboolean(state, false); + return 1; + } + + auto ref1 = (DFRefHeader*)lua_touserdata(state, 1); + auto ref2 = (DFRefHeader*)lua_touserdata(state, 2); + lua_pushboolean(state, ref1->ptr == ref2->ptr); + return 1; +} + +static const struct_field_info *find_field(lua_State *state, int index, const char *mode) { lua_pushvalue(state, index); lua_rawget(state, UPVAL_FIELDTABLE); if (!lua_islightuserdata(state, -1)) - field_error(state, index, "not found"); + field_error(state, index, "not found", mode); void *p = lua_touserdata(state, -1); lua_pop(state, 1); return (struct_field_info*)p; } +static uint8_t *get_object_addr(lua_State *state, int obj, int field, const char *mode) +{ + if (!lua_isuserdata(state, obj) || + !lua_getmetatable(state, obj)) + field_error(state, field, "invalid object", mode); + + if (!lua_equal(state, -1, UPVAL_METATABLE)) + field_error(state, field, "invalid object metatable", mode); + + lua_pop(state, 1); + + auto ref = (DFRefHeader*)lua_touserdata(state, obj); + return (uint8_t*)ref->ptr; +} + static int read_field(lua_State *state, const struct_field_info *field, void *ptr) { switch (field->mode) @@ -290,7 +416,7 @@ static int read_field(lua_State *state, const struct_field_info *field, void *pt return field->type->lua_read(state, 2, ptr); case struct_field_info::POINTER: - return do_read_pointer(state, 2, ptr, field->type); + return df::pointer_identity::lua_read(state, 2, ptr, field->type); case struct_field_info::STATIC_ARRAY: case struct_field_info::STL_VECTOR_PTR: @@ -300,14 +426,14 @@ static int read_field(lua_State *state, const struct_field_info *field, void *pt } } -static void write_field(lua_State *state, const struct_field_info *field, void *ptr) +static void write_field(lua_State *state, const struct_field_info *field, void *ptr, int value_idx) { switch (field->mode) { case struct_field_info::STATIC_STRING: { size_t size; - const char *str = lua_tolstring(state, -1, &size); + const char *str = lua_tolstring(state, value_idx, &size); if (!str) field_error(state, 2, "string expected", "write"); memcpy(ptr, str, std::min(size+1, size_t(field->count))); @@ -317,11 +443,11 @@ static void write_field(lua_State *state, const struct_field_info *field, void * case struct_field_info::PRIMITIVE: case struct_field_info::SUBSTRUCT: case struct_field_info::CONTAINER: - field->type->lua_write(state, 2, ptr, 3); + field->type->lua_write(state, 2, ptr, value_idx); return; case struct_field_info::POINTER: - do_write_pointer(state, 2, ptr, field->type, 3); + df::pointer_identity::lua_write(state, 2, ptr, field->type, value_idx); case struct_field_info::STATIC_ARRAY: case struct_field_info::STL_VECTOR_PTR: @@ -332,22 +458,70 @@ static void write_field(lua_State *state, const struct_field_info *field, void * } } +static int meta_type_tostring(lua_State *state) +{ + if (!lua_getmetatable(state, 1)) + return 0; + + lua_getfield(state, -1, "__metatable"); + const char *cname = lua_tostring(state, -1); + + lua_pushstring(state, stl_sprintf("", cname).c_str()); + return 1; +} + +static int meta_ptr_tostring(lua_State *state) +{ + uint8_t *ptr = get_object_addr(state, 1, 0, "access"); + + lua_getfield(state, UPVAL_METATABLE, "__metatable"); + const char *cname = lua_tostring(state, -1); + + lua_pushstring(state, stl_sprintf("<%s: 0x%08x>", cname, (unsigned)ptr).c_str()); + return 1; +} + +static int get_metafield(lua_State *state) +{ + lua_rawget(state, UPVAL_METATABLE); + return 1; +} + +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"); + if (!field) + return get_metafield(state); + return read_field(state, field, ptr + field->offset); +} + +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"); + write_field(state, field, ptr + field->offset, 3); + return 0; +} + static int meta_global_index(lua_State *state) { - const struct_field_info *field = find_field(state, 2); + const struct_field_info *field = find_field(state, 2, "read"); + if (!field) + return get_metafield(state); void *ptr = *(void**)field->offset; if (!ptr) - field_error(state, 2, "global address not known"); + field_error(state, 2, "global address not known", "read"); return read_field(state, field, ptr); } static int meta_global_newindex(lua_State *state) { - const struct_field_info *field = find_field(state, 2); + const struct_field_info *field = find_field(state, 2, "write"); void *ptr = *(void**)field->offset; if (!ptr) field_error(state, 2, "global address not known", "write"); - write_field(state, field, ptr); + write_field(state, field, ptr, 3); return 0; } @@ -369,7 +543,7 @@ static void IndexFields(lua_State *state, const struct_field_info *fields) case struct_field_info::POINTER: lua_pushstring(state,fields->name); lua_pushlightuserdata(state,(void*)fields); - lua_settable(state,base+2); + lua_rawset(state,base+2); // fallthrough case struct_field_info::STATIC_ARRAY: @@ -378,12 +552,40 @@ static void IndexFields(lua_State *state, const struct_field_info *fields) case struct_field_info::STL_VECTOR_PTR: lua_pushstring(state,fields->name); lua_pushlightuserdata(state,(void*)fields); - lua_settable(state,base+1); + lua_rawset(state,base+1); break; } } } +static void SetPtrMethods(lua_State *state, int meta_idx, void *node) +{ + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_COMPARE_NAME); + lua_setfield(state, meta_idx, "__eq"); + + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPETABLE_NAME); + lua_pushvalue(state, meta_idx); + 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); +} + +static void SetStructMethod(lua_State *state, int meta_idx, int ftable_idx, + lua_CFunction function, const char *name) +{ + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPETABLE_NAME); + lua_pushvalue(state, meta_idx); + lua_pushvalue(state, ftable_idx); + lua_pushcclosure(state, function, 3); + lua_setfield(state, meta_idx, name); +} + static void MakeFieldMetatable(lua_State *state, struct_identity *pstruct, lua_CFunction reader, lua_CFunction writer) { @@ -395,32 +597,52 @@ static void MakeFieldMetatable(lua_State *state, struct_identity *pstruct, lua_pushstring(state, pstruct->getName()); lua_setfield(state, base+1, "__metatable"); - lua_pushlightuserdata(state, pstruct); - lua_setfield(state, base+1, "_identity"); + SetStructMethod(state, base+1, base+2, reader, "__index"); + SetStructMethod(state, base+1, base+3, writer, "__newindex"); - 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"); + // Custom fields - 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"); + lua_pushlightuserdata(state, pstruct); + lua_setfield(state, base+1, "_identity"); // returns: [metatable readfields writefields]; } -static int push_object_internal(lua_State *state, type_identity *type, void *ptr, bool in_method) +static void EnableMetaField(lua_State *state, int ftable_idx, const char *name) { - return 0; + lua_pushlightuserdata(state, NULL); + lua_setfield(state, ftable_idx, name); } -static void *get_object_internal(lua_State *state, type_identity *type, int val_index, bool in_method) +static void BuildTypeMetatable(lua_State *state, type_identity *type) { - return NULL; + int base = lua_gettop(state); + + switch (type->type()) + { + 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; + + case IDTYPE_PRIMITIVE: + case IDTYPE_ENUM: + case IDTYPE_POINTER: + luaL_error(state, "primitive not implemented"); + + case IDTYPE_BITFIELD: + luaL_error(state, "bitfield not implemented"); + + case IDTYPE_CONTAINER: + case IDTYPE_STL_PTR_VECTOR: + luaL_error(state, "container not implemented"); + } } static void RenderTypeChildren(lua_State *state, const std::vector &children); @@ -456,8 +678,8 @@ static void RenderType(lua_State *state, compound_identity *node) lua_dup(state); lua_pushinteger(state, i); - lua_settable(state, base); - lua_settable(state, base); + lua_rawset(state, base); + lua_rawset(state, base); } if (eid->getFirstItem() <= eid->getLastItem()) @@ -493,14 +715,28 @@ static void RenderType(lua_State *state, compound_identity *node) lua_swap(state); // -> meta curtable freeze_table(state, true, "global"); + 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, false, node->getName()); + { + freeze_table(state, true, node->getName()); + + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPE_TOSTRING_NAME); + lua_setfield(state, -2, "__tostring"); + + lua_pop(state, 1); + } + + SaveInTable(state, node, DFHACK_TYPEID_TABLE_NAME); } static void RenderTypeChildren(lua_State *state, const std::vector &children) @@ -510,7 +746,7 @@ static void RenderTypeChildren(lua_State *state, const std::vectorgetName()); lua_swap(state); - lua_settable(state, -3); + lua_rawset(state, -3); } } @@ -518,8 +754,17 @@ static void DoAttach(lua_State *state) { int base = lua_gettop(state); + lua_newtable(state); + lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_TYPEID_TABLE_NAME); + lua_pushcfunction(state, change_error); - lua_setfield(state, LUA_REGISTRYINDEX, "DFHack.ChangeError"); + lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_CHANGEERROR_NAME); + + lua_pushcfunction(state, meta_ptr_compare); + lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_COMPARE_NAME); + + lua_pushcfunction(state, meta_type_tostring); + lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_TYPE_TOSTRING_NAME); luaL_register(state, "df", no_functions); @@ -540,7 +785,7 @@ static void DoAttach(lua_State *state) void DFHack::AttachDFGlobals(lua_State *state) { - if (luaL_newmetatable(state, "DFHack.DFTypes")) + if (luaL_newmetatable(state, DFHACK_TYPETABLE_NAME)) DoAttach(state); lua_pop(state, 1); diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index df3af1bb4..8562a18d6 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -306,7 +306,7 @@ namespace DFHack DFHACK_EXPORT void AttachDFGlobals(lua_State *state); - DFHACK_EXPORT int PushDFObject(lua_State *state, type_identity *type, void *ptr); + DFHACK_EXPORT void PushDFObject(lua_State *state, type_identity *type, void *ptr); DFHACK_EXPORT void *GetDFObject(lua_State *state, type_identity *type, int val_index); template diff --git a/library/include/DataIdentity.h b/library/include/DataIdentity.h index 4443518ab..28426c58a 100644 --- a/library/include/DataIdentity.h +++ b/library/include/DataIdentity.h @@ -55,6 +55,9 @@ namespace DFHack type_identity *getTarget() { return target; } + static int lua_read(lua_State *state, int fname_idx, void *ptr, type_identity *target); + static void lua_write(lua_State *state, int fname_idx, void *ptr, type_identity *target, int val_index); + 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); }; From 9b78fffe9255e0fd14b6e446652598a36372b934 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 21 Mar 2012 13:26:53 +0400 Subject: [PATCH 06/16] Support containers in the lua wrapper. --- library/DataDefs.cpp | 35 +++ library/DataStaticsFields.cpp | 5 +- library/LuaWrapper.cpp | 444 +++++++++++++++++++++++++++------ library/include/DataDefs.h | 13 + library/include/DataIdentity.h | 197 +++++++++++++-- 5 files changed, 602 insertions(+), 92 deletions(-) diff --git a/library/DataDefs.cpp b/library/DataDefs.cpp index e8ee14712..9576afb7b 100644 --- a/library/DataDefs.cpp +++ b/library/DataDefs.cpp @@ -34,6 +34,7 @@ distribution. #include "tinythread.h" // must be last due to MS stupidity #include "DataDefs.h" +#include "DataIdentity.h" #include "MiscUtils.h" @@ -62,6 +63,14 @@ void compound_identity::doInit(Core *) top_scope.push_back(this); } +std::string compound_identity::getFullName() +{ + if (scope_parent) + return scope_parent->getFullName() + "." + getName(); + else + return getName(); +} + static tthread::mutex *known_mutex = NULL; void compound_identity::Init(Core *core) @@ -130,6 +139,32 @@ bool struct_identity::is_subclass(struct_identity *actual) return false; } +std::string pointer_identity::getFullName() +{ + return (target ? target->getFullName() : std::string("void")) + "*"; +} + +std::string container_identity::getFullName(type_identity *item) +{ + return "<" + (item ? item->getFullName() : std::string("void")) + ">"; +} + +std::string ptr_container_identity::getFullName(type_identity *item) +{ + return "<" + (item ? item->getFullName() : std::string("void")) + "*>"; +} + +std::string bit_container_identity::getFullName(type_identity *) +{ + return ""; +} + +std::string df::buffer_container_identity::getFullName(type_identity *item) +{ + return (item ? item->getFullName() : std::string("void")) + + (size > 0 ? stl_sprintf("[%d]", size) : std::string("[]")); +} + virtual_identity::virtual_identity(size_t size, TAllocateFn alloc, const char *dfhack_name, const char *original_name, virtual_identity *parent, const struct_field_info *fields) diff --git a/library/DataStaticsFields.cpp b/library/DataStaticsFields.cpp index 22ce9b6df..89736369f 100644 --- a/library/DataStaticsFields.cpp +++ b/library/DataStaticsFields.cpp @@ -15,7 +15,7 @@ namespace df { #define NUMBER_IDENTITY_TRAITS(type) \ - number_identity identity_traits::identity; + number_identity identity_traits::identity(#type); NUMBER_IDENTITY_TRAITS(char); NUMBER_IDENTITY_TRAITS(int8_t); @@ -32,6 +32,9 @@ namespace df { stl_string_identity identity_traits::identity; pointer_identity identity_traits::identity; stl_ptr_vector_identity identity_traits >::identity; + stl_bit_vector_identity identity_traits >::identity; + + buffer_container_identity buffer_container_identity::base_instance; #undef NUMBER_IDENTITY_TRAITS } diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp index 7081cd259..d4755bc0f 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 >::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::vectorgetName()); + 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); diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index 8562a18d6..a466ea9d8 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -52,6 +52,8 @@ namespace DFHack IDTYPE_PRIMITIVE, IDTYPE_POINTER, IDTYPE_CONTAINER, + IDTYPE_PTR_CONTAINER, + IDTYPE_BIT_CONTAINER, IDTYPE_BITFIELD, IDTYPE_ENUM, IDTYPE_STRUCT, @@ -85,6 +87,11 @@ namespace DFHack 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; + + virtual std::string getFullName() = 0; + virtual void build_metatable(lua_State *state); + + virtual bool isContainer() { return false; } }; class DFHACK_EXPORT constructed_identity : public type_identity { @@ -124,6 +131,8 @@ namespace DFHack public: const char *getName() { return dfhack_name; } + virtual std::string getFullName(); + compound_identity *getScopeParent() { return scope_parent; } const std::vector &getScopeChildren() { return scope_children; } static const std::vector &getTopScope() { return top_scope; } @@ -222,6 +231,8 @@ namespace DFHack const struct_field_info *getFields() { return fields; } bool is_subclass(struct_identity *subtype); + + virtual void build_metatable(lua_State *state); }; class DFHACK_EXPORT global_identity : public struct_identity { @@ -230,6 +241,8 @@ namespace DFHack : struct_identity(0,NULL,NULL,"global",NULL,fields) {} virtual identity_type type() { return IDTYPE_GLOBAL; } + + virtual void build_metatable(lua_State *state); }; #ifdef _MSC_VER diff --git a/library/include/DataIdentity.h b/library/include/DataIdentity.h index 28426c58a..ac5903beb 100644 --- a/library/include/DataIdentity.h +++ b/library/include/DataIdentity.h @@ -55,6 +55,9 @@ namespace DFHack type_identity *getTarget() { return target; } + std::string getFullName(); + virtual void build_metatable(lua_State *state); + static int lua_read(lua_State *state, int fname_idx, void *ptr, type_identity *target); static void lua_write(lua_State *state, int fname_idx, void *ptr, type_identity *target, int val_index); @@ -72,7 +75,57 @@ namespace DFHack virtual identity_type type() { return IDTYPE_CONTAINER; } + std::string getFullName() { return getFullName(item); } + + virtual void build_metatable(lua_State *state); + virtual bool isContainer() { return true; } + type_identity *getItemType() { return item; } + type_identity *getIndexEnumType() { return ienum; } + + virtual std::string getFullName(type_identity *item); + + int lua_item_count(lua_State *state, void *ptr); + + virtual int lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx); + virtual void lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index); + + protected: + virtual int item_count(void *ptr) = 0; + virtual void *item_pointer(void *ptr, int idx) = 0; + }; + + class DFHACK_EXPORT ptr_container_identity : public container_identity { + public: + ptr_container_identity(size_t size, TAllocateFn alloc, + type_identity *item, enum_identity *ienum = NULL) + : container_identity(size, alloc, item, ienum) {}; + + virtual identity_type type() { return IDTYPE_PTR_CONTAINER; } + + std::string getFullName(type_identity *item); + + virtual int lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx); + virtual void lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index); + }; + + class DFHACK_EXPORT bit_container_identity : public container_identity { + public: + bit_container_identity(size_t size, TAllocateFn alloc, enum_identity *ienum = NULL) + : container_identity(size, alloc, NULL, ienum) {}; + + virtual identity_type type() { return IDTYPE_BIT_CONTAINER; } + + std::string getFullName(type_identity *item); + + virtual int lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx); + virtual void lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index); + + protected: + virtual void *item_pointer(void *, int) { return NULL; } + + virtual bool get_item(void *ptr, int idx) = 0; + virtual void set_item(void *ptr, int idx, bool val) = 0; }; } @@ -81,10 +134,17 @@ namespace df using DFHack::primitive_identity; using DFHack::pointer_identity; using DFHack::container_identity; + using DFHack::ptr_container_identity; + using DFHack::bit_container_identity; class number_identity_base : public primitive_identity { + const char *name; + public: - number_identity_base(size_t size) : primitive_identity(size) {}; + number_identity_base(size_t size, const char *name) + : primitive_identity(size), name(name) {}; + + std::string getFullName() { return name; } 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); @@ -97,7 +157,7 @@ namespace df template class number_identity : public number_identity_base { public: - number_identity() : number_identity_base(sizeof(T)) {} + number_identity(const char *name) : number_identity_base(sizeof(T), name) {} protected: virtual double read(void *ptr) { return double(*(T*)ptr); } virtual void write(void *ptr, double val) { *(T*)ptr = T(val); } @@ -107,6 +167,8 @@ namespace df public: bool_identity() : primitive_identity(sizeof(bool)) {}; + std::string getFullName() { return "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); }; @@ -115,17 +177,118 @@ namespace df public: stl_string_identity() : primitive_identity(sizeof(std::string)) {}; + std::string getFullName() { return "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 { + class stl_ptr_vector_identity : public ptr_container_identity { public: stl_ptr_vector_identity(type_identity *item = NULL, enum_identity *ienum = NULL) - : container_identity(sizeof(std::vector),allocator_fn >,item, ienum) + : ptr_container_identity(sizeof(std::vector),allocator_fn >,item, ienum) {}; + std::string getFullName(type_identity *item) { + return "vector" + ptr_container_identity::getFullName(item); + } + virtual DFHack::identity_type type() { return DFHack::IDTYPE_STL_PTR_VECTOR; } + + protected: + virtual int item_count(void *ptr) { + return ((std::vector*)ptr)->size(); + }; + virtual void *item_pointer(void *ptr, int idx) { + return &(*(std::vector*)ptr)[idx]; + } + }; + + class buffer_container_identity : public container_identity { + int size; + + public: + buffer_container_identity() + : container_identity(0, NULL, NULL, NULL), size(0) + {} + + buffer_container_identity(int size, type_identity *item, enum_identity *ienum = NULL) + : container_identity(item->byte_size()*size, NULL, item, ienum), size(size) + {} + + std::string getFullName(type_identity *item); + + static buffer_container_identity base_instance; + + virtual int lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx); + virtual void lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index); + + protected: + virtual int item_count(void *ptr) { return size; } + virtual void *item_pointer(void *ptr, int idx) { return NULL; } + }; + + template + class stl_container_identity : public container_identity { + const char *name; + + public: + stl_container_identity(const char *name, type_identity *item, enum_identity *ienum = NULL) + : container_identity(sizeof(T), &allocator_fn, item, ienum), name(name) + {} + + std::string getFullName(type_identity *item) { + return name + container_identity::getFullName(item); + } + + protected: + virtual int item_count(void *ptr) { return ((T*)ptr)->size(); } + virtual void *item_pointer(void *ptr, int idx) { return &(*(T*)ptr)[idx]; } + }; + + template + class bit_array_identity : public bit_container_identity { + public: + typedef BitArray container; + + bit_array_identity(enum_identity *ienum = NULL) + : bit_container_identity(sizeof(container), &allocator_fn, ienum) + {} + + std::string getFullName(type_identity *item) { + return "BitArray" + bit_container_identity::getFullName(item); + } + + protected: + virtual int item_count(void *ptr) { return ((container*)ptr)->size * 8; } + virtual bool get_item(void *ptr, int idx) { + return ((container*)ptr)->is_set(T(idx)); + } + virtual void set_item(void *ptr, int idx, bool val) { + ((container*)ptr)->set(T(idx), val); + } + }; + + class stl_bit_vector_identity : public bit_container_identity { + public: + typedef std::vector container; + + stl_bit_vector_identity(enum_identity *ienum = NULL) + : bit_container_identity(sizeof(container), &allocator_fn, ienum) + {} + + std::string getFullName(type_identity *item) { + return "vector" + bit_container_identity::getFullName(item); + } + + protected: + virtual int item_count(void *ptr) { return ((container*)ptr)->size(); } + virtual bool get_item(void *ptr, int idx) { + return (*(container*)ptr)[idx]; + } + virtual void set_item(void *ptr, int idx, bool val) { + (*(container*)ptr)[idx] = val; + } }; #define NUMBER_IDENTITY_TRAITS(type) \ @@ -165,6 +328,11 @@ namespace df static stl_ptr_vector_identity *get() { return &identity; } }; + template<> struct identity_traits > { + static stl_bit_vector_identity identity; + static stl_bit_vector_identity *get() { return &identity; } + }; + #undef NUMBER_IDENTITY_TRAITS // Container declarations @@ -194,7 +362,7 @@ namespace df }; template struct identity_traits > { - static container_identity *get(); + static bit_container_identity *get(); }; template struct identity_traits > { @@ -216,17 +384,14 @@ namespace df template container_identity *identity_traits::get() { - typedef T container[sz]; - static container_identity identity(sizeof(container), NULL, - identity_traits::get()); + static buffer_container_identity identity(sz, identity_traits::get()); return &identity; } template container_identity *identity_traits >::get() { typedef std::vector container; - static container_identity identity(sizeof(container), &allocator_fn, - identity_traits::get()); + static stl_container_identity identity("vector", identity_traits::get()); return &identity; } @@ -239,26 +404,22 @@ namespace df template container_identity *identity_traits >::get() { typedef std::deque container; - static container_identity identity(sizeof(container), &allocator_fn, - identity_traits::get()); + static stl_container_identity identity("deque", identity_traits::get()); return &identity; } template - container_identity *identity_traits >::get() { - typedef BitArray container; + bit_container_identity *identity_traits >::get() { static type_identity *eid = identity_traits::get(); static enum_identity *reid = eid->type() == DFHack::IDTYPE_ENUM ? (enum_identity*)eid : NULL; - static container_identity identity(sizeof(container), &allocator_fn, - &identity_traits::identity, reid); + static bit_array_identity identity(reid); return &identity; } template container_identity *identity_traits >::get() { typedef DfArray container; - static container_identity identity(sizeof(container), &allocator_fn, - identity_traits::get()); + static stl_container_identity identity("DfArray", identity_traits::get()); return &identity; } } From ad10303cecd1e60a2e50b85a5f6f08656ff1bbf9 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 21 Mar 2012 20:04:37 +0400 Subject: [PATCH 07/16] Implement bitfields and add a _kind metadata field to types and objects. --- library/LuaWrapper.cpp | 399 ++++++++++++++++++++++++++++++++++--- library/include/DataDefs.h | 37 +++- 2 files changed, 407 insertions(+), 29 deletions(-) diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp index d4755bc0f..a7d830474 100644 --- a/library/LuaWrapper.cpp +++ b/library/LuaWrapper.cpp @@ -48,32 +48,81 @@ 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); } +/* + * Registry name: hash of type metatables <-> type identities. + */ #define DFHACK_TYPETABLE_NAME "DFHack::DFTypes" + +/* + * Registry name: hash of type identity -> node in df.etc... + */ #define DFHACK_TYPEID_TABLE_NAME "DFHack::DFTypeIds" + +/* + * Registry name: hash of enum/bitfield identity -> index lookup table + */ #define DFHACK_ENUM_TABLE_NAME "DFHack::DFEnums" + +// Function registry names #define DFHACK_CHANGEERROR_NAME "DFHack::ChangeError" #define DFHACK_COMPARE_NAME "DFHack::ComparePtrs" #define DFHACK_TYPE_TOSTRING_NAME "DFHack::TypeToString" +/* + * Upvalue: contents of DFHACK_TYPETABLE_NAME + */ #define UPVAL_TYPETABLE lua_upvalueindex(1) + +/* + * Expected metatable of the current object. + */ #define UPVAL_METATABLE lua_upvalueindex(2) + +/* + * Table mapping field names to indices or data structure pointers. + * Enum index table is linked into here via getmetatable($).__index. + * Fields that are actually in UPVAL_METATABLE are marked with NULL light udata. + */ #define UPVAL_FIELDTABLE lua_upvalueindex(3) +/* + * Only for containers: light udata with container identity. + */ #define UPVAL_CONTAINER_ID lua_upvalueindex(4) + +/* + * Only for containers: light udata with item identity. + */ #define UPVAL_ITEM_ID lua_upvalueindex(5) + +/* + * Only for containers: if not nil, overrides the item count. + */ #define UPVAL_ITEM_COUNT lua_upvalueindex(6) namespace { + /** + * Object references are represented as userdata instances + * with an appropriate metatable; the payload of userdata is + * this structure: + */ struct DFRefHeader { void *ptr; }; + /* + * The system might be extended to carry some simple + * objects inline inside the reference buffer. + */ inline bool is_self_contained(DFRefHeader *ptr) { void **pp = &ptr->ptr; return **(void****)pp == (pp + 1); } } +/** + * Report an error while accessing a field (index = field name). + */ static void field_error(lua_State *state, int index, const char *err, const char *mode) { lua_getfield(state, UPVAL_METATABLE, "__metatable"); @@ -83,6 +132,9 @@ static void field_error(lua_State *state, int index, const char *err, const char mode, (cname ? cname : "?"), (fname ? fname : "?"), err); } +/* + * If is_method is true, these use UPVAL_TYPETABLE to save a hash lookup. + */ static void 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); @@ -96,7 +148,9 @@ void *DFHack::GetDFObject(lua_State *state, type_identity *type, int val_index) return get_object_internal(state, type, val_index, false); } -/* Primitive identity methods */ +/************************************** + * Identity object read/write methods * + **************************************/ int constructed_identity::lua_read(lua_State *state, int fname_idx, void *ptr) { @@ -274,6 +328,9 @@ static int change_error(lua_State *state) luaL_error(state, "Attempt to change a read-only table.\n"); } +/** + * Wrap a table so that it can't be modified. + */ static void freeze_table(lua_State *state, bool leave_metatable = false, const char *name = NULL) { // rv = {}; setmetatable(rv, { __index = in, __newindex = change_error, __metatable = name }) @@ -297,6 +354,10 @@ static void freeze_table(lua_State *state, bool leave_metatable = false, const c lua_pop(state, 1); } +/** + * Look up the key on the stack in DFHACK_TYPETABLE; + * if found, put result on the stack and return true. + */ static bool LookupTypeInfo(lua_State *state, bool in_method) { // stack: [lookup key] @@ -356,6 +417,9 @@ static void SaveTypeInfo(lua_State *state, void *node) static void BuildTypeMetatable(lua_State *state, type_identity *type); +/** + * Push the pointer as DF object ref using metatable on the stack. + */ static void push_object_ref(lua_State *state, void *ptr) { // stack: [metatable] @@ -367,8 +431,15 @@ static void push_object_ref(lua_State *state, void *ptr) // stack: [userdata] } +/** + * Push the pointer using given identity. + */ static void push_object_internal(lua_State *state, type_identity *type, void *ptr, bool in_method) { + /* + * If NULL pointer or no type, push something simple + */ + if (!ptr || !type) { if (!ptr) @@ -379,7 +450,10 @@ static void push_object_internal(lua_State *state, type_identity *type, void *pt return; } - // Resolve actual class using vtable + /* + * Resolve actual class using vtable + */ + if (type->type() == IDTYPE_CLASS) { virtual_identity *class_vid = virtual_identity::get(virtual_ptr(ptr)); @@ -387,6 +461,10 @@ static void push_object_internal(lua_State *state, type_identity *type, void *pt type = class_vid; } + /* + * Resolve metatable by identity, and push the object + */ + lua_pushlightuserdata(state, type); // () -> type if (!LookupTypeInfo(state, in_method)) // type -> metatable? @@ -395,11 +473,20 @@ static void push_object_internal(lua_State *state, type_identity *type, void *pt push_object_ref(state, ptr); // metatable -> userdata } +/** + * Verify that the value matches the identity, and return ptr if so. + */ static void *get_object_internal(lua_State *state, type_identity *type, int val_index, bool in_method) { + /* + * Non-userdata results in NULL; nil for NULL gets handled here too. + */ if (!lua_isuserdata(state, val_index)) return NULL; + /* + * Light user data is allowed with null type; otherwise bail out. + */ if (!lua_getmetatable(state, val_index)) // () -> metatable? { if (!type && lua_islightuserdata(state, val_index)) @@ -408,7 +495,10 @@ static void *get_object_internal(lua_State *state, type_identity *type, int val_ return NULL; } - // Verify known metatable and correct type + /* + * Verify that the metatable is known, and refers to the correct type. + * Here doing reverse lookup of identity by metatable. + */ if (!LookupTypeInfo(state, in_method)) // metatable -> type? return NULL; @@ -420,11 +510,18 @@ static void *get_object_internal(lua_State *state, type_identity *type, int val_ if (!ok) return NULL; + /* + * Finally decode the reference. + */ auto ref = (DFRefHeader*)lua_touserdata(state, val_index); return ref->ptr; } - +/** + * Metamethod: compare two DF object references. + * + * Equal if same pointer and same metatable. + */ static int meta_ptr_compare(lua_State *state) { if (!lua_isuserdata(state, 1) || !lua_isuserdata(state, 2) || @@ -447,6 +544,9 @@ static int meta_ptr_compare(lua_State *state) return 1; } +/** + * Resolve the field name in UPVAL_FIELDTABLE, die if not found. + */ static void lookup_field(lua_State *state, int index, const char *mode) { lua_pushvalue(state, index); @@ -465,6 +565,10 @@ static void *find_field(lua_State *state, int index, const char *mode) return p; } +/** + * Verify that the object is a DF ref with UPVAL_METATABLE. + * If everything ok, extract the address. + */ static uint8_t *get_object_addr(lua_State *state, int obj, int field, const char *mode) { if (!lua_isuserdata(state, obj) || @@ -548,6 +652,9 @@ static void write_field(lua_State *state, const struct_field_info *field, void * } } +/** + * Metamethod: represent a type node as string. + */ static int meta_type_tostring(lua_State *state) { if (!lua_getmetatable(state, 1)) @@ -560,6 +667,9 @@ static int meta_type_tostring(lua_State *state) return 1; } +/** + * Metamethod: represent a DF object reference as string. + */ static int meta_ptr_tostring(lua_State *state) { uint8_t *ptr = get_object_addr(state, 1, 0, "access"); @@ -571,12 +681,16 @@ static int meta_ptr_tostring(lua_State *state) return 1; } +// Resolve the field in the metatable and return static int get_metafield(lua_State *state) { lua_rawget(state, UPVAL_METATABLE); return 1; } +/** + * Metamethod: __index for structures. + */ static int meta_struct_index(lua_State *state) { uint8_t *ptr = get_object_addr(state, 1, 2, "read"); @@ -586,6 +700,9 @@ static int meta_struct_index(lua_State *state) return read_field(state, field, ptr + field->offset); } +/** + * Metamethod: __newindex for structures. + */ static int meta_struct_newindex(lua_State *state) { uint8_t *ptr = get_object_addr(state, 1, 2, "write"); @@ -594,6 +711,10 @@ static int meta_struct_newindex(lua_State *state) return 0; } +/** + * Metamethod: __index for primitives, i.e. simple object references. + * Fields point to identity, or NULL for metafields. + */ static int meta_primitive_index(lua_State *state) { uint8_t *ptr = get_object_addr(state, 1, 2, "read"); @@ -603,6 +724,9 @@ static int meta_primitive_index(lua_State *state) return type->lua_read(state, 2, ptr); } +/** + * Metamethod: __newindex for primitives. + */ static int meta_primitive_newindex(lua_State *state) { uint8_t *ptr = get_object_addr(state, 1, 2, "write"); @@ -611,6 +735,9 @@ static int meta_primitive_newindex(lua_State *state) return 0; } +/** + * Metamethod: __len for containers. + */ static int meta_container_len(lua_State *state) { uint8_t *ptr = get_object_addr(state, 1, 0, "get length"); @@ -620,54 +747,155 @@ static int meta_container_len(lua_State *state) return 1; } +/** + * Field lookup for containers: + * + * - Numbers are indices and handled directly. + * - NULL userdata are metafields; push and exit; + */ 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); + + if (lua_isuserdata(state, -1) && !lua_touserdata(state, -1)) + { + lua_pop(state, 1); + get_metafield(state); + return 0; + } + return -1; } -static int check_container_index(lua_State *state, container_identity *container, void *ptr, +/** + * Index verification: number and in range. + */ +static int check_container_index(lua_State *state, int len, 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; } +/** + * Metamethod: __index for containers. + */ 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); - } + if (!iidx) + return 1; auto id = (container_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID); - int idx = check_container_index(state, id, ptr, 2, iidx, "read"); + int len = id->lua_item_count(state, ptr); + int idx = check_container_index(state, len, 2, iidx, "read"); return id->lua_item_read(state, 2, ptr, idx); } +/** + * Metamethod: __index for containers. + */ 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"); + int len = id->lua_item_count(state, ptr); + int idx = check_container_index(state, len, 2, iidx, "write"); id->lua_item_write(state, 2, ptr, idx, 3); return 0; } +/** + * Metamethod: __len for bitfields. + */ +static int meta_bitfield_len(lua_State *state) +{ + uint8_t *ptr = get_object_addr(state, 1, 0, "get size"); + auto id = (bitfield_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID); + lua_pushinteger(state, id->getNumBits()); + return 1; +} + +/** + * Metamethod: __index for bitfields. + */ +static int meta_bitfield_index(lua_State *state) +{ + uint8_t *ptr = get_object_addr(state, 1, 2, "read"); + int iidx = lookup_container_field(state, 2, "read"); + if (!iidx) + return 1; + + auto id = (bitfield_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID); + + // whole + if (lua_isuserdata(state, iidx) && lua_touserdata(state, iidx) == id) + { + lua_Integer intv = 0; + memcpy(&intv, ptr, std::min(sizeof(intv), size_t(id->byte_size()))); + lua_pushinteger(state, intv); + return 1; + } + + int idx = check_container_index(state, id->getNumBits(), 2, iidx, "read"); + int size = id->getBits()[idx].size; + + int value = getBitfieldField(ptr, idx, size); + if (size <= 1) + lua_pushboolean(state, value != 0); + else + lua_pushinteger(state, value); + return 1; +} + +/** + * Metamethod: __newindex for bitfields. + */ +static int meta_bitfield_newindex(lua_State *state) +{ + uint8_t *ptr = get_object_addr(state, 1, 2, "write"); + int iidx = lookup_container_field(state, 2, "write"); + + auto id = (bitfield_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID); + + // whole + if (lua_isuserdata(state, iidx) && lua_touserdata(state, iidx) == id) + { + if (!lua_isnumber(state, 3)) + field_error(state, 2, "number expected", "write"); + + lua_Integer intv = lua_tointeger(state, 3); + memcpy(ptr, &intv, std::min(sizeof(intv), size_t(id->byte_size()))); + return 0; + } + + int idx = check_container_index(state, id->getNumBits(), 2, iidx, "write"); + int size = id->getBits()[idx].size; + + if (lua_isboolean(state, 3) || lua_isnil(state, 3)) + setBitfieldField(ptr, idx, size, lua_toboolean(state, 3)); + else if (lua_isnumber(state, 3)) + setBitfieldField(ptr, idx, size, lua_tointeger(state, 3)); + else + field_error(state, 2, "number or boolean expected", "write"); + return 0; +} + +/** + * Metamethod: __index for df.global + */ static int meta_global_index(lua_State *state) { auto field = (struct_field_info*)find_field(state, 2, "read"); @@ -679,6 +907,9 @@ static int meta_global_index(lua_State *state) return read_field(state, field, ptr); } +/** + * Metamethod: __newindex for df.global + */ static int meta_global_newindex(lua_State *state) { auto field = (struct_field_info*)find_field(state, 2, "write"); @@ -689,6 +920,9 @@ static int meta_global_newindex(lua_State *state) return 0; } +/** + * Add fields in the array to the UPVAL_FIELDTABLE candidates on the stack. + */ static void IndexFields(lua_State *state, const struct_field_info *fields) { // stack: read write @@ -722,12 +956,18 @@ static void IndexFields(lua_State *state, const struct_field_info *fields) } } +/** + * Enable a metafield by injecting an entry into a UPVAL_FIELDTABLE. + */ 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); } +/** + * Set metatable properties common to all actual DF object references. + */ static void SetPtrMethods(lua_State *state, int meta_idx, int read_idx) { lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_COMPARE_NAME); @@ -739,8 +979,12 @@ static void SetPtrMethods(lua_State *state, int meta_idx, int read_idx) lua_setfield(state, meta_idx, "__tostring"); EnableMetaField(state, read_idx, "_type"); + EnableMetaField(state, read_idx, "_kind"); } +/** + * Add a struct-style (3 upvalues) metamethod to the metatable. + */ static void SetStructMethod(lua_State *state, int meta_idx, int ftable_idx, lua_CFunction function, const char *name) { @@ -751,7 +995,10 @@ 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) +/** + * Make a metatable with most common fields, and two empty tables for UPVAL_FIELDTABLE. + */ +static void MakeMetatable(lua_State *state, type_identity *type, const char *kind) { int base = lua_gettop(state); lua_newtable(state); // metatable @@ -770,16 +1017,22 @@ static void MakeMetatable(lua_State *state, type_identity *type) } lua_setfield(state, base+1, "_type"); + lua_pushstring(state, kind); + lua_setfield(state, base+1, "_kind"); + lua_newtable(state); // read lua_newtable(state); // write } +/** + * Make a struct-style object metatable. + */ static void MakeFieldMetatable(lua_State *state, struct_identity *pstruct, lua_CFunction reader, lua_CFunction writer) { int base = lua_gettop(state); - MakeMetatable(state, pstruct); // meta, read, write + MakeMetatable(state, pstruct, "struct"); // meta, read, write for (struct_identity *p = pstruct; p; p = p->getParent()) { @@ -792,11 +1045,14 @@ static void MakeFieldMetatable(lua_State *state, struct_identity *pstruct, // returns: [metatable readfields writefields]; } +/** + * Make a primitive-style metatable + */ static void MakePrimitiveMetatable(lua_State *state, type_identity *type) { int base = lua_gettop(state); - MakeMetatable(state, type); + MakeMetatable(state, type, "primitive"); SetPtrMethods(state, base+1, base+2); EnableMetaField(state, base+2, "value", type); @@ -806,6 +1062,9 @@ static void MakePrimitiveMetatable(lua_State *state, type_identity *type) SetStructMethod(state, base+1, base+3, meta_primitive_newindex, "__newindex"); } +/** + * Add a 6 upvalue metamethod to the metatable. + */ 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) @@ -825,6 +1084,10 @@ static void SetContainerMethod(lua_State *state, int meta_idx, int ftable_idx, lua_setfield(state, meta_idx, name); } +/** + * If ienum refers to a valid enum, attach its keys to UPVAL_FIELDTABLE, + * and the enum itself to the _enum metafield. + */ static void AttachEnumKeys(lua_State *state, int base, type_identity *ienum) { LookupInTable(state, ienum, DFHACK_ENUM_TABLE_NAME); @@ -840,14 +1103,22 @@ static void AttachEnumKeys(lua_State *state, int base, type_identity *ienum) } else lua_pop(state, 1); + + LookupInTable(state, ienum, DFHACK_TYPEID_TABLE_NAME); + lua_setfield(state, base+1, "_enum"); + + EnableMetaField(state, base+2, "_enum"); } +/** + * Make a container-style object metatable. + */ static void MakeContainerMetatable(lua_State *state, container_identity *type, type_identity *item, int count, type_identity *ienum) { int base = lua_gettop(state); - MakeMetatable(state, type); + MakeMetatable(state, type, "container"); SetPtrMethods(state, base+1, base+2); lua_pushstring(state, type->getFullName(item).c_str()); @@ -871,6 +1142,9 @@ static void MakeContainerMetatable(lua_State *state, container_identity *type, AttachEnumKeys(state, base, ienum); } +/* + * Metatable construction identity methods. + */ void type_identity::build_metatable(lua_State *state) { MakePrimitiveMetatable(state, this); @@ -891,6 +1165,23 @@ void pointer_identity::build_metatable(lua_State *state) EnableMetaField(state, base+3, "target", this); } +void bitfield_identity::build_metatable(lua_State *state) +{ + int base = lua_gettop(state); + + MakeMetatable(state, this, "bitfield"); + SetPtrMethods(state, base+1, base+2); + + SetContainerMethod(state, base+1, base+2, meta_bitfield_len, "__len", this, NULL, -1); + SetContainerMethod(state, base+1, base+2, meta_bitfield_index, "__index", this, NULL, -1); + SetContainerMethod(state, base+1, base+3, meta_bitfield_newindex, "__newindex", this, NULL, -1); + + AttachEnumKeys(state, base, this); + + EnableMetaField(state, base+2, "whole", this); + EnableMetaField(state, base+3, "whole", this); +} + void struct_identity::build_metatable(lua_State *state) { int base = lua_gettop(state); @@ -912,6 +1203,10 @@ static void BuildTypeMetatable(lua_State *state, type_identity *type) SaveTypeInfo(state, type); } +/** + * Construct a metatable for an object type folded into the field descriptor. + * This is done to reduce compile-time symbol table bloat due to templates. + */ static void GetAdHocMetatable(lua_State *state, const struct_field_info *field) { lua_pushlightuserdata(state, (void*)field); @@ -947,8 +1242,23 @@ static void GetAdHocMetatable(lua_State *state, const struct_field_info *field) } } +/* + * Recursive walk of scopes to construct the df... tree. + */ + static void RenderTypeChildren(lua_State *state, const std::vector &children); +static void AssociateId(lua_State *state, int table, int val, const char *name) +{ + lua_pushinteger(state, val); + lua_pushstring(state, name); + lua_dup(state); + lua_pushinteger(state, val); + + lua_rawset(state, table); + lua_rawset(state, table); +} + static void RenderType(lua_State *state, compound_identity *node) { assert(node->getName()); @@ -965,24 +1275,29 @@ static void RenderType(lua_State *state, compound_identity *node) switch (node->type()) { + case IDTYPE_STRUCT: + lua_pushstring(state, "struct-type"); + lua_setfield(state, base, "_kind"); + break; + + case IDTYPE_CLASS: + lua_pushstring(state, "class-type"); + lua_setfield(state, base, "_kind"); + break; + case IDTYPE_ENUM: { + lua_pushstring(state, "enum-type"); + lua_setfield(state, base, "_kind"); + enum_identity *eid = (enum_identity*)node; const char *const *keys = eid->getKeys(); // For enums, set mapping between keys and values for (int64_t i = eid->getFirstItem(), j = 0; i <= eid->getLastItem(); i++, j++) { - if (!keys[j]) - continue; - - lua_pushinteger(state, i); - lua_pushstring(state, keys[j]); - lua_dup(state); - lua_pushinteger(state, i); - - lua_rawset(state, base); - lua_rawset(state, base); + if (keys[j]) + AssociateId(state, base, i, keys[j]); } if (eid->getFirstItem() <= eid->getLastItem()) @@ -998,6 +1313,32 @@ static void RenderType(lua_State *state, compound_identity *node) } break; + case IDTYPE_BITFIELD: + { + lua_pushstring(state, "bitfield-type"); + lua_setfield(state, base, "_kind"); + + bitfield_identity *eid = (bitfield_identity*)node; + auto bits = eid->getBits(); + + for (int i = 0; i < eid->getNumBits(); i++) + { + if (bits[i].name) + AssociateId(state, base, i, bits[i].name); + if (bits[i].size > 1) + i += bits[i].size-1; + } + + lua_pushinteger(state, 0); + lua_setfield(state, base, "_first_item"); + + lua_pushinteger(state, eid->getNumBits()-1); + lua_setfield(state, base, "_last_item"); + + SaveInTable(state, node, DFHACK_ENUM_TABLE_NAME); + } + break; + default: break; } @@ -1081,6 +1422,10 @@ static void DoAttach(lua_State *state) lua_pop(state, 1); } +/** + * Initialize access to DF objects from the interpreter + * context, unless it has already been done. + */ void DFHack::AttachDFGlobals(lua_State *state) { if (luaL_newmetatable(state, DFHACK_TYPETABLE_NAME)) diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index a466ea9d8..9ec266fab 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -85,10 +85,11 @@ namespace DFHack virtual identity_type type() = 0; + virtual std::string getFullName() = 0; + + // For internal use in the lua wrapper 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; - - virtual std::string getFullName() = 0; virtual void build_metatable(lua_State *state); virtual bool isContainer() { return false; } @@ -159,6 +160,8 @@ namespace DFHack int getNumBits() { return num_bits; } const bitfield_item_info *getBits() { return bits; } + + virtual void build_metatable(lua_State *state); }; class DFHACK_EXPORT enum_identity : public compound_identity { @@ -317,9 +320,21 @@ namespace DFHack void InitDataDefGlobals(Core *core); + // LUA wrapper + + /** + * Make DF objects available to the given interpreter. + */ DFHACK_EXPORT void AttachDFGlobals(lua_State *state); + /** + * Push the pointer onto the stack as a wrapped DF object of the given type. + */ DFHACK_EXPORT void PushDFObject(lua_State *state, type_identity *type, void *ptr); + + /** + * Check that the value is a wrapped DF object of the given type, and if so return the pointer. + */ DFHACK_EXPORT void *GetDFObject(lua_State *state, type_identity *type, int val_index); template @@ -620,6 +635,24 @@ namespace DFHack { flagarray_to_string(&tmp, val); return join_strings(sep, tmp); } + + // LUA wrapper + + /** + * Push the pointer onto the stack as a wrapped DF object of a specific type. + */ + template + void PushDFObject(lua_State *state, T *ptr) { + PushDFObject(state, df::identity_traits::get(), ptr); + } + + /** + * Check that the value is a wrapped DF object of the correct type, and if so return the pointer. + */ + template + T *GetDFObject(lua_State *state, int val_index) { + return GetDFObject(state, df::identity_traits::get(), val_index); + } } #define ENUM_ATTR(enum,attr,val) (df::enum_traits::attrs(val).attr) From 27824642d90a8bca445af4e6f5d51a89d8317708 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 22 Mar 2012 10:56:32 +0400 Subject: [PATCH 08/16] Minor refactoring of container indexing and object allocation. --- library/DataDefs.cpp | 39 +++++++++++++++++++++++++--- library/LuaWrapper.cpp | 25 +++++------------- library/include/DataDefs.h | 47 ++++++++++++++++++++-------------- library/include/DataIdentity.h | 38 +++++++++++++++++---------- library/xml | 2 +- 5 files changed, 95 insertions(+), 56 deletions(-) diff --git a/library/DataDefs.cpp b/library/DataDefs.cpp index 9576afb7b..6fcd1141a 100644 --- a/library/DataDefs.cpp +++ b/library/DataDefs.cpp @@ -40,6 +40,37 @@ distribution. using namespace DFHack; + +void *type_identity::do_allocate_pod() { + void *p = malloc(size); + memset(p, 0, size); + return p; +} + +void type_identity::do_copy_pod(void *tgt, const void *src) { + memmove(tgt, src, size); +}; + +void *type_identity::allocate() { + if (can_allocate()) + return do_allocate(); + else + return NULL; +} + +bool type_identity::copy(void *tgt, const void *src) { + if (can_allocate() && tgt && src) + do_copy(tgt, src); + else + return false; +} + +void *enum_identity::do_allocate() { + void *p = malloc(byte_size()); + memcpy(p, &first_item_value, std::min(byte_size(), sizeof(int64_t))); + return p; +} + /* The order of global object constructor calls is * undefined between compilation units. Therefore, * this list has to be plain data, so that it gets @@ -95,19 +126,19 @@ void compound_identity::Init(Core *core) */ } -bitfield_identity::bitfield_identity(size_t size, TAllocateFn alloc, +bitfield_identity::bitfield_identity(size_t size, compound_identity *scope_parent, const char *dfhack_name, int num_bits, const bitfield_item_info *bits) - : compound_identity(size, alloc, scope_parent, dfhack_name), bits(bits), num_bits(num_bits) + : compound_identity(size, NULL, scope_parent, dfhack_name), bits(bits), num_bits(num_bits) { } -enum_identity::enum_identity(size_t size, TAllocateFn alloc, +enum_identity::enum_identity(size_t size, 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), + : compound_identity(size, NULL, scope_parent, dfhack_name), first_item_value(first_item_value), last_item_value(last_item_value), keys(keys), base_type(base_type) { diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp index a7d830474..770f8f5af 100644 --- a/library/LuaWrapper.cpp +++ b/library/LuaWrapper.cpp @@ -265,29 +265,29 @@ int container_identity::lua_item_count(lua_State *state, void *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); + void *pitem = item_pointer(id, ptr, idx); 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); + void *pitem = item_pointer(id, ptr, idx); 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); + void *pitem = item_pointer(&df::identity_traits::identity, ptr, idx); 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); + void *pitem = item_pointer(&df::identity_traits::identity, ptr, idx); df::pointer_identity::lua_write(state, fname_idx, pitem, id, val_index); } @@ -307,25 +307,12 @@ void bit_container_identity::lua_item_write(lua_State *state, int fname_idx, voi 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) { luaL_error(state, "Attempt to change a read-only table.\n"); + return 0; } /** @@ -618,6 +605,8 @@ static int read_field(lua_State *state, const struct_field_info *field, void *pt case struct_field_info::END: return 0; } + + return 0; } static void write_field(lua_State *state, const struct_field_info *field, void *ptr, int value_idx) diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index 9ec266fab..da9ffd4b5 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -69,14 +69,12 @@ namespace DFHack protected: type_identity(size_t size) : size(size) {}; - virtual void *do_instantiate() { - void *p = malloc(size); - memset(p, 0, size); - return p; - } - virtual void do_copy(void *tgt, const void *src) { - memmove(tgt, src, size); - }; + void *do_allocate_pod(); + void do_copy_pod(void *tgt, const void *src); + + virtual bool can_allocate() { return true; } + virtual void *do_allocate() { return do_allocate_pod(); } + virtual void do_copy(void *tgt, const void *src) { do_copy_pod(tgt, src); } public: virtual ~type_identity() {} @@ -93,6 +91,9 @@ namespace DFHack virtual void build_metatable(lua_State *state); virtual bool isContainer() { return false; } + + void *allocate(); + bool copy(void *tgt, const void *src); }; class DFHACK_EXPORT constructed_identity : public type_identity { @@ -102,13 +103,9 @@ namespace DFHack constructed_identity(size_t size, TAllocateFn alloc) : type_identity(size), allocator(alloc) {}; - virtual void *do_instantiate() { - return allocator ? allocator(NULL,NULL) : type_identity::do_instantiate(); - } - virtual void do_copy(void *tgt, const void *src) { - if (allocator) allocator(tgt,src); - else type_identity::do_copy(tgt, src); - }; + virtual bool can_allocate() { return (allocator != NULL); } + virtual void *do_allocate() { return allocator(NULL,NULL); } + virtual void do_copy(void *tgt, const void *src) { allocator(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); @@ -151,8 +148,13 @@ namespace DFHack const bitfield_item_info *bits; int num_bits; + protected: + virtual bool can_allocate() { return true; } + virtual void *do_allocate() { return do_allocate_pod(); } + virtual void do_copy(void *tgt, const void *src) { do_copy_pod(tgt, src); } + public: - bitfield_identity(size_t size, TAllocateFn alloc, + bitfield_identity(size_t size, compound_identity *scope_parent, const char *dfhack_name, int num_bits, const bitfield_item_info *bits); @@ -171,8 +173,13 @@ namespace DFHack type_identity *base_type; + protected: + virtual bool can_allocate() { return true; } + virtual void *do_allocate(); + virtual void do_copy(void *tgt, const void *src) { do_copy_pod(tgt, src); } + public: - enum_identity(size_t size, TAllocateFn alloc, + enum_identity(size_t size, compound_identity *scope_parent, const char *dfhack_name, type_identity *base_type, int64_t first_item_value, int64_t last_item_value, @@ -266,6 +273,8 @@ namespace DFHack static void *get_vtable(virtual_ptr instance_ptr) { return *(void**)instance_ptr; } + bool can_allocate() { return struct_identity::can_allocate() && (vtable_ptr != NULL); } + public: virtual_identity(size_t size, TAllocateFn alloc, const char *dfhack_name, const char *original_name, @@ -295,8 +304,8 @@ namespace DFHack } public: - bool can_instantiate() { return (vtable_ptr != NULL); } - virtual_ptr instantiate() { return can_instantiate() ? (virtual_ptr)do_instantiate() : NULL; } + bool can_instantiate() { return can_allocate(); } + virtual_ptr instantiate() { return can_instantiate() ? (virtual_ptr)do_allocate() : NULL; } static virtual_ptr clone(virtual_ptr obj); public: diff --git a/library/include/DataIdentity.h b/library/include/DataIdentity.h index ac5903beb..987793828 100644 --- a/library/include/DataIdentity.h +++ b/library/include/DataIdentity.h @@ -92,7 +92,7 @@ namespace DFHack protected: virtual int item_count(void *ptr) = 0; - virtual void *item_pointer(void *ptr, int idx) = 0; + virtual void *item_pointer(type_identity *item, void *ptr, int idx) = 0; }; class DFHACK_EXPORT ptr_container_identity : public container_identity { @@ -122,7 +122,7 @@ namespace DFHack virtual void lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index); protected: - virtual void *item_pointer(void *, int) { return NULL; } + virtual void *item_pointer(type_identity *, void *, int) { return NULL; } virtual bool get_item(void *ptr, int idx) = 0; virtual void set_item(void *ptr, int idx, bool val) = 0; @@ -185,6 +185,11 @@ namespace df class stl_ptr_vector_identity : public ptr_container_identity { public: + /* + * This class assumes that std::vector is equivalent + * in layout and behavior to std::vector for any T. + */ + stl_ptr_vector_identity(type_identity *item = NULL, enum_identity *ienum = NULL) : ptr_container_identity(sizeof(std::vector),allocator_fn >,item, ienum) {}; @@ -199,7 +204,7 @@ namespace df virtual int item_count(void *ptr) { return ((std::vector*)ptr)->size(); }; - virtual void *item_pointer(void *ptr, int idx) { + virtual void *item_pointer(type_identity *, void *ptr, int idx) { return &(*(std::vector*)ptr)[idx]; } }; @@ -220,12 +225,11 @@ namespace df static buffer_container_identity base_instance; - virtual int lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx); - virtual void lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index); - protected: virtual int item_count(void *ptr) { return size; } - virtual void *item_pointer(void *ptr, int idx) { return NULL; } + virtual void *item_pointer(type_identity *item, void *ptr, int idx) { + return ((uint8_t*)ptr) + idx * item->byte_size(); + } }; template @@ -243,29 +247,35 @@ namespace df protected: virtual int item_count(void *ptr) { return ((T*)ptr)->size(); } - virtual void *item_pointer(void *ptr, int idx) { return &(*(T*)ptr)[idx]; } + virtual void *item_pointer(type_identity *item, void *ptr, int idx) { + return &(*(T*)ptr)[idx]; + } }; - template class bit_array_identity : public bit_container_identity { public: - typedef BitArray container; + /* + * This class assumes that BitArray is equivalent + * in layout and behavior to BitArray for any T. + */ + + typedef BitArray container; bit_array_identity(enum_identity *ienum = NULL) : bit_container_identity(sizeof(container), &allocator_fn, ienum) {} std::string getFullName(type_identity *item) { - return "BitArray" + bit_container_identity::getFullName(item); + return "BitArray<>"; } protected: virtual int item_count(void *ptr) { return ((container*)ptr)->size * 8; } virtual bool get_item(void *ptr, int idx) { - return ((container*)ptr)->is_set(T(idx)); + return ((container*)ptr)->is_set(idx); } virtual void set_item(void *ptr, int idx, bool val) { - ((container*)ptr)->set(T(idx), val); + ((container*)ptr)->set(idx, val); } }; @@ -412,7 +422,7 @@ namespace df bit_container_identity *identity_traits >::get() { static type_identity *eid = identity_traits::get(); static enum_identity *reid = eid->type() == DFHack::IDTYPE_ENUM ? (enum_identity*)eid : NULL; - static bit_array_identity identity(reid); + static bit_array_identity identity(reid); return &identity; } diff --git a/library/xml b/library/xml index c90a2d499..b28296a8c 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit c90a2d499024319ea9aa4f98b3b61df7bba2fc62 +Subproject commit b28296a8c0c2e5aaa522840598738109018f0146 From 8f72a642b2400a05ef9e766bc0b1ba214c817dcc Mon Sep 17 00:00:00 2001 From: Warmist Date: Thu, 22 Mar 2012 21:47:33 +0200 Subject: [PATCH 09/16] Fixed getxyz(), getCreatureAtPos() (also returns nil/creature) and empregnate to work with look/talk --- plugins/Dfusion/luafiles/common.lua | 27 +++++++++++-------------- plugins/Dfusion/luafiles/tools/init.lua | 2 ++ 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/plugins/Dfusion/luafiles/common.lua b/plugins/Dfusion/luafiles/common.lua index bf41d7103..ecb5b5023 100644 --- a/plugins/Dfusion/luafiles/common.lua +++ b/plugins/Dfusion/luafiles/common.lua @@ -476,28 +476,25 @@ function getSelectedUnit() end end function getxyz() -- this will return pointers x,y and z coordinates. - local off=VersionInfo.getGroup("Position"):getAddress("cursor_xyz") -- lets find where in memory its being held - -- now lets read them (they are double words (or unsigned longs or 4 bits each) and go in sucesion - local x=engine.peekd(off) - local y=engine.peekd(off+4) --next is 4 from start - local z=engine.peekd(off+8) --next is 8 from start - --print("Pointer @:"..x..","..y..","..z) + local x=df.cursor.x + local y=df.cursor.y + local z=df.cursor.z return x,y,z -- return the coords end -function GetCreatureAtPos(x,y,z) -- gets the creature index @ x,y,z coord +function getCreatureAtPos(x,y,z) -- gets the creature index @ x,y,z coord --local x,y,z=getxyz() --get 'X' coords - local vector=engine.peek(VersionInfo.getGroup("Creatures"):getAddress("vector"),ptr_vector) -- load all creatures - for i = 0, vector:size()-1 do -- look into all creatures offsets - local curoff=vector:getval(i) -- get i-th creatures offset - local cx=engine.peek(curoff,ptr_Creature.x) --get its coordinates - local cy=engine.peek(curoff,ptr_Creature.y) - local cz=engine.peek(curoff,ptr_Creature.z) + local vector=df.world.units.all -- load all creatures + for i = 0, vector.size-1 do -- look into all creatures offsets + local curpos=vector[i]:deref().pos --get its coordinates + local cx=curpos.x + local cy=curpos.y + local cz=curpos.z if cx==x and cy==y and cz==z then --compare them - return i --return index + return vector[i]:deref() --return index end end print("Creature not found!") - return -1 + return nil end function Allocate(size) diff --git a/plugins/Dfusion/luafiles/tools/init.lua b/plugins/Dfusion/luafiles/tools/init.lua index a12a01481..630de564f 100644 --- a/plugins/Dfusion/luafiles/tools/init.lua +++ b/plugins/Dfusion/luafiles/tools/init.lua @@ -264,6 +264,8 @@ function tools.empregnate(unit) if unit==nil then unit=getSelectedUnit() end + if unit==nil then + unit=getCreatureAtPos(getxyz()) if unit==nil then error("Failed to empregnate. Unit not selected/valide") end From f25d15ce9c6efa9e76ad7afe4ccaac1762c390ee Mon Sep 17 00:00:00 2001 From: Warmist Date: Thu, 22 Mar 2012 23:27:25 +0200 Subject: [PATCH 10/16] Stupid mistake fixed --- plugins/Dfusion/luafiles/tools/init.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/Dfusion/luafiles/tools/init.lua b/plugins/Dfusion/luafiles/tools/init.lua index 630de564f..43747aba6 100644 --- a/plugins/Dfusion/luafiles/tools/init.lua +++ b/plugins/Dfusion/luafiles/tools/init.lua @@ -264,8 +264,11 @@ function tools.empregnate(unit) if unit==nil then unit=getSelectedUnit() end + if unit==nil then unit=getCreatureAtPos(getxyz()) + end + if unit==nil then error("Failed to empregnate. Unit not selected/valide") end From ead28db451b3f433bc241f3b2b83e04c924cbacd Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 23 Mar 2012 09:38:49 +0400 Subject: [PATCH 11/16] Remove the return type from lua_read, because it always returns 1. --- library/LuaWrapper.cpp | 68 ++++++++++++++++++---------------- library/include/DataDefs.h | 6 +-- library/include/DataIdentity.h | 16 ++++---- 3 files changed, 47 insertions(+), 43 deletions(-) diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp index 770f8f5af..7071d15d2 100644 --- a/library/LuaWrapper.cpp +++ b/library/LuaWrapper.cpp @@ -152,10 +152,9 @@ void *DFHack::GetDFObject(lua_State *state, type_identity *type, int val_index) * Identity object read/write methods * **************************************/ -int constructed_identity::lua_read(lua_State *state, int fname_idx, void *ptr) +void constructed_identity::lua_read(lua_State *state, int fname_idx, void *ptr) { push_object_internal(state, this, ptr); - return 1; } void constructed_identity::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) @@ -163,9 +162,9 @@ void constructed_identity::lua_write(lua_State *state, int fname_idx, void *ptr, field_error(state, fname_idx, "complex object", "write"); } -int enum_identity::lua_read(lua_State *state, int fname_idx, void *ptr) +void enum_identity::lua_read(lua_State *state, int fname_idx, void *ptr) { - return base_type->lua_read(state, fname_idx, ptr); + base_type->lua_read(state, fname_idx, ptr); } void enum_identity::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) @@ -173,10 +172,9 @@ void enum_identity::lua_write(lua_State *state, int fname_idx, void *ptr, int va 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) +void 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) @@ -187,10 +185,9 @@ void df::number_identity_base::lua_write(lua_State *state, int fname_idx, void * write(ptr, lua_tonumber(state, val_index)); } -int df::bool_identity::lua_read(lua_State *state, int fname_idx, void *ptr) +void 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) @@ -205,11 +202,10 @@ void df::bool_identity::lua_write(lua_State *state, int fname_idx, void *ptr, in 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) +void 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) @@ -222,15 +218,14 @@ void df::stl_string_identity::lua_write(lua_State *state, int fname_idx, void *p *(std::string*)ptr = std::string(bytes, size); } -int df::pointer_identity::lua_read(lua_State *state, int fname_idx, void *ptr, type_identity *target) +void df::pointer_identity::lua_read(lua_State *state, int fname_idx, void *ptr, type_identity *target) { push_object_internal(state, target, *(void**)ptr); - return 1; } -int df::pointer_identity::lua_read(lua_State *state, int fname_idx, void *ptr) +void df::pointer_identity::lua_read(lua_State *state, int fname_idx, void *ptr) { - return lua_read(state, fname_idx, ptr, target); + lua_read(state, fname_idx, ptr, target); } void df::pointer_identity::lua_write(lua_State *state, int fname_idx, void *ptr, @@ -263,11 +258,11 @@ int container_identity::lua_item_count(lua_State *state, void *ptr) return item_count(ptr); } -int container_identity::lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx) +void 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 = item_pointer(id, ptr, idx); - return id->lua_read(state, fname_idx, pitem); + 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) @@ -277,11 +272,11 @@ void container_identity::lua_item_write(lua_State *state, int fname_idx, void *p 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 ptr_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 = item_pointer(&df::identity_traits::identity, ptr, idx); - return df::pointer_identity::lua_read(state, fname_idx, pitem, id); + 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) @@ -291,10 +286,9 @@ void ptr_container_identity::lua_item_write(lua_State *state, int fname_idx, voi 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) +void 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) @@ -573,7 +567,7 @@ static uint8_t *get_object_addr(lua_State *state, int obj, int field, const char 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) +static void read_field(lua_State *state, const struct_field_info *field, void *ptr) { switch (field->mode) { @@ -581,32 +575,38 @@ static int read_field(lua_State *state, const struct_field_info *field, void *pt { int len = strnlen((char*)ptr, field->count); lua_pushlstring(state, (char*)ptr, len); - return 1; + return; } case struct_field_info::PRIMITIVE: case struct_field_info::SUBSTRUCT: - return field->type->lua_read(state, 2, ptr); + field->type->lua_read(state, 2, ptr); + return; case struct_field_info::POINTER: - return df::pointer_identity::lua_read(state, 2, ptr, field->type); + df::pointer_identity::lua_read(state, 2, ptr, field->type); + return; 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); + { + field->type->lua_read(state, 2, ptr); + return; + } + // fallthrough case struct_field_info::STATIC_ARRAY: case struct_field_info::STL_VECTOR_PTR: GetAdHocMetatable(state, field); push_object_ref(state, ptr); - return 1; + return; case struct_field_info::END: - return 0; + break; } - return 0; + lua_pushnil(state); } static void write_field(lua_State *state, const struct_field_info *field, void *ptr, int value_idx) @@ -686,7 +686,8 @@ static int meta_struct_index(lua_State *state) auto field = (struct_field_info*)find_field(state, 2, "read"); if (!field) return get_metafield(state); - return read_field(state, field, ptr + field->offset); + read_field(state, field, ptr + field->offset); + return 1; } /** @@ -710,7 +711,8 @@ static int meta_primitive_index(lua_State *state) auto type = (type_identity*)find_field(state, 2, "read"); if (!type) return get_metafield(state); - return type->lua_read(state, 2, ptr); + type->lua_read(state, 2, ptr); + return 1; } /** @@ -788,7 +790,8 @@ static int meta_container_index(lua_State *state) auto id = (container_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID); int len = id->lua_item_count(state, ptr); int idx = check_container_index(state, len, 2, iidx, "read"); - return id->lua_item_read(state, 2, ptr, idx); + id->lua_item_read(state, 2, ptr, idx); + return 1; } /** @@ -893,7 +896,8 @@ static int meta_global_index(lua_State *state) void *ptr = *(void**)field->offset; if (!ptr) field_error(state, 2, "global address not known", "read"); - return read_field(state, field, ptr); + read_field(state, field, ptr); + return 1; } /** diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index da9ffd4b5..17751d90d 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -86,7 +86,7 @@ namespace DFHack virtual std::string getFullName() = 0; // For internal use in the lua wrapper - virtual int lua_read(lua_State *state, int fname_idx, void *ptr) = 0; + virtual void 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; virtual void build_metatable(lua_State *state); @@ -107,7 +107,7 @@ namespace DFHack virtual void *do_allocate() { return allocator(NULL,NULL); } virtual void do_copy(void *tgt, const void *src) { allocator(tgt,src); } - virtual int lua_read(lua_State *state, int fname_idx, void *ptr); + virtual void 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); }; @@ -194,7 +194,7 @@ namespace DFHack type_identity *getBaseType() { return base_type; } - virtual int lua_read(lua_State *state, int fname_idx, void *ptr); + virtual void 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); }; diff --git a/library/include/DataIdentity.h b/library/include/DataIdentity.h index 987793828..77e43f99b 100644 --- a/library/include/DataIdentity.h +++ b/library/include/DataIdentity.h @@ -58,10 +58,10 @@ namespace DFHack std::string getFullName(); virtual void build_metatable(lua_State *state); - static int lua_read(lua_State *state, int fname_idx, void *ptr, type_identity *target); + static void lua_read(lua_State *state, int fname_idx, void *ptr, type_identity *target); static void lua_write(lua_State *state, int fname_idx, void *ptr, type_identity *target, int val_index); - virtual int lua_read(lua_State *state, int fname_idx, void *ptr); + virtual void 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); }; @@ -87,7 +87,7 @@ namespace DFHack int lua_item_count(lua_State *state, void *ptr); - virtual int lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx); + virtual void lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx); virtual void lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index); protected: @@ -105,7 +105,7 @@ namespace DFHack std::string getFullName(type_identity *item); - virtual int lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx); + virtual void lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx); virtual void lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index); }; @@ -118,7 +118,7 @@ namespace DFHack std::string getFullName(type_identity *item); - virtual int lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx); + virtual void lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx); virtual void lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index); protected: @@ -146,7 +146,7 @@ namespace df std::string getFullName() { return name; } - virtual int lua_read(lua_State *state, int fname_idx, void *ptr); + virtual void 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: @@ -169,7 +169,7 @@ namespace df std::string getFullName() { return "bool"; } - virtual int lua_read(lua_State *state, int fname_idx, void *ptr); + virtual void 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); }; @@ -179,7 +179,7 @@ namespace df std::string getFullName() { return "string"; } - virtual int lua_read(lua_State *state, int fname_idx, void *ptr); + virtual void 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); }; From ccc8fac166ad04e67b581da44918a504865811a7 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 23 Mar 2012 10:56:29 +0400 Subject: [PATCH 12/16] Get rid of the write mode field table in metamethods. Two separate tables can be confusing, e.g. if a builtin field overrides a writable custom one only in the read table. --- depends/lua/CMakeLists.txt | 3 + library/LuaWrapper.cpp | 123 +++++++++++++++------------------ library/include/DataIdentity.h | 1 - 3 files changed, 60 insertions(+), 67 deletions(-) diff --git a/depends/lua/CMakeLists.txt b/depends/lua/CMakeLists.txt index 6a97bd434..23ea6a485 100644 --- a/depends/lua/CMakeLists.txt +++ b/depends/lua/CMakeLists.txt @@ -1,6 +1,9 @@ PROJECT ( lua C ) CMAKE_MINIMUM_REQUIRED(VERSION 2.8) +# TODO: make this RelWithDebInfo only +ADD_DEFINITIONS(-DLUA_USE_APICHECK) + IF(WIN32) ADD_DEFINITIONS(-D_CRT_SECURE_NO_DEPRECATE ) ELSE() diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp index 7071d15d2..21938627b 100644 --- a/library/LuaWrapper.cpp +++ b/library/LuaWrapper.cpp @@ -127,7 +127,7 @@ static void field_error(lua_State *state, int index, const char *err, const char { lua_getfield(state, UPVAL_METATABLE, "__metatable"); const char *cname = lua_tostring(state, -1); - const char *fname = lua_tostring(state, index); + const char *fname = index ? lua_tostring(state, index) : "*"; luaL_error(state, "Cannot %s field %s.%s: %s.", mode, (cname ? cname : "?"), (fname ? fname : "?"), err); } @@ -335,6 +335,14 @@ static void freeze_table(lua_State *state, bool leave_metatable = false, const c lua_pop(state, 1); } +static void LookupInTable(lua_State *state, const char *tname) +{ + lua_getfield(state, LUA_REGISTRYINDEX, tname); + lua_swap(state); + lua_rawget(state, -2); + lua_remove(state, -2); +} + /** * Look up the key on the stack in DFHACK_TYPETABLE; * if found, put result on the stack and return true. @@ -349,10 +357,7 @@ static bool LookupTypeInfo(lua_State *state, bool in_method) } else { - lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPETABLE_NAME); - lua_swap(state); - lua_rawget(state, -2); - lua_remove(state, -2); + LookupInTable(state, DFHACK_TYPETABLE_NAME); } // stack: [info] @@ -556,7 +561,7 @@ static uint8_t *get_object_addr(lua_State *state, int obj, int field, const char !lua_getmetatable(state, obj)) field_error(state, field, "invalid object", mode); - if (!lua_equal(state, -1, UPVAL_METATABLE)) + if (!lua_rawequal(state, -1, UPVAL_METATABLE)) field_error(state, field, "invalid object metatable", mode); lua_pop(state, 1); @@ -697,6 +702,8 @@ static int meta_struct_newindex(lua_State *state) { uint8_t *ptr = get_object_addr(state, 1, 2, "write"); auto field = (struct_field_info*)find_field(state, 2, "write"); + if (!field) + field_error(state, 2, "builtin property", "write"); write_field(state, field, ptr + field->offset, 3); return 0; } @@ -722,6 +729,8 @@ 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"); + if (!type) + field_error(state, 2, "builtin property", "write"); type->lua_write(state, 2, ptr, 3); return 0; } @@ -744,15 +753,18 @@ static int meta_container_len(lua_State *state) * - Numbers are indices and handled directly. * - NULL userdata are metafields; push and exit; */ -static int lookup_container_field(lua_State *state, int field, const char *mode) +static int lookup_container_field(lua_State *state, int field, bool write = false) { if (lua_type(state, field) == LUA_TNUMBER) return field; - lookup_field(state, field, mode); + lookup_field(state, field, write ? "write" : "read"); if (lua_isuserdata(state, -1) && !lua_touserdata(state, -1)) { + if (write) + field_error(state, field, "builtin property", "write"); + lua_pop(state, 1); get_metafield(state); return 0; @@ -783,7 +795,7 @@ static int check_container_index(lua_State *state, int len, 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"); + int iidx = lookup_container_field(state, 2); if (!iidx) return 1; @@ -800,7 +812,7 @@ static int meta_container_index(lua_State *state) 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"); + int iidx = lookup_container_field(state, 2, true); auto id = (container_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID); int len = id->lua_item_count(state, ptr); @@ -826,7 +838,7 @@ static int meta_bitfield_len(lua_State *state) static int meta_bitfield_index(lua_State *state) { uint8_t *ptr = get_object_addr(state, 1, 2, "read"); - int iidx = lookup_container_field(state, 2, "read"); + int iidx = lookup_container_field(state, 2); if (!iidx) return 1; @@ -858,7 +870,7 @@ static int meta_bitfield_index(lua_State *state) static int meta_bitfield_newindex(lua_State *state) { uint8_t *ptr = get_object_addr(state, 1, 2, "write"); - int iidx = lookup_container_field(state, 2, "write"); + int iidx = lookup_container_field(state, 2, true); auto id = (bitfield_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID); @@ -906,6 +918,8 @@ static int meta_global_index(lua_State *state) static int meta_global_newindex(lua_State *state) { auto field = (struct_field_info*)find_field(state, 2, "write"); + if (!field) + field_error(state, 2, "builtin property", "write"); void *ptr = *(void**)field->offset; if (!ptr) field_error(state, 2, "global address not known", "write"); @@ -916,35 +930,24 @@ static int meta_global_newindex(lua_State *state) /** * Add fields in the array to the UPVAL_FIELDTABLE candidates on the stack. */ -static void IndexFields(lua_State *state, const struct_field_info *fields) +static void IndexFields(lua_State *state, struct_identity *pstruct) { - // stack: read write + // stack: fieldtable - int base = lua_gettop(state) - 2; + int base = lua_gettop(state); - for (; fields; ++fields) + for (struct_identity *p = pstruct; p; p = p->getParent()) { - switch (fields->mode) - { - case struct_field_info::END: - return; + auto fields = p->getFields(); - 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_rawset(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_rawset(state,base+1); + for (; fields; ++fields) + { + if (fields->mode == struct_field_info::END) break; + + lua_pushstring(state,fields->name); + lua_pushlightuserdata(state,(void*)fields); + lua_rawset(state,base); } } } @@ -989,7 +992,7 @@ static void SetStructMethod(lua_State *state, int meta_idx, int ftable_idx, } /** - * Make a metatable with most common fields, and two empty tables for UPVAL_FIELDTABLE. + * Make a metatable with most common fields, and an empty table for UPVAL_FIELDTABLE. */ static void MakeMetatable(lua_State *state, type_identity *type, const char *kind) { @@ -1005,6 +1008,7 @@ static void MakeMetatable(lua_State *state, type_identity *type, const char *kin LookupInTable(state, type, DFHACK_TYPEID_TABLE_NAME); if (lua_isnil(state, -1)) { + // Copy the string from __metatable if no real type lua_pop(state, 1); lua_getfield(state, base+1, "__metatable"); } @@ -1013,8 +1017,7 @@ static void MakeMetatable(lua_State *state, type_identity *type, const char *kin lua_pushstring(state, kind); lua_setfield(state, base+1, "_kind"); - lua_newtable(state); // read - lua_newtable(state); // write + lua_newtable(state); // fieldtable } /** @@ -1025,15 +1028,12 @@ static void MakeFieldMetatable(lua_State *state, struct_identity *pstruct, { int base = lua_gettop(state); - MakeMetatable(state, pstruct, "struct"); // meta, read, write + MakeMetatable(state, pstruct, "struct"); // meta, fields - for (struct_identity *p = pstruct; p; p = p->getParent()) - { - IndexFields(state, p->getFields()); - } + IndexFields(state, pstruct); SetStructMethod(state, base+1, base+2, reader, "__index"); - SetStructMethod(state, base+1, base+3, writer, "__newindex"); + SetStructMethod(state, base+1, base+2, writer, "__newindex"); // returns: [metatable readfields writefields]; } @@ -1046,13 +1046,13 @@ static void MakePrimitiveMetatable(lua_State *state, type_identity *type) int base = lua_gettop(state); MakeMetatable(state, type, "primitive"); + 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"); + SetStructMethod(state, base+1, base+2, meta_primitive_newindex, "__newindex"); } /** @@ -1090,9 +1090,7 @@ static void AttachEnumKeys(lua_State *state, int base, type_identity *ienum) 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); @@ -1114,6 +1112,7 @@ static void MakeContainerMetatable(lua_State *state, container_identity *type, MakeMetatable(state, type, "container"); SetPtrMethods(state, base+1, base+2); + // Update the type name using full info lua_pushstring(state, type->getFullName(item).c_str()); lua_dup(state); lua_setfield(state, base+1, "__metatable"); @@ -1130,7 +1129,7 @@ static void MakeContainerMetatable(lua_State *state, container_identity *type, 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); + SetContainerMethod(state, base+1, base+2, meta_container_newindex, "__newindex", type, item, count); AttachEnumKeys(state, base, ienum); } @@ -1148,31 +1147,21 @@ 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 bitfield_identity::build_metatable(lua_State *state) { int base = lua_gettop(state); MakeMetatable(state, this, "bitfield"); + SetPtrMethods(state, base+1, base+2); SetContainerMethod(state, base+1, base+2, meta_bitfield_len, "__len", this, NULL, -1); SetContainerMethod(state, base+1, base+2, meta_bitfield_index, "__index", this, NULL, -1); - SetContainerMethod(state, base+1, base+3, meta_bitfield_newindex, "__newindex", this, NULL, -1); + SetContainerMethod(state, base+1, base+2, meta_bitfield_newindex, "__newindex", this, NULL, -1); AttachEnumKeys(state, base, this); EnableMetaField(state, base+2, "whole", this); - EnableMetaField(state, base+3, "whole", this); } void struct_identity::build_metatable(lua_State *state) @@ -1191,7 +1180,7 @@ static void BuildTypeMetatable(lua_State *state, type_identity *type) { type->build_metatable(state); - lua_pop(state, 2); + lua_pop(state, 1); SaveTypeInfo(state, type); } @@ -1229,7 +1218,7 @@ static void GetAdHocMetatable(lua_State *state, const struct_field_info *field) luaL_error(state, "Invalid ad-hoc field: %d", field->mode); } - lua_pop(state, 2); + lua_pop(state, 1); SaveTypeInfo(state, (void*)field); } @@ -1263,9 +1252,6 @@ static void RenderType(lua_State *state, compound_identity *node) int base = lua_gettop(state); - lua_pushlightuserdata(state, node); - lua_setfield(state, base, "_identity"); - switch (node->type()) { case IDTYPE_STRUCT: @@ -1344,12 +1330,14 @@ static void RenderType(lua_State *state, compound_identity *node) { BuildTypeMetatable(state, node); + // Set metatable for the inner table lua_dup(state); lua_setmetatable(state, base); lua_swap(state); // -> meta curtable freeze_table(state, true, "global"); + // Copy __newindex to the outer metatable lua_getfield(state, base, "__newindex"); lua_setfield(state, -2, "__newindex"); @@ -1363,6 +1351,9 @@ static void RenderType(lua_State *state, compound_identity *node) lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPE_TOSTRING_NAME); lua_setfield(state, -2, "__tostring"); + lua_pushlightuserdata(state, node); + lua_setfield(state, -2, "_identity"); + lua_pop(state, 1); SaveInTable(state, node, DFHACK_TYPEID_TABLE_NAME); diff --git a/library/include/DataIdentity.h b/library/include/DataIdentity.h index 77e43f99b..7b576b117 100644 --- a/library/include/DataIdentity.h +++ b/library/include/DataIdentity.h @@ -56,7 +56,6 @@ namespace DFHack type_identity *getTarget() { return target; } std::string getFullName(); - virtual void build_metatable(lua_State *state); static void lua_read(lua_State *state, int fname_idx, void *ptr, type_identity *target); static void lua_write(lua_State *state, int fname_idx, void *ptr, type_identity *target, int val_index); From 78437310d0e032880d19ee97eac67af60b369258 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 23 Mar 2012 11:30:54 +0400 Subject: [PATCH 13/16] Add a sizeof method/function to retrieve object/type size and address. --- library/LuaWrapper.cpp | 121 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 110 insertions(+), 11 deletions(-) diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp index 21938627b..a394b4cfd 100644 --- a/library/LuaWrapper.cpp +++ b/library/LuaWrapper.cpp @@ -67,6 +67,7 @@ inline void lua_swap(lua_State *state) { lua_insert(state, -2); } #define DFHACK_CHANGEERROR_NAME "DFHack::ChangeError" #define DFHACK_COMPARE_NAME "DFHack::ComparePtrs" #define DFHACK_TYPE_TOSTRING_NAME "DFHack::TypeToString" +#define DFHACK_SIZEOF_NAME "DFHack::Sizeof" /* * Upvalue: contents of DFHACK_TYPETABLE_NAME @@ -417,6 +418,14 @@ static void push_object_ref(lua_State *state, void *ptr) // stack: [userdata] } +static void *get_object_ref(lua_State *state, int val_index) +{ + assert(!lua_islightuserdata(state, val_index)); + + auto ref = (DFRefHeader*)lua_touserdata(state, val_index); + return ref->ptr; +} + /** * Push the pointer using given identity. */ @@ -499,8 +508,42 @@ static void *get_object_internal(lua_State *state, type_identity *type, int val_ /* * Finally decode the reference. */ - auto ref = (DFRefHeader*)lua_touserdata(state, val_index); - return ref->ptr; + return get_object_ref(state, val_index); +} + +/** + * Given a DF object reference or type, safely retrieve its identity pointer. + */ +static type_identity *get_object_identity(lua_State *state, int objidx, const char *ctx) +{ + if (!lua_getmetatable(state, objidx)) + luaL_error(state, "Invalid object in %s", ctx); + + // Verify object type validity + if (lua_isuserdata(state, objidx)) + { + lua_dup(state); + LookupInTable(state, DFHACK_TYPETABLE_NAME); + } + else + { + lua_pushvalue(state, objidx); + LookupInTable(state, DFHACK_TYPEID_TABLE_NAME); + } + + if (lua_isnil(state, -1)) + luaL_error(state, "Invalid object metatable in %s", ctx); + lua_pop(state, 1); + + // Extract identity from metatable + lua_getfield(state, -1, "_identity"); + + type_identity *id = (type_identity*)lua_touserdata(state, -1); + if (!id) + luaL_error(state, "Invalid object identity in %s", ctx); + + lua_pop(state, 2); + return id; } /** @@ -517,19 +560,63 @@ static int meta_ptr_compare(lua_State *state) return 1; } - if (!lua_equal(state, -1, -2)) + if (get_object_ref(state, 1) != get_object_ref(state, 2)) + { + lua_pushboolean(state, false); + return 1; + } + + if (!lua_rawequal(state, -1, -2)) { // todo: nonidentical type comparison lua_pushboolean(state, false); return 1; } - auto ref1 = (DFRefHeader*)lua_touserdata(state, 1); - auto ref2 = (DFRefHeader*)lua_touserdata(state, 2); - lua_pushboolean(state, ref1->ptr == ref2->ptr); + lua_pushboolean(state, true); return 1; } +/** + * Method: sizeof for DF object references. + * + * Returns: size[, address] + */ +static int meta_sizeof(lua_State *state) +{ + int argc = lua_gettop(state); + + if (argc != 1) + luaL_error(state, "Usage: object:sizeof() or df.sizeof(object)"); + + // Two special cases: nil and lightuserdata for NULL and void* + if (lua_isnil(state, 1)) + { + lua_pushnil(state); + lua_pushinteger(state, 0); + return 2; + } + + if (lua_islightuserdata(state, 1)) + { + lua_pushnil(state); + lua_pushnumber(state, (size_t)lua_touserdata(state, 1)); + return 2; + } + + type_identity *id = get_object_identity(state, 1, "df.sizeof()"); + + lua_pushinteger(state, id->byte_size()); + + if (lua_isuserdata(state, 1)) + { + lua_pushnumber(state, (size_t)get_object_ref(state, 1)); + return 2; + } + else + return 1; +} + /** * Resolve the field name in UPVAL_FIELDTABLE, die if not found. */ @@ -566,8 +653,7 @@ static uint8_t *get_object_addr(lua_State *state, int obj, int field, const char lua_pop(state, 1); - auto ref = (DFRefHeader*)lua_touserdata(state, obj); - return (uint8_t*)ref->ptr; + return (uint8_t*)get_object_ref(state, obj); } static void GetAdHocMetatable(lua_State *state, const struct_field_info *field); @@ -847,9 +933,9 @@ static int meta_bitfield_index(lua_State *state) // whole if (lua_isuserdata(state, iidx) && lua_touserdata(state, iidx) == id) { - lua_Integer intv = 0; + size_t intv = 0; memcpy(&intv, ptr, std::min(sizeof(intv), size_t(id->byte_size()))); - lua_pushinteger(state, intv); + lua_pushnumber(state, intv); return 1; } @@ -880,7 +966,7 @@ static int meta_bitfield_newindex(lua_State *state) if (!lua_isnumber(state, 3)) field_error(state, 2, "number expected", "write"); - lua_Integer intv = lua_tointeger(state, 3); + size_t intv = (size_t)lua_tonumber(state, 3); memcpy(ptr, &intv, std::min(sizeof(intv), size_t(id->byte_size()))); return 0; } @@ -974,8 +1060,12 @@ static void SetPtrMethods(lua_State *state, int meta_idx, int read_idx) lua_pushcclosure(state, meta_ptr_tostring, 2); lua_setfield(state, meta_idx, "__tostring"); + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_SIZEOF_NAME); + lua_setfield(state, meta_idx, "sizeof"); + EnableMetaField(state, read_idx, "_type"); EnableMetaField(state, read_idx, "_kind"); + EnableMetaField(state, read_idx, "sizeof"); } /** @@ -1326,6 +1416,9 @@ static void RenderType(lua_State *state, compound_identity *node) assert(base == lua_gettop(state)); + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_SIZEOF_NAME); + lua_setfield(state, base, "sizeof"); + if (node->type() == IDTYPE_GLOBAL) { BuildTypeMetatable(state, node); @@ -1389,6 +1482,9 @@ static void DoAttach(lua_State *state) lua_pushcfunction(state, meta_type_tostring); lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_TYPE_TOSTRING_NAME); + lua_pushcfunction(state, meta_sizeof); + lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_SIZEOF_NAME); + luaL_register(state, "df", no_functions); { @@ -1398,6 +1494,9 @@ static void DoAttach(lua_State *state) // Render the type structure RenderTypeChildren(state, compound_identity::getTopScope()); + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_SIZEOF_NAME); + lua_setfield(state, -2, "sizeof"); + freeze_table(state, true, "df"); lua_remove(state, -2); lua_setmetatable(state, -2); From 6b2006361dfc2e976c70f8b5507acb9f8a7224db Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 23 Mar 2012 11:54:59 +0400 Subject: [PATCH 14/16] Add a _displace method that implements offsetting a pointer by an int. --- library/LuaWrapper.cpp | 75 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 2 deletions(-) diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp index a394b4cfd..9214a4c5d 100644 --- a/library/LuaWrapper.cpp +++ b/library/LuaWrapper.cpp @@ -68,6 +68,7 @@ inline void lua_swap(lua_State *state) { lua_insert(state, -2); } #define DFHACK_COMPARE_NAME "DFHack::ComparePtrs" #define DFHACK_TYPE_TOSTRING_NAME "DFHack::TypeToString" #define DFHACK_SIZEOF_NAME "DFHack::Sizeof" +#define DFHACK_DISPLACE_NAME "DFHack::Displace" /* * Upvalue: contents of DFHACK_TYPETABLE_NAME @@ -514,7 +515,8 @@ static void *get_object_internal(lua_State *state, type_identity *type, int val_ /** * Given a DF object reference or type, safely retrieve its identity pointer. */ -static type_identity *get_object_identity(lua_State *state, int objidx, const char *ctx) +static type_identity *get_object_identity(lua_State *state, int objidx, + const char *ctx, bool allow_type = false) { if (!lua_getmetatable(state, objidx)) luaL_error(state, "Invalid object in %s", ctx); @@ -527,6 +529,9 @@ static type_identity *get_object_identity(lua_State *state, int objidx, const ch } else { + if (!allow_type) + luaL_error(state, "Object expected in %s", ctx); + lua_pushvalue(state, objidx); LookupInTable(state, DFHACK_TYPEID_TABLE_NAME); } @@ -604,7 +609,7 @@ static int meta_sizeof(lua_State *state) return 2; } - type_identity *id = get_object_identity(state, 1, "df.sizeof()"); + type_identity *id = get_object_identity(state, 1, "df.sizeof()", true); lua_pushinteger(state, id->byte_size()); @@ -617,6 +622,62 @@ static int meta_sizeof(lua_State *state) return 1; } +/** + * Method: displace for DF object references. + * + * Returns: a reference with the same type, but modified address + */ +static int meta_displace(lua_State *state) +{ + int argc = lua_gettop(state); + + bool has_step = (argc >= 3); + if ((argc < 2 || argc > 3) || + !lua_isnumber(state, 2) || + (has_step && !lua_isnumber(state, 3))) + { + luaL_error(state, "Usage: object:_displace(index[,step]) or df._displace(object,...)"); + } + + int index = lua_tointeger(state, 2); + int step = has_step ? lua_tointeger(state, 3) : 1; + + // Two special cases: nil and lightuserdata for NULL and void* + if (lua_isnil(state, 1)) + { + lua_pushnil(state); + return 1; + } + + if (lua_islightuserdata(state, 1)) + { + if (!has_step) + luaL_error(state, "Step is mandatory in _displace of void*"); + + auto ptr = (uint8_t*)lua_touserdata(state, 1); + lua_pushlightuserdata(state, ptr + index*step); + return 1; + } + + type_identity *id = get_object_identity(state, 1, "df._displace()"); + + if (!has_step) + step = id->byte_size(); + + if (index == 0 || step == 0) + { + lua_pushvalue(state, 1); + } + else + { + auto ptr = (uint8_t*)get_object_ref(state, 1); + lua_getmetatable(state, 1); + push_object_ref(state, ptr + index*step); + } + + return 1; +} + /** * Resolve the field name in UPVAL_FIELDTABLE, die if not found. */ @@ -1063,9 +1124,14 @@ static void SetPtrMethods(lua_State *state, int meta_idx, int read_idx) lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_SIZEOF_NAME); lua_setfield(state, meta_idx, "sizeof"); + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_DISPLACE_NAME); + lua_setfield(state, meta_idx, "_displace"); + EnableMetaField(state, read_idx, "_type"); EnableMetaField(state, read_idx, "_kind"); + EnableMetaField(state, read_idx, "sizeof"); + EnableMetaField(state, read_idx, "_displace"); } /** @@ -1485,6 +1551,9 @@ static void DoAttach(lua_State *state) lua_pushcfunction(state, meta_sizeof); lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_SIZEOF_NAME); + lua_pushcfunction(state, meta_displace); + lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_DISPLACE_NAME); + luaL_register(state, "df", no_functions); { @@ -1496,6 +1565,8 @@ static void DoAttach(lua_State *state) lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_SIZEOF_NAME); lua_setfield(state, -2, "sizeof"); + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_DISPLACE_NAME); + lua_setfield(state, -2, "_displace"); freeze_table(state, true, "df"); lua_remove(state, -2); From 2b1f8aa2bbb2bc67a661c2930f6d379bae55ca75 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 23 Mar 2012 12:55:29 +0400 Subject: [PATCH 15/16] Add a _field method that returns refs to struct and container items. Hack: allocate ad-hoc pointer identities as full lua userdata. --- library/LuaWrapper.cpp | 145 +++++++++++++++++++++++++++++++-- library/include/DataIdentity.h | 3 + 2 files changed, 142 insertions(+), 6 deletions(-) diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp index 9214a4c5d..d6edef4e2 100644 --- a/library/LuaWrapper.cpp +++ b/library/LuaWrapper.cpp @@ -63,6 +63,11 @@ inline void lua_swap(lua_State *state) { lua_insert(state, -2); } */ #define DFHACK_ENUM_TABLE_NAME "DFHack::DFEnums" +/* + * Registry name: hash of pointer target identity <-> adhoc pointer identity userdata. + */ +#define DFHACK_PTR_IDTABLE_NAME "DFHack::PtrDFTypes" + // Function registry names #define DFHACK_CHANGEERROR_NAME "DFHack::ChangeError" #define DFHACK_COMPARE_NAME "DFHack::ComparePtrs" @@ -150,6 +155,8 @@ void *DFHack::GetDFObject(lua_State *state, type_identity *type, int val_index) return get_object_internal(state, type, val_index, false); } +static void push_adhoc_pointer(lua_State *state, void *ptr, type_identity *target); + /************************************** * Identity object read/write methods * **************************************/ @@ -260,6 +267,13 @@ int container_identity::lua_item_count(lua_State *state, void *ptr) return item_count(ptr); } +void container_identity::lua_item_reference(lua_State *state, int fname_idx, void *ptr, int idx) +{ + auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID); + void *pitem = item_pointer(id, ptr, idx); + push_object_internal(state, id, pitem); +} + void 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); @@ -274,6 +288,13 @@ void container_identity::lua_item_write(lua_State *state, int fname_idx, void *p id->lua_write(state, fname_idx, pitem, val_index); } +void ptr_container_identity::lua_item_reference(lua_State *state, int fname_idx, void *ptr, int idx) +{ + auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID); + void *pitem = item_pointer(id, ptr, idx); + push_adhoc_pointer(state, pitem, id); +} + void ptr_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); @@ -288,6 +309,11 @@ void ptr_container_identity::lua_item_write(lua_State *state, int fname_idx, voi df::pointer_identity::lua_write(state, fname_idx, pitem, id, val_index); } +void bit_container_identity::lua_item_reference(lua_State *state, int, void *, int) +{ + lua_pushnil(state); +} + void bit_container_identity::lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx) { lua_pushboolean(state, get_item(ptr, idx)); @@ -761,6 +787,37 @@ static void read_field(lua_State *state, const struct_field_info *field, void *p lua_pushnil(state); } +static void field_reference(lua_State *state, const struct_field_info *field, void *ptr) +{ + switch (field->mode) + { + case struct_field_info::PRIMITIVE: + case struct_field_info::SUBSTRUCT: + push_object_internal(state, field->type, ptr); + return; + + case struct_field_info::POINTER: + push_adhoc_pointer(state, ptr, field->type); + return; + + case struct_field_info::CONTAINER: + read_field(state, field, ptr); + return; + + case struct_field_info::STATIC_STRING: + case struct_field_info::STATIC_ARRAY: + case struct_field_info::STL_VECTOR_PTR: + GetAdHocMetatable(state, field); + push_object_ref(state, ptr); + return; + + case struct_field_info::END: + break; + } + + lua_pushnil(state); +} + static void write_field(lua_State *state, const struct_field_info *field, void *ptr, int value_idx) { switch (field->mode) @@ -842,6 +899,21 @@ static int meta_struct_index(lua_State *state) return 1; } +/** + * Method: _field for structures. + */ +static int meta_struct_field_reference(lua_State *state) +{ + if (lua_gettop(state) != 2) + luaL_error(state, "Usage: object._field(name)"); + uint8_t *ptr = get_object_addr(state, 1, 2, "reference"); + auto field = (struct_field_info*)find_field(state, 2, "reference"); + if (!field) + field_error(state, 2, "builtin property", "reference"); + field_reference(state, field, ptr + field->offset); + return 1; +} + /** * Metamethod: __newindex for structures. */ @@ -900,17 +972,17 @@ static int meta_container_len(lua_State *state) * - Numbers are indices and handled directly. * - NULL userdata are metafields; push and exit; */ -static int lookup_container_field(lua_State *state, int field, bool write = false) +static int lookup_container_field(lua_State *state, int field, const char *mode = NULL) { if (lua_type(state, field) == LUA_TNUMBER) return field; - lookup_field(state, field, write ? "write" : "read"); + lookup_field(state, field, mode ? mode : "read"); if (lua_isuserdata(state, -1) && !lua_touserdata(state, -1)) { - if (write) - field_error(state, field, "builtin property", "write"); + if (mode) + field_error(state, field, "builtin property", mode); lua_pop(state, 1); get_metafield(state); @@ -953,13 +1025,30 @@ static int meta_container_index(lua_State *state) return 1; } +/** + * Method: _field for containers. + */ +static int meta_container_field_reference(lua_State *state) +{ + if (lua_gettop(state) != 2) + luaL_error(state, "Usage: object._field(index)"); + uint8_t *ptr = get_object_addr(state, 1, 2, "reference"); + int iidx = lookup_container_field(state, 2, "reference"); + + auto id = (container_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID); + int len = id->lua_item_count(state, ptr); + int idx = check_container_index(state, len, 2, iidx, "reference"); + id->lua_item_reference(state, 2, ptr, idx); + return 1; +} + /** * Metamethod: __index for containers. */ 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, true); + int iidx = lookup_container_field(state, 2, "write"); auto id = (container_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID); int len = id->lua_item_count(state, ptr); @@ -1017,7 +1106,7 @@ static int meta_bitfield_index(lua_State *state) static int meta_bitfield_newindex(lua_State *state) { uint8_t *ptr = get_object_addr(state, 1, 2, "write"); - int iidx = lookup_container_field(state, 2, true); + int iidx = lookup_container_field(state, 2, "write"); auto id = (bitfield_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID); @@ -1130,6 +1219,8 @@ static void SetPtrMethods(lua_State *state, int meta_idx, int read_idx) EnableMetaField(state, read_idx, "_type"); EnableMetaField(state, read_idx, "_kind"); + EnableMetaField(state, read_idx, "_field"); + EnableMetaField(state, read_idx, "sizeof"); EnableMetaField(state, read_idx, "_displace"); } @@ -1287,6 +1378,8 @@ static void MakeContainerMetatable(lua_State *state, container_identity *type, SetContainerMethod(state, base+1, base+2, meta_container_index, "__index", type, item, count); SetContainerMethod(state, base+1, base+2, meta_container_newindex, "__newindex", type, item, count); + SetContainerMethod(state, base+1, base+2, meta_container_field_reference, "_field", type, item, count); + AttachEnumKeys(state, base, ienum); } @@ -1324,6 +1417,7 @@ void struct_identity::build_metatable(lua_State *state) { int base = lua_gettop(state); MakeFieldMetatable(state, this, meta_struct_index, meta_struct_newindex); + SetStructMethod(state, base+1, base+2, meta_struct_field_reference, "_field"); SetPtrMethods(state, base+1, base+2); } @@ -1360,6 +1454,11 @@ static void GetAdHocMetatable(lua_State *state, const struct_field_info *field) break; } + case struct_field_info::STATIC_STRING: + MakeContainerMetatable(state, &df::buffer_container_identity::base_instance, + &df::identity_traits::identity, field->count, NULL); + break; + case struct_field_info::STATIC_ARRAY: MakeContainerMetatable(state, &df::buffer_container_identity::base_instance, field->type, field->count, field->eid); @@ -1380,6 +1479,37 @@ static void GetAdHocMetatable(lua_State *state, const struct_field_info *field) } } +static void push_adhoc_pointer(lua_State *state, void *ptr, type_identity *target) +{ + if (!target) + { + push_object_internal(state, &df::identity_traits::identity, ptr); + return; + } + + LookupInTable(state, target, DFHACK_PTR_IDTABLE_NAME); + + type_identity *id = (type_identity*)lua_touserdata(state, -1); + lua_pop(state, 1); + + if (!id) + { + /* + * HACK: relies on + * 1) pointer_identity destructor being no-op + * 2) lua gc never moving objects in memory + */ + + void *newobj = lua_newuserdata(state, sizeof(pointer_identity)); + id = new (newobj) pointer_identity(target); + + SaveInTable(state, target, DFHACK_PTR_IDTABLE_NAME); + lua_pop(state, 1); + } + + push_object_internal(state, id, ptr); +} + /* * Recursive walk of scopes to construct the df... tree. */ @@ -1533,6 +1663,9 @@ static void DoAttach(lua_State *state) { int base = lua_gettop(state); + lua_newtable(state); + lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_PTR_IDTABLE_NAME); + lua_newtable(state); lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_TYPEID_TABLE_NAME); diff --git a/library/include/DataIdentity.h b/library/include/DataIdentity.h index 7b576b117..808634798 100644 --- a/library/include/DataIdentity.h +++ b/library/include/DataIdentity.h @@ -86,6 +86,7 @@ namespace DFHack int lua_item_count(lua_State *state, void *ptr); + virtual void lua_item_reference(lua_State *state, int fname_idx, void *ptr, int idx); virtual void lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx); virtual void lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index); @@ -104,6 +105,7 @@ namespace DFHack std::string getFullName(type_identity *item); + virtual void lua_item_reference(lua_State *state, int fname_idx, void *ptr, int idx); virtual void lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx); virtual void lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index); }; @@ -117,6 +119,7 @@ namespace DFHack std::string getFullName(type_identity *item); + virtual void lua_item_reference(lua_State *state, int fname_idx, void *ptr, int idx); virtual void lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx); virtual void lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index); From e10b1a50a352fe63ca674310673c8b19dcee48fa Mon Sep 17 00:00:00 2001 From: Warmist Date: Fri, 23 Mar 2012 11:07:54 +0200 Subject: [PATCH 16/16] Onfunction start --- plugins/Dfusion/luafiles/init.lua | 3 ++- plugins/Dfusion/luafiles/onfunction/locations.lua | 9 +++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/plugins/Dfusion/luafiles/init.lua b/plugins/Dfusion/luafiles/init.lua index ccb51f572..97766a978 100644 --- a/plugins/Dfusion/luafiles/init.lua +++ b/plugins/Dfusion/luafiles/init.lua @@ -72,7 +72,8 @@ table.insert(plugins,{"adv_tools","some tools for (mainly) advneturer hacking"}) table.insert(plugins,{"triggers","a function calling plug (discontinued...)"}) table.insert(plugins,{"migrants","multi race imigrations"}) -table.insert(plugins,{"onfunction","run lua on some df function"})--]=] +--]=] +table.insert(plugins,{"onfunction","run lua on some df function"}) table.insert(plugins,{"editor","edit internals of df",EditDF}) table.insert(plugins,{"saves","run current worlds's init.lua",RunSaved}) loadall(plugins) diff --git a/plugins/Dfusion/luafiles/onfunction/locations.lua b/plugins/Dfusion/luafiles/onfunction/locations.lua index 57043b7a3..7849fc45d 100644 --- a/plugins/Dfusion/luafiles/onfunction/locations.lua +++ b/plugins/Dfusion/luafiles/onfunction/locations.lua @@ -1,5 +1,5 @@ if WINDOWS then --windows function defintions - onfunction.AddFunction(0x55499D+offsets.base(),"Move") --on creature move found with "watch mem=xcoord" + --[=[onfunction.AddFunction(0x55499D+offsets.base(),"Move") --on creature move found with "watch mem=xcoord" onfunction.AddFunction(0x275933+offsets.base(),"Die",{creature="edi"}) --on creature death? found by watching dead flag then stepping until new function onfunction.AddFunction(0x2c1834+offsets.base(),"CreateCreature",{protocreature="eax"}) --arena onfunction.AddFunction(0x349640+offsets.base(),"AddItem",{item="esp"}) --or esp @@ -7,8 +7,9 @@ if WINDOWS then --windows function defintions onfunction.AddFunction(0x3d4301+offsets.base(),"Make_Item",{item_type="esp"}) onfunction.AddFunction(0x5af826+offsets.base(),"Hurt",{target="esi",attacker={off=0x74,rtype=DWORD,reg="esp"}}) onfunction.AddFunction(0x3D5886+offsets.base(),"Flip",{building="esi"}) - onfunction.AddFunction(0x35E340+offsets.base(),"ItemCreate") + onfunction.AddFunction(0x35E340+offsets.base(),"ItemCreate")--]=] + onfunction.AddFunction(4B34B6+offsets.base(),"ReactionFinish") --esp item. Ecx creature, edx? else --linux - onfunction.AddFunction(0x899befe+offsets.base(),"Move") -- found out by attaching watch... - onfunction.AddFunction(0x850eecd+offsets.base(),"Die",{creature="ebx"}) -- same + --[=[onfunction.AddFunction(0x899befe+offsets.base(),"Move") -- found out by attaching watch... + onfunction.AddFunction(0x850eecd+offsets.base(),"Die",{creature="ebx"}) -- same--]=] end