一只拖鞋 发表于 2024-7-15 18:49

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]
查看完整版本: xLua源码解析——Lua调用C#