分析了一下XLua與C#之間的通信方式,發現和SLua,Ulua的區別不是很大。
Lua調用C#:
都是需要先生成一個個wrap文件,C#才能被lua調用。
wrap文件相當於一個接口,Lua先調用 wrap文件 然後 wrap 再調用C#,在 wrap 文件裏面實際上是把C#的類函數,字段壓入到lua虛擬機的虛擬棧上,再由lua虛擬機出棧後給lua調用的。
當索引系統API、dll庫或者第三方庫時,無法將代碼的具體實現進行代碼生成,採用C#的反射方式實現交互,缺點是執行效率低。
也就是說Lua調用C#其實就是:lua->wrap->C#
那麼在XLua中C#又是如何調用Lua的呢?
看源碼很容易知道,其實是使用如下函數:
LuaEnv luaenv = new LuaEnv();//創建Lua虛擬機
luaenv.DoString("CS.UnityEngine.Debug.Log('hello world')");執行lua代碼
根據XLua的文檔,DoString可以直接執行字符串代碼,也可以加載lua文件執行,
分析源碼發現DoString其實是調用的外部DLL中的xluaL_loadbuffer函數,如下 DoString 定義:
public object[] DoString(byte[] chunk, string chunkName = "chunk", LuaTable env = null)
{
#if THREAD_SAFE || HOTFIX_ENABLE
lock (luaEnvLock)
{
#endif
var _L = L;
int oldTop = LuaAPI.lua_gettop(_L);
int errFunc = LuaAPI.load_error_func(_L, errorFuncRef);
if (LuaAPI.xluaL_loadbuffer(_L, chunk, chunk.Length, chunkName) == 0)
{
if (env != null)
{
env.push(_L);
LuaAPI.lua_setfenv(_L, -2);
}
if (LuaAPI.lua_pcall(_L, 0, -1, errFunc) == 0)
{
LuaAPI.lua_remove(_L, errFunc);
return translator.popValues(_L, oldTop);
}
else
ThrowExceptionFromError(oldTop);
}
else
ThrowExceptionFromError(oldTop);
return null;
#if THREAD_SAFE || HOTFIX_ENABLE
}
而 xluaL_loadbuffer 外部引入聲明如下:
[DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
public static extern int xluaL_loadbuffer(IntPtr L, byte[] buff, int size, string name);
也就說xluaL_loadbuffer的函數實現並不在源文件中,而是在外部DLL文件中實現的。
繼續查找發現它其實是xlua.dll裏面的函數,在 xlua.dll 源碼xlua.c文件中發現如下定義:
LUALIB_API int xluaL_loadbuffer (lua_State *L, const char *buff, int size,
const char *name) {
return luaL_loadbuffer(L, buff, size, name);
}
根據文件後綴,其實它就是C代碼,而且調用的luaL_loadbuffer其實就是lua源碼裏面的函數。Xlua只是把它封裝了一下而已。
繼續查找Lua源碼,分析Lua.5.3.3的源碼發現xluaL_loadbuffer 其實是調用了 lua_load 函數,而 lua_load 實現如下:
LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data,
const char *chunkname, const char *mode) {
ZIO z;
int status;
lua_lock(L);
if (!chunkname) chunkname = "?";
luaZ_init(L, &z, reader, data);
status = luaD_protectedparser(L, &z, chunkname, mode);
if (status == LUA_OK) { /* no errors? */
LClosure *f = clLvalue(L->top - 1); /* get newly created function */
if (f->nupvalues >= 1) { /* does it have an upvalue? */
/* get global table from registry */
Table *reg = hvalue(&G(L)->l_registry);
const TValue *gt = luaH_getint(reg, LUA_RIDX_GLOBALS);
/* set global table as 1st upvalue of 'f' (may be LUA_ENV) */
setobj(L, f->upvals[0]->v, gt);
luaC_upvalbarrier(L, f->upvals[0]);
}
}
lua_unlock(L);
return status;
}
上述代碼中調用了luaD_protectedparser來進行parse過程, 在luaD_protectedparser中又調用了f_parser ,在f_parser中根據一些選擇來分別處理不同的情況,這就是lua的詞法語法語義分析過程。
從上述分析發現,其實C#調用lua,就是C#先調用C代碼,然後C調用lua的過程,因爲Lua的源碼是C寫的,lua的代碼需要Lua虛擬機解釋執行,也就是需要C代碼來解析執行。
歡迎批評指正。
首發在原創技術博客:www.fgreen.org