MacroDog

Lua与

2020-02-26

1 Lua与unity交互

Lua 调用 C# xlua虚拟机在运行是会维护一张CS表用来索引调用的Csharp中的方法。

当调用C#中的方法时会先去查找CS表中的方法。

LuaEnv.cs
init_xlua = @" 
...
            CS = CS or {}
            setmetatable(CS, metatable)
...
            "

lua调用c#有两张方式一种是生成适配的代码一种是使用反射


LuaEnv.cs
init_xlua 中
 ...
    //给CS这张表设置__index 元方法
    //目的是 当lua脚本调用csharp方法能够找到对应的代码
  local metatable = {}
   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
             CS = CS or {}
            setmetatable(CS, metatable)
 ...

反射调用

相关代码

ObjectTranslator.cs
TryDelayWrapLoader
...
#if !GEN_CODE_MINIMIZE && !ENABLE_IL2CPP && (UNITY_EDITOR || XLUA_GENERAL) && !FORCE_REFLECTION && !NET_STANDARD_2_0
                if (!DelegateBridge.Gen_Flag && !type.IsEnum() && !typeof(Delegate).IsAssignableFrom(type) && Utils.IsPublic(type))
                {
                    Type wrap = ce.EmitTypeWrap(type);
                    MethodInfo method = wrap.GetMethod("__Register", BindingFlags.Static | BindingFlags.Public);
                    method.Invoke(null, new object[] { L });
                }
                else
                {
                    Utils.ReflectionWrap(L, type, privateAccessibleFlags.Contains(type)); //反射调用
                }

...
Utils.cs
ReflectionWrap
...
public static void ReflectionWrap(RealStatePtr L, Type type, bool privateAccessible)
		{
			...
            LuaCSFunction item_getter;
			LuaCSFunction item_setter;

			//这里是将类中所有的静态变量放入cls_getter和cls_setter表中,静态方法放入cls_field中(cls_getter与cls_setter也在cls_field中)。属性放入item_setter和item_setter闭包中
			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); 
...
			//将cls_getter放入元表_index  cls_setter同理
          //init class meta
            LuaAPI.xlua_pushasciistring(L, "__index");
            LuaAPI.lua_pushvalue(L, cls_getter);
            LuaAPI.lua_pushvalue(L, cls_field);
            translator.Push(L, type.BaseType());
            LuaAPI.xlua_pushasciistring(L, LuaClassIndexsFieldName);
            LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);
            LuaAPI.gen_cls_indexer(L);
			 //store in lua indexs function tables
            LuaAPI.xlua_pushasciistring(L, LuaClassIndexsFieldName);
            LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);

...
			LuaAPI.xlua_pushasciistring(L, "__newindex");
            LuaAPI.lua_pushvalue(L, cls_setter);
            translator.Push(L, type.BaseType());
            LuaAPI.xlua_pushasciistring(L, LuaClassNewIndexsFieldName);
            LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);
            LuaAPI.gen_cls_newindexer(L);
			//store in lua newindexs function tables
            LuaAPI.xlua_pushasciistring(L, LuaClassNewIndexsFieldName);
            LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);

...
Utils.cs
...
static void makeReflectionWrap(RealStatePtr L, Type type, int cls_field, int cls_getter, int cls_setter,
            int obj_field, int obj_getter, int obj_setter, int obj_meta, out LuaCSFunction item_getter, out LuaCSFunction item_setter, BindingFlags access)
        {
...
			             
 			IEnumerable<MethodInfo> extend_methods = GetExtensionMethodsOf(type);
            if (extend_methods != null)
            {
                foreach (var extend_method in extend_methods)
                {
                    MethodKey method_key = new MethodKey { Name = extend_method.Name, IsStatic = false };
                    List<MemberInfo> overloads;
                    if (pending_methods.TryGetValue(method_key, out overloads))
                    {
                        overloads.Add(extend_method);
                        continue;
                    }
                    else
                    {
                        overloads = new List<MemberInfo>() { extend_method };
                        pending_methods.Add(method_key, overloads);
                    }
                }
            }

			//对于非静态成员方法包装成一个闭包放入obj_meta原表中
            foreach (var kv in pending_methods)
            {
                if (kv.Key.Name.StartsWith("op_")) // 操作符
                {
                    LuaAPI.xlua_pushasciistring(L, InternalGlobals.supportOp[kv.Key.Name]);
                    translator.PushFixCSFunction(L,
                        new LuaCSFunction(translator.methodWrapsCache._GenMethodWrap(type, kv.Key.Name, kv.Value.ToArray()).Call));
                    LuaAPI.lua_rawset(L, obj_meta);
                }
                else
                {
                    LuaAPI.xlua_pushasciistring(L, kv.Key.Name);
                    translator.PushFixCSFunction(L,
                        new LuaCSFunction(translator.methodWrapsCache._GenMethodWrap(type, kv.Key.Name, kv.Value.ToArray()).Call));
                    LuaAPI.lua_rawset(L, kv.Key.IsStatic ? cls_field : obj_field);
                }
            }
...
		}
...

通过以上代码可以看出反射调用,就是通过c#反射将类中静态变量和静态方法放入生成的cls_field表中而对于非静态的方法和变量通过生成一个cfuntion闭包放入__tostring原表中(当lua调用时这个闭包会返回方法地址)。对于非静态的方法xlua通过,将方法通过MethodWrap将方法和需要和参数(索引)作为一个LuaCSFuncitom放在fix_cs_functions列表里

...
foreach (var kv in pending_methods)
            {
                if (kv.Key.Name.StartsWith("op_")) // 操作符
                {
                    LuaAPI.xlua_pushasciistring(L, InternalGlobals.supportOp[kv.Key.Name]);
                    translator.PushFixCSFunction(L,
                        new LuaCSFunction(translator.methodWrapsCache._GenMethodWrap(type, kv.Key.Name, kv.Value.ToArray()).Call));
                    LuaAPI.lua_rawset(L, obj_meta);
                }
                else
                {
                    LuaAPI.xlua_pushasciistring(L, kv.Key.Name);
                    
                    translator.PushFixCSFunction(L,
                        new LuaCSFunction(translator.methodWrapsCache._GenMethodWrap(type, kv.Key.Name, kv.Value.ToArray()).Call));
                        //同时将这方法放入obj_field表中
                    LuaAPI.lua_rawset(L, kv.Key.IsStatic ? cls_field : obj_field);
                }
            }
...

到此反射调用到处为止。 warp
xlua中通过打标签的方法 标记需要被lua调用的类和方法,在生成代码的时候将类和方法放入cs表里


Similar Posts

下一篇 RealTimeShadow

Comments