summaryrefslogtreecommitdiff
path: root/library/LuaWrapper.cpp
diff options
context:
space:
mode:
authorAlexander Gavrilov2012-03-20 13:56:29 +0400
committerAlexander Gavrilov2012-03-20 13:56:29 +0400
commit6c661bcaa907952f3ffdc4ff546ec88cfcc36d72 (patch)
treeeeb3b20f6b32cb95c036f4a33701b30020afc0bd /library/LuaWrapper.cpp
parentdbbd9acfad8d5994f321840e3d695fe8a67ac315 (diff)
downloaddfhack-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.cpp339
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)