diff options
| author | Alexander Gavrilov | 2012-03-20 13:56:29 +0400 |
|---|---|---|
| committer | Alexander Gavrilov | 2012-03-20 13:56:29 +0400 |
| commit | 6c661bcaa907952f3ffdc4ff546ec88cfcc36d72 (patch) | |
| tree | eeb3b20f6b32cb95c036f4a33701b30020afc0bd /library/LuaWrapper.cpp | |
| parent | dbbd9acfad8d5994f321840e3d695fe8a67ac315 (diff) | |
| download | dfhack-6c661bcaa907952f3ffdc4ff546ec88cfcc36d72.tar.gz dfhack-6c661bcaa907952f3ffdc4ff546ec88cfcc36d72.tar.bz2 dfhack-6c661bcaa907952f3ffdc4ff546ec88cfcc36d72.tar.xz | |
Add support for primitive type fields in lua wrapper.
Diffstat (limited to 'library/LuaWrapper.cpp')
| -rw-r--r-- | library/LuaWrapper.cpp | 339 |
1 files changed, 334 insertions, 5 deletions
diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp index 3ed4a35c..27935ae5 100644 --- a/library/LuaWrapper.cpp +++ b/library/LuaWrapper.cpp @@ -48,6 +48,155 @@ 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); } +#define UPVAL_TYPETABLE lua_upvalueindex(1) +#define UPVAL_METATABLE lua_upvalueindex(2) +#define UPVAL_FIELDTABLE lua_upvalueindex(3) + +namespace { + struct DFRefHeader { + void *ptr; + }; + + inline bool is_self_contained(DFRefHeader *ptr) { + void **pp = &ptr->ptr; + return **(void****)pp == (pp + 1); + } +} + +static void field_error(lua_State *state, int index, const char *err, const char *mode = "read") +{ + lua_getfield(state, UPVAL_METATABLE, "__metatable"); + const char *cname = lua_tostring(state, -1); + const char *fname = lua_tostring(state, index); + luaL_error(state, "Cannot %s field %s.%s: %s.", + mode, (cname ? cname : "?"), (fname ? fname : "?"), err); +} + +static int 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); + +int DFHack::PushDFObject(lua_State *state, type_identity *type, void *ptr) +{ + return 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); +} + +/* Primitive identity methods */ + +int constructed_identity::lua_read(lua_State *state, int fname_idx, void *ptr) +{ + return 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"); +} + +int enum_identity::lua_read(lua_State *state, int fname_idx, void *ptr) +{ + return 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); +} + +int df::number_identity_base::lua_read(lua_State *state, int fname_idx, void *ptr) +{ + lua_pushnumber(state, read(ptr)); + return 1; +} + +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)); +} + +int df::bool_identity::lua_read(lua_State *state, int fname_idx, void *ptr) +{ + lua_pushboolean(state, *(bool*)ptr); + return 1; +} + +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)) + *pb = lua_toboolean(state, val_index); + else if (lua_isnumber(state, val_index)) + *pb = lua_tonumber(state, val_index); + else + field_error(state, fname_idx, "boolean or number expected", "write"); +} + +int 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()); + return 1; +} + +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); +} + +static int do_read_pointer(lua_State *state, int, void *ptr, type_identity *target) +{ + void *val = *(void**)ptr; + + if (val == NULL) + { + lua_pushnil(state); + return 1; + } + else + return push_object_internal(state, target, val); +} + +int df::pointer_identity::lua_read(lua_State *state, int fname_idx, void *ptr) +{ + return do_read_pointer(state, fname_idx, ptr, target); +} + +static void do_write_pointer(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) +{ + do_write_pointer(state, fname_idx, ptr, target, val_index); +} + +/* */ + static int change_error(lua_State *state) { luaL_error(state, "Attempt to change a read-only table.\n"); @@ -111,6 +260,169 @@ static bool RegisterTypeInfo(lua_State *state, void *node) return added; } +static const struct_field_info *find_field(lua_State *state, int index, const char *mode = "read") +{ + lua_pushvalue(state, index); + lua_rawget(state, UPVAL_FIELDTABLE); + + if (!lua_islightuserdata(state, -1)) + field_error(state, index, "not found"); + + void *p = lua_touserdata(state, -1); + lua_pop(state, 1); + return (struct_field_info*)p; +} + +static int 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 1; + } + + case struct_field_info::PRIMITIVE: + case struct_field_info::SUBSTRUCT: + case struct_field_info::CONTAINER: + return field->type->lua_read(state, 2, ptr); + + case struct_field_info::POINTER: + return do_read_pointer(state, 2, ptr, field->type); + + case struct_field_info::STATIC_ARRAY: + case struct_field_info::STL_VECTOR_PTR: + + case struct_field_info::END: + return 0; + } +} + +static void write_field(lua_State *state, const struct_field_info *field, void *ptr) +{ + switch (field->mode) + { + case struct_field_info::STATIC_STRING: + { + size_t size; + const char *str = lua_tolstring(state, -1, &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, 3); + return; + + case struct_field_info::POINTER: + do_write_pointer(state, 2, ptr, field->type, 3); + + 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; + } +} + +static int meta_global_index(lua_State *state) +{ + const struct_field_info *field = find_field(state, 2); + void *ptr = *(void**)field->offset; + if (!ptr) + field_error(state, 2, "global address not known"); + return read_field(state, field, ptr); +} + +static int meta_global_newindex(lua_State *state) +{ + const struct_field_info *field = find_field(state, 2); + void *ptr = *(void**)field->offset; + if (!ptr) + field_error(state, 2, "global address not known", "write"); + write_field(state, field, ptr); + return 0; +} + +static void IndexFields(lua_State *state, const struct_field_info *fields) +{ + int base = lua_gettop(state); + lua_newtable(state); // read + lua_newtable(state); // write + + for (; fields->mode != struct_field_info::END; ++fields) + { + switch (fields->mode) + { + case struct_field_info::END: + break; + + case struct_field_info::PRIMITIVE: + case struct_field_info::STATIC_STRING: + case struct_field_info::POINTER: + lua_pushstring(state,fields->name); + lua_pushlightuserdata(state,(void*)fields); + lua_settable(state,base+2); + // fallthrough + + case struct_field_info::STATIC_ARRAY: + case struct_field_info::SUBSTRUCT: + case struct_field_info::CONTAINER: + case struct_field_info::STL_VECTOR_PTR: + lua_pushstring(state,fields->name); + lua_pushlightuserdata(state,(void*)fields); + lua_settable(state,base+1); + break; + } + } +} + +static void MakeFieldMetatable(lua_State *state, struct_identity *pstruct, + lua_CFunction reader, lua_CFunction writer) +{ + int base = lua_gettop(state); + + lua_newtable(state); // metatable + IndexFields(state, pstruct->getFields()); // read, write + + lua_pushstring(state, pstruct->getName()); + lua_setfield(state, base+1, "__metatable"); + + lua_pushlightuserdata(state, pstruct); + lua_setfield(state, base+1, "_identity"); + + lua_getfield(state, LUA_REGISTRYINDEX, "DFHack::DFTypes"); + lua_pushvalue(state, base+1); + lua_pushvalue(state, base+2); + lua_pushcclosure(state, reader, 3); + lua_setfield(state, base+1, "__index"); + + lua_getfield(state, LUA_REGISTRYINDEX, "DFHack::DFTypes"); + lua_pushvalue(state, base+1); + lua_pushvalue(state, base+3); + lua_pushcclosure(state, writer, 3); + lua_setfield(state, base+1, "__newindex"); + + // returns: [metatable readfields writefields]; +} + +static int push_object_internal(lua_State *state, type_identity *type, void *ptr, bool in_method) +{ + return 0; +} + +static void *get_object_internal(lua_State *state, type_identity *type, int val_index, bool in_method) +{ + return NULL; +} + static void RenderTypeChildren(lua_State *state, const std::vector<compound_identity*> &children); static void RenderType(lua_State *state, compound_identity *node) @@ -150,13 +462,11 @@ static void RenderType(lua_State *state, compound_identity *node) if (eid->getFirstItem() <= eid->getLastItem()) { - lua_pushstring(state, "_first_item"); lua_pushinteger(state, eid->getFirstItem()); - lua_settable(state, base); + lua_setfield(state, base, "_first_item"); - lua_pushstring(state, "_last_item"); lua_pushinteger(state, eid->getLastItem()); - lua_settable(state, base); + lua_setfield(state, base, "_last_item"); } SaveTypeInfo(state, node); @@ -171,7 +481,26 @@ static void RenderType(lua_State *state, compound_identity *node) assert(base == lua_gettop(state)); - freeze_table(state, false, node->getName()); + if (node->type() == IDTYPE_GLOBAL) + { + auto gid = (global_identity*)node; + + MakeFieldMetatable(state, gid, meta_global_index, meta_global_newindex); + lua_pop(state, 2); + + lua_dup(state); + lua_setmetatable(state, base); + lua_swap(state); // -> meta curtable + + freeze_table(state, true, "global"); + lua_getfield(state, base, "__newindex"); + lua_setfield(state, -2, "__newindex"); + lua_pop(state, 1); + + lua_remove(state, base); + } + else + freeze_table(state, false, node->getName()); } static void RenderTypeChildren(lua_State *state, const std::vector<compound_identity*> &children) |
