找回密码
 立即注册
查看: 151|回复: 0

xLua源码解析——Lua调用C#

[复制链接]
发表于 2024-7-15 18:49 | 显示全部楼层 |阅读模式
Lua调用C#的类,实际上是访谒类型对应的table。该table会在初度调用的时候创建。以下面的代码为例来了解整个调用流程:
  1. CS.UnityEngine.GameObject(”Test”)// 实例化go对象
复制代码
如何触发创建

CS表在LuaEnv构造函数内执行init_xlua的Lua代码时候创建的,初度调用CS.UnityEngine时候,会执行CS元表的__index方式。
  1. local metatable = {}
  2. local import_type = xlua.import_type
  3. function metatable:__index(key)
  4.     local fqn = rawget(self, '.fqn')
  5.     fqn = ((fqn and fqn .. '.') or '') .. key
  6.     local obj = import_type(fqn)
  7.     if obj == nil then
  8.         -- It might be an assembly, so we load it too.
  9.         obj = { ['.fqn'] = fqn }
  10.         setmetatable(obj, metatable)
  11.     elseif obj == true then
  12.         return rawget(self, key)
  13.     end
  14.     -- Cache this lookup
  15.     rawset(self, key, obj)
  16.     return obj
  17. end
复制代码
这时的fqn是”UnityEngine”,调用import_type(fnq)导入类型,也就是创建类型对应的table。
obj为true暗示类型创建了table,否则可能是定名空间(或者不存在),所以也创建一个类似CS的表,用于后续的查询。最后缓存obj,下次访谒就能直接获取。
import_type函数是通过LuaEnv实例化代码中的translator.OpenLib(rawL)绑定的,实际调用的是XLua.StaticLuaCallbacks.ImportType方式。
  1. [MonoPInvokeCallback(typeof(LuaCSFunction))]
  2. public static int ImportType(RealStatePtr L)
  3. {
  4.     try
  5.     {
  6.         ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L);
  7.         string className = LuaAPI.lua_tostring(L, 1);
  8.         Type type = translator.FindType(className);
  9.         if (type != null)
  10.         {
  11.             if (translator.GetTypeId(L, type) >= 0)
  12.             {
  13.                 LuaAPI.lua_pushboolean(L, true);
  14.             }
  15.             else
  16.             {
  17.                 return LuaAPI.luaL_error(L, ”can not load type ” + type);
  18.             }
  19.         }
  20.         else
  21.         {
  22.             LuaAPI.lua_pushnil(L);
  23.         }
  24.         return 1;
  25.     }
  26.     catch (System.Exception e)
  27.     {
  28.         return LuaAPI.luaL_error(L, ”c# exception in xlua.import_type:” + e);
  29.     }
  30. }
复制代码
translator.FindType(className)确定是否是一个类,不是则返回nil。translator.GetTypeId(L,type)查找获创建table。
接下来继续看getTypeId方式。先判断是否数组或者委托,是就直接返回对应的meta,否则就要走创建流程。
  1. internal int getTypeId(RealStatePtr L, Type type, out bool is_first, LOGLEVEL log_level = LOGLEVEL.WARN)
  2. {
  3.     int type_id;
  4.     is_first = false;
  5.     if (!typeIdMap.TryGetValue(type, out type_id)) // no reference
  6.     {
  7.         if (type.IsArray)
  8.         {
  9.             if (common_array_meta == -1) throw new Exception(”Fatal Exception! Array Metatable not inited!”);
  10.             return common_array_meta;
  11.         }
  12.         if (typeof(MulticastDelegate).IsAssignableFrom(type))
  13.         {
  14.             if (common_delegate_meta == -1) throw new Exception(”Fatal Exception! Delegate Metatable not inited!”);
  15.             TryDelayWrapLoader(L, type);
  16.             return common_delegate_meta;
  17.         }
  18.         is_first = true;
  19.         Type alias_type = null;
  20.         aliasCfg.TryGetValue(type, out alias_type);
  21.         LuaAPI.luaL_getmetatable(L, alias_type == null ? type.FullName : alias_type.FullName);
  22.         if (LuaAPI.lua_isnil(L, -1)) //no meta yet, try to use reflection meta
  23.         {
  24.             LuaAPI.lua_pop(L, 1);
  25.             if (TryDelayWrapLoader(L, alias_type == null ? type : alias_type))
  26.             {
  27.                 LuaAPI.luaL_getmetatable(L, alias_type == null ? type.FullName : alias_type.FullName);
  28.             }
  29.             else
  30.             {
  31.                 throw new Exception(”Fatal: can not load metatable of type:” + type);
  32.             }
  33.         }
  34.         //循环依赖,自身依赖本身的class,比如有个自身类型的静态readonly对象。
  35.         if (typeIdMap.TryGetValue(type, out type_id))
  36.         {
  37.             LuaAPI.lua_pop(L, 1);
  38.         }
  39.         else
  40.         {
  41.             ......
  42.             typeIdMap.Add(type, type_id);
  43.         }
  44.     }
  45.     return type_id;
  46. }
