关于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

发布了22 篇原创文章 · 获赞 14 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/a13677972680/article/details/104246319