|
环境: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[Utils.LuaIndexsFieldName] = setmetatable({}, mt)
// register[Utils.LuaNewIndexsFieldName] = setmetatable({}, mt)
// register[Utils.LuaClassIndexsFieldName] = setmetatable({}, mt)
// register[Utils.LuaClassNewIndexsFieldName] = 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[MAIN_SHREAD] = rawL
LuaAPI.xlua_pushasciistring(rawL, MAIN_SHREAD);
LuaAPI.lua_pushthread(rawL);
LuaAPI.lua_rawset(rawL, LuaIndexes.LUA_REGISTRYINDEX);
// register[CSHARP_NAMESPACE] = 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(&#34;s_gen_reg_dumb_obj&#34;, 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, &#34;mscorlib,&#34;);
addAssemblieByName(assemblies_usorted, &#34;System,&#34;);
addAssemblieByName(assemblies_usorted, &#34;System.Core,&#34;);
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, &#34;__mode&#34;);
LuaAPI.xlua_pushasciistring(L, &#34;v&#34;);
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, &#39;.fqn&#39;)
fqn = ((fqn and fqn .. &#39;.&#39;) or &#39;&#39;) .. key
-- 是否能导入类型(c#类转为对应table)
local obj = import_type(fqn)
if obj == nil then
-- It might be an assembly, so we load it too.
obj = { [&#39;.fqn&#39;] = 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(&#39;No such type: &#39; .. rawget(self, &#39;.fqn&#39;), 2)
end
-- A non-type has been called; e.g. foo = System.Foo()
function metatable:__call(...)
local n = select(&#39;#&#39;, ...)
local fqn = rawget(self, &#39;.fqn&#39;)
if n > 0 then
--看是否泛型,如果是,则返回“确定了类型”的泛型
local gt = import_generic_type(fqn, ...)
if gt then
return rawget(CS, gt)
end
end
error(&#39;No such type: &#39; .. 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, &#39;f&#39;)
return info and info.func
end
function setfenv(fn, env)
if type(fn) == &#39;number&#39; then fn = getfunction(fn + 1) end
local i = 1
while true do
local name = debug.getupvalue(fn, i)
if name == &#39;_ENV&#39; 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) == &#39;number&#39; then fn = getfunction(fn + 1) end
local i = 1
while true do
local name, val = debug.getupvalue(fn, i)
if name == &#39;_ENV&#39; 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) == &#39;table&#39;) and field or { [field] = func }
for k, v in pairs(tbl) do
local cflag = &#39;&#39;
if k == &#39;.ctor&#39; then
cflag = &#39;_c&#39;
k = &#39;ctor&#39;
end
local f = type(v) == &#39;function&#39; and v or nil
xlua.access(cs, cflag .. &#39;__Hotfix0_&#39; .. k, f) -- at least one
pcall(function()
for i = 1, 99 do
xlua.access(cs, cflag .. &#39;__Hotfix&#39; .. i .. &#39;_&#39; .. 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[name].UnderlyingSystemType
rawset(parent, name, impl)
end
local base_mt = {
__index = function(t, k)
local csobj = t[&#39;__csobj&#39;]
local func = csobj[&#39;<>xLuaBaseProxy_&#39; .. 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#将会了解到整个创建流程。 |
|