柚子快報激活碼778899分享:XLua原理(一)
柚子快報激活碼778899分享:XLua原理(一)
項目中活動都是用xlua開發(fā)的,項目周更熱修也是用xlua的hotfix特性來做的?,F(xiàn)研究底層原理,對于項目性能有個更好的把控。
本文認(rèn)為看到該文章的人已具備使用xlua開發(fā)的能力,只研究介紹下xlua的底層實現(xiàn)原理。
一.lua和c#交互原理
概括:
通過棧來實現(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#訪問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:開辟lua虛擬機(jī),執(zhí)行l(wèi)ua程序 LuaAPI.lua_close:關(guān)閉lua虛擬機(jī),釋放資源 LuaAPI.luaL_loadbuffer:編譯一段lua代碼,但沒執(zhí)行 LuaAPI.lua_pcall:執(zhí)行l(wèi)ua代碼,這時候可以用
這里:先把addandsub方法壓棧,然后把10和7分別壓棧。所以看到的內(nèi)存里的數(shù)據(jù)如批注所示。
在執(zhí)行第2個lua_pcall。會把之前的堆棧信息清空,把函數(shù)執(zhí)行返回的結(jié)果進(jìn)行壓棧操作
1.2 lua訪問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 提供對非托管類型的操作
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)境,類似上面的luaL_newstate。
具體的事情:
XLua框架中最重要的一個類,那就是LuaEnv。它包含了lua中的狀態(tài)機(jī)RealStatePrt。lua的G表,還有注冊表LuaIndexes.LUA_REGISTRYINDEX等等,下面從LuaEnv的構(gòu)造函數(shù)開始,看看這個類做了些什么事情
//節(jié)選LuaEnv構(gòu)造函數(shù)部分代碼
//拿到Lua中的注冊表
LuaIndexes.LUA_REGISTRYINDEX = LuaAPI.xlua_get_registry_index
//創(chuàng)建Lua狀態(tài)機(jī)
rawL = LuaAPI.luaL_newstate()
//十分重要的一個對象,用于c#和lua的交互
translator = new ObjectTranslator(this, rawL);
translator.createFunctionMetatable(rawL); //添加_gc元方法到注冊表
translator.OpenLib(rawL); //將init_xlua中會用到的方法,全部定義出來
//添加搜索路徑
AddSearcher(StaticLuaCallbacks.LoadBuiltinLib, 2);
//添加自定義解析Lua文件的方法,對應(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就是類型和命名空間名,通過import_type去獲取對應(yīng)的udata并且入棧
function metatable:__index(key)
--查詢key不調(diào)用元方法,更簡單的表達(dá)只在自己的表內(nèi)查詢
local fqn = rawget(self,'.fqn')
fqn = ((fqn and fqn .. '.') or '') .. key
--查詢C#類型
local obj = import_type(fqn)
--如果不是 再次查詢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#對象 就不要再newindex了,避免產(chǎn)生未知的錯誤
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本身語言特性
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:會把C#沒有緩存的命名空間下的元數(shù)據(jù)導(dǎo)入進(jìn)來。
__call:與index同理,這里能看出來了為什么在lua里調(diào)C#方法是CS.UnityEngine.Vector3(x,x,x)。
這步是讓lua可以去訪問C#對象。
因為在Lua中定義了全局的CS表,并且在lua中調(diào)用CS的時候,會先調(diào)用StaticLuaCallbacks.LoadCS獲取到注冊表中的CS表,然后在調(diào)用XXX的時候,如果訪問到了CS表中不存在的元素,則會調(diào)用其元表,在元表中通過映射到c#的StaticLuaCallbacks.ImportType方法完成查找。
lua在查找時調(diào)用了import_type進(jìn)行查找調(diào)用,提前在OpenLib里把方法進(jìn)行注冊:
而importType會從中轉(zhuǎn)器查詢,查詢的過程就是從一個維護(hù)的字典里看能不能取到,取不到就加載命名空間并注入到lua虛擬機(jī)中。
后面介紹lua與C#之間通信會詳解介紹
參考:
Lua與C#交互原理(轉(zhuǎn))_lua與c#的交互原理-CSDN博客
https://zhuanlan.zhihu.com/p/441169478
柚子快報激活碼778899分享:XLua原理(一)
好文閱讀
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。