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

Xlua调用c#原理浅析

[复制链接]
发表于 2023-4-12 08:09 | 显示全部楼层 |阅读模式
最近项目很忙好久没更新了。
虽然一直在用Xlua,也对lua实现原理有稍微的了解。但仍然不是很理解C#到底是如何和lua进行交互的,比如在lua中写一段CS.UnityEngine.GameObject到底是如何调用到c#中的。上周也是浅浅的学习了下,今天来记录下解析过程。
入口:LuaEnv.Init()
  1. string init_xlua =@"
  2.                 local metatable = {}
  3.                 local rawget = rawget
  4.                 local setmetatable = setmetatable
  5.                 local import_type = xlua.import_type
  6.                 local import_generic_type = xlua.import_generic_type
  7.                 local load_assembly = xlua.load_assembly
  8.                 function metatable:__index(key)
  9.                     local fqn = rawget(self,'.fqn')
  10.                     fqn = ((fqn and fqn .. '.') or '') .. key
  11.                     local obj = import_type(fqn)
  12.                     if obj == nil then
  13.                         -- It might be an assembly, so we load it too.
  14.                         obj = { ['.fqn'] = fqn }
  15.                         setmetatable(obj, metatable)
  16.                     elseif obj == true then
  17.                         return rawget(self, key)
  18.                     end
  19.                     -- Cache this lookup
  20.                     rawset(self, key, obj)
  21.                     return obj
  22.                 end
  23.                
  24.                 -- A non-type has been called; e.g. foo = System.Foo()
  25.                 function metatable:__call(...)
  26.                     local n = select('#', ...)
  27.                     local fqn = rawget(self,'.fqn')
  28.                     if n > 0 then
  29.                         local gt = import_generic_type(fqn, ...)
  30.                         if gt then
  31.                             return rawget(CS, gt)
  32.                         end
  33.                     end
  34.                     error('No such type: ' .. fqn, 2)
  35.                 end
  36.                 CS = CS or {}
  37.                 setmetatable(CS, metatable)";DoString(init_xlua,"Init");
复制代码
初始化。执行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
  1. 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转为TypeType type = translator.FindType(className);if(type !=null){if(translator.GetTypeId(L, type)>=0)//返回lua表id。没有则调用translator.TryDelayWrapLoader设置表{
  2.         LuaAPI.lua_pushboolean(L,true);}else{return LuaAPI.luaL_error(L,"can not load type "+ type);}}else{
  3.     LuaAPI.lua_pushnil(L);}
复制代码
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文件分析
  1. publicstaticvoid__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
  2.         Utils.BeginObjectRegister(type, L, translator,0,12,8,3);//在-3的表(method)表中加入"GetComponent"字段,对应c# 中的 GetComponents方法
  3.         Utils.RegisterFunc(L, Utils.METHOD_IDX,"GetComponent", _m_GetComponent);...//在-2的表(getter)表中加入"transform"字段,对应c#中的_g_get_transform属性
  4.         Utils.RegisterFunc(L, Utils.GETTER_IDX,"transform", _g_get_transform);...//在-1的表(setter)表中加入"layer"字段,对应c#中的_s_set_layer属性
  5.         Utils.RegisterFunc(L, Utils.SETTER_IDX,"layer", _s_set_layer);...//这个方法主要用于创建元方法__index和__newindex,通过绑定c闭包的方式实现,然后将设置好的元方法放到注册表中。
  6.         Utils.EndObjectRegister(type, L, translator,null,null,null,null,null);//这个方法主要是给GameObject的静态值(静态方法,静态对象)设置元表,与上述非静态值做的事情差不多
  7.         Utils.BeginClassRegister(type, L, __CreateInstance,6,0,0);//在-4的表(类的静态表cls_idx)表中加入"transform"字段,对应c#中的_g_get_transform属性
  8.         Utils.RegisterFunc(L, Utils.CLS_IDX,"CreatePrimitive", _m_CreatePrimitive_xlua_st_);...//给静态表设置元方法__index和__newindex,
  9.         Utils.EndClassRegister(type, L, translator);}
复制代码
2. 通过反射

Utils.ReflectionWrap(L, type, privateAccessibleFlags.Contains(type));
Utils.ReflectionWrap函数
  1. //创建几张表,分别保存属性,方法等
  2.                         LuaAPI.lua_newtable(L);int cls_meta = LuaAPI.lua_gettop(L);
  3.                         LuaAPI.lua_newtable(L);int obj_field = LuaAPI.lua_gettop(L);//成员方法
  4.                         LuaAPI.lua_newtable(L);int obj_getter = LuaAPI.lua_gettop(L);//成员字段,属性 get
  5.                         LuaAPI.lua_newtable(L);int obj_setter = LuaAPI.lua_gettop(L);//成员字段,属性 set
  6.                         LuaAPI.lua_newtable(L);int cls_field = LuaAPI.lua_gettop(L);//静态方法
  7.                         LuaAPI.lua_newtable(L);int cls_getter = LuaAPI.lua_gettop(L);//静态字段,属性 get
  8.                         LuaAPI.lua_newtable(L);int cls_setter = LuaAPI.lua_gettop(L);//静态字段,属性 setLuaCSFunction 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等元方法
  9.                         LuaAPI.xlua_pushasciistring(L,"__gc");
  10.                         LuaAPI.lua_pushstdcallcfunction(L, translator.metaFunctions.GcMeta);
  11.                         LuaAPI.lua_rawset(L, obj_meta);
  12.                         LuaAPI.xlua_pushasciistring(L,"__tostring");
  13.                         LuaAPI.lua_pushstdcallcfunction(L, translator.metaFunctions.ToStringMeta);
  14.                         LuaAPI.lua_rawset(L, obj_meta);//省略很多代码.............................//如果是枚举则添加强转。使得lua中能使用Enum.__CastFrom(int)将int转为enumif(type !=null&& type.IsEnum()){
  15.                                 LuaAPI.xlua_pushasciistring(L,"__CastFrom");
  16.                                 translator.PushFixCSFunction(L,genEnumCastFrom(type));
  17.                                 LuaAPI.lua_rawset(L, cls_field);}//省略很多代码...........//设置构造函数LuaCSFunction constructor =typeof(Delegate).IsAssignableFrom(type)? translator.metaFunctions.DelegateCtor : translator.methodWrapsCache.GetConstructorWrap(type);if(constructor ==null){
  18.                                 constructor =(RealStatePtr LL)=>{return LuaAPI.luaL_error(LL,"No constructor for "+ type);};}
  19.                         LuaAPI.xlua_pushasciistring(L,"__call");
  20.                         translator.PushFixCSFunction(L, constructor);
  21.                         LuaAPI.lua_rawset(L, cls_meta);
复制代码
makeReflectionWrap方法
  1. 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 fieldif(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)){
  2.                                         fieldName ="&"+ fieldName;}if(field.IsStatic &&(field.IsInitOnly || field.IsLiteral)){
  3.                                         LuaAPI.xlua_pushasciistring(L, fieldName);
  4.                                         translator.PushAny(L, field.GetValue(null));
  5.                                         LuaAPI.lua_rawset(L, cls_field);}else{
  6.                                         LuaAPI.xlua_pushasciistring(L, fieldName);
  7.                                         translator.PushFixCSFunction(L,genFieldGetter(type, field));
  8.                                         LuaAPI.lua_rawset(L,field.IsStatic ? cls_getter : obj_getter);
  9.                        
  10.                                         LuaAPI.xlua_pushasciistring(L, fieldName);
  11.                                         translator.PushFixCSFunction(L,genFieldSetter(type, field));
  12.                                         LuaAPI.lua_rawset(L,field.IsStatic ? cls_setter : obj_setter);}}//类似还有将类的方法也放入表等.........................
复制代码
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

GMT+8, 2024-11-23 08:41 , Processed in 0.092267 second(s), 25 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

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