diff --git a/library/DataStaticsFields.cpp b/library/DataStaticsFields.cpp index 89736369f..02a12b972 100644 --- a/library/DataStaticsFields.cpp +++ b/library/DataStaticsFields.cpp @@ -8,6 +8,7 @@ #include "df/ui.h" #include "DataIdentity.h" +#include "DataFuncs.h" #include @@ -43,6 +44,7 @@ namespace df { #define FLD(mode, name) struct_field_info::mode, #name, offsetof(CUR_STRUCT, name) #define GFLD(mode, name) struct_field_info::mode, #name, (size_t)&df::global::name +#define METHOD(mode, name) struct_field_info::mode, #name, 0, wrap_function(&CUR_STRUCT::name) #define FLD_END struct_field_info::END // Field definitions diff --git a/library/LuaTypes.cpp b/library/LuaTypes.cpp index 25cdfa354..bede74301 100644 --- a/library/LuaTypes.cpp +++ b/library/LuaTypes.cpp @@ -36,6 +36,7 @@ distribution. #include "DataDefs.h" #include "DataIdentity.h" #include "LuaWrapper.h" +#include "DataFuncs.h" #include "MiscUtils.h" @@ -49,6 +50,16 @@ using namespace DFHack::LuaWrapper; * Identity object read/write methods * **************************************/ +void function_identity_base::lua_read(lua_State *state, int fname_idx, void *ptr) +{ + field_error(state, fname_idx, "executable code", "read"); +} + +void function_identity_base::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) +{ + field_error(state, fname_idx, "executable code", "write"); +} + void constructed_identity::lua_read(lua_State *state, int fname_idx, void *ptr) { push_object_internal(state, this, ptr); @@ -264,12 +275,31 @@ static void lookup_field(lua_State *state, int index, const char *mode) field_error(state, index, "not found", mode); } +// Resolve the field in the metatable and return +static int get_metafield(lua_State *state) +{ + lua_rawget(state, UPVAL_METATABLE); + return 1; +} + static void *find_field(lua_State *state, int index, const char *mode) { lookup_field(state, index, mode); + // Methods + if (lua_isfunction(state, -1)) + return NULL; + + // Otherwise must be a pointer + if (!lua_isuserdata(state, -1)) + field_error(state, index, "corrupted field table", mode); + void *p = lua_touserdata(state, -1); lua_pop(state, 1); + + // NULL => metafield + if (!p) + get_metafield(state); return p; } @@ -295,6 +325,10 @@ static void read_field(lua_State *state, const struct_field_info *field, void *p return; } + case struct_field_info::OBJ_METHOD: + case struct_field_info::CLASS_METHOD: + // error + case struct_field_info::PRIMITIVE: case struct_field_info::SUBSTRUCT: field->type->lua_read(state, 2, ptr); @@ -339,6 +373,10 @@ static void field_reference(lua_State *state, const struct_field_info *field, vo push_adhoc_pointer(state, ptr, field->type); return; + case struct_field_info::OBJ_METHOD: + case struct_field_info::CLASS_METHOD: + // error + case struct_field_info::CONTAINER: read_field(state, field, ptr); return; @@ -371,6 +409,10 @@ static void write_field(lua_State *state, const struct_field_info *field, void * return; } + case struct_field_info::OBJ_METHOD: + case struct_field_info::CLASS_METHOD: + // error + case struct_field_info::PRIMITIVE: case struct_field_info::SUBSTRUCT: case struct_field_info::CONTAINER: @@ -418,13 +460,6 @@ 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. */ @@ -433,7 +468,7 @@ static int meta_struct_index(lua_State *state) uint8_t *ptr = get_object_addr(state, 1, 2, "read"); auto field = (struct_field_info*)find_field(state, 2, "read"); if (!field) - return get_metafield(state); + return 1; read_field(state, field, ptr + field->offset); return 1; } @@ -448,7 +483,7 @@ static int meta_struct_field_reference(lua_State *state) 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_error(state, 2, "builtin property or method", "reference"); field_reference(state, field, ptr + field->offset); return 1; } @@ -461,7 +496,7 @@ 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"); + field_error(state, 2, "builtin property or method", "write"); write_field(state, field, ptr + field->offset, 3); return 0; } @@ -475,7 +510,7 @@ 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 1; type->lua_read(state, 2, ptr); return 1; } @@ -488,7 +523,7 @@ 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"); + field_error(state, 2, "builtin property or method", "write"); type->lua_write(state, 2, ptr, 3); return 0; } @@ -521,7 +556,7 @@ static int lookup_container_field(lua_State *state, int field, const char *mode if (lua_isuserdata(state, -1) && !lua_touserdata(state, -1)) { if (mode) - field_error(state, field, "builtin property", mode); + field_error(state, field, "builtin property or method", mode); lua_pop(state, 1); get_metafield(state); @@ -727,7 +762,7 @@ static int meta_global_index(lua_State *state) { auto field = (struct_field_info*)find_field(state, 2, "read"); if (!field) - return get_metafield(state); + return 1; void *ptr = *(void**)field->offset; if (!ptr) field_error(state, 2, "global address not known", "read"); @@ -742,7 +777,7 @@ 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"); + field_error(state, 2, "builtin property or method", "write"); void *ptr = *(void**)field->offset; if (!ptr) field_error(state, 2, "global address not known", "write"); @@ -750,27 +785,65 @@ static int meta_global_newindex(lua_State *state) return 0; } +/** + * Wrapper for c++ methods and functions. + */ +static int meta_call_function(lua_State *state) +{ + auto id = (function_identity_base*)lua_touserdata(state, UPVAL_CONTAINER_ID); + if (lua_gettop(state) != id->getNumArgs()) + field_error(state, UPVAL_METHOD_NAME, "invalid argument count", "invoke"); + id->invoke(state, 1); + return 1; +} + +/** + * Create a closure invoking the given function, and add it to the field table. + */ +static void AddMethodWrapper(lua_State *state, int meta_idx, int field_idx, + const char *name, function_identity_base *fun) +{ + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPETABLE_NAME); + lua_pushvalue(state, meta_idx); + lua_pushfstring(state, "%s()", name); + lua_pushlightuserdata(state, fun); + lua_pushcclosure(state, meta_call_function, 4); + + lua_setfield(state, field_idx, name); +} + /** * Add fields in the array to the UPVAL_FIELDTABLE candidates on the stack. */ static void IndexFields(lua_State *state, struct_identity *pstruct) { - // stack: fieldtable + // stack: metatable fieldtable - int base = lua_gettop(state); + int base = lua_gettop(state) - 2; for (struct_identity *p = pstruct; p; p = p->getParent()) { auto fields = p->getFields(); + if (!fields) + continue; - for (; fields; ++fields) + for (int i = 0; fields[i].mode != struct_field_info::END; ++i) { - if (fields->mode == struct_field_info::END) + switch (fields[i].mode) + { + case struct_field_info::OBJ_METHOD: + AddMethodWrapper(state, base+1, base+2, fields[i].name, + (function_identity_base*)fields[i].type); break; - lua_pushstring(state,fields->name); - lua_pushlightuserdata(state,(void*)fields); - lua_rawset(state,base); + case struct_field_info::CLASS_METHOD: + break; + + default: + lua_pushstring(state,fields[i].name); + lua_pushlightuserdata(state,(void*)&fields[i]); + lua_rawset(state,base+2); + } } } } diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index 48b79721b..0ec51f510 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -49,6 +49,7 @@ namespace DFHack enum identity_type { IDTYPE_GLOBAL, + IDTYPE_FUNCTION, IDTYPE_PRIMITIVE, IDTYPE_POINTER, IDTYPE_CONTAINER, @@ -109,12 +110,12 @@ namespace DFHack constructed_identity(size_t size, TAllocateFn alloc) : type_identity(size), allocator(alloc) {}; - virtual bool isPrimitive() { return false; } - virtual bool isConstructed() { return true; } - 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); } + public: + virtual bool isPrimitive() { return false; } + virtual bool isConstructed() { return true; } 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); @@ -221,7 +222,9 @@ namespace DFHack STATIC_ARRAY, SUBSTRUCT, CONTAINER, - STL_VECTOR_PTR + STL_VECTOR_PTR, + OBJ_METHOD, + CLASS_METHOD }; Mode mode; const char *name; @@ -409,6 +412,14 @@ namespace df template void *allocator_fn(void *out, const void *in) { if (out) { *(T*)out = *(const T*)in; return out; } + else if (in) { delete (T*)in; return (T*)in; } + else return new T(); + } + + template + void *allocator_nodel_fn(void *out, const void *in) { + if (out) { *(T*)out = *(const T*)in; return out; } + else if (in) { return NULL; } else return new T(); } diff --git a/library/include/DataFuncs.h b/library/include/DataFuncs.h new file mode 100644 index 000000000..1dc651489 --- /dev/null +++ b/library/include/DataFuncs.h @@ -0,0 +1,129 @@ +/* +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 "DataIdentity.h" +#include "LuaWrapper.h" + +namespace df { + template struct function_wrapper {}; + + /* + * Since templates can't match variable arg count, + * a separate specialization is needed for every + * supported count value... + * + * The FW_TARGS ugliness is needed because of + * commas not wrapped in () + */ + +#define INVOKE_VOID(call) \ + call; lua_pushnil(state); +#define INVOKE_RV(call) \ + RT rv = call; df::identity_traits::get()->lua_read(state, UPVAL_METHOD_NAME, &rv); +#define LOAD_CLASS() \ + CT *self = (CT*)DFHack::LuaWrapper::get_object_addr(state, base++, UPVAL_METHOD_NAME, "invoke"); +#define LOAD_ARG(type) \ + type v##type; df::identity_traits::get()->lua_write(state, UPVAL_METHOD_NAME, &v##type, base++); + +#define INSTANTIATE_WRAPPERS(Count, FArgs, Args, Loads) \ + template struct function_wrapper { \ + static const bool is_method = false; \ + static const int num_args = Count; \ + static void execute(lua_State *state, int base, void (*cb) FArgs) { Loads; INVOKE_VOID(cb Args); } \ + }; \ + template struct function_wrapper { \ + static const bool is_method = false; \ + static const int num_args = Count; \ + static void execute(lua_State *state, int base, RT (*cb) FArgs) { Loads; INVOKE_RV(cb Args); } \ + }; \ + template struct function_wrapper { \ + static const bool is_method = true; \ + static const int num_args = Count+1; \ + static void execute(lua_State *state, int base, void (CT::*cb) FArgs) { \ + LOAD_CLASS() Loads; INVOKE_VOID((self->*cb) Args); } \ + }; \ + template struct function_wrapper { \ + static const bool is_method = true; \ + static const int num_args = Count+1; \ + static void execute(lua_State *state, int base, RT (CT::*cb) FArgs) { \ + LOAD_CLASS(); Loads; INVOKE_RV((self->*cb) Args); } \ + }; + +#define FW_TARGSC +#define FW_TARGS +INSTANTIATE_WRAPPERS(0, (), (), ;) +#undef FW_TARGS + +#undef FW_TARGSC +#define FW_TARGSC FW_TARGS, +#define FW_TARGS class A1 +INSTANTIATE_WRAPPERS(1, (A1), (vA1), LOAD_ARG(A1);) +#undef FW_TARGS + +#define FW_TARGS class A1, class A2 +INSTANTIATE_WRAPPERS(2, (A1,A2), (vA1,vA2), LOAD_ARG(A1); LOAD_ARG(A2);) +#undef FW_TARGS + +#define FW_TARGS class A1, class A2, class A3 +INSTANTIATE_WRAPPERS(3, (A1,A2,A3), (vA1,vA2,vA3), LOAD_ARG(A1); LOAD_ARG(A2); LOAD_ARG(A3);) +#undef FW_TARGS + +#define FW_TARGS class A1, class A2, class A3, class A4 +INSTANTIATE_WRAPPERS(4, (A1,A2,A3,A4), (vA1,vA2,vA3,vA4), + LOAD_ARG(A1); LOAD_ARG(A2); LOAD_ARG(A3); LOAD_ARG(A4);) +#undef FW_TARGS + +#undef FW_TARGSC +#undef INSTANTIATE_WRAPPERS +#undef INVOKE_VOID +#undef INVOKE_RV +#undef LOAD_CLASS +#undef LOAD_ARG + + template + class function_identity : public function_identity_base { + T ptr; + + public: + typedef function_wrapper wrapper; + + function_identity(T ptr) + : function_identity_base(wrapper::num_args), ptr(ptr) {}; + + virtual void invoke(lua_State *state, int base) { wrapper::execute(state, base, ptr); } + }; + + template + inline function_identity_base *wrap_function(T ptr) { + static function_identity identity(ptr); + return &identity; + } +} \ No newline at end of file diff --git a/library/include/DataIdentity.h b/library/include/DataIdentity.h index e0f864225..a135fc00b 100644 --- a/library/include/DataIdentity.h +++ b/library/include/DataIdentity.h @@ -37,6 +37,23 @@ distribution. namespace DFHack { + class DFHACK_EXPORT function_identity_base : public type_identity { + int num_args; + + public: + function_identity_base(int num_args) : type_identity(0), num_args(num_args) {}; + + virtual identity_type type() { return IDTYPE_FUNCTION; } + + int getNumArgs() { return num_args; } + std::string getFullName() { return "function"; } + + virtual void invoke(lua_State *state, int base) = 0; + + 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); + }; + class DFHACK_EXPORT primitive_identity : public type_identity { public: primitive_identity(size_t size) : type_identity(size) {}; @@ -145,6 +162,7 @@ namespace DFHack namespace df { + using DFHack::function_identity_base; using DFHack::primitive_identity; using DFHack::pointer_identity; using DFHack::container_identity; @@ -451,44 +469,44 @@ namespace df // Container definitions template - primitive_identity *identity_traits >::get() { + inline primitive_identity *identity_traits >::get() { return identity_traits::get(); } template - pointer_identity *identity_traits::get() { + inline pointer_identity *identity_traits::get() { static pointer_identity identity(identity_traits::get()); return &identity; } template - container_identity *identity_traits::get() { + inline container_identity *identity_traits::get() { static buffer_container_identity identity(sz, identity_traits::get()); return &identity; } template - container_identity *identity_traits >::get() { + inline container_identity *identity_traits >::get() { typedef std::vector container; static stl_container_identity identity("vector", identity_traits::get()); return &identity; } template - stl_ptr_vector_identity *identity_traits >::get() { + inline stl_ptr_vector_identity *identity_traits >::get() { static stl_ptr_vector_identity identity(identity_traits::get()); return &identity; } template - container_identity *identity_traits >::get() { + inline container_identity *identity_traits >::get() { typedef std::deque container; static stl_container_identity identity("deque", identity_traits::get()); return &identity; } template - bit_container_identity *identity_traits >::get() { + inline 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); @@ -496,7 +514,7 @@ namespace df } template - container_identity *identity_traits >::get() { + inline container_identity *identity_traits >::get() { typedef DfArray container; static stl_container_identity identity("DfArray", identity_traits::get()); return &identity; diff --git a/library/xml b/library/xml index 55a120d5b..a1d71773e 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 55a120d5bc823b783aeb8b6931ccaa31402fd850 +Subproject commit a1d71773ef127010391f795337184c56146c8dda