• xlua源码分析(二)lua Call C#的无wrap实现


    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    那么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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    xlua_tryget_cachedud函数就是通过C#缓存拿到的index,去lua层的缓存去拿userdata,lua层的缓存与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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    由于这个缓存是弱表,意味着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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    取不到的话就通过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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    但是,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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    负责这件事情的函数就是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 table
        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);
        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 namespace
        SetCSTable(L, type, cls_field);
        //finish set cls_field to namespace
        LuaAPI.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 metatable
        LuaAPI.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 tables
        LuaAPI.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 __index
    
        LuaAPI.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 tables
        LuaAPI.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 metatable
    
        LuaAPI.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 meta
        LuaAPI.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 tables
        LuaAPI.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 tables
        LuaAPI.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 __newindex
    
        LuaCSFunction 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));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150

    相比于tolua只使用两个table,xlua使用了若干的table来辅助索引查找C#的方法和成员。从代码中可以看出,cls_meta,cls_field,cls_getter和cls_setter是用直接给类访问用的,比如一些静态的方法与成员,lua层可以通过namespace和类名直接访问。而相应地,obj_meta,obj_field,obj_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来实现继承机制。

    那么field,getter,setter这三种table是如何跟meta进行关联的呢?xlua使用了一种非常巧妙的机制,以userdata的__index为例,它其实对应着一个函数,这个函数使用包含field,getter,setter这三种table在内,以及其他的一些参数,作为upvalue来引用。

    LUA_API int gen_obj_indexer(lua_State *L) {
    	lua_pushnil(L);
    	lua_pushcclosure(L, obj_indexer, 7);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    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 method
    			return 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 getter
    			lua_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;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72

    我们着重看一下第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));
    
    • 1

    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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    因此如果基类信息还没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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    这个函数首先会从lua层获取当前要wrap的type,生成唯一的type_id,并把类型信息wrap到lua层,然后再使用一次rawget把__index方法放回lua层,这样lua层就可以继续递归查找了。在例子中,想要调用到GetComponent得沿着LuaBehaviour=>MonoBehaviour=>Behaviour=>Component这条链一直查找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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    推测这么做的原因可能是为了少一些MonoPInvokeCallback吧:)

  • 相关阅读:
    docker安装UnlockMusic(音乐格式转换工具 )
    iOS开发之iOS15.6之后拉流LFLiveKit,画面模糊及16.1马赛克问题
    java计算机毕业设计springboot+vue股票交易模拟系统
    【学习笔记】RabbitMQ02:交换机,以及结合springboot快速开始
    【寻路】超级简单的A星寻路算法实现
    Ubuntu 22.04 更新完内核重启卡在 grub 命令行解决办法
    Docker和anaconda的区别?
    删除vxe-table右上角的工具栏
    轨迹预测——day 57 基于车道交叉和考虑驾驶方式的终点生成模型的前目标车辆轨迹预测
    具有标记和笔记功能的文件管理器TagSpaces
  • 原文地址:https://blog.csdn.net/weixin_45776473/article/details/134215854