關於XLua與C#之間的通信分析

分析了一下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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章