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表里