• Xlua热更原理浅析


    最近项目很忙好久没更新了。
    虽然一直在用Xlua,也对lua实现原理有稍微的了解。但仍然不是很理解C#到底是如何和lua进行交互的,比如在lua中写一段CS.UnityEngine.GameObject到底是如何调用到c#中的。上周也是浅浅的学习了下,今天来记录下解析过程。

    入口:LuaEnv.Init()

    string init_xlua = @" 
                    local metatable = {}
                    local rawget = rawget
                    local setmetatable = setmetatable
                    local import_type = xlua.import_type
                    local import_generic_type = xlua.import_generic_type
                    local load_assembly = xlua.load_assembly
    
                    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
                    
                    -- A non-type has been called; e.g. foo = System.Foo()
                    function metatable:__call(...)
                        local n = select('#', ...)
                        local fqn = rawget(self,'.fqn')
                        if n > 0 then
                            local gt = import_generic_type(fqn, ...)
                            if gt then
                                return rawget(CS, gt)
                            end
                        end
                        error('No such type: ' .. fqn, 2)
                    end
    
                    CS = CS or {}
                    setmetatable(CS, metatable)";
     DoString(init_xlua, "Init");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    初始化。执行lua代码,设置lua全局CS表。设置CS的元表、以及__index、__call元方法。

    __index元方法调用了xlua.import_type方法

    我们可以在ObjectTranslator.OpenLib 中看到该方法将C#中的ObjectTranslator.importTypeFunction函数放入了lua表中并且命名为import_type。故__index原方法实际上是调用了StaticLuaCallbacks.ImportType方法

    importTypeFunction = new LuaCSFunction(StaticLuaCallbacks.ImportType);

    LuaAPI.xlua_pushasciistring(L, “import_type”);
    LuaAPI.lua_pushstdcallcfunction(L,importTypeFunction);
    LuaAPI.lua_rawset(L, -3);


    StaticLuaCallbacks.ImportType
    ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L);
    //获取调用名,如在lua中调用CS.UnityEngine.GameObject则将会调用3次StaticLuaCallbacks.ImportType。
    //className分别为“CS”、”CS.UnityEngine“ 、“CS.UnityEngine.GameObject”
    //还会调用一次CS.UnityEngine.GameObject()构造函数,但触发的为call元方法,触发import_generic_type。下面会讲
    string className = LuaAPI.lua_tostring(L, 1);
    //通过反射将string转为Type
    Type type = translator.FindType(className);
    if (type != null)
    {
        if (translator.GetTypeId(L, type) >= 0)//返回lua表id。没有则调用translator.TryDelayWrapLoader设置表
        {
            LuaAPI.lua_pushboolean(L, true);
        }
        else
        {
            return LuaAPI.luaL_error(L, "can not load type " + type);
        }
    }
    else
    {
        LuaAPI.lua_pushnil(L);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    translator.getTypeId调用translator.TryDelayWrapLoader。


    TryDelayWrapLoader(RealStatePtr L, Type type)函数

    translator.TryDelayWrapLoader将对应的类的方法,属性等设置到lua表并且返回表id。该函数主要通过两种方式来将类映射到lua表中。主要有以下两种方案。

    1. 通过C#得Emit机制生成wrap文件,并且调用__Register进行注册

    Type wrap = ce.EmitTypeWrap(type);
    MethodInfo method = wrap.GetMethod(“__Register”, BindingFlags.Static | BindingFlags.Public);
    method.Invoke(null, new object[] { L });

    UnityEngine.GameObject类的wrap文件分析

    public static void __Register(RealStatePtr L)
     {
    	ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L);
    	System.Type type = typeof(UnityEngine.GameObject);
        //给GameObject这个类的非静态值创建元表,并且在元表中加入元方法_gc,__tostring. 然后再加入method, getter, setter这三个表
        //meta: -4, method:-3, getter: -2, setter: -1
    	Utils.BeginObjectRegister(type, L, translator, 0, 12, 8, 3);
        //在-3的表(method)表中加入"GetComponent"字段,对应c# 中的 GetComponents方法
    	Utils.RegisterFunc(L, Utils.METHOD_IDX, "GetComponent", _m_GetComponent);
        ...
        //在-2的表(getter)表中加入"transform"字段,对应c#中的_g_get_transform属性
    	Utils.RegisterFunc(L, Utils.GETTER_IDX, "transform", _g_get_transform);
        ...
         //在-1的表(setter)表中加入"layer"字段,对应c#中的_s_set_layer属性
    	Utils.RegisterFunc(L, Utils.SETTER_IDX, "layer", _s_set_layer);
        ...
         //这个方法主要用于创建元方法__index和__newindex,通过绑定c闭包的方式实现,然后将设置好的元方法放到注册表中。
    	Utils.EndObjectRegister(type, L, translator, null, null, null, null, null);
      	//这个方法主要是给GameObject的静态值(静态方法,静态对象)设置元表,与上述非静态值做的事情差不多
    	Utils.BeginClassRegister(type, L, __CreateInstance, 6, 0, 0);
        //在-4的表(类的静态表cls_idx)表中加入"transform"字段,对应c#中的_g_get_transform属性
    	Utils.RegisterFunc(L, Utils.CLS_IDX, "CreatePrimitive", _m_CreatePrimitive_xlua_st_);
       ...
        //给静态表设置元方法__index和__newindex,
    	Utils.EndClassRegister(type, L, translator);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    2. 通过反射

    Utils.ReflectionWrap(L, type, privateAccessibleFlags.Contains(type));

    Utils.ReflectionWrap函数

    			//创建几张表,分别保存属性,方法等
    			LuaAPI.lua_newtable(L);
    			int cls_meta = LuaAPI.lua_gettop(L);
    
    			LuaAPI.lua_newtable(L);
    			int obj_field = LuaAPI.lua_gettop(L);//成员方法
    			LuaAPI.lua_newtable(L);
    			int obj_getter = LuaAPI.lua_gettop(L);//成员字段,属性 get
    			LuaAPI.lua_newtable(L);
    			int obj_setter = LuaAPI.lua_gettop(L);//成员字段,属性 set
    			LuaAPI.lua_newtable(L);
    			int cls_field = LuaAPI.lua_gettop(L);//静态方法
    			LuaAPI.lua_newtable(L);
    			int cls_getter = LuaAPI.lua_gettop(L);//静态字段,属性 get
    			LuaAPI.lua_newtable(L);
    			int cls_setter = LuaAPI.lua_gettop(L);//静态字段,属性 set
    
    			LuaCSFunction item_getter;
    			LuaCSFunction item_setter;
    			//通过反射将类的方法、字段、属性等分别放入lua表中
    			makeReflectionWrap(L, type, cls_field, cls_getter, cls_setter, obj_field, obj_getter, obj_setter, obj_meta,
    				out item_getter, out item_setter, privateAccessible ? (BindingFlags.Public | BindingFlags.NonPublic) : BindingFlags.Public);
    			// init obj metatable
    			// 添加gc、tostring等元方法
    			LuaAPI.xlua_pushasciistring(L, "__gc");
    			LuaAPI.lua_pushstdcallcfunction(L, translator.metaFunctions.GcMeta);
    			LuaAPI.lua_rawset(L, obj_meta);
    
    			LuaAPI.xlua_pushasciistring(L, "__tostring");
    			LuaAPI.lua_pushstdcallcfunction(L, translator.metaFunctions.ToStringMeta);
    			LuaAPI.lua_rawset(L, obj_meta);
    			//省略很多代码.............................
    			//如果是枚举则添加强转。使得lua中能使用Enum.__CastFrom(int)将int转为enum
    			if (type != null && type.IsEnum())
    			{
    				LuaAPI.xlua_pushasciistring(L, "__CastFrom");
    				translator.PushFixCSFunction(L, genEnumCastFrom(type));
    				LuaAPI.lua_rawset(L, cls_field);
    			}
    
    			//省略很多代码...........
    			//设置构造函数
    			LuaCSFunction constructor = typeof(Delegate).IsAssignableFrom(type) ? translator.metaFunctions.DelegateCtor : translator.methodWrapsCache.GetConstructorWrap(type);
    			if (constructor == null)
    			{
    				constructor = (RealStatePtr LL) =>
    				{
    					return LuaAPI.luaL_error(LL, "No constructor for " + type);
    				};
    			}
    
    			LuaAPI.xlua_pushasciistring(L, "__call");
    			translator.PushFixCSFunction(L, constructor);
    			LuaAPI.lua_rawset(L, cls_meta);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    makeReflectionWrap方法

    			ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L);
    			BindingFlags flag = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | access;
    			FieldInfo[] fields = type.GetFields(flag);
    			EventInfo[] all_events = type.GetEvents(flag | BindingFlags.Public | BindingFlags.NonPublic);
    			//将反射获得的类的字段放入表中
    			for (int i = 0; i < fields.Length; ++i)
    			{
    				FieldInfo field = fields[i];
    				string fieldName = field.Name;
    				// skip hotfix inject field
    				if (field.IsStatic && (field.Name.StartsWith("__Hotfix") || field.Name.StartsWith("_c__Hotfix")) && typeof(Delegate).IsAssignableFrom(field.FieldType))
    				{
    					continue;
    				}
    				if (all_events.Any(e => e.Name == fieldName))
    				{
    					fieldName = "&" + fieldName;
    				}
    			
    				if (field.IsStatic && (field.IsInitOnly || field.IsLiteral))
    				{
    					LuaAPI.xlua_pushasciistring(L, fieldName);
    					translator.PushAny(L, field.GetValue(null));
    					LuaAPI.lua_rawset(L, cls_field);
    				}
    				else
    				{
    					LuaAPI.xlua_pushasciistring(L, fieldName);
    					translator.PushFixCSFunction(L, genFieldGetter(type, field));
    					LuaAPI.lua_rawset(L, field.IsStatic ? cls_getter : obj_getter);
    			
    					LuaAPI.xlua_pushasciistring(L, fieldName);
    					translator.PushFixCSFunction(L, genFieldSetter(type, field));
    					LuaAPI.lua_rawset(L, field.IsStatic ? cls_setter : obj_setter);
    				}
    			}
    			//类似还有将类的方法也放入表等.........................
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
  • 相关阅读:
    Harmony SDK API 版本 与 Harmony OS 版本对照表,及如何查看鸿蒙手机Harmony SDK Api 版本
    java计算机毕业设计医院远程诊断系统源程序+mysql+系统+lw文档+远程调试
    7-90 螺旋方阵
    哪种护眼灯孩子用着最好?盘点五款用眼舒适度高的护眼灯
    Java14新增特性
    微服务治理:Nacos, Zookeeper, consul, etcd, Eureka等 5 个常用微服务注册工具对比
    android Intent(意图)
    零代码即可将数据可视化应用到企业管理中
    [信息论]信道容量迭代算法程序设计(基于C++&Matlab实现)
    【技术追踪】SAM(Segment Anything Model)代码解析与结构绘制之Prompt Encoder
  • 原文地址:https://blog.csdn.net/u014516123/article/details/126580164