diff --git a/depends/lua/CMakeLists.txt b/depends/lua/CMakeLists.txt index 23ea6a485..aa0a1b914 100644 --- a/depends/lua/CMakeLists.txt +++ b/depends/lua/CMakeLists.txt @@ -1,4 +1,4 @@ -PROJECT ( lua C ) +PROJECT ( lua CXX ) CMAKE_MINIMUM_REQUIRED(VERSION 2.8) # TODO: make this RelWithDebInfo only @@ -77,9 +77,12 @@ src/lundump.c src/lvm.c src/lzio.c ) +# compile with C++ compiler +set_source_files_properties(${SRC_LIBLUA} PROPERTIES LANGUAGE CXX) +# append headers to sources to make them show up in MSVC GUI LIST(APPEND SRC_LIBLUA ${HDR_LIBLUA}) -ADD_LIBRARY ( lua SHARED EXCLUDE_FROM_ALL ${SRC_LIBLUA} ) +ADD_LIBRARY ( lua SHARED ${SRC_LIBLUA} ) TARGET_LINK_LIBRARIES ( lua ${LIBS}) install(TARGETS lua diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 72bf91e62..4d6767b31 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -27,6 +27,8 @@ include/Console.h include/Core.h include/ColorText.h include/DataDefs.h +include/DataIdentity.h +include/LuaWrapper.h include/Error.h include/Export.h include/Hooks.h @@ -41,7 +43,6 @@ include/TileTypes.h include/Types.h include/VersionInfo.h include/VersionInfoFactory.h -include/Virtual.h include/RemoteClient.h include/RemoteServer.h include/RemoteTools.h @@ -56,6 +57,7 @@ Core.cpp ColorText.cpp DataDefs.cpp LuaWrapper.cpp +LuaTypes.cpp DataStatics.cpp DataStaticsCtor.cpp DataStaticsFields.cpp @@ -63,7 +65,6 @@ MiscUtils.cpp PluginManager.cpp TileTypes.cpp VersionInfoFactory.cpp -Virtual.cpp RemoteClient.cpp RemoteServer.cpp RemoteTools.cpp diff --git a/library/DataDefs.cpp b/library/DataDefs.cpp index 6fcd1141a..712d4563f 100644 --- a/library/DataDefs.cpp +++ b/library/DataDefs.cpp @@ -164,6 +164,9 @@ void struct_identity::doInit(Core *core) bool struct_identity::is_subclass(struct_identity *actual) { + if (!has_children && actual != this) + return false; + for (; actual; actual = actual->getParent()) if (actual == this) return true; diff --git a/library/LuaTypes.cpp b/library/LuaTypes.cpp new file mode 100644 index 000000000..25cdfa354 --- /dev/null +++ b/library/LuaTypes.cpp @@ -0,0 +1,979 @@ +/* +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 "LuaWrapper.h" + +#include "MiscUtils.h" + +#include +#include + +using namespace DFHack; +using namespace DFHack::LuaWrapper; + +/************************************** + * Identity object read/write methods * + **************************************/ + +void constructed_identity::lua_read(lua_State *state, int fname_idx, void *ptr) +{ + 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"); +} + +void enum_identity::lua_read(lua_State *state, int fname_idx, void *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) +{ + base_type->lua_write(state, fname_idx, ptr, val_index); +} + +void df::number_identity_base::lua_read(lua_State *state, int fname_idx, void *ptr) +{ + lua_pushnumber(state, read(ptr)); +} + +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)); +} + +void df::bool_identity::lua_read(lua_State *state, int fname_idx, void *ptr) +{ + lua_pushboolean(state, *(bool*)ptr); +} + +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) || lua_isnil(state, val_index)) + *pb = lua_toboolean(state, val_index); + else if (lua_isnumber(state, val_index)) + *pb = lua_tointeger(state, val_index); + else + field_error(state, fname_idx, "boolean or number expected", "write"); +} + +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()); +} + +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); +} + +void df::pointer_identity::lua_read(lua_State *state, int fname_idx, void *ptr, type_identity *target) +{ + push_object_internal(state, target, *(void**)ptr); +} + +void df::pointer_identity::lua_read(lua_State *state, int fname_idx, void *ptr) +{ + lua_read(state, fname_idx, ptr, target); +} + +void df::pointer_identity::lua_write(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, false); + 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) +{ + lua_write(state, fname_idx, ptr, target, val_index); +} + +int container_identity::lua_item_count(lua_State *state, void *ptr, CountMode mode) +{ + if (lua_isnumber(state, UPVAL_ITEM_COUNT)) + return lua_tointeger(state, UPVAL_ITEM_COUNT); + else + return item_count(ptr, mode); +} + +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); + void *pitem = item_pointer(id, ptr, idx); + 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) +{ + 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); +} + +bool container_identity::lua_insert(lua_State *state, int fname_idx, void *ptr, int idx, int val_index) +{ + auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID); + + char tmp[32]; + void *pitem = &tmp; + + if (id->isPrimitive()) + { + if (id->isConstructed()) + luaL_error(state, "Temporaries of type %s not supported", id->getFullName().c_str()); + + assert(id->byte_size() <= sizeof(tmp)); + id->lua_write(state, fname_idx, pitem, val_index); + } + else + { + pitem = get_object_internal(state, id, val_index, false); + if (!pitem) + field_error(state, fname_idx, "incompatible object type", "insert"); + } + + return insert(ptr, idx, pitem); +} + +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); + void *pitem = item_pointer(&df::identity_traits::identity, ptr, idx); + 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) +{ + 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); +} + +bool ptr_container_identity::lua_insert(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 = NULL; + df::pointer_identity::lua_write(state, fname_idx, &pitem, id, val_index); + + return insert(ptr, idx, pitem); +} + +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)); +} + +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"); +} + +/** + * 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); + lua_gettable(state, UPVAL_FIELDTABLE); // uses metatable with enum keys + + 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 p; +} + +static uint8_t *check_method_call(lua_State *state, int min_args, int max_args) +{ + int argc = lua_gettop(state)-1; + if (argc < min_args || argc > max_args) + field_error(state, UPVAL_METHOD_NAME, "wrong argument count", "call"); + + return get_object_addr(state, 1, UPVAL_METHOD_NAME, "call"); +} + +static void GetAdHocMetatable(lua_State *state, const struct_field_info *field); + +static void 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; + } + + case struct_field_info::PRIMITIVE: + case struct_field_info::SUBSTRUCT: + field->type->lua_read(state, 2, ptr); + return; + + case struct_field_info::POINTER: + 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()) + { + 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; + + case struct_field_info::END: + break; + } + + 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) + { + case struct_field_info::STATIC_STRING: + { + size_t 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))); + return; + } + + case struct_field_info::PRIMITIVE: + case struct_field_info::SUBSTRUCT: + case struct_field_info::CONTAINER: + field->type->lua_write(state, 2, ptr, value_idx); + return; + + case struct_field_info::POINTER: + 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: + field_error(state, 2, "complex object", "write"); + + case struct_field_info::END: + return; + } +} + +/** + * Metamethod: represent a type node as string. + */ +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; +} + +/** + * 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"); + + 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; +} + +// 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"); + auto field = (struct_field_info*)find_field(state, 2, "read"); + if (!field) + return get_metafield(state); + read_field(state, field, ptr + field->offset); + 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. + */ +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; +} + +/** + * 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"); + auto type = (type_identity*)find_field(state, 2, "read"); + if (!type) + return get_metafield(state); + type->lua_read(state, 2, ptr); + return 1; +} + +/** + * Metamethod: __newindex for primitives. + */ +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; +} + +/** + * Metamethod: __len for containers. + */ +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, container_identity::COUNT_LEN); + lua_pushinteger(state, len); + 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 = NULL) +{ + if (lua_type(state, field) == LUA_TNUMBER) + return field; + + lookup_field(state, field, mode ? mode : "read"); + + if (lua_isuserdata(state, -1) && !lua_touserdata(state, -1)) + { + if (mode) + field_error(state, field, "builtin property", mode); + + lua_pop(state, 1); + get_metafield(state); + return 0; + } + + return -1; +} + +/** + * 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); + if (idx < 0 || (idx >= len && len >= 0)) + 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); + if (!iidx) + return 1; + + auto id = (container_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID); + int len = id->lua_item_count(state, ptr, container_identity::COUNT_READ); + int idx = check_container_index(state, len, 2, iidx, "read"); + id->lua_item_read(state, 2, ptr, idx); + 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, container_identity::COUNT_WRITE); + 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, "write"); + + auto id = (container_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID); + int len = id->lua_item_count(state, ptr, container_identity::COUNT_WRITE); + int idx = check_container_index(state, len, 2, iidx, "write"); + id->lua_item_write(state, 2, ptr, idx, 3); + return 0; +} + +/** + * Method: resize container + */ +static int method_container_resize(lua_State *state) +{ + uint8_t *ptr = check_method_call(state, 1, 1); + + auto id = (container_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID); + int idx = check_container_index(state, -1, UPVAL_METHOD_NAME, 2, "call"); + + if (!id->resize(ptr, idx)) + field_error(state, UPVAL_METHOD_NAME, "not supported", "call"); + return 0; +} + +/** + * Method: erase item from container + */ +static int method_container_erase(lua_State *state) +{ + uint8_t *ptr = check_method_call(state, 1, 1); + + auto id = (container_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID); + int len = id->lua_item_count(state, ptr, container_identity::COUNT_LEN); + int idx = check_container_index(state, len, UPVAL_METHOD_NAME, 2, "call"); + + if (!id->erase(ptr, idx)) + field_error(state, UPVAL_METHOD_NAME, "not supported", "call"); + return 0; +} + +/** + * Method: insert item into container + */ +static int method_container_insert(lua_State *state) +{ + uint8_t *ptr = check_method_call(state, 2, 2); + + auto id = (container_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID); + int len = id->lua_item_count(state, ptr, container_identity::COUNT_LEN); + if (len >= 0) len++; + int idx = check_container_index(state, len, UPVAL_METHOD_NAME, 2, "call"); + + if (!id->lua_insert(state, UPVAL_METHOD_NAME, ptr, idx, 3)) + field_error(state, UPVAL_METHOD_NAME, "not supported", "call"); + 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); + 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) + { + size_t intv = 0; + memcpy(&intv, ptr, std::min(sizeof(intv), size_t(id->byte_size()))); + lua_pushnumber(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"); + + size_t intv = (size_t)lua_tonumber(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"); + if (!field) + return get_metafield(state); + void *ptr = *(void**)field->offset; + if (!ptr) + field_error(state, 2, "global address not known", "read"); + read_field(state, field, ptr); + return 1; +} + +/** + * Metamethod: __newindex for df.global + */ +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"); + write_field(state, field, ptr, 3); + return 0; +} + +/** + * Add fields in the array to the UPVAL_FIELDTABLE candidates on the stack. + */ +static void IndexFields(lua_State *state, struct_identity *pstruct) +{ + // stack: fieldtable + + int base = lua_gettop(state); + + for (struct_identity *p = pstruct; p; p = p->getParent()) + { + auto fields = p->getFields(); + + 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); + } + } +} + +/** + * 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, "struct"); // meta, fields + + IndexFields(state, pstruct); + + SetStructMethod(state, base+1, base+2, reader, "__index"); + SetStructMethod(state, base+1, base+2, writer, "__newindex"); + + // 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, "primitive"); + + SetPtrMethods(state, base+1, base+2); + + EnableMetaField(state, base+2, "value", type); + + SetStructMethod(state, base+1, base+2, meta_primitive_index, "__index"); + SetStructMethod(state, base+1, base+2, meta_primitive_newindex, "__newindex"); +} + +static void AddContainerMethodFun(lua_State *state, int meta_idx, int field_idx, + lua_CFunction function, const char *name, + type_identity *container, type_identity *item, int count) +{ + lua_pushfstring(state, "%s()", name); + SetContainerMethod(state, meta_idx, lua_gettop(state), function, name, container, item, count); + lua_pop(state, 1); + + EnableMetaField(state, field_idx, name); +} + +/** + * 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, "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"); + lua_setfield(state, base+1, "_type"); + + lua_pushlightuserdata(state, item); + lua_setfield(state, base+1, "_field_identity"); + + if (count >= 0) + { + lua_pushinteger(state, count); + lua_setfield(state, base+1, "_count"); + } + + 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+2, meta_container_newindex, "__newindex", type, item, count); + + SetContainerMethod(state, base+1, base+2, meta_container_field_reference, "_field", type, item, count); + + AddContainerMethodFun(state, base+1, base+2, method_container_resize, "resize", type, item, count); + AddContainerMethodFun(state, base+1, base+2, method_container_erase, "erase", type, item, count); + AddContainerMethodFun(state, base+1, base+2, method_container_insert, "insert", type, item, count); + + AttachEnumKeys(state, base+1, base+2, ienum); +} + +/* + * Metatable construction identity methods. + */ +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 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+2, meta_bitfield_newindex, "__newindex", this, NULL, -1); + + AttachEnumKeys(state, base+1, base+2, this); + + EnableMetaField(state, base+2, "whole", this); +} + +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); +} + +void global_identity::build_metatable(lua_State *state) +{ + MakeFieldMetatable(state, this, meta_global_index, meta_global_newindex); +} + +/** + * 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); + + 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_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); + 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); + } + + lua_pop(state, 1); + + SaveTypeInfo(state, (void*)field); + } +} + +void LuaWrapper::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); +} diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp index d6edef4e2..7a871e4be 100644 --- a/library/LuaWrapper.cpp +++ b/library/LuaWrapper.cpp @@ -35,6 +35,7 @@ distribution. // must be last due to MS stupidity #include "DataDefs.h" #include "DataIdentity.h" +#include "LuaWrapper.h" #include "MiscUtils.h" @@ -42,95 +43,14 @@ distribution. #include using namespace DFHack; +using namespace DFHack::LuaWrapper; 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" - -/* - * 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" -#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 - */ -#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) +void LuaWrapper::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); @@ -139,194 +59,14 @@ 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); - void DFHack::PushDFObject(lua_State *state, type_identity *type, void *ptr) { 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); -} - -static void push_adhoc_pointer(lua_State *state, void *ptr, type_identity *target); - -/************************************** - * Identity object read/write methods * - **************************************/ - -void constructed_identity::lua_read(lua_State *state, int fname_idx, void *ptr) -{ - 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"); -} - -void enum_identity::lua_read(lua_State *state, int fname_idx, void *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) -{ - base_type->lua_write(state, fname_idx, ptr, val_index); -} - -void df::number_identity_base::lua_read(lua_State *state, int fname_idx, void *ptr) -{ - lua_pushnumber(state, read(ptr)); -} - -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)); -} - -void df::bool_identity::lua_read(lua_State *state, int fname_idx, void *ptr) -{ - lua_pushboolean(state, *(bool*)ptr); -} - -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) || lua_isnil(state, val_index)) - *pb = lua_toboolean(state, val_index); - else if (lua_isnumber(state, val_index)) - *pb = lua_tointeger(state, val_index); - else - field_error(state, fname_idx, "boolean or number expected", "write"); -} - -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()); -} - -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); -} - -void df::pointer_identity::lua_read(lua_State *state, int fname_idx, void *ptr, type_identity *target) -{ - push_object_internal(state, target, *(void**)ptr); -} - -void df::pointer_identity::lua_read(lua_State *state, int fname_idx, void *ptr) -{ - lua_read(state, fname_idx, ptr, target); -} - -void df::pointer_identity::lua_write(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) -{ - 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); -} - -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); - void *pitem = item_pointer(id, ptr, idx); - 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) -{ - 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); -} - -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); - void *pitem = item_pointer(&df::identity_traits::identity, ptr, idx); - 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) -{ - 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); -} - -void bit_container_identity::lua_item_reference(lua_State *state, int, void *, int) +void *DFHack::GetDFObject(lua_State *state, type_identity *type, int val_index, bool exact_type) { - 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)); -} - -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"); + return get_object_internal(state, type, val_index, exact_type, false); } /* */ @@ -375,7 +115,7 @@ static void LookupInTable(lua_State *state, const char *tname) * 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) +bool LuaWrapper::LookupTypeInfo(lua_State *state, bool in_method) { // stack: [lookup key] @@ -390,7 +130,7 @@ static bool LookupTypeInfo(lua_State *state, bool in_method) // stack: [info] - if (lua_isnil(state, -1)) + if (!lua_islightuserdata(state, -1)) { lua_pop(state, 1); return false; @@ -399,7 +139,7 @@ static bool LookupTypeInfo(lua_State *state, bool in_method) return true; } -static void LookupInTable(lua_State *state, void *id, const char *tname) +void LuaWrapper::LookupInTable(lua_State *state, void *id, const char *tname) { lua_getfield(state, LUA_REGISTRYINDEX, tname); lua_pushlightuserdata(state, id); @@ -407,7 +147,7 @@ static void LookupInTable(lua_State *state, void *id, const char *tname) lua_remove(state, -2); } -static void SaveInTable(lua_State *state, void *node, const char *tname) +void LuaWrapper::SaveInTable(lua_State *state, void *node, const char *tname) { // stack: [info] lua_getfield(state, LUA_REGISTRYINDEX, tname); @@ -424,7 +164,7 @@ static void SaveInTable(lua_State *state, void *node, const char *tname) // stack: [info] } -static void SaveTypeInfo(lua_State *state, void *node) +void LuaWrapper::SaveTypeInfo(lua_State *state, void *node) { SaveInTable(state, node, DFHACK_TYPETABLE_NAME); } @@ -434,7 +174,7 @@ 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) +void LuaWrapper::push_object_ref(lua_State *state, void *ptr) { // stack: [metatable] auto ref = (DFRefHeader*)lua_newuserdata(state, sizeof(DFRefHeader)); @@ -445,7 +185,7 @@ static void push_object_ref(lua_State *state, void *ptr) // stack: [userdata] } -static void *get_object_ref(lua_State *state, int val_index) +void *LuaWrapper::get_object_ref(lua_State *state, int val_index) { assert(!lua_islightuserdata(state, val_index)); @@ -456,7 +196,7 @@ static void *get_object_ref(lua_State *state, int val_index) /** * Push the pointer using given identity. */ -static void push_object_internal(lua_State *state, type_identity *type, void *ptr, bool in_method) +void LuaWrapper::push_object_internal(lua_State *state, type_identity *type, void *ptr, bool in_method) { /* * If NULL pointer or no type, push something simple @@ -495,10 +235,115 @@ static void push_object_internal(lua_State *state, type_identity *type, void *pt push_object_ref(state, ptr); // metatable -> userdata } +static void fetch_container_details(lua_State *state, int meta, type_identity **pitem, int *pcount) +{ + if (!meta) return; + + lua_getfield(state, meta, "_field_identity"); + *pitem = (type_identity*)lua_touserdata(state, -1); + lua_pop(state, 1); + + if (pcount) + { + lua_getfield(state, meta, "_count"); + if (lua_isnumber(state, -1)) + *pcount = lua_tointeger(state, -1); + lua_pop(state, 1); + } +} + +/** + * Check if type1 and type2 are compatible, possibly using additional metatable data. + */ +static bool is_type_compatible(lua_State *state, type_identity *type1, int meta1, + type_identity *type2, int meta2, bool exact_equal) +{ + if (type1 == type2) + return true; + if (!exact_equal && !type1) + return true; + if (!type1 || !type2) + return false; + + auto t1 = type1->type(); + if (t1 != type2->type()) + return false; + + switch (t1) + { + case IDTYPE_POINTER: + return is_type_compatible(state, + ((pointer_identity*)type1)->getTarget(), 0, + ((pointer_identity*)type2)->getTarget(), 0, + exact_equal); + break; + + case IDTYPE_BUFFER: + { + auto b1 = (df::buffer_container_identity*)type1; + auto b2 = (df::buffer_container_identity*)type2; + type_identity *item1 = b1->getItemType(), *item2 = b2->getItemType(); + int count1 = b1->getSize(), count2 = b2->getSize(); + + fetch_container_details(state, meta1, &item1, &count1); + fetch_container_details(state, meta2, &item2, &count2); + + return item1 && item2 && count1 == count2 && + is_type_compatible(state, item1, 0, item2, 0, true); + } + + case IDTYPE_STL_PTR_VECTOR: + { + auto b1 = (df::stl_ptr_vector_identity*)type1; + auto b2 = (df::stl_ptr_vector_identity*)type2; + type_identity *item1 = b1->getItemType(), *item2 = b2->getItemType(); + + fetch_container_details(state, meta1, &item1, NULL); + fetch_container_details(state, meta1, &item2, NULL); + + return is_type_compatible(state, item1, 0, item2, 0, exact_equal); + } + + case IDTYPE_STRUCT: + case IDTYPE_CLASS: + { + auto b1 = (struct_identity*)type1; + auto b2 = (struct_identity*)type2; + + return (!exact_equal && b1->is_subclass(b2)); + } + + default: + return false; + } +} + +static bool is_type_compatible(lua_State *state, type_identity *type1, int meta1, + int meta2, bool exact_equal) +{ + lua_getfield(state, meta2, "_identity"); + auto type2 = (type_identity*)lua_touserdata(state, -1); + lua_pop(state, 1); + + return is_type_compatible(state, type1, meta1, type2, meta2, exact_equal); +} + +static bool is_type_compatible(lua_State *state, int meta1, int meta2, bool exact_equal) +{ + if (lua_rawequal(state, meta1, meta2)) + return true; + + lua_getfield(state, meta1, "_identity"); + auto type1 = (type_identity*)lua_touserdata(state, -1); + lua_pop(state, 1); + + return is_type_compatible(state, type1, meta1, meta2, exact_equal); +} + /** * 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) +void *LuaWrapper::get_object_internal(lua_State *state, type_identity *type, int val_index, bool exact_type, bool in_method) { /* * Non-userdata results in NULL; nil for NULL gets handled here too. @@ -524,14 +369,23 @@ static void *get_object_internal(lua_State *state, type_identity *type, int val_ if (!LookupTypeInfo(state, in_method)) // metatable -> type? return NULL; - bool ok = lua_islightuserdata(state, -1) && - (!type || lua_touserdata(state, -1) == type); + if (type && lua_touserdata(state, -1) != type) + { + /* + * If valid but different type, do an intelligent comparison. + */ + lua_pop(state, 1); // type -> () + lua_getmetatable(state, val_index); + + if (!is_type_compatible(state, type, 0, lua_gettop(state), exact_type)) + { + lua_pop(state, 1); // metatable -> () + return NULL; + } + } lua_pop(state, 1); // type -> () - if (!ok) - return NULL; - /* * Finally decode the reference. */ @@ -539,32 +393,42 @@ 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. + * Check if the object and metatable are a valid DF reference or type. */ -static type_identity *get_object_identity(lua_State *state, int objidx, - const char *ctx, bool allow_type = false) +static bool is_valid_metatable(lua_State *state, int objidx, int metaidx) { - 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); + lua_pushvalue(state, metaidx); + lua_rawget(state, UPVAL_TYPETABLE); } else { - if (!allow_type) - luaL_error(state, "Object expected in %s", ctx); - lua_pushvalue(state, objidx); LookupInTable(state, DFHACK_TYPEID_TABLE_NAME); } - if (lua_isnil(state, -1)) - luaL_error(state, "Invalid object metatable in %s", ctx); + bool ok = !lua_isnil(state, -1); lua_pop(state, 1); + return ok; +} + +/** + * 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, bool allow_type = false, + bool keep_metatable = false) +{ + if (!lua_getmetatable(state, objidx)) + luaL_error(state, "Invalid object in %s", ctx); + + if (!allow_type && !lua_isuserdata(state, objidx)) + luaL_error(state, "Object expected in %s", ctx); + + if (!is_valid_metatable(state, objidx, -1)) + luaL_error(state, "Invalid object metatable in %s", ctx); // Extract identity from metatable lua_getfield(state, -1, "_identity"); @@ -573,10 +437,32 @@ static type_identity *get_object_identity(lua_State *state, int objidx, if (!id) luaL_error(state, "Invalid object identity in %s", ctx); - lua_pop(state, 2); + lua_pop(state, keep_metatable ? 1 : 2); return id; } +static void check_type_compatible(lua_State *state, int obj1, int obj2, + type_identity **type1, type_identity **type2, + const char *ctx, bool allow_type, bool exact) +{ + int base = lua_gettop(state); + + *type1 = get_object_identity(state, obj1, ctx, allow_type, true); + *type2 = get_object_identity(state, obj2, ctx, allow_type, true); + + if (!is_type_compatible(state, *type1, base+1, *type2, base+2, exact)) + { + lua_getfield(state, base+1, "__metatable"); + const char *cname1 = lua_tostring(state, -1); + lua_getfield(state, base+2, "__metatable"); + const char *cname2 = lua_tostring(state, -1); + + luaL_error(state, "Types %s and %s incompatible in %s", cname1, cname2, ctx); + } + + lua_pop(state, 2); +} + /** * Metamethod: compare two DF object references. * @@ -585,25 +471,14 @@ static type_identity *get_object_identity(lua_State *state, int objidx, 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 (get_object_ref(state, 1) != get_object_ref(state, 2)) + !lua_getmetatable(state, 1) || !lua_getmetatable(state, 2) || + get_object_ref(state, 1) != get_object_ref(state, 2) || + !is_type_compatible(state, 3, 4, true)) { lua_pushboolean(state, false); return 1; } - if (!lua_rawequal(state, -1, -2)) - { - // todo: nonidentical type comparison - lua_pushboolean(state, false); - return 1; - } - lua_pushboolean(state, true); return 1; } @@ -705,149 +580,72 @@ static int meta_displace(lua_State *state) } /** - * Resolve the field name in UPVAL_FIELDTABLE, die if not found. + * Method: allocation for DF object references. */ -static void lookup_field(lua_State *state, int index, const char *mode) +static int meta_new(lua_State *state) { - lua_pushvalue(state, index); - lua_gettable(state, UPVAL_FIELDTABLE); // uses metatable with enum keys + int argc = lua_gettop(state); - if (lua_isnil(state, -1)) - field_error(state, index, "not found", mode); -} + if (argc != 1) + luaL_error(state, "Usage: object:new() or df.new(object)"); -static void *find_field(lua_State *state, int index, const char *mode) -{ - lookup_field(state, index, mode); + type_identity *id = get_object_identity(state, 1, "df.new()", true); - void *p = lua_touserdata(state, -1); - lua_pop(state, 1); - return p; -} + void *ptr = id->allocate(); + if (!ptr) + { + lua_pushnil(state); + return 1; + } -/** - * 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) || - !lua_getmetatable(state, obj)) - field_error(state, field, "invalid object", mode); - - if (!lua_rawequal(state, -1, UPVAL_METATABLE)) - field_error(state, field, "invalid object metatable", mode); + if (lua_isuserdata(state, 1)) + { + lua_getmetatable(state, 1); + push_object_ref(state, ptr); - lua_pop(state, 1); + id->copy(ptr, get_object_ref(state, 1)); + } + else + push_object_internal(state, id, ptr); - return (uint8_t*)get_object_ref(state, obj); + return 1; } -static void GetAdHocMetatable(lua_State *state, const struct_field_info *field); - -static void read_field(lua_State *state, const struct_field_info *field, void *ptr) +/** + * Method: assign data between objects. + */ +static int meta_assign(lua_State *state) { - switch (field->mode) - { - case struct_field_info::STATIC_STRING: - { - int len = strnlen((char*)ptr, field->count); - lua_pushlstring(state, (char*)ptr, len); - return; - } - - case struct_field_info::PRIMITIVE: - case struct_field_info::SUBSTRUCT: - field->type->lua_read(state, 2, ptr); - return; - - case struct_field_info::POINTER: - 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()) - { - field->type->lua_read(state, 2, ptr); - return; - } - // fallthrough + int argc = lua_gettop(state); - case struct_field_info::STATIC_ARRAY: - case struct_field_info::STL_VECTOR_PTR: - GetAdHocMetatable(state, field); - push_object_ref(state, ptr); - return; + if (argc != 2) + luaL_error(state, "Usage: target:assign(src) or df.assign(target,src)"); - case struct_field_info::END: - break; - } + type_identity *id1, *id2; + check_type_compatible(state, 1, 2, &id1, &id2, "df.assign()", false, false); - lua_pushnil(state); -} + if (!id1->copy(get_object_ref(state, 1), get_object_ref(state, 2))) + luaL_error(state, "No copy support for %s", id1->getFullName().c_str()); -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); + return 0; } -static void write_field(lua_State *state, const struct_field_info *field, void *ptr, int value_idx) +/** + * Verify that the object is a DF ref with UPVAL_METATABLE. + * If everything ok, extract the address. + */ +uint8_t *LuaWrapper::get_object_addr(lua_State *state, int obj, int field, const char *mode) { - switch (field->mode) - { - case struct_field_info::STATIC_STRING: - { - size_t 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))); - return; - } - - case struct_field_info::PRIMITIVE: - case struct_field_info::SUBSTRUCT: - case struct_field_info::CONTAINER: - field->type->lua_write(state, 2, ptr, value_idx); - return; + if (!lua_isuserdata(state, obj) || + !lua_getmetatable(state, obj)) + field_error(state, field, "invalid object", mode); - case struct_field_info::POINTER: - df::pointer_identity::lua_write(state, 2, ptr, field->type, value_idx); + if (!lua_rawequal(state, -1, UPVAL_METATABLE)) + field_error(state, field, "invalid object metatable", mode); - case struct_field_info::STATIC_ARRAY: - case struct_field_info::STL_VECTOR_PTR: - field_error(state, 2, "complex object", "write"); + lua_pop(state, 1); - case struct_field_info::END: - return; - } + return (uint8_t*)get_object_ref(state, obj); } /** @@ -879,319 +677,39 @@ 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"); - auto field = (struct_field_info*)find_field(state, 2, "read"); - if (!field) - return get_metafield(state); - read_field(state, field, ptr + field->offset); - 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. - */ -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; -} - -/** - * 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"); - auto type = (type_identity*)find_field(state, 2, "read"); - if (!type) - return get_metafield(state); - type->lua_read(state, 2, ptr); - return 1; -} - -/** - * Metamethod: __newindex for primitives. - */ -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; -} - -/** - * Metamethod: __len for containers. + * Make a metatable with most common fields, and an empty table for UPVAL_FIELDTABLE. */ -static int meta_container_len(lua_State *state) +void LuaWrapper::MakeMetatable(lua_State *state, type_identity *type, const char *kind) { - 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; -} + int base = lua_gettop(state); + lua_newtable(state); // metatable -/** - * 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 = NULL) -{ - if (lua_type(state, field) == LUA_TNUMBER) - return field; + lua_pushstring(state, type->getFullName().c_str()); + lua_setfield(state, base+1, "__metatable"); - lookup_field(state, field, mode ? mode : "read"); + lua_pushlightuserdata(state, type); + lua_setfield(state, base+1, "_identity"); - if (lua_isuserdata(state, -1) && !lua_touserdata(state, -1)) + LookupInTable(state, type, DFHACK_TYPEID_TABLE_NAME); + if (lua_isnil(state, -1)) { - if (mode) - field_error(state, field, "builtin property", mode); - + // Copy the string from __metatable if no real type lua_pop(state, 1); - get_metafield(state); - return 0; - } - - return -1; -} - -/** - * 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); - 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); - if (!iidx) - return 1; - - 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"); - id->lua_item_read(state, 2, ptr, idx); - 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, "write"); - - 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, "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); - 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) - { - size_t intv = 0; - memcpy(&intv, ptr, std::min(sizeof(intv), size_t(id->byte_size()))); - lua_pushnumber(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"); - - size_t intv = (size_t)lua_tonumber(state, 3); - memcpy(ptr, &intv, std::min(sizeof(intv), size_t(id->byte_size()))); - return 0; + lua_getfield(state, base+1, "__metatable"); } + lua_setfield(state, base+1, "_type"); - 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"); - if (!field) - return get_metafield(state); - void *ptr = *(void**)field->offset; - if (!ptr) - field_error(state, 2, "global address not known", "read"); - read_field(state, field, ptr); - return 1; -} - -/** - * Metamethod: __newindex for df.global - */ -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"); - write_field(state, field, ptr, 3); - return 0; -} - -/** - * Add fields in the array to the UPVAL_FIELDTABLE candidates on the stack. - */ -static void IndexFields(lua_State *state, struct_identity *pstruct) -{ - // stack: fieldtable - - int base = lua_gettop(state); - - for (struct_identity *p = pstruct; p; p = p->getParent()) - { - auto fields = p->getFields(); - - for (; fields; ++fields) - { - if (fields->mode == struct_field_info::END) - break; + lua_pushstring(state, kind); + lua_setfield(state, base+1, "_kind"); - lua_pushstring(state,fields->name); - lua_pushlightuserdata(state,(void*)fields); - lua_rawset(state,base); - } - } + lua_newtable(state); // fieldtable } /** * 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) +void LuaWrapper::EnableMetaField(lua_State *state, int ftable_idx, const char *name, void *id) { lua_pushlightuserdata(state, id); lua_setfield(state, ftable_idx, name); @@ -1200,7 +718,7 @@ static void EnableMetaField(lua_State *state, int ftable_idx, const char *name, /** * Set metatable properties common to all actual DF object references. */ -static void SetPtrMethods(lua_State *state, int meta_idx, int read_idx) +void LuaWrapper::SetPtrMethods(lua_State *state, int meta_idx, int read_idx) { lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_COMPARE_NAME); lua_setfield(state, meta_idx, "__eq"); @@ -1210,26 +728,33 @@ 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"); - - 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, "_field"); + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_SIZEOF_NAME); + lua_setfield(state, meta_idx, "sizeof"); EnableMetaField(state, read_idx, "sizeof"); + + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_NEW_NAME); + lua_setfield(state, meta_idx, "new"); + EnableMetaField(state, read_idx, "new"); + + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_ASSIGN_NAME); + lua_setfield(state, meta_idx, "assign"); + EnableMetaField(state, read_idx, "assign"); + + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_DISPLACE_NAME); + lua_setfield(state, meta_idx, "_displace"); EnableMetaField(state, read_idx, "_displace"); } /** * 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) +void LuaWrapper::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); @@ -1238,76 +763,12 @@ static void SetStructMethod(lua_State *state, int meta_idx, int ftable_idx, lua_setfield(state, meta_idx, name); } -/** - * 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) -{ - 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)) - { - // Copy the string from __metatable if no real type - lua_pop(state, 1); - lua_getfield(state, base+1, "__metatable"); - } - lua_setfield(state, base+1, "_type"); - - lua_pushstring(state, kind); - lua_setfield(state, base+1, "_kind"); - - lua_newtable(state); // fieldtable -} - -/** - * 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, "struct"); // meta, fields - - IndexFields(state, pstruct); - - SetStructMethod(state, base+1, base+2, reader, "__index"); - SetStructMethod(state, base+1, base+2, writer, "__newindex"); - - // 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, "primitive"); - - SetPtrMethods(state, base+1, base+2); - - EnableMetaField(state, base+2, "value", type); - - SetStructMethod(state, base+1, base+2, meta_primitive_index, "__index"); - SetStructMethod(state, base+1, base+2, 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) +void LuaWrapper::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_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPETABLE_NAME); lua_pushvalue(state, meta_idx); @@ -1328,7 +789,7 @@ static void SetContainerMethod(lua_State *state, int meta_idx, int ftable_idx, * 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) +void LuaWrapper::AttachEnumKeys(lua_State *state, int meta_idx, int ftable_idx, type_identity *ienum) { LookupInTable(state, ienum, DFHACK_ENUM_TABLE_NAME); @@ -1337,93 +798,15 @@ static void AttachEnumKeys(lua_State *state, int base, type_identity *ienum) lua_newtable(state); lua_swap(state); lua_setfield(state, -2, "__index"); - lua_setmetatable(state, base+2); + lua_setmetatable(state, ftable_idx); } 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, "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"); - lua_setfield(state, base+1, "_type"); - - lua_pushlightuserdata(state, item); - lua_setfield(state, base+1, "_field_identity"); + lua_setfield(state, meta_idx, "_enum"); - if (count >= 0) - { - lua_pushinteger(state, count); - lua_setfield(state, base+1, "_count"); - } - - 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+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); -} - -/* - * Metatable construction identity methods. - */ -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 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+2, meta_bitfield_newindex, "__newindex", this, NULL, -1); - - AttachEnumKeys(state, base, this); - - EnableMetaField(state, base+2, "whole", this); -} - -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); -} - -void global_identity::build_metatable(lua_State *state) -{ - MakeFieldMetatable(state, this, meta_global_index, meta_global_newindex); + EnableMetaField(state, ftable_idx, "_enum"); } static void BuildTypeMetatable(lua_State *state, type_identity *type) @@ -1435,81 +818,6 @@ 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); - - 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_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); - 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); - } - - lua_pop(state, 1); - - SaveTypeInfo(state, (void*)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. */ @@ -1614,6 +922,8 @@ static void RenderType(lua_State *state, compound_identity *node) lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_SIZEOF_NAME); lua_setfield(state, base, "sizeof"); + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_NEW_NAME); + lua_setfield(state, base, "new"); if (node->type() == IDTYPE_GLOBAL) { @@ -1681,12 +991,22 @@ 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_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPETABLE_NAME); + lua_pushcclosure(state, meta_sizeof, 1); lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_SIZEOF_NAME); - lua_pushcfunction(state, meta_displace); + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPETABLE_NAME); + lua_pushcclosure(state, meta_displace, 1); lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_DISPLACE_NAME); + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPETABLE_NAME); + lua_pushcclosure(state, meta_new, 1); + lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_NEW_NAME); + + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPETABLE_NAME); + lua_pushcclosure(state, meta_assign, 1); + lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_ASSIGN_NAME); + luaL_register(state, "df", no_functions); { @@ -1698,8 +1018,12 @@ 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_NEW_NAME); + lua_setfield(state, -2, "new"); lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_DISPLACE_NAME); lua_setfield(state, -2, "_displace"); + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_ASSIGN_NAME); + lua_setfield(state, -2, "assign"); freeze_table(state, true, "df"); lua_remove(state, -2); diff --git a/library/VersionInfoFactory.cpp b/library/VersionInfoFactory.cpp index 66cea00a9..cf63e3b1a 100644 --- a/library/VersionInfoFactory.cpp +++ b/library/VersionInfoFactory.cpp @@ -47,6 +47,7 @@ VersionInfoFactory::~VersionInfoFactory() { clear(); } + void VersionInfoFactory::clear() { // for each stored version, delete @@ -83,11 +84,11 @@ void VersionInfoFactory::ParseVersion (TiXmlElement* entry, VersionInfo* mem) TiXmlElement* pMemEntry; const char *cstr_name = entry->Attribute("name"); if (!cstr_name) - throw Error::MemoryXmlBadAttribute("name"); + throw Error::SymbolsXmlBadAttribute("name"); const char *cstr_os = entry->Attribute("os-type"); if (!cstr_os) - throw Error::MemoryXmlBadAttribute("os-type"); + throw Error::SymbolsXmlBadAttribute("os-type"); string os = cstr_os; mem->setVersion(cstr_name); @@ -106,7 +107,7 @@ void VersionInfoFactory::ParseVersion (TiXmlElement* entry, VersionInfo* mem) } else { - throw Error::MemoryXmlBadAttribute("os-type"); + throw Error::SymbolsXmlBadAttribute("os-type"); } // process additional entries @@ -121,7 +122,7 @@ void VersionInfoFactory::ParseVersion (TiXmlElement* entry, VersionInfo* mem) { const char *cstr_key = pMemEntry->Attribute("name"); if(!cstr_key) - throw Error::MemoryXmlUnderspecifiedEntry(cstr_name); + throw Error::SymbolsXmlUnderspecifiedEntry(cstr_name); const char *cstr_value = pMemEntry->Attribute("value"); if(!cstr_value) { @@ -134,14 +135,14 @@ void VersionInfoFactory::ParseVersion (TiXmlElement* entry, VersionInfo* mem) { const char *cstr_value = pMemEntry->Attribute("value"); if(!cstr_value) - throw Error::MemoryXmlUnderspecifiedEntry(cstr_name); + throw Error::SymbolsXmlUnderspecifiedEntry(cstr_name); mem->addMD5(cstr_value); } else if (type == "binary-timestamp") { const char *cstr_value = pMemEntry->Attribute("value"); if(!cstr_value) - throw Error::MemoryXmlUnderspecifiedEntry(cstr_name); + throw Error::SymbolsXmlUnderspecifiedEntry(cstr_name); mem->addPE(strtol(cstr_value, 0, 16)); } } // for @@ -157,7 +158,7 @@ bool VersionInfoFactory::loadFile(string path_to_xml) { error = true; cerr << "failed!\n"; - throw Error::MemoryXmlParse(doc.ErrorDesc(), doc.ErrorId(), doc.ErrorRow(), doc.ErrorCol()); + throw Error::SymbolsXmlParse(doc.ErrorDesc(), doc.ErrorId(), doc.ErrorRow(), doc.ErrorCol()); } else { @@ -174,13 +175,13 @@ bool VersionInfoFactory::loadFile(string path_to_xml) if (!pElem) { error = true; - throw Error::MemoryXmlNoRoot(); + throw Error::SymbolsXmlNoRoot(); } string m_name=pElem->Value(); if(m_name != "data-definition") { error = true; - throw Error::MemoryXmlNoRoot(); + throw Error::SymbolsXmlNoRoot(); } // save this for later hRoot=TiXmlHandle(pElem); diff --git a/library/Virtual.cpp b/library/Virtual.cpp deleted file mode 100644 index 6706f93be..000000000 --- a/library/Virtual.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "Internal.h" - -#include -#include -#include - -#include "MemAccess.h" -#include "Core.h" -#include "Virtual.h" -using namespace DFHack; - -std::string t_virtual::getClassName() const -{ - Core & c = Core::getInstance(); - return c.p->readClassName(vptr); -} \ No newline at end of file diff --git a/library/include/BitArray.h b/library/include/BitArray.h index ff17550a7..fd9bd98fc 100644 --- a/library/include/BitArray.h +++ b/library/include/BitArray.h @@ -184,10 +184,15 @@ namespace DFHack memcpy(m_data, other.m_data,m_size*sizeof(T)); } + typedef T value_type; + T *data() { return m_data; } const T *data() const { return m_data; } unsigned size() const { return m_size; } + T *begin() { return m_data; } + T *end() { return m_data+m_size; } + T& operator[] (unsigned i) { return m_data[i]; } const T& operator[] (unsigned i) const { return m_data[i]; } @@ -217,5 +222,15 @@ namespace DFHack memcpy(data(), other.data(), sizeof(T)*size()); return *this; } + + void erase(T *ptr) { + memmove(ptr, ptr+1, sizeof(T)*(m_size - (ptr - m_data))); m_size--; + } + void insert(T *ptr, const T &item) { + int idx = ptr - m_data; + resize(m_size+1); + memmove(m_data + idx + 1, m_data + idx, sizeof(T)*(m_size - idx - 1)); + m_data[idx] = item; + } }; } \ No newline at end of file diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index 17751d90d..48b79721b 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -58,6 +58,7 @@ namespace DFHack IDTYPE_ENUM, IDTYPE_STRUCT, IDTYPE_CLASS, + IDTYPE_BUFFER, IDTYPE_STL_PTR_VECTOR }; @@ -90,6 +91,11 @@ namespace DFHack virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) = 0; virtual void build_metatable(lua_State *state); + // lua_read doesn't just return a reference to the object + virtual bool isPrimitive() { return true; } + // needs constructor/destructor + virtual bool isConstructed() { return false; } + // inherits from container_identity virtual bool isContainer() { return false; } void *allocate(); @@ -103,6 +109,9 @@ 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); } @@ -160,6 +169,8 @@ namespace DFHack virtual identity_type type() { return IDTYPE_BITFIELD; } + virtual bool isConstructed() { return false; } + int getNumBits() { return num_bits; } const bitfield_item_info *getBits() { return bits; } @@ -194,6 +205,9 @@ namespace DFHack type_identity *getBaseType() { return base_type; } + virtual bool isPrimitive() { return true; } + virtual bool isConstructed() { return false; } + 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); }; @@ -344,7 +358,7 @@ namespace DFHack /** * 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); + DFHACK_EXPORT void *GetDFObject(lua_State *state, type_identity *type, int val_index, bool exact_type = false); template T *ifnull(T *a, T *b) { return a ? a : b; } @@ -639,7 +653,7 @@ namespace DFHack { * Represent flag array bits as a string, using sep as join separator. */ template - inline std::string bitfield_to_string(const BitArray &val, const std::string &sep = " ") { + inline std::string flagarray_to_string(const BitArray &val, const std::string &sep = " ") { std::vector tmp; flagarray_to_string(&tmp, val); return join_strings(sep, tmp); @@ -659,8 +673,8 @@ namespace DFHack { * 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); + T *GetDFObject(lua_State *state, int val_index, bool exact_type = false) { + return (T*)GetDFObject(state, df::identity_traits::get(), val_index, exact_type); } } diff --git a/library/include/DataIdentity.h b/library/include/DataIdentity.h index 808634798..e0f864225 100644 --- a/library/include/DataIdentity.h +++ b/library/include/DataIdentity.h @@ -84,14 +84,24 @@ namespace DFHack virtual std::string getFullName(type_identity *item); - int lua_item_count(lua_State *state, void *ptr); + enum CountMode { + COUNT_LEN, COUNT_READ, COUNT_WRITE + }; + + int lua_item_count(lua_State *state, void *ptr, CountMode cnt); 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); + virtual bool resize(void *ptr, int size) { return false; } + virtual bool erase(void *ptr, int index) { return false; } + virtual bool insert(void *ptr, int index, void *pitem) { return false; } + + virtual bool lua_insert(lua_State *state, int fname_idx, void *ptr, int idx, int val_index); + protected: - virtual int item_count(void *ptr) = 0; + virtual int item_count(void *ptr, CountMode cnt) = 0; virtual void *item_pointer(type_identity *item, void *ptr, int idx) = 0; }; @@ -108,6 +118,8 @@ namespace DFHack 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); + + virtual bool lua_insert(lua_State *state, int fname_idx, void *ptr, int idx, int val_index); }; class DFHACK_EXPORT bit_container_identity : public container_identity { @@ -175,25 +187,33 @@ namespace df virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index); }; - class stl_string_identity : public primitive_identity { + class stl_string_identity : public DFHack::constructed_identity { public: - stl_string_identity() : primitive_identity(sizeof(std::string)) {}; + stl_string_identity() + : constructed_identity(sizeof(std::string), &allocator_fn) + {}; std::string getFullName() { return "string"; } + virtual DFHack::identity_type type() { return DFHack::IDTYPE_PRIMITIVE; } + + virtual bool isPrimitive() { 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); }; class stl_ptr_vector_identity : public ptr_container_identity { public: + typedef std::vector container; + /* * 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) + : ptr_container_identity(sizeof(container),allocator_fn,item, ienum) {}; std::string getFullName(type_identity *item) { @@ -202,12 +222,27 @@ namespace df virtual DFHack::identity_type type() { return DFHack::IDTYPE_STL_PTR_VECTOR; } + virtual bool resize(void *ptr, int size) { + (*(container*)ptr).resize(size); + return true; + } + virtual bool erase(void *ptr, int size) { + auto &ct = *(container*)ptr; + ct.erase(ct.begin()+size); + return true; + } + virtual bool insert(void *ptr, int idx, void *item) { + auto &ct = *(container*)ptr; + ct.insert(ct.begin()+idx, item); + return true; + } + protected: - virtual int item_count(void *ptr) { - return ((std::vector*)ptr)->size(); + virtual int item_count(void *ptr, CountMode) { + return ((container*)ptr)->size(); }; virtual void *item_pointer(type_identity *, void *ptr, int idx) { - return &(*(std::vector*)ptr)[idx]; + return &(*(container*)ptr)[idx]; } }; @@ -224,11 +259,14 @@ namespace df {} std::string getFullName(type_identity *item); + int getSize() { return size; } + + virtual DFHack::identity_type type() { return DFHack::IDTYPE_BUFFER; } static buffer_container_identity base_instance; protected: - virtual int item_count(void *ptr) { return size; } + virtual int item_count(void *ptr, CountMode) { return size; } virtual void *item_pointer(type_identity *item, void *ptr, int idx) { return ((uint8_t*)ptr) + idx * item->byte_size(); } @@ -247,8 +285,23 @@ namespace df return name + container_identity::getFullName(item); } + virtual bool resize(void *ptr, int size) { + (*(T*)ptr).resize(size); + return true; + } + virtual bool erase(void *ptr, int size) { + auto &ct = *(T*)ptr; + ct.erase(ct.begin()+size); + return true; + } + virtual bool insert(void *ptr, int idx, void *item) { + auto &ct = *(T*)ptr; + ct.insert(ct.begin()+idx, *(typename T::value_type*)item); + return true; + } + protected: - virtual int item_count(void *ptr) { return ((T*)ptr)->size(); } + virtual int item_count(void *ptr, CountMode) { return ((T*)ptr)->size(); } virtual void *item_pointer(type_identity *item, void *ptr, int idx) { return &(*(T*)ptr)[idx]; } @@ -271,8 +324,15 @@ namespace df return "BitArray<>"; } + virtual bool resize(void *ptr, int size) { + ((container*)ptr)->resize(size); + return true; + } + protected: - virtual int item_count(void *ptr) { return ((container*)ptr)->size * 8; } + virtual int item_count(void *ptr, CountMode cnt) { + return cnt == COUNT_LEN ? ((container*)ptr)->size * 8 : -1; + } virtual bool get_item(void *ptr, int idx) { return ((container*)ptr)->is_set(idx); } @@ -293,8 +353,15 @@ namespace df return "vector" + bit_container_identity::getFullName(item); } + virtual bool resize(void *ptr, int size) { + (*(container*)ptr).resize(size); + return true; + } + protected: - virtual int item_count(void *ptr) { return ((container*)ptr)->size(); } + virtual int item_count(void *ptr, CountMode) { + return ((container*)ptr)->size(); + } virtual bool get_item(void *ptr, int idx) { return (*(container*)ptr)[idx]; } diff --git a/library/include/Error.h b/library/include/Error.h index 1a25d1cbb..159a84713 100644 --- a/library/include/Error.h +++ b/library/include/Error.h @@ -24,9 +24,6 @@ distribution. #pragma once -#ifndef ERROR_H_INCLUDED -#define ERROR_H_INCLUDED - #include "Export.h" #include "Pragma.h" #include @@ -42,176 +39,34 @@ namespace DFHack * the whole array of DFHack exceptions from the rest */ class DFHACK_EXPORT All : public std::exception{}; - class DFHACK_EXPORT AllMemdef : public All{}; - class DFHACK_EXPORT NoProcess : public All - { - public: - virtual const char* what() const throw() - { - return "couldn't find a suitable process"; - } - }; - - class DFHACK_EXPORT CantAttach : public All - { - public: - virtual const char* what() const throw() - { - return "couldn't attach to process"; - } - }; - - class DFHACK_EXPORT NoMapLoaded : public All - { - public: - virtual const char* what() const throw() - { - return "no map has been loaded in the dwarf fortress process"; - } - }; - - class DFHACK_EXPORT BadMapDimensions : public All - { - public: - BadMapDimensions(uint32_t& _x, uint32_t& _y) : x(_x), y(_y) {} - const uint32_t x; - const uint32_t y; - - virtual const char* what() const throw() - { - return "both x and y needs to be between 0 and 48"; - } - }; - - // a call to DFHack::mem_info::get* failed - class DFHACK_EXPORT MissingMemoryDefinition : public AllMemdef - { - public: - MissingMemoryDefinition(const char* _type, const std::string _key) : type(_type), key(_key) - { - std::stringstream s; - s << "memory object not declared: type=" << type << " key=" << key; - full = s.str(); - } - // Used by functios using integer keys, such as getTrait - MissingMemoryDefinition(const char* _type, uint32_t _key) : type(_type) - { - std::stringstream s1; - s1 << _key; - key = s1.str(); - - std::stringstream s; - s << "memory object not declared: type=" << type << " key=" << key; - full = s.str(); - } - virtual ~MissingMemoryDefinition() throw(){}; - - std::string full; - const std::string type; - std::string key; - - virtual const char* what() const throw() - { - return full.c_str(); - } - }; - - // a call to DFHack::mem_info::get* failed - class DFHACK_EXPORT UnsetMemoryDefinition : public AllMemdef - { - public: - UnsetMemoryDefinition(const char* _type, const std::string _key) : type(_type), key(_key) - { - std::stringstream s; - s << "memory object not set: type " << type << " key " << key; - full = s.str(); - } - // Used by functios using integer keys, such as getTrait - UnsetMemoryDefinition(const char* _type, uint32_t _key) : type(_type) - { - std::stringstream s1; - s1 << _key; - key = s1.str(); - - std::stringstream s; - s << "memory object not set: type " << type << " key " << key; - full = s.str(); - } - virtual ~UnsetMemoryDefinition() throw(){}; - - std::string full; - const std::string type; - std::string key; - - virtual const char* what() const throw() - { - return full.c_str(); - } - }; - - // a call to DFHack::mem_info::get* failed - class DFHACK_EXPORT InvalidMemoryDefinition : public AllMemdef - { - public: - InvalidMemoryDefinition(const char* _type, const std::string _key) : type(_type), key(_key) - { - std::stringstream s; - s << "memory object is INVALID: type " << type << " key " << key; - full = s.str(); - } - // Used by functios using integer keys, such as getTrait - InvalidMemoryDefinition(const char* _type, uint32_t _key) : type(_type) - { - std::stringstream s1; - s1 << _key; - key = s1.str(); - - std::stringstream s; - s << "memory object is INVALID: type " << type << " key " << key; - full = s.str(); - } - virtual ~InvalidMemoryDefinition() throw(){}; - - std::string full; - const std::string type; - std::string key; - - virtual const char* what() const throw() - { - return full.c_str(); - } - }; - - + class DFHACK_EXPORT AllSymbols : public All{}; // Syntax errors and whatnot, the xml can't be read - class DFHACK_EXPORT MemoryXmlParse : public All + class DFHACK_EXPORT SymbolsXmlParse : public AllSymbols { public: - MemoryXmlParse(const char* _desc, int _id, int _row, int _col) + SymbolsXmlParse(const char* _desc, int _id, int _row, int _col) :desc(_desc), id(_id), row(_row), col(_col) { std::stringstream s; s << "error " << id << ": " << desc << ", at row " << row << " col " << col; full = s.str(); } - std::string full; const std::string desc; const int id; const int row; const int col; - virtual ~MemoryXmlParse() throw(){}; - + virtual ~SymbolsXmlParse() throw(){}; virtual const char* what() const throw() { return full.c_str(); } }; - class DFHACK_EXPORT MemoryXmlBadAttribute : public All + class DFHACK_EXPORT SymbolsXmlBadAttribute : public All { public: - MemoryXmlBadAttribute(const char* _attr) : attr(_attr) + SymbolsXmlBadAttribute(const char* _attr) : attr(_attr) { std::stringstream s; s << "attribute is either missing or invalid: " << attr; @@ -219,58 +74,34 @@ namespace DFHack } std::string full; std::string attr; - - virtual ~MemoryXmlBadAttribute() throw(){}; - + virtual ~SymbolsXmlBadAttribute() throw(){}; virtual const char* what() const throw() { return full.c_str(); } }; - class DFHACK_EXPORT MemoryXmlNoRoot : public All + class DFHACK_EXPORT SymbolsXmlNoRoot : public All { public: - MemoryXmlNoRoot() {} - - virtual ~MemoryXmlNoRoot() throw(){}; - - virtual const char* what() const throw() - { - return "no pElem found"; - } - }; - - class DFHACK_EXPORT MemoryXmlNoDFExtractor : public All - { - public: - MemoryXmlNoDFExtractor(const char* _name) : name(_name) - { - std::stringstream s; - s << "DFExtractor != " << name; - full = s.str(); - } - virtual ~MemoryXmlNoDFExtractor() throw(){}; - - std::string name; - std::string full; - + SymbolsXmlNoRoot() {} + virtual ~SymbolsXmlNoRoot() throw(){}; virtual const char* what() const throw() { - return full.c_str(); + return "Symbol file is missing root element."; } }; - class DFHACK_EXPORT MemoryXmlUnderspecifiedEntry : public All + class DFHACK_EXPORT SymbolsXmlUnderspecifiedEntry : public All { public: - MemoryXmlUnderspecifiedEntry(const char * _where) : where(_where) + SymbolsXmlUnderspecifiedEntry(const char * _where) : where(_where) { std::stringstream s; - s << "underspecified MemInfo entry, each entry needs to set both the name attribute and have a value. parent: " << where; + s << "Underspecified symbol file entry, each entry needs to set both the name attribute and have a value. parent: " << where; full = s.str(); } - virtual ~MemoryXmlUnderspecifiedEntry() throw(){}; + virtual ~SymbolsXmlUnderspecifiedEntry() throw(){}; std::string where; std::string full; virtual const char* what() const throw() @@ -278,104 +109,6 @@ namespace DFHack return full.c_str(); } }; - - class DFHACK_EXPORT MemoryXmlUnknownType : public All - { - public: - MemoryXmlUnknownType(const char* _type) : type(_type) - { - std::stringstream s; - s << "unknown MemInfo type: " << type; - full = s.str(); - } - virtual ~MemoryXmlUnknownType() throw(){}; - - std::string type; - std::string full; - - virtual const char* what() const throw() - { - return full.c_str(); - } - }; - - class DFHACK_EXPORT SHMServerDisappeared : public All - { - public: - SHMServerDisappeared(){} - virtual ~SHMServerDisappeared() throw(){}; - virtual const char* what() const throw() - { - return "The server process has disappeared"; - } - }; - class DFHACK_EXPORT SHMLockingError : public All - { - public: - SHMLockingError(const char* _type) : type(_type) - { - std::stringstream s; - s << "SHM locking error: " << type; - full = s.str(); - } - virtual ~SHMLockingError() throw(){}; - - std::string type; - std::string full; - - virtual const char* what() const throw() - { - return full.c_str(); - } - }; - class DFHACK_EXPORT MemoryAccessDenied : public All - { - public: - std::string descr; - MemoryAccessDenied(uint64_t address) - { - std::stringstream s; - s << "Invalid memory access @0x" << std::hex << address; - descr = s.str(); - } - virtual ~MemoryAccessDenied() throw(){}; - virtual const char* what() const throw() - { - return descr.c_str(); - } - }; - class DFHACK_EXPORT SHMVersionMismatch : public All - { - public: - SHMVersionMismatch() {} - virtual ~SHMVersionMismatch() throw(){}; - virtual const char* what() const throw() - { - return "SHM VERSION MISMATCH"; - } - }; - class DFHACK_EXPORT SHMAttachFailure : public All - { - public: - SHMAttachFailure() {} - virtual ~SHMAttachFailure() throw(){}; - virtual const char* what() const throw() - { - return "SHM ATTACH FAILURE"; - } - }; - class DFHACK_EXPORT ModuleNotInitialized : public All - { - public: - ModuleNotInitialized() {} - virtual ~ModuleNotInitialized() throw(){}; - virtual const char* what() const throw() - { - return "Programmer error: module not initialized!"; - } - }; - } } -#endif // ERROR_H_INCLUDED diff --git a/library/include/LuaWrapper.h b/library/include/LuaWrapper.h new file mode 100644 index 000000000..2d98aeda6 --- /dev/null +++ b/library/include/LuaWrapper.h @@ -0,0 +1,190 @@ +/* +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" + +#include +#include + +/** + * Internal header file of the lua wrapper. + */ + +namespace DFHack { namespace LuaWrapper { + +/* + * 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" + +/* + * 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" +#define DFHACK_TYPE_TOSTRING_NAME "DFHack::TypeToString" +#define DFHACK_SIZEOF_NAME "DFHack::Sizeof" +#define DFHACK_DISPLACE_NAME "DFHack::Displace" +#define DFHACK_NEW_NAME "DFHack::New" +#define DFHACK_ASSIGN_NAME "DFHack::Assign" + +/* + * 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) +#define UPVAL_METHOD_NAME 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) + + inline void lua_dup(lua_State *state) { lua_pushvalue(state, -1); } + inline void lua_swap(lua_State *state) { lua_insert(state, -2); } + + /** + * Object references are represented as userdata instances + * with an appropriate metatable; the payload of userdata is + * this structure: + */ + struct DFRefHeader { + void *ptr; + }; + + /** + * Push the pointer as DF object ref using metatable on the stack. + */ + void push_object_ref(lua_State *state, void *ptr); + void *get_object_ref(lua_State *state, int val_index); + + /* + * 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). + */ + void field_error(lua_State *state, int index, const char *err, const char *mode); + + /* + * If is_method is true, these use UPVAL_TYPETABLE to save a hash lookup. + */ + void push_object_internal(lua_State *state, type_identity *type, void *ptr, bool in_method = true); + void *get_object_internal(lua_State *state, type_identity *type, int val_index, bool exact_type, bool in_method = true); + + void push_adhoc_pointer(lua_State *state, void *ptr, type_identity *target); + + /** + * Verify that the object is a DF ref with UPVAL_METATABLE. + * If everything ok, extract the address. + */ + uint8_t *get_object_addr(lua_State *state, int obj, int field, const char *mode); + + void LookupInTable(lua_State *state, void *id, const char *tname); + void SaveInTable(lua_State *state, void *node, const char *tname); + void SaveTypeInfo(lua_State *state, void *node); + + /** + * Look up the key on the stack in DFHACK_TYPETABLE; + * if found, put result on the stack and return true. + */ + bool LookupTypeInfo(lua_State *state, bool in_method); + + /** + * Make a metatable with most common fields, and an empty table for UPVAL_FIELDTABLE. + */ + void MakeMetatable(lua_State *state, type_identity *type, const char *kind); + /** + * Enable a metafield by injecting an entry into a UPVAL_FIELDTABLE. + */ + void EnableMetaField(lua_State *state, int ftable_idx, const char *name, void *id = NULL); + /** + * Set metatable properties common to all actual DF object references. + */ + void SetPtrMethods(lua_State *state, int meta_idx, int read_idx); + /** + * Add a struct-style (3 upvalues) metamethod to the metatable. + */ + void SetStructMethod(lua_State *state, int meta_idx, int ftable_idx, + lua_CFunction function, const char *name); + /** + * Add a 6 upvalue metamethod to the metatable. + */ + 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); + /** + * If ienum refers to a valid enum, attach its keys to UPVAL_FIELDTABLE, + * and the enum itself to the _enum metafield. + */ + void AttachEnumKeys(lua_State *state, int meta_idx, int ftable_idx, type_identity *ienum); +}} + diff --git a/library/include/Virtual.h b/library/include/Virtual.h deleted file mode 100644 index 3bb02dcb7..000000000 --- a/library/include/Virtual.h +++ /dev/null @@ -1,36 +0,0 @@ -/* -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 -namespace DFHack -{ - /// very generic representation of a virtual class... just the pointer to the vtable. - /// this is intended for instances where wrapping the classes properly hasn't been done yet. - struct t_virtual - { - void * vptr; - std::string getClassName() const; - }; -} \ No newline at end of file diff --git a/library/include/modules/Gui.h b/library/include/modules/Gui.h index 6ffe7b8ed..f1e0335ae 100644 --- a/library/include/modules/Gui.h +++ b/library/include/modules/Gui.h @@ -25,7 +25,6 @@ distribution. #pragma once #include "Export.h" #include "Module.h" -#include "Virtual.h" #include "BitArray.h" #include "ColorText.h" #include diff --git a/library/include/modules/Items.h b/library/include/modules/Items.h index 6f10cb49d..e58c9214b 100644 --- a/library/include/modules/Items.h +++ b/library/include/modules/Items.h @@ -29,7 +29,6 @@ distribution. #include "Export.h" #include "Module.h" #include "Types.h" -#include "Virtual.h" #include "modules/Materials.h" #include "MemAccess.h" @@ -123,8 +122,6 @@ DFHACK_EXPORT bool copyItem(df::item * source, dfh_item & target); /// write copied item back to its origin DFHACK_EXPORT bool writeItem(const dfh_item & item); -/// get the class name of an item -DFHACK_EXPORT std::string getItemClass(const df::item * item); /// who owns this item we already read? DFHACK_EXPORT int32_t getItemOwnerID(const df::item * item); DFHACK_EXPORT df::unit *getItemOwner(const df::item * item); diff --git a/library/include/modules/Maps.h b/library/include/modules/Maps.h index 67e2aac89..873da7a9e 100644 --- a/library/include/modules/Maps.h +++ b/library/include/modules/Maps.h @@ -34,7 +34,6 @@ distribution. #include "Module.h" #include "modules/Vegetation.h" #include -#include "Virtual.h" #include "BitArray.h" #include "modules/Materials.h" diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index 7a2f5c71f..19618fc10 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -41,7 +41,6 @@ using namespace std; #include "modules/Units.h" #include "ModuleFactory.h" #include "Core.h" -#include "Virtual.h" #include "MiscUtils.h" #include "df/world.h" @@ -502,10 +501,3 @@ bool Items::removeItemOwner(df::item * item) return true; } - -std::string Items::getItemClass(const df::item * item) -{ - const t_virtual * virt = (t_virtual *) item; - return virt->getClassName(); -} - diff --git a/library/modules/Maps.cpp b/library/modules/Maps.cpp index 32c9ac60b..db66086e0 100644 --- a/library/modules/Maps.cpp +++ b/library/modules/Maps.cpp @@ -88,7 +88,10 @@ bool Maps::IsValid () void Maps::getSize (uint32_t& x, uint32_t& y, uint32_t& z) { if (!IsValid()) - throw DFHack::Error::ModuleNotInitialized(); + { + x = y = z = 0; + return; + } x = world->map.x_count_block; y = world->map.y_count_block; z = world->map.z_count_block; @@ -98,7 +101,10 @@ void Maps::getSize (uint32_t& x, uint32_t& y, uint32_t& z) void Maps::getPosition (int32_t& x, int32_t& y, int32_t& z) { if (!IsValid()) - throw DFHack::Error::ModuleNotInitialized(); + { + x = y = z = 0; + return; + } x = world->map.region_x; y = world->map.region_y; z = world->map.region_z; diff --git a/library/modules/Windows.cpp b/library/modules/Windows.cpp index 196e4d71a..0cdd7e831 100644 --- a/library/modules/Windows.cpp +++ b/library/modules/Windows.cpp @@ -24,7 +24,6 @@ distribution. #include "Export.h" #include "Module.h" -#include "Virtual.h" #include "BitArray.h" #include diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index ff03de3e6..5875ce877 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -60,6 +60,8 @@ ADD_CUSTOM_COMMAND( DEPENDS protoc-bin ${PROJECT_PROTOS} ) +SET_SOURCE_FILES_PROPERTIES( Brushes.h PROPERTIES HEADER_FILE_ONLY TRUE ) + # Plugins OPTION(BUILD_SUPPORTED "Build the supported plugins (reveal, probe, etc.)." ON) if (BUILD_SUPPORTED) @@ -79,8 +81,8 @@ if (BUILD_SUPPORTED) DFHACK_PLUGIN(vdig vdig.cpp) DFHACK_PLUGIN(colonies colonies.cpp) DFHACK_PLUGIN(mode mode.cpp) - DFHACK_PLUGIN(liquids liquids.cpp) - DFHACK_PLUGIN(tiletypes tiletypes.cpp) + DFHACK_PLUGIN(liquids liquids.cpp Brushes.h) + DFHACK_PLUGIN(tiletypes tiletypes.cpp Brushes.h) DFHACK_PLUGIN(tubefill tubefill.cpp) DFHACK_PLUGIN(autodump autodump.cpp) DFHACK_PLUGIN(cleanowned cleanowned.cpp) diff --git a/plugins/tiletypes.cpp b/plugins/tiletypes.cpp index d704f030a..32d0ba191 100644 --- a/plugins/tiletypes.cpp +++ b/plugins/tiletypes.cpp @@ -20,6 +20,7 @@ using std::set; #include "TileTypes.h" #include "modules/MapCache.h" #include "df/tile_dig_designation.h" +#include "Brushes.h" using namespace MapExtras; using namespace DFHack; using namespace df::enums; @@ -487,150 +488,6 @@ void help( std::ostream & out, const std::string &option) } } -typedef std::vector coord_vec; - -class Brush -{ -public: - virtual ~Brush() {}; - virtual coord_vec points(MapExtras::MapCache &mc, DFHack::DFCoord start) = 0; -}; -/** - * generic 3D rectangle brush. you can specify the dimensions of - * the rectangle and optionally which tile is its 'center' - */ -class RectangleBrush : public Brush -{ - int x_, y_, z_; - int cx_, cy_, cz_; - -public: - RectangleBrush(int x, int y, int z = 1, int centerx = -1, int centery = -1, int centerz = -1) - { - if (centerx == -1) - { - cx_ = x/2; - } - else - { - cx_ = centerx; - } - - if (centery == -1) - { - cy_ = y/2; - } - else - { - cy_ = centery; - } - - if (centerz == -1) - { - cz_ = z/2; - } - else - { - cz_ = centerz; - } - - x_ = x; - y_ = y; - z_ = z; - }; - - coord_vec points(MapExtras::MapCache &mc, DFHack::DFCoord start) - { - coord_vec v; - DFHack::DFCoord iterstart(start.x - cx_, start.y - cy_, start.z - cz_); - DFHack::DFCoord iter = iterstart; - for (int xi = 0; xi < x_; xi++) - { - for (int yi = 0; yi < y_; yi++) - { - for (int zi = 0; zi < z_; zi++) - { - if(mc.testCoord(iter)) - v.push_back(iter); - - iter.z++; - } - - iter.z = iterstart.z; - iter.y++; - } - - iter.y = iterstart.y; - iter.x ++; - } - - return v; - }; - - ~RectangleBrush(){}; -}; - -/** - * stupid block brush, legacy. use when you want to apply something to a whole DF map block. - */ -class BlockBrush : public Brush -{ -public: - BlockBrush() {}; - ~BlockBrush() {}; - - coord_vec points(MapExtras::MapCache &mc, DFHack::DFCoord start) - { - coord_vec v; - DFHack::DFCoord blockc = start % 16; - DFHack::DFCoord iterc = blockc * 16; - if (!mc.testCoord(start)) - return v; - - for (int xi = 0; xi < 16; xi++) - { - for (int yi = 0; yi < 16; yi++) - { - v.push_back(iterc); - iterc.y++; - } - iterc.x++; - } - - return v; - }; -}; - -/** - * Column from a position through open space tiles - * example: create a column of magma - */ -class ColumnBrush : public Brush -{ -public: - ColumnBrush(){}; - - ~ColumnBrush(){}; - - coord_vec points(MapExtras::MapCache &mc, DFHack::DFCoord start) - { - coord_vec v; - bool juststarted = true; - while (mc.testCoord(start)) - { - df::tiletype tt = mc.tiletypeAt(start); - if(LowPassable(tt) || juststarted && HighPassable(tt)) - { - v.push_back(start); - juststarted = false; - start.z++; - } - else break; - } - return v; - }; -}; - CommandHistory tiletypes_hist; command_result df_tiletypes (color_ostream &out, vector & parameters);