xlua实现原理

类型

一切从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 gen_to_be_invoked = (ObservableList)translator.FastGetCSObj(L, 1);

                gen_to_be_invoked.OnValueChanged = translator.GetDelegate.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实现原理)