分析了一下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代码来解析执行。