南昌网站设计怎么选,官方微信公众号怎么创建,程序开发步骤不包括,wordpress可视化建站xlua源码分析#xff08;二#xff09;lua Call C#的无wrap实现 上一节我们主要分析了xlua中C# Call lua的实现思路#xff0c;本节我们将根据Examples 03_UIEvent#xff0c;分析lua Call C#的底层实现。例子场景里有一个简单的UI面板#xff0c;面板中包含一个input fie… xlua源码分析二lua Call C#的无wrap实现 上一节我们主要分析了xlua中C# Call lua的实现思路本节我们将根据Examples 03_UIEvent分析lua Call C#的底层实现。例子场景里有一个简单的UI面板面板中包含一个input field一个button 输入任意文本点击button就会打印出输入的内容 响应点击事件的代码是在lua层位于ButtonInteraction.lua.txt这个文件中lua代码很简单就是一个简单的函数
function start()print(lua start...)self:GetComponent(Button).onClick:AddListener(function()print(clicked, you input is ..input:GetComponent(InputField).text ..)end)
end那么C#层从哪里读取到这个文件的呢可以看到Button这个GameObject上绑了上一节我们提到过的LuaBehaviour组件而组件里设置的Lua Script就是这个文件了 上一节我们说过LuaBehaviour组件会在Awake的时候会执行lua代码获取lua层写的start函数然后在MonoBehaviour的Start中执行它。在lua层的start函数中首先可以发现一个self这个self也是在C#层Awake的时候设置的对应的就是C#的LuaBehaviour对象。和tolua一样xlua也会把C#对象当作userdata来处理每个要push到lua层的C#类型都有唯一的type_id对应到不同的metatable用来定义userdata的行为。并且除了值类型和枚举类型之外所有push到lua层的C#对象都会在C#层缓存这一点也是和tolua一样的甚至缓存的数据结构也大差不差。
public void Push(RealStatePtr L, object o)
{if (needcache (is_enum ? enumMap.TryGetValue(o, out index) : reverseMap.TryGetValue(o, out index))){if (LuaAPI.xlua_tryget_cachedud(L, index, cacheRef) 1){return;}}bool is_first;int type_id getTypeId(L, type, out is_first);index addObject(o, is_valuetype, is_enum);LuaAPI.xlua_pushcsobj(L, index, type_id, needcache, cacheRef);
}xlua_tryget_cachedud函数就是通过C#缓存拿到的index去lua层的缓存去拿userdatalua层的缓存与C#不同它只负责查询不负责存储因此是一个value为弱引用的弱表这一点和tolua也是一样的xlua在初始化时就会将这个弱表准备好
LuaAPI.lua_newtable(L);
LuaAPI.lua_newtable(L);
LuaAPI.xlua_pushasciistring(L, __mode);
LuaAPI.xlua_pushasciistring(L, v);
LuaAPI.lua_rawset(L, -3);
LuaAPI.lua_setmetatable(L, -2);
cacheRef LuaAPI.luaL_ref(L, LuaIndexes.LUA_REGISTRYINDEX);由于这个缓存是弱表意味着userdata在被真正gc之前弱表里对应的值有可能已经不存在了。那么xlua_tryget_cachedud这个函数有可能是取不到userdata的
LUA_API int xlua_tryget_cachedud(lua_State *L, int key, int cache_ref) {lua_rawgeti(L, LUA_REGISTRYINDEX, cache_ref);lua_rawgeti(L, -1, key);if (!lua_isnil(L, -1)){lua_remove(L, -2);return 1;}lua_pop(L, 2);return 0;
}取不到的话就通过xlua_pushcsobj这个函数新增一个userdata
static void cacheud(lua_State *L, int key, int cache_ref) {lua_rawgeti(L, LUA_REGISTRYINDEX, cache_ref);lua_pushvalue(L, -2);lua_rawseti(L, -2, key);lua_pop(L, 1);
}LUA_API void xlua_pushcsobj(lua_State *L, int key, int meta_ref, int need_cache, int cache_ref) {int* pointer (int*)lua_newuserdata(L, sizeof(int));*pointer key;if (need_cache) cacheud(L, key, cache_ref);lua_rawgeti(L, LUA_REGISTRYINDEX, meta_ref);lua_setmetatable(L, -2);
}但是xlua设置userdata metatable的做法和tolua完全不同。xlua使用delay wrap的策略即只有某个C#类型的对象push到了lua层才会将这个C#类型的信息真正地加载到lua层在此之前这个metatable并不存在而tolua默认是在一开始就wrap的这样的话类型一多初始化的时间就大大增加而且根据二八定律可能绝大部分的类型在一开始压根用不到。
那么这个delay wrap具体是怎么实现的呢既然它是在C#对象push到lua层触发的那么显而易见在获取这个类的type_id时就要把C#类的信息加载进来了
internal int getTypeId(RealStatePtr L, Type type, out bool is_first, LOGLEVEL log_level LOGLEVEL.WARN)
{int type_id;if (!typeIdMap.TryGetValue(type, out type_id)) // no reference{LuaAPI.luaL_getmetatable(L, alias_type null ? type.FullName : alias_type.FullName);if (LuaAPI.lua_isnil(L, -1)) //no meta yet, try to use reflection meta{LuaAPI.lua_pop(L, 1);if (TryDelayWrapLoader(L, alias_type null ? type : alias_type)){LuaAPI.luaL_getmetatable(L, alias_type null ? type.FullName : alias_type.FullName);}else{throw new Exception(Fatal: can not load metatable of type: type);}}typeIdMap.Add(type, type_id);}return type_id;
}负责这件事情的函数就是TryDelayWrapLoader。在例子中由于我们没有生成过类的wrap默认就会使用反射的方式来注册各种C#方法与成员。具体实现的逻辑比较复杂主要在ReflectionWrap这个函数中
public static void ReflectionWrap(RealStatePtr L, Type type, bool privateAccessible)
{LuaAPI.lua_checkstack(L, 20);int top_enter LuaAPI.lua_gettop(L);ObjectTranslator translator ObjectTranslatorPool.Instance.Find(L);//create obj meta tableLuaAPI.luaL_getmetatable(L, type.FullName);if (LuaAPI.lua_isnil(L, -1)){LuaAPI.lua_pop(L, 1);LuaAPI.luaL_newmetatable(L, type.FullName);}LuaAPI.lua_pushlightuserdata(L, LuaAPI.xlua_tag());LuaAPI.lua_pushnumber(L, 1);LuaAPI.lua_rawset(L, -3);int obj_meta LuaAPI.lua_gettop(L);LuaAPI.lua_newtable(L);int cls_meta LuaAPI.lua_gettop(L);LuaAPI.lua_newtable(L);int obj_field LuaAPI.lua_gettop(L);LuaAPI.lua_newtable(L);int obj_getter LuaAPI.lua_gettop(L);LuaAPI.lua_newtable(L);int obj_setter LuaAPI.lua_gettop(L);LuaAPI.lua_newtable(L);int cls_field LuaAPI.lua_gettop(L);//set cls_field to namespaceSetCSTable(L, type, cls_field);//finish set cls_field to namespaceLuaAPI.lua_newtable(L);int cls_getter LuaAPI.lua_gettop(L);LuaAPI.lua_newtable(L);int cls_setter LuaAPI.lua_gettop(L);LuaCSFunction item_getter;LuaCSFunction item_setter;makeReflectionWrap(L, type, cls_field, cls_getter, cls_setter, obj_field, obj_getter, obj_setter, obj_meta,out item_getter, out item_setter, privateAccessible ? (BindingFlags.Public | BindingFlags.NonPublic) : BindingFlags.Public);// init obj metatableLuaAPI.xlua_pushasciistring(L, __gc);LuaAPI.lua_pushstdcallcfunction(L, translator.metaFunctions.GcMeta);LuaAPI.lua_rawset(L, obj_meta);LuaAPI.xlua_pushasciistring(L, __tostring);LuaAPI.lua_pushstdcallcfunction(L, translator.metaFunctions.ToStringMeta);LuaAPI.lua_rawset(L, obj_meta);LuaAPI.xlua_pushasciistring(L, __index);LuaAPI.lua_pushvalue(L, obj_field);LuaAPI.lua_pushvalue(L, obj_getter);translator.PushFixCSFunction(L, item_getter);translator.PushAny(L, type.BaseType());LuaAPI.xlua_pushasciistring(L, LuaIndexsFieldName);LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);LuaAPI.lua_pushnil(L);LuaAPI.gen_obj_indexer(L);//store in lua indexs function tablesLuaAPI.xlua_pushasciistring(L, LuaIndexsFieldName);LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);translator.Push(L, type);LuaAPI.lua_pushvalue(L, -3);LuaAPI.lua_rawset(L, -3);LuaAPI.lua_pop(L, 1);LuaAPI.lua_rawset(L, obj_meta); // set __indexLuaAPI.xlua_pushasciistring(L, __newindex);LuaAPI.lua_pushvalue(L, obj_setter);translator.PushFixCSFunction(L, item_setter);translator.Push(L, type.BaseType());LuaAPI.xlua_pushasciistring(L, LuaNewIndexsFieldName);LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);LuaAPI.lua_pushnil(L);LuaAPI.gen_obj_newindexer(L);//store in lua newindexs function tablesLuaAPI.xlua_pushasciistring(L, LuaNewIndexsFieldName);LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);translator.Push(L, type);LuaAPI.lua_pushvalue(L, -3);LuaAPI.lua_rawset(L, -3);LuaAPI.lua_pop(L, 1);LuaAPI.lua_rawset(L, obj_meta); // set __newindex//finish init obj metatableLuaAPI.xlua_pushasciistring(L, UnderlyingSystemType);translator.PushAny(L, type);LuaAPI.lua_rawset(L, cls_field);if (type ! null type.IsEnum()){LuaAPI.xlua_pushasciistring(L, __CastFrom);translator.PushFixCSFunction(L, genEnumCastFrom(type));LuaAPI.lua_rawset(L, cls_field);}//init class metaLuaAPI.xlua_pushasciistring(L, __index);LuaAPI.lua_pushvalue(L, cls_getter);LuaAPI.lua_pushvalue(L, cls_field);translator.Push(L, type.BaseType());LuaAPI.xlua_pushasciistring(L, LuaClassIndexsFieldName);LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);LuaAPI.gen_cls_indexer(L);//store in lua indexs function tablesLuaAPI.xlua_pushasciistring(L, LuaClassIndexsFieldName);LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);translator.Push(L, type);LuaAPI.lua_pushvalue(L, -3);LuaAPI.lua_rawset(L, -3);LuaAPI.lua_pop(L, 1);LuaAPI.lua_rawset(L, cls_meta); // set __index LuaAPI.xlua_pushasciistring(L, __newindex);LuaAPI.lua_pushvalue(L, cls_setter);translator.Push(L, type.BaseType());LuaAPI.xlua_pushasciistring(L, LuaClassNewIndexsFieldName);LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);LuaAPI.gen_cls_newindexer(L);//store in lua newindexs function tablesLuaAPI.xlua_pushasciistring(L, LuaClassNewIndexsFieldName);LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);translator.Push(L, type);LuaAPI.lua_pushvalue(L, -3);LuaAPI.lua_rawset(L, -3);LuaAPI.lua_pop(L, 1);LuaAPI.lua_rawset(L, cls_meta); // set __newindexLuaCSFunction constructor typeof(Delegate).IsAssignableFrom(type) ? translator.metaFunctions.DelegateCtor : translator.methodWrapsCache.GetConstructorWrap(type);if (constructor null){constructor (RealStatePtr LL) {return LuaAPI.luaL_error(LL, No constructor for type);};}LuaAPI.xlua_pushasciistring(L, __call);translator.PushFixCSFunction(L, constructor);LuaAPI.lua_rawset(L, cls_meta);LuaAPI.lua_pushvalue(L, cls_meta);LuaAPI.lua_setmetatable(L, cls_field);LuaAPI.lua_pop(L, 8);System.Diagnostics.Debug.Assert(top_enter LuaAPI.lua_gettop(L));
}相比于tolua只使用两个tablexlua使用了若干的table来辅助索引查找C#的方法和成员。从代码中可以看出cls_metacls_fieldcls_getter和cls_setter是用直接给类访问用的比如一些静态的方法与成员lua层可以通过namespace和类名直接访问。而相应地obj_metaobj_fieldobj_getter和obj_setter是给userdata访问用的对应C#层实例方法与成员。从命名中也可看出field对应的是C#的字段和方法getter对应的是C#的get属性setter对应的是set属性meta就是对外设置的metatable了。cls_meta中包含__index__newindex__call这三个元方法这样lua层就可以通过类名创建一个C#对象obj_meta中包含__index__newindex__gc__tostring这四个元方法并且它就是userdata的type_id。__index__newindex这两个元方法还会通过registry表记录对应的type来进行额外的缓存这么做的目的主要是为了基类查找xlua不像tolua一样嵌套使用多个metatable来实现继承机制。
那么fieldgettersetter这三种table是如何跟meta进行关联的呢xlua使用了一种非常巧妙的机制以userdata的__index为例它其实对应着一个函数这个函数使用包含fieldgettersetter这三种table在内以及其他的一些参数作为upvalue来引用。
LUA_API int gen_obj_indexer(lua_State *L) {lua_pushnil(L);lua_pushcclosure(L, obj_indexer, 7);return 0;
}obj_indexer这个函数持有了7个upvalue是有点多注释里也标明了每个upvalue的用途
//upvalue --- [1]: methods, [2]:getters, [3]:csindexer, [4]:base, [5]:indexfuncs, [6]:arrayindexer, [7]:baseindex
//param --- [1]: obj, [2]: key
LUA_API int obj_indexer(lua_State *L) { if (!lua_isnil(L, lua_upvalueindex(1))) {lua_pushvalue(L, 2);lua_gettable(L, lua_upvalueindex(1));if (!lua_isnil(L, -1)) {//has methodreturn 1;}lua_pop(L, 1);}if (!lua_isnil(L, lua_upvalueindex(2))) {lua_pushvalue(L, 2);lua_gettable(L, lua_upvalueindex(2));if (!lua_isnil(L, -1)) {//has getterlua_pushvalue(L, 1);lua_call(L, 1, 1);return 1;}lua_pop(L, 1);}if (!lua_isnil(L, lua_upvalueindex(6)) lua_type(L, 2) LUA_TNUMBER) {lua_pushvalue(L, lua_upvalueindex(6));lua_pushvalue(L, 1);lua_pushvalue(L, 2);lua_call(L, 2, 1);return 1;}if (!lua_isnil(L, lua_upvalueindex(3))) {lua_pushvalue(L, lua_upvalueindex(3));lua_pushvalue(L, 1);lua_pushvalue(L, 2);lua_call(L, 2, 2);if (lua_toboolean(L, -2)) {return 1;}lua_pop(L, 2);}if (!lua_isnil(L, lua_upvalueindex(4))) {lua_pushvalue(L, lua_upvalueindex(4));while(!lua_isnil(L, -1)) {lua_pushvalue(L, -1);lua_gettable(L, lua_upvalueindex(5));if (!lua_isnil(L, -1)) // found{lua_replace(L, lua_upvalueindex(7)); //baseindex indexfuncs[base]lua_pop(L, 1);break;}lua_pop(L, 1);lua_getfield(L, -1, BaseType);lua_remove(L, -2);}lua_pushnil(L);lua_replace(L, lua_upvalueindex(4));//base nil}if (!lua_isnil(L, lua_upvalueindex(7))) {lua_settop(L, 2);lua_pushvalue(L, lua_upvalueindex(7));lua_insert(L, 1);lua_call(L, 2, 1);return 1;} else {return 0;}
}我们着重看一下第4个upvalue的情况走到这里说明在当前类中没有查找到例子中的GetComponent方法是在Component类里在LuaBehaviour类里自然是查找不到的那么就需要不断地往父类查找。第4个upvalue是当前类的基类类型base type第5个upvalue就是缓存了当前所有type的__index元方法函数那么自然而然就要去这个缓存中查找base type的__index元方法然后把事情直接交给它做就好了这其实就是一个递归的做法。为了避免下次还要从缓存中查找基类这里直接把第4个upvalue置为空然后把基类的__index元方法缓存到第7个upvalue上。
那问题来了我们之前提到xlua是delay wrap的在访问C#对象的时候它的基类信息很可能还没wrap到lua层。所以这里也需要获取一下基类的type_id。在从缓存中获取__index元方法时代码中使用的是
lua_gettable(L, lua_upvalueindex(5));lua_gettable是会触发metatable的这个缓存table在xlua初始化时就设置了一个metatable
LuaAPI.lua_newtable(rawL); //metatable of indexs and newindexs functions
LuaAPI.xlua_pushasciistring(rawL, __index);
LuaAPI.lua_pushstdcallcfunction(rawL, StaticLuaCallbacks.MetaFuncIndex);
LuaAPI.lua_rawset(rawL, -3);LuaAPI.xlua_pushasciistring(rawL, Utils.LuaIndexsFieldName);
LuaAPI.lua_newtable(rawL);
LuaAPI.lua_pushvalue(rawL, -3);
LuaAPI.lua_setmetatable(rawL, -2);
LuaAPI.lua_rawset(rawL, LuaIndexes.LUA_REGISTRYINDEX);因此如果基类信息还没wrap就会触发到C#层的MetaFuncIndex方法
public static int MetaFuncIndex(RealStatePtr L)
{try{ObjectTranslator translator ObjectTranslatorPool.Instance.Find(L);Type type translator.FastGetCSObj(L, 2) as Type;if (type null){return LuaAPI.luaL_error(L, #2 param need a System.Type!);}translator.GetTypeId(L, type);LuaAPI.lua_pushvalue(L, 2);LuaAPI.lua_rawget(L, 1);return 1;}catch (System.Exception e){return LuaAPI.luaL_error(L, c# exception in MetaFuncIndex: e);}
}这个函数首先会从lua层获取当前要wrap的type生成唯一的type_id并把类型信息wrap到lua层然后再使用一次rawget把__index方法放回lua层这样lua层就可以继续递归查找了。在例子中想要调用到GetComponent得沿着LuaBehaviourMonoBehaviourBehaviourComponent这条链一直查找3次才能找到。
最后push到lua层的这些C#函数都是使用PushFixCSFunction这个方法完成的这个方法把push到lua层的函数统一放到一个list中管理实际调用时根据list中的索引触发具体的某个函数
internal void PushFixCSFunction(RealStatePtr L, LuaCSFunction func)
{if (func null){LuaAPI.lua_pushnil(L);}else{LuaAPI.xlua_pushinteger(L, fix_cs_functions.Count);fix_cs_functions.Add(func);LuaAPI.lua_pushstdcallcfunction(L, metaFunctions.FixCSFunctionWraper, 1);}
}static int FixCSFunction(RealStatePtr L)
{try{ObjectTranslator translator ObjectTranslatorPool.Instance.Find(L);int idx LuaAPI.xlua_tointeger(L, LuaAPI.xlua_upvalueindex(1));LuaCSFunction func (LuaCSFunction)translator.GetFixCSFunction(idx);return func(L);}catch (Exception e){return LuaAPI.luaL_error(L, c# exception in FixCSFunction: e);}
}推测这么做的原因可能是为了少一些MonoPInvokeCallback吧