柚子快報(bào)激活碼778899分享:XLua原理(一)
柚子快報(bào)激活碼778899分享:XLua原理(一)
項(xiàng)目中活動(dòng)都是用xlua開(kāi)發(fā)的,項(xiàng)目周更熱修也是用xlua的hotfix特性來(lái)做的?,F(xiàn)研究底層原理,對(duì)于項(xiàng)目性能有個(gè)更好的把控。
本文認(rèn)為看到該文章的人已具備使用xlua開(kāi)發(fā)的能力,只研究介紹下xlua的底層實(shí)現(xiàn)原理。
一.lua和c#交互原理
概括:
通過(guò)棧來(lái)實(shí)現(xiàn)。lua調(diào)用c#就是將lua層的參數(shù)和c#導(dǎo)出函數(shù)入棧,然后執(zhí)行函數(shù)。c#調(diào)用lua就是將c#層的參數(shù)和lua函數(shù)入棧,然后執(zhí)行函數(shù)
1.1 C#訪問(wèn)lua
直接上代碼:
private void Demo1()
{
IntPtr L = LuaAPI.luaL_newstate();
if (LuaAPI.luaL_loadbuffer(L, @"function addandsub(x,y)
return x+y , x-y end", "selfTagChunk") != 0)
{
Debug.LogError(LuaAPI.lua_tostring(L,-1));
}
//LuaAPI.lua_pushnumber(L,20);
//LuaAPI.lua_pushnumber(L,21);
LuaAPI.lua_pcall(L, 0, 0, 0);
LuaAPI.xlua_getglobal(L, "addandsub");
LuaAPI.lua_pushnumber(L,10);
LuaAPI.lua_pushnumber(L,7);
int valueB = LuaAPI.xlua_tointeger(L, -1);//7
int valueB2 = LuaAPI.xlua_tointeger(L, -2);//10
LuaTypes luaTypeB3 = LuaAPI.lua_type(L, -3);//function
int valueC4 = LuaAPI.xlua_tointeger(L, 3);//7
int valueC3 = LuaAPI.xlua_tointeger(L, 2);//10
LuaTypes luaTypeC2 = LuaAPI.lua_type(L, 1);//function
if (LuaAPI.lua_pcall(L, 2, 2, 0) != 0)
{
Debug.LogError(LuaAPI.lua_tostring(L,-1));
}
int value = LuaAPI.xlua_tointeger(L, -1); //3
int value2 = LuaAPI.xlua_tointeger(L, -2); //17
LuaAPI.lua_close(L);
}
Api備注:
LuaAPI.luaL_newstate:開(kāi)辟lua虛擬機(jī),執(zhí)行l(wèi)ua程序 LuaAPI.lua_close:關(guān)閉lua虛擬機(jī),釋放資源 LuaAPI.luaL_loadbuffer:編譯一段lua代碼,但沒(méi)執(zhí)行 LuaAPI.lua_pcall:執(zhí)行l(wèi)ua代碼,這時(shí)候可以用
這里:先把a(bǔ)ddandsub方法壓棧,然后把10和7分別壓棧。所以看到的內(nèi)存里的數(shù)據(jù)如批注所示。
在執(zhí)行第2個(gè)lua_pcall。會(huì)把之前的堆棧信息清空,把函數(shù)執(zhí)行返回的結(jié)果進(jìn)行壓棧操作
1.2 lua訪問(wèn)C#
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void TestCSFunction(IntPtr L);
[MonoPInvokeCallback(typeof(TestCSFunction))]
public static void TestLuaCallCSharp(IntPtr L)
{
Debug.Log("TestLuaCallCSharp");
}
private void Demo2()
{
IntPtr L = LuaAPI.luaL_newstate();
//Marshal 提供對(duì)非托管類(lèi)型的操作
IntPtr function = Marshal.GetFunctionPointerForDelegate(new TestCSFunction(TestLuaCallCSharp));
//函數(shù)入棧
LuaAPI.lua_pushcclosure(L,function,0);
LuaAPI.lua_pcall(L, 0, 0, 0);
LuaAPI.lua_close(L);
}
UnmanagedFunctionPointer:定義為了讓其不受C#托管管理 MonoPInvokeCallback:標(biāo)記可以使其用C或C++調(diào)用
上述可以看到是把C#函數(shù)包裝成指針,然后進(jìn)行壓棧操作供lua調(diào)用
二.xlua中的LuaEnv
private void Demo3()
{
LuaEnv luaenv = new LuaEnv();
luaenv.DoString("CS.UnityEngine.Debug.Log('hello world')");
luaenv.Dispose();
}
LuaEnv:是xlua封裝好的lua環(huán)境,類(lèi)似上面的luaL_newstate。
具體的事情:
XLua框架中最重要的一個(gè)類(lèi),那就是LuaEnv。它包含了lua中的狀態(tài)機(jī)RealStatePrt。lua的G表,還有注冊(cè)表LuaIndexes.LUA_REGISTRYINDEX等等,下面從LuaEnv的構(gòu)造函數(shù)開(kāi)始,看看這個(gè)類(lèi)做了些什么事情
//節(jié)選LuaEnv構(gòu)造函數(shù)部分代碼
//拿到Lua中的注冊(cè)表
LuaIndexes.LUA_REGISTRYINDEX = LuaAPI.xlua_get_registry_index
//創(chuàng)建Lua狀態(tài)機(jī)
rawL = LuaAPI.luaL_newstate()
//十分重要的一個(gè)對(duì)象,用于c#和lua的交互
translator = new ObjectTranslator(this, rawL);
translator.createFunctionMetatable(rawL); //添加_gc元方法到注冊(cè)表
translator.OpenLib(rawL); //將init_xlua中會(huì)用到的方法,全部定義出來(lái)
//添加搜索路徑
AddSearcher(StaticLuaCallbacks.LoadBuiltinLib, 2);
//添加自定義解析Lua文件的方法,對(duì)應(yīng)的是LuaEnv.CustomLoader
AddSearcher(StaticLuaCallbacks.LoadFromCustomLoaders, 3);
#if !XLUA_GENERAL
AddSearcher(StaticLuaCallbacks.LoadFromResource, 4);
AddSearcher(StaticLuaCallbacks.LoadFromStreamingAssetsPath, -1);
#endif
//十分重要??! 初始化xLua
DoString(init_xlua, "Init");
#if !UNITY_SWITCH || UNITY_EDITOR
AddBuildin("socket.core", StaticLuaCallbacks.LoadSocketCore);
AddBuildin("socket", StaticLuaCallbacks.LoadSocketCore);
#endif
AddBuildin("CS", StaticLuaCallbacks.LoadCS);
調(diào)用Init方法:
private string init_xlua = @"
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
--fqn就是類(lèi)型和命名空間名,通過(guò)import_type去獲取對(duì)應(yīng)的udata并且入棧
function metatable:__index(key)
--查詢(xún)key不調(diào)用元方法,更簡(jiǎn)單的表達(dá)只在自己的表內(nèi)查詢(xún)
local fqn = rawget(self,'.fqn')
fqn = ((fqn and fqn .. '.') or '') .. key
--查詢(xún)C#類(lèi)型
local obj = import_type(fqn)
--如果不是 再次查詢(xún)C#命名空間
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
--既然是C#對(duì)象 就不要再newindex了,避免產(chǎn)生未知的錯(cuò)誤
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 這下知道了 typeof是xlua自己搞的,不是lua本身語(yǔ)言特性
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 {[field] = 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[name].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
";
設(shè)置了metatble擴(kuò)充原方法,并且設(shè)置CS的元表示metatable:
__index:會(huì)把C#沒(méi)有緩存的命名空間下的元數(shù)據(jù)導(dǎo)入進(jìn)來(lái)。
__call:與index同理,這里能看出來(lái)了為什么在lua里調(diào)C#方法是CS.UnityEngine.Vector3(x,x,x)。
這步是讓lua可以去訪問(wèn)C#對(duì)象。
因?yàn)樵贚ua中定義了全局的CS表,并且在lua中調(diào)用CS的時(shí)候,會(huì)先調(diào)用StaticLuaCallbacks.LoadCS獲取到注冊(cè)表中的CS表,然后在調(diào)用XXX的時(shí)候,如果訪問(wèn)到了CS表中不存在的元素,則會(huì)調(diào)用其元表,在元表中通過(guò)映射到c#的StaticLuaCallbacks.ImportType方法完成查找。
lua在查找時(shí)調(diào)用了import_type進(jìn)行查找調(diào)用,提前在OpenLib里把方法進(jìn)行注冊(cè):
而importType會(huì)從中轉(zhuǎn)器查詢(xún),查詢(xún)的過(guò)程就是從一個(gè)維護(hù)的字典里看能不能取到,取不到就加載命名空間并注入到lua虛擬機(jī)中。
后面介紹lua與C#之間通信會(huì)詳解介紹
參考:
Lua與C#交互原理(轉(zhuǎn))_lua與c#的交互原理-CSDN博客
https://zhuanlan.zhihu.com/p/441169478
柚子快報(bào)激活碼778899分享:XLua原理(一)
好文閱讀
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。
轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。