1. 程式人生 > >LuaInterface的反射呼叫機制研究

LuaInterface的反射呼叫機制研究

一直不明白LuaInterface和lua之間反射呼叫的原理,花了兩天時間讀了一下程式碼,稍微總結了一下 附上所用LuaInterface的地址,可以用git直接clone 下面進入正題: 先說說兩個關鍵的載入函式,也是進行反射呼叫的基礎: 1、LoadAssembly 在Lua呼叫load_assembly後會被呼叫,目的是載入某個程式集到程式集快取中 通過程式碼可以發現這裡只是通過棧上傳入的字串載入對應的程式集,並加入到快取中
(注意)這個程式集快取在開啟時就已經Add了正在執行的程式集,不需要重複載入
2、ImportType 在Lua呼叫import_type時被呼叫,從已經載入的程式集中查詢對應的類,沒有找到返回空,找到則呼叫pushType
,往
堆疊上壓一種新的型別,並快取luaNet_objects元表中,matable的型別為luaNet_class。這一步的主要作用是方便C#將這些型別壓入堆疊後將返回值壓進棧 後會呼叫到把CLR Object壓進棧的一般方法
這裡會優先查詢objectsBackMap這個索引表中查詢這個object是否已經快取,如果已經快取返回對應的index,然後直接在名為luaNet_objects的元表中取第index項,即為要進棧的object 如果當前的索引表中沒有找到這個物件,則說明需要插入一個新的物件,則呼叫pushNewObject
這裡metatable為luaNet_metatable時說明要構建相應型別的元表,名稱為AssemblyQualifiedName,這個元表會包含一個名為cache的table欄位,用來快取方法,和一個已經在全域性域的luaNet_indexfunction作為其__index原方法
由於現在傳入的metatable欄位是luaNet_class,所以是直接取名為luaNet_class的元表(這個元表在ObjectTranslator初始化時已經生成好了,過程在createClassMetatable中
然後將luaNet_class元表賦給新建的userdata,並將這個userdata設定為luaNet_objects元表的第index項,並留一份拷貝在棧上,至此,大工告成,型別成功import。當下次要壓入同樣型別到棧上時,只需要查詢索引表找到index,然後去luaNet_objects元表的第index項直接取就好了。 然後是lua呼叫相應方法和返回值的過程:
1、CLR Object呼叫方法的流程是先呼叫自己的__index元方法,對應luaNet_indexfuction方法,定義如下:
這裡會先取得以自己的元表(o.GetType().AssemblyQualifiedName為名的元表),檢視相應的CSFuntion是否已經快取在cache欄位中,如果有就直接從cache欄位裡取,如果沒有則呼叫get_object_member,利用反射的結構去查詢對應的函式,並將結果和函式體返回到這裡,快取後將函式體返回 其中get_object_member方法值得一提: 它在C#對應的方法為getMethod: 這裡會從堆疊取出物件和函式名,然後開始查詢過程: 大部分情況下會走到這裡: 可以注意到這有一個Hashtable型別的memberCache,其中key為objType,value為另一個子HashTable,這個子HashTable儲存了方法名和對應的LuaCSFuction。memberCache主要是在每次getMethod後快取根據型別和方法名查詢到的方法(LuaCSFuction),下一次在查詢相同的方法時,直接從這個快取表返回,省去了每次都要反射查詢的問題
(C#這裡有memberCache在快取,lua那邊每一個類的元表上還有Cache欄位在快取,這個相互關係待明確) 目前考慮memberCache這邊函式和成員都有快取,Cache這邊只快取函式 這裡Getmember如果成功則將LuaCSFunction和結果入棧,回到剛才的luaNet_indexfuction方法處理 這裡的LuaCSFunction實際上是一個函式代理,執行它就相當執行LuaMethodWrapper的call方法,這是一次反射呼叫,也就是說LuaInterface所有的C#函式呼叫實際上都是反射呼叫 LuaMethodWrapper這個類的作用就是根據提供的型別和方法名,反射呼叫相應的方法,並將結果壓棧給lua 如果返回型別是一個基本型別,就呼叫相應的Lua API入棧:
如果返回型別是一個CLR型別,則會在push時呼叫:
至此就將一個封裝好的object(userdata,同時設定了cache域和繫結__index元方法)壓棧,至此就完成了全部的呼叫過程。 如果有不準確的地方,歡迎大家在下面評論留言討論!