xlua实现原理

作者: 塘朗山小钻风 | 来源:发表于2021-03-06 12:08 被阅读0次

    类型

    一切从LuaEnv.cs中的init_xlua开始。


    local metatable = {}

                local rawget = rawget

                local setmetatable = setmetatable

                local import_type = xlua.import_type

                local import_generic_type = xlua.import_generic_type

                local load_assembly = xlua.load_assembly

                function metatable:__index(key)

                    local fqn = rawget(self,'.fqn')

                    fqn = ((fqn and fqn .. '.') or '') .. 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) // 也是这样的方式能处理嵌套的namespace

                    elseif obj == true then

                        return rawget(self, key)

                    end

                    -- Cache this lookup

                    rawset(self, key, obj)

                    return obj

                end

                function metatable:__newindex()

                    error('No such type: ' .. rawget(self,'.fqn'), 2)

                end

                -- A non-type has been called; e.g. foo = System.Foo()

                function metatable:__call(...)

                    local n = select('#', ...)

                    local fqn = rawget(self,'.fqn')

                    if n > 0 then

                        local gt = import_generic_type(fqn, ...)

                        if gt then

                            return rawget(CS, gt)

                        end

                    end

                    error('No such type: ' .. fqn, 2)

                end

                CS = CS or {}

                setmetatable(CS, metatable)


    在这里rawget,setmetatable使用的lua原生的。import_type,import_generic_type,load_assembly用的是c#中扩展出来的。xlua这个全局table是在xlua的C代码中定义。CS.SOMETYPE时通过metatable就load对应的类型。

    函数调用

    先要说下lua基础的函数都在LuaDLL.cs中,这些函数都是从luaC源码中来的。lua和别的语言协作就是在lua调用栈中工作,这些函数不可或缺。要知道对象的函数调用就引出对象在lua层的表示。lua中表示外部数据结构用userdata和lightuserdata(区别就是lightuserdata自己管理分配和回收)。从UnityEngineGameObjectWrap的__CreateInstance能看出来:是ObjectTranslator.Push把对象推到lua中去。实际是调用了xlua_pushcsobj。源代码在xlua.c中。


    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);

    }


    代码内部的lua_newuserdata表示对象是以userdata传给lua的。第三个参数meta_ref就索引了metatable,函数调用时就会从metatable中获取函数调用。metatable是从luaL_getmetatable获取的。更加类型名获取metatable。如果以前没加载呢,初次加载执行TryDelayWrapLoader。根据是否有生成Wrap有不同调用。没生成wrap的用反射注册metatable(函数是ReflectionWrap):


    //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());

    ... 下面用反射把类静态函数,成员函数,getter,setter填充好


    如果有生成wrap的话,在BeginObjectRegister/EndObjectRegister,BeginClassRegister/EndClassRegister这配对调用中把类静态函数,成员函数,getter,setter设置好。

    然后就能函数调用了。经过中间wrap代码的效率更高。

    delegate/event

    在lua代码中为C#委托增加监听要怎么做?先要把委托标记为[CSharpCallLua]。在lua代码中一段

    csharpInstance.OnValueChanged= function() end


    Utils.RegisterFunc(L, Utils.SETTER_IDX, "OnValueChanged", _s_set_OnValueChanged);

    static int _s_set_OnValueChanged(RealStatePtr L)函数如下
    ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L);

                    ObservableList<BubbleWord> gen_to_be_invoked = (ObservableList<BubbleWord>)translator.FastGetCSObj(L, 1);

                    gen_to_be_invoked.OnValueChanged = translator.GetDelegate<ObservableList<BubbleWord>.ValueChangedHandler>(L, 2);


    GetDelegate内部调用CreateDelegateBridge。然后从生成的DelegateBridge获取对应的delegate。会根据[CSharpCallLua]配置依据函数签名生成很多很多的delegate签名函数,找不到就只能反射获取了(在ObjectTranslator的getDelegate函数中)。如果使用了hotfix会生成更多hotfix依赖更多delegate


    [XLua.GCOptimize]

    如果是值类型而且比较小,可以直接传递给lua(就是内存字节推送)。


    public void PushUnityEngineVector3(RealStatePtr L, UnityEngine.Vector3 val)

            {

                if (UnityEngineVector3_TypeID == -1)

                {

        bool is_first;

                    UnityEngineVector3_TypeID = getTypeId(L, typeof(UnityEngine.Vector3), out is_first);

                }

                IntPtr buff = LuaAPI.xlua_pushstruct(L, 12, UnityEngineVector3_TypeID);

                if (!CopyByValue.Pack(buff, 0, val))

                {

                    throw new Exception("pack fail fail for UnityEngine.Vector3 ,value="+val);

                }

            }


    获取到lua中当然也可以用CopyByValue.UnPack见以前文章。没有GC效率高

    相关文章

      网友评论

        本文标题:xlua实现原理

        本文链接:https://www.haomeiwen.com/subject/yqyjqltx.html