/* 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; static luaL_Reg no_functions[] = { { NULL, NULL } }; /** * Report an error while accessing a field (index = field name). */ 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); const char *fname = index ? lua_tostring(state, index) : "*"; luaL_error(state, "Cannot %s field %s.%s: %s.", mode, (cname ? cname : "?"), (fname ? fname : "?"), err); } 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, bool exact_type) { return get_object_internal(state, type, val_index, exact_type, false); } /* */ static int change_error(lua_State *state) { luaL_error(state, "Attempt to change a read-only table.\n"); return 0; } /** * Wrap a table so that it can't be modified. */ static void freeze_table(lua_State *state, bool leave_metatable = false, const char *name = NULL) { // rv = {}; setmetatable(rv, { __index = in, __newindex = change_error, __metatable = name }) int base = lua_gettop(state); lua_newtable(state); lua_swap(state); lua_setfield(state, base, "__index"); lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_CHANGEERROR_NAME); 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 LookupInTable(lua_State *state, const char *tname) { lua_getfield(state, LUA_REGISTRYINDEX, tname); lua_swap(state); lua_rawget(state, -2); lua_remove(state, -2); } /** * Look up the key on the stack in DFHACK_TYPETABLE; * if found, put result on the stack and return true. */ bool LuaWrapper::LookupTypeInfo(lua_State *state, bool in_method) { // stack: [lookup key] if (in_method) { lua_rawget(state, UPVAL_TYPETABLE); } else { LookupInTable(state, DFHACK_TYPETABLE_NAME); } // stack: [info] if (!lua_islightuserdata(state, -1)) { lua_pop(state, 1); return false; } else return true; } void LuaWrapper::LookupInTable(lua_State *state, void *id, const char *tname) { lua_getfield(state, LUA_REGISTRYINDEX, tname); lua_pushlightuserdata(state, id); lua_rawget(state, -2); lua_remove(state, -2); } void LuaWrapper::SaveInTable(lua_State *state, void *node, const char *tname) { // stack: [info] lua_getfield(state, LUA_REGISTRYINDEX, tname); lua_pushlightuserdata(state, node); lua_pushvalue(state, -3); lua_rawset(state, -3); lua_pushvalue(state, -2); lua_pushlightuserdata(state, node); lua_rawset(state, -3); lua_pop(state, 1); // stack: [info] } void LuaWrapper::SaveTypeInfo(lua_State *state, void *node) { SaveInTable(state, node, DFHACK_TYPETABLE_NAME); } static void BuildTypeMetatable(lua_State *state, type_identity *type); /** * Push the pointer as DF object ref using metatable on the stack. */ void LuaWrapper::push_object_ref(lua_State *state, void *ptr) { // stack: [metatable] auto ref = (DFRefHeader*)lua_newuserdata(state, sizeof(DFRefHeader)); ref->ptr = ptr; lua_swap(state); lua_setmetatable(state, -2); // stack: [userdata] } void *LuaWrapper::get_object_ref(lua_State *state, int val_index) { assert(!lua_islightuserdata(state, val_index)); auto ref = (DFRefHeader*)lua_touserdata(state, val_index); return ref->ptr; } /** * Push the pointer using given identity. */ 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 */ if (!ptr || !type) { if (!ptr) lua_pushnil(state); else lua_pushlightuserdata(state, ptr); return; } /* * Resolve actual class using vtable */ if (type->type() == IDTYPE_CLASS) { virtual_identity *class_vid = virtual_identity::get(virtual_ptr(ptr)); if (class_vid) type = class_vid; } /* * Resolve metatable by identity, and push the object */ lua_pushlightuserdata(state, type); // () -> type if (!LookupTypeInfo(state, in_method)) // type -> metatable? BuildTypeMetatable(state, type); // () -> metatable 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. */ 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. */ if (!lua_isuserdata(state, val_index)) return NULL; /* * Light user data is allowed with null type; otherwise bail out. */ if (!lua_getmetatable(state, val_index)) // () -> metatable? { if (!type && lua_islightuserdata(state, val_index)) return lua_touserdata(state, val_index); return NULL; } /* * Verify that the metatable is known, and refers to the correct type. * Here doing reverse lookup of identity by metatable. */ if (!LookupTypeInfo(state, in_method)) // metatable -> type? return NULL; 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 -> () /* * Finally decode the reference. */ return get_object_ref(state, val_index); } /** * Check if the object and metatable are a valid DF reference or type. */ static bool is_valid_metatable(lua_State *state, int objidx, int metaidx) { // Verify object type validity if (lua_isuserdata(state, objidx)) { lua_pushvalue(state, metaidx); lua_rawget(state, UPVAL_TYPETABLE); } else { lua_pushvalue(state, objidx); LookupInTable(state, DFHACK_TYPEID_TABLE_NAME); } 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"); type_identity *id = (type_identity*)lua_touserdata(state, -1); if (!id) luaL_error(state, "Invalid object identity in %s", ctx); 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. * * Equal if same pointer and same metatable. */ 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) || get_object_ref(state, 1) != get_object_ref(state, 2) || !is_type_compatible(state, 3, 4, true)) { lua_pushboolean(state, false); return 1; } lua_pushboolean(state, true); return 1; } /** * Method: sizeof for DF object references. * * Returns: size[, address] */ static int meta_sizeof(lua_State *state) { int argc = lua_gettop(state); if (argc != 1) luaL_error(state, "Usage: object:sizeof() or df.sizeof(object)"); // Two special cases: nil and lightuserdata for NULL and void* if (lua_isnil(state, 1)) { lua_pushnil(state); lua_pushinteger(state, 0); return 2; } if (lua_islightuserdata(state, 1)) { lua_pushnil(state); lua_pushnumber(state, (size_t)lua_touserdata(state, 1)); return 2; } type_identity *id = get_object_identity(state, 1, "df.sizeof()", true); lua_pushinteger(state, id->byte_size()); if (lua_isuserdata(state, 1)) { lua_pushnumber(state, (size_t)get_object_ref(state, 1)); return 2; } else return 1; } /** * Method: displace for DF object references. * * Returns: a reference with the same type, but modified address */ static int meta_displace(lua_State *state) { int argc = lua_gettop(state); bool has_step = (argc >= 3); if ((argc < 2 || argc > 3) || !lua_isnumber(state, 2) || (has_step && !lua_isnumber(state, 3))) { luaL_error(state, "Usage: object:_displace(index[,step]) or df._displace(object,...)"); } int index = lua_tointeger(state, 2); int step = has_step ? lua_tointeger(state, 3) : 1; // Two special cases: nil and lightuserdata for NULL and void* if (lua_isnil(state, 1)) { lua_pushnil(state); return 1; } if (lua_islightuserdata(state, 1)) { if (!has_step) luaL_error(state, "Step is mandatory in _displace of void*"); auto ptr = (uint8_t*)lua_touserdata(state, 1); lua_pushlightuserdata(state, ptr + index*step); return 1; } type_identity *id = get_object_identity(state, 1, "df._displace()"); if (!has_step) step = id->byte_size(); if (index == 0 || step == 0) { lua_pushvalue(state, 1); } else { auto ptr = (uint8_t*)get_object_ref(state, 1); lua_getmetatable(state, 1); push_object_ref(state, ptr + index*step); } return 1; } /** * Method: allocation for DF object references. */ static int meta_new(lua_State *state) { int argc = lua_gettop(state); if (argc != 1) luaL_error(state, "Usage: object:new() or df.new(object)"); type_identity *id = get_object_identity(state, 1, "df.new()", true); void *ptr = id->allocate(); if (!ptr) { lua_pushnil(state); return 1; } if (lua_isuserdata(state, 1)) { lua_getmetatable(state, 1); push_object_ref(state, ptr); id->copy(ptr, get_object_ref(state, 1)); } else push_object_internal(state, id, ptr); return 1; } /** * Method: assign data between objects. */ static int meta_assign(lua_State *state) { int argc = lua_gettop(state); if (argc != 2) luaL_error(state, "Usage: target:assign(src) or df.assign(target,src)"); type_identity *id1, *id2; check_type_compatible(state, 1, 2, &id1, &id2, "df.assign()", false, false); 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()); return 0; } /** * 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) { 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); lua_pop(state, 1); return (uint8_t*)get_object_ref(state, obj); } /** * 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; } /** * Make a metatable with most common fields, and an empty table for UPVAL_FIELDTABLE. */ void LuaWrapper::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 } /** * Enable a metafield by injecting an entry into a UPVAL_FIELDTABLE. */ void LuaWrapper::EnableMetaField(lua_State *state, int ftable_idx, const char *name, void *id) { lua_pushlightuserdata(state, id); lua_setfield(state, ftable_idx, name); } /** * Set metatable properties common to all actual DF object references. */ 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"); lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPETABLE_NAME); lua_pushvalue(state, meta_idx); lua_pushcclosure(state, meta_ptr_tostring, 2); lua_setfield(state, meta_idx, "__tostring"); 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. */ 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); lua_pushvalue(state, ftable_idx); lua_pushcclosure(state, function, 3); lua_setfield(state, meta_idx, name); } /** * Add a 6 upvalue metamethod to the metatable. */ 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); lua_pushvalue(state, ftable_idx); lua_pushlightuserdata(state, container); lua_pushlightuserdata(state, item); if (count < 0) lua_pushnil(state); else lua_pushinteger(state, count); lua_pushcclosure(state, function, 6); lua_setfield(state, meta_idx, name); } /** * If ienum refers to a valid enum, attach its keys to UPVAL_FIELDTABLE, * and the enum itself to the _enum metafield. */ void LuaWrapper::AttachEnumKeys(lua_State *state, int meta_idx, int ftable_idx, type_identity *ienum) { LookupInTable(state, ienum, DFHACK_ENUM_TABLE_NAME); if (!lua_isnil(state, -1)) { lua_newtable(state); lua_swap(state); lua_setfield(state, -2, "__index"); lua_setmetatable(state, ftable_idx); } else lua_pop(state, 1); LookupInTable(state, ienum, DFHACK_TYPEID_TABLE_NAME); lua_setfield(state, meta_idx, "_enum"); EnableMetaField(state, ftable_idx, "_enum"); } static void BuildTypeMetatable(lua_State *state, type_identity *type) { type->build_metatable(state); lua_pop(state, 1); SaveTypeInfo(state, type); } /* * Recursive walk of scopes to construct the df... tree. */ static void RenderTypeChildren(lua_State *state, const std::vector &children); static void AssociateId(lua_State *state, int table, int val, const char *name) { lua_pushinteger(state, val); lua_pushstring(state, name); lua_dup(state); lua_pushinteger(state, val); lua_rawset(state, table); lua_rawset(state, table); } static void RenderType(lua_State *state, compound_identity *node) { assert(node->getName()); std::string name = node->getFullName(); lua_newtable(state); if (!lua_checkstack(state, 20)) return; int base = lua_gettop(state); switch (node->type()) { case IDTYPE_STRUCT: lua_pushstring(state, "struct-type"); lua_setfield(state, base, "_kind"); break; case IDTYPE_CLASS: lua_pushstring(state, "class-type"); lua_setfield(state, base, "_kind"); break; case IDTYPE_ENUM: { lua_pushstring(state, "enum-type"); lua_setfield(state, base, "_kind"); enum_identity *eid = (enum_identity*)node; const char *const *keys = eid->getKeys(); // For enums, set mapping between keys and values for (int64_t i = eid->getFirstItem(), j = 0; i <= eid->getLastItem(); i++, j++) { if (keys[j]) AssociateId(state, base, i, keys[j]); } if (eid->getFirstItem() <= eid->getLastItem()) { lua_pushinteger(state, eid->getFirstItem()); lua_setfield(state, base, "_first_item"); lua_pushinteger(state, eid->getLastItem()); lua_setfield(state, base, "_last_item"); } SaveInTable(state, node, DFHACK_ENUM_TABLE_NAME); } break; case IDTYPE_BITFIELD: { lua_pushstring(state, "bitfield-type"); lua_setfield(state, base, "_kind"); bitfield_identity *eid = (bitfield_identity*)node; auto bits = eid->getBits(); for (int i = 0; i < eid->getNumBits(); i++) { if (bits[i].name) AssociateId(state, base, i, bits[i].name); if (bits[i].size > 1) i += bits[i].size-1; } lua_pushinteger(state, 0); lua_setfield(state, base, "_first_item"); lua_pushinteger(state, eid->getNumBits()-1); lua_setfield(state, base, "_last_item"); SaveInTable(state, node, DFHACK_ENUM_TABLE_NAME); } break; default: break; } RenderTypeChildren(state, node->getScopeChildren()); assert(base == lua_gettop(state)); 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) { BuildTypeMetatable(state, node); // Set metatable for the inner table lua_dup(state); lua_setmetatable(state, base); lua_swap(state); // -> meta curtable freeze_table(state, true, "global"); // Copy __newindex to the outer metatable lua_getfield(state, base, "__newindex"); lua_setfield(state, -2, "__newindex"); lua_remove(state, base); } else { freeze_table(state, true, name.c_str()); } lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPE_TOSTRING_NAME); lua_setfield(state, -2, "__tostring"); lua_pushlightuserdata(state, node); lua_setfield(state, -2, "_identity"); lua_pop(state, 1); SaveInTable(state, node, DFHACK_TYPEID_TABLE_NAME); } 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_rawset(state, -3); } } static void DoAttach(lua_State *state) { int base = lua_gettop(state); lua_newtable(state); lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_PTR_IDTABLE_NAME); lua_newtable(state); lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_TYPEID_TABLE_NAME); lua_newtable(state); lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_ENUM_TABLE_NAME); lua_pushcfunction(state, change_error); lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_CHANGEERROR_NAME); lua_pushcfunction(state, meta_ptr_compare); lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_COMPARE_NAME); lua_pushcfunction(state, meta_type_tostring); lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_TYPE_TOSTRING_NAME); lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPETABLE_NAME); lua_pushcclosure(state, meta_sizeof, 1); lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_SIZEOF_NAME); 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); { // Assign df a metatable with read-only contents lua_newtable(state); // Render the type structure RenderTypeChildren(state, compound_identity::getTopScope()); 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); lua_setmetatable(state, -2); } lua_pop(state, 1); } /** * Initialize access to DF objects from the interpreter * context, unless it has already been done. */ void DFHack::AttachDFGlobals(lua_State *state) { if (luaL_newmetatable(state, DFHACK_TYPETABLE_NAME)) DoAttach(state); lua_pop(state, 1); }