xLua源码解析——Lua调用C#
Lua调用C#的类,实际上是访谒类型对应的table。该table会在初度调用的时候创建。以下面的代码为例来了解整个调用流程:CS.UnityEngine.GameObject(”Test”)// 实例化go对象如何触发创建
CS表在LuaEnv构造函数内执行init_xlua的Lua代码时候创建的,初度调用CS.UnityEngine时候,会执行CS元表的__index方式。
local metatable = {}
local import_type = xlua.import_type
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这时的fqn是”UnityEngine”,调用import_type(fnq)导入类型,也就是创建类型对应的table。
obj为true暗示类型创建了table,否则可能是定名空间(或者不存在),所以也创建一个类似CS的表,用于后续的查询。最后缓存obj,下次访谒就能直接获取。
import_type函数是通过LuaEnv实例化代码中的translator.OpenLib(rawL)绑定的,实际调用的是XLua.StaticLuaCallbacks.ImportType方式。
public static int ImportType(RealStatePtr L)
{
try
{
ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L);
string className = LuaAPI.lua_tostring(L, 1);
Type type = translator.FindType(className);
if (type != null)
{
if (translator.GetTypeId(L, type) >= 0)
{
LuaAPI.lua_pushboolean(L, true);
}
else
{
return LuaAPI.luaL_error(L, ”can not load type ” + type);
}
}
else
{
LuaAPI.lua_pushnil(L);
}
return 1;
}
catch (System.Exception e)
{
return LuaAPI.luaL_error(L, ”c# exception in xlua.import_type:” + e);
}
}translator.FindType(className)确定是否是一个类,不是则返回nil。translator.GetTypeId(L,type)查找获创建table。
接下来继续看getTypeId方式。先判断是否数组或者委托,是就直接返回对应的meta,否则就要走创建流程。
internal int getTypeId(RealStatePtr L, Type type, out bool is_first, LOGLEVEL log_level = LOGLEVEL.WARN)
{
int type_id;
is_first = false;
if (!typeIdMap.TryGetValue(type, out type_id)) // no reference
{
if (type.IsArray)
{
if (common_array_meta == -1) throw new Exception(”Fatal Exception! Array Metatable not inited!”);
return common_array_meta;
}
if (typeof(MulticastDelegate).IsAssignableFrom(type))
{
if (common_delegate_meta == -1) throw new Exception(”Fatal Exception! Delegate Metatable not inited!”);
TryDelayWrapLoader(L, type);
return common_delegate_meta;
}
is_first = true;
Type alias_type = null;
aliasCfg.TryGetValue(type, out alias_type);
LuaAPI.luaL_getmetatable(L, alias_type == null ? type.FullName : alias_type.FullName);
if (LuaAPI.lua_isnil(L, -1)) //no meta yet, try to use reflection meta
{
LuaAPI.lua_pop(L, 1);
if (TryDelayWrapLoader(L, alias_type == null ? type : alias_type))
{
LuaAPI.luaL_getmetatable(L, alias_type == null ? type.FullName : alias_type.FullName);
}
else
{
throw new Exception(”Fatal: can not load metatable of type:” + type);
}
}
//循环依赖,自身依赖本身的class,比如有个自身类型的静态readonly对象。
if (typeIdMap.TryGetValue(type, out type_id))
{
LuaAPI.lua_pop(L, 1);
}
else
{
......
typeIdMap.Add(type, type_id);
}
}
return type_id;
}可以看出,创建的逻辑在TryDelayWrapLoader方式。
方式先判断是通过生成代码还是反射创建:有loader函数则调用loader函数创建table,否则调用Utils.ReflectionWrap通过反射生成。下面只分析生成代码流程,反射部门直接看Utils.ReflectionWrap方式即可。
public bool TryDelayWrapLoader(RealStatePtr L, Type type)
{
if (loaded_types.ContainsKey(type)) return true;
loaded_types.Add(type, true);
LuaAPI.luaL_newmetatable(L, type.FullName); //先建一个metatable,因为加载过程可能会需要用到
LuaAPI.lua_pop(L, 1);
Action<RealStatePtr> loader;
int top = LuaAPI.lua_gettop(L);
if (delayWrap.TryGetValue(type, out loader))
{
delayWrap.Remove(type);
loader(L);
}
else
{
......
Utils.ReflectionWrap(L, type, privateAccessibleFlags.Contains(type));
......
}
......
return true;
}关联table创建方式
delayWrap字典实际上存的是类型的代码,就例子来说,对应的是下面的方式:
public class UnityEngineGameObjectWrap
{
public static void __Register(RealStatePtr L)
{
......
}
}这些方式都是通过XLua_Gen_Initer_Register__类注册函数放到Initer,Initer在LueEnv构造函数内调用,最终放到上面的delayWrap字典里。
public class XLua_Gen_Initer_Register__
{
static void wrapInit0(LuaEnv luaenv, ObjectTranslator translator)
{
......
translator.DelayWrapLoader(typeof(UnityEngine.Debug), UnityEngineDebugWrap.__Register);
......
}
static void Init(LuaEnv luaenv, ObjectTranslator translator)
{
wrapInit0(luaenv, translator);
......
}
static XLua_Gen_Initer_Register__()
{
XLua.LuaEnv.AddIniter(Init);
}
}如何创建类的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方式。
// 源码有删减,每个类型注册只保留一条
public class UnityEngineGameObjectWrap
{
public static void __Register(RealStatePtr L)
{
ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L);
System.Type type = typeof(UnityEngine.GameObject);
// 动态部门
Utils.BeginObjectRegister(type, L, translator, 0, 13, 9, 3);
Utils.RegisterFunc(L, Utils.METHOD_IDX, ”GetComponent”, _m_GetComponent);
......
Utils.RegisterFunc(L, Utils.GETTER_IDX, ”transform”, _g_get_transform);
......
Utils.RegisterFunc(L, Utils.SETTER_IDX, ”layer”, _s_set_layer);
......
Utils.EndObjectRegister(type, L, translator, null, null,
null, null, null);
// 静态部门
Utils.BeginClassRegister(type, L, __CreateInstance, 6, 0, 0);
Utils.RegisterFunc(L, Utils.CLS_IDX, ”Find”, _m_Find_xlua_st_);
......
Utils.EndClassRegister(type, L, translator);
}
}无论动态和静态部门,实际上都是3个函数调用,下面分隔静态和动态两部门进行分析。
创建静态部门table
BeginClassRegister
[*]创建cls_table、meta_table、getter_table、setter_table 4个表,
[*]cls_table设置到CS.UnityEngine.Debug和CS下。
[*]meta_table设置为cls_table的元表
public static void BeginClassRegister(Type type, RealStatePtr L, LuaCSFunction creator, int class_field_count,
int static_getter_count, int static_setter_count)
{
ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L);
LuaAPI.lua_createtable(L, 0, class_field_count);
// cls_table=type,用于typeof方式访谒获取类型
LuaAPI.xlua_pushasciistring(L, ”UnderlyingSystemType”);
translator.PushAny(L, type);
LuaAPI.lua_rawset(L, -3);
int cls_table = LuaAPI.lua_gettop(L);
// CS.UnityEngine.Debug = cls_table
// CS = cls_table
SetCSTable(L, type, cls_table);
// 创建meta_table = {__call = creator}
LuaAPI.lua_createtable(L, 0, 3);
int meta_table = LuaAPI.lua_gettop(L);
if (creator != null)
{
LuaAPI.xlua_pushasciistring(L, ”__call”);
LuaAPI.lua_pushstdcallcfunction(L, creator);
LuaAPI.lua_rawset(L, -3);
}
// getter_table = {}
if (static_getter_count == 0)
{
LuaAPI.lua_pushnil(L);
}
else
{
LuaAPI.lua_createtable(L, 0, static_getter_count);
}
// setter_table = {}
if (static_setter_count == 0)
{
LuaAPI.lua_pushnil(L);
}
else
{
LuaAPI.lua_createtable(L, 0, static_setter_count);
}
// setmetetable(cls_table, mete_table)
LuaAPI.lua_pushvalue(L, meta_table);
LuaAPI.lua_setmetatable(L, cls_table);
}重点看SetCSTable方式,此方式把cls_table设置到CS.UnityEngine.GameObject和CS下。这就是为什么能够在前面local obj = import_type(fqn),成果为true的时,能直接rawget(self, key)得到table的原因。
public static void SetCSTable(RealStatePtr L, Type type, int cls_table)
{
int oldTop = LuaAPI.lua_gettop(L);
cls_table = abs_idx(oldTop, cls_table);
LuaAPI.xlua_pushasciistring(L, LuaEnv.CSHARP_NAMESPACE);
LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);
// path = {`UnityEngine`, `Debug`}
List<string> path = getPathOfType(type);
// 查抄除最后一个table外的其他table是否存在,不存在则创建
for (int i = 0; i < path.Count - 1; ++i)
{
LuaAPI.xlua_pushasciistring(L, path);
if (0 != LuaAPI.xlua_pgettable(L, -2))
{
var err = LuaAPI.lua_tostring(L, -1);
LuaAPI.lua_settop(L, oldTop);
throw new Exception(”SetCSTable for [” + type + ”] error: ” + err);
}
if (LuaAPI.lua_isnil(L, -1))
{
LuaAPI.lua_pop(L, 1);
LuaAPI.lua_createtable(L, 0, 0);
LuaAPI.xlua_pushasciistring(L, path);
LuaAPI.lua_pushvalue(L, -2);
LuaAPI.lua_rawset(L, -4);
}
else if (!LuaAPI.lua_istable(L, -1))
{
LuaAPI.lua_settop(L, oldTop);
throw new Exception(”SetCSTable for [” + type + ”] error: ancestors is not a table!”);
}
LuaAPI.lua_remove(L, -2);
}
// 设置CS.UnityEngine.Debug = cls_table
LuaAPI.xlua_pushasciistring(L, path);
LuaAPI.lua_pushvalue(L, cls_table);
LuaAPI.lua_rawset(L, -3);
LuaAPI.lua_pop(L, 1);
// 设置CS = cls_table
LuaAPI.xlua_pushasciistring(L, LuaEnv.CSHARP_NAMESPACE);
LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);
ObjectTranslatorPool.Instance.Find(L).PushAny(L, type);
LuaAPI.lua_pushvalue(L, cls_table);
LuaAPI.lua_rawset(L, -3);
LuaAPI.lua_pop(L, 1);
}BeginClassRegister调用完毕后,数据栈上是这样的:
RegisterFunc
把方式注册到对应的表上,第二个参数就是对应表在仓库上的位置。
public static void RegisterFunc(RealStatePtr L, int idx, string name, LuaCSFunction func)
{
idx = abs_idx(LuaAPI.lua_gettop(L), idx);
LuaAPI.xlua_pushasciistring(L, name);
LuaAPI.lua_pushstdcallcfunction(L, func);
LuaAPI.lua_rawset(L, idx);
}对于静态部门,idx的定义如下,也就是BeginClassRegister调用后仓库上对应表的索引:
public const int CLS_IDX = -4;
public const int CLS_META_IDX = -3;
public const int CLS_GETTER_IDX = -2;
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 = cls_indexer
[*]保留到调集:LuaNewIndexs = cls_newindexer
[*]数据栈弹出前面的4个table。
public static void EndClassRegister(Type type, RealStatePtr L, ObjectTranslator translator)
{
int top = LuaAPI.lua_gettop(L);
int cls_idx = abs_idx(top, CLS_IDX);
int cls_getter_idx = abs_idx(top, CLS_GETTER_IDX);
int cls_setter_idx = abs_idx(top, CLS_SETTER_IDX);
int cls_meta_idx = abs_idx(top, CLS_META_IDX);
//begin cls index
LuaAPI.xlua_pushasciistring(L, ”__index”);
LuaAPI.lua_pushvalue(L, cls_getter_idx);// p1
LuaAPI.lua_pushvalue(L, cls_idx);// p2
translator.Push(L, type.BaseType());// p3
LuaAPI.xlua_pushasciistring(L, LuaClassIndexsFieldName);
LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);// p4
// 将p1~4和函数内部新增的p5共5个参数作为upvalue,创建闭包函数cls_indexer
LuaAPI.gen_cls_indexer(L);
// 设置cls_indexers = cls_indexer
LuaAPI.xlua_pushasciistring(L, LuaClassIndexsFieldName);
LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);//store in lua indexs function tables
translator.Push(L, type);
LuaAPI.lua_pushvalue(L, -3);
LuaAPI.lua_rawset(L, -3);
LuaAPI.lua_pop(L, 1);
// 设置meta_table = cls_indexer
LuaAPI.lua_rawset(L, cls_meta_idx);
//end cls index
// newindex逻辑类似,不再此外注释
//begin cls newindex
LuaAPI.xlua_pushasciistring(L, ”__newindex”);
LuaAPI.lua_pushvalue(L, cls_setter_idx);
translator.Push(L, type.BaseType());
LuaAPI.xlua_pushasciistring(L, LuaClassNewIndexsFieldName);
LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);
LuaAPI.gen_cls_newindexer(L);
LuaAPI.xlua_pushasciistring(L, LuaClassNewIndexsFieldName);
LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);//store in lua newindexs function tables
translator.Push(L, type);
LuaAPI.lua_pushvalue(L, -3);
LuaAPI.lua_rawset(L, -3);
LuaAPI.lua_pop(L, 1);
LuaAPI.lua_rawset(L, cls_meta_idx);
//end cls newindex
LuaAPI.lua_pop(L, 4);
}这个函数的重点其实是创建出来的cls_indexer和cls_newindexer闭包函数,后续访谒类的属性或者方式,城市调用到这个函数。这里以gen_cls_indexer为例看具体实现。
LUA_API int gen_obj_indexer(lua_State *L) {
lua_pushnil(L);// 第5个upvalue,用来存放基类的cls_indexer闭包函数
lua_pushcclosure(L, obj_indexer, 5);
return 0;
}
//upvalue --- :getters, :feilds, :base, :indexfuncs, :baseindex
//param --- : obj, : key
LUA_API int cls_indexer(lua_State *L) {
// 从getter_table中查找
if (!lua_isnil(L, lua_upvalueindex(1))) {
lua_pushvalue(L, 2);
lua_gettable(L, lua_upvalueindex(1));
if (!lua_isnil(L, -1)) {//has getter
lua_call(L, 0, 1);
return 1;
}
lua_pop(L, 1);
}
// 从cls_table中查找
if (!lua_isnil(L, lua_upvalueindex(2))) {
lua_pushvalue(L, 2);
lua_rawget(L, lua_upvalueindex(2));
if (!lua_isnil(L, -1)) {//has feild
return 1;
}
lua_pop(L, 1);
}
// 初度访谒,查找按照upvalue的基类类型和upvalue的cls_indexers调集,
// 查找cls_indexer闭包函数,设置到第5个upvalue的位置。然后清空upvalue。
if (!lua_isnil(L, lua_upvalueindex(3))) {
lua_pushvalue(L, lua_upvalueindex(3));
while(!lua_isnil(L, -1)) {
lua_pushvalue(L, -1);
lua_gettable(L, lua_upvalueindex(4));
if (!lua_isnil(L, -1)) // found
{
lua_replace(L, lua_upvalueindex(5)); //baseindex = indexfuncs
lua_pop(L, 1);
break;
}
lua_pop(L, 1);
lua_getfield(L, -1, ”BaseType”);
lua_remove(L, -2);
}
lua_pushnil(L);
lua_replace(L, lua_upvalueindex(3));//base = nil
}
// 执行基类的cls_indexer闭包函数继续查找
if (!lua_isnil(L, lua_upvalueindex(5))) {
lua_settop(L, 2);
lua_pushvalue(L, lua_upvalueindex(5));
lua_insert(L, 1);
lua_call(L, 2, 1);
return 1;
} else {
lua_pushnil(L);
return 1;
}
}gen_cls_newindexer的实现思路是一致的,具体不再赘述。
Lua模拟实现
使用Lua代码模拟类的静态部弟子成table的行为。
-- BeginClassRegister
local cls_table = {}
local meta_table = {__call = __CreateInstance}
local getter_table = {}
local setter_table = {}
CS.UnityEngine.Debug = cls_table
CS = cls_table
setmetatable(cls_table, meta_table)
-- RegisterFunc 只模拟一条
cls_table[”Find”] = _m_Find_xlua_st_
-- EndClassRegister
local clsIndexer = gen_cls_indexer()
local clsNewIndexer = gen_cls_newindexer()
meta_table[”__index”] = clsIndexer
meta_table[”__newindex”] = clsNewIndexer
register[”LuaClassIndexs”] = clsIndexer
register[”LuaClassNewIndexs”] = clsNewIndexer创建动态部门table
动态部门的创建逻辑与静态部门长短常接近的,斗劲大的差异是,动态部门table是一个元表,在实例化的时候设置为对象的元表。下面主要通过注释说明,最后结合Lua模拟代码去理解。
BeginObjectRegister
创建meta、method、getter、setter四个元表放在数据栈上:
//meta: -4, method:-3, getter: -2, setter: -1
public static void BeginObjectRegister(Type type, RealStatePtr L, ObjectTranslator translator, int meta_count, int method_count, int getter_count,
int setter_count, int type_id = -1)
{
if (type == null)
{
if (type_id == -1) throw new Exception(”Fatal: must provide a type of type_id”);
LuaAPI.xlua_rawgeti(L, LuaIndexes.LUA_REGISTRYINDEX, type_id);
}
else
{
// 按照类型名获取元表meta_table,没有就创建一个放栈上
LuaAPI.luaL_getmetatable(L, type.FullName);
if (LuaAPI.lua_isnil(L, -1))
{
LuaAPI.lua_pop(L, 1);
LuaAPI.luaL_newmetatable(L, type.FullName);
}
}
// 设置标识,meta_table = 1
LuaAPI.lua_pushlightuserdata(L, LuaAPI.xlua_tag());
LuaAPI.lua_pushnumber(L, 1);
LuaAPI.lua_rawset(L, -3);
if ((type == null || !translator.HasCustomOp(type)) && type != typeof(decimal))
{
LuaAPI.xlua_pushasciistring(L, ”__gc”);
LuaAPI.lua_pushstdcallcfunction(L, translator.metaFunctions.GcMeta);
LuaAPI.lua_rawset(L, -3);
}
// meta_table = translator.metaFunctions.ToStringMeta
LuaAPI.xlua_pushasciistring(L, ”__tostring”);
LuaAPI.lua_pushstdcallcfunction(L, translator.metaFunctions.ToStringMeta);
LuaAPI.lua_rawset(L, -3);
// 创建method_table放到数据栈上,没有也放nil占位
if (method_count == 0)
{
LuaAPI.lua_pushnil(L);
}
else
{
LuaAPI.lua_createtable(L, 0, method_count);
}
// 创建getter_table放到数据栈上,没有也放nil占位
if (getter_count == 0)
{
LuaAPI.lua_pushnil(L);
}
else
{
LuaAPI.lua_createtable(L, 0, getter_count);
}
// 创建getter_table放到数据栈上,没有也放nil占位
if (setter_count == 0)
{
LuaAPI.lua_pushnil(L);
}
else
{
LuaAPI.lua_createtable(L, 0, setter_count);
}
}BeginObjectRegister调用后,4个表在数据栈上的位置就是:
RegisterFunc
和静态部门独一的差异,idx的定义变为:
public const int OBJ_META_IDX = -4;
public const int METHOD_IDX = -3;
public const int GETTER_IDX = -2;
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。具体看源码:
public static void EndObjectRegister(Type type, RealStatePtr L, ObjectTranslator translator, LuaCSFunction csIndexer,
LuaCSFunction csNewIndexer, Type base_type, LuaCSFunction arrayIndexer, LuaCSFunction arrayNewIndexer)
{
int top = LuaAPI.lua_gettop(L);
int meta_idx = abs_idx(top, OBJ_META_IDX);
int method_idx = abs_idx(top, METHOD_IDX);
int getter_idx = abs_idx(top, GETTER_IDX);
int setter_idx = abs_idx(top, SETTER_IDX);
//begin index gen
LuaAPI.xlua_pushasciistring(L, ”__index”);
LuaAPI.lua_pushvalue(L, method_idx);// 参数1
LuaAPI.lua_pushvalue(L, getter_idx);// 参数2
// 参数3
if (csIndexer == null)
{
LuaAPI.lua_pushnil(L);
}
else
{
LuaAPI.lua_pushstdcallcfunction(L, csIndexer);
}
// 参数4
translator.Push(L, type == null ? base_type : type.BaseType());
// 参数5
LuaAPI.xlua_pushasciistring(L, LuaIndexsFieldName);
LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);
// 参数6
if (arrayIndexer == null)
{
LuaAPI.lua_pushnil(L);
}
else
{
LuaAPI.lua_pushstdcallcfunction(L, arrayIndexer);
}
// 将上面6个参数 + 内部1个参数作为upvalue创建闭包函数
LuaAPI.gen_obj_indexer(L);
// register=obj_indexer
if (type != null)
{
LuaAPI.xlua_pushasciistring(L, LuaIndexsFieldName);
LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);//store in lua indexs function tables
translator.Push(L, type);
LuaAPI.lua_pushvalue(L, -3);
LuaAPI.lua_rawset(L, -3);
LuaAPI.lua_pop(L, 1);
}
LuaAPI.lua_rawset(L, meta_idx);
//end index gen
//begin newindex gen
LuaAPI.xlua_pushasciistring(L, ”__newindex”);
LuaAPI.lua_pushvalue(L, setter_idx);
if (csNewIndexer == null)
{
LuaAPI.lua_pushnil(L);
}
else
{
LuaAPI.lua_pushstdcallcfunction(L, csNewIndexer);
}
translator.Push(L, type == null ? base_type : type.BaseType());
LuaAPI.xlua_pushasciistring(L, LuaNewIndexsFieldName);
LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);
if (arrayNewIndexer == null)
{
LuaAPI.lua_pushnil(L);
}
else
{
LuaAPI.lua_pushstdcallcfunction(L, arrayNewIndexer);
}
LuaAPI.gen_obj_newindexer(L);
// register = obj_newindexer
if (type != null)
{
LuaAPI.xlua_pushasciistring(L, LuaNewIndexsFieldName);
LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);//store in lua newindexs function tables
translator.Push(L, type);
LuaAPI.lua_pushvalue(L, -3);
LuaAPI.lua_rawset(L, -3);
LuaAPI.lua_pop(L, 1);
}
LuaAPI.lua_rawset(L, meta_idx);
//end new index gen
LuaAPI.lua_pop(L, 4);
}看看gen_obj_indexer的实现:
//upvalue --- : methods, :getters, :csindexer, :base,
// :indexfuncs, :arrayindexer, :baseindex
//param --- : obj, : key
LUA_API int obj_indexer(lua_State *L) {
// method有没有?
if (!lua_isnil(L, lua_upvalueindex(1))) {
lua_pushvalue(L, 2);
lua_gettable(L, lua_upvalueindex(1));
if (!lua_isnil(L, -1)) {//has method
return 1;
}
lua_pop(L, 1);
}
// getter有没有?
if (!lua_isnil(L, lua_upvalueindex(2))) {
lua_pushvalue(L, 2);
lua_gettable(L, lua_upvalueindex(2));
if (!lua_isnil(L, -1)) {//has getter
lua_pushvalue(L, 1);
lua_call(L, 1, 1);
return 1;
}
lua_pop(L, 1);
}
// arrayindexer方式存在,而且key是number,则调用arrayIndexer方式
if (!lua_isnil(L, lua_upvalueindex(6)) && lua_type(L, 2) == LUA_TNUMBER) {
lua_pushvalue(L, lua_upvalueindex(6));
lua_pushvalue(L, 1);
lua_pushvalue(L, 2);
lua_call(L, 2, 1);
return 1;
}
// csindexer存在,则调用方式看能否获取
if (!lua_isnil(L, lua_upvalueindex(3))) {
lua_pushvalue(L, lua_upvalueindex(3));
lua_pushvalue(L, 1);
lua_pushvalue(L, 2);
lua_call(L, 2, 2);
if (lua_toboolean(L, -2)) {
return 1;
}
lua_pop(L, 2);
}
// 如果有父类,则找到父类的obj_indexer函数,设置到第7个upvalue的位置,然后把自身置空。
// 可以理解为只是一个延迟设置基类obj_indexer的逻辑。
if (!lua_isnil(L, lua_upvalueindex(4))) {
lua_pushvalue(L, lua_upvalueindex(4));
while(!lua_isnil(L, -1)) {
lua_pushvalue(L, -1);
lua_gettable(L, lua_upvalueindex(5));
if (!lua_isnil(L, -1)) // found
{
lua_replace(L, lua_upvalueindex(7)); //baseindex = indexfuncs
lua_pop(L, 1);
break;
}
// 没找到则继续找父类的父类
lua_pop(L, 1);
lua_getfield(L, -1, ”BaseType”);
lua_remove(L, -2);
}
lua_pushnil(L);
lua_replace(L, lua_upvalueindex(4));//base = nil
}
// 调用父类的obj_indexer方式查找
if (!lua_isnil(L, lua_upvalueindex(7))) {
lua_settop(L, 2);
lua_pushvalue(L, lua_upvalueindex(7));
lua_insert(L, 1);
lua_call(L, 2, 1);
return 1;
} else {
return 0;
}
}obj_newindexer和obj_indexer逻辑一样,这里不再赘述。
Lua模拟实现
-- 1.BeginObjectRegister
local meta_table = register = {}
mete_table = 1
mete_table[”__string”] = translator.metaFunctions.ToStringMeta
local method_table = {}
local getter_table = {}
local setter_table = {}
-- 2.RegisterFunc 只展示一条,其他表同样措置
method_table[”GetComponent”] = _m_GetComponent
-- 3.EndObjectRegister 只模拟index,newindex逻辑一样
local obj_indexer = gen_obj_indexer()
local obj_newindexer = gen_obj_newindexer()
meta_table[”__index”] = obj_indexer
mete_table[”__newindex”] = obj_newindexer
register[”LuaIndexs”] = obj_indexer
register[”LuaNewIndexs”] = obj_newindexer至此,一个C#类转换为table的流程就跑完了。
indexer和newindexer调集
看了上面的分析,能发现类创建table时生成的所有indexer和newindexer城市放入注册表register的4个字典里面缓存起来。后续可以通过类型type查找。
// cs对象__index和__newindex表
string LuaIndexsFieldName = ”LuaIndexs”;
string LuaNewIndexsFieldName = ”LuaNewIndexs”;
// cs类__index和__newindex表
string LuaClassIndexsFieldName = ”LuaClassIndexs”;
string LuaClassNewIndexsFieldName = ”LuaClassNewIndexs”;
// 示例:
// register = obj_indexer
// type是cs的Type类型,obj_indexer是闭包函数实例化对象
实例化GameObject对象,在Lua中实际上是调用元表的__call方式。
上面创建静态部门,就是生成的__CreateInstance作为BeginClassRegister的creater参数设置到元表的__call方式。
__CreateInstance方式就是按照参数的个数调用C#层的实例化逻辑。
static int __CreateInstance(RealStatePtr L)
{
try
{
ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L);
// 2个参数?
if (LuaAPI.lua_gettop(L) == 2 && (LuaAPI.lua_isnil(L, 2) || LuaAPI.lua_type(L, 2) == LuaTypes.LUA_TSTRING))
{
string _name = LuaAPI.lua_tostring(L, 2);
var gen_ret = new UnityEngine.GameObject(_name);
translator.Push(L, gen_ret);
return 1;
}
// 1个参数?
if (LuaAPI.lua_gettop(L) == 1)
{
var gen_ret = new UnityEngine.GameObject();
translator.Push(L, gen_ret);
return 1;
}
// 大于2个参数?
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)))
{
string _name = LuaAPI.lua_tostring(L, 2);
System.Type[] _components = translator.GetParams<System.Type>(L, 3);
var gen_ret = new UnityEngine.GameObject(_name, _components);
translator.Push(L, gen_ret);
return 1;
}
}
catch (System.Exception gen_e)
{
return LuaAPI.luaL_error(L, ”c# exception:” + gen_e);
}
return LuaAPI.luaL_error(L, ”invalid arguments to UnityEngine.GameObject constructor!”);
}实例化C#对象后,需要创建userdata持有对象,并按照type找到前面创建的元表,把元表设置为userdata的元表。这部门逻辑在translator.Push内。
public void Push(RealStatePtr L, object o)
{
if (o == null)
{
LuaAPI.lua_pushnil(L);
return;
}
int index = -1;
Type type = o.GetType();
#if !UNITY_WSA || UNITY_EDITOR
bool is_enum = type.IsEnum;
bool is_valuetype = type.IsValueType;
#else
bool is_enum = type.GetTypeInfo().IsEnum;
bool is_valuetype = type.GetTypeInfo().IsValueType;
#endif
bool needcache = !is_valuetype || is_enum;// 引用类型和枚举需要缓存
if (needcache && (is_enum ? enumMap.TryGetValue(o, out index) : reverseMap.TryGetValue(o, out index)))
{
if (LuaAPI.xlua_tryget_cachedud(L, index, cacheRef) == 1)
{
return;
}
//这里实在太经典了,weaktable先删除,然后GC会延迟调用,当index会循环操作的时候,不注释这行将会导致反复释放
//collectObject(index);
}
bool is_first;
int type_id = getTypeId(L, type, out is_first);
//如果一个type的定义含本身静态readonly实例时,getTypeId会push一个实例,这时候应该用这个实例
if (is_first && needcache && (is_enum ? enumMap.TryGetValue(o, out index) : reverseMap.TryGetValue(o, out index)))
{
if (LuaAPI.xlua_tryget_cachedud(L, index, cacheRef) == 1)
{
return;
}
}
index = addObject(o, is_valuetype, is_enum);
LuaAPI.xlua_pushcsobj(L, index, type_id, needcache, cacheRef);
}前面判断了引用类型和枚举是否有缓存,有就从缓存获取即可,否则就通过type获取type_id。这个getTypeId的方式就是前面Lua访谒C#时,查抄或者创建table的方式。
addObject方式很简单,但是很重要,Lua用到的C#对象,要不被回收,就是在这放入了objects对象表,并返回索引index。
int addObject(object obj, bool is_valuetype, bool is_enum)
{
int index = objects.Add(obj);
if (is_enum)
{
enumMap = index;
}
else if (!is_valuetype)
{
reverseMap = index;
}
return index;
}最后就是调用c实现的的xlua_pushcsobj方式,最后一个调用参数cacheRef是什么值?
在ObjectTranslator构造函数内,创建了一个弱value的Lua表,通过luaL_ref方式保留在注册表register下,cacheRef就保留了这个索引。也就是说,通过这个值,能够在注册表register下找到这个表。
#define lua_newuserdata(L,s) lua_newuserdatauv(L,s,1)
LUA_API void xlua_pushcsobj(lua_State *L, int key, int meta_ref, int need_cache, int cache_ref) {
// 创建userdata,返回的是userdata memory块的指针,宏内设置memory块大小就是1个int
int* pointer = (int*)lua_newuserdata(L, sizeof(int));
*pointer = key;
// 把userdata缓存到cache_ref表中
if (need_cache) cacheud(L, key, cache_ref);
// 获取类型对应的元表
lua_rawgeti(L, LUA_REGISTRYINDEX, meta_ref);
// 设置userdata的元表
lua_setmetatable(L, -2);
}
static void cacheud(lua_State *L, int key, int cache_ref) {
lua_rawgeti(L, LUA_REGISTRYINDEX, cache_ref);
lua_pushvalue(L, -2);// push userdata
lua_rawseti(L, -2, key);// cache_ref = userdata
lua_pop(L, 1);// pop cache_ref
}至此,数据栈的栈顶就是GameObject实例的userdata。
页:
[1]