diff --git a/CMakeLists.txt b/CMakeLists.txt index d39d940e2..1c538e246 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,6 +103,7 @@ ENDIF() # use shared libraries for protobuf ADD_DEFINITIONS(-DPROTOBUF_USE_DLLS) +ADD_DEFINITIONS(-DLUA_BUILD_AS_DLL) if(UNIX) add_definitions(-D_LINUX) diff --git a/depends/lua/CMakeLists.txt b/depends/lua/CMakeLists.txt index b135f221d..6a97bd434 100644 --- a/depends/lua/CMakeLists.txt +++ b/depends/lua/CMakeLists.txt @@ -76,8 +76,13 @@ src/lzio.c ) LIST(APPEND SRC_LIBLUA ${HDR_LIBLUA}) -ADD_LIBRARY ( lua STATIC EXCLUDE_FROM_ALL ${SRC_LIBLUA} ) +ADD_LIBRARY ( lua SHARED EXCLUDE_FROM_ALL ${SRC_LIBLUA} ) TARGET_LINK_LIBRARIES ( lua ${LIBS}) + +install(TARGETS lua + LIBRARY DESTINATION ${DFHACK_LIBRARY_DESTINATION} + RUNTIME DESTINATION ${DFHACK_LIBRARY_DESTINATION}) + IDE_FOLDER(lua "Depends") #SET ( SRC_LUA src/lua.c ) diff --git a/depends/lua/include/luaconf.h b/depends/lua/include/luaconf.h index 660793356..b202967b3 100644 --- a/depends/lua/include/luaconf.h +++ b/depends/lua/include/luaconf.h @@ -151,11 +151,20 @@ ** the libraries, you may want to use the following definition (define ** LUA_BUILD_AS_DLL to get it). */ +#ifdef __cplusplus + #define LUA_API_EXTERN extern "C" +#else + #define LUA_API_EXTERN extern +#endif #if defined(LUA_BUILD_AS_DLL) - #if defined(LUA_CORE) || defined(LUA_LIB) - #define LUA_API __declspec(dllexport) + #if defined(_MSC_VER) + #if defined(LUA_CORE) || defined(LUA_LIB) + #define LUA_API __declspec(dllexport) LUA_API_EXTERN + #else + #define LUA_API __declspec(dllimport) LUA_API_EXTERN + #endif #else - #define LUA_API __declspec(dllimport) + #define LUA_API LUA_API_EXTERN __attribute__ ((visibility("default"))) #endif #else #ifdef __cplusplus diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 7d3c4d6bb..3b1c6e7bf 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -55,6 +55,7 @@ SET(MAIN_SOURCES Core.cpp ColorText.cpp DataDefs.cpp +LuaWrapper.cpp DataStatics.cpp DataStaticsCtor.cpp DataStaticsFields.cpp @@ -251,7 +252,7 @@ ENDIF() #effectively disables debug builds... SET_TARGET_PROPERTIES(dfhack PROPERTIES DEBUG_POSTFIX "-debug" ) -TARGET_LINK_LIBRARIES(dfhack protobuf-lite clsocket ${PROJECT_LIBS}) +TARGET_LINK_LIBRARIES(dfhack protobuf-lite clsocket lua ${PROJECT_LIBS}) SET_TARGET_PROPERTIES(dfhack PROPERTIES LINK_INTERFACE_LIBRARIES "") TARGET_LINK_LIBRARIES(dfhack-client protobuf-lite clsocket) diff --git a/library/DataStaticsFields.cpp b/library/DataStaticsFields.cpp index 0e6e9957e..a9d2f3121 100644 --- a/library/DataStaticsFields.cpp +++ b/library/DataStaticsFields.cpp @@ -13,6 +13,27 @@ #pragma GCC diagnostic ignored "-Winvalid-offsetof" +namespace df { +#define ATOM_IDENTITY_TRAITS(type) \ + primitive_identity identity_traits::identity(sizeof(type)); + + ATOM_IDENTITY_TRAITS(char); + ATOM_IDENTITY_TRAITS(int8_t); + ATOM_IDENTITY_TRAITS(uint8_t); + ATOM_IDENTITY_TRAITS(int16_t); + ATOM_IDENTITY_TRAITS(uint16_t); + ATOM_IDENTITY_TRAITS(int32_t); + ATOM_IDENTITY_TRAITS(uint32_t); + ATOM_IDENTITY_TRAITS(int64_t); + ATOM_IDENTITY_TRAITS(uint64_t); + ATOM_IDENTITY_TRAITS(bool); + ATOM_IDENTITY_TRAITS(float); + ATOM_IDENTITY_TRAITS(std::string); + ATOM_IDENTITY_TRAITS(void*); + +#undef ATOM_IDENTITY_TRAITS +} + #define TID(type) (&identity_traits< type >::identity) #define FLD(mode, name) struct_field_info::mode, #name, offsetof(CUR_STRUCT, name) diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp new file mode 100644 index 000000000..3ed4a35cd --- /dev/null +++ b/library/LuaWrapper.cpp @@ -0,0 +1,218 @@ +/* +https://github.com/peterix/dfhack +Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include "Internal.h" + +#include +#include +#include + +#include "MemAccess.h" +#include "Core.h" +#include "VersionInfo.h" +#include "tinythread.h" +// must be last due to MS stupidity +#include "DataDefs.h" +#include "DataIdentity.h" + +#include "MiscUtils.h" + +#include +#include + +using namespace DFHack; + +static luaL_Reg no_functions[] = { { NULL, NULL } }; + +inline void lua_dup(lua_State *state) { lua_pushvalue(state, -1); } +inline void lua_swap(lua_State *state) { lua_insert(state, -2); } + +static int change_error(lua_State *state) +{ + luaL_error(state, "Attempt to change a read-only table.\n"); +} + +static void freeze_table(lua_State *state, bool leave_metatable = false, const char *name = NULL) +{ + // rv = {}; setmetatable(rv, { __index = in, __newindex = change_error, __metatable = name }) + int base = lua_gettop(state); + lua_newtable(state); + lua_swap(state); + lua_setfield(state, base, "__index"); + lua_getfield(state, LUA_REGISTRYINDEX, "DFHack.ChangeError"); + lua_setfield(state, base, "__newindex"); + lua_newtable(state); + lua_swap(state); + lua_dup(state); + lua_setmetatable(state, base); + if (name) + { + lua_pushstring(state, name); + lua_setfield(state, -2, "__metatable"); + } + // result: [frozen table] [metatable] + if (!leave_metatable) + lua_pop(state, 1); +} + +static void SaveTypeInfo(lua_State *state, void *node) +{ + lua_getfield(state, LUA_REGISTRYINDEX, "DFHack.DFTypes"); + lua_pushlightuserdata(state, node); + lua_pushvalue(state, -3); + lua_settable(state, -3); + lua_pop(state, 1); +} + +static bool RegisterTypeInfo(lua_State *state, void *node) +{ + lua_getfield(state, LUA_REGISTRYINDEX, "DFHack.DFTypes"); + int base = lua_gettop(state); + + lua_pushlightuserdata(state, node); + lua_rawget(state, base); + + bool added = false; + + if (lua_isnil(state, -1)) + { + lua_pop(state, 1); + + lua_newtable(state); + lua_pushlightuserdata(state, node); + lua_pushvalue(state, -2); + lua_rawset(state, base); + + added = true; + } + + lua_remove(state, -2); + return added; +} + +static void RenderTypeChildren(lua_State *state, const std::vector &children); + +static void RenderType(lua_State *state, compound_identity *node) +{ + assert(node->getName()); + + lua_newtable(state); + if (!lua_checkstack(state, 20)) + return; + + int base = lua_gettop(state); + + lua_pushlightuserdata(state, node); + lua_setfield(state, base, "_identity"); + + switch (node->type()) + { + case IDTYPE_ENUM: + { + enum_identity *eid = (enum_identity*)node; + const char *const *keys = eid->getKeys(); + + // For enums, set mapping between keys and values + for (int64_t i = eid->getFirstItem(), j = 0; i <= eid->getLastItem(); i++, j++) + { + if (!keys[j]) + continue; + + lua_pushinteger(state, i); + lua_pushstring(state, keys[j]); + lua_dup(state); + lua_pushinteger(state, i); + + lua_settable(state, base); + lua_settable(state, base); + } + + if (eid->getFirstItem() <= eid->getLastItem()) + { + lua_pushstring(state, "_first_item"); + lua_pushinteger(state, eid->getFirstItem()); + lua_settable(state, base); + + lua_pushstring(state, "_last_item"); + lua_pushinteger(state, eid->getLastItem()); + lua_settable(state, base); + } + + SaveTypeInfo(state, node); + } + break; + + default: + break; + } + + RenderTypeChildren(state, node->getScopeChildren()); + + assert(base == lua_gettop(state)); + + freeze_table(state, false, node->getName()); +} + +static void RenderTypeChildren(lua_State *state, const std::vector &children) +{ + for (size_t i = 0; i < children.size(); i++) + { + RenderType(state, children[i]); + lua_pushstring(state, children[i]->getName()); + lua_swap(state); + lua_settable(state, -3); + } +} + +static void DoAttach(lua_State *state) +{ + int base = lua_gettop(state); + + lua_pushcfunction(state, change_error); + lua_setfield(state, LUA_REGISTRYINDEX, "DFHack.ChangeError"); + + luaL_register(state, "df", no_functions); + + { + // Assign df a metatable with read-only contents + lua_newtable(state); + + // Render the type structure + RenderTypeChildren(state, compound_identity::getTopScope()); + + freeze_table(state, true, "df"); + lua_remove(state, -2); + lua_setmetatable(state, -2); + } + + lua_pop(state, 1); +} + +void DFHack::AttachDFGlobals(lua_State *state) +{ + if (luaL_newmetatable(state, "DFHack.DFTypes")) + DoAttach(state); + + lua_pop(state, 1); +} diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index 2a9567182..3a7679b65 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -37,6 +37,8 @@ distribution. #undef interface #endif +typedef struct lua_State lua_State; + /* * Definitions of DFHack namespace structs used by generated headers. */ @@ -155,6 +157,8 @@ namespace DFHack virtual identity_type type() { return IDTYPE_ENUM; } + int64_t getFirstItem() { return first_item_value; } + int64_t getLastItem() { return last_item_value; } int getCount() { return int(last_item_value-first_item_value+1); } const char *const *getKeys() { return keys; } }; @@ -280,6 +284,8 @@ namespace DFHack void InitDataDefGlobals(Core *core); + DFHACK_EXPORT void AttachDFGlobals(lua_State *state); + template T *ifnull(T *a, T *b) { return a ? a : b; } diff --git a/library/include/DataIdentity.h b/library/include/DataIdentity.h index 679023fcf..d354beb03 100644 --- a/library/include/DataIdentity.h +++ b/library/include/DataIdentity.h @@ -65,8 +65,7 @@ namespace df template<> struct identity_traits { \ static primitive_identity identity; \ static primitive_identity *get() { return &identity; } \ - }; \ - primitive_identity identity_traits::identity(sizeof(type)); + }; ATOM_IDENTITY_TRAITS(char); ATOM_IDENTITY_TRAITS(int8_t);