|
Lua调用C#的类,实际上是访谒类型对应的table。该table会在初度调用的时候创建。以下面的代码为例来了解整个调用流程:- CS.UnityEngine.GameObject(”Test”)// 实例化go对象
复制代码 如何触发创建
CS表在LuaEnv构造函数内执行init_xlua的Lua代码时候创建的,初度调用CS.UnityEngine时候,会执行CS元表的__index方式。- local metatable = {}
- local import_type = xlua.import_type
- function metatable:__index(key)
- local fqn = rawget(self, '.fqn')
- fqn = ((fqn and fqn .. '.') or '') .. key
- local obj = import_type(fqn)
- if obj == nil then
- -- It might be an assembly, so we load it too.
- obj = { ['.fqn'] = fqn }
- setmetatable(obj, metatable)
- elseif obj == true then
- return rawget(self, key)
- end
- -- Cache this lookup
- rawset(self, key, obj)
- return obj
- end
复制代码 这时的fqn是”UnityEngine”,调用import_type(fnq)导入类型,也就是创建类型对应的table。
obj为true暗示类型创建了table,否则可能是定名空间(或者不存在),所以也创建一个类似CS的表,用于后续的查询。最后缓存obj,下次访谒就能直接获取。
import_type函数是通过LuaEnv实例化代码中的translator.OpenLib(rawL)绑定的,实际调用的是XLua.StaticLuaCallbacks.ImportType方式。- [MonoPInvokeCallback(typeof(LuaCSFunction))]
- public static int ImportType(RealStatePtr L)
- {
- try
- {
- ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L);
- string className = LuaAPI.lua_tostring(L, 1);
- Type type = translator.FindType(className);
- if (type != null)
- {
- if (translator.GetTypeId(L, type) >= 0)
- {
- LuaAPI.lua_pushboolean(L, true);
- }
- else
- {
- return LuaAPI.luaL_error(L, ”can not load type ” + type);
- }
- }
- else
- {
- LuaAPI.lua_pushnil(L);
- }
- return 1;
- }
- catch (System.Exception e)
- {
- return LuaAPI.luaL_error(L, ”c# exception in xlua.import_type:” + e);
- }
- }
复制代码 translator.FindType(className)确定是否是一个类,不是则返回nil。translator.GetTypeId(L,type)查找获创建table。
接下来继续看getTypeId方式。先判断是否数组或者委托,是就直接返回对应的meta,否则就要走创建流程。- internal int getTypeId(RealStatePtr L, Type type, out bool is_first, LOGLEVEL log_level = LOGLEVEL.WARN)
- {
- int type_id;
- is_first = false;
- if (!typeIdMap.TryGetValue(type, out type_id)) // no reference
- {
- if (type.IsArray)
- {
- if (common_array_meta == -1) throw new Exception(”Fatal Exception! Array Metatable not inited!”);
- return common_array_meta;
- }
- if (typeof(MulticastDelegate).IsAssignableFrom(type))
- {
- if (common_delegate_meta == -1) throw new Exception(”Fatal Exception! Delegate Metatable not inited!”);
- TryDelayWrapLoader(L, type);
- return common_delegate_meta;
- }
- is_first = true;
- Type alias_type = null;
- aliasCfg.TryGetValue(type, out alias_type);
- LuaAPI.luaL_getmetatable(L, alias_type == null ? type.FullName : alias_type.FullName);
- if (LuaAPI.lua_isnil(L, -1)) //no meta yet, try to use reflection meta
- {
- LuaAPI.lua_pop(L, 1);
- if (TryDelayWrapLoader(L, alias_type == null ? type : alias_type))
- {
- LuaAPI.luaL_getmetatable(L, alias_type == null ? type.FullName : alias_type.FullName);
- }
- else
- {
- throw new Exception(”Fatal: can not load metatable of type:” + type);
- }
- }
- //循环依赖,自身依赖本身的class,比如有个自身类型的静态readonly对象。
- if (typeIdMap.TryGetValue(type, out type_id))
- {
- LuaAPI.lua_pop(L, 1);
- }
- else
- {
- ......
- typeIdMap.Add(type, type_id);
- }
- }
- return type_id;
- }
复制代码 可以看出,创建的逻辑在TryDelayWrapLoader方式。
方式先判断是通过生成代码还是反射创建:有loader函数则调用loader函数创建table,否则调用Utils.ReflectionWrap通过反射生成。下面只分析生成代码流程,反射部门直接看Utils.ReflectionWrap方式即可。- public bool TryDelayWrapLoader(RealStatePtr L, Type type)
- {
- if (loaded_types.ContainsKey(type)) return true;
- loaded_types.Add(type, true);
- LuaAPI.luaL_newmetatable(L, type.FullName); //先建一个metatable,因为加载过程可能会需要用到
- LuaAPI.lua_pop(L, 1);
- Action<RealStatePtr> loader;
- int top = LuaAPI.lua_gettop(L);
- if (delayWrap.TryGetValue(type, out loader))
- {
- delayWrap.Remove(type);
- loader(L);
- }
- else
- {
- ......
- Utils.ReflectionWrap(L, type, privateAccessibleFlags.Contains(type));
- ......
- }
- ......
- return true;
- }
复制代码 关联table创建方式
delayWrap字典实际上存的是类型的代码,就例子来说,对应的是下面的方式:- public class UnityEngineGameObjectWrap
- {
- public static void __Register(RealStatePtr L)
- {
- ......
- }
- }
复制代码 这些方式都是通过XLua_Gen_Initer_Register__类注册函数放到Initer,Initer在LueEnv构造函数内调用,最终放到上面的delayWrap字典里。- public class XLua_Gen_Initer_Register__
- {
- static void wrapInit0(LuaEnv luaenv, ObjectTranslator translator)
- {
- ......
- translator.DelayWrapLoader(typeof(UnityEngine.Debug), UnityEngineDebugWrap.__Register);
- ......
- }
- static void Init(LuaEnv luaenv, ObjectTranslator translator)
- {
- wrapInit0(luaenv, translator);
- ......
- }
- static XLua_Gen_Initer_Register__()
- {
- XLua.LuaEnv.AddIniter(Init);
- }
- }
复制代码 如何创建类的table
在分析如何生成table之前,先把类的组成分成2部门:
- 实例(对象)部门:function、field、property
- 静态(类)部门:function、field、property
对于Lua来说,模拟function、field是没问题的,property实际上也可以拆分为getter和setter两个方式。在实例化之前,无法知道对象field的值,所以不需要考虑。那么其他部门其实都能通过Lua的元表模拟。
table的生成就是按照上面的分类进行划分,把一个类分为Object和Class两个表。此中Object只是一个元表,因为实际的Field要等实例化的时候才设置,而类的静态部门就是一个完整的表。看看UnityEngine.Debug的__register方式。- // 源码有删减,每个类型注册只保留一条
- public class UnityEngineGameObjectWrap
- {
- public static void __Register(RealStatePtr L)
- {
- ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L);
- System.Type type = typeof(UnityEngine.GameObject);
- // 动态部门
- Utils.BeginObjectRegister(type, L, translator, 0, 13, 9, 3);
- Utils.RegisterFunc(L, Utils.METHOD_IDX, ”GetComponent”, _m_GetComponent);
- ......
- Utils.RegisterFunc(L, Utils.GETTER_IDX, ”transform”, _g_get_transform);
- ......
- Utils.RegisterFunc(L, Utils.SETTER_IDX, ”layer”, _s_set_layer);
- ......
- Utils.EndObjectRegister(type, L, translator, null, null,
- null, null, null);
- // 静态部门
- Utils.BeginClassRegister(type, L, __CreateInstance, 6, 0, 0);
- Utils.RegisterFunc(L, Utils.CLS_IDX, ”Find”, _m_Find_xlua_st_);
- ......
- Utils.EndClassRegister(type, L, translator);
- }
- }
复制代码 无论动态和静态部门,实际上都是3个函数调用,下面分隔静态和动态两部门进行分析。
创建静态部门table
BeginClassRegister
- 创建cls_table、meta_table、getter_table、setter_table 4个表,
- cls_table设置到CS.UnityEngine.Debug和CS[type]下。
- meta_table设置为cls_table的元表
- public static void BeginClassRegister(Type type, RealStatePtr L, LuaCSFunction creator, int class_field_count,
- int static_getter_count, int static_setter_count)
- {
- ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L);
- LuaAPI.lua_createtable(L, 0, class_field_count);
- // cls_table[UnderlyingSystemType]=type,用于typeof方式访谒获取类型
- LuaAPI.xlua_pushasciistring(L, ”UnderlyingSystemType”);
- translator.PushAny(L, type);
- LuaAPI.lua_rawset(L, -3);
- int cls_table = LuaAPI.lua_gettop(L);
- // CS.UnityEngine.Debug = cls_table
- // CS[type] = cls_table
- SetCSTable(L, type, cls_table);
- // 创建meta_table = {__call = creator}
- LuaAPI.lua_createtable(L, 0, 3);
- int meta_table = LuaAPI.lua_gettop(L);
- if (creator != null)
- {
- LuaAPI.xlua_pushasciistring(L, ”__call”);
- LuaAPI.lua_pushstdcallcfunction(L, creator);
- LuaAPI.lua_rawset(L, -3);
- }
- // getter_table = {}
- if (static_getter_count == 0)
- {
- LuaAPI.lua_pushnil(L);
- }
- else
- {
- LuaAPI.lua_createtable(L, 0, static_getter_count);
- }
- // setter_table = {}
- if (static_setter_count == 0)
- {
- LuaAPI.lua_pushnil(L);
- }
- else
- {
- LuaAPI.lua_createtable(L, 0, static_setter_count);
- }
- // setmetetable(cls_table, mete_table)
- LuaAPI.lua_pushvalue(L, meta_table);
- LuaAPI.lua_setmetatable(L, cls_table);
- }
复制代码 重点看SetCSTable方式,此方式把cls_table设置到CS.UnityEngine.GameObject和CS[type]下。这就是为什么能够在前面local obj = import_type(fqn),成果为true的时,能直接rawget(self, key)得到table的原因。- public static void SetCSTable(RealStatePtr L, Type type, int cls_table)
- {
- int oldTop = LuaAPI.lua_gettop(L);
- cls_table = abs_idx(oldTop, cls_table);
- LuaAPI.xlua_pushasciistring(L, LuaEnv.CSHARP_NAMESPACE);
- LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);
- // path = {`UnityEngine`, `Debug`}
- List<string> path = getPathOfType(type);
- // 查抄除最后一个table外的其他table是否存在,不存在则创建
- for (int i = 0; i < path.Count - 1; ++i)
- {
- LuaAPI.xlua_pushasciistring(L, path[i]);
- if (0 != LuaAPI.xlua_pgettable(L, -2))
- {
- var err = LuaAPI.lua_tostring(L, -1);
- LuaAPI.lua_settop(L, oldTop);
- throw new Exception(”SetCSTable for [” + type + ”] error: ” + err);
- }
- if (LuaAPI.lua_isnil(L, -1))
- {
- LuaAPI.lua_pop(L, 1);
- LuaAPI.lua_createtable(L, 0, 0);
- LuaAPI.xlua_pushasciistring(L, path[i]);
- LuaAPI.lua_pushvalue(L, -2);
- LuaAPI.lua_rawset(L, -4);
- }
- else if (!LuaAPI.lua_istable(L, -1))
- {
- LuaAPI.lua_settop(L, oldTop);
- throw new Exception(”SetCSTable for [” + type + ”] error: ancestors is not a table!”);
- }
- LuaAPI.lua_remove(L, -2);
- }
- // 设置CS.UnityEngine.Debug = cls_table
- LuaAPI.xlua_pushasciistring(L, path[path.Count - 1]);
- LuaAPI.lua_pushvalue(L, cls_table);
- LuaAPI.lua_rawset(L, -3);
- LuaAPI.lua_pop(L, 1);
- // 设置CS[type] = cls_table
- LuaAPI.xlua_pushasciistring(L, LuaEnv.CSHARP_NAMESPACE);
- LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);
- ObjectTranslatorPool.Instance.Find(L).PushAny(L, type);
- LuaAPI.lua_pushvalue(L, cls_table);
- LuaAPI.lua_rawset(L, -3);
- LuaAPI.lua_pop(L, 1);
- }
复制代码 BeginClassRegister调用完毕后,数据栈上是这样的:
RegisterFunc
把方式注册到对应的表上,第二个参数就是对应表在仓库上的位置。- public static void RegisterFunc(RealStatePtr L, int idx, string name, LuaCSFunction func)
- {
- idx = abs_idx(LuaAPI.lua_gettop(L), idx);
- LuaAPI.xlua_pushasciistring(L, name);
- LuaAPI.lua_pushstdcallcfunction(L, func);
- LuaAPI.lua_rawset(L, idx);
- }
复制代码 对于静态部门,idx的定义如下,也就是BeginClassRegister调用后仓库上对应表的索引:- public const int CLS_IDX = -4;
- public const int CLS_META_IDX = -3;
- public const int CLS_GETTER_IDX = -2;
- public const int CLS_SETTER_IDX = -1;
复制代码 EndClassRegister
- 将getter_table、cls_table、baseType、cls_indexers、nil(占位)作为upvalue创建cls_indexer闭包函数
- 将setter_table、baseType、cls_newindexers、nil(占位)作为upvalue创建cls_newindexer闭包函数
- meta_table[”__index”] = cls_indexer,meta_table[”__newindex”] = cls_newindexer
- 保留到调集:LuaIndexs[type] = cls_indexer
- 保留到调集:LuaNewIndexs[type] = cls_newindexer
- 数据栈弹出前面的4个table。
- public static void EndClassRegister(Type type, RealStatePtr L, ObjectTranslator translator)
- {
- int top = LuaAPI.lua_gettop(L);
- int cls_idx = abs_idx(top, CLS_IDX);
- int cls_getter_idx = abs_idx(top, CLS_GETTER_IDX);
- int cls_setter_idx = abs_idx(top, CLS_SETTER_IDX);
- int cls_meta_idx = abs_idx(top, CLS_META_IDX);
- //begin cls index
- LuaAPI.xlua_pushasciistring(L, ”__index”);
- LuaAPI.lua_pushvalue(L, cls_getter_idx);// p1
- LuaAPI.lua_pushvalue(L, cls_idx);// p2
- translator.Push(L, type.BaseType());// p3
- LuaAPI.xlua_pushasciistring(L, LuaClassIndexsFieldName);
- LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);// p4
- // 将p1~4和函数内部新增的p5共5个参数作为upvalue,创建闭包函数cls_indexer
- LuaAPI.gen_cls_indexer(L);
- // 设置cls_indexers[type] = cls_indexer
- LuaAPI.xlua_pushasciistring(L, LuaClassIndexsFieldName);
- LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);//store in lua indexs function tables
- translator.Push(L, type);
- LuaAPI.lua_pushvalue(L, -3);
- LuaAPI.lua_rawset(L, -3);
- LuaAPI.lua_pop(L, 1);
- // 设置meta_table[__index] = cls_indexer
- LuaAPI.lua_rawset(L, cls_meta_idx);
- //end cls index
- // newindex逻辑类似,不再此外注释
- //begin cls newindex
- LuaAPI.xlua_pushasciistring(L, ”__newindex”);
- LuaAPI.lua_pushvalue(L, cls_setter_idx);
- translator.Push(L, type.BaseType());
- LuaAPI.xlua_pushasciistring(L, LuaClassNewIndexsFieldName);
- LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);
- LuaAPI.gen_cls_newindexer(L);
- LuaAPI.xlua_pushasciistring(L, LuaClassNewIndexsFieldName);
- LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);//store in lua newindexs function tables
- translator.Push(L, type);
- LuaAPI.lua_pushvalue(L, -3);
- LuaAPI.lua_rawset(L, -3);
- LuaAPI.lua_pop(L, 1);
- LuaAPI.lua_rawset(L, cls_meta_idx);
- //end cls newindex
- LuaAPI.lua_pop(L, 4);
- }
复制代码 这个函数的重点其实是创建出来的cls_indexer和cls_newindexer闭包函数,后续访谒类的属性或者方式,城市调用到这个函数。这里以gen_cls_indexer为例看具体实现。- LUA_API int gen_obj_indexer(lua_State *L) {
- lua_pushnil(L);// 第5个upvalue,用来存放基类的cls_indexer闭包函数
- lua_pushcclosure(L, obj_indexer, 5);
- return 0;
- }
- //upvalue --- [1]:getters, [2]:feilds, [3]:base, [4]:indexfuncs, [5]:baseindex
- //param --- [1]: obj, [2]: key
- LUA_API int cls_indexer(lua_State *L) {
- // 从getter_table中查找
- if (!lua_isnil(L, lua_upvalueindex(1))) {
- lua_pushvalue(L, 2);
- lua_gettable(L, lua_upvalueindex(1));
- if (!lua_isnil(L, -1)) {//has getter
- lua_call(L, 0, 1);
- return 1;
- }
- lua_pop(L, 1);
- }
- // 从cls_table中查找
- if (!lua_isnil(L, lua_upvalueindex(2))) {
- lua_pushvalue(L, 2);
- lua_rawget(L, lua_upvalueindex(2));
- if (!lua_isnil(L, -1)) {//has feild
- return 1;
- }
- lua_pop(L, 1);
- }
- // 初度访谒,查找按照upvalue[3]的基类类型和upvalue[4]的cls_indexers调集,
- // 查找cls_indexer闭包函数,设置到第5个upvalue的位置。然后清空upvalue[3]。
- if (!lua_isnil(L, lua_upvalueindex(3))) {
- lua_pushvalue(L, lua_upvalueindex(3));
- while(!lua_isnil(L, -1)) {
- lua_pushvalue(L, -1);
- lua_gettable(L, lua_upvalueindex(4));
- if (!lua_isnil(L, -1)) // found
- {
- lua_replace(L, lua_upvalueindex(5)); //baseindex = indexfuncs[base]
- lua_pop(L, 1);
- break;
- }
- lua_pop(L, 1);
- lua_getfield(L, -1, ”BaseType”);
- lua_remove(L, -2);
- }
- lua_pushnil(L);
- lua_replace(L, lua_upvalueindex(3));//base = nil
- }
- // 执行基类的cls_indexer闭包函数继续查找
- if (!lua_isnil(L, lua_upvalueindex(5))) {
- lua_settop(L, 2);
- lua_pushvalue(L, lua_upvalueindex(5));
- lua_insert(L, 1);
- lua_call(L, 2, 1);
- return 1;
- } else {
- lua_pushnil(L);
- return 1;
- }
- }
复制代码 gen_cls_newindexer的实现思路是一致的,具体不再赘述。
Lua模拟实现
使用Lua代码模拟类的静态部弟子成table的行为。- -- BeginClassRegister
- local cls_table = {}
- local meta_table = {__call = __CreateInstance}
- local getter_table = {}
- local setter_table = {}
- CS.UnityEngine.Debug = cls_table
- CS[type] = cls_table
- setmetatable(cls_table, meta_table)
- -- RegisterFunc 只模拟一条
- cls_table[”Find”] = _m_Find_xlua_st_
- -- EndClassRegister
- local clsIndexer = gen_cls_indexer()
- local clsNewIndexer = gen_cls_newindexer()
- meta_table[”__index”] = clsIndexer
- meta_table[”__newindex”] = clsNewIndexer
- register[”LuaClassIndexs”][type] = clsIndexer
- register[”LuaClassNewIndexs”][type] = clsNewIndexer
复制代码 创建动态部门table
动态部门的创建逻辑与静态部门长短常接近的,斗劲大的差异是,动态部门table是一个元表,在实例化的时候设置为对象的元表。下面主要通过注释说明,最后结合Lua模拟代码去理解。
BeginObjectRegister
创建meta、method、getter、setter四个元表放在数据栈上:- //meta: -4, method:-3, getter: -2, setter: -1
- public static void BeginObjectRegister(Type type, RealStatePtr L, ObjectTranslator translator, int meta_count, int method_count, int getter_count,
- int setter_count, int type_id = -1)
- {
- if (type == null)
- {
- if (type_id == -1) throw new Exception(”Fatal: must provide a type of type_id”);
- LuaAPI.xlua_rawgeti(L, LuaIndexes.LUA_REGISTRYINDEX, type_id);
- }
- else
- {
- // 按照类型名获取元表meta_table,没有就创建一个放栈上
- LuaAPI.luaL_getmetatable(L, type.FullName);
- if (LuaAPI.lua_isnil(L, -1))
- {
- LuaAPI.lua_pop(L, 1);
- LuaAPI.luaL_newmetatable(L, type.FullName);
- }
- }
- // 设置标识,meta_table[0] = 1
- LuaAPI.lua_pushlightuserdata(L, LuaAPI.xlua_tag());
- LuaAPI.lua_pushnumber(L, 1);
- LuaAPI.lua_rawset(L, -3);
- if ((type == null || !translator.HasCustomOp(type)) && type != typeof(decimal))
- {
- LuaAPI.xlua_pushasciistring(L, ”__gc”);
- LuaAPI.lua_pushstdcallcfunction(L, translator.metaFunctions.GcMeta);
- LuaAPI.lua_rawset(L, -3);
- }
- // meta_table[__tostring] = translator.metaFunctions.ToStringMeta
- LuaAPI.xlua_pushasciistring(L, ”__tostring”);
- LuaAPI.lua_pushstdcallcfunction(L, translator.metaFunctions.ToStringMeta);
- LuaAPI.lua_rawset(L, -3);
- // 创建method_table放到数据栈上,没有也放nil占位
- if (method_count == 0)
- {
- LuaAPI.lua_pushnil(L);
- }
- else
- {
- LuaAPI.lua_createtable(L, 0, method_count);
- }
- // 创建getter_table放到数据栈上,没有也放nil占位
- if (getter_count == 0)
- {
- LuaAPI.lua_pushnil(L);
- }
- else
- {
- LuaAPI.lua_createtable(L, 0, getter_count);
- }
- // 创建getter_table放到数据栈上,没有也放nil占位
- if (setter_count == 0)
- {
- LuaAPI.lua_pushnil(L);
- }
- else
- {
- LuaAPI.lua_createtable(L, 0, setter_count);
- }
- }
复制代码 BeginObjectRegister调用后,4个表在数据栈上的位置就是:
RegisterFunc
和静态部门独一的差异,idx的定义变为:- public const int OBJ_META_IDX = -4;
- public const int METHOD_IDX = -3;
- public const int GETTER_IDX = -2;
- public const int SETTER_IDX = -1;
复制代码 EndObjectRegister
创建obj_indexer和obj_newindexer闭包函数,设置meta_table的__index和__newindex。将包罗method_table、getter_table、setter_table等参数作为闭包函数的upvalue被使用。然后缓存两个闭包函数到注册表下对应的子表。最后弹出BeginObjectRegister创建的4个table。具体看源码:- public static void EndObjectRegister(Type type, RealStatePtr L, ObjectTranslator translator, LuaCSFunction csIndexer,
- LuaCSFunction csNewIndexer, Type base_type, LuaCSFunction arrayIndexer, LuaCSFunction arrayNewIndexer)
- {
- int top = LuaAPI.lua_gettop(L);
- int meta_idx = abs_idx(top, OBJ_META_IDX);
- int method_idx = abs_idx(top, METHOD_IDX);
- int getter_idx = abs_idx(top, GETTER_IDX);
- int setter_idx = abs_idx(top, SETTER_IDX);
- //begin index gen
- LuaAPI.xlua_pushasciistring(L, ”__index”);
- LuaAPI.lua_pushvalue(L, method_idx);// 参数1
- LuaAPI.lua_pushvalue(L, getter_idx);// 参数2
- // 参数3
- if (csIndexer == null)
- {
- LuaAPI.lua_pushnil(L);
- }
- else
- {
- LuaAPI.lua_pushstdcallcfunction(L, csIndexer);
- }
- // 参数4
- translator.Push(L, type == null ? base_type : type.BaseType());
- // 参数5
- LuaAPI.xlua_pushasciistring(L, LuaIndexsFieldName);
- LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);
- // 参数6
- if (arrayIndexer == null)
- {
- LuaAPI.lua_pushnil(L);
- }
- else
- {
- LuaAPI.lua_pushstdcallcfunction(L, arrayIndexer);
- }
- // 将上面6个参数 + 内部1个参数作为upvalue创建闭包函数
- LuaAPI.gen_obj_indexer(L);
- // register[LuaIndexsFieldName][type]=obj_indexer
- if (type != null)
- {
- LuaAPI.xlua_pushasciistring(L, LuaIndexsFieldName);
- LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);//store in lua indexs function tables
- translator.Push(L, type);
- LuaAPI.lua_pushvalue(L, -3);
- LuaAPI.lua_rawset(L, -3);
- LuaAPI.lua_pop(L, 1);
- }
- LuaAPI.lua_rawset(L, meta_idx);
- //end index gen
- //begin newindex gen
- LuaAPI.xlua_pushasciistring(L, ”__newindex”);
- LuaAPI.lua_pushvalue(L, setter_idx);
- if (csNewIndexer == null)
- {
- LuaAPI.lua_pushnil(L);
- }
- else
- {
- LuaAPI.lua_pushstdcallcfunction(L, csNewIndexer);
- }
- translator.Push(L, type == null ? base_type : type.BaseType());
- LuaAPI.xlua_pushasciistring(L, LuaNewIndexsFieldName);
- LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);
- if (arrayNewIndexer == null)
- {
- LuaAPI.lua_pushnil(L);
- }
- else
- {
- LuaAPI.lua_pushstdcallcfunction(L, arrayNewIndexer);
- }
- LuaAPI.gen_obj_newindexer(L);
- // register[LuaNewIndexsFieldName][type] = obj_newindexer
- if (type != null)
- {
- LuaAPI.xlua_pushasciistring(L, LuaNewIndexsFieldName);
- LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);//store in lua newindexs function tables
- translator.Push(L, type);
- LuaAPI.lua_pushvalue(L, -3);
- LuaAPI.lua_rawset(L, -3);
- LuaAPI.lua_pop(L, 1);
- }
- LuaAPI.lua_rawset(L, meta_idx);
- //end new index gen
- LuaAPI.lua_pop(L, 4);
- }
复制代码 看看gen_obj_indexer的实现:- //upvalue --- [1]: methods, [2]:getters, [3]:csindexer, [4]:base,
- // [5]:indexfuncs, [6]:arrayindexer, [7]:baseindex
- //param --- [1]: obj, [2]: key
- LUA_API int obj_indexer(lua_State *L) {
- // method有没有?
- if (!lua_isnil(L, lua_upvalueindex(1))) {
- lua_pushvalue(L, 2);
- lua_gettable(L, lua_upvalueindex(1));
- if (!lua_isnil(L, -1)) {//has method
- return 1;
- }
- lua_pop(L, 1);
- }
- // getter有没有?
- if (!lua_isnil(L, lua_upvalueindex(2))) {
- lua_pushvalue(L, 2);
- lua_gettable(L, lua_upvalueindex(2));
- if (!lua_isnil(L, -1)) {//has getter
- lua_pushvalue(L, 1);
- lua_call(L, 1, 1);
- return 1;
- }
- lua_pop(L, 1);
- }
- // arrayindexer方式存在,而且key是number,则调用arrayIndexer方式
- if (!lua_isnil(L, lua_upvalueindex(6)) && lua_type(L, 2) == LUA_TNUMBER) {
- lua_pushvalue(L, lua_upvalueindex(6));
- lua_pushvalue(L, 1);
- lua_pushvalue(L, 2);
- lua_call(L, 2, 1);
- return 1;
- }
- // csindexer存在,则调用方式看能否获取
- if (!lua_isnil(L, lua_upvalueindex(3))) {
- lua_pushvalue(L, lua_upvalueindex(3));
- lua_pushvalue(L, 1);
- lua_pushvalue(L, 2);
- lua_call(L, 2, 2);
- if (lua_toboolean(L, -2)) {
- return 1;
- }
- lua_pop(L, 2);
- }
- // 如果有父类,则找到父类的obj_indexer函数,设置到第7个upvalue的位置,然后把自身置空。
- // 可以理解为只是一个延迟设置基类obj_indexer的逻辑。
- if (!lua_isnil(L, lua_upvalueindex(4))) {
- lua_pushvalue(L, lua_upvalueindex(4));
- while(!lua_isnil(L, -1)) {
- lua_pushvalue(L, -1);
- lua_gettable(L, lua_upvalueindex(5));
- if (!lua_isnil(L, -1)) // found
- {
- lua_replace(L, lua_upvalueindex(7)); //baseindex = indexfuncs[base]
- lua_pop(L, 1);
- break;
- }
- // 没找到则继续找父类的父类
- lua_pop(L, 1);
- lua_getfield(L, -1, ”BaseType”);
- lua_remove(L, -2);
- }
- lua_pushnil(L);
- lua_replace(L, lua_upvalueindex(4));//base = nil
- }
- // 调用父类的obj_indexer方式查找
- if (!lua_isnil(L, lua_upvalueindex(7))) {
- lua_settop(L, 2);
- lua_pushvalue(L, lua_upvalueindex(7));
- lua_insert(L, 1);
- lua_call(L, 2, 1);
- return 1;
- } else {
- return 0;
- }
- }
复制代码 obj_newindexer和obj_indexer逻辑一样,这里不再赘述。
Lua模拟实现
- -- 1.BeginObjectRegister
- local meta_table = register[type.FullName] = {}
- mete_table[0] = 1
- mete_table[”__string”] = translator.metaFunctions.ToStringMeta
- local method_table = {}
- local getter_table = {}
- local setter_table = {}
- -- 2.RegisterFunc 只展示一条,其他表同样措置
- method_table[”GetComponent”] = _m_GetComponent
- -- 3.EndObjectRegister 只模拟index,newindex逻辑一样
- local obj_indexer = gen_obj_indexer()
- local obj_newindexer = gen_obj_newindexer()
- meta_table[”__index”] = obj_indexer
- mete_table[”__newindex”] = obj_newindexer
- register[”LuaIndexs”][type] = obj_indexer
- register[”LuaNewIndexs”][type] = obj_newindexer
复制代码 至此,一个C#类转换为table的流程就跑完了。
indexer和newindexer调集
看了上面的分析,能发现类创建table时生成的所有indexer和newindexer城市放入注册表register的4个字典里面缓存起来。后续可以通过类型type查找。- // cs对象__index和__newindex表
- string LuaIndexsFieldName = ”LuaIndexs”;
- string LuaNewIndexsFieldName = ”LuaNewIndexs”;
- // cs类__index和__newindex表
- string LuaClassIndexsFieldName = ”LuaClassIndexs”;
- string LuaClassNewIndexsFieldName = ”LuaClassNewIndexs”;
- // 示例:
- // register[LuaIndexsFieldName][type] = obj_indexer
- // type是cs的Type类型,obj_indexer是闭包函数
复制代码 实例化对象
实例化GameObject对象,在Lua中实际上是调用元表的__call方式。
上面创建静态部门,就是生成的__CreateInstance作为BeginClassRegister的creater参数设置到元表的__call方式。
__CreateInstance方式就是按照参数的个数调用C#层的实例化逻辑。- static int __CreateInstance(RealStatePtr L)
- {
- try
- {
- ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L);
- // 2个参数?
- if (LuaAPI.lua_gettop(L) == 2 && (LuaAPI.lua_isnil(L, 2) || LuaAPI.lua_type(L, 2) == LuaTypes.LUA_TSTRING))
- {
- string _name = LuaAPI.lua_tostring(L, 2);
- var gen_ret = new UnityEngine.GameObject(_name);
- translator.Push(L, gen_ret);
- return 1;
- }
- // 1个参数?
- if (LuaAPI.lua_gettop(L) == 1)
- {
- var gen_ret = new UnityEngine.GameObject();
- translator.Push(L, gen_ret);
- return 1;
- }
- // 大于2个参数?
- if (LuaAPI.lua_gettop(L) >= 2 && (LuaAPI.lua_isnil(L, 2) || LuaAPI.lua_type(L, 2) == LuaTypes.LUA_TSTRING) && (LuaTypes.LUA_TNONE == LuaAPI.lua_type(L, 3) || translator.Assignable<System.Type>(L, 3)))
- {
- string _name = LuaAPI.lua_tostring(L, 2);
- System.Type[] _components = translator.GetParams<System.Type>(L, 3);
- var gen_ret = new UnityEngine.GameObject(_name, _components);
- translator.Push(L, gen_ret);
- return 1;
- }
- }
- catch (System.Exception gen_e)
- {
- return LuaAPI.luaL_error(L, ”c# exception:” + gen_e);
- }
- return LuaAPI.luaL_error(L, ”invalid arguments to UnityEngine.GameObject constructor!”);
- }
复制代码 实例化C#对象后,需要创建userdata持有对象,并按照type找到前面创建的元表,把元表设置为userdata的元表。这部门逻辑在translator.Push内。- public void Push(RealStatePtr L, object o)
- {
- if (o == null)
- {
- LuaAPI.lua_pushnil(L);
- return;
- }
- int index = -1;
- Type type = o.GetType();
- #if !UNITY_WSA || UNITY_EDITOR
- bool is_enum = type.IsEnum;
- bool is_valuetype = type.IsValueType;
- #else
- bool is_enum = type.GetTypeInfo().IsEnum;
- bool is_valuetype = type.GetTypeInfo().IsValueType;
- #endif
- bool needcache = !is_valuetype || is_enum;// 引用类型和枚举需要缓存
- if (needcache && (is_enum ? enumMap.TryGetValue(o, out index) : reverseMap.TryGetValue(o, out index)))
- {
- if (LuaAPI.xlua_tryget_cachedud(L, index, cacheRef) == 1)
- {
- return;
- }
- //这里实在太经典了,weaktable先删除,然后GC会延迟调用,当index会循环操作的时候,不注释这行将会导致反复释放
- //collectObject(index);
- }
- bool is_first;
- int type_id = getTypeId(L, type, out is_first);
- //如果一个type的定义含本身静态readonly实例时,getTypeId会push一个实例,这时候应该用这个实例
- if (is_first && needcache && (is_enum ? enumMap.TryGetValue(o, out index) : reverseMap.TryGetValue(o, out index)))
- {
- if (LuaAPI.xlua_tryget_cachedud(L, index, cacheRef) == 1)
- {
- return;
- }
- }
- index = addObject(o, is_valuetype, is_enum);
- LuaAPI.xlua_pushcsobj(L, index, type_id, needcache, cacheRef);
- }
复制代码 前面判断了引用类型和枚举是否有缓存,有就从缓存获取即可,否则就通过type获取type_id。这个getTypeId的方式就是前面Lua访谒C#时,查抄或者创建table的方式。
addObject方式很简单,但是很重要,Lua用到的C#对象,要不被回收,就是在这放入了objects对象表,并返回索引index。- int addObject(object obj, bool is_valuetype, bool is_enum)
- {
- int index = objects.Add(obj);
- if (is_enum)
- {
- enumMap[obj] = index;
- }
- else if (!is_valuetype)
- {
- reverseMap[obj] = index;
- }
- return index;
- }
复制代码 最后就是调用c实现的的xlua_pushcsobj方式,最后一个调用参数cacheRef是什么值?
在ObjectTranslator构造函数内,创建了一个弱value的Lua表,通过luaL_ref方式保留在注册表register下,cacheRef就保留了这个索引。也就是说,通过这个值,能够在注册表register下找到这个表。- #define lua_newuserdata(L,s) lua_newuserdatauv(L,s,1)
- LUA_API void xlua_pushcsobj(lua_State *L, int key, int meta_ref, int need_cache, int cache_ref) {
- // 创建userdata,返回的是userdata memory块的指针,宏内设置memory块大小就是1个int
- int* pointer = (int*)lua_newuserdata(L, sizeof(int));
- *pointer = key;
- // 把userdata缓存到cache_ref表中
- if (need_cache) cacheud(L, key, cache_ref);
- // 获取类型对应的元表
- lua_rawgeti(L, LUA_REGISTRYINDEX, meta_ref);
- // 设置userdata的元表
- lua_setmetatable(L, -2);
- }
- static void cacheud(lua_State *L, int key, int cache_ref) {
- lua_rawgeti(L, LUA_REGISTRYINDEX, cache_ref);
- lua_pushvalue(L, -2);// push userdata
- lua_rawseti(L, -2, key);// cache_ref[key] = userdata
- lua_pop(L, 1);// pop cache_ref
- }
复制代码 至此,数据栈的栈顶就是GameObject实例的userdata。 |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|