复制代码
可以看出,创建的逻辑在TryDelayWrapLoader方式。
方式先判断是通过生成代码还是反射创建:有loader函数则调用loader函数创建table,否则调用Utils.ReflectionWrap通过反射生成。下面只分析生成代码流程,反射部门直接看Utils.ReflectionWrap方式即可。
  1. public bool TryDelayWrapLoader(RealStatePtr L, Type type)
  2. {
  3.     if (loaded_types.ContainsKey(type)) return true;
  4.     loaded_types.Add(type, true);
  5.     LuaAPI.luaL_newmetatable(L, type.FullName); //先建一个metatable,因为加载过程可能会需要用到
  6.     LuaAPI.lua_pop(L, 1);
  7.     Action<RealStatePtr> loader;
  8.     int top = LuaAPI.lua_gettop(L);
  9.     if (delayWrap.TryGetValue(type, out loader))
  10.     {
  11.         delayWrap.Remove(type);
  12.         loader(L);
  13.     }
  14.     else
  15.     {
  16.         ......
  17.         Utils.ReflectionWrap(L, type, privateAccessibleFlags.Contains(type));
  18.         ......
  19.     }
  20.     ......
  21.     return true;
  22. }
复制代码
关联table创建方式

delayWrap字典实际上存的是类型的代码,就例子来说,对应的是下面的方式:
  1. public class UnityEngineGameObjectWrap
  2. {
  3.     public static void __Register(RealStatePtr L)
  4.     {
  5.         ......
  6.     }
  7. }
复制代码
这些方式都是通过XLua_Gen_Initer_Register__类注册函数放到Initer,Initer在LueEnv构造函数内调用,最终放到上面的delayWrap字典里。
  1. public class XLua_Gen_Initer_Register__
  2. {
  3.     static void wrapInit0(LuaEnv luaenv, ObjectTranslator translator)
  4.     {
  5.         ......
  6.         translator.DelayWrapLoader(typeof(UnityEngine.Debug), UnityEngineDebugWrap.__Register);
  7.         ......
  8.     }
  9.     static void Init(LuaEnv luaenv, ObjectTranslator translator)
  10.     {
  11.         wrapInit0(luaenv, translator);
  12.         ......
  13.     }
  14.     static XLua_Gen_Initer_Register__()
  15.     {
  16.         XLua.LuaEnv.AddIniter(Init);
  17.     }
  18. }
复制代码
如何创建类的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方式。
  1. // 源码有删减,每个类型注册只保留一条
  2. public class UnityEngineGameObjectWrap
  3. {
  4.     public static void __Register(RealStatePtr L)
  5.     {
  6.         ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L);
  7.         System.Type type = typeof(UnityEngine.GameObject);
  8.         // 动态部门
  9.         Utils.BeginObjectRegister(type, L, translator, 0, 13, 9, 3);
  10.         Utils.RegisterFunc(L, Utils.METHOD_IDX, ”GetComponent”, _m_GetComponent);
  11.         ......
  12.         Utils.RegisterFunc(L, Utils.GETTER_IDX, ”transform”, _g_get_transform);
  13.         ......
  14.         Utils.RegisterFunc(L, Utils.SETTER_IDX, ”layer”, _s_set_layer);
  15.         ......
  16.         Utils.EndObjectRegister(type, L, translator, null, null,
  17.             null, null, null);
  18.         // 静态部门
  19.         Utils.BeginClassRegister(type, L, __CreateInstance, 6, 0, 0);
  20.         Utils.RegisterFunc(L, Utils.CLS_IDX, ”Find”, _m_Find_xlua_st_);
  21.         ......
  22.         Utils.EndClassRegister(type, L, translator);
  23.     }
  24. }
复制代码
无论动态和静态部门,实际上都是3个函数调用,下面分隔静态和动态两部门进行分析。
创建静态部门table

BeginClassRegister


  • 创建cls_table、meta_table、getter_table、setter_table 4个表,
  • cls_table设置到CS.UnityEngine.Debug和CS[type]下。
  • meta_table设置为cls_table的元表
  1. public static void BeginClassRegister(Type type, RealStatePtr L, LuaCSFunction creator, int class_field_count,
  2.     int static_getter_count, int static_setter_count)
  3. {
  4.     ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L);
  5.     LuaAPI.lua_createtable(L, 0, class_field_count);
  6.     // cls_table[UnderlyingSystemType]=type,用于typeof方式访谒获取类型
  7.     LuaAPI.xlua_pushasciistring(L, ”UnderlyingSystemType”);
  8.     translator.PushAny(L, type);
  9.     LuaAPI.lua_rawset(L, -3);
  10.     int cls_table = LuaAPI.lua_gettop(L);
  11.     // CS.UnityEngine.Debug = cls_table
  12.     // CS[type] = cls_table
  13.     SetCSTable(L, type, cls_table);
  14.     // 创建meta_table = {__call = creator}
  15.     LuaAPI.lua_createtable(L, 0, 3);
  16.     int meta_table = LuaAPI.lua_gettop(L);
  17.     if (creator != null)
  18.     {
  19.         LuaAPI.xlua_pushasciistring(L, ”__call”);
  20.         LuaAPI.lua_pushstdcallcfunction(L, creator);
  21.         LuaAPI.lua_rawset(L, -3);
  22.     }
  23.     // getter_table = {}
  24.     if (static_getter_count == 0)
  25.     {
  26.         LuaAPI.lua_pushnil(L);
  27.     }
  28.     else
  29.     {
  30.         LuaAPI.lua_createtable(L, 0, static_getter_count);
  31.     }
  32.     // setter_table = {}
  33.     if (static_setter_count == 0)
  34.     {
  35.         LuaAPI.lua_pushnil(L);
  36.     }
  37.     else
  38.     {
  39.         LuaAPI.lua_createtable(L, 0, static_setter_count);
  40.     }
  41.     // setmetetable(cls_table, mete_table)
  42.     LuaAPI.lua_pushvalue(L, meta_table);
  43.     LuaAPI.lua_setmetatable(L, cls_table);
  44. }
复制代码
重点看SetCSTable方式,此方式把cls_table设置到CS.UnityEngine.GameObject和CS[type]下。这就是为什么能够在前面local obj = import_type(fqn),成果为true的时,能直接rawget(self, key)得到table的原因。
  1. public static void SetCSTable(RealStatePtr L, Type type, int cls_table)
  2. {
  3.     int oldTop = LuaAPI.lua_gettop(L);
  4.     cls_table = abs_idx(oldTop, cls_table);
  5.     LuaAPI.xlua_pushasciistring(L, LuaEnv.CSHARP_NAMESPACE);
  6.     LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);
  7.     // path = {`UnityEngine`, `Debug`}
  8.     List<string> path = getPathOfType(type);
  9.     // 查抄除最后一个table外的其他table是否存在,不存在则创建
  10.     for (int i = 0; i < path.Count - 1; ++i)
  11.     {
  12.         LuaAPI.xlua_pushasciistring(L, path[i]);
  13.         if (0 != LuaAPI.xlua_pgettable(L, -2))
  14.         {
  15.             var err = LuaAPI.lua_tostring(L, -1);
  16.             LuaAPI.lua_settop(L, oldTop);
  17.             throw new Exception(”SetCSTable for [” + type + ”] error: ” + err);
  18.         }
  19.         if (LuaAPI.lua_isnil(L, -1))
  20.         {
  21.             LuaAPI.lua_pop(L, 1);
  22.             LuaAPI.lua_createtable(L, 0, 0);
  23.             LuaAPI.xlua_pushasciistring(L, path[i]);
  24.             LuaAPI.lua_pushvalue(L, -2);
  25.             LuaAPI.lua_rawset(L, -4);
  26.         }
  27.         else if (!LuaAPI.lua_istable(L, -1))
  28.         {
  29.             LuaAPI.lua_settop(L, oldTop);
  30.             throw new Exception(”SetCSTable for [” + type + ”] error: ancestors is not a table!”);
  31.         }
  32.         LuaAPI.lua_remove(L, -2);
  33.     }
  34.     // 设置CS.UnityEngine.Debug = cls_table
  35.     LuaAPI.xlua_pushasciistring(L, path[path.Count - 1]);
  36.     LuaAPI.lua_pushvalue(L, cls_table);
  37.     LuaAPI.lua_rawset(L, -3);
  38.     LuaAPI.lua_pop(L, 1);
  39.     // 设置CS[type] = cls_table
  40.     LuaAPI.xlua_pushasciistring(L, LuaEnv.CSHARP_NAMESPACE);
  41.     LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);
  42.     ObjectTranslatorPool.Instance.Find(L).PushAny(L, type);
  43.     LuaAPI.lua_pushvalue(L, cls_table);
  44.     LuaAPI.lua_rawset(L, -3);
  45.     LuaAPI.lua_pop(L, 1);
  46. }
复制代码
BeginClassRegister调用完毕后,数据栈上是这样的:


RegisterFunc

把方式注册到对应的表上,第二个参数就是对应表在仓库上的位置。
  1. public static void RegisterFunc(RealStatePtr L, int idx, string name, LuaCSFunction func)
  2. {
  3.     idx = abs_idx(LuaAPI.lua_gettop(L), idx);
  4.     LuaAPI.xlua_pushasciistring(L, name);
  5.     LuaAPI.lua_pushstdcallcfunction(L, func);
  6.     LuaAPI.lua_rawset(L, idx);
  7. }
复制代码
对于静态部门,idx的定义如下,也就是BeginClassRegister调用后仓库上对应表的索引:
  1. public const int CLS_IDX = -4;
  2. public const int CLS_META_IDX = -3;
  3. public const int CLS_GETTER_IDX = -2;
  4. 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。
  1. public static void EndClassRegister(Type type, RealStatePtr L, ObjectTranslator translator)
  2. {
  3.     int top = LuaAPI.lua_gettop(L);
  4.     int cls_idx = abs_idx(top, CLS_IDX);
  5.     int cls_getter_idx = abs_idx(top, CLS_GETTER_IDX);
  6.     int cls_setter_idx = abs_idx(top, CLS_SETTER_IDX);
  7.     int cls_meta_idx = abs_idx(top, CLS_META_IDX);
  8.     //begin cls index
  9.     LuaAPI.xlua_pushasciistring(L, ”__index”);
  10.     LuaAPI.lua_pushvalue(L, cls_getter_idx);// p1
  11.     LuaAPI.lua_pushvalue(L, cls_idx);// p2
  12.     translator.Push(L, type.BaseType());// p3
  13.     LuaAPI.xlua_pushasciistring(L, LuaClassIndexsFieldName);
  14.     LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);// p4
  15.     // 将p1~4和函数内部新增的p5共5个参数作为upvalue,创建闭包函数cls_indexer
  16.     LuaAPI.gen_cls_indexer(L);
  17.     // 设置cls_indexers[type] = cls_indexer
  18.     LuaAPI.xlua_pushasciistring(L, LuaClassIndexsFieldName);
  19.     LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);//store in lua indexs function tables
  20.     translator.Push(L, type);
  21.     LuaAPI.lua_pushvalue(L, -3);
  22.     LuaAPI.lua_rawset(L, -3);
  23.     LuaAPI.lua_pop(L, 1);
  24.     // 设置meta_table[__index] = cls_indexer
  25.     LuaAPI.lua_rawset(L, cls_meta_idx);
  26.     //end cls index
  27.     // newindex逻辑类似,不再此外注释
  28.     //begin cls newindex
  29.     LuaAPI.xlua_pushasciistring(L, ”__newindex”);
  30.     LuaAPI.lua_pushvalue(L, cls_setter_idx);
  31.     translator.Push(L, type.BaseType());
  32.     LuaAPI.xlua_pushasciistring(L, LuaClassNewIndexsFieldName);
  33.     LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);
  34.     LuaAPI.gen_cls_newindexer(L);
  35.     LuaAPI.xlua_pushasciistring(L, LuaClassNewIndexsFieldName);
  36.     LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);//store in lua newindexs function tables
  37.     translator.Push(L, type);
  38.     LuaAPI.lua_pushvalue(L, -3);
  39.     LuaAPI.lua_rawset(L, -3);
  40.     LuaAPI.lua_pop(L, 1);
  41.     LuaAPI.lua_rawset(L, cls_meta_idx);
  42.     //end cls newindex
  43.     LuaAPI.lua_pop(L, 4);
  44. }
复制代码
这个函数的重点其实是创建出来的cls_indexer和cls_newindexer闭包函数,后续访谒类的属性或者方式,城市调用到这个函数。这里以gen_cls_indexer为例看具体实现。
  1. LUA_API int gen_obj_indexer(lua_State *L) {
  2.     lua_pushnil(L);// 第5个upvalue,用来存放基类的cls_indexer闭包函数
  3.     lua_pushcclosure(L, obj_indexer, 5);
  4.     return 0;
  5. }
  6. //upvalue --- [1]:getters, [2]:feilds, [3]:base, [4]:indexfuncs, [5]:baseindex
  7. //param   --- [1]: obj, [2]: key
  8. LUA_API int cls_indexer(lua_State *L) {
  9.     // 从getter_table中查找
  10.     if (!lua_isnil(L, lua_upvalueindex(1))) {
  11.         lua_pushvalue(L, 2);
  12.         lua_gettable(L, lua_upvalueindex(1));
  13.         if (!lua_isnil(L, -1)) {//has getter
  14.             lua_call(L, 0, 1);
  15.             return 1;
  16.         }
  17.         lua_pop(L, 1);
  18.     }
  19.     // 从cls_table中查找
  20.     if (!lua_isnil(L, lua_upvalueindex(2))) {
  21.         lua_pushvalue(L, 2);
  22.         lua_rawget(L, lua_upvalueindex(2));
  23.         if (!lua_isnil(L, -1)) {//has feild
  24.             return 1;
  25.         }
  26.         lua_pop(L, 1);
  27.     }
  28.     // 初度访谒,查找按照upvalue[3]的基类类型和upvalue[4]的cls_indexers调集,
  29.     // 查找cls_indexer闭包函数,设置到第5个upvalue的位置。然后清空upvalue[3]。
  30.     if (!lua_isnil(L, lua_upvalueindex(3))) {
  31.         lua_pushvalue(L, lua_upvalueindex(3));
  32.         while(!lua_isnil(L, -1)) {
  33.             lua_pushvalue(L, -1);
  34.             lua_gettable(L, lua_upvalueindex(4));
  35.             if (!lua_isnil(L, -1)) // found
  36.             {
  37.                 lua_replace(L, lua_upvalueindex(5)); //baseindex = indexfuncs[base]
  38.                 lua_pop(L, 1);
  39.                 break;
  40.             }
  41.             lua_pop(L, 1);
  42.             lua_getfield(L, -1, ”BaseType”);
  43.             lua_remove(L, -2);
  44.         }
  45.         lua_pushnil(L);
  46.         lua_replace(L, lua_upvalueindex(3));//base = nil
  47.     }
  48.     // 执行基类的cls_indexer闭包函数继续查找
  49.     if (!lua_isnil(L, lua_upvalueindex(5))) {
  50.         lua_settop(L, 2);
  51.         lua_pushvalue(L, lua_upvalueindex(5));
  52.         lua_insert(L, 1);
  53.         lua_call(L, 2, 1);
  54.         return 1;
  55.     } else {
  56.         lua_pushnil(L);
  57.         return 1;
  58.     }
  59. }
复制代码
gen_cls_newindexer的实现思路是一致的,具体不再赘述。
Lua模拟实现

使用Lua代码模拟类的静态部弟子成table的行为。
  1. -- BeginClassRegister
  2. local cls_table = {}
  3. local meta_table = {__call = __CreateInstance}
  4. local getter_table = {}
  5. local setter_table = {}
  6. CS.UnityEngine.Debug = cls_table
  7. CS[type] = cls_table
  8. setmetatable(cls_table, meta_table)
  9. -- RegisterFunc 只模拟一条
  10. cls_table[”Find”] = _m_Find_xlua_st_
  11. -- EndClassRegister
  12. local clsIndexer = gen_cls_indexer()
  13. local clsNewIndexer = gen_cls_newindexer()
  14. meta_table[”__index”] = clsIndexer
  15. meta_table[”__newindex”] = clsNewIndexer
  16. register[”LuaClassIndexs”][type] = clsIndexer
  17. register[”LuaClassNewIndexs”][type] = clsNewIndexer
复制代码
创建动态部门table

动态部门的创建逻辑与静态部门长短常接近的,斗劲大的差异是,动态部门table是一个元表,在实例化的时候设置为对象的元表。下面主要通过注释说明,最后结合Lua模拟代码去理解。
BeginObjectRegister

创建meta、method、getter、setter四个元表放在数据栈上:
  1. //meta: -4, method:-3, getter: -2, setter: -1
  2. public static void BeginObjectRegister(Type type, RealStatePtr L, ObjectTranslator translator, int meta_count, int method_count, int getter_count,
  3.     int setter_count, int type_id = -1)
  4. {
  5.     if (type == null)
  6.     {
  7.         if (type_id == -1) throw new Exception(”Fatal: must provide a type of type_id”);
  8.         LuaAPI.xlua_rawgeti(L, LuaIndexes.LUA_REGISTRYINDEX, type_id);
  9.     }
  10.     else
  11.     {
  12.         // 按照类型名获取元表meta_table,没有就创建一个放栈上
  13.         LuaAPI.luaL_getmetatable(L, type.FullName);
  14.         if (LuaAPI.lua_isnil(L, -1))
  15.         {
  16.             LuaAPI.lua_pop(L, 1);
  17.             LuaAPI.luaL_newmetatable(L, type.FullName);
  18.         }
  19.     }
  20.     // 设置标识,meta_table[0] = 1
  21.     LuaAPI.lua_pushlightuserdata(L, LuaAPI.xlua_tag());
  22.     LuaAPI.lua_pushnumber(L, 1);
  23.     LuaAPI.lua_rawset(L, -3);
  24.     if ((type == null || !translator.HasCustomOp(type)) && type != typeof(decimal))
  25.     {
  26.         LuaAPI.xlua_pushasciistring(L, ”__gc”);
  27.         LuaAPI.lua_pushstdcallcfunction(L, translator.metaFunctions.GcMeta);
  28.         LuaAPI.lua_rawset(L, -3);
  29.     }
  30.     // meta_table[__tostring] = translator.metaFunctions.ToStringMeta
  31.     LuaAPI.xlua_pushasciistring(L, ”__tostring”);
  32.     LuaAPI.lua_pushstdcallcfunction(L, translator.metaFunctions.ToStringMeta);
  33.     LuaAPI.lua_rawset(L, -3);
  34.     // 创建method_table放到数据栈上,没有也放nil占位
  35.     if (method_count == 0)
  36.     {
  37.         LuaAPI.lua_pushnil(L);
  38.     }
  39.     else
  40.     {
  41.         LuaAPI.lua_createtable(L, 0, method_count);
  42.     }
  43.     // 创建getter_table放到数据栈上,没有也放nil占位
  44.     if (getter_count == 0)
  45.     {
  46.         LuaAPI.lua_pushnil(L);
  47.     }
  48.     else
  49.     {
  50.         LuaAPI.lua_createtable(L, 0, getter_count);
  51.     }
  52.     // 创建getter_table放到数据栈上,没有也放nil占位
  53.     if (setter_count == 0)
  54.     {
  55.         LuaAPI.lua_pushnil(L);
  56.     }
  57.     else
  58.     {
  59.         LuaAPI.lua_createtable(L, 0, setter_count);
  60.     }
  61. }
复制代码
BeginObjectRegister调用后,4个表在数据栈上的位置就是:


RegisterFunc

和静态部门独一的差异,idx的定义变为:
  1. public const int OBJ_META_IDX = -4;
  2. public const int METHOD_IDX = -3;
  3. public const int GETTER_IDX = -2;
  4. 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。具体看源码:
  1. public static void EndObjectRegister(Type type, RealStatePtr L, ObjectTranslator translator, LuaCSFunction csIndexer,
  2.     LuaCSFunction csNewIndexer, Type base_type, LuaCSFunction arrayIndexer, LuaCSFunction arrayNewIndexer)
  3. {
  4.     int top = LuaAPI.lua_gettop(L);
  5.     int meta_idx = abs_idx(top, OBJ_META_IDX);
  6.     int method_idx = abs_idx(top, METHOD_IDX);
  7.     int getter_idx = abs_idx(top, GETTER_IDX);
  8.     int setter_idx = abs_idx(top, SETTER_IDX);
  9.     //begin index gen
  10.     LuaAPI.xlua_pushasciistring(L, ”__index”);
  11.     LuaAPI.lua_pushvalue(L, method_idx);// 参数1
  12.     LuaAPI.lua_pushvalue(L, getter_idx);// 参数2
  13.     // 参数3
  14.     if (csIndexer == null)
  15.     {
  16.         LuaAPI.lua_pushnil(L);
  17.     }
  18.     else
  19.     {
  20.         LuaAPI.lua_pushstdcallcfunction(L, csIndexer);
  21.     }
  22.     // 参数4
  23.     translator.Push(L, type == null ? base_type : type.BaseType());
  24.     // 参数5
  25.     LuaAPI.xlua_pushasciistring(L, LuaIndexsFieldName);
  26.     LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);
  27.     // 参数6
  28.     if (arrayIndexer == null)
  29.     {
  30.         LuaAPI.lua_pushnil(L);
  31.     }
  32.     else
  33.     {
  34.         LuaAPI.lua_pushstdcallcfunction(L, arrayIndexer);
  35.     }
  36.     // 将上面6个参数 + 内部1个参数作为upvalue创建闭包函数
  37.     LuaAPI.gen_obj_indexer(L);
  38.     // register[LuaIndexsFieldName][type]=obj_indexer
  39.     if (type != null)
  40.     {
  41.         LuaAPI.xlua_pushasciistring(L, LuaIndexsFieldName);
  42.         LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);//store in lua indexs function tables
  43.         translator.Push(L, type);
  44.         LuaAPI.lua_pushvalue(L, -3);
  45.         LuaAPI.lua_rawset(L, -3);
  46.         LuaAPI.lua_pop(L, 1);
  47.     }
  48.     LuaAPI.lua_rawset(L, meta_idx);
  49.     //end index gen
  50.     //begin newindex gen
  51.     LuaAPI.xlua_pushasciistring(L, ”__newindex”);
  52.     LuaAPI.lua_pushvalue(L, setter_idx);
  53.     if (csNewIndexer == null)
  54.     {
  55.         LuaAPI.lua_pushnil(L);
  56.     }
  57.     else
  58.     {
  59.         LuaAPI.lua_pushstdcallcfunction(L, csNewIndexer);
  60.     }
  61.     translator.Push(L, type == null ? base_type : type.BaseType());
  62.     LuaAPI.xlua_pushasciistring(L, LuaNewIndexsFieldName);
  63.     LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);
  64.     if (arrayNewIndexer == null)
  65.     {
  66.         LuaAPI.lua_pushnil(L);
  67.     }
  68.     else
  69.     {
  70.         LuaAPI.lua_pushstdcallcfunction(L, arrayNewIndexer);
  71.     }
  72.     LuaAPI.gen_obj_newindexer(L);
  73.     // register[LuaNewIndexsFieldName][type] = obj_newindexer
  74.     if (type != null)
  75.     {
  76.         LuaAPI.xlua_pushasciistring(L, LuaNewIndexsFieldName);
  77.         LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);//store in lua newindexs function tables
  78.         translator.Push(L, type);
  79.         LuaAPI.lua_pushvalue(L, -3);
  80.         LuaAPI.lua_rawset(L, -3);
  81.         LuaAPI.lua_pop(L, 1);
  82.     }
  83.     LuaAPI.lua_rawset(L, meta_idx);
  84.     //end new index gen
  85.     LuaAPI.lua_pop(L, 4);
  86. }
复制代码
看看gen_obj_indexer的实现:
  1. //upvalue --- [1]: methods, [2]:getters, [3]:csindexer, [4]:base,
  2. //            [5]:indexfuncs, [6]:arrayindexer, [7]:baseindex
  3. //param   --- [1]: obj, [2]: key
  4. LUA_API int obj_indexer(lua_State *L) {   
  5.     // method有没有?
  6.     if (!lua_isnil(L, lua_upvalueindex(1))) {
  7.         lua_pushvalue(L, 2);
  8.         lua_gettable(L, lua_upvalueindex(1));
  9.         if (!lua_isnil(L, -1)) {//has method
  10.             return 1;
  11.         }
  12.         lua_pop(L, 1);
  13.     }
  14.     // getter有没有?
  15.     if (!lua_isnil(L, lua_upvalueindex(2))) {
  16.         lua_pushvalue(L, 2);
  17.         lua_gettable(L, lua_upvalueindex(2));
  18.         if (!lua_isnil(L, -1)) {//has getter
  19.             lua_pushvalue(L, 1);
  20.             lua_call(L, 1, 1);
  21.             return 1;
  22.         }
  23.         lua_pop(L, 1);
  24.     }
  25.     // arrayindexer方式存在,而且key是number,则调用arrayIndexer方式
  26.     if (!lua_isnil(L, lua_upvalueindex(6)) && lua_type(L, 2) == LUA_TNUMBER) {
  27.         lua_pushvalue(L, lua_upvalueindex(6));
  28.         lua_pushvalue(L, 1);
  29.         lua_pushvalue(L, 2);
  30.         lua_call(L, 2, 1);
  31.         return 1;
  32.     }
  33.     // csindexer存在,则调用方式看能否获取
  34.     if (!lua_isnil(L, lua_upvalueindex(3))) {
  35.         lua_pushvalue(L, lua_upvalueindex(3));
  36.         lua_pushvalue(L, 1);
  37.         lua_pushvalue(L, 2);
  38.         lua_call(L, 2, 2);
  39.         if (lua_toboolean(L, -2)) {
  40.             return 1;
  41.         }
  42.         lua_pop(L, 2);
  43.     }
  44.     // 如果有父类,则找到父类的obj_indexer函数,设置到第7个upvalue的位置,然后把自身置空。
  45.     // 可以理解为只是一个延迟设置基类obj_indexer的逻辑。
  46.     if (!lua_isnil(L, lua_upvalueindex(4))) {
  47.         lua_pushvalue(L, lua_upvalueindex(4));
  48.         while(!lua_isnil(L, -1)) {
  49.             lua_pushvalue(L, -1);
  50.             lua_gettable(L, lua_upvalueindex(5));
  51.             if (!lua_isnil(L, -1)) // found
  52.             {
  53.                 lua_replace(L, lua_upvalueindex(7)); //baseindex = indexfuncs[base]
  54.                 lua_pop(L, 1);
  55.                 break;
  56.             }
  57.             // 没找到则继续找父类的父类
  58.             lua_pop(L, 1);
  59.             lua_getfield(L, -1, ”BaseType”);
  60.             lua_remove(L, -2);
  61.         }
  62.         lua_pushnil(L);
  63.         lua_replace(L, lua_upvalueindex(4));//base = nil
  64.     }
  65.     // 调用父类的obj_indexer方式查找
  66.     if (!lua_isnil(L, lua_upvalueindex(7))) {
  67.         lua_settop(L, 2);
  68.         lua_pushvalue(L, lua_upvalueindex(7));
  69.         lua_insert(L, 1);
  70.         lua_call(L, 2, 1);
  71.         return 1;
  72.     } else {
  73.         return 0;
  74.     }
  75. }
复制代码
obj_newindexer和obj_indexer逻辑一样,这里不再赘述。
Lua模拟实现
  1. -- 1.BeginObjectRegister
  2. local meta_table = register[type.FullName] = {}
  3. mete_table[0] = 1
  4. mete_table[”__string”] = translator.metaFunctions.ToStringMeta
  5. local method_table = {}
  6. local getter_table = {}
  7. local setter_table = {}
  8. -- 2.RegisterFunc 只展示一条,其他表同样措置
  9. method_table[”GetComponent”] = _m_GetComponent
  10. -- 3.EndObjectRegister 只模拟index,newindex逻辑一样
  11. local obj_indexer = gen_obj_indexer()
  12. local obj_newindexer = gen_obj_newindexer()
  13. meta_table[”__index”] = obj_indexer
  14. mete_table[”__newindex”] = obj_newindexer
  15. register[”LuaIndexs”][type] = obj_indexer
  16. register[”LuaNewIndexs”][type] = obj_newindexer
复制代码
至此,一个C#类转换为table的流程就跑完了。
indexer和newindexer调集

看了上面的分析,能发现类创建table时生成的所有indexer和newindexer城市放入注册表register的4个字典里面缓存起来。后续可以通过类型type查找。
  1. // cs对象__index和__newindex表
  2. string LuaIndexsFieldName = ”LuaIndexs”;
  3. string LuaNewIndexsFieldName = ”LuaNewIndexs”;
  4. // cs类__index和__newindex表
  5. string LuaClassIndexsFieldName = ”LuaClassIndexs”;
  6. string LuaClassNewIndexsFieldName = ”LuaClassNewIndexs”;
  7. // 示例:
  8. // register[LuaIndexsFieldName][type] = obj_indexer
  9. // type是cs的Type类型,obj_indexer是闭包函数
复制代码
实例化对象

实例化GameObject对象,在Lua中实际上是调用元表的__call方式。
上面创建静态部门,就是生成的__CreateInstance作为BeginClassRegister的creater参数设置到元表的__call方式。
__CreateInstance方式就是按照参数的个数调用C#层的实例化逻辑。
  1. static int __CreateInstance(RealStatePtr L)
  2. {
  3.     try
  4.     {
  5.         ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L);
  6.         // 2个参数?
  7.         if (LuaAPI.lua_gettop(L) == 2 && (LuaAPI.lua_isnil(L, 2) || LuaAPI.lua_type(L, 2) == LuaTypes.LUA_TSTRING))
  8.         {
  9.             string _name = LuaAPI.lua_tostring(L, 2);
  10.             var gen_ret = new UnityEngine.GameObject(_name);
  11.             translator.Push(L, gen_ret);
  12.             return 1;
  13.         }
  14.         // 1个参数?
  15.         if (LuaAPI.lua_gettop(L) == 1)
  16.         {
  17.             var gen_ret = new UnityEngine.GameObject();
  18.             translator.Push(L, gen_ret);
  19.             return 1;
  20.         }
  21.         // 大于2个参数?
  22.         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)))
  23.         {
  24.             string _name = LuaAPI.lua_tostring(L, 2);
  25.             System.Type[] _components = translator.GetParams<System.Type>(L, 3);
  26.             var gen_ret = new UnityEngine.GameObject(_name, _components);
  27.             translator.Push(L, gen_ret);
  28.             return 1;
  29.         }
  30.     }
  31.     catch (System.Exception gen_e)
  32.     {
  33.         return LuaAPI.luaL_error(L, ”c# exception:” + gen_e);
  34.     }
  35.     return LuaAPI.luaL_error(L, ”invalid arguments to UnityEngine.GameObject constructor!”);
  36. }
复制代码
实例化C#对象后,需要创建userdata持有对象,并按照type找到前面创建的元表,把元表设置为userdata的元表。这部门逻辑在translator.Push内。
  1. public void Push(RealStatePtr L, object o)
  2. {
  3.     if (o == null)
  4.     {
  5.         LuaAPI.lua_pushnil(L);
  6.         return;
  7.     }
  8.     int index = -1;
  9.     Type type = o.GetType();
  10. #if !UNITY_WSA || UNITY_EDITOR
  11.     bool is_enum = type.IsEnum;
  12.     bool is_valuetype = type.IsValueType;
  13. #else
  14.     bool is_enum = type.GetTypeInfo().IsEnum;
  15.     bool is_valuetype = type.GetTypeInfo().IsValueType;
  16. #endif
  17.     bool needcache = !is_valuetype || is_enum;// 引用类型和枚举需要缓存
  18.     if (needcache && (is_enum ? enumMap.TryGetValue(o, out index) : reverseMap.TryGetValue(o, out index)))
  19.     {
  20.         if (LuaAPI.xlua_tryget_cachedud(L, index, cacheRef) == 1)
  21.         {
  22.             return;
  23.         }
  24.         //这里实在太经典了,weaktable先删除,然后GC会延迟调用,当index会循环操作的时候,不注释这行将会导致反复释放
  25.         //collectObject(index);
  26.     }
  27.     bool is_first;
  28.     int type_id = getTypeId(L, type, out is_first);
  29.     //如果一个type的定义含本身静态readonly实例时,getTypeId会push一个实例,这时候应该用这个实例
  30.     if (is_first && needcache && (is_enum ? enumMap.TryGetValue(o, out index) : reverseMap.TryGetValue(o, out index)))
  31.     {
  32.         if (LuaAPI.xlua_tryget_cachedud(L, index, cacheRef) == 1)
  33.         {
  34.             return;
  35.         }
  36.     }
  37.     index = addObject(o, is_valuetype, is_enum);
  38.     LuaAPI.xlua_pushcsobj(L, index, type_id, needcache, cacheRef);
  39. }
复制代码
前面判断了引用类型和枚举是否有缓存,有就从缓存获取即可,否则就通过type获取type_id。这个getTypeId的方式就是前面Lua访谒C#时,查抄或者创建table的方式。
addObject方式很简单,但是很重要,Lua用到的C#对象,要不被回收,就是在这放入了objects对象表,并返回索引index。
  1. int addObject(object obj, bool is_valuetype, bool is_enum)
  2. {
  3.     int index = objects.Add(obj);
  4.     if (is_enum)
  5.     {
  6.         enumMap[obj] = index;
  7.     }
  8.     else if (!is_valuetype)
  9.     {
  10.         reverseMap[obj] = index;
  11.     }
  12.     return index;
  13. }
复制代码
最后就是调用c实现的的xlua_pushcsobj方式,最后一个调用参数cacheRef是什么值?
在ObjectTranslator构造函数内,创建了一个弱value的Lua表,通过luaL_ref方式保留在注册表register下,cacheRef就保留了这个索引。也就是说,通过这个值,能够在注册表register下找到这个表。
  1. #define lua_newuserdata(L,s)    lua_newuserdatauv(L,s,1)
  2. LUA_API void xlua_pushcsobj(lua_State *L, int key, int meta_ref, int need_cache, int cache_ref) {
  3.     // 创建userdata,返回的是userdata memory块的指针,宏内设置memory块大小就是1个int
  4.     int* pointer = (int*)lua_newuserdata(L, sizeof(int));
  5.     *pointer = key;
  6.     // 把userdata缓存到cache_ref表中
  7.     if (need_cache) cacheud(L, key, cache_ref);
  8.     // 获取类型对应的元表
  9.     lua_rawgeti(L, LUA_REGISTRYINDEX, meta_ref);
  10.     // 设置userdata的元表
  11.     lua_setmetatable(L, -2);
  12. }
  13. static void cacheud(lua_State *L, int key, int cache_ref) {
  14.     lua_rawgeti(L, LUA_REGISTRYINDEX, cache_ref);
  15.     lua_pushvalue(L, -2);// push userdata
  16.     lua_rawseti(L, -2, key);// cache_ref[key] = userdata
  17.     lua_pop(L, 1);// pop cache_ref
  18. }
复制代码
至此,数据栈的栈顶就是GameObject实例的userdata。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-9-8 09:51 , Processed in 0.225584 second(s), 27 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表