0921学习 agile Posted on Sep 21 2023 面试 lua xlua ```lua ... function metatable:__index(key) ... local obj = import_type(fqn) if obj == nil then -- It might be an assembly, so we load it too. obj = { ['.fqn'] = fqn } setmetatable(obj, metatable) elseif obj == true then return rawget(self, key) end ... end CS = CS or {} //cs绑定的元表 setmetatable(CS, metatable) --- ``` ### `import_type`简单实现(`StaticLuaCallbacks.ImportType`): --- ```C# ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L); string className = LuaAPI.lua_tostring(L, 1); Type type = translator.FindType(className); if (type != null) { if (translator.GetTypeId(L, type) >= 0) { LuaAPI.lua_pushboolean(L, true); } else { return LuaAPI.luaL_error(L, "can not load type " + type); } } else { LuaAPI.lua_pushnil(L); } return 1; ``` - 该方法是一个闭包,有一个返回值,如果类型存在就返回`true`,不然就返回`nil` - `translator.GetTypeId`:返回值是`type_id`,该`type_id`也就是该类型对应的table在注册表中的`key`,,可以这样表示:`LUA_REGISTRYINDEX[type_id] = type_table`,也可以这样表示`LUA_REGISTRYINDEX[type.FullName] = type_table` --- ### GetTypeId方法实现: --- ```C# internal int getTypeId(RealStatePtr L, Type type, out bool is_first, LOGLEVEL log_level = LOGLEVEL.WARN) { int type_id; is_first = false; //判断是否已经访问过该类型,如果访问过就会将类型保存在typeIdMap中 if (!typeIdMap.TryGetValue(type, out type_id)) // no reference { //判断是否是数组,数组在初始化LuaEnv的时候就 common_array_meta = LuaAPI.luaL_ref(L, LuaIndexes.LUA_REGISTRYINDEX);被赋值了。 //CreateArrayMetatable在这个方法中注册实例方法到type_table中 if (type.IsArray) { if (common_array_meta == -1) throw new Exception("Fatal Exception! Array Metatable not inited!"); return common_array_meta; } //判断是否是委托,委托也是在初始化的LuaEnv的时候就被赋值了common_delegate_meta = LuaAPI.luaL_ref(L, LuaIndexes.LUA_REGISTRYINDEX); //CreateDelegateMetatable在这个方法中注册了实例到type_table中 if (typeof(MulticastDelegate).IsAssignableFrom(type)) { if (common_delegate_meta == -1) throw new Exception("Fatal Exception! Delegate Metatable not inited!"); TryDelayWrapLoader(L, type); return common_delegate_meta; } is_first = true; Type alias_type = null; aliasCfg.TryGetValue(type, out alias_type); //将注册表中以type.FullName为key的元表push到栈顶. LuaAPI.luaL_getmetatable(L, alias_type == null ? type.FullName : alias_type.FullName); //发现栈顶为nil,也就是目前没有以type.FullName为key的元表 if (LuaAPI.lua_isnil(L, -1)) //no meta yet, try to use reflection meta { //将该nil推出栈 LuaAPI.lua_pop(L, 1); //通过wraploader加载该type if (TryDelayWrapLoader(L, alias_type == null ? type : alias_type)) { //加载成功后,将type_table推到栈顶 LuaAPI.luaL_getmetatable(L, alias_type == null ? type.FullName : alias_type.FullName); } else { throw new Exception("Fatal: can not load metatable of type:" + type); } } //循环依赖,自身依赖自己的class,比如有个自身类型的静态readonly对象。 if (typeIdMap.TryGetValue(type, out type_id)) { LuaAPI.lua_pop(L, 1); } else { if (type.IsEnum()) { LuaAPI.xlua_pushasciistring(L, "__band"); LuaAPI.lua_pushstdcallcfunction(L, metaFunctions.EnumAndMeta); LuaAPI.lua_rawset(L, -3); LuaAPI.xlua_pushasciistring(L, "__bor"); LuaAPI.lua_pushstdcallcfunction(L, metaFunctions.EnumOrMeta); LuaAPI.lua_rawset(L, -3); } if (typeof(IEnumerable).IsAssignableFrom(type)) { LuaAPI.xlua_pushasciistring(L, "__pairs"); //从注册表中获取key为enumerable_pairs_func的值,并将他推到栈顶 LuaAPI.lua_getref(L, enumerable_pairs_func); //从虚拟栈中"-3"处获得"table",栈顶获得"value", //栈顶下面一个元素获得"key"。相当于在Lua环境中执行"table[key] = value"命令 //函数在执行结束后,会弹出"key"和"value"。 LuaAPI.lua_rawset(L, -3); } //再插入一个type_table LuaAPI.lua_pushvalue(L, -1); //生成一个唯一的"key",将栈顶的值出栈作为"value",从虚拟栈的索引"LuaIndexes.LUA_REGISTRYINDEX"处获得"table",之后做相当于"table[key] = value"的操作 //函数返回生成的"key" //相当于LUA_REGISTRYINDEX[type_id]=type_table type_id = LuaAPI.luaL_ref(L, LuaIndexes.LUA_REGISTRYINDEX); LuaAPI.lua_pushnumber(L, type_id); //为table中的key赋值. t[n] = v .其中t是index处的table , v为栈顶元素 //调用完成后弹出栈顶元素. //也就是type_table[1]=type_id LuaAPI.xlua_rawseti(L, -2, 1); LuaAPI.lua_pop(L, 1); //如果是值类型将type_id加入到typeMap //是个struct if (type.IsValueType()) { typeMap.Add(type_id, type); } //加入到typeIdMap typeIdMap.Add(type, type_id); } } return type_id; } ``` --- ###`xlua_gettypeid`方法与`getTypeId`方法联动 --- ```c LUA_API int xlua_gettypeid(lua_State *L, int idx) { int type_id = -1; //如果lua类型是userdata类型 if (lua_type(L, idx) == LUA_TUSERDATA) { //获取该userdata的对应的元表也就是type_table if (lua_getmetatable (L, idx)) { /从虚拟栈中"index"处获得"table",从Lua环境中获取"table[n]"的值, //也就是将type_table[1]推到栈顶 //在getTypeId中刚好设置了一发type_table[1]=type_id,所以获取的值就是type_id lua_rawgeti(L, -1, 1); if (lua_type(L, -1) == LUA_TNUMBER) { type_id = (int)lua_tointeger(L, -1); } lua_pop(L, 2); } } return type_id; } ``` --- - `getTypeId`方法中会将`type_id`保存到`type_table`中 - `xlua_gettypeid`通过判断是否是userdata,来获取该userdata的`metatable`,而`metatable`也就是对应的`type_table`,而`type_table`中保存有`type_id` - `getTypeId`方法中如果遇到没有加载过的类型,会通过`TryDelayWrapLoader`方法加载 - `TryDelayWrapLoader`方法重点就在于`delayWrap`这个字段,他的类型是`Dictionary<Type, Action<RealStatePtr>>`,根据`type`,获取对应`action`,直接执行该`action`,就能加载 - `delayWrap`中的action是在`XLua_Gen_Initer_Register__`这个类中注册加入的,他是个自动生成的类,有个静态构造方法。 ```C# static void wrapInit0(LuaEnv luaenv, ObjectTranslator translator){ //添加__register方法到delayWrap字典中 translator.DelayWrapLoader(typeof(object), SystemObjectWrap.__Register); } static void Init(LuaEnv luaenv, ObjectTranslator translator) { wrapInit0(luaenv, translator); translator.AddInterfaceBridgeCreator(typeof(System.Collections.IEnumerator), SystemCollectionsIEnumeratorBridge.__Create); ... } static XLua_Gen_Initer_Register__() { //将init回调方法注入到luaenv中,在luaenv初始化大时候会执行该init方法 XLua.LuaEnv.AddIniter(Init); } ``` --- - action方法也就是对应**Wrap__Register方法,将对应类型相关数据注入 --- ###`**Wrap__Register`方法 --- ```C# public static void __Register(RealStatePtr L) { ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L); System.Type type = typeof(Tutorial.BaseClass); //开始准备注入实例前期准备 Utils.BeginObjectRegister(type, L, translator, 0, 2, 1, 1); //将方法注入到METHOD_IDX table中 Utils.RegisterFunc(L, Utils.METHOD_IDX, "BMFunc", _m_BMFunc); Utils.RegisterFunc(L, Utils.METHOD_IDX, "GetSomeBaseData", _m_GetSomeBaseData); //将方法注入到GETTER_IDX table中 Utils.RegisterFunc(L, Utils.GETTER_IDX, "BMF", _g_get_BMF); Utils.RegisterFunc(L, Utils.SETTER_IDX, "BMF", _s_set_BMF); //设置该metable的__index,以及__newIndex Utils.EndObjectRegister(type, L, translator, null, null, null, null, null); //开始准备注入静态类变量 Utils.BeginClassRegister(type, L, __CreateInstance, 2, 1, 1); //注册方法到CLS_IDX table中 Utils.RegisterFunc(L, Utils.CLS_IDX, "BSFunc", _m_BSFunc_xlua_st_); //注册方法到CLS_GETTER_IDX table中 Utils.RegisterFunc(L, Utils.CLS_GETTER_IDX, "BSF", _g_get_BSF); //注册方法到CLS_SETTER_IDX table中 Utils.RegisterFunc(L, Utils.CLS_SETTER_IDX, "BSF", _s_set_BSF); //设置该metable的__index,以及__newIndex Utils.EndClassRegister(type, L, translator); } ``` --- ###`BeginObjectRegister` --- ```C# //meta: -4, method:-3, getter: -2, setter: -1 public static void BeginObjectRegister(Type type, RealStatePtr L, ObjectTranslator translator, int meta_count, int method_count, int getter_count, int setter_count, int type_id = -1) { //这一段主要就是要将type_table推到栈顶 //分别通过type_id,或者type.FullName来实现 if (type == null) { if (type_id == -1) throw new Exception("Fatal: must provide a type of type_id"); LuaAPI.xlua_rawgeti(L, LuaIndexes.LUA_REGISTRYINDEX, type_id); } else { LuaAPI.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); //为table中的key赋值. t[k] = v . 其中t是index处的table , v为栈顶元素. k为-2处的元素. //也就是type_table[xlua_tag()]=1 LuaAPI.lua_rawset(L, -3); if ((type == null || !translator.HasCustomOp(type)) && type != typeof(decimal)) { LuaAPI.xlua_pushasciistring(L, "__gc"); LuaAPI.lua_pushstdcallcfunction(L, translator.metaFunctions.GcMeta); //type_table["__gc"] = translator.metaFunctions.GcMeta LuaAPI.lua_rawset(L, -3); } LuaAPI.xlua_pushasciistring(L, "__tostring"); LuaAPI.lua_pushstdcallcfunction(L, translator.metaFunctions.ToStringMeta); //type_table["__tostring"] = translator.metaFunctions.ToStringMeta LuaAPI.lua_rawset(L, -3); if (method_count == 0) { LuaAPI.lua_pushnil(L); } else { //创建method table LuaAPI.lua_createtable(L, 0, method_count); } if (getter_count == 0) { LuaAPI.lua_pushnil(L); } else { //创建get_table LuaAPI.lua_createtable(L, 0, getter_count); } if (setter_count == 0) { LuaAPI.lua_pushnil(L); } else { //创建set_table LuaAPI.lua_createtable(L, 0, setter_count); } } ``` --- - 执行完`BeginObjectRegister`方法,栈中含有四个table,分别是`type_table,method_table,getter_table,setter_table)` - `type_table`结构目前如下: ```lua type_table = { tag = 1, __gc = translator.metaFunctions.GcMeta, __tostring = translator.metaFunctions.ToStringMeta, [1] = type_id, --在getTypeId()中添加的 } ``` --- ###`RegisterFunc` --- ```C# public static void RegisterFunc(RealStatePtr L, int idx, string name, LuaCSFunction func) { idx = abs_idx(LuaAPI.lua_gettop(L), idx); LuaAPI.xlua_pushasciistring(L, name); LuaAPI.lua_pushstdcallcfunction(L, func); //对应idx的table中注入方法 LuaAPI.lua_rawset(L, idx); } ``` --- ###`EndObjectRegister` --- ```C# public static void EndObjectRegister(Type type, RealStatePtr L, ObjectTranslator translator, LuaCSFunction csIndexer, LuaCSFunction csNewIndexer, Type base_type, LuaCSFunction arrayIndexer, LuaCSFunction arrayNewIndexer) { int top = LuaAPI.lua_gettop(L); //获取栈中对应index,原先是-4,-3,-2,-1,转换成正数 int meta_idx = abs_idx(top, OBJ_META_IDX); int method_idx = abs_idx(top, METHOD_IDX); int getter_idx = abs_idx(top, GETTER_IDX); int setter_idx = abs_idx(top, SETTER_IDX); //begin index gen LuaAPI.xlua_pushasciistring(L, "__index"); //method table LuaAPI.lua_pushvalue(L, method_idx); //getter table LuaAPI.lua_pushvalue(L, getter_idx); //csIndexer 方法 LuaAPI.lua_pushstdcallcfunction(L, csIndexer); //basetype userdata translator.Push(L, type == null ? base_type : type.BaseType()); //LUA_REGISTRYINDEX["LuaIndexs"] table LuaAPI.xlua_pushasciistring(L, LuaIndexsFieldName); LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX); //arrayIndexer方法 LuaAPI.lua_pushstdcallcfunction(L, arrayIndexer); //生成obj_indexer闭包 //upvalue有method table,getter table,csIndexer 方法,basetype userdata,LUA_REGISTRYINDEX["LuaIndexs"] table,arrayIndexer方法,nil LuaAPI.gen_obj_indexer(L); if (type != null) { LuaAPI.xlua_pushasciistring(L, LuaIndexsFieldName); LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX); //store in lua indexs function tables translator.Push(L, type); //注入obj_indexer闭包 LuaAPI.lua_pushvalue(L, -3); //LUA_REGISTRYINDEX["LuaIndexs"] = { // [type userdata] = obj_indexer, //} //LUA_REGISTRYINDEX["LuaIndexs"]这个table在luaEnv中创建,其metable为{__index=StaticLuaCallbacks.MetaFuncIndex} LuaAPI.lua_rawset(L, -3); LuaAPI.lua_pop(L, 1); } //type_table["__index"] = obj_indexer LuaAPI.lua_rawset(L, meta_idx); //end index gen //begin newindex gen LuaAPI.xlua_pushasciistring(L, "__newindex"); //setter table LuaAPI.lua_pushvalue(L, setter_idx); //csNewIndexer方法 LuaAPI.lua_pushstdcallcfunction(L, csNewIndexer); //basetype userdata translator.Push(L, type == null ? base_type : type.BaseType()); //LUA_REGISTRYINDEX["LuaNewIndexs"] table LuaAPI.xlua_pushasciistring(L, LuaNewIndexsFieldName); LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX); //arrayNewIndexer方法 LuaAPI.lua_pushstdcallcfunction(L, arrayNewIndexer); //生成obj_newindexer闭包 //upvalue有setter table,csNewIndexer方法,basetype userdata,LUA_REGISTRYINDEX["LuaNewIndexs"] table,arrayNewIndexer方法,nil LuaAPI.gen_obj_newindexer(L); if (type != null) { LuaAPI.xlua_pushasciistring(L, LuaNewIndexsFieldName); LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX); //store in lua newindexs function tables translator.Push(L, type); //注入obj_newindexer闭包 LuaAPI.lua_pushvalue(L, -3); //LUA_REGISTRYINDEX["LuaNewIndexs"] = { // [type userdata] = obj_newindexer, //} //LUA_REGISTRYINDEX["LuaNewIndexs"]这个table在luaEnv中创建,其metable为{__index=StaticLuaCallbacks.MetaFuncIndex} LuaAPI.lua_rawset(L, -3); LuaAPI.lua_pop(L, 1); } //type_table["__newIndex"] = obj_newindexer LuaAPI.lua_rawset(L, meta_idx); //end new index gen LuaAPI.lua_pop(L, 4); } ``` --- - 从上面可以看出`EndObjectRegister`主要就是为`type_table`生成index,newindex闭包,以及将这两个闭包保存到`LUA_REGISTRYINDEX["LuaNewIndexs"]` 以及`LUA_REGISTRYINDEX["LuaIndexs"]`table中 - `LUA_REGISTRYINDEX["LuaNewIndexs"] = { [type userdata] = obj_newindexer, }` - `LUA_REGISTRYINDEX["LuaIndexs"] = { [type userdata] = obj_indexer, }` - 如果要查找type中某个字段,会通过index这个闭包,到upvalue中查找 - `type_table`结构目前如下: ```lua type_table = { tag = 1, __gc = translator.metaFunctions.GcMeta, __tostring = translator.metaFunctions.ToStringMeta, [1] = type_id, --在getTypeId()中添加的 __newIndex = obj_newindexer, __index = obj_indexer, } ``` --- ###BeginClassRegister --- ```C# public static void BeginClassRegister(Type type, RealStatePtr L, LuaCSFunction creator, int class_field_count, int static_getter_count, int static_setter_count) { ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L); //创建class_field table LuaAPI.lua_createtable(L, 0, class_field_count); LuaAPI.xlua_pushasciistring(L, "UnderlyingSystemType"); //将type userdata推到栈顶 translator.PushAny(L, type); //class_field["UnderlyingSystemType"] = type userdata LuaAPI.lua_rawset(L, -3); int cls_table = LuaAPI.lua_gettop(L); //将cls_field table放到CS table中 //CS = { // [path] = cls_field, // [type userdata] = cls_field, //} SetCSTable(L, type, cls_table); //cls_field_meta table LuaAPI.lua_createtable(L, 0, 3); int meta_table = LuaAPI.lua_gettop(L); //creator也就是__CreateInstance方法 if (creator != null) { LuaAPI.xlua_pushasciistring(L, "__call"); LuaAPI.lua_pushstdcallcfunction(L, creator); //cls_field_meta["__call"] = __CreateInstance LuaAPI.lua_rawset(L, -3); } //static_getter table LuaAPI.lua_createtable(L, 0, static_getter_count); //static_setter table LuaAPI.lua_createtable(L, 0, static_setter_count); LuaAPI.lua_pushvalue(L, meta_table); //设置cls_field table的元表为 cls_field_meta table LuaAPI.lua_setmetatable(L, cls_table); } ``` - `BeginClassRegister`方法主要创建了四个table,分别是`cls_field`,`cls_field_meta`,`static_getter`,`static_setter`,可以将类方法或者类字段放入到`cls_field`,`static_getter`,`static_setter`table中。 - `CS` 就是`LUA_REGISTRYINDEX[CSHARP_NAMESPACE]`table,在luaEnv初始化的时候赋值给`CS` - `cls_field_meta["__call"] = __CreateInstance` 注意__CreateInstance方法还是很重要的,在创建一个类的时候就会调用到该方法,就会将对应的实例PushAny - `class_field` table 当前长这样`class_field["UnderlyingSystemType"] = type userdata` - `SetCSTable`方法会根据type类型名,以及包名,将cls_field放到对应位置。大体是这样 ```lua CS = { UnityEngine = { ["typename"] = cls_field, }, [type userdata] = cls_field, } ``` - 可以看到`cls_field`,可以通过`typename` 或者`type userdata`找到 --- ###`EndClassRegister` --- ```C# public static void EndClassRegister(Type type, RealStatePtr L, ObjectTranslator translator) { int top = LuaAPI.lua_gettop(L); //cls_field table的index int cls_idx = abs_idx(top, CLS_IDX); //static_getter table的index int cls_getter_idx = abs_idx(top, CLS_GETTER_IDX); //static_setter table的index int cls_setter_idx = abs_idx(top, CLS_SETTER_IDX); //cls_field_meta table的index int cls_meta_idx = abs_idx(top, CLS_META_IDX); //begin cls index LuaAPI.xlua_pushasciistring(L, "__index"); //static_getter LuaAPI.lua_pushvalue(L, cls_getter_idx); //cls_field LuaAPI.lua_pushvalue(L, cls_idx); //basetype translator.Push(L, type.BaseType()); LuaAPI.xlua_pushasciistring(L, LuaClassIndexsFieldName); //LUA_REGISTRYINDEX["LuaClassIndexs"] LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX); //生成cls_indexer闭包 //upvalue分别是static_getter,cls_field,basetype,LUA_REGISTRYINDEX["LuaClassIndexs"],nil LuaAPI.gen_cls_indexer(L); LuaAPI.xlua_pushasciistring(L, LuaClassIndexsFieldName); LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX); //store in lua indexs function tables translator.Push(L, type); //LUA_REGISTRYINDEX["LuaClassIndexs"] = { // [type userdata] = cls_indexer //} LuaAPI.lua_pushvalue(L, -3); LuaAPI.lua_rawset(L, -3); LuaAPI.lua_pop(L, 1); //cls_field_meta["__index"]=cls_indexer LuaAPI.lua_rawset(L, cls_meta_idx); //end cls index //begin cls newindex LuaAPI.xlua_pushasciistring(L, "__newindex"); //static_setter table LuaAPI.lua_pushvalue(L, cls_setter_idx); //basetype userdata translator.Push(L, type.BaseType()); LuaAPI.xlua_pushasciistring(L, LuaClassNewIndexsFieldName); //LUA_REGISTRYINDEX["LuaClassNewIndexs"] table LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX); //生成cls_newindexer闭包 //upvalue分别是static_setter,basetype userdata,LUA_REGISTRYINDEX["LuaClassNewIndexs"] ,nil LuaAPI.gen_cls_newindexer(L); LuaAPI.xlua_pushasciistring(L, LuaClassNewIndexsFieldName); LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX); //store in lua newindexs function tables translator.Push(L, type); LuaAPI.lua_pushvalue(L, -3); //LUA_REGISTRYINDEX["LuaClassNewIndexs"]={ // [type userdata] = cls_newindexer //} LuaAPI.lua_rawset(L, -3); LuaAPI.lua_pop(L, 1); //cls_field_meta["__newindex"]=cls_newindexer LuaAPI.lua_rawset(L, cls_meta_idx); //end cls newindex LuaAPI.lua_pop(L, 4); } ``` --- - `EndClassRegister`方法中主要是给cls_field_meta添加`index`,以及`__newIndex`两个闭包 - `cls_field_meta`结构如下: ```lua cls_field_meta = { __call = __CreateInstance, __index = cls_indexer, __newindex = cls_newindexer, } ``` - `cls_field`的元表是`cls_field_meta` - `cls_field`结构如下: ```lua cls_field = { UnderlyingSystemType = type userdata } ``` - `LUA_REGISTRYINDEX["LuaClassIndexs"]`结构如下: ```lua LUA_REGISTRYINDEX["LuaClassIndexs"] = { [type userdata] = cls_indexer } ``` - `LUA_REGISTRYINDEX["LuaClassNewIndexs"]`结构如下: ```lua LUA_REGISTRYINDEX["LuaClassNewIndexs"] = { [type userdata] = cls_newindexer } ``` --- 上诉就是import_type全部的流程 --- ###LuaEnv初始化方法 --- ```C# public LuaEnv() { #if THREAD_SAFE || HOTFIX_ENABLE lock(luaEnvLock) #endif { //获取注册表index LuaIndexes.LUA_REGISTRYINDEX = LuaAPI.xlua_get_registry_index(); // 创建luastate,转换为ptr指针 rawL = LuaAPI.luaL_newstate(); //Init Base Libs LuaAPI.luaopen_xlua(rawL); LuaAPI.luaopen_i64lib(rawL); //ObjectTranslator 这个类很重要 //unity实体保存在该类的objects字段中 //reverseMap也保存了一份实体,同事该实体的index //delayWrap字段也在该类中 //interfaceBridgeCreators这个也在该类中 //typeIdMap,typeMap //该类还有个字段cacheRef,这个cacheRef关联的table很重要,他是个弱引用table,他的value存有实体对应的userdata translator = new ObjectTranslator(this, rawL); //创建LuaCSFunction类型的type_class translator.createFunctionMetatable(rawL); //设置xlua.import_type,xlua.import_generic_type,xlua.cast,xlua.load_assembly,xlua.access等一堆方法 //common_array_meta ref初始化 //common_delegate_meta ref初始化 translator.OpenLib(rawL); //将translator是配对的,放到ObjectTranslatorPool池中 ObjectTranslatorPool.Instance.Add(rawL, translator); //panic为终止函数,当代码出现错误且未被保护时,会调用panic函数并终止宿主程 LuaAPI.lua_atpanic(rawL, StaticLuaCallbacks.Panic); LuaAPI.lua_pushstdcallcfunction(rawL, StaticLuaCallbacks.Print); //_G["print"] = StaticLuaCallbacks.Print if (0 != LuaAPI.xlua_setglobal(rawL, "print")) { throw new Exception("call xlua_setglobal fail!"); } //template engine lib register TemplateEngine.LuaTemplate.OpenLib(rawL); //package.searcher搜索器设置 //lua搜索路径,先找loadBuildtinlib AddSearcher(StaticLuaCallbacks.LoadBuiltinLib, 2); // just after the preload searcher AddSearcher(StaticLuaCallbacks.LoadFromCustomLoaders, 3); #if !XLUA_GENERAL AddSearcher(StaticLuaCallbacks.LoadFromResource, 4); AddSearcher(StaticLuaCallbacks.LoadFromStreamingAssetsPath, -1); #endif //执行init_xlua代码,该代码会创建一个CS table,以及metatable DoString(init_xlua, "Init"); init_xlua = null; #if (!UNITY_SWITCH && !UNITY_WEBGL) || UNITY_EDITOR AddBuildin("socket.core", StaticLuaCallbacks.LoadSocketCore); AddBuildin("socket", StaticLuaCallbacks.LoadSocketCore); #endif AddBuildin("CS", StaticLuaCallbacks.LoadCS); //创建metafunc table LuaAPI.lua_newtable(rawL); //metatable of indexs and newindexs functions LuaAPI.xlua_pushasciistring(rawL, "__index"); LuaAPI.lua_pushstdcallcfunction(rawL, StaticLuaCallbacks.MetaFuncIndex); //metafunc["__index"] = 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); //创建LUA_REGISTRYINDEX["LuaIndexs"] table //并设置该table的元表为metafunc table LuaAPI.lua_rawset(rawL, LuaIndexes.LUA_REGISTRYINDEX); LuaAPI.xlua_pushasciistring(rawL, Utils.LuaNewIndexsFieldName); LuaAPI.lua_newtable(rawL); LuaAPI.lua_pushvalue(rawL, -3); LuaAPI.lua_setmetatable(rawL, -2); //创建LUA_REGISTRYINDEX["LuaNewIndexs"] table //并设置该table的元表为metafunc table LuaAPI.lua_rawset(rawL, LuaIndexes.LUA_REGISTRYINDEX); LuaAPI.xlua_pushasciistring(rawL, Utils.LuaClassIndexsFieldName); LuaAPI.lua_newtable(rawL); LuaAPI.lua_pushvalue(rawL, -3); LuaAPI.lua_setmetatable(rawL, -2); //创建LUA_REGISTRYINDEX["LuaClassIndexsFieldName"] table //并设置该table的元表为metafunc table LuaAPI.lua_rawset(rawL, LuaIndexes.LUA_REGISTRYINDEX); LuaAPI.xlua_pushasciistring(rawL, Utils.LuaClassNewIndexsFieldName); LuaAPI.lua_newtable(rawL); LuaAPI.lua_pushvalue(rawL, -3); LuaAPI.lua_setmetatable(rawL, -2); //创建LUA_REGISTRYINDEX["LuaClassNewIndexs"] table //并设置该table的元表为metafunc table LuaAPI.lua_rawset(rawL, LuaIndexes.LUA_REGISTRYINDEX); LuaAPI.lua_pop(rawL, 1); // pop metatable of indexs and newindexs functions LuaAPI.xlua_pushasciistring(rawL, MAIN_SHREAD); LuaAPI.lua_pushthread(rawL); //LUA_REGISTRYINDEX["xlua_main_thread"] = rawl thread LuaAPI.lua_rawset(rawL, LuaIndexes.LUA_REGISTRYINDEX); LuaAPI.xlua_pushasciistring(rawL, CSHARP_NAMESPACE); if (0 != LuaAPI.xlua_getglobal(rawL, "CS")) { throw new Exception("get CS fail!"); } //LUA_REGISTRYINDEX[CSHARP_NAMESPACE] = //CS table LuaAPI.lua_rawset(rawL, LuaIndexes.LUA_REGISTRYINDEX); if (0 != LuaAPI.xlua_getglobal(rawL, "_G")) { throw new Exception("get _G fail!"); } //获取LuaTable _G translator.Get(rawL, -1, out _G); LuaAPI.lua_pop(rawL, 1); errorFuncRef = LuaAPI.get_error_func_ref(rawL); if (initers != null) { for (int i = 0; i < initers.Count; i++) { //运行XLua_Gen_Initer_Register__等生成代码的init方法回调 initers[i](this, translator); } } //绑定Array实体方法,对应type_id:common_array_meta translator.CreateArrayMetatable(rawL); //绑定MulticastDelegate实体方法,对应type_id:common_delegate_meta translator.CreateDelegateMetatable(rawL); //初始化enumerable_pairs_func translator.CreateEnumerablePairs(rawL); } } ``` --- - 初始化各种lib,初始化lua_state(rawL),记录注册表index - 初始化xlua.***方法,初始化`ObjectTranslator`类 ``` //类型转换 objectCasters = new ObjectCasters(this); //类型检查 objectCheckers = new ObjectCheckers(this); methodWrapsCache = new MethodWrapsCache(this, objectCheckers, objectCasters); //静态回调方法类 metaFunctions=new StaticLuaCallbacks(); //importTypeFunction绑定ImportType回调方法 importTypeFunction = new LuaCSFunction(StaticLuaCallbacks.ImportType); //loadAssemblyFunction绑定LoadAssembly回调方法 loadAssemblyFunction = new LuaCSFunction(StaticLuaCallbacks.LoadAssembly); //castFunction绑定Cast回调方法 castFunction = new LuaCSFunction(StaticLuaCallbacks.Cast); //创建一个cache table,cache table用来保存userdata //cachetable[index]=userdata LuaAPI.lua_newtable(L); //创建cachetable的元表,该元表设置为{"__mode"="v"} //也就是cache 是一个弱引用表,value值为弱引用 LuaAPI.lua_newtable(L); LuaAPI.xlua_pushasciistring(L, "__mode"); LuaAPI.xlua_pushasciistring(L, "v"); LuaAPI.lua_rawset(L, -3); //设置为cachetable的元表 LuaAPI.lua_setmetatable(L, -2); //cachetable的引用id:cacheRef //LUA_REGISTRYINDEX[cacheRef] = cachetable cacheRef = LuaAPI.luaL_ref(L, LuaIndexes.LUA_REGISTRYINDEX); ``` - 设置搜索器package.searcher - metafun table的创建`metafun={"__index" = StaticLuaCallbacks.MetaFuncIndex}` - 创建`LUA_REGISTRYINDEX["LuaIndexs"]` table,并设置该table的元表为`metafunc table` - 创建`LUA_REGISTRYINDEX["LuaNewIndexs"]` table,并设置该table的元表为metafunc table - 创建`LUA_REGISTRYINDEX["LuaClassIndexsFieldName"]` table,并设置该table的元表为metafunc table - 创建`LUA_REGISTRYINDEX["LuaClassNewIndexs"]` table,并设置该table的元表为metafunc table - 运行`XLua_Gen_Initer_Register__`等生成代码的init方法回调 --- ####require(modname): --- - 首先查找`package.loaded`表,检测`modname`是否被加载过 - 如果被加载过,require返回`package.loaded[modname]`中保存的值 - 否则,它试着为模块寻找加载器。require加载模块按照package.searchers序列的指引来查找加载器 --- ###`__CreateInstance` --- ```C# [MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))] static int __CreateInstance(RealStatePtr L) { ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L); if(LuaAPI.lua_gettop(L) == 1) { //创建一个实例 var gen_ret = new object(); //直接将该实例push进去,真正是保存在objects池里 translator.PushAny(L, gen_ret); return 1; } } ``` --- ###`Push方法` --- ```C# public void Push(RealStatePtr L, object o) { //注意如果该o类型为unity Object类型,则 o==null这样判断是有问题的,因为如果o被destroy之后,o所对应的ptr为null了,但如果还没被c# gc,此时他还不是null,unity在Component类中重写了==这个操作符,所以这里得这么判断 //加上Equals,让UnityObject的多态可以生效 //if(o == null || o.Equals(null)) if (o == null) { LuaAPI.lua_pushnil(L); return; } //add进objectpools的index int index = -1; Type type = o.GetType(); bool is_enum = type.GetTypeInfo().IsEnum; bool is_valuetype = type.GetTypeInfo().IsValueType; //非数值或者enum类型 bool needcache = !is_valuetype || is_enum; // if (needcache && (is_enum ? enumMap.TryGetValue(o, out index) : reverseMap.TryGetValue(o, out index))) { // if (LuaAPI.xlua_tryget_cachedud(L, index, cacheRef) == 1) { return; } //这里实在太经典了,weaktable先删除,然后GC会延迟调用,当index会循环利用的时候,不注释这行将会导致重复释放 //collectObject(index); } bool is_first; //根据type获取对应type_id,保存在typeIdMap中 int type_id = getTypeId(L, type, out is_first); //如果一个type的定义含本身静态readonly实例时,getTypeId会push一个实例,这时候应该用这个实例 if (is_first && needcache && (is_enum ? enumMap.TryGetValue(o, out index) : reverseMap.TryGetValue(o, out index))) { //判断cache_table[index]是否存在,存在返回1,否则返回0。注意cache_table[index]是一个弱引用的userdata,如果没用就可能被回收 if (LuaAPI.xlua_tryget_cachedud(L, index, cacheRef) == 1) { return; } } //添加obj到objects池中,并返回对应index //enum类型会顺便保持到enumMap //非数值类型顺便会保存到reverseMap index = addObject(o, is_valuetype, is_enum); //注意meta_ref就是type_id,获取到的type_table //key对应的就是Object.add返回的index //cache_ref 对应的就是cache_table的ref //通过这个index使得userdata和obj相关联了 LuaAPI.xlua_pushcsobj(L, index, type_id, needcache, cacheRef); } ``` --- ####`xlua_tryget_cachedud` --- ```c //判断cache_table[index]是否存在,存在返回1,否则返回0 LUA_API int xlua_tryget_cachedud(lua_State *L, int key, int cache_ref) { //根据cache_ref,加载cache_table lua_rawgeti(L, LUA_REGISTRYINDEX, cache_ref); //将cache_table[key]推到栈顶 lua_rawgeti(L, -1, key); if (!lua_isnil(L, -1)) { lua_remove(L, -2); return 1; } lua_pop(L, 2); return 0; } ``` --- ####`addObject` --- ```C# //添加obj到objects池中,并返回对应index //enum类型会顺便保持到enumMap //非数值类型顺便会保存到reverseMap int addObject(object obj, bool is_valuetype, bool is_enum) { //添加到ObjectPool中 //有个类似dictionary中freelist一样概念的东西 //表示回收之后重新可以使用的slot坑位的index //ObjectPool采用了一种叫FreeList的数据结构来保存userdata和obj,除了不方便遍历外,其他操作如:增、删、查询都是O ( 1 ) O(1)O(1)的复杂度,这里可以简单认为其就是一个Dictionary<int, object> int index = objects.Add(obj); if (is_enum) { enumMap[obj] = index; } else if (!is_valuetype) { reverseMap[obj] = index; } return index; } ``` --- ####`` --- ```c static void cacheud(lua_State *L, int key, int cache_ref) { //cache_table推送到栈顶 lua_rawgeti(L, LUA_REGISTRYINDEX, cache_ref); //-2就表示userdata lua_pushvalue(L, -2); //cache_table[key]=userdata lua_rawseti(L, -2, key); lua_pop(L, 1); } //注意meta_ref就是type_id,获取到的type_table //key对应的就是Object.add返回的index //cache_ref 对应的就是cache_table的ref LUA_API void xlua_pushcsobj(lua_State *L, int key, int meta_ref, int need_cache, int cache_ref) { //创建一个userdata int* pointer = (int*)lua_newuserdata(L, sizeof(int)); //该指针指向的值就是key,也就是index *pointer = key; //将该key也就是index保持到cache_table中 if (need_cache) cacheud(L, key, cache_ref); //将该userdata的元表绑定为type_table //此时userdata就跟type_table相关联了 lua_rawgeti(L, LUA_REGISTRYINDEX, meta_ref); lua_setmetatable(L, -2); } ``` --- - 整理一下这个流程: - 通过`objects.Add`来产生唯一`index` - 通过`xlua_pushcsobj`将index存放在`userdata`中 - 通过`xlua_tocsobj_fast`获取到存放在userdata中的唯一index - 通过`objects.Get`来根据唯一`index`获取到obj - 对`userdata产生引用的地方`,`xlua_pushcsobj`中将userdata存放在`cache_table`中,但`cache_table`是个弱表,不会对userdata产生任何的引用 - obj产生引用的地方,`ObjectTranslator.addObject`方法中,`objects,enumMap,reverseMap,`都对obj产生了引用。 - 一个userdata传递给lua后,如果在lua下没有地方引用这个userdata了,那么它就会被GC了。只要lua层还有地方引用这个obj(对应的userdata),那么这个obj就不会被GC - userdata的GC处理是通过`StaticLuaCallbacks.LuaGC`方法处理的 ```c# //返回*udata,也就是返回添加进objectpool的index值 LUA_API int xlua_tocsobj_safe(lua_State *L,int index) { int *udata = (int *)lua_touserdata (L,index); if (udata != NULL) { if (lua_getmetatable(L,index)) { lua_pushlightuserdata(L, &tag); lua_rawget(L,-2); if (!lua_isnil (L,-1)) { lua_pop (L, 2); return *udata; } lua_pop (L, 2); } } return -1; } [MonoPInvokeCallback(typeof(LuaCSFunction))] public static int LuaGC(RealStatePtr L){ //返回*udata,也就是返回添加进objectpool的index值 int udata = LuaAPI.xlua_tocsobj_safe(L, 1); if (udata != -1) { ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L); if ( translator != null ) { //释放obj translator.collectObject(udata); } } return 0; } //根据index,释放objects,enumMap,reverseMap中的obj internal void collectObject(int obj_index_to_collect) { object o; //objects含有该index if (objects.TryGetValue(obj_index_to_collect, out o)) { objects.Remove(obj_index_to_collect); if (o != null) { int obj_index; //lua gc是先把weak table移除后再调用__gc,这期间同一个对象可能再次push到lua,关联到新的index bool is_enum = o.GetType().IsEnum(); if ((is_enum ? enumMap.TryGetValue(o, out obj_index) : reverseMap.TryGetValue(o, out obj_index)) && obj_index == obj_index_to_collect) { if (is_enum) { enumMap.Remove(o); } else { reverseMap.Remove(o); } } } } } ``` --- ### lua层还存在引用,obj仍然会被GC的情况 --- - 当一个`UnityEngine.Object`被`Destroy`后,通过`==`判断会发现其和`null`相等,如果把该Obj转换成`System.Object`再和`null`比较发现是不相等的,原因就是Unity在`UnityEngine.Object`重写了`==`以及`!=`操作符。`Destory`方法运行之后,仅仅只是该`Obj`指向c++的ptr指向null了,但c# gc还没走,还没回收,但unity为了统一起见,就重写了该操作符。 - xLua采用了一个Tick GC轮询,当发现一个`UnityEngine.Object`被`Destroy`后,会对其`userdata`做一些清理 --- ```C# int last_check_point = 0; int max_check_per_tick = 20; static bool ObjectValidCheck(object obj) { // 如果obj是UnityEngine.Object而且==null,则任何其已经被Destroy了 return (!(obj is UnityEngine.Object)) || ((obj as UnityEngine.Object) != null); } Func<object, bool> object_valid_checker = new Func<object, bool>(ObjectValidCheck); public void Tick() { last_check_point = translator.objects.Check(last_check_point, max_check_per_tick, object_valid_checker, translator.reverseMap); } public int Check(int check_pos, int max_check, Func<object, bool> checker, Dictionary<object, int> reverse_map) { if (count == 0) { return 0; } for (int i = 0; i < Math.Min(max_check, count); ++i) { check_pos %= count; //转换成System.Object比较引用确定不是为null if (list[check_pos].next == ALLOCED && !Object.ReferenceEquals(list[check_pos].obj, null)) { //确定为UnityEngine.Object类型,且转换成对应类型比较为null if (!checker(list[check_pos].obj)) { //将对应index的objects设置为null object obj = Replace(check_pos, null); int obj_index; //这里再加了一层判断index,是因为怕在移除的时候,重新加进来的index if (reverse_map.TryGetValue(obj, out obj_index) && obj_index == check_pos) { reverse_map.Remove(obj); } } } ++check_pos; } return check_pos %= count; } ``` --- - `objects.Check`函数内部会遍历`指定数量(默认20)`的obj,通过`ObjectValidCheck`判断其是否已经失效(被`Destroy`)了,如果失效的话,对应的obj会被赋`null`。这样`外部通过userdata查询到的obj就会是null` - 此时就会出现`userdata`自身不是`nil`,只是其对应的`obj`是null - 在场景切换的时候,由于unity中gameobject已经被destory,此时还是指定默认20去遍历回收的话,会出现一堆gameobject没有被回收的情况,出现内存泄露情况 ###循环引用 - 如果obj需要保证userdata的生命周期,会存储一个userdata的引用,那这个引用应该什么时候释放呢? - 可以通过LuaBase来实现吗?它是采用Dispose和析构函数的方式来释放lua引用的。答案是不行,原因是他push的时候`((LuaBase)o).push(L); `直接这样操作的,根本没有跟objectpool打交道。 ```C# internal virtual void push(RealStatePtr L) { LuaAPI.lua_getref(L, luaReference); } ``` - 而如果我们用这套析构方式会产生什么问题呢?由于obj自身被`objects`和`enummap`,`reverseMap`引用着,这个引用必须等到`userdata`被`GC`才会释放,而`userdata`却又被`obj`引用了,从而产生循环引用,所以obj根本不可能触发到析构函数,也无法释放userdata引用,最后造成内存泄漏 - 统一的`Destroy函数`来做释放。所有`Object不用时都需要调用这个函数来做清理工作`,调用这个函数后,就可以认为其已经不能再使用了,这样就可以在这个`Destroy函数里清理userdata的引用` --- ###总结table ```lua --CS元表的__index会调用import_type CS = { UnityEngine = { ["typename"] = cls_field, }, [type userdata] = cls_field, } LUA_REGISTRYINDEX[CSHARP_NAMESPACE] = CS --cls_field的元表为cls_field_meta cls_field = { UnderlyingSystemType = type userdata } cls_field_meta = { __call = __CreateInstance, __index = cls_indexer, __newindex = cls_newindexer, } metafun = { __index = StaticLuaCallbacks.MetaFuncIndex } --元表为metafun --作用是获取cls_newindexer的upvalue LUA_REGISTRYINDEX["LuaClassNewIndexs"]={ [type userdata] = cls_newindexer } --元表为metafun --作用是获取cls_indexer的upvalue LUA_REGISTRYINDEX["LuaClassIndexs"] = { [type userdata] = cls_indexer } type_table = { tag = 1, __gc = translator.metaFunctions.GcMeta, __tostring = translator.metaFunctions.ToStringMeta, [1] = type_id, --在getTypeId()中添加的 __newIndex = obj_newindexer, __index = obj_indexer, } LUA_REGISTRYINDEX[type_id] = type_table LUA_REGISTRYINDEX[type.FullName] = type_table --元表为metafun --作用是获取obj_newindexer的upvalue LUA_REGISTRYINDEX["LuaNewIndexs"] = { [type userdata] = obj_newindexer, } --元表为metafun --作用是获取obj_indexer的upvalue LUA_REGISTRYINDEX["LuaIndexs"] = { [type userdata] = obj_indexer, } --弱表 --userdata的元表是type_table cache_table ={ [index] = userdata } ``` - `LuaCallCSharp`标签会生成`类名Wrap`文件,里面至少会包括`__Register`,`__CreateInstance`方法 - `CSharpCallLua`标签,如果添加在委托,全部委托都生成在`DelegatesGensBridge.cs`文件中 ```C# public void __Gen_Delegate_Imp0() { ... } public double __Gen_Delegate_Imp1(double p0, double p1){ ... } public void __Gen_Delegate_Imp2(string p0){ ... } public override Delegate GetDelegateByType(Type type){ if (type == typeof(System.Action)) { return new System.Action(__Gen_Delegate_Imp0); } if (type == typeof(UnityEngine.Events.UnityAction)) { return new UnityEngine.Events.UnityAction(__Gen_Delegate_Imp0); } if (type == typeof(System.Func<double, double, double>)) { return new System.Func<double, double, double>(__Gen_Delegate_Imp1); } ... } ``` --- - `CSharpCallLua`标签,如果添加在接口上,则会生成`包名_类名Bridge`文件,该文件有个类`包名类名Bridge`继承于`LuBase`,同时实现了该接口 ```C# public class TutorialCSCallLuaItfDBridge : LuaBase, Tutorial.CSCallLua.ItfD{ public static LuaBase __Create(int reference, LuaEnv luaenv) { return new TutorialCSCallLuaItfDBridge(reference, luaenv); } public TutorialCSCallLuaItfDBridge(int reference, LuaEnv luaenv) : base(reference, luaenv) { } 。。。 实现对应接口方法 } ``` - `LuaCallCSharp`,`CSharpCallLua`,`BlackList`,`GCOptimize`也支持放在一个`static list`上 ```C# //lua中要使用到C#库的配置,比如C#标准库,或者Unity API,第三方库等。 [LuaCallCSharp] public static List<Type> LuaCallCSharp = new List<Type>() { typeof(System.Object), typeof(UnityEngine.Object), } //C#静态调用Lua的配置(包括事件的原型),仅可以配delegate,interface [CSharpCallLua] public static List<Type> CSharpCallLua = new List<Type>() { typeof(Action), typeof(Func<double, double, double>), typeof(System.Collections.IEnumerator) }; [BlackList] public static List<List<string>> BlackList = new List<List<string>>() { new List<string>(){"System.Xml.XmlNodeList", "ItemOf"}, } [GCOptimize] static List<Type> GCOptimize = new List<Type>(){ typeof(UnityEngine.Vector2), typeof(UnityEngine.Vector3), } ``` - `GCOptimize`标签注释过的struct,会在`WrapPusher`文件中生成三个方法,同时`PackUnpack`文件也会生成push方法 --- ####`WrapPusher文件` --- ```C# typedef struct { int fake_id; unsigned int len; char data[1]; } CSharpStruct; //meta_ref 就是对应的type_id LUA_API void *xlua_pushstruct(lua_State *L, unsigned int size, int meta_ref) { //生成一个CSharpStruct类型的userdata CSharpStruct *css = (CSharpStruct *)lua_newuserdata(L, size + sizeof(int) + sizeof(unsigned int)); css->fake_id = -1; css->len = size; //将type_table推送到栈顶 lua_rawgeti(L, LUA_REGISTRYINDEX, meta_ref); //设置该userdata的元表为栈顶的type_table lua_setmetatable(L, -2); return css; } int UnityEngineVector2_TypeID = -1; public void PushUnityEngineVector2(RealStatePtr L, UnityEngine.Vector2 val) { if (UnityEngineVector2_TypeID == -1) { bool is_first; //获取UnityEngine.Vector2对应typeId,并保存在UnityEngineVector2_TypeID中 UnityEngineVector2_TypeID = getTypeId(L, typeof(UnityEngine.Vector2), out is_first); } //生成一个CSharpStruct类型的userdata,并将他元表绑定为LUA_REGISTRYINDEX[UnityEngineVector2_TypeID] IntPtr buff = LuaAPI.xlua_pushstruct(L, 8, UnityEngineVector2_TypeID); //直接操控内存赋值到struct中 if (!CopyByValue.Pack(buff, 0, val)) { throw new Exception("pack fail fail for UnityEngine.Vector2 ,value="+val); } } public void Get(RealStatePtr L, int index, out UnityEngine.Vector2 val) { //获取lua type LuaTypes type = LuaAPI.lua_type(L, index); if (type == LuaTypes.LUA_TUSERDATA ) { //判断type_id是否一致 if (LuaAPI.xlua_gettypeid(L, index) != UnityEngineVector2_TypeID) { throw new Exception("invalid userdata for UnityEngine.Vector2"); } //获取该userdata的指针 IntPtr buff = LuaAPI.lua_touserdata(L, index); //直接操控内存返回值 if (!CopyByValue.UnPack(buff, 0, out val)) { throw new Exception("unpack fail for UnityEngine.Vector2"); } } else if (type ==LuaTypes.LUA_TTABLE) { //如果是table类型 CopyByValue.UnPack(this, L, index, out val); } else { //objectCasters类型强转返回 val = (UnityEngine.Vector2)objectCasters.GetCaster(typeof(UnityEngine.Vector2))(L, index, null); } } public void UpdateUnityEngineVector2(RealStatePtr L, int index, UnityEngine.Vector2 val) { if (LuaAPI.lua_type(L, index) == LuaTypes.LUA_TUSERDATA) { if (LuaAPI.xlua_gettypeid(L, index) != UnityEngineVector2_TypeID) { throw new Exception("invalid userdata for UnityEngine.Vector2"); } //获取struct userdata指针 IntPtr buff = LuaAPI.lua_touserdata(L, index); //操控内存设置值 if (!CopyByValue.Pack(buff, 0, val)) { throw new Exception("pack fail for UnityEngine.Vector2 ,value="+val); } } else { throw new Exception("try to update a data with lua type:" + LuaAPI.lua_type(L, index)); } } ``` --- ####`PackUnpack文件` --- ```C# //设置值 LUALIB_API int xlua_pack_float2(void *p, int offset, float f1, float f2) { //获取指针 CSharpStruct *css = (CSharpStruct *)p; if (css->fake_id != -1 || css->len < offset + sizeof(float) * 2) { return 0; } else { //获取偏移指针 float *pos = (float *)(&(css->data[0]) + offset); //直接设置值 pos[0] = f1; pos[1] = f2; return 1; } } //获取值 LUALIB_API int xlua_unpack_float2(void *p, int offset, float *f1, float *f2) { CSharpStruct *css = (CSharpStruct *)p; if (css->fake_id != -1 || css->len < offset + sizeof(float) * 2) { return 0; } else { float *pos = (float *)(&(css->data[0]) + offset); *f1 = pos[0]; *f2 = pos[1]; return 1; } } public static bool Pack(IntPtr buff, int offset, UnityEngine.Vector2 field) { if(!LuaAPI.xlua_pack_float2(buff, offset, field.x, field.y)) { return false; } return true; } public static bool UnPack(IntPtr buff, int offset, out UnityEngine.Vector2 field) { field = default(UnityEngine.Vector2); float x = default(float); float y = default(float); if(!LuaAPI.xlua_unpack_float2(buff, offset, out x, out y)) { return false; } field.x = x; field.y = y; return true; } ``` --- - 添加了GCOptimize标签之后,xlua给我们做了这些工作: - 生成struct的值拷贝代码,用于把struct里头各字段拷贝到一块非托管内存(Pack函数),以及从非托管内存拷贝出各字段的值(UnPack函数) - c#传struct到lua:调用lua的api,申请一块userdata(对于c#来说是非托管代码),调用Pack把struct打包进去; - lua回传到给c#:调用UnPack解出struct; - struct的方法还是沿用c#原本的实现 - 该方案的缺点是因为还是调用原来的c#实现,从lua经C语言,再经pinvoke调用到C#,这个调用的成本已经远远大于一些简单方法执行的开销 - 相比table方案更省内存,只是struct的大小加上一个头部 - 支持的struct类型宽泛的多,简单方便 --- ###xlua.genaccessor直接操控内存 --- ```C #define DIRECT_ACCESS(type, push_func, to_func) \ int xlua_struct_get_##type(lua_State *L) {\ CSharpStruct *css = (CSharpStruct *)lua_touserdata(L, 1);\ int offset = xlua_tointeger(L, lua_upvalueindex(1));\ type val;\ if (css == NULL || css->fake_id != -1 || css->len < offset + sizeof(type)) {\ return luaL_error(L, "invalid c# struct!");\ } else {\ memcpy(&val, (&(css->data[0]) + offset), sizeof(type));\ push_func(L, val);\ return 1;\ }\ }\ \ int xlua_struct_set_##type(lua_State *L) { \ CSharpStruct *css = (CSharpStruct *)lua_touserdata(L, 1);\ int offset = xlua_tointeger(L, lua_upvalueindex(1));\ type val;\ if (css == NULL || css->fake_id != -1 || css->len < offset + sizeof(type)) {\ return luaL_error(L, "invalid c# struct!");\ } else {\ val = (type)to_func(L, 2);\ memcpy((&(css->data[0]) + offset), &val, sizeof(type));\ return 0;\ }\ }\ static const lua_CFunction direct_getters[10] = { xlua_struct_get_int8_t, xlua_struct_get_uint8_t, xlua_struct_get_int16_t, xlua_struct_get_uint16_t, xlua_struct_get_int32_t, xlua_struct_get_uint32_t, xlua_struct_get_int64_t, xlua_struct_get_uint64_t, xlua_struct_get_float, xlua_struct_get_double }; static const lua_CFunction direct_setters[10] = { xlua_struct_set_int8_t, xlua_struct_set_uint8_t, xlua_struct_set_int16_t, xlua_struct_set_uint16_t, xlua_struct_set_int32_t, xlua_struct_set_uint32_t, xlua_struct_set_int64_t, xlua_struct_set_uint64_t, xlua_struct_set_float, xlua_struct_set_double }; LUA_API int gen_css_access(lua_State *L) { int offset = xlua_tointeger(L, 1); int type = xlua_tointeger(L, 2); if (offset < 0) { return luaL_error(L, "offset must larger than 0"); } if (type < T_INT8 || type > T_DOUBLE) { return luaL_error(L, "unknow tag[%d]", type); } lua_pushvalue(L, 1); lua_pushcclosure(L, direct_getters[type], 1); lua_pushvalue(L, 1); lua_pushcclosure(L, direct_setters[type], 1); lua_pushcclosure(L, nop, 0); return 3; } ``` --- ```lua --通过xlua.genaccessor实现不经过C#直接操作内存 --userdata比table更省内存,但操作字段比table性能稍低 function test_vector3(title, v1, v2) print(title) v1.x = 100 print(v1.x, v1.y, v1.z) print(v1, v2) print(v1 + v2) v1:Set(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z) print(v1) print(CS.UnityEngine.Vector3.Normalize(v1)) end test_vector3('----before change metatable----', CS.UnityEngine.Vector3(1, 2, 3), CS.UnityEngine.Vector3(7, 8, 9)) local get_x, set_x = xlua.genaccessor(0, 8) local get_y, set_y = xlua.genaccessor(4, 8) local get_z, set_z = xlua.genaccessor(8, 8) local fields_getters = { x = get_x, y = get_y, z = get_z } local fields_setters = { x = set_x, y = set_y, z = set_z } local ins_methods = { Set = function(o, x, y, z) set_x(o, x) set_y(o, y) set_z(o, z) end } local mt = { __index = function(o, k) --print('__index', k) if ins_methods[k] then return ins_methods[k] end return fields_getters[k] and fields_getters[k](o) end, __newindex = function(o, k, v) if fields_setters[k] then fields_setters[k](o, v) else error('no such field ' .. k) end end, __tostring = function(o) return string.format('vector3 { %f, %f, %f}', o.x, o.y, o.z) end, __add = function(a, b) return CS.UnityEngine.Vector3(a.x + b.x, a.y + b.y, a.z + b.z) end } xlua.setmetatable(CS.UnityEngine.Vector3, mt) test_vector3('----after change metatable----', CS.UnityEngine.Vector3(1, 2, 3), CS.UnityEngine.Vector3(7, 8, 9)) ``` --- ###直接操控内存 --- ```C typedef struct SelfVector2 { float x; float y; } SelfVector2; const char *const Vector2_KEY = "Self_Vector2"; #define checkVector2(L) (SelfVector2*)luaL_checkudata(L, 1, Vector2_KEY) //声明一个回调方法,需要在c#中再次声明 typedef void (*NativeCallBack)(const char *, float f, void *p); NativeCallBack _callbackMap; //接收c#中的回调方法 LUALIB_API void RegisterNativeCallback(NativeCallBack nativeCallBack) { _callbackMap = nativeCallBack; } static int l_new(lua_State *L) { size_t byteSize = sizeof(SelfVector2); SelfVector2 *v = (SelfVector2 *) lua_newuserdata(L, byteSize); luaL_setmetatable(L, Vector2_KEY); v->x = (float) luaL_checknumber(L, 1); v->y = (float) luaL_checknumber(L, 2); lua_pushvalue(L, -1); _callbackMap("l_new===", v->x, (void *) v); _callbackMap("l_new==meta=", v->x, (void *) lua_touserdata(L, -1)); lua_pop(L, 1); return 1; } static int l_get(lua_State *L) { SelfVector2 *vector2 = checkVector2(L); lua_pushnumber(L, vector2->x); lua_pushnumber(L, vector2->y); _callbackMap("l_get===", vector2->x, (void *) vector2); return 2; } static int l_set(lua_State *L) { SelfVector2 *vector2 = checkVector2(L); int length = lua_gettop(L); if (length >= 3) { vector2->x = (float) luaL_checknumber(L, 2); vector2->y = (float) luaL_checknumber(L, 3); } else if (length >= 2) { vector2->x = (float) luaL_checknumber(L, 2); } _callbackMap("l_set===", vector2->x, (void *) vector2); return 0; } static const luaL_Reg vector2lib[] = { {"new", l_new}, {NULL, NULL} }; static const luaL_Reg vector2meta[] = { {"get", l_get}, {"set", l_set}, {NULL, NULL} }; LUALIB_API void vector_get(void *p, float *x, float *y) { SelfVector2 *vector2 = (SelfVector2 *) p; *x = vector2->x; *y = vector2->y; _callbackMap("vector_get", *x, p); } LUALIB_API void vector_set_x(void *p, float x) { SelfVector2 *vector2 = (SelfVector2 *) p; vector2->x = x; _callbackMap("vector_set_x", vector2->x, p); } LUALIB_API void vector_set_y(void *p, float y) { SelfVector2 *vector2 = (SelfVector2 *) p; vector2->y = y; _callbackMap("vector_set_y", vector2->y, p); } LUALIB_API void vector_set(void *p, float x, float y) { SelfVector2 *vector2 = (SelfVector2 *) p; vector2->x = x; vector2->y = y; _callbackMap("vector_set", vector2->x, p); } LUA_API void luaopen_xlua(lua_State *L) { //生成metatable luaL_newmetatable(L, Vector2_KEY); lua_pushstring(L, "__index"); lua_pushvalue(L, -2); //设置metabable index lua_settable(L, -3); //将vector2meta方法注册到metatable中 luaL_setfuncs(L, vector2meta, 0); //新建一个lib,并注册到global,方便lua调用 luaL_newlib(L, vector2lib); lua_setglobal(L, "selfVector2"); } ``` --- ```C# [DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)] public static extern void vector_get(IntPtr L, out float x, out float y); [DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)] public static extern void vector_set_x(IntPtr L, float x); [DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)] public static extern void vector_set_y(IntPtr L, float y); [DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)] public static extern void vector_set(IntPtr L, float x, float y); //注册c中的委托 [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void NativeCallback(string args, float f, IntPtr p); [DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)] public static extern void RegisterNativeCallback(IntPtr callback); //也可以直接注册为NativeCallback类型,而不是指针 //public static extern void RegisterNativeCallback(NativeCallback callback); [MonoPInvokeCallback(typeof(NativeCallback))] public static void CallbackLog(string args, float f, IntPtr p) { Debug.Log($"callbacklog:{args},f:{f},p:{p}"); } ``` --- ```C# //注册c#中的方法到c中 LuaAPI.RegisterNativeCallback(Marshal.GetFunctionPointerForDelegate(new LuaAPI.NativeCallback(LuaAPI .CallbackLog))); //如果注册的不是指针,就不需要将方法转换成委托指针 //LuaAPI.RegisterNativeCallback(LuaAPI.CallbackLog); RegisterVector2Func(rawL); public static void RegisterVector2Func(System.IntPtr L) { string name = "lua_self_vector2_bind"; LuaAPI.lua_pushstdcallcfunction(L, Vector2BindingFunction); if (0 != LuaAPI.xlua_setglobal(L, name)) { throw new Exception("call xlua_setglobal fail!"); } } [StructLayout(LayoutKind.Sequential)] public struct SelfVector2 { public float x; public float y; } public interface ISelfVector2 { unsafe IntPtr selfVector2 { get; set; } } public static ISelfVector2 _selfVector2; [MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))] public static int Vector2BindingFunction(IntPtr L) { unsafe { if (_selfVector2 != null) { _selfVector2.selfVector2 = LuaAPI.lua_touserdata(L, 1); SelfVector2* t = (SelfVector2*)_selfVector2.selfVector2; Debug.Log($"t:{t->x},{t->y},{_selfVector2.selfVector2}"); _selfVector2 = null; } return 0; } } ``` --- ```C# public unsafe IntPtr selfVector2 { get; set; } if (Input.GetKeyDown(KeyCode.A)) { unsafe { if (selfVector2 != IntPtr.Zero) { Lua.vector_get(selfVector2, out var x, out var y); Debug.Log($"x:{x},y:{y}"); } } } if (Input.GetKeyDown(KeyCode.B)) { unsafe { if (selfVector2 != IntPtr.Zero) { Random r = new Random(); LuaEnv.SelfVector2* t = (LuaEnv.SelfVector2*)selfVector2; Lua.vector_set((IntPtr)t, r.Next(10, 20), r.Next(100, 200)); } } } ``` --- ```lua local self_vector2 = nil function start() self_vector2 = selfVector2.new(2,3) lua_self_vector2_bind(self_vector2) local x,y = self_vector2:get() print(x,y) self_vector2:set(4,6) print("get:",self_vector2:get()) end function update() if CS.UnityEngine.Input.GetKeyDown(CS.UnityEngine.KeyCode.Alpha0) then print("current:",self_vector2:get()) end if CS.UnityEngine.Input.GetKeyDown(CS.UnityEngine.KeyCode.Alpha1) then self_vector2:set(math.random(1, 10),math.random(30, 40)) end end ``` --- ###使用带有ref或out修饰的函数 --- ```C# public class Lesson5 { public int RefOutFun(int a, out int b, ref int c) { b = a; c = b+c; return 3; } } ``` --- ```lua local a1,b1,c1=CS.Lesson5().RefOutFun(2,4) //3 print(a1) //2 print(b1) //6 print(c1) ``` --- - `out修饰的参数`不传值,`ref修饰的参数`需要传值,ref类型的参数若不传值则使用默认值作为参数,`第一个返回值是return返回`的,`其他是ref或out的数值` --- ###`C#访问lua` - `DClass d = luaenv.Global.Get<T>("****");`该段代码会访问global这个`LuaTable`的方法`Get<T>` ####`Get<TKey, TValue>` ```C# public void Get<TKey, TValue>(TKey key, out TValue value) { var L = luaEnv.L; var translator = luaEnv.translator; int oldTop = LuaAPI.lua_gettop(L); //根据luaReference将globaltable 压栈 LuaAPI.lua_getref(L, luaReference); //将key压栈 translator.PushByType(L, key); //globaltable没有找到对应key,抛出异常 if (0 != LuaAPI.xlua_pgettable(L, -2)) { string err = LuaAPI.lua_tostring(L, -1); LuaAPI.lua_settop(L, oldTop); throw new Exception("get field [" + key + "] error:" + err); } //获取value 对应lua类型 LuaTypes lua_type = LuaAPI.lua_type(L, -1); Type type_of_value = typeof(TValue); //c#类型是值类型,但lua类型是nil,抛出异常 if (lua_type == LuaTypes.LUA_TNIL && type_of_value.IsValueType()) { throw new InvalidCastException("can not assign nil to " + type_of_value.GetFriendlyName()); } try { //ObjectTranslator的Get<T>方法中获取value值 //当前值在index为-1 translator.Get(L, -1, out value); } catch (Exception e) { throw e; } finally { LuaAPI.lua_settop(L, oldTop); } } ``` --- ####`ObjectTranslator的Get<T>` ```C# public void Get<T>(RealStatePtr L, int index, out T v) { Func<RealStatePtr, int, T> get_func; //从get_func_with_type为Dictionary<Type, Delegate>()类型 //存储常规值类型,以及InPtr类型,类型转换方法 if (tryGetGetFuncByType(typeof(T), out get_func)) { v = get_func(L, index); } else { //其他类型value获取通过ObjectTranslator的GetObject方法 v = (T)GetObject(L, index, typeof(T)); } } ``` --- ####`ObjectTranslator.GetObject` --- ```C# public object GetObject(RealStatePtr L, int index, Type type) { int udata = LuaAPI.xlua_tocsobj_safe(L, index); //如果该obj保存在objects中,则userdata的值保存的是objects.add的 index,也就是不可能为-1 if (udata != -1) { //根据index获取对应object object obj = objects.Get(udata); //判断是否是RawObject,RawObject就是xlua提供出来封装一层的Int,Float RawObject rawObject = obj as RawObject; return rawObject == null ? obj : rawObject.Target; } else { //判断是否是userdata if (LuaAPI.lua_type(L, index) == LuaTypes.LUA_TUSERDATA) { GetCSObject get; int type_id = LuaAPI.xlua_gettypeid(L, index); //decimal数值也是通过userdata保存的 if (type_id != -1 && type_id == decimal_type_id) { decimal d; Get(L, index, out d); return d; } Type type_of_struct; //struct类型也是通过userdata保存的 //struct类型需要添加LuaCallCSharp标签才能转换 if (type_id != -1 && typeMap.TryGetValue(type_id, out type_of_struct) && type.IsAssignableFrom(type_of_struct) && custom_get_funcs.TryGetValue(type, out get)) { return get(L, index); } } //通过ObjectCasters的GetCaster方法获取转换方法转换 return (objectCasters.GetCaster(type)(L, index, null)); } } ``` --- ```C# //table的转换方法 private object getLuaTable(RealStatePtr L, int idx, object target) { //userdata类型 if (LuaAPI.lua_type(L, idx) == LuaTypes.LUA_TUSERDATA) { //获取obj object obj = translator.SafeGetCSObj(L, idx); return (obj != null && obj is LuaTable) ? obj : null; } //判断是否是table类型 if (!LuaAPI.lua_istable(L, idx)) { return null; } //再插入一份到栈顶 LuaAPI.lua_pushvalue(L, idx); //创建栈顶的引用,并返回,pop栈顶元素 //初始化一个LuaTable return new LuaTable(LuaAPI.luaL_ref(L), translator.luaEnv); } //function的转换 private object getLuaFunction(RealStatePtr L, int idx, object target) { //是个userdata if (LuaAPI.lua_type(L, idx) == LuaTypes.LUA_TUSERDATA) { //获取obj object obj = translator.SafeGetCSObj(L, idx); return (obj != null && obj is LuaFunction) ? obj : null; } if (!LuaAPI.lua_isfunction(L, idx)) { return null; } LuaAPI.lua_pushvalue(L, idx); //返回一个luafunction,可以看出来如果get<LuaFunction>每次都会new个luaFunction return new LuaFunction(LuaAPI.luaL_ref(L), translator.luaEnv); } public ObjectCast GetCaster(Type type) { if (type.IsByRef) type = type.GetElementType(); Type underlyingType = Nullable.GetUnderlyingType(type); if (underlyingType != null) { return genNullableCaster(GetCaster(underlyingType)); } ObjectCast oc; //castersMap默认 //castersMap[typeof(byte[])] = getBytes; //castersMap[typeof(IntPtr)] = getIntptr; //castersMap[typeof(LuaTable)] = getLuaTable; //castersMap[typeof(LuaFunction)] = getLuaFunction; if (!castersMap.TryGetValue(type, out oc)) { //字典中没发现,生成type类型的caster //ObjectCast.genCaster方法 oc = genCaster(type); castersMap.Add(type, oc); } return oc; } ``` --- ####`ObjectCast.genCaster` --- ```C# private ObjectCast genCaster(Type type) { ObjectCast fixTypeGetter = (RealStatePtr L, int idx, object target) => { if (LuaAPI.lua_type(L, idx) == LuaTypes.LUA_TUSERDATA) { object obj = translator.SafeGetCSObj(L, idx); return (obj != null && type.IsAssignableFrom(obj.GetType())) ? obj : null; } return null; }; //委托类型 if (typeof(Delegate).IsAssignableFrom(type)) { return (RealStatePtr L, int idx, object target) => { object obj = fixTypeGetter(L, idx, target); if (obj != null) return obj; //通过ObjectTranslator.CreateDelegateBridge创建委托链接 return translator.CreateDelegateBridge(L, type, idx); }; } else if (typeof(DelegateBridgeBase).IsAssignableFrom(type)) { return (RealStatePtr L, int idx, object target) => { 。。。 //通过ObjectTranslator.CreateDelegateBridge创建委托链接,类型传的是null return translator.CreateDelegateBridge(L, null, idx); }; } else if (type.IsInterface()) { return (RealStatePtr L, int idx, object target) => { 。。。 //通过ObjectTranslator.CreateInterfaceBridge创建接口链接 return translator.CreateInterfaceBridge(L, type, idx); }; } //enum else if (type.IsEnum()) { return (RealStatePtr L, int idx, object target) => { 。。。 LuaTypes lua_type = LuaAPI.lua_type(L, idx); //可以是string类型 if (lua_type == LuaTypes.LUA_TSTRING) { return Enum.Parse(type, LuaAPI.lua_tostring(L, idx)); } //也可以是number类型 else if (lua_type == LuaTypes.LUA_TNUMBER) { return Enum.ToObject(type, LuaAPI.xlua_tointeger(L, idx)); } }; } //array else if (type.IsArray) { return (RealStatePtr L, int idx, object target) => { 。。。 uint len = LuaAPI.xlua_objlen(L, idx); int n = LuaAPI.lua_gettop(L); idx = idx > 0 ? idx : LuaAPI.lua_gettop(L) + idx + 1;// abs of index Type et = type.GetElementType(); //获取子类型转换 ObjectCast elementCaster = GetCaster(et); //创建array Array ary = target == null ? Array.CreateInstance(et, (int)len) : target as Array; 。。。 return ary; }; } //list else if (typeof(IList).IsAssignableFrom(type) && type.IsGenericType()) { Type elementType = type.GetGenericArguments()[0]; //获取子类型转换器 ObjectCast elementCaster = GetCaster(elementType); return (RealStatePtr L, int idx, object target) => { 。。。 //activator创建类型 obj = target == null ? Activator.CreateInstance(type) : target; 。。。 return obj; }; } else if (typeof(IDictionary).IsAssignableFrom(type) && type.IsGenericType()) { Type keyType = type.GetGenericArguments()[0]; //获取key,value的转换器 ObjectCast keyCaster = GetCaster(keyType); Type valueType = type.GetGenericArguments()[1]; ObjectCast valueCaster = GetCaster(valueType); return (RealStatePtr L, int idx, object target) => { 。。。 //初始化dic IDictionary dic = (target == null ? Activator.CreateInstance(type) : target) as IDictionary; ... return dic; }; } //class类型,且有默认构造方法 else if ((type.IsClass() && type.GetConstructor(System.Type.EmptyTypes) != null) || (type.IsValueType() && !type.IsEnum())) //class has default construtor { return (RealStatePtr L, int idx, object target) => { 。。。 //初始化创建 obj = target == null ? Activator.CreateInstance(type) : target; 。。。 //字段初始化 foreach (FieldInfo field in type.GetFields()) { 。。。 } return obj; }; } else { return fixTypeGetter; } } ``` --- ####`ObjectTranslator.CreateDelegateBridge` --- ```C# Dictionary<int, WeakReference> delegate_bridges = new Dictionary<int, WeakReference>(); public object CreateDelegateBridge(RealStatePtr L, Type delegateType, int idx) { LuaAPI.lua_pushvalue(L, idx); //LUA_REGISTRYINDEX[delegtate] = reference LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX); //说明之前注册过该类型delegate if (!LuaAPI.lua_isnil(L, -1)) { //获取对应reference int referenced = LuaAPI.xlua_tointeger(L, -1); LuaAPI.lua_pop(L, 1); //判断dic中的bridges是否还能使用 if (delegate_bridges[referenced].IsAlive) { //表示Value类型就是DelegateBridgeBase类型 if (delegateType == null) { return delegate_bridges[referenced].Target; } //获取bridge DelegateBridgeBase exist_bridge = delegate_bridges[referenced].Target as DelegateBridgeBase; Delegate exist_delegate; //判断brige中是否存在对应类型delegate if (exist_bridge.TryGetDelegate(delegateType, out exist_delegate)) { return exist_delegate; } else { //不存在就通过getDelegate方法重新获取一发 exist_delegate = getDelegate(exist_bridge, delegateType); exist_bridge.AddDelegate(delegateType, exist_delegate); return exist_delegate; } } } else { LuaAPI.lua_pop(L, 1); } LuaAPI.lua_pushvalue(L, idx); //delegate对应的引用index,并将栈顶value推出 //LUA_REGISTRYINDEX[reference] = delegate int reference = LuaAPI.luaL_ref(L); //再插入一份delegate LuaAPI.lua_pushvalue(L, idx); LuaAPI.lua_pushnumber(L, reference); //LUA_REGISTRYINDEX[delegate] = reference LuaAPI.lua_rawset(L, LuaIndexes.LUA_REGISTRYINDEX); DelegateBridgeBase bridge; //初始化DelegateBridge,同时传入reference bridge = new DelegateBridge(reference, luaEnv); //value本身就是DelegateBridgeBase类型 if (delegateType == null) { delegate_bridges[reference] = new WeakReference(bridge); return bridge; } //获取bridge中对应delegateType类型的delegate var ret = getDelegate(bridge, delegateType); //brige中添加对应类型的delegate bridge.AddDelegate(delegateType, ret); //添加到dic中 delegate_bridges[reference] = new WeakReference(bridge); return ret; } ``` --- ####`ObjectTranslator.getDelegate` --- ```C# Delegate getDelegate(DelegateBridgeBase bridge, Type delegateType) { //获取bridege默认的delegate //自动生成的代码 Delegate ret = bridge.GetDelegateByType(delegateType); ... Func<DelegateBridgeBase, Delegate> delegateCreator; if (!delegateCreatorCache.TryGetValue(delegateType, out delegateCreator)){ ... //反射获取设置 delegateCreator= (o) =>foundMethod.CreateDelegate(delegateType, o); break; } delegateCreatorCache.Add(delegateType, delegateCreator); 。。。 } ret = delegateCreator(bridge); if (ret != null) { return ret; } } ``` --- ####`DelegateBridge.GetDelegateByType` --- ```C# public override Delegate GetDelegateByType(Type type) { if (type == typeof(System.Action)) { return new System.Action(__Gen_Delegate_Imp0); } 。。。。 } public void __Gen_Delegate_Imp0() { RealStatePtr L = luaEnv.rawL; //pcall_prepare执行的就是以下操作 // LuaAPI.xlua_rawgeti(L, LuaIndexes.LUA_REGISTRYINDEX, errorFuncRef); // LuaAPI.xlua_rawgeti(L, LuaIndexes.LUA_REGISTRYINDEX, luaReference); // int errFunc = LuaAPI.lua_gettop(L) - 1; int errFunc = LuaAPI.pcall_prepare(L, errorFuncRef, luaReference); PCall(L, 0, 0, errFunc); LuaAPI.lua_settop(L, errFunc - 1); } ``` --- ####delegate以及LuaFunction总结比较 - `LuaFunction`不会生成一个对应的bridge,是因为castersMap中有个`getLuaFunction`转换方法。每次Get一次`luaFunction`都会new一个`luaFunction` - `delegate` 性能要好一点,类型也安全点,每个lua代码都有一个对应的`bridge`。再通过`GetDelegateByType`,new一个委托出来,但这个会被缓存下来,每一个lua代码只会执行一次。 - xlua是推荐使用委托方式的,因为委托是类型安全的,而且避免了每次的new luaFunction,同时如果luafunction 直接call的话不可避免的会出现装箱拆箱情况,虽然可以通过添加`Action<T1,T2>`来实现,但维护麻烦 --- 0922学习 0915学习