From 6a409d6ebe2153be222baaf399036a42693b6674 Mon Sep 17 00:00:00 2001 From: "Robert G. Jakabosky" Date: Sat, 13 Oct 2012 04:36:50 -0700 Subject: [PATCH] Add support for importing objects from other LuaNativeObject modules. --- native_objects.lua | 22 +++++ native_objects/gen_lua.lua | 145 ++++++++++++++++++++++++++++++++- native_objects/gen_lua_ffi.lua | 51 +++++++++++- native_objects/lang_lua.lua | 40 +++++++++ native_objects/stages.lua | 4 + 5 files changed, 258 insertions(+), 4 deletions(-) diff --git a/native_objects.lua b/native_objects.lua index 259d7ef..30378ac 100644 --- a/native_objects.lua +++ b/native_objects.lua @@ -249,6 +249,28 @@ function object(name) end end +function import_object(mod) + return function (name) + return function (rec) + rec = rec or {} + local userdata_type = rec.userdata_type or 'generic' + rec.userdata_type = userdata_type + if userdata_type == 'generic' or userdata_type == 'embed' or userdata_type == 'simple ptr' then + ctype(name .. " *", rec,"import_object") + rec.is_ptr = true + rec.name = name + -- map the c_type to this record + new_c_type(name, rec) + else + ctype(name, rec, "import_object") + end + -- external module name. + rec.mod_name = mod + return rec +end +end +end + function interface(name) return function (rec) local rec = ctype(name, rec,"interface") diff --git a/native_objects/gen_lua.lua b/native_objects/gen_lua.lua index 869334d..5cedaa6 100644 --- a/native_objects/gen_lua.lua +++ b/native_objects/gen_lua.lua @@ -144,6 +144,7 @@ typedef void (*dyn_caster_t)(void **obj, obj_type **type); #define OBJ_TYPE_FLAG_WEAK_REF (1<<0) #define OBJ_TYPE_SIMPLE (1<<1) +#define OBJ_TYPE_IMPORT (1<<2) struct obj_type { dyn_caster_t dcaster; /**< caster to support casting to sub-objects. */ int32_t id; /**< type's id. */ @@ -249,6 +250,48 @@ local objHelperFunc = [[ #define OBJ_DATA_HIDDEN_METATABLE 1 #endif +static FUNC_UNUSED int obj_import_external_type(lua_State *L, obj_type *type) { + /* find the external type's metatable using it's name. */ + lua_pushstring(L, type->name); + lua_rawget(L, LUA_REGISTRYINDEX); /* external type's metatable. */ + if(!lua_isnil(L, -1)) { + /* found it. Now we will map our 'type' pointer to the metatable. */ + /* REGISTERY[lightuserdata] = REGISTERY[type->name] */ + lua_pushlightuserdata(L, type); /* use our 'type' pointer as lookup key. */ + lua_pushvalue(L, -2); /* dup. type's metatable. */ + lua_rawset(L, LUA_REGISTRYINDEX); /* save external type's metatable. */ + /* NOTE: top of Lua stack still has the type's metatable. */ + return 1; + } else { + lua_pop(L, 1); /* pop nil. */ + } + return 0; +} + +static FUNC_UNUSED int obj_import_external_ffi_type(lua_State *L, obj_type *type) { + /* find the external type's metatable using it's name. */ + lua_pushstring(L, type->name); + lua_rawget(L, LUA_REGISTRYINDEX); /* external type's metatable. */ + if(!lua_isnil(L, -1)) { + /* found it. Now we will map our 'type' pointer to the C check function. */ + /* _priv_table[lightuserdata] = REGISTERY[type->name].c_check */ + lua_getfield(L, -1, "c_check"); + lua_remove(L, -2); /* remove metatable. */ + if(lua_isfunction(L, -1)) { + lua_pushlightuserdata(L, type); /* use our 'type' pointer as lookup key. */ + lua_pushvalue(L, -2); /* dup. check function */ + lua_rawset(L, -4); /* save check function to module's private table. */ + /* NOTE: top of Lua stack still has the type's C check function. */ + return 1; + } else { + lua_pop(L, 1); /* pop non function value. */ + } + } else { + lua_pop(L, 1); /* pop nil. */ + } + return 0; +} + static FUNC_UNUSED obj_udata *obj_udata_toobj(lua_State *L, int _index) { obj_udata *ud; size_t len; @@ -272,10 +315,23 @@ static FUNC_UNUSED int obj_udata_is_compatible(lua_State *L, obj_udata *ud, void obj_type *ud_type; lua_pushlightuserdata(L, type); lua_rawget(L, LUA_REGISTRYINDEX); /* type's metatable. */ +recheck_metatable: if(lua_rawequal(L, -1, -2)) { *obj = ud->obj; /* same type no casting needed. */ return 1; + } else if(lua_isnil(L, -1)) { + lua_pop(L, 1); /* pop nil. */ + if((type->flags & OBJ_TYPE_IMPORT) == 0) { + /* can't resolve internal type. */ + luaL_error(L, "Unknown object type(id=%d, name=%s)", type->id, type->name); + } + /* try to import external type. */ + if(obj_import_external_type(L, type)) { + /* imported type, re-try metatable check. */ + goto recheck_metatable; + } + /* External type not yet available, so the object can't be compatible. */ } else { /* Different types see if we can cast to the required type. */ lua_rawgeti(L, -2, type->id); @@ -339,6 +395,7 @@ static FUNC_UNUSED obj_udata *obj_udata_luacheck_internal(lua_State *L, int _ind /* check for function. */ if(!lua_isnil(L, -1)) { +got_check_func: /* pass cdata value to type checking function. */ lua_pushvalue(L, _index); lua_call(L, 1, 1); @@ -350,7 +407,15 @@ static FUNC_UNUSED obj_udata *obj_udata_luacheck_internal(lua_State *L, int _ind } lua_pop(L, 2); } else { - lua_pop(L, 1); + lua_pop(L, 1); /* pop nil. */ + if(type->flags & OBJ_TYPE_IMPORT) { + /* try to import external ffi type. */ + if(obj_import_external_ffi_type(L, type)) { + /* imported type. */ + goto got_check_func; + } + /* External type not yet available, so the object can't be compatible. */ + } } } if(not_delete) { @@ -552,9 +617,23 @@ static FUNC_UNUSED void * obj_simple_udata_luacheck(lua_State *L, int _index, ob if(lua_getmetatable(L, _index)) { lua_pushlightuserdata(L, type); lua_rawget(L, LUA_REGISTRYINDEX); /* type's metatable. */ +recheck_metatable: if(lua_rawequal(L, -1, -2)) { lua_pop(L, 2); /* pop both metatables. */ return ud; + } else if(lua_isnil(L, -1)) { + lua_pop(L, 1); /* pop nil. */ + if((type->flags & OBJ_TYPE_IMPORT) == 0) { + /* can't resolve internal type. */ + luaL_error(L, "Unknown object type(id=%d, name=%s)", type->id, type->name); + } + /* try to import external type. */ + if(obj_import_external_type(L, type)) { + /* imported type, re-try metatable check. */ + goto recheck_metatable; + } + /* External type not yet available, so the object can't be compatible. */ + return 0; } } } else if(!lua_isnoneornil(L, _index)) { @@ -568,6 +647,7 @@ static FUNC_UNUSED void * obj_simple_udata_luacheck(lua_State *L, int _index, ob /* check for function. */ if(!lua_isnil(L, -1)) { +got_check_func: /* pass cdata value to type checking function. */ lua_pushvalue(L, _index); lua_call(L, 1, 1); @@ -576,6 +656,15 @@ static FUNC_UNUSED void * obj_simple_udata_luacheck(lua_State *L, int _index, ob lua_pop(L, 2); return (void *)lua_topointer(L, _index); } + } else { + if(type->flags & OBJ_TYPE_IMPORT) { + /* try to import external ffi type. */ + if(obj_import_external_ffi_type(L, type)) { + /* imported type. */ + goto got_check_func; + } + /* External type not yet available, so the object can't be compatible. */ + } } } luaL_typerror(L, _index, type->name); /* is not a userdata value. */ @@ -1094,6 +1183,40 @@ static void obj_type_register_implements(lua_State *L, const reg_impl *impls) { ]] +-- template for imported type *_check macros. +local obj_import_type_check = { +['simple'] = [[ +#define obj_type_${object_name}_check(L, _index) \ + *((${object_name} *)obj_simple_udata_luacheck(L, _index, &(obj_type_${object_name}))) +#define obj_type_${object_name}_optional(L, _index) \ + *((${object_name} *)obj_simple_udata_luaoptional(L, _index, &(obj_type_${object_name}))) +]], +['simple ptr'] = [[ +#define obj_type_${object_name}_check(L, _index) \ + *((${object_name} **)obj_simple_udata_luacheck(L, _index, &(obj_type_${object_name}))) +#define obj_type_${object_name}_optional(L, _index) \ + *((${object_name} **)obj_simple_udata_luaoptional(L, _index, &(obj_type_${object_name}))) +]], +['embed'] = [[ +#define obj_type_${object_name}_check(L, _index) \ + (${object_name} *)obj_simple_udata_luacheck(L, _index, &(obj_type_${object_name})) +#define obj_type_${object_name}_optional(L, _index) \ + (${object_name} *)obj_simple_udata_luaoptional(L, _index, &(obj_type_${object_name})) +]], +['object id'] = [[ +#define obj_type_${object_name}_check(L, _index) \ + (${object_name})(uintptr_t)obj_udata_luacheck(L, _index, &(obj_type_${object_name})) +#define obj_type_${object_name}_optional(L, _index) \ + (${object_name})(uintptr_t)obj_udata_luaoptional(L, _index, &(obj_type_${object_name})) +]], +['generic'] = [[ +#define obj_type_${object_name}_check(L, _index) \ + obj_udata_luacheck(L, _index, &(obj_type_${object_name})) +#define obj_type_${object_name}_optional(L, _index) \ + obj_udata_luaoptional(L, _index, &(obj_type_${object_name})) +]], +} + -- templates for typed *_check/*_delete/*_push macros. local obj_type_check_delete_push = { ['simple'] = [[ @@ -1755,6 +1878,26 @@ object_end = function(self, rec, parent) parent:copy_parts(rec, parts) end, +import_object_end = function(self, rec, parent) + rec:add_var('object_name', rec.name) + -- create check/delete/push macros + local ud_type = rec.userdata_type + rec:write_part("obj_check_delete_push", { + obj_import_type_check[ud_type], + '\n' + }) + + -- build obj_type info. + rec:write_part('obj_types', { + '#define obj_type_id_${object_name} ', rec._obj_id, '\n', + '#define ', rec._obj_type_name, ' (obj_types[obj_type_id_${object_name}])\n', + ' { NULL, ', rec._obj_id , ', OBJ_TYPE_IMPORT, "${object_name}" },\n' + }) + -- copy parts to parent + local parts = { "obj_types", "obj_check_delete_push" } + rec:vars_parts(parts) + parent:copy_parts(rec, parts) +end, callback_state = function(self, rec, parent) rec:add_var('wrap_type', rec.wrap_type) rec:add_var('base_type', rec.base_type) diff --git a/native_objects/gen_lua_ffi.lua b/native_objects/gen_lua_ffi.lua index 5e19975..8c82ed4 100644 --- a/native_objects/gen_lua_ffi.lua +++ b/native_objects/gen_lua_ffi.lua @@ -375,15 +375,19 @@ do end -- type checking function for C API. - _priv[obj_type] = function(obj) + local function c_check(obj) if ffi.istype(obj_ctype, obj) then return obj._wrapped_val end return nil end + _priv[obj_type] = c_check -- push function for C API. reg_table[obj_type] = function(ptr) return obj_type_${object_name}_push(ffi.cast("${object_name} *", ptr)[0]) end + -- export check functions for use in other modules. + obj_mt.c_check = c_check + obj_mt.ffi_check = obj_type_${object_name}_check end ]], @@ -427,15 +431,19 @@ do end -- type checking function for C API. - _priv[obj_type] = function(ptr) + local function c_check(ptr) if ffi.istype(obj_ctype, ptr) then return ptr end return nil end + _priv[obj_type] = c_check -- push function for C API. reg_table[obj_type] = function(ptr) return obj_type_${object_name}_push(ffi.cast(obj_ctype, ptr)[0]) end + -- export check functions for use in other modules. + obj_mt.c_check = c_check + obj_mt.ffi_check = obj_type_${object_name}_check end ]], @@ -471,10 +479,11 @@ do end -- type checking function for C API. - _priv[obj_type] = function(obj) + local function c_check(obj) if ffi.istype(obj_type, obj) then return obj end return nil end + _priv[obj_type] = c_check -- push function for C API. reg_table[obj_type] = function(ptr) local obj = obj_ctype() @@ -482,6 +491,9 @@ do return obj end + -- export check functions for use in other modules. + obj_mt.c_check = c_check + obj_mt.ffi_check = obj_type_${object_name}_check end ]], @@ -546,6 +558,9 @@ do return obj_type_${object_name}_push(ffi.cast('uintptr_t',ptr), flags) end + -- export check functions for use in other modules. + obj_mt.c_check = obj_type_${object_name}_check + obj_mt.ffi_check = obj_type_${object_name}_check end ]], @@ -599,6 +614,9 @@ ${dyn_caster} return obj_type_${object_name}_push(ffi.cast(obj_ctype,ptr), flags) end + -- export check functions for use in other modules. + obj_mt.c_check = obj_type_${object_name}_check + obj_mt.ffi_check = obj_type_${object_name}_check end ]], @@ -655,6 +673,9 @@ ${dyn_caster} return obj_type_${object_name}_push(ffi.cast(obj_ctype,ptr), flags) end + -- export check functions for use in other modules. + obj_mt.c_check = obj_type_${object_name}_check + obj_mt.ffi_check = obj_type_${object_name}_check end ]], @@ -1203,6 +1224,30 @@ object_end = function(self, rec, parent) end end, +import_object_end = function(self, rec, parent) + rec:add_var('object_name', rec.name) + -- create FFI check functions + rec:write_part("ffi_obj_type", [[ +local function obj_type_${object_name}_check(obj) + -- try to import FFI check function from external module. + local mt = reg_table['${object_name}'] + if mt then + local ffi_check = mt.ffi_check + if ffi_check then + obj_type_${object_name}_check = ffi_check + return ffi_check(obj) + end + end + return error("Expected '${object_name}'", 2) +end + +]]) + + -- copy parts to parent + local ffi_parts = { "ffi_obj_type" } + rec:vars_parts(ffi_parts) + parent:copy_parts(rec, ffi_parts) +end, callback_state = function(self, rec, parent) rec:add_var('wrap_type', rec.wrap_type) rec:add_var('base_type', rec.base_type) diff --git a/native_objects/lang_lua.lua b/native_objects/lang_lua.lua index 764d62c..0ad88c4 100644 --- a/native_objects/lang_lua.lua +++ b/native_objects/lang_lua.lua @@ -196,6 +196,46 @@ reg_stage_parser("lang_type_process", { end rec._ffi_push_error = rec._ffi_push end, + import_object = function(self, rec, parent) + rec.lang_type = 'userdata' + local type_name = 'obj_type_' .. rec.name + rec._obj_type_name = type_name + + -- create _check/_delete/_push functions + rec._define = function(self, var) + return var.c_type .. ' ${'..var.name..'};\n' + end + rec._check = function(self, var) + return '${'..var.name..'} = '..type_name..'_check(L,${'..var.name..'::idx});\n' + end + rec._opt = function(self, var) + return '${'..var.name..'} = '..type_name..'_optional(L,${'..var.name..'::idx});\n' + end + rec._delete = function(self, var, flags) + error("Can't delete an imported type.") + end + rec._to = rec._check + rec._push = function(self, var, flags) + error("Can't push an imported type.") + end + rec._ffi_define = function(self, var) + return '' + end + rec._ffi_check = function(self, var) + local name = '${' .. var.name .. '}' + return name .. ' = '..type_name..'_check('..name..')\n' + end + rec._ffi_opt = function(self, var) + local name = '${' .. var.name .. '}' + return name .. ' = '..name..' and '..type_name..'_check('..name..') or nil\n' + end + rec._ffi_delete = function(self, var, has_flags) + error("Can't delete an imported type.") + end + rec._ffi_push = function(self, var, flags, unwrap) + error("Can't push an imported type.") + end + end, object = function(self, rec, parent) rec.lang_type = 'userdata' local type_name = 'obj_type_' .. rec.name diff --git a/native_objects/stages.lua b/native_objects/stages.lua index beba0c3..699034d 100644 --- a/native_objects/stages.lua +++ b/native_objects/stages.lua @@ -182,6 +182,10 @@ object = function(self, rec, parent) rec._obj_id = self._obj_cnt self._obj_cnt = self._obj_cnt + 1 end, +import_object = function(self, rec, parent) + rec._obj_id = self._obj_cnt + self._obj_cnt = self._obj_cnt + 1 +end, extends = function(self, rec, parent) -- find base-object record. local base = resolve_c_type(rec.name)