maltadirk 发表于 2022-6-22 15:04

xLua源码解析——初始化xLua

环境:xlua_v2.1.15,lua5.4.1

XLua.LuaEnv luaenv = new XLua.LuaEnv();
luaenv.DoString("CS.UnityEngine.Debug.Log('hello world')");
luaenv.Dispose();
这是xLua指引的快速入门例子,实例化LueEnv,执行lua代码,释放LueEnv实例。
下面看看LuaEnv实例化做了什么事情。
LuaEnv实例化

结合注释,先过一遍源码
public LuaEnv()
{
    // 判断xLua的c代码版本与c#代码版本是否一致
    if (LuaAPI.xlua_get_lib_version() != LIB_VERSION_EXPECT)
    {
      throw new InvalidProgramException("wrong lib version expect:"
            + LIB_VERSION_EXPECT + " but got:" + LuaAPI.xlua_get_lib_version());
    }

#if THREAD_SAFE || HOTFIX_ENABLE
    lock(luaEnvLock)
#endif
    {
      // 记录lua注册表索引,这是一个很重要的表,后续会大量使用
      LuaIndexes.LUA_REGISTRYINDEX = LuaAPI.xlua_get_registry_index();
#if GEN_CODE_MINIMIZE
      LuaAPI.xlua_set_csharp_wrapper_caller(InternalGlobals.CSharpWrapperCallerPtr);
#endif
      // Create State
      rawL = LuaAPI.luaL_newstate();

      //Init Base Libs
      LuaAPI.luaopen_xlua(rawL);// 注册xlua的c方法库
      LuaAPI.luaopen_i64lib(rawL);// 注册uint64库

      translator = new ObjectTranslator(this, rawL);// 用于lua与c#之间传值
      translator.createFunctionMetatable(rawL);// 创建LuaCSFunction的元表

      translator.OpenLib(rawL);// 注册xlua的cs方法库
      ObjectTranslatorPool.Instance.Add(rawL, translator);

      LuaAPI.lua_atpanic(rawL, StaticLuaCallbacks.Panic);// 设置lua异常处理函数

#if !XLUA_GENERAL
      LuaAPI.lua_pushstdcallcfunction(rawL, StaticLuaCallbacks.Print);// 设置print函数
      if (0 != LuaAPI.xlua_setglobal(rawL, "print"))
      {
            throw new Exception("call xlua_setglobal fail!");
      }
#endif

      //template engine lib register
      TemplateEngine.LuaTemplate.OpenLib(rawL);// 注册template库
      // 添加require时用到的查找器
      AddSearcher(StaticLuaCallbacks.LoadBuiltinLib, 2); // just after the preload searcher
      AddSearcher(StaticLuaCallbacks.LoadFromCustomLoaders, 3);
#if !XLUA_GENERAL
      AddSearcher(StaticLuaCallbacks.LoadFromResource, 4);
      AddSearcher(StaticLuaCallbacks.LoadFromStreamingAssetsPath, -1);
#endif
      DoString(init_xlua, "Init");
      init_xlua = null;

      // 添加内置库注册函数,等需要这些库的时候才调用方法去加载,
      // 在上面StaticLuaCallbacks.LoadBuiltinLib函数的逻辑中加载
#if (!UNITY_SWITCH && !UNITY_WEBGL) || UNITY_EDITOR
      AddBuildin("socket.core", StaticLuaCallbacks.LoadSocketCore);
      AddBuildin("socket", StaticLuaCallbacks.LoadSocketCore);
#endif

      AddBuildin("CS", StaticLuaCallbacks.LoadCS);

      // local mt = {__index = StaticLuaCallbacks.MetaFuncIndex}
      LuaAPI.lua_newtable(rawL); //metatable of indexs and newindexs functions
      LuaAPI.xlua_pushasciistring(rawL, "__index");
      LuaAPI.lua_pushstdcallcfunction(rawL, StaticLuaCallbacks.MetaFuncIndex);
      LuaAPI.lua_rawset(rawL, -3);

      // register = setmetatable({}, mt)
      // register = setmetatable({}, mt)
      // register = setmetatable({}, mt)
      // register = setmetatable({}, mt)
      LuaAPI.xlua_pushasciistring(rawL, Utils.LuaIndexsFieldName);
      LuaAPI.lua_newtable(rawL);
      LuaAPI.lua_pushvalue(rawL, -3);
      LuaAPI.lua_setmetatable(rawL, -2);
      LuaAPI.lua_rawset(rawL, LuaIndexes.LUA_REGISTRYINDEX);
      ......

      LuaAPI.lua_pop(rawL, 1); // pop metatable of indexs and newindexs functions

      // register = rawL
      LuaAPI.xlua_pushasciistring(rawL, MAIN_SHREAD);
      LuaAPI.lua_pushthread(rawL);
      LuaAPI.lua_rawset(rawL, LuaIndexes.LUA_REGISTRYINDEX);

      // register = CS
      LuaAPI.xlua_pushasciistring(rawL, CSHARP_NAMESPACE);
      if (0 != LuaAPI.xlua_getglobal(rawL, "CS"))
      {
            throw new Exception("get CS fail!");
      }
      LuaAPI.lua_rawset(rawL, LuaIndexes.LUA_REGISTRYINDEX);

#if !XLUA_GENERAL && (!UNITY_WSA || UNITY_EDITOR)
      // 设置类型的别名
      translator.Alias(typeof(Type), "System.MonoType");
#endif
      // LuaEnv._G持有lua的_G表
      if (0 != LuaAPI.xlua_getglobal(rawL, "_G"))
      {
            throw new Exception("get _G fail!");
      }
      translator.Get(rawL, -1, out _G);
      LuaAPI.lua_pop(rawL, 1);

      errorFuncRef = LuaAPI.get_error_func_ref(rawL);

      // 调用所有初始化器,绑定类型与构造table的方法
      if (initers != null)
      {
            for (int i = 0; i < initers.Count; i++)
            {
                initers(this, translator);
            }
      }

      translator.CreateArrayMetatable(rawL);
      translator.CreateDelegateMetatable(rawL);
      translator.CreateEnumerablePairs(rawL);
    }
}
总结一下,构造方法执行了以下行为:

[*]实例化lua主线程rawL
[*]注册基础库xlua(包含c和cs的方法)、uint64、template
[*]实例化ObjectTranslator,并创建函数、数组、委托的元表,枚举迭代的函数
[*]设置print、panic函数
[*]添加require时查找lua文件的查找器
[*]调用init_xlua的lua代码
[*]创建表:LuaIndexs、LuaNewIndexs、LuaClassIndexs、LuaClassNewIndexs,这4个表用于记录后续C#的type与index和newindex的映射关系。4个表+CS表+rawL,都放到注册表中。
[*]调用所有初始化器,记录C#的type与创建对应table的函数的映射关系,后续首次访问某个类型时,就会调用函数构造出table给lua层使用。这些函数就是由xlua的general code生成的。
ObjectTranslator实例化

在LuaEnv实例化的时候,同时也会实例化ObjectTranslator,这个类的作用是将lua数据栈的值转换为c#类型的值和将c#类型的值压入lua的数据栈,是lua和c#交互的桥梁。
public ObjectTranslator(LuaEnv luaenv, RealStatePtr L)
{
#if XLUA_GENERAL || (UNITY_WSA && !UNITY_EDITOR)
    var dumb_field = typeof(ObjectTranslator).GetField("s_gen_reg_dumb_obj", BindingFlags.Static| BindingFlags.DeclaredOnly | BindingFlags.NonPublic);
    if (dumb_field != null)
    {
      dumb_field.GetValue(null);
    }
#endif
    // 记录用到的程序集,后续用于通过名字查找类型type
    assemblies = new List<Assembly>();

#if (UNITY_WSA && !ENABLE_IL2CPP) && !UNITY_EDITOR
    var assemblies_usorted = Utils.GetAssemblies();
#else
    assemblies.Add(Assembly.GetExecutingAssembly());
    var assemblies_usorted = AppDomain.CurrentDomain.GetAssemblies();
#endif
    addAssemblieByName(assemblies_usorted, "mscorlib,");
    addAssemblieByName(assemblies_usorted, "System,");
    addAssemblieByName(assemblies_usorted, "System.Core,");
    foreach (Assembly assembly in assemblies_usorted)
    {
      if (!assemblies.Contains(assembly))
      {
            assemblies.Add(assembly);
      }
    }

    this.luaEnv = luaenv;
    // 将lua数据栈上的值转换为c#类型的函数集合
    objectCasters = new ObjectCasters(this);
    // 检查lua数据栈上的值是否是c#类型的函数集合
    objectCheckers = new ObjectCheckers(this);
    // 用于生成和缓存methodWraps
    methodWrapsCache = new MethodWrapsCache(this, objectCheckers, objectCasters);
    // 某些类型通用元表用的方法,例如gc、tostring等。
    metaFunctions = new StaticLuaCallbacks();
    // c#的类型转换为table的方法
    importTypeFunction = new LuaCSFunction(StaticLuaCallbacks.ImportType);
    loadAssemblyFunction = new LuaCSFunction(StaticLuaCallbacks.LoadAssembly);
    castFunction = new LuaCSFunction(StaticLuaCallbacks.Cast);

    // 创建弱value的表,把表缓存在注册表上,返回的cacheRef用于后续获取这个表
    // 当引用类型传入lua时,就会缓存到这个表里。
    LuaAPI.lua_newtable(L);
    LuaAPI.lua_newtable(L);
    LuaAPI.xlua_pushasciistring(L, "__mode");
    LuaAPI.xlua_pushasciistring(L, "v");
    LuaAPI.lua_rawset(L, -3);
    LuaAPI.lua_setmetatable(L, -2);
    cacheRef = LuaAPI.luaL_ref(L, LuaIndexes.LUA_REGISTRYINDEX);

    initCSharpCallLua();
}
init_xlua

LuaEnv实例化另一个重点是执行init_xlua的lua代码,这部分lua代码主要行为是:

[*]创建CS表,设置CS的元表,通过元表的__index函数实现首次访问C#类型时才调用对应函数创建该类型的table。
[*]创建全局函数typeof,实际上是返回的是创建C#类型对应table是,设置的UnderlyingSystemType值
[*]创建全局函数cast(xlua.cast)、setenv、getenv、base
[*]新增xlua库函数:hotfix、getmetatable、setmetatable、setclass
具体结合源码上的注释看:
--- LuaEnv构造方法执行的init_lua代码
local metatable = {}
local rawget = rawget
local setmetatable = setmetatable
local import_type = xlua.import_type
local import_generic_type = xlua.import_generic_type
local load_assembly = xlua.load_assembly

function metatable:__index(key)
    local fqn = rawget(self, '.fqn')
    fqn = ((fqn and fqn .. '.') or '') .. key
    -- 是否能导入类型(c#类转为对应table)
    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

function metatable:__newindex()
    error('No such type: ' .. rawget(self, '.fqn'), 2)
end

-- A non-type has been called; e.g. foo = System.Foo()
function metatable:__call(...)
    local n = select('#', ...)
    local fqn = rawget(self, '.fqn')
    if n > 0 then
      --看是否泛型,如果是,则返回“确定了类型”的泛型
      local gt = import_generic_type(fqn, ...)
      if gt then
            return rawget(CS, gt)
      end
    end
    error('No such type: ' .. fqn, 2)
end

CS = CS or {}
setmetatable(CS, metatable)

typeof = function(t) return t.UnderlyingSystemType end
cast = xlua.cast
if not setfenv or not getfenv then
    local function getfunction(level)
      local info = debug.getinfo(level + 1, 'f')
      return info and info.func
    end

    function setfenv(fn, env)
      if type(fn) == 'number' then fn = getfunction(fn + 1) end
      local i = 1
      while true do
            local name = debug.getupvalue(fn, i)
            if name == '_ENV' then
                debug.upvaluejoin(fn, i, (function()
                  return env
                end), 1)
                break
            elseif not name then
                break
            end

            i = i + 1
      end

      return fn
    end

    function getfenv(fn)
      if type(fn) == 'number' then fn = getfunction(fn + 1) end
      local i = 1
      while true do
            local name, val = debug.getupvalue(fn, i)
            if name == '_ENV' then
                return val
            elseif not name then
                break
            end
            i = i + 1
      end
    end
end

xlua.hotfix = function(cs, field, func)
    if func == nil then func = false end
    local tbl = (type(field) == 'table') and field or { = func }
    for k, v in pairs(tbl) do
      local cflag = ''
      if k == '.ctor' then
            cflag = '_c'
            k = 'ctor'
      end
      local f = type(v) == 'function' and v or nil
      xlua.access(cs, cflag .. '__Hotfix0_' .. k, f) -- at least one
      pcall(function()
            for i = 1, 99 do
                xlua.access(cs, cflag .. '__Hotfix' .. i .. '_' .. k, f)
            end
      end)
    end
    xlua.private_accessible(cs)
end
xlua.getmetatable = function(cs)
    return xlua.metatable_operation(cs)
end
xlua.setmetatable = function(cs, mt)
    return xlua.metatable_operation(cs, mt)
end
xlua.setclass = function(parent, name, impl)
    impl.UnderlyingSystemType = parent.UnderlyingSystemType
    rawset(parent, name, impl)
end

local base_mt = {
    __index = function(t, k)
      local csobj = t['__csobj']
      local func = csobj['<>xLuaBaseProxy_' .. k]
      return function(_, ...)
            return func(csobj, ...)
      end
    end
}
base = function(csobj)
    return setmetatable({ __csobj = csobj }, base_mt)
end以上就是整个LuaEnv初始化的流程。总结下来就是:

[*]初始化lua环境
[*]绑定lua和c#类型转换处理方法
[*]初始化各种基础表。
所以在初始化阶段,是不会把每个c#类型转换为table的,只有访问到才会去创建。下一篇Lua访问c#将会了解到整个创建流程。
页: [1]
查看完整版本: xLua源码解析——初始化xLua