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<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效率高

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343