From a3e526abdb01a07ba3679ec3e6b3e7a7028c6aec Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 1 Apr 2012 17:32:57 +0400 Subject: [PATCH] Add df.is_instance(a,b) to check if typeof(b) is subtype of typeof(a). Allows both objects and types as arguments. Also accepts nil and primitives as b, returning nil. --- library/LuaWrapper.cpp | 65 ++++++++++++++++++++++++++++++------ library/include/LuaWrapper.h | 1 + 2 files changed, 55 insertions(+), 11 deletions(-) diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp index b9314f07b..5b2ff5631 100644 --- a/library/LuaWrapper.cpp +++ b/library/LuaWrapper.cpp @@ -431,9 +431,9 @@ type_identity *LuaWrapper::get_object_identity(lua_State *state, int objidx, return id; } -static void check_type_compatible(lua_State *state, int obj1, int obj2, +static bool check_type_compatible(lua_State *state, int obj1, int obj2, type_identity **type1, type_identity **type2, - const char *ctx, bool allow_type, bool exact) + const char *ctx, bool allow_type, bool exact, bool error = true) { int base = lua_gettop(state); @@ -442,6 +442,12 @@ static void check_type_compatible(lua_State *state, int obj1, int obj2, if (!is_type_compatible(state, *type1, base+1, *type2, base+2, exact)) { + if (!error) + { + lua_pop(state, 2); + return false; + } + lua_getfield(state, base+1, "__metatable"); const char *cname1 = lua_tostring(state, -1); lua_getfield(state, base+2, "__metatable"); @@ -451,6 +457,7 @@ static void check_type_compatible(lua_State *state, int obj1, int obj2, } lua_pop(state, 2); + return true; } /** @@ -741,6 +748,30 @@ static int meta_assign(lua_State *state) return 0; } +/** + * Method: check if the two objects are assignment-compatible. + */ +static int meta_is_instance(lua_State *state) +{ + int argc = lua_gettop(state); + + if (argc != 2) + luaL_error(state, "Usage: type:is_instance(obj) or df.is_instance(type,obj)"); + + // If garbage as second argument, return nil + if (!lua_istable(state, 2) && (!lua_isuserdata(state,2) || lua_islightuserdata(state, 2))) + { + lua_pushnil(state); + return 1; + } + + type_identity *id1, *id2; + bool ok = check_type_compatible(state, 1, 2, &id1, &id2, "df.is_instance()", true, false, false); + + lua_pushboolean(state, ok); + return 1; +} + /** * Method: deallocation for DF object references. */ @@ -1063,12 +1094,7 @@ static void FillEnumKeys(lua_State *state, int ftable, enum_identity *eid) // Create a new table attached to ftable as __index lua_newtable(state); - - lua_dup(state); - lua_setmetatable(state, ftable); - int base = lua_gettop(state); - lua_newtable(state); // For enums, set mapping between keys and values @@ -1102,28 +1128,36 @@ static void FillEnumKeys(lua_State *state, int ftable, enum_identity *eid) } lua_setfield(state, base, "__index"); - lua_pop(state, 1); + lua_setmetatable(state, ftable); } static void FillBitfieldKeys(lua_State *state, int ftable, bitfield_identity *eid) { + // Create a new table attached to ftable as __index + lua_newtable(state); + int base = lua_gettop(state); + lua_newtable(state); + auto bits = eid->getBits(); for (int i = 0; i < eid->getNumBits(); i++) { if (bits[i].name) - AssociateId(state, ftable, i, bits[i].name); + AssociateId(state, base+1, i, bits[i].name); if (bits[i].size > 1) i += bits[i].size-1; } lua_pushinteger(state, 0); - lua_setfield(state, ftable, "_first_item"); + lua_setfield(state, base+1, "_first_item"); lua_pushinteger(state, eid->getNumBits()-1); - lua_setfield(state, ftable, "_last_item"); + lua_setfield(state, base+1, "_last_item"); SaveInTable(state, eid, DFHACK_ENUM_TABLE_NAME); + + lua_setfield(state, base, "__index"); + lua_setmetatable(state, ftable); } static void RenderType(lua_State *state, compound_identity *node) @@ -1218,6 +1252,9 @@ static void RenderType(lua_State *state, compound_identity *node) lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_NEW_NAME); lua_setfield(state, ftable, "new"); + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_IS_INSTANCE_NAME); + lua_setfield(state, ftable, "is_instance"); + lua_pop(state, 2); } @@ -1273,6 +1310,10 @@ static int DoAttach(lua_State *state) lua_pushcclosure(state, meta_assign, 1); lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_ASSIGN_NAME); + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPETABLE_NAME); + lua_pushcclosure(state, meta_is_instance, 1); + lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_IS_INSTANCE_NAME); + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPETABLE_NAME); lua_pushcclosure(state, meta_delete, 1); lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_DELETE_NAME); @@ -1294,6 +1335,8 @@ static int DoAttach(lua_State *state) lua_setfield(state, -2, "_displace"); lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_ASSIGN_NAME); lua_setfield(state, -2, "assign"); + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_IS_INSTANCE_NAME); + lua_setfield(state, -2, "is_instance"); lua_pushlightuserdata(state, NULL); lua_setfield(state, -2, "NULL"); diff --git a/library/include/LuaWrapper.h b/library/include/LuaWrapper.h index aa6e511b1..9cd7b5018 100644 --- a/library/include/LuaWrapper.h +++ b/library/include/LuaWrapper.h @@ -68,6 +68,7 @@ namespace DFHack { namespace LuaWrapper { #define DFHACK_DISPLACE_NAME "DFHack::Displace" #define DFHACK_NEW_NAME "DFHack::New" #define DFHACK_ASSIGN_NAME "DFHack::Assign" +#define DFHACK_IS_INSTANCE_NAME "DFHack::IsInstance" #define DFHACK_DELETE_NAME "DFHack::Delete" #define DFHACK_EMPTY_TABLE_NAME "DFHack::EmptyTable"