diff --git a/Doc/faq.md b/Doc/faq.md index 494bd3b..af468eb 100644 --- a/Doc/faq.md +++ b/Doc/faq.md @@ -1,32 +1,32 @@ -## 执行Patch for android或者Patch for ios时,报“"please put template file for android/ios in IFixToolKit directory!” +## 鎵цPatch for android鎴栬匬atch for ios鏃讹紝鎶モ"please put template file for android/ios in IFixToolKit directory!鈥 -解决这个错误需要制作一个编译模版文件放到IFixToolKit目录: +瑙e喅杩欎釜閿欒闇瑕佸埗浣滀竴涓紪璇戞ā鐗堟枃浠舵斁鍒癐FixToolKit鐩綍锛 -* 假如你制作的是android的模版,请执行一次普通的android构建,在构建的过程中,到“工程目录/Temp”目录把UnityTempFile打头的文件都拷贝出来,其中一个“UnityTempFile开头”的文件就是你刚刚打包命令行文件,可以根据文件时间或者文件里头的命令行参数(ios会有这么一行:-define:UNITY_IOS,android会有-define:UNITY_ANDROID,而且必须没有UNITY_EDITOR)找到它,拷贝到IFixToolKit目录,如果你在window获取的UnityTempFile,重命名为android.win.tpl,如果是mac下获取的,重命名为android.osx.tpl;(这步对于一个项目,如果你不升级unity版本,不更改条件编译宏,仅需做一次即可) -* ios的模版文件改名为ios.osx.tpl,如果你希望在window下制作ios补丁,复制一份改名为ios.win.tpl,打开这个文件,把链接的引擎dll,系统级dll的路径按window的unity安装目录修改。 +* 鍋囧浣犲埗浣滅殑鏄痑ndroid鐨勬ā鐗堬紝璇锋墽琛屼竴娆℃櫘閫氱殑android鏋勫缓锛屽湪鏋勫缓鐨勮繃绋嬩腑锛屽埌鈥滃伐绋嬬洰褰/Temp鈥濈洰褰曟妸UnityTempFile鎵撳ご鐨勬枃浠堕兘鎷疯礉鍑烘潵锛屽叾涓竴涓淯nityTempFile寮澶粹濈殑鏂囦欢灏辨槸浣犲垰鍒氭墦鍖呭懡浠よ鏂囦欢锛屽彲浠ユ牴鎹枃浠舵椂闂存垨鑰呮枃浠堕噷澶寸殑鍛戒护琛屽弬鏁帮紙ios浼氭湁杩欎箞涓琛岋細-define:UNITY_IOS锛宎ndroid浼氭湁-define:UNITY_ANDROID锛岃屼笖蹇呴』娌℃湁UNITY_EDITOR锛夋壘鍒板畠锛屾嫹璐濆埌IFixToolKit鐩綍锛屽鏋滀綘鍦╳indow鑾峰彇鐨刄nityTempFile锛岄噸鍛藉悕涓篴ndroid.win.tpl锛屽鏋滄槸mac涓嬭幏鍙栫殑锛岄噸鍛藉悕涓篴ndroid.osx.tpl锛涳紙杩欐瀵逛簬涓涓」鐩紝濡傛灉浣犱笉鍗囩骇unity鐗堟湰锛屼笉鏇存敼鏉′欢缂栬瘧瀹忥紝浠呴渶鍋氫竴娆″嵆鍙級 +* ios鐨勬ā鐗堟枃浠舵敼鍚嶄负ios.osx.tpl锛屽鏋滀綘甯屾湜鍦╳indow涓嬪埗浣渋os琛ヤ竵锛屽鍒朵竴浠芥敼鍚嶄负ios.win.tpl锛屾墦寮杩欎釜鏂囦欢锛屾妸閾炬帴鐨勫紩鎿巇ll锛岀郴缁熺骇dll鐨勮矾寰勬寜window鐨剈nity瀹夎鐩綍淇敼銆 -## IL2CPP 出现报错`IL2CPP error for method 'System.Object IFix.Core.EvaluationStackOperation::ToObject(IFix.Core.Value*,IFix.Core.Value*,System.Object[],System.Type,IFix.Core.VirtualMachine,System.Boolean)'` +## IL2CPP 鍑虹幇鎶ラ敊`IL2CPP error for method 'System.Object IFix.Core.EvaluationStackOperation::ToObject(IFix.Core.Value*,IFix.Core.Value*,System.Object[],System.Type,IFix.Core.VirtualMachine,System.Boolean)'` -应该是自己手动编译`IFix.Core.dll`导致 +搴旇鏄嚜宸辨墜鍔ㄧ紪璇慲IFix.Core.dll`瀵艰嚧 -修改iFix.Core源代码后,需要通过`build_for_unity.bat`脚本进行构建 +淇敼iFix.Core婧愪唬鐮佸悗锛岄渶瑕侀氳繃`build_for_unity.bat`鑴氭湰杩涜鏋勫缓 -## 生成 Patch 的时候遇到`Error: the new assembly must not be inject, please reimport the project!`报错 +## 鐢熸垚 Patch 鐨勬椂鍊欓亣鍒癭Error: the new assembly must not be inject, please reimport the project!`鎶ラ敊 -生成 Patch 的 dll,不能进行注入 +杩欎釜dll鎵ц杩囨敞鍏ワ紝涓嶈兘瀵规敞鍏ヨ繃鐨刣ll鐢熸垚patch锛屽湪Unit閫夊伐绋嬫牴鐩綍锛屽彸閿塺eimport -## 补丁制作的条件编译宏如何处理 +## 琛ヤ竵鍒朵綔鐨勬潯浠剁紪璇戝畯濡備綍澶勭悊 -如果是Unity2018.3版本及以上,由于Unity开放了C#编译接口,所以InjectFix在Unity2018.3版本直接支持Android和iOS的补丁生成,直接执行对应菜单即可。 +濡傛灉鏄疷nity2018.3鐗堟湰鍙婁互涓婏紝鐢变簬Unity寮鏀句簡C#缂栬瘧鎺ュ彛锛屾墍浠njectFix鍦║nity2018.3鐗堟湰鐩存帴鏀寔Android鍜宨OS鐨勮ˉ涓佺敓鎴愶紝鐩存帴鎵ц瀵瑰簲鑿滃崟鍗冲彲銆 -但如果低于Unity2018.3版本,则要用比较麻烦的方式:按对应平台的编译参数把Assembly-CSharp.dll编译出来,然后调用IFix.Editor.IFixEditor.GenPatch去生成补丁。 +浣嗗鏋滀綆浜嶶nity2018.3鐗堟湰锛屽垯瑕佺敤姣旇緝楹荤儲鐨勬柟寮忥細鎸夊搴斿钩鍙扮殑缂栬瘧鍙傛暟鎶夾ssembly-CSharp.dll缂栬瘧鍑烘潵锛岀劧鍚庤皟鐢↖Fix.Editor.IFixEditor.GenPatch鍘荤敓鎴愯ˉ涓併 -Unity编译是在工程的Temp目录新建一个文件,把命令行参数放到那个文件,然后执行类似(目录根据自己的unity安装情况而定)如下命令进行编译: +Unity缂栬瘧鏄湪宸ョ▼鐨凾emp鐩綍鏂板缓涓涓枃浠讹紝鎶婂懡浠よ鍙傛暟鏀惧埌閭d釜鏂囦欢锛岀劧鍚庢墽琛岀被浼硷紙鐩綍鏍规嵁鑷繁鐨剈nity瀹夎鎯呭喌鑰屽畾锛夊涓嬪懡浠よ繘琛岀紪璇戯細 ~~~bash "D:\Program Files\Unity201702\Editor\Data\MonoBleedingEdge\bin\mono.exe" "D:\Program Files\Unity201702\Editor\Data\MonoBleedingEdge\lib\mono\4.5\mcs.exe" @Temp/UnityTempFile-55a959adddae39f4aaa18507dd165989 ~~~ -你可以尝试一次编辑器下的手机版本打包,然后到工程目录下的Temp目录把那个临时文件拷贝出来(编译完会自动删掉,所以要手快)。 +浣犲彲浠ュ皾璇曚竴娆$紪杈戝櫒涓嬬殑鎵嬫満鐗堟湰鎵撳寘锛岀劧鍚庡埌宸ョ▼鐩綍涓嬬殑Temp鐩綍鎶婇偅涓复鏃舵枃浠舵嫹璐濆嚭鏉ワ紙缂栬瘧瀹屼細鑷姩鍒犳帀锛屾墍浠ヨ鎵嬪揩锛夈 -这个文件大多数地方都不会变的,变的主要是C#文件列表,可以改为动态生成这个文件:C#文件列表根据当前项目生成,其它保持不变。然后用这个文件作为输入来编译。 +杩欎釜鏂囦欢澶у鏁板湴鏂归兘涓嶄細鍙樼殑锛屽彉鐨勪富瑕佹槸C#鏂囦欢鍒楄〃锛屽彲浠ユ敼涓哄姩鎬佺敓鎴愯繖涓枃浠讹細C#鏂囦欢鍒楄〃鏍规嵁褰撳墠椤圭洰鐢熸垚锛屽叾瀹冧繚鎸佷笉鍙樸傜劧鍚庣敤杩欎釜鏂囦欢浣滀负杈撳叆鏉ョ紪璇戙 diff --git a/Doc/quick_start_en.md b/Doc/quick_start_en.md index c49c4b4..9790bd7 100644 --- a/Doc/quick_start_en.md +++ b/Doc/quick_start_en.md @@ -1,4 +1,4 @@ -锘## Quick Start +## Quick Start ### Access example diff --git a/Doc/user_manual.md b/Doc/user_manual.md new file mode 100644 index 0000000..aa071d0 --- /dev/null +++ b/Doc/user_manual.md @@ -0,0 +1,331 @@ +# IFix浣跨敤鎵嬪唽 + +### [IFix.Patch] + +##### 鐢ㄩ + +鈥 鍦ㄨˉ涓侀樁娈典娇鐢紱鍘熺敓浠g爜淇銆傚鏋滃彂鐜版煇涓嚱鏁版湁閿欒锛屽氨鍙互浣跨敤璇ユ爣绛剧粰鍑芥暟鎵撹ˉ涓侊紝鎵撲笂杩欎釜鏍囩鐨勫嚱鏁帮紝绔ラ瀷浠氨鍙互闅忔剰淇敼璇ュ嚱鏁般 + +##### 鐢ㄦ硶 + +鈥 璇ユ爣绛惧彧鑳界敤鍦ㄦ柟娉曚笂锛岀洿鎺ュ湪瑕佷慨鏀圭殑鍑芥暟涓婇潰鏍囨敞涓涓嬭繖涓爣绛惧嵆鍙 + +##### 涓句緥 + +鈥 杩欎釜鍑芥暟鏈潵鐨勬剰鎬濇槸涓や釜鍊肩浉鍔狅紝浣嗙幇鍦ㄥ啓閿欎簡锛屾墍浠ュ彲浠ョ粰璇ュ嚱鏁版墦涓奫IFix.Patch]鏍囩锛岀劧鍚庝慨鏀瑰氨鍙互浜 + +```c# +public int Add(int a,int b) +{ + return a*b; +} +``` + +```c# +[IFix.Patch] +public int Add(int a,int b) +{ + return a+b; +} +``` + +### [IFix.Interpret] + +##### 鐢ㄩ + +鈥 鍦ㄨˉ涓侀樁娈典娇鐢紱鏂板浠g爜銆傚湪琛ヤ竵闃舵锛岀闉嬩滑杩樻湁鏂扮殑闇姹傦紝鎯虫柊澧炰釜瀛楁锛屽嚱鏁版垨鑰呯被锛屽彲浠ョ敤[IFix.Interpret]鏍囩瀹炵幇銆 + +##### 鐢ㄦ硶 + +鈥 璇ユ爣绛惧彲浠ョ敤鍦ㄥ瓧娈碉紝灞炴э紝鏂规硶锛岀被鍨嬩笂锛岀洿鎺ュ湪瑕佹柊澧炵殑浠g爜涓婇潰鏍囨敞涓涓嬭繖涓爣绛惧嵆鍙 + +##### 涓句緥 + +鈥 鏂板涓涓瓧娈 + +```c# +public class Test +{ + [IFix.Interpret] + public int intValue = 0; +} +``` + +鈥 鏂板涓涓睘鎬 + +```c# +private string name;//杩欎釜name瀛楁鏄師鐢熺殑 + +public string Name +{ + [IFix.Interpret] + set + { + name = value; + } + [IFix.Interpret] + get + { + return name; + } +} + +[IFix.Interpret] +public string Id +{ + set; + get; +} + +``` + +鈥 鏂板涓涓嚱鏁 + +```c# +[IFix.Interpret] +public int Sub(int a,int b) +{ + return a-b; +} +``` + +鈥 鏂板涓涓被 + +```c# +[IFix.Interpret] +public class NewClass +{ + ... +} +``` + +### [IFix.CustomBridge] + +##### 鐢ㄩ + +鈥 鍦ㄦ敞鍏ラ樁娈典娇鐢紱 鎶婁竴涓櫄鎷熸満鐨勭被閫傞厤鍒板師鐢焛nterface鎴栬呮妸涓涓櫄鎷熸満鐨勫嚱鏁伴傞厤鍒板師鐢焏elegate銆 + +鈥 浠涔堟椂鍊欓渶瑕佺敤鍒板憿锛 + +- 淇浠g爜璧嬪间竴涓棴鍖呭埌涓涓猟elegate鍙橀噺锛 +- 淇浠g爜鐨刄nity鍗忕▼鐢ㄤ簡yield return锛 +- 鏂板涓涓嚱鏁帮紝璧嬪煎埌涓涓猟elegate鍙橀噺锛 +- 鏂板涓涓被锛岃祴鍊煎埌涓涓師鐢焛nterface鍙橀噺锛 +- 鏂板鍑芥暟锛岀敤浜唝ield return锛 + +##### 鐢ㄦ硶 + +鈥 璇ユ爣绛惧彧鑳界敤鍦ㄧ被涓婏紝鍦ㄧ闉嬩滑绋嬪簭鐨勬煇涓湴鏂癸紝鍐欎笂涓涓潤鎬佺被锛岄噷闈㈡湁涓涓潤鎬佸瓧娈碉紝鍊煎氨鏄痠nterface鍜宒elegate鐨勭被鍨嬮泦鍚 + + 锛侊紒娉ㄦ剰锛岃閰嶇疆绫讳笉鑳芥斁鍒癊ditor鐩綍锛屼笖涓嶈兘鍐呭祵鍒板彟澶栦竴涓被閲屽ご銆 + +##### 涓句緥 + +鈥 鏂板涓涓被锛岃绫诲疄鐜颁簡涓涓帴鍙 + +```c# +public interface ISubSystem +{ + bool running { get; } + void Print(); +} + +[IFix.Interpret] +public class SubSystem : ISubSystem +{ + public bool running { get { return true; } } + public void Print() + { + UnityEngine.Debug.Log("SubSystem1.Print"); + } +} +``` + +鈥 鏂板鍑芥暟锛堟垨鑰呬慨澶嶄唬鐮乕IFix.Patch]鐨刄nity鍗忕▼锛夛紝鐢ㄥ埌浜 yield return + +```c# +[IFix.Interpret] +public IEnumerator TestInterface() +{ + yield return new WaitForSeconds(1); + UnityEngine.Debug.Log("wait one second"); +} +``` + +鈥 鏂板鍑芥暟锛堟垨鑰呬慨澶嶄唬鐮乕IFix.Patch]锛夛紝璧嬪煎埌涓涓猟elegate鍙橀噺 + +```c# +public class Test +{ + public delegate int MyDelegate(int a, int b); + + [IFix.Interpret] + public MyDelegate TestDelegate() + { + return (a,b) => a + b; + } +} +``` + +```c# +[IFix.CustomBridge] +public static class AdditionalBridge +{ + static List bridge = new List() + { + typeof(ISubSystem), + typeof(IEnumerator), + typeof(Test.MyDelegate) + }; +} +``` + + + +### [Configure] + +##### 鐢ㄩ + +鈥 鍦ㄦ敞鍏ラ樁娈典娇鐢紱閰嶇疆绫伙紝閲岄潰瀛樺偍鐨勬槸涓浜涙敞鍏ユ椂闇瑕佹敞鍏ユ垨杩囨护鐨勪笢瑗裤 + +##### 鐢ㄦ硶 + +鈥 璇ユ爣绛惧彧鑳界敤鍦ㄧ被涓婏紝璇ョ被蹇呴』鍦‥ditor鏂囦欢澶逛笅 銆 + +##### 涓句緥 + +```c# +[Configure] +public class TestCfg +{ + +} +``` + +### [IFix] + +##### 鐢ㄩ + +鈥 鍦ㄦ敞鍏ラ樁娈典娇鐢紱鐢ㄦ潵瀛樺偍鎵鏈変綘璁や负灏嗘潵鍙兘浼氶渶瑕佷慨澶嶇殑绫荤殑闆嗗悎銆傝鏍囩鍜孾IFix.Patch]鏈夊叧鑱旓紝鍥犱负濡傛灉鍙戠幇鏌愪釜鍑芥暟闇瑕佷慨澶嶏紝鐩存帴鎵撲笂[IFix.Patch]鏍囩灏卞彲浠ヤ簡锛屼絾鏄墠鎻愭槸锛岃繖涓渶瑕佷慨澶嶇殑鍑芥暟鐨勭被蹇呴』鍦╗IFix]涓嬨 + +##### 鐢ㄦ硶 + +鈥 璇ユ爣绛惧彧鑳界敤鍦ㄥ睘鎬т笂锛孋onfigure绫讳腑鐨勪竴涓潤鎬佸睘鎬э紝get寰楀埌鐨勬槸鍙兘浼氶渶瑕佷慨澶嶇殑鍑芥暟鎵鏈夌被鐨勯泦鍚 + +##### 涓句緥 + +鈥 璁や负Test绫婚噷闈㈢殑鍑芥暟鍙兘浼氬嚭閿欙紝鎵浠ユ妸瀹冧滑鏀惧埌[IFix]鏍囩涓嬶紝褰揟est绫讳腑鐨凙dd鍑芥暟闇瑕佷慨澶嶏紝鐩存帴鎵撴爣绛句慨鏀瑰嵆鍙 + +```c# +[Configure] +public class TestCfg +{ + [IFix] + static IEnumerable hotfix + { + get + { + return new List() + { + typeof(Test) + }; + } + } +} + +public class Test +{ + [IFix.Patch] + public int Add(int a,int b) + { + return a+b; + } +} +``` + +### [Filter] + +##### 鐢ㄩ + +鈥 鍦ㄦ敞鍏ラ樁娈典娇鐢紱鐢ㄦ潵瀛樺偍鎯宠杩囨护鐨勪笢瑗裤傚湪娉ㄥ叆闃舵锛屽嚒鏄湪[IFix]鏍囩涓嬬殑灞炴ч噷闈㈢殑鍊硷紝閮戒細琚敞鍏ラ傞厤浠g爜锛屼絾鏄鏋滀笉鎯冲鏌愪釜鍑芥暟杩涜娉ㄥ叆锛屽彲浠ョ敤璇ユ爣绛捐繘琛岃繃婊ゃ + +##### 鐢ㄦ硶 + +鈥 璇ユ爣绛惧彧鑳界敤鍦ㄦ柟娉曚笂锛孋onfigure绫讳腑鐨勪竴涓潤鎬佹柟娉曘 + +##### 涓句緥 + +鈥 瑙夊緱Test绫婚噷鐨勫嚱鏁板彲鑳戒細闇瑕佷慨澶嶏紝浣嗘槸Test绫婚噷闈㈢殑Div鍜孧ult涓嶅彲鑳芥湁闂锛屽彲浠ユ妸杩欎袱涓嚱鏁拌繃婊ゆ帀銆 + +```c# +public class Test +{ + [IFix.Patch] + public int Add(int a,int b) + { + return a+b; + } + public int Sub(int a,int b) + { + return a-b; + } + public int Div(int a,int b) + { + return a/b; + } + public int Mult(int a,int b) + { + return a*b; + } +} + +[Configure] +public class TestCfg +{ + [IFix] + static IEnumerable hotfix + { + get + { + return new List() + { + typeof(Test) + }; + } + } + [Filter] + static bool Filter(System.Reflection.MethodInfo methodInfo) + { + return methodInfo.DeclaringType.FullName == "Test" + && (methodInfo.Name == "Div" || methodInfo.Name == "Mult"); + } +} + +``` + + + +### 娉ㄦ剰浜嬮」 + +- 濡傛灉瑙夊緱鏌愪釜绫荤殑鍑芥暟鍙兘浼氶渶瑕佷慨澶嶏紝閭d箞涓瀹氳鎶婅绫绘斁鍒癊ditor鐩綍涓媅Configure]绫荤殑[IFix]闈欐佸瓧娈甸噷锛涚劧鍚庢墠鍙互瀵规煇涓嚱鏁拌繘琛孾IFix.Patch]銆 +- 娑夊強鍒癷nterface鍜宒elegate锛屽鏋滄妸涓涓櫄鎷熸満鐨勭被閫傞厤鍒板師鐢焛nterface鎴栬呮妸涓涓櫄鎷熸満鐨勫嚱鏁伴傞厤鍒板師鐢焏elegate 锛屼竴瀹氳鏀惧埌[IFix.CustomBridge]绫荤殑闈欐佸瓧娈甸噷銆 +- 鎵撲笂[Configure]鏍囩鐨勭被锛屽繀椤绘斁鍦‥ditor鐩綍涓嬨 +- [IFix]锛孾Filter]杩欎簺鏍囩蹇呴』鏀惧湪鎵撲笂[Configure]鏍囩鐨勭被閲屻 +- 鍦╗IFix.Patch]鏃讹紝涓嶆敮鎸佷慨澶嶆硾鍨嬪嚱鏁帮紝涓嶆敮鎸佷慨澶嶆瀯閫犲嚱鏁帮紝涓嶆敮鎸佸湪鍘熺敓绫讳腑鏂板瀛楁銆 +- 鍦╗IFix.Interpret]鏃讹紝涓嶆敮鎸佹柊澧炵被缁ф壙鍘熺敓绫伙紝涓嶆敮鎸佹柊澧炵被鏄硾鍨嬬被銆 + + + + + +### 鎬荤粨 + +| 鏍囩 | 浣跨敤闃舵 | 鐢ㄩ | 鐢ㄦ硶 | +| :-----------------: | :------: | :------------------------: | :----------------------------------------------------------: | +| [IFix.Patch] | 琛ヤ竵 | 淇鍑芥暟 | 鍙兘鏀惧湪鍑芥暟涓 | +| [IFix.Interpret] | 琛ヤ竵 | 鏂板瀛楁锛屽睘鎬э紝鍑芥暟锛岀被鍨 | 鏀惧湪瀛楁锛屽睘鎬э紝鍑芥暟锛岀被鍨嬩笂 | +| [IFix.CustomBridge] | 娉ㄥ叆 | interface鍜宒elegate妗ユ帴 | 鍙兘鏀惧湪鍗曠嫭鍐欎竴涓潤鎬佺被涓婏紝瀛樺偍铏氭嫙鏈虹殑绫婚傞厤鍒板師鐢焛nterface鎴栬呰櫄鎷熸満鐨勫嚱鏁伴傞厤鍒板師鐢焏elegate锛岃绫讳笉鑳芥斁Editor鐩綍 | +| [Configure] | 娉ㄥ叆 | 閰嶇疆绫 | 鍙兘鏀惧湪鍗曠嫭鍐欎竴涓瓨鏀惧湪Editor鐩綍涓嬬殑绫讳笂 | +| [IFix] | 娉ㄥ叆 | 鍙兘闇瑕佷慨澶嶅嚱鏁扮殑绫荤殑闆嗗悎 | 鍙兘鏀惧湪[Configure]绫荤殑涓涓潤鎬佸睘鎬т笂 | +| [Filter] | 娉ㄥ叆 | 涓嶆兂鍙戠敓娉ㄥ叆鐨勫嚱鏁 | 鍙兘鏀惧湪[Configure]绫荤殑涓涓潤鎬佸嚱鏁颁笂 | + diff --git a/Doc/user_manual_en.md b/Doc/user_manual_en.md new file mode 100644 index 0000000..e8059d9 --- /dev/null +++ b/Doc/user_manual_en.md @@ -0,0 +1,302 @@ +# IFix Manual + +### [IFix.Patch] + +##### use + +鈥 Used in the patch stage,native code fix. If you find an error in a function, you can use the label to patch the function, and the function with this label can modify the function at will. + +##### usage + +鈥 This label can only be used on functions, just mark this label directly on the function to be modified. + +##### eg + +鈥 This function originally meant to add two values, but now it is wrong, so you can label the function with [IFix.Patch] and modify it. + +```c# +public int Add(int a,int b) +{ + return a*b; +} +``` + +```c# +[IFix.Patch] +public int Add(int a,int b) +{ + return a+b; +} +``` + +### [IFix.Interpret] + +##### use + +鈥 Used in the patch stage, add code. In the patch stage, people still have new requirements. If you want to add a function or class, you can use the [IFix.Interpret] label to implement it. + +##### usage + +鈥 This label can be used in properties , functions, and classes. Just mark this label directly on the code to be added. + +##### eg + +鈥 Add a new property + +```c# +private string name;//The name field is native + +public string Name +{ + [IFix.Interpret] + set + { + name = value; + } + [IFix.Interpret] + get + { + return name; + } +} +``` + +鈥 Add a new function + +```c# +[IFix.Interpret] +public int Sub(int a,int b) +{ + return a-b; +} +``` + +鈥 Add a new class + +```c# +[IFix.Interpret] +public class NewClass +{ + ... +} +``` + +### [IFix.CustomBridge] + +##### use + +鈥 Used in the injection stage, adapt a virtual machine class to the native interface or adapt a virtual machine function to the native delegate. + +鈥 When do I need to use it? + +- Fix the code to assign a closure to a delegate variable; +- The Unity coroutine that fixes the code uses yield return; +- Add a function and assign it to a delegate variable; +- Add a new class and assign it to a native interface variable; +- Added function, using yield return; + +##### usage + +鈥 This label can only be used on the class. Somewhere in people's program, write a static class with a static field and the value is the type collection of interface and delegate. + +##### eg + +鈥 Add a new class, which implements an interface. + +```c# +public interface ISubSystem +{ + bool running { get; } + void Print(); +} + +[IFix.Interpret] +public class SubSystem : ISubSystem +{ + public bool running { get { return true; } } + public void Print() + { + UnityEngine.Debug.Log("SubSystem1.Print"); + } +} +``` + +鈥 Add a new function (or Unity coroutine with fixed code [IFix.Patch]), using yield return. + +```c# +[IFix.Interpret] +public IEnumerator TestInterface() +{ + yield return new WaitForSeconds(1); + UnityEngine.Debug.Log("wait one second"); +} +``` + +鈥 Add a new function (or fix the code [IFix.Patch]) and assign it to a delegate variable. + +```c# +public class Test +{ + public delegate int MyDelegate(int a, int b); + + [IFix.Interpret] + public MyDelegate TestDelegate() + { + return (a,b) => a + b; + } +} +``` + +```c# +[IFix.CustomBridge] +public static class AdditionalBridge +{ + static List bridge = new List() + { + typeof(ISubSystem), + typeof(IEnumerator), + typeof(Test.MyDelegate) + }; +} +``` + +### [Configure] + +##### use + +鈥 Used in the injection stage, configuration class, which stores some things that need to be injected or filtered during injection. + +##### usage + +鈥 The label can only be used on the class, the class must be in the Editor folder. + +##### eg + +```c# +[Configure] +public class TestCfg +{ + +} +``` + +### [IFix] + +##### use + +鈥 Used in the injection stage, used to store a collection of all classes that you think may need to be fixed in the future. This label is related to [IFix.Patch], because if you find that a function needs to be fixed, just label the [IFix.Patch] label , but the premise is that the class of the function that needs to be fixed must be under [IFix]. + +##### usage + +鈥 This label can only be used on properties, a static property in the Configure class, get is a collection of all classes of functions that may need to be fixed. + +##### eg + +鈥 I think the functions in the Test class may be wrong, so put them under the [IFix] label. When the Add function in the Test class needs to be fixed, just label it and modify it. + +```c# +[Configure] +public class TestCfg +{ + [IFix] + static IEnumerable hotfix + { + get + { + return new List() + { + typeof(Test) + }; + } + } +} + +public class Test +{ + [IFix.Patch] + public int Add(int a,int b) + { + return a+b; + } +} +``` + +### [Filter] + +##### use + +鈥 Used in the injection stage, used to store what you want to filter. In the injection stage, all values in the properties under the [IFix] label will be injected into the adaptation code, but if you don't want to inject a function, you can use this label to filter. + +##### usage + +鈥 This label can only be used on functions, a static function in the Configure class. + +##### eg + +鈥 I think the functions in the Test class may be wrong, so put them under the [IFix] label. When the Add function in the Test class needs to be fixed, just label it and modify it. + +```c# +public class Test +{ + [IFix.Patch] + public int Add(int a,int b) + { + return a+b; + } + public int Sub(int a,int b) + { + return a-b; + } + public int Div(int a,int b) + { + return a/b; + } + public int Mult(int a,int b) + { + return a*b; + } +} + +[Configure] +public class TestCfg +{ + [IFix] + static IEnumerable hotfix + { + get + { + return new List() + { + typeof(Test) + }; + } + } + [Filter] + static bool Filter(System.Reflection.MethodInfo methodInfo) + { + return methodInfo.DeclaringType.FullName == "Test" + && (methodInfo.Name == "Div" || methodInfo.Name == "Mult"); + } +} +``` + +### Precautions + +- If you think that a function of a certain class may need to be fixed, you must put the class in the [IFix] static field of the [Configure] class in the Editor directory, then you can perform [IFix.Patch] on a certain function. +- When it comes to interface and delegate, if you adapt a virtual machine class to a native interface or adapt a virtual machine function to a native delegate, you must put it in the static field of the [IFix.CustomBridge] class. +- The class marked with [Configure] must be placed in the Editor directory. +- [IFix], [Filter] These labels must be placed in the class marked with [Configure]. +- In [IFix.Patch], it does not support fixing generic functions, repairing constructors, or adding fields to native classes. +- In [IFix.Interpret], new classes are not supported to inherit native classes, and new classes are not supported as generic classes. + +### In conclusion + +| Label | Use stage | Use | Usage | +| :-----------------: | :-------: | :---------------------------------------------------------: | :----------------------------------------------------------: | +| [IFix.Patch] | patch | Fix function | Can only be placed on functions | +| [IFix.Interpret] | patch | New properties, functions, types | On properties, functions, types | +| [IFix.CustomBridge] | inject | interface and delegate bridge | It can only be placed on a separate static class, the class of the storage virtual machine is adapted to the native interface or the function of the virtual machine is adapted to the native delegate | +| [Configure] | inject | Configuration class | Can only be placed on a class that is written separately and stored in the Editor directory | +| [IFix] | inject | The collection of classes that may need to fix the function | Can only be placed on a static property of the [Configure] class | +| [Filter] | inject | Functions that do not want to be injected | Can only be placed on a static function of the [Configure] class | + diff --git a/LICENSE b/LICENSE index 2b192e9..b9b43f8 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ Tencent is pleased to support the open source community by making InjectFix available. -Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. +Copyright (C) 2019 Tencent. All rights reserved. InjectFix is licensed under the MIT License, except for the third-party components listed below which may be subject to thier corresponding license terms. diff --git a/README.md b/README.md index dd46a9b..0d379ed 100644 --- a/README.md +++ b/README.md @@ -31,9 +31,11 @@ * IFixToolKit鎷疯礉鍒癠nity椤圭洰鐨凙ssets鍚岀骇鐩綍 * Assets/IFix锛孉ssets/Plugins鎷疯礉鍒癠nity椤圭洰鐨凙ssets涓 -## 蹇熷叆闂 +## 鏂囨。 -[濡備綍浣跨敤锛焆(./Doc/quick_start.md) +* [蹇熷叆闂╙(./Doc/quick_start.md) +* [浣跨敤鎵嬪唽](./Doc/user_manual.md) +* [FAQ](./Doc/faq.md) ## 鎶鏈敮鎸 diff --git a/README_en.md b/README_en.md index 6d22c08..73c9b06 100644 --- a/README_en.md +++ b/README_en.md @@ -29,7 +29,8 @@ Can be used for bug fixes in Unity, supporting Unity鈥檚 full range * Copy IFixToolKit to a sibling directory of Assets in the Unity project * Copy Assets/IFix and Assets/Plugins under Assets in the Unity project -## Quick Start - -[How to use? ](./Doc/quick_start_en.md) +## Doc +* [Quick Start](./Doc/quick_start_en.md) +* [Manual](./Doc/user_manual_en.md) +* [FAQ](./Doc/faq_en.md) diff --git a/Source/Misc/LiveDotNet/Editor/LiveDotNet.cs b/Source/Misc/LiveDotNet/Editor/LiveDotNet.cs index 2af7dff..da3acdb 100644 --- a/Source/Misc/LiveDotNet/Editor/LiveDotNet.cs +++ b/Source/Misc/LiveDotNet/Editor/LiveDotNet.cs @@ -1,6 +1,6 @@ /* * Tencent is pleased to support the open source community by making InjectFix available. - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2019 Tencent. All rights reserved. * InjectFix is licensed under the MIT License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. */ @@ -14,10 +14,10 @@ namespace IFix.Editor { - //输入三个信息: - // 1、平台(ios、android) - // 2、手机的ip地址 - // 3、端口信息,端口信息要和PatchReceiver配置对应上 + //杈撳叆涓変釜淇℃伅锛 + // 1銆佸钩鍙帮紙ios銆乤ndroid锛 + // 2銆佹墜鏈虹殑ip鍦板潃 + // 3銆佺鍙d俊鎭紝绔彛淇℃伅瑕佸拰PatchReceiver閰嶇疆瀵瑰簲涓 public class LiveDotNet : EditorWindow { private int platformIndex = 0; @@ -41,8 +41,8 @@ void OnGUI() doPatch(); } - //1、IFixEditor.GenPlatformPatch会生成生成补丁文件 - //2、发送给手机 + //1銆両FixEditor.GenPlatformPatch浼氱敓鎴愮敓鎴愯ˉ涓佹枃浠 + //2銆佸彂閫佺粰鎵嬫満 void doPatch() { IFixEditor.Platform platform = platformIndex == 0 ? IFixEditor.Platform.ios : IFixEditor.Platform.android; @@ -60,9 +60,9 @@ void doPatch() File.Delete(patchPath); } - //1、对手机建立TCP链接 - //2、发送整个包 - //3、关闭链接 + //1銆佸鎵嬫満寤虹珛TCP閾炬帴 + //2銆佸彂閫佹暣涓寘 + //3銆佸叧闂摼鎺 void doSend(byte[] bytes, IPEndPoint remoteEndPoint) { try diff --git a/Source/Misc/LiveDotNet/Editor/LiveDotNetConfig.cs b/Source/Misc/LiveDotNet/Editor/LiveDotNetConfig.cs index 306688e..a638cd9 100644 --- a/Source/Misc/LiveDotNet/Editor/LiveDotNetConfig.cs +++ b/Source/Misc/LiveDotNet/Editor/LiveDotNetConfig.cs @@ -1,6 +1,6 @@ /* * Tencent is pleased to support the open source community by making InjectFix available. - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2019 Tencent. All rights reserved. * InjectFix is licensed under the MIT License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. */ @@ -9,8 +9,8 @@ using IFix; using System; -//1、配置类必须打[Configure]标签 -//2、必须放Editor目录 +//1銆侀厤缃被蹇呴』鎵揫Configure]鏍囩 +//2銆佸繀椤绘斁Editor鐩綍 [Configure] public class LiveDotNetConfig { @@ -21,7 +21,7 @@ static IEnumerable hotfix { return new List() { - //演示的类 + //婕旂ず鐨勭被 typeof(RotateCube) }; } diff --git a/Source/Misc/LiveDotNet/PatchReceiver.cs b/Source/Misc/LiveDotNet/PatchReceiver.cs index 9b1a367..f13aaae 100644 --- a/Source/Misc/LiveDotNet/PatchReceiver.cs +++ b/Source/Misc/LiveDotNet/PatchReceiver.cs @@ -1,13 +1,13 @@ /* * Tencent is pleased to support the open source community by making InjectFix available. - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2019 Tencent. All rights reserved. * InjectFix is licensed under the MIT License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. */ -//!!仅仅简单的开tcp,接收到数据没做校验就直接执行 -//!!所以切记这个仅仅用于平时开发调试使用 -//!!最终游戏发布包务必删除 +//!!浠呬粎绠鍗曠殑寮tcp锛屾帴鏀跺埌鏁版嵁娌″仛鏍¢獙灏辩洿鎺ユ墽琛 +//!!鎵浠ュ垏璁拌繖涓粎浠呯敤浜庡钩鏃跺紑鍙戣皟璇曚娇鐢 +//!!鏈缁堟父鎴忓彂甯冨寘鍔″繀鍒犻櫎 #warning "Please remove PatchReceiver.cs from release package." using UnityEngine; @@ -22,32 +22,32 @@ namespace IFix { public class PatchReceiver : MonoBehaviour { - //默认的补丁文件保存名 + //榛樿鐨勮ˉ涓佹枃浠朵繚瀛樺悕 const string PERSISTENT_FILE_NAME = "__LAST_RUN_SAVED_PATCH"; - //接收缓冲区的大小 + //鎺ユ敹缂撳啿鍖虹殑澶у皬 const int BUFFER_SIZE = 1024; Stream patch = null; - //需要跟LiveDotNet.cs的端口配套 + //闇瑕佽窡LiveDotNet.cs鐨勭鍙i厤濂 public int Port = 8080; - //这个设置为true的话,补丁文件会保存到文件,重启应用后仍然生效 + //杩欎釜璁剧疆涓簍rue鐨勮瘽锛岃ˉ涓佹枃浠朵細淇濆瓨鍒版枃浠讹紝閲嶅惎搴旂敤鍚庝粛鐒剁敓鏁 public bool Persistent = false; Socket listener = null; string lastRunSavePath; - //1、监听 - //2、accpet到链接后,接收该链接所有数据 - //3、把数据作为补丁包加载 + //1銆佺洃鍚 + //2銆乤ccpet鍒伴摼鎺ュ悗锛屾帴鏀惰閾炬帴鎵鏈夋暟鎹 + //3銆佹妸鏁版嵁浣滀负琛ヤ竵鍖呭姞杞 void ReceivePatch() { byte[] bytes = new byte[BUFFER_SIZE]; - //监听所有地址 + //鐩戝惉鎵鏈夊湴鍧 IPAddress ipAddress = IPAddress.Parse("0.0.0.0"); IPEndPoint localEndPoint = new IPEndPoint(ipAddress, Port); @@ -107,7 +107,7 @@ void ReceivePatch() } } - //如果设置了持久化,在Awake时加载上次保存的补丁文件 + //濡傛灉璁剧疆浜嗘寔涔呭寲锛屽湪Awake鏃跺姞杞戒笂娆′繚瀛樼殑琛ヤ竵鏂囦欢 void Awake() { lastRunSavePath = Application.persistentDataPath + Path.DirectorySeparatorChar + PERSISTENT_FILE_NAME; @@ -119,7 +119,7 @@ void Awake() } } DontDestroyOnLoad(gameObject); - //启动线程来接收补丁,不卡主线程,两者通过patch变量来交接数据 + //鍚姩绾跨▼鏉ユ帴鏀惰ˉ涓侊紝涓嶅崱涓荤嚎绋嬶紝涓よ呴氳繃patch鍙橀噺鏉ヤ氦鎺ユ暟鎹 new Thread(ReceivePatch).Start(); } diff --git a/Source/Misc/LiveDotNet/RotateCube.cs b/Source/Misc/LiveDotNet/RotateCube.cs index caa2c06..cdabd21 100644 --- a/Source/Misc/LiveDotNet/RotateCube.cs +++ b/Source/Misc/LiveDotNet/RotateCube.cs @@ -1,6 +1,6 @@ /* * Tencent is pleased to support the open source community by making InjectFix available. - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2019 Tencent. All rights reserved. * InjectFix is licensed under the MIT License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. */ @@ -8,7 +8,7 @@ using UnityEngine; using IFix; -//用来演示修改代码后,立即刷新到真机 +//鐢ㄦ潵婕旂ず淇敼浠g爜鍚庯紝绔嬪嵆鍒锋柊鍒扮湡鏈 public class RotateCube : MonoBehaviour { public Light theLight; @@ -16,9 +16,9 @@ public class RotateCube : MonoBehaviour [Patch] void Update() { - //旋转 + //鏃嬭浆 transform.Rotate(Vector3.up * Time.deltaTime * 20); - //改变颜色 + //鏀瑰彉棰滆壊 theLight.color = new Color(Mathf.Sin(Time.time) / 2 + 0.5f, 0, 0, 1); } } diff --git a/Source/UnityProj/Assets/Helloworld/Calc.cs b/Source/UnityProj/Assets/Helloworld/Calc.cs index 3c58bce..d92ac12 100644 --- a/Source/UnityProj/Assets/Helloworld/Calc.cs +++ b/Source/UnityProj/Assets/Helloworld/Calc.cs @@ -1,16 +1,16 @@ /* * Tencent is pleased to support the open source community by making InjectFix available. - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2019 Tencent. All rights reserved. * InjectFix is licensed under the MIT License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. */ namespace IFix.Test { - //HelloworldCfg.cs里配置了这个类型 + //HelloworldCfg.cs閲岄厤缃簡杩欎釜绫诲瀷 public class Calculator { - //修改成正确的逻辑后,打开如下注释,生成的补丁将修正该函数 + //淇敼鎴愭纭殑閫昏緫鍚庯紝鎵撳紑濡備笅娉ㄩ噴锛岀敓鎴愮殑琛ヤ竵灏嗕慨姝h鍑芥暟 //[Patch] public int Add(int a, int b) { @@ -21,5 +21,15 @@ public int Sub(int a, int b) { return a / b; } + + public int Mult(int a, int b) + { + return a * b; + } + + public int Div(int a, int b) + { + return a / b; + } } } diff --git a/Source/UnityProj/Assets/Helloworld/Editor/HelloworldCfg.cs b/Source/UnityProj/Assets/Helloworld/Editor/HelloworldCfg.cs index 06f3467..3af5f17 100644 --- a/Source/UnityProj/Assets/Helloworld/Editor/HelloworldCfg.cs +++ b/Source/UnityProj/Assets/Helloworld/Editor/HelloworldCfg.cs @@ -1,6 +1,6 @@ /* * Tencent is pleased to support the open source community by making InjectFix available. - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2019 Tencent. All rights reserved. * InjectFix is licensed under the MIT License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. */ @@ -9,8 +9,8 @@ using IFix; using System; -//1、配置类必须打[Configure]标签 -//2、必须放Editor目录 +//1銆侀厤缃被蹇呴』鎵揫Configure]鏍囩 +//2銆佸繀椤绘斁Editor鐩綍 [Configure] public class HelloworldCfg { @@ -23,9 +23,16 @@ static IEnumerable hotfix { typeof(Helloworld), typeof(IFix.Test.Calculator), - //AnotherClass在Pro Standard Assets下,会编译到Assembly-CSharp-firstpass.dll下,用来演示多dll的修复 + //AnotherClass鍦≒ro Standard Assets涓嬶紝浼氱紪璇戝埌Assembly-CSharp-firstpass.dll涓嬶紝鐢ㄦ潵婕旂ず澶歞ll鐨勪慨澶 typeof(AnotherClass), }; } } + + [IFix.Filter] + static bool Filter(System.Reflection.MethodInfo methodInfo) + { + return methodInfo.DeclaringType.FullName == "IFix.Test.Calculator" + && (methodInfo.Name == "Div" || methodInfo.Name == "Mult"); + } } diff --git a/Source/UnityProj/Assets/Helloworld/Helloworld.cs b/Source/UnityProj/Assets/Helloworld/Helloworld.cs index 9fc99ad..cf17496 100644 --- a/Source/UnityProj/Assets/Helloworld/Helloworld.cs +++ b/Source/UnityProj/Assets/Helloworld/Helloworld.cs @@ -1,6 +1,6 @@ /* * Tencent is pleased to support the open source community by making InjectFix available. - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2019 Tencent. All rights reserved. * InjectFix is licensed under the MIT License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. */ @@ -50,16 +50,16 @@ void test() var anotherClass = new AnotherClass(1); //AnotherClass in Assembly-CSharp-firstpass.dll var ret = anotherClass.Call(i => i + 1); - UnityEngine.Debug.Log("anotherClass.Call, ret = " + ret); - - //test for InjectFix/Fix(Android) InjectFix/Fix(IOS) Menu for unity 2018.3 or newer + UnityEngine.Debug.Log("anotherClass.Call, ret = " + ret); + + //test for InjectFix/Fix(Android) InjectFix/Fix(IOS) Menu for unity 2018.3 or newer #if UNITY_2018_3_OR_NEWER #if UNITY_IOS UnityEngine.Debug.Log("UNITY_IOS"); -#endif +#endif #if UNITY_EDITOR - UnityEngine.Debug.Log("UNITY_EDITOR"); -#endif + UnityEngine.Debug.Log("UNITY_EDITOR"); +#endif #if UNITY_ANDROID UnityEngine.Debug.Log("UNITY_ANDROID"); #endif diff --git a/Source/UnityProj/Assets/IFix/Editor/Configure.cs b/Source/UnityProj/Assets/IFix/Editor/Configure.cs index 4289d6f..6b033e8 100644 --- a/Source/UnityProj/Assets/IFix/Editor/Configure.cs +++ b/Source/UnityProj/Assets/IFix/Editor/Configure.cs @@ -1,6 +1,6 @@ /* * Tencent is pleased to support the open source community by making InjectFix available. - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2019 Tencent. All rights reserved. * InjectFix is licensed under the MIT License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. */ @@ -39,6 +39,11 @@ public class ReverseWrapperAttribute : Attribute { } + [AttributeUsage(AttributeTargets.Method)] + public class FilterAttribute : Attribute + { + } + public static class Configure { // @@ -81,6 +86,29 @@ where type.IsDefined(typeof(ConfigureAttribute), false) return tagsMap; } + public static List GetFilters() + { + var types = from assembly in AppDomain.CurrentDomain.GetAssemblies() + where !(assembly.ManifestModule is System.Reflection.Emit.ModuleBuilder) + from type in assembly.GetTypes() + where type.IsDefined(typeof(ConfigureAttribute), false) + select type; + + List filters = new List(); + foreach (var type in types) + { + foreach (var method in type.GetMethods(BindingFlags.Static | BindingFlags.Public + | BindingFlags.NonPublic | BindingFlags.DeclaredOnly)) + { + if(method.IsDefined(typeof(IFix.FilterAttribute), false)) + { + filters.Add(method); + } + } + } + return filters; + } + public static IEnumerable GetTagMethods(Type tagType, string searchAssembly) { return (from assembly in AppDomain.CurrentDomain.GetAssemblies() @@ -93,5 +121,44 @@ from method in type.GetMethods(BindingFlags.Instance | BindingFlags.Static | Bin where method.IsDefined(tagType, false) select method); } + + public static IEnumerable GetTagFields(Type tagType, string searchAssembly) + { + return (from assembly in AppDomain.CurrentDomain.GetAssemblies() + where !(assembly.ManifestModule is System.Reflection.Emit.ModuleBuilder) + && (assembly.GetName().Name == searchAssembly) + where assembly.CodeBase.IndexOf("ScriptAssemblies") != -1 + from type in assembly.GetTypes() + from field in type.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public + | BindingFlags.NonPublic) + where field.IsDefined(tagType, false) + select field); + } + + public static IEnumerable GetTagProperties(Type tagType, string searchAssembly) + { + return (from assembly in AppDomain.CurrentDomain.GetAssemblies() + where !(assembly.ManifestModule is System.Reflection.Emit.ModuleBuilder) + && (assembly.GetName().Name == searchAssembly) + where assembly.CodeBase.IndexOf("ScriptAssemblies") != -1 + from type in assembly.GetTypes() + from property in type.GetProperties(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public + | BindingFlags.NonPublic) + where property.IsDefined(tagType, false) + select property); + } + + public static IEnumerable GetTagClasses(Type tagType, string searchAssembly) + { + return (from assembly in AppDomain.CurrentDomain.GetAssemblies() + where !(assembly.ManifestModule is System.Reflection.Emit.ModuleBuilder) + && (assembly.GetName().Name == searchAssembly) + where assembly.CodeBase.IndexOf("ScriptAssemblies") != -1 + from type in assembly.GetTypes() + where type.IsDefined(tagType, false) + select type + ); + + } } } diff --git a/Source/UnityProj/Assets/IFix/Editor/ILFixEditor.cs b/Source/UnityProj/Assets/IFix/Editor/ILFixEditor.cs index 02abd2c..ab14aa5 100644 --- a/Source/UnityProj/Assets/IFix/Editor/ILFixEditor.cs +++ b/Source/UnityProj/Assets/IFix/Editor/ILFixEditor.cs @@ -1,6 +1,6 @@ /* * Tencent is pleased to support the open source community by making InjectFix available. - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2019 Tencent. All rights reserved. * InjectFix is licensed under the MIT License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. */ @@ -62,10 +62,13 @@ public class IFixEditor //澶囦唤鏂囦欢鐨勬椂闂存埑鐢熸垚鏍煎紡 const string TIMESTAMP_FORMAT = "yyyyMMddHHmmss"; + //娉ㄥ叆鐨勭洰鏍囨枃浠跺す + private static string targetAssembliesFolder = ""; + //system("mono ifix.exe [args]") public static void CallIFix(List args) { -#if UNITY_EDITOR_OSX +#if UNITY_EDITOR_OSX || UNITY_EDITOR_LINUX var mono_path = Path.Combine(Path.GetDirectoryName(typeof(UnityEngine.Debug).Module.FullyQualifiedName), "../MonoBleedingEdge/bin/mono"); if(!File.Exists(mono_path)) @@ -92,9 +95,9 @@ public static void CallIFix(List args) Process hotfix_injection = new Process(); hotfix_injection.StartInfo.FileName = mono_path; #if UNITY_5_6_OR_NEWER - hotfix_injection.StartInfo.Arguments = "--runtime=v4.0.30319 \"" + inject_tool_path + "\" \"" + hotfix_injection.StartInfo.Arguments = "--debug --runtime=v4.0.30319 \"" + inject_tool_path + "\" \"" #else - hotfix_injection.StartInfo.Arguments = "\"" + inject_tool_path + "\" \"" + hotfix_injection.StartInfo.Arguments = "--debug \"" + inject_tool_path + "\" \"" #endif + string.Join("\" \"", args.ToArray()) + "\""; hotfix_injection.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; @@ -149,7 +152,19 @@ public static void InjectAssemblys() UnityEngine.Debug.LogError("compiling or playing"); return; } - InjectAllAssemblys(); + EditorUtility.DisplayProgressBar("Inject", "injecting...", 0); + try + { + InjectAllAssemblys(); + } + catch(Exception e) + { + UnityEngine.Debug.LogError(e); + } + EditorUtility.ClearProgressBar(); +#if UNITY_2019_3_OR_NEWER + EditorUtility.RequestScriptReload(); +#endif } public static bool AutoInject = true; //鍙互鍦ㄥ閮ㄧ鐢ㄦ帀鑷姩娉ㄥ叆 @@ -212,11 +227,15 @@ public static void InjectAssembly(string assembly) "IFix.ReverseWrapperAttribute", }); + var filters = Configure.GetFilters(); + var processCfgPath = "./process_cfg"; //璇ョ▼搴忛泦鏄惁鏈夐厤缃簡浜涚被锛屽鏋滄病鏈夊氨璺宠繃娉ㄥ叆鎿嶄綔 bool hasSomethingToDo = false; + var blackList = new List(); + using (BinaryWriter writer = new BinaryWriter(new FileStream(processCfgPath, FileMode.Create, FileAccess.Write))) { @@ -241,15 +260,35 @@ public static void InjectAssembly(string assembly) { writer.Write(GetCecilTypeName(cfgItem.Key)); writer.Write(cfgItem.Value); + if (filters.Count > 0 && kv.Key == "IFix.IFixAttribute") + { + foreach(var method in cfgItem.Key.GetMethods(BindingFlags.Instance + | BindingFlags.Static | BindingFlags.Public + | BindingFlags.NonPublic | BindingFlags.DeclaredOnly)) + { + foreach(var filter in filters) + { + if ((bool)filter.Invoke(null, new object[] + { + method + })) + { + blackList.Add(method); + } + } + } + } } } + + writeMethods(writer, blackList); } if (hasSomethingToDo) { var core_path = "./Assets/Plugins/IFix.Core.dll"; - var assembly_path = string.Format("./Library/ScriptAssemblies/{0}.dll", assembly); + var assembly_path = string.Format("./Library/{0}/{1}.dll", targetAssembliesFolder, assembly); var patch_path = string.Format("./{0}.ill.bytes", assembly); List args = new List() { "-inject", core_path, assembly_path, processCfgPath, patch_path, assembly_path }; @@ -282,6 +321,8 @@ public static void InjectAllAssemblys() return; } + targetAssembliesFolder = GetScriptAssembliesFolder(); + foreach (var assembly in injectAssemblys) { InjectAssembly(assembly); @@ -292,6 +333,16 @@ public static void InjectAllAssemblys() AssetDatabase.Refresh(); } + private static string GetScriptAssembliesFolder() + { + var assembliesFolder = "PlayerScriptAssemblies"; + if (!Directory.Exists(string.Format("./Library/{0}/", assembliesFolder))) + { + assembliesFolder = "ScriptAssemblies"; + } + return assembliesFolder; + } + //榛樿鐨勬敞鍏ュ強澶囦唤绋嬪簭闆 //鍙﹀鍙互鐩存帴璋冪敤InjectAssembly瀵瑰叾瀹冪▼搴忛泦杩涜娉ㄥ叆銆 static string[] injectAssemblys = new string[] @@ -311,7 +362,7 @@ static void doBackup(string ts) Directory.CreateDirectory(BACKUP_PATH); } - var scriptAssembliesDir = "./Library/ScriptAssemblies/"; + var scriptAssembliesDir = string.Format("./Library/{0}/", targetAssembliesFolder); foreach (var assembly in injectAssemblys) { @@ -342,7 +393,7 @@ static void doBackup(string ts) /// 鏃堕棿鎴 static void doRestore(string ts) { - var scriptAssembliesDir = "./Library/ScriptAssemblies/"; + var scriptAssembliesDir = string.Format("./Library/{0}/", targetAssembliesFolder); foreach (var assembly in injectAssemblys) { @@ -373,7 +424,11 @@ static void doRestore(string ts) //cecil閲岀殑绫诲悕琛ㄧず鍜.net鏍囧噯骞朵笉涓鏍凤紝杩欓噷鍋氫簺杞崲 static string GetCecilTypeName(Type type) { - if (type.IsGenericType) + if (type.IsByRef && type.GetElementType().IsGenericType) + { + return GetCecilTypeName(type.GetElementType()) + "&"; + } + else if (type.IsGenericType) { if (type.IsGenericTypeDefinition) { @@ -532,7 +587,7 @@ private static string getCompileArguments(Platform platform, string output) //TODO: 鐩墠鐨勫仛娉曟尯绻佺悙鐨勶紝闇瑕佺敤鎴峰幓鑾峰彇Unity鐨勭紪璇戝懡浠ゆ枃浠讹紝鏇村ソ鐨勫仛娉曞簲璇ユ槸鐩存帴 public static void Compile(string compileArgFile) { -#if UNITY_EDITOR_OSX +#if UNITY_EDITOR_OSX || UNITY_EDITOR_LINUX var monoPath = Path.Combine(Path.GetDirectoryName(typeof(UnityEngine.Debug).Module.FullyQualifiedName), "../MonoBleedingEdge/bin/mono"); var mcsPath = Path.Combine(Path.GetDirectoryName(typeof(UnityEngine.Debug).Module.FullyQualifiedName), @@ -600,10 +655,10 @@ public static void GenPlatformPatch(Platform platform, string patchOutputDir, scriptCompilationSettings.group = BuildTargetGroup.iOS; scriptCompilationSettings.target = BuildTarget.iOS; } - else - { - scriptCompilationSettings.group = BuildTargetGroup.Standalone; - scriptCompilationSettings.target = BuildTarget.StandaloneWindows; + else + { + scriptCompilationSettings.group = BuildTargetGroup.Standalone; + scriptCompilationSettings.target = BuildTarget.StandaloneWindows; } ScriptCompilationResult scriptCompilationResult = PlayerBuildInterface.CompilePlayerScripts(scriptCompilationSettings, outputDir); @@ -653,6 +708,83 @@ static void writeMethods(BinaryWriter writer, List methods) } } + static void writeFields(BinaryWriter writer, List fields) + { + var fieldGroups = fields.GroupBy(m => m.DeclaringType).ToList(); + writer.Write(fieldGroups.Count); + foreach (var fieldGroup in fieldGroups) + { + writer.Write(GetCecilTypeName(fieldGroup.Key)); + writer.Write(fieldGroup.Count()); + foreach (var field in fieldGroup) + { + writer.Write(field.Name); + writer.Write(GetCecilTypeName(field.FieldType)); + } + } + } + + static void writeProperties(BinaryWriter writer, List properties) + { + var PropertyGroups = properties.GroupBy(m => m.DeclaringType).ToList(); + writer.Write(PropertyGroups.Count); + foreach (var PropertyGroup in PropertyGroups) + { + writer.Write(GetCecilTypeName(PropertyGroup.Key)); + writer.Write(PropertyGroup.Count()); + foreach (var Property in PropertyGroup) + { + writer.Write(Property.Name); + writer.Write(GetCecilTypeName(Property.PropertyType)); + } + } + } + + static void writeClasses(BinaryWriter writer, List classes) + { + writer.Write(classes.Count); + foreach (var classGroup in classes) + { + writer.Write(GetCecilTypeName(classGroup)); + } + } + + static bool hasGenericParameter(Type type) + { + if (type.IsByRef || type.IsArray) + { + return hasGenericParameter(type.GetElementType()); + } + if (type.IsGenericType) + { + foreach (var typeArg in type.GetGenericArguments()) + { + if (hasGenericParameter(typeArg)) + { + return true; + } + } + return false; + } + return type.IsGenericParameter; + } + + static bool hasGenericParameter(MethodBase method) + { + if (method.IsGenericMethodDefinition || method.IsGenericMethod) return true; + if (!method.IsConstructor && hasGenericParameter((method as MethodInfo).ReturnType)) return true; + + foreach (var param in method.GetParameters()) + { + if (hasGenericParameter(param.ParameterType)) + { + return true; + } + } + return false; + + } + /// /// 鐢熸垚patch /// @@ -665,7 +797,7 @@ public static void GenPatch(string assembly, string assemblyCSharpPath string corePath = "./Assets/Plugins/IFix.Core.dll", string patchPath = "Assembly-CSharp.patch.bytes") { var patchMethods = Configure.GetTagMethods(typeof(PatchAttribute), assembly).ToList(); - var genericMethod = patchMethods.FirstOrDefault(m => m.IsGenericMethodDefinition || m.IsGenericMethod); + var genericMethod = patchMethods.FirstOrDefault(m => hasGenericParameter(m)); if (genericMethod != null) { throw new InvalidDataException("not support generic method: " + genericMethod); @@ -677,7 +809,10 @@ public static void GenPatch(string assembly, string assemblyCSharpPath } var newMethods = Configure.GetTagMethods(typeof(InterpretAttribute), assembly).ToList(); - genericMethod = newMethods.FirstOrDefault(m => m.IsGenericMethodDefinition || m.IsGenericMethod); + var newFields = Configure.GetTagFields(typeof(InterpretAttribute), assembly).ToList(); + var newProperties = Configure.GetTagProperties(typeof(InterpretAttribute), assembly).ToList(); + var newClasses = Configure.GetTagClasses(typeof(InterpretAttribute), assembly).ToList(); + genericMethod = newMethods.FirstOrDefault(m => hasGenericParameter(m)); if (genericMethod != null) { throw new InvalidDataException("not support generic method: " + genericMethod); @@ -690,6 +825,9 @@ public static void GenPatch(string assembly, string assemblyCSharpPath { writeMethods(writer, patchMethods); writeMethods(writer, newMethods); + writeFields(writer, newFields); + writeProperties(writer, newProperties); + writeClasses(writer, newClasses); } List args = new List() { "-patch", corePath, assemblyCSharpPath, "null", @@ -717,11 +855,21 @@ select Path.GetDirectoryName(asm.ManifestModule.FullyQualifiedName)).Distinct()) [MenuItem("InjectFix/Fix", false, 2)] public static void Patch() { - foreach (var assembly in injectAssemblys) + EditorUtility.DisplayProgressBar("Generate Patch for Edtior", "patching...", 0); + try + { + foreach (var assembly in injectAssemblys) + { + var assembly_path = string.Format("./Library/{0}/{1}.dll", GetScriptAssembliesFolder(), assembly); + GenPatch(assembly, assembly_path, "./Assets/Plugins/IFix.Core.dll", + string.Format("{0}.patch.bytes", assembly)); + } + } + catch (Exception e) { - GenPatch(assembly, string.Format("./Library/ScriptAssemblies/{0}.dll", assembly), - "./Assets/Plugins/IFix.Core.dll", string.Format("{0}.patch.bytes", assembly)); + UnityEngine.Debug.LogError(e); } + EditorUtility.ClearProgressBar(); } #if UNITY_2018_3_OR_NEWER @@ -729,7 +877,14 @@ public static void Patch() public static void CompileToAndroid() { EditorUtility.DisplayProgressBar("Generate Patch for Android", "patching...", 0); - GenPlatformPatch(Platform.android, ""); + try + { + GenPlatformPatch(Platform.android, ""); + } + catch(Exception e) + { + UnityEngine.Debug.LogError(e); + } EditorUtility.ClearProgressBar(); } @@ -737,7 +892,14 @@ public static void CompileToAndroid() public static void CompileToIOS() { EditorUtility.DisplayProgressBar("Generate Patch for IOS", "patching...", 0); - GenPlatformPatch(Platform.ios, ""); + try + { + GenPlatformPatch(Platform.ios, ""); + } + catch(Exception e) + { + UnityEngine.Debug.LogError(e); + } EditorUtility.ClearProgressBar(); } #endif diff --git a/Source/UnityProj/Assets/NewClass/Editor/NewClassTestCfg.cs b/Source/UnityProj/Assets/NewClass/Editor/NewClassTestCfg.cs new file mode 100644 index 0000000..d836926 --- /dev/null +++ b/Source/UnityProj/Assets/NewClass/Editor/NewClassTestCfg.cs @@ -0,0 +1,21 @@ +锘縰sing System.Collections; +using System.Collections.Generic; +using UnityEngine; +using IFix; +using System; + +[Configure] +public class NewClassTestCfg { + + [IFix] + static IEnumerable hotfix + { + get + { + return new List() + { + typeof(NewClassTest) + }; + } + } +} diff --git a/Source/UnityProj/Assets/NewClass/NewClassTest.cs b/Source/UnityProj/Assets/NewClass/NewClassTest.cs new file mode 100644 index 0000000..cdc97c4 --- /dev/null +++ b/Source/UnityProj/Assets/NewClass/NewClassTest.cs @@ -0,0 +1,146 @@ +锘縰sing System; +using System.Collections.Generic; +using UnityEngine; +using IFix.Core; +using System.IO; + +//1銆佹墽琛岃彍鍗曗淚njectFix/Fix鈥濈敓鎴愯ˉ涓侊紱 +//2銆佹敞閲娾淣ewBehaviourScript鈥濓紝鈥淪ubSystem2鈥濅袱涓被锛屼互鍙奛ewClassTest鐨処nit鍑芥暟閲屽ごnew SubSystem2鐨勯偅琛岃鍙ワ紱 +//3銆佹墽琛岃彍鍗曗淚njectFix/Inject鈥濓紝妯℃嫙绾夸笂娌℃湁鈥淣ewBehaviourScript鈥濓紝鈥淪ubSystem2鈥濈殑鐗堟湰锛 +//4銆丯ewClassTest.cs鎷栧埌鍦烘櫙锛岃繍琛岀湅涓嬫晥鏋滐紝姝ゆ椂鍙姞杞絊ubSystem1锛 +//5銆佹妸鐢熸垚鐨勮ˉ涓佹嫹璐濆埌Resources涓嬶紝鍐嶆杩愯鐪嬩笅鏁堟灉锛 + +public interface IMonoBehaviour +{ + void Start();//绠鍗昫emo锛屽彧瀹氫箟浜哠tart鏂规硶锛屽疄闄匒wake锛孶pdate锛孫nDestroy銆傘傘傞兘绫讳技 + + void Update(); +} + +public interface ISubSystem +{ + bool running { get; } + + void Destroy(); + + void Start(); + + void Stop(); +} + +public class SubSystem1 : ISubSystem +{ + public bool running { get { return true; } } + + public void Start() + { + Debug.Log("SubSystem1.Start"); + } + + public void Stop() + { + Debug.Log("SubSystem1.Stop"); + } + + public void Destroy() + { + Debug.Log("SubSystem1.Destroy"); + } +} + +//[IFix.Interpret] +public class NewBehaviourScript : IMonoBehaviour +{ + private int tick = 0; + + public void Start() + { + Debug.Log("NewBehaviourScript.Start"); + } + + public void Update() + { + if (tick++ % 60 == 0) + { + Debug.Log("NewBehaviourScript.Update"); + } + } +} + +//[IFix.Interpret] +public class SubSystem2 : ISubSystem +{ + public bool running { get { return true; } } + + public void Start() + { + Debug.Log("SubSystem2.Start, create GameObject and attach a NewBehaviourScript"); + var go = new GameObject("hehe"); + var behaviour = go.AddComponent(typeof(VMBehaviourScript)) as VMBehaviourScript; + behaviour.VMMonoBehaviour = new NewBehaviourScript(); + } + + public void Stop() + { + Debug.Log("SubSystem2.Stop"); + } + + public void Destroy() + { + Debug.Log("SubSystem2.Destroy"); + } +} + +public class NewClassTest : MonoBehaviour +{ + List subsystems = new List(); + + void Awake() + { + var patch = Resources.Load("Assembly-CSharp.patch"); + if (patch != null) + { + Debug.Log("loading Assembly-CSharp.patch ..."); + var sw = System.Diagnostics.Stopwatch.StartNew(); + PatchManager.Load(new MemoryStream(patch.bytes)); + Debug.Log("patch Assembly-CSharp.patch, using " + sw.ElapsedMilliseconds + " ms"); + } + Init(); + } + + [IFix.Patch] + private void Init() + { + subsystems.Add(new SubSystem1()); + subsystems.Add(new SubSystem2()); + } + + + void Start() + { + foreach (var subSystem in subsystems) + { + subSystem.Start(); + } + } + + void OnDestroy() + { + foreach (var subSystem in subsystems) + { + subSystem.Destroy(); + } + } +} + + + +[IFix.CustomBridge] +public static class AdditionalBridge +{ + static List bridge = new List() + { + typeof(ISubSystem), + typeof(IMonoBehaviour) + }; +} \ No newline at end of file diff --git a/Source/UnityProj/Assets/NewClass/NewClassTest.cs.meta b/Source/UnityProj/Assets/NewClass/NewClassTest.cs.meta new file mode 100644 index 0000000..39848e8 --- /dev/null +++ b/Source/UnityProj/Assets/NewClass/NewClassTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 979878655427a4b4c835f28982c140ae +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Source/UnityProj/Assets/NewClass/VMBehaviourScript.cs b/Source/UnityProj/Assets/NewClass/VMBehaviourScript.cs new file mode 100644 index 0000000..0b89ca2 --- /dev/null +++ b/Source/UnityProj/Assets/NewClass/VMBehaviourScript.cs @@ -0,0 +1,25 @@ +锘縰sing System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class VMBehaviourScript : MonoBehaviour +{ + public IMonoBehaviour VMMonoBehaviour { get;set; } + + void Start() + { + if (VMMonoBehaviour != null) + { + VMMonoBehaviour.Start(); + } + } + + // Update is called once per frame + void Update() + { + if (VMMonoBehaviour != null) + { + VMMonoBehaviour.Update(); + } + } +} diff --git a/Source/UnityProj/Assets/NewClass/VMBehaviourScript.cs.meta b/Source/UnityProj/Assets/NewClass/VMBehaviourScript.cs.meta new file mode 100644 index 0000000..9f1ef76 --- /dev/null +++ b/Source/UnityProj/Assets/NewClass/VMBehaviourScript.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b02b35b5e1bd10f48b1cdcad58a741d5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Source/UnityProj/Assets/Plugins/IFix.Core.dll b/Source/UnityProj/Assets/Plugins/IFix.Core.dll deleted file mode 100644 index ea63366..0000000 Binary files a/Source/UnityProj/Assets/Plugins/IFix.Core.dll and /dev/null differ diff --git a/Source/UnityProj/Assets/Plugins/IFix.Core.dll.meta b/Source/UnityProj/Assets/Plugins/IFix.Core.dll.meta index 13364df..ae7e328 100644 --- a/Source/UnityProj/Assets/Plugins/IFix.Core.dll.meta +++ b/Source/UnityProj/Assets/Plugins/IFix.Core.dll.meta @@ -1,20 +1,33 @@ fileFormatVersion: 2 guid: 7c6cf326f60d243479929e308c3123fb -timeCreated: 1519909605 -licenseType: Pro PluginImporter: - serializedVersion: 1 + externalObjects: {} + serializedVersion: 2 iconMap: {} executionOrder: {} + defineConstraints: [] isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 platformData: - Any: + - first: + Any: + second: enabled: 1 settings: {} - Editor: + - first: + Editor: Editor + second: enabled: 0 settings: DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU userData: assetBundleName: assetBundleVariant: diff --git a/Source/UnityProj/Assets/Pro Standard Assets/AnotherClass.cs b/Source/UnityProj/Assets/Pro Standard Assets/AnotherClass.cs index 15ae8e9..4b0d42a 100644 --- a/Source/UnityProj/Assets/Pro Standard Assets/AnotherClass.cs +++ b/Source/UnityProj/Assets/Pro Standard Assets/AnotherClass.cs @@ -1,6 +1,6 @@ /* * Tencent is pleased to support the open source community by making InjectFix available. - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2019 Tencent. All rights reserved. * InjectFix is licensed under the MIT License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. */ diff --git a/Source/UnityProj/IFixToolKit/IFix.exe b/Source/UnityProj/IFixToolKit/IFix.exe deleted file mode 100644 index 5514a31..0000000 Binary files a/Source/UnityProj/IFixToolKit/IFix.exe and /dev/null differ diff --git a/Source/UnityProj/IFixToolKit/IFix.exe.mdb b/Source/UnityProj/IFixToolKit/IFix.exe.mdb deleted file mode 100644 index 36f8aee..0000000 Binary files a/Source/UnityProj/IFixToolKit/IFix.exe.mdb and /dev/null differ diff --git a/Source/VSProj/ShuffleInstruction.cs b/Source/VSProj/ShuffleInstruction.cs index aafb25b..73e02c2 100644 --- a/Source/VSProj/ShuffleInstruction.cs +++ b/Source/VSProj/ShuffleInstruction.cs @@ -1,6 +1,6 @@ /* * Tencent is pleased to support the open source community by making InjectFix available. - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2019 Tencent. All rights reserved. * InjectFix is licensed under the MIT License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. */ diff --git a/Source/VSProj/Src/Builder/FileVirtualMachineBuilder.cs b/Source/VSProj/Src/Builder/FileVirtualMachineBuilder.cs index 08f69f9..c960641 100644 --- a/Source/VSProj/Src/Builder/FileVirtualMachineBuilder.cs +++ b/Source/VSProj/Src/Builder/FileVirtualMachineBuilder.cs @@ -1,6 +1,6 @@ -/* +锘/* * Tencent is pleased to support the open source community by making InjectFix available. - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2019 Tencent. All rights reserved. * InjectFix is licensed under the MIT License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. */ @@ -215,13 +215,19 @@ static FieldInfo getRedirectField(MethodBase method) return redirectField; } - static int getMapId(Type idMapType, MethodBase method) + static int getMapId(List idMapArray, MethodBase method) { IDTagAttribute id = Attribute.GetCustomAttribute(method, typeof(IDTagAttribute), false) as IDTagAttribute; int overrideId = id == null ? 0 : id.ID; var fieldName = string.Format("{0}-{1}{2}", method.DeclaringType.FullName.Replace('.', '-') .Replace('+', '-'), method.Name, overrideId); - var field = idMapType.GetField(fieldName); + FieldInfo field = null; + + for (int i = 0; i < idMapArray.Count; i++) + { + field = idMapArray[i].GetField(fieldName); + if (field != null) break; + } if (field == null) { throw new Exception(string.Format("cat not find id field: {0}, for {1}", fieldName, method)); @@ -252,6 +258,10 @@ static int[] readSlotInfo(BinaryReader reader, Dictionary itfMe | BindingFlags.Instance)) { int methodId = reader.ReadInt32(); + if (!itfMethodToId.ContainsKey(method)) + { + throw new Exception("can not find slot for " + method + " of " + itf); + } slots[itfMethodToId[method]] = methodId; //VirtualMachine._Info(string.Format("<<< {0} [{1}]", method, methodId)); } @@ -260,7 +270,7 @@ static int[] readSlotInfo(BinaryReader reader, Dictionary itfMe } // #lizard forgives - unsafe static public VirtualMachine Load(Stream stream) + unsafe static public VirtualMachine Load(Stream stream, bool checkNew = true) { List nativePointers = new List(); @@ -269,6 +279,7 @@ unsafe static public VirtualMachine Load(Stream stream) Type[] externTypes; MethodBase[] externMethods; List exceptionHandlers = new List(); + Dictionary newFieldInfo = new Dictionary(); string[] internStrings; FieldInfo[] fieldInfos; Type[] staticFieldTypes; @@ -365,13 +376,46 @@ unsafe static public VirtualMachine Load(Stream stream) fieldInfos = new FieldInfo[reader.ReadInt32()]; for (int i = 0; i < fieldInfos.Length; i++) { - var type = externTypes[reader.ReadInt32()]; + var isNewField = reader.ReadBoolean(); + var declaringType = externTypes[reader.ReadInt32()]; var fieldName = reader.ReadString(); - fieldInfos[i] = type.GetField(fieldName, + + fieldInfos[i] = declaringType.GetField(fieldName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); - if (fieldInfos[i] == null) + + if(!isNewField) { - throw new Exception("can not load field [" + fieldName + "] of " + type); + if(fieldInfos[i] == null) + { + throw new Exception("can not load field [" + fieldName + "] " + " of " + declaringType); + } + } + else + { + var fieldType = externTypes[reader.ReadInt32()]; + var methodId = reader.ReadInt32(); + + if(fieldInfos[i] == null) + { + newFieldInfo.Add(i, new NewFieldInfo{ + Name = fieldName, + FieldType = fieldType, + DeclaringType = declaringType, + MethodId = methodId, + }); + } + else + { + if(fieldInfos[i].FieldType != fieldType) + { + throw new Exception("can not change existing field [" + declaringType + "." + fieldName + "]'s type " + " from " + fieldInfos[i].FieldType + " to " + fieldType); + } + else + { + if(checkNew) + throw new Exception(declaringType + "." + fieldName + " is expected to be a new field , but it already exists "); + } + } } } @@ -409,16 +453,29 @@ unsafe static public VirtualMachine Load(Stream stream) for (int i = 0; i < anonymousStoreyInfos.Length; i++) { int fieldNum = reader.ReadInt32(); + int[] fieldTypes = new int[fieldNum]; + for (int fieldIdx = 0; fieldIdx < fieldNum; ++fieldIdx) + { + fieldTypes[fieldIdx] = reader.ReadInt32(); + } int ctorId = reader.ReadInt32(); int ctorParamNum = reader.ReadInt32(); var slots = readSlotInfo(reader, itfMethodToId, externTypes, maxId); + int virtualMethodNum = reader.ReadInt32(); + int[] vTable = new int[virtualMethodNum]; + for (int vm = 0 ;vm < virtualMethodNum; vm++) + { + vTable[vm] = reader.ReadInt32(); + } anonymousStoreyInfos[i] = new AnonymousStoreyInfo() { CtorId = ctorId, FieldNum = fieldNum, + FieldTypes = fieldTypes, CtorParamNum = ctorParamNum, - Slots = slots + Slots = slots, + VTable = vTable }; } @@ -436,6 +493,7 @@ unsafe static public VirtualMachine Load(Stream stream) ExceptionHandlers = exceptionHandlers.ToArray(), InternStrings = internStrings, FieldInfos = fieldInfos, + NewFieldInfos = newFieldInfo, AnonymousStoreyInfos = anonymousStoreyInfos, StaticFieldTypes = staticFieldTypes, Cctors = cctors @@ -450,8 +508,15 @@ unsafe static public VirtualMachine Load(Stream stream) } virtualMachine.WrappersManager = wrapperManager; - var idMapTypeName = reader.ReadString(); - var idMapType = Type.GetType(idMapTypeName, true); + var assemblyStr = reader.ReadString(); + var idMapList = new List(); + for(int i = 0; i < 100; i++) + { + + var idMapType = Type.GetType("IFix.IDMAP" + i + assemblyStr, false); + if (idMapType == null) break; + idMapList.Add(idMapType); + } lock (removers) { @@ -496,7 +561,7 @@ unsafe static public VirtualMachine Load(Stream stream) { var fixMethod = readMethod(reader, externTypes); var fixMethodId = reader.ReadInt32(); - var pos = getMapId(idMapType, fixMethod); + var pos = getMapId(idMapList, fixMethod); methodIdArray[i] = fixMethodId; posArray[i] = pos; if (pos > maxPos) @@ -520,6 +585,20 @@ unsafe static public VirtualMachine Load(Stream stream) }; } + if (checkNew) + { + int newClassCount = reader.ReadInt32(); + for (int i = 0; i < newClassCount; i++) + { + var newClassFullName = reader.ReadString(); + var newClassName = Type.GetType(newClassFullName); + if (newClassName != null) + { + throw new Exception(newClassName + " class is expected to be a new class , but it already exists "); + } + } + } + return virtualMachine; } } diff --git a/Source/VSProj/Src/Builder/SimpleVirtualMachineBuilder.cs b/Source/VSProj/Src/Builder/SimpleVirtualMachineBuilder.cs index 6641591..f7273c0 100644 --- a/Source/VSProj/Src/Builder/SimpleVirtualMachineBuilder.cs +++ b/Source/VSProj/Src/Builder/SimpleVirtualMachineBuilder.cs @@ -1,6 +1,6 @@ /* * Tencent is pleased to support the open source community by making InjectFix available. - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2019 Tencent. All rights reserved. * InjectFix is licensed under the MIT License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. */ diff --git a/Source/VSProj/Src/Core/AnonymousStorey.cs b/Source/VSProj/Src/Core/AnonymousStorey.cs index e429c65..646883b 100644 --- a/Source/VSProj/Src/Core/AnonymousStorey.cs +++ b/Source/VSProj/Src/Core/AnonymousStorey.cs @@ -1,6 +1,6 @@ /* * Tencent is pleased to support the open source community by making InjectFix available. - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2019 Tencent. All rights reserved. * InjectFix is licensed under the MIT License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. */ @@ -10,15 +10,43 @@ namespace IFix.Core { - //匿名类模拟 + //鍖垮悕绫绘ā鎷 public class AnonymousStorey { Value[] unmanagedFields; object[] managedFields; - public AnonymousStorey(int fieldNum) + internal int typeId; + protected VirtualMachine virtualMachine; + int equalMethodId; + int finalizeMethodId; + int getHashCodeMethodId; + int toStringMethodId; + + public AnonymousStorey(int fieldNum, int[] fieldTypes,int typeID, int[] vTable, VirtualMachine virtualMachine) { unmanagedFields = new Value[fieldNum]; managedFields = new object[fieldNum]; + for (int i = 0; i < fieldTypes.Length; ++i) + { + if (fieldTypes[i] > 0) + { + unmanagedFields[i].Type = ValueType.ValueType; + unmanagedFields[i].Value1 = i; + int id = fieldTypes[i] - 1; + managedFields[i] = Activator.CreateInstance(virtualMachine.ExternTypes[id]); + } + else if (fieldTypes[i] == -2) + { + unmanagedFields[i].Type = ValueType.Object; + unmanagedFields[i].Value1 = i; + } + } + typeId = typeID; + this.virtualMachine = virtualMachine; + equalMethodId = vTable[0]; + finalizeMethodId = vTable[1]; + getHashCodeMethodId = vTable[2]; + toStringMethodId = vTable[3]; } unsafe internal void Ldfld(int fieldIndex, Value* evaluationStackBase, Value* evaluationStackPointer, @@ -67,13 +95,71 @@ unsafe internal void Set(int fieldIndex, object obj, Type type, VirtualMachine v EvaluationStackOperation.PushObject(b, b + fieldIndex, managedFields, obj, type); } } + + public bool ObjectEquals(object obj) + { + return base.Equals(obj); + } + + public override bool Equals(object obj) + { + if(equalMethodId == -1) + return ObjectEquals(obj); + Call call = Call.Begin(); + call.PushObject(this); + call.PushObject(obj); + virtualMachine.Execute(equalMethodId, ref call, 2, 0); + return call.GetBoolean(0); + } + + public int ObjectGetHashCode() + { + return base.GetHashCode(); + } + + public override int GetHashCode() + { + if(getHashCodeMethodId == -1) + return ObjectGetHashCode(); + Call call = Call.Begin(); + call.PushObject(this); + virtualMachine.Execute(getHashCodeMethodId, ref call, 1, 0); + return call.GetInt32(0); + } + + public string ObjectToString() + { + return base.ToString(); + } + + public override string ToString() + { + if (toStringMethodId == -1) + return ObjectToString(); + Call call = Call.Begin(); + call.PushObject(this); + virtualMachine.Execute(toStringMethodId, ref call, 1, 0); + return call.GetAsType(0); + } + + ~AnonymousStorey() + { + if (finalizeMethodId != -1) + { + Call call = Call.Begin(); + call.PushObject(this); + virtualMachine.Execute(finalizeMethodId, ref call, 1, 0); + } + } } public class AnonymousStoreyInfo { public int FieldNum = 0; + public int[] FieldTypes = null; public int CtorId = 0; public int CtorParamNum = 0; public int[] Slots = null; + public int[] VTable = null; } } \ No newline at end of file diff --git a/Source/VSProj/Src/Core/DataDefine.cs b/Source/VSProj/Src/Core/DataDefine.cs index 140117b..5e9830c 100644 --- a/Source/VSProj/Src/Core/DataDefine.cs +++ b/Source/VSProj/Src/Core/DataDefine.cs @@ -1,6 +1,6 @@ /* * Tencent is pleased to support the open source community by making InjectFix available. - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2019 Tencent. All rights reserved. * InjectFix is licensed under the MIT License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. */ diff --git a/Source/VSProj/Src/Core/GenericDelegate.cs b/Source/VSProj/Src/Core/GenericDelegate.cs index 4eb4c6f..72c70b5 100644 --- a/Source/VSProj/Src/Core/GenericDelegate.cs +++ b/Source/VSProj/Src/Core/GenericDelegate.cs @@ -1,6 +1,6 @@ /* * Tencent is pleased to support the open source community by making InjectFix available. - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2019 Tencent. All rights reserved. * InjectFix is licensed under the MIT License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. */ @@ -10,22 +10,22 @@ using System.Linq; using System.Reflection; -//ifix会分析闭包,针对闭包对应的delegate生成适配器 -//但是有的情况是原来那个地方不是用闭包,修复时用了闭包,这时会报找不到适配器的错误。 -//这种问题可以通过CustomBridage配置来避免,但是很多时候用户无法预知这种情况。 -//这里就是为了减少这种情况的影响:参数个数不超过4个,且均为引用类型, -//无返回值或者返回值是引用类型,这里能够做到(通过泛型)自动生成适配器。 +//ifix浼氬垎鏋愰棴鍖咃紝閽堝闂寘瀵瑰簲鐨刣elegate鐢熸垚閫傞厤鍣 +//浣嗘槸鏈夌殑鎯呭喌鏄師鏉ラ偅涓湴鏂逛笉鏄敤闂寘锛屼慨澶嶆椂鐢ㄤ簡闂寘锛岃繖鏃朵細鎶ユ壘涓嶅埌閫傞厤鍣ㄧ殑閿欒銆 +//杩欑闂鍙互閫氳繃CustomBridage閰嶇疆鏉ラ伩鍏嶏紝浣嗘槸寰堝鏃跺欑敤鎴锋棤娉曢鐭ヨ繖绉嶆儏鍐点 +//杩欓噷灏辨槸涓轰簡鍑忓皯杩欑鎯呭喌鐨勫奖鍝嶏細鍙傛暟涓暟涓嶈秴杩4涓紝涓斿潎涓哄紩鐢ㄧ被鍨嬶紝 +//鏃犺繑鍥炲兼垨鑰呰繑鍥炲兼槸寮曠敤绫诲瀷锛岃繖閲岃兘澶熷仛鍒帮紙閫氳繃娉涘瀷锛夎嚜鍔ㄧ敓鎴愰傞厤鍣ㄣ namespace IFix.Core { internal class GenericDelegateFactory { - //无返回值泛型方法 + //鏃犺繑鍥炲兼硾鍨嬫柟娉 static MethodInfo[] genericAction = null; - //有返回值泛型方法 + //鏈夎繑鍥炲兼硾鍨嬫柟娉 static MethodInfo[] genericFunc = null; - //泛型delegate适配器构造器的缓存 + //娉涘瀷delegate閫傞厤鍣ㄦ瀯閫犲櫒鐨勭紦瀛 static Dictionary> genericDelegateCreatorCache = new Dictionary>(); @@ -54,7 +54,7 @@ internal static Delegate Create(Type delegateType, VirtualMachine virtualMachine Func genericDelegateCreator; if (!genericDelegateCreatorCache.TryGetValue(delegateType, out genericDelegateCreator)) { - //如果泛型方法数组未初始化 + //濡傛灉娉涘瀷鏂规硶鏁扮粍鏈垵濮嬪寲 if (genericAction == null) { PreventStripping(null); @@ -73,20 +73,20 @@ internal static Delegate Create(Type delegateType, VirtualMachine virtualMachine || parameters.Any(p => p.ParameterType.IsValueType || p.ParameterType.IsByRef) ) { - //如果不在支持的范围,则生成一个永远返回空的构造器 + //濡傛灉涓嶅湪鏀寔鐨勮寖鍥达紝鍒欑敓鎴愪竴涓案杩滆繑鍥炵┖鐨勬瀯閫犲櫒 genericDelegateCreator = (x) => null; } else { if (delegateMethod.ReturnType == typeof(void) && parameters.Length == 0) { - //对无参无返回值特殊处理 + //瀵规棤鍙傛棤杩斿洖鍊肩壒娈婂鐞 var methodInfo = genericAction[0]; genericDelegateCreator = (o) => Delegate.CreateDelegate(delegateType, o, methodInfo); } else { - //根据参数个数,返回值找到泛型实现 + //鏍规嵁鍙傛暟涓暟锛岃繑鍥炲兼壘鍒版硾鍨嬪疄鐜 var typeArgs = parameters.Select(pinfo => pinfo.ParameterType); MethodInfo genericMethodInfo = null; if (delegateMethod.ReturnType == typeof(void)) @@ -96,39 +96,39 @@ internal static Delegate Create(Type delegateType, VirtualMachine virtualMachine else { genericMethodInfo = genericFunc[parameters.Length]; - //如果是有返回值,需要加上返回值作为泛型实参 + //濡傛灉鏄湁杩斿洖鍊硷紝闇瑕佸姞涓婅繑鍥炲间綔涓烘硾鍨嬪疄鍙 typeArgs = typeArgs.Concat(new Type[] { delegateMethod.ReturnType }); } - //实例化泛型方法 + //瀹炰緥鍖栨硾鍨嬫柟娉 var methodInfo = genericMethodInfo.MakeGenericMethod(typeArgs.ToArray()); - //构造器 + //鏋勯犲櫒 genericDelegateCreator = (o) => Delegate.CreateDelegate(delegateType, o, methodInfo); } } - //缓存构造器,下次调用直接返回 + //缂撳瓨鏋勯犲櫒锛屼笅娆¤皟鐢ㄧ洿鎺ヨ繑鍥 genericDelegateCreatorCache[delegateType] = genericDelegateCreator; } - //创建delegate + //鍒涘缓delegate return genericDelegateCreator(new GenericDelegate(virtualMachine, methodId, anonObj)); } } - //泛型适配器 + //娉涘瀷閫傞厤鍣 internal class GenericDelegate { - //指向的虚拟机对象 + //鎸囧悜鐨勮櫄鎷熸満瀵硅薄 VirtualMachine virtualMachine; - //虚拟机方法id + //铏氭嫙鏈烘柟娉昳d int methodId; - //绑定的匿名对象 + //缁戝畾鐨勫尶鍚嶅璞 object anonObj; - //预计算,是否要把anonObj push的标志未 + //棰勮绠楋紝鏄惁瑕佹妸anonObj push鐨勬爣蹇楁湭 bool pushSelf; - //预计算,如果有anonObj参数个数则要+1 + //棰勮绠楋紝濡傛灉鏈塧nonObj鍙傛暟涓暟鍒欒+1 int extraArgNum; internal GenericDelegate(VirtualMachine virtualMachine, int methodId, object anonObj) @@ -153,16 +153,16 @@ public void Action() public void Action(T1 p1) where T1 : class { - //创建call对象 + //鍒涘缓call瀵硅薄 Call call = Call.Begin(); if (pushSelf) { - //如果有绑定的匿名对象,push + //濡傛灉鏈夌粦瀹氱殑鍖垮悕瀵硅薄锛宲ush call.PushObject(anonObj); } - //push第一个参数 + //push绗竴涓弬鏁 call.PushObject(p1); - //调用指定id的虚拟机方法 + //璋冪敤鎸囧畾id鐨勮櫄鎷熸満鏂规硶 virtualMachine.Execute(methodId, ref call, 1 + extraArgNum); } @@ -237,7 +237,7 @@ public TResult Func(T1 p1) } call.PushObject(p1); virtualMachine.Execute(methodId, ref call, 1 + extraArgNum); - //从栈上获取结果 + //浠庢爤涓婅幏鍙栫粨鏋 return (TResult)call.GetObject(); } diff --git a/Source/VSProj/Src/Core/Il2CppSetOptionAttribute.cs b/Source/VSProj/Src/Core/Il2CppSetOptionAttribute.cs index 931c3e0..3554fd1 100644 --- a/Source/VSProj/Src/Core/Il2CppSetOptionAttribute.cs +++ b/Source/VSProj/Src/Core/Il2CppSetOptionAttribute.cs @@ -1,6 +1,6 @@ /* * Tencent is pleased to support the open source community by making InjectFix available. - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2019 Tencent. All rights reserved. * InjectFix is licensed under the MIT License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. */ diff --git a/Source/VSProj/Src/Core/Instruction.cs b/Source/VSProj/Src/Core/Instruction.cs index 56ad778..0c7ef34 100644 --- a/Source/VSProj/Src/Core/Instruction.cs +++ b/Source/VSProj/Src/Core/Instruction.cs @@ -1,6 +1,6 @@ -锘/* +/* * Tencent is pleased to support the open source community by making InjectFix available. - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2019 Tencent. All rights reserved. * InjectFix is licensed under the MIT License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. */ @@ -85,6 +85,8 @@ public enum Code Conv_U4, Conv_U8, Callvirt, + Callvirtvirt, + Ldvirtftn2, Cpobj, Ldobj, Ldstr, diff --git a/Source/VSProj/Src/Core/ObjectClone.cs b/Source/VSProj/Src/Core/ObjectClone.cs index 656cb8c..646dbc7 100644 --- a/Source/VSProj/Src/Core/ObjectClone.cs +++ b/Source/VSProj/Src/Core/ObjectClone.cs @@ -1,6 +1,6 @@ /* * Tencent is pleased to support the open source community by making InjectFix available. - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2019 Tencent. All rights reserved. * InjectFix is licensed under the MIT License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. */ @@ -28,7 +28,7 @@ public ObjectClone() // | BindingFlags.NonPublic); //var p = Expression.Parameter(typeof(object), "obj"); //var mce = Expression.Call(p, methodInfo); - //cloneFunc = Expression.Lambda>(mce, p).Compile();//TODO: 需要用到jit么? + //cloneFunc = Expression.Lambda>(mce, p).Compile();//TODO: 闇瑕佺敤鍒癹it涔堬紵 } public object Clone(object obj) diff --git a/Source/VSProj/Src/Core/ReflectionMethodInvoker.cs b/Source/VSProj/Src/Core/ReflectionMethodInvoker.cs index b1f01e9..f4a6a52 100644 --- a/Source/VSProj/Src/Core/ReflectionMethodInvoker.cs +++ b/Source/VSProj/Src/Core/ReflectionMethodInvoker.cs @@ -1,6 +1,6 @@ /* * Tencent is pleased to support the open source community by making InjectFix available. - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2019 Tencent. All rights reserved. * InjectFix is licensed under the MIT License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. */ @@ -46,7 +46,7 @@ public ReflectionMethodInvoker(MethodBase method) for (int i = 0; i < paramerInfos.Length; i++) { - outFlags[i] = paramerInfos[i].IsOut; + outFlags[i] = !paramerInfos[i].IsIn && paramerInfos[i].IsOut; if (paramerInfos[i].ParameterType.IsByRef) { refFlags[i] = true; @@ -134,7 +134,7 @@ public unsafe void Invoke(VirtualMachine virtualMachine, ref Call call, bool isI if (isInstantiate || (method.IsConstructor && method.DeclaringType.IsValueType)) { - ret = ctor.Invoke(args);//TODO: Delegate创建用Delegate.CreateDelegate + ret = ctor.Invoke(args);//TODO: Delegate鍒涘缓鐢―elegate.CreateDelegate } else { @@ -144,10 +144,10 @@ public unsafe void Invoke(VirtualMachine virtualMachine, ref Call call, bool isI instance = EvaluationStackOperation.ToObject(call.evaluationStackBase, call.argumentBase, managedStack, method.DeclaringType, virtualMachine, false); } - //Nullable仍然是值类型,只是新增了是否为null的标志位,仍然通过传地址的方式进行方法调用, - //但这在反射调用行不通,参数是object类型,boxing到object就是null,所以会触发 - //“Non-static method requires a target”异常 - //所以这只能特殊处理一下 + //Nullable浠嶇劧鏄肩被鍨嬶紝鍙槸鏂板浜嗘槸鍚︿负null鐨勬爣蹇椾綅锛屼粛鐒堕氳繃浼犲湴鍧鐨勬柟寮忚繘琛屾柟娉曡皟鐢紝 + //浣嗚繖鍦ㄥ弽灏勮皟鐢ㄨ涓嶉氾紝鍙傛暟鏄痮bject绫诲瀷锛宐oxing鍒皁bject灏辨槸null锛屾墍浠ヤ細瑙﹀彂 + //鈥淣on-static method requires a target鈥濆紓甯 + //鎵浠ヨ繖鍙兘鐗规畩澶勭悊涓涓 if (isNullableHasValue) { ret = (instance != null); @@ -158,7 +158,14 @@ public unsafe void Invoke(VirtualMachine virtualMachine, ref Call call, bool isI } else { - ret = method.Invoke(instance, args); + if (method.IsStatic == false && instance == null) + { + throw new TargetException(string.Format("can not invoke method [{0}.{1}], Non-static method require instance but got null.", method.DeclaringType, method.Name)); + } + else + { + ret = method.Invoke(instance, args); + } } } @@ -183,7 +190,11 @@ public unsafe void Invoke(VirtualMachine virtualMachine, ref Call call, bool isI } } } - //catch (TargetInvocationException e) + catch (TargetInvocationException e) + { + throw e.InnerException; + } + //catch (TargetException e) //{ // //VirtualMachine._Info("exception method: " + method + ", in " + method.DeclaringType + ", msg:" // + e.InnerException); diff --git a/Source/VSProj/Src/Core/StackOperation.cs b/Source/VSProj/Src/Core/StackOperation.cs index 1a300c0..083067b 100644 --- a/Source/VSProj/Src/Core/StackOperation.cs +++ b/Source/VSProj/Src/Core/StackOperation.cs @@ -1,6 +1,6 @@ /* * Tencent is pleased to support the open source community by making InjectFix available. - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2019 Tencent. All rights reserved. * InjectFix is licensed under the MIT License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. */ @@ -11,6 +11,7 @@ namespace IFix.Core using System.Reflection; using System.Runtime.InteropServices; using System.Threading; + using System.Collections.Generic; [StructLayout(LayoutKind.Sequential)] unsafe struct UnmanagedStack @@ -40,8 +41,8 @@ public ThreadStackInfo() ManagedStack = new object[VirtualMachine.MAX_EVALUATION_STACK_SIZE]; } - //去掉析构,正常而言,静态变量不会析构,如果整个虚拟机释放的话,通过Marshal.AllocHGlobal分配的非托管 - //内存应该也会自动释放吧? + //鍘绘帀鏋愭瀯锛屾甯歌岃█锛岄潤鎬佸彉閲忎笉浼氭瀽鏋勶紝濡傛灉鏁翠釜铏氭嫙鏈洪噴鏀剧殑璇濓紝閫氳繃Marshal.AllocHGlobal鍒嗛厤鐨勯潪鎵樼 + //鍐呭瓨搴旇涔熶細鑷姩閲婃斁鍚э紵 //~ThreadStackInfo() //{ // //VirtualMachine._Info("~ThreadStackInfo"); @@ -55,11 +56,11 @@ public ThreadStackInfo() // Marshal.FreeHGlobal(unmanagedStackHandler); //} - //本来ThreadStatic是很合适的方案,但据说Unity下的ThreadStatic会Crash, - //Unity文档:https://docs.unity3d.com/Manual/Attributes.html - //相关issue链接:https://issuetracker.unity3d.com/issues/ + //鏈潵ThreadStatic鏄緢鍚堥傜殑鏂规锛屼絾鎹Unity涓嬬殑ThreadStatic浼欳rash锛 + //Unity鏂囨。锛歨ttps://docs.unity3d.com/Manual/Attributes.html + //鐩稿叧issue閾炬帴锛歨ttps://issuetracker.unity3d.com/issues/ // e-document-threadstatic-attribute-must-not-be-used-i-will-cause-crashes - //issue内容: + //issue鍐呭锛 //This is a known limitation of the liveness check, as the we don't handle thread static or //context static variables as roots when performing the collection. //The crash will happen in mono_unity_liveness_calculation_from_statics @@ -88,6 +89,10 @@ unsafe internal static class EvaluationStackOperation { internal static void UnboxPrimitive(Value* evaluationStackPointer, object obj, Type type) { + if (obj.GetType().IsEnum) + { + obj = Convert.ChangeType(obj, type); + } if (obj is int) { evaluationStackPointer->Type = ValueType.Integer; @@ -162,7 +167,7 @@ internal static void UnboxPrimitive(Value* evaluationStackPointer, object obj, T throw new NotImplementedException("Unbox a " + obj.GetType() + " to " + type); } - internal static object mGet(bool isArray, object root, int layer, int[] fieldIdList, FieldInfo[] fieldInfos) + internal static object mGet(bool isArray, object root, int layer, int[] fieldIdList, FieldInfo[] fieldInfos, Dictionary newFieldInfos) { //Console.WriteLine("mGet " + root); var fieldId = fieldIdList[layer]; @@ -175,21 +180,33 @@ internal static object mGet(bool isArray, object root, int layer, int[] fieldIdL else { var fieldInfo = fieldInfos[fieldId]; + + if(fieldInfo == null) + { + return newFieldInfos[fieldId].GetValue(root); + } + return fieldInfo.GetValue(root); } } else { var fieldInfo = fieldInfos[fieldId]; + + if(fieldInfo == null) + { + return newFieldInfos[fieldId].GetValue(mGet(isArray, root, layer - 1, fieldIdList, fieldInfos, newFieldInfos)); + } + //VirtualMachine._Info("before --- " + fieldInfo); - var ret = fieldInfo.GetValue(mGet(isArray, root, layer - 1, fieldIdList, fieldInfos)); + var ret = fieldInfo.GetValue(mGet(isArray, root, layer - 1, fieldIdList, fieldInfos, newFieldInfos)); //VirtualMachine._Info("after --- " + fieldInfo); return ret; } } internal static void mSet(bool isArray, object root, object val, int layer, int[] fieldIdList, - FieldInfo[] fieldInfos) + FieldInfo[] fieldInfos, Dictionary newFieldInfos) { var fieldId = fieldIdList[layer]; if (layer == 0) @@ -201,22 +218,37 @@ internal static void mSet(bool isArray, object root, object val, int layer, int[ else { var fieldInfo = fieldInfos[fieldId]; - //VirtualMachine._Info("set1 " + val.GetType() + " to " + fieldInfo + " of " + root.GetType() - // + ", root.hc = " + root.GetHashCode()); - fieldInfo.SetValue(root, val); + + if(fieldInfo == null) + { + newFieldInfos[fieldId].SetValue(root, val); + } + else + { + //VirtualMachine._Info("set1 " + val.GetType() + " to " + fieldInfo + " of " + root.GetType() + // + ", root.hc = " + root.GetHashCode()); + fieldInfo.SetValue(root, val); + } } } else { var fieldInfo = fieldInfos[fieldId]; //VirtualMachine._Info("before get " + fieldInfo); - var parent = mGet(isArray, root, layer - 1, fieldIdList, fieldInfos); + var parent = mGet(isArray, root, layer - 1, fieldIdList, fieldInfos, newFieldInfos); //VirtualMachine._Info("after get " + fieldInfo); //VirtualMachine._Info("before set " + fieldInfo); - fieldInfo.SetValue(parent, val); + if(fieldInfo == null) + { + newFieldInfos[fieldId].SetValue(parent, val); + } + else + { + fieldInfo.SetValue(parent, val); + } //VirtualMachine._Info("set2 " + val.GetType() + " to " + fieldInfo + " of " + parent.GetType()); //VirtualMachine._Info("after set " + fieldInfo); - mSet(isArray, root, parent, layer - 1, fieldIdList, fieldInfos); + mSet(isArray, root, parent, layer - 1, fieldIdList, fieldInfos, newFieldInfos); } } @@ -224,8 +256,8 @@ internal static void mSet(bool isArray, object root, object val, int layer, int[ internal static unsafe object ToObject(Value* evaluationStackBase, Value* evaluationStackPointer, object[] managedStack, Type type, VirtualMachine virtualMachine, bool valueTypeClone = true) { - //未初始化的local引用可能作为out参数反射调用 - //TODO: 验证值类型out参数,对应参数位置是否可以是null? + //鏈垵濮嬪寲鐨刲ocal寮曠敤鍙兘浣滀负out鍙傛暟鍙嶅皠璋冪敤 + //TODO: 楠岃瘉鍊肩被鍨媜ut鍙傛暟锛屽搴斿弬鏁颁綅缃槸鍚﹀彲浠ユ槸null锛 switch (evaluationStackPointer->Type) { case ValueType.Integer: @@ -331,7 +363,7 @@ internal static unsafe object ToObject(Value* evaluationStackBase, Value* evalua var fieldIdList = fieldAddr.FieldIdList; return mGet(evaluationStackPointer->Value2 != -1, fieldAddr.Object, fieldIdList.Length - 1, - fieldIdList, virtualMachine.fieldInfos); + fieldIdList, virtualMachine.fieldInfos, virtualMachine.newFieldInfos); } else { @@ -339,6 +371,11 @@ internal static unsafe object ToObject(Value* evaluationStackBase, Value* evalua { var fieldInfo = virtualMachine.fieldInfos[evaluationStackPointer->Value2]; var obj = managedStack[evaluationStackPointer->Value1]; + if(fieldInfo == null) + { + virtualMachine.newFieldInfos[evaluationStackPointer->Value2].CheckInit(virtualMachine, obj); + return virtualMachine.newFieldInfos[evaluationStackPointer->Value2].GetValue(obj); + } return fieldInfo.GetValue(obj); } else @@ -358,6 +395,12 @@ internal static unsafe object ToObject(Value* evaluationStackBase, Value* evalua if (fieldIndex >= 0) { var fieldInfo = virtualMachine.fieldInfos[fieldIndex]; + if(fieldInfo == null) + { + virtualMachine.newFieldInfos[fieldIndex].CheckInit(virtualMachine, null); + + return virtualMachine.newFieldInfos[fieldIndex].GetValue(null); + } return fieldInfo.GetValue(null); } else @@ -407,7 +450,7 @@ public static void PushObject(Value* evaluationStackBase, Value* evaluationStack } public static void UpdateReference(Value* evaluationStackBase, Value* evaluationStackPointer, - object[] managedStack, object obj, VirtualMachine virtualMachine, Type type) //反射专用 + object[] managedStack, object obj, VirtualMachine virtualMachine, Type type) //鍙嶅皠涓撶敤 { switch (evaluationStackPointer->Type) { @@ -435,7 +478,7 @@ public static void UpdateReference(Value* evaluationStackBase, Value* evaluation //} mSet(evaluationStackPointer->Value2 != -1, fieldAddr.Object, obj, fieldIdList.Length - 1, - fieldIdList, virtualMachine.fieldInfos); + fieldIdList, virtualMachine.fieldInfos, virtualMachine.newFieldInfos); } else { @@ -444,12 +487,19 @@ public static void UpdateReference(Value* evaluationStackBase, Value* evaluation var fieldInfo = virtualMachine.fieldInfos[evaluationStackPointer->Value2]; - //VirtualMachine._Info("update field: " + fieldInfo); - //VirtualMachine._Info("update field of: " + fieldInfo.DeclaringType); - //VirtualMachine._Info("update ref obj: " - // + managedStack[evaluationStackPointer->Value1]); - //VirtualMachine._Info("update ref obj idx: " + evaluationStackPointer->Value1); - fieldInfo.SetValue(managedStack[evaluationStackPointer->Value1], obj); + if(fieldInfo == null) + { + virtualMachine.newFieldInfos[evaluationStackPointer->Value2].SetValue(managedStack[evaluationStackPointer->Value1], obj);; + } + else + { + //VirtualMachine._Info("update field: " + fieldInfo); + //VirtualMachine._Info("update field of: " + fieldInfo.DeclaringType); + //VirtualMachine._Info("update ref obj: " + // + managedStack[evaluationStackPointer->Value1]); + //VirtualMachine._Info("update ref obj idx: " + evaluationStackPointer->Value1); + fieldInfo.SetValue(managedStack[evaluationStackPointer->Value1], obj); + } } else { @@ -460,13 +510,20 @@ public static void UpdateReference(Value* evaluationStackBase, Value* evaluation } break; } - case ValueType.StaticFieldReference://更新完毕,直接return + case ValueType.StaticFieldReference://鏇存柊瀹屾瘯锛岀洿鎺eturn { var fieldIndex = evaluationStackPointer->Value1; if (fieldIndex >= 0) { var fieldInfo = virtualMachine.fieldInfos[evaluationStackPointer->Value1]; - fieldInfo.SetValue(null, obj); + if(fieldInfo == null) + { + virtualMachine.newFieldInfos[evaluationStackPointer->Value1].SetValue(null, obj);; + } + else + { + fieldInfo.SetValue(null, obj); + } } else { @@ -487,7 +544,7 @@ unsafe public struct Call internal object[] managedStack; - internal Value* currentTop;//用于push状态 + internal Value* currentTop;//鐢ㄤ簬push鐘舵 internal Value** topWriteBack; @@ -504,6 +561,18 @@ public static Call Begin() }; } + internal static Call BeginForStack(ThreadStackInfo stack) + { + return new Call() + { + managedStack = stack.ManagedStack, + currentTop = stack.UnmanagedStack->Top, + argumentBase = stack.UnmanagedStack->Top, + evaluationStackBase = stack.UnmanagedStack->Base, + topWriteBack = &(stack.UnmanagedStack->Top) + }; + } + public void PushBoolean(bool b) { currentTop->Value1 = b ? 1 : 0; @@ -706,7 +775,7 @@ public T GetAsType(int offset = 0) return (T)GetObject(offset); } - public void PushObjectAsResult(object obj, Type type) //反射专用 + public void PushObjectAsResult(object obj, Type type) //鍙嶅皠涓撶敤 { EvaluationStackOperation.PushObject(evaluationStackBase, argumentBase, managedStack, obj, type); currentTop = argumentBase + 1; @@ -720,7 +789,7 @@ public void PushRef(int offset) currentTop++; } - public void UpdateReference(int offset, object obj, VirtualMachine virtualMachine, Type type) //反射专用 + public void UpdateReference(int offset, object obj, VirtualMachine virtualMachine, Type type) //鍙嶅皠涓撶敤 { EvaluationStackOperation.UpdateReference(ThreadStackInfo.Stack.UnmanagedStack->Base, argumentBase + offset, managedStack, obj, virtualMachine, type); @@ -728,7 +797,7 @@ public void UpdateReference(int offset, object obj, VirtualMachine virtualMachin public static void End(ref Call call) { - //Top的维护 + //Top鐨勭淮鎶 //ThreadStackInfo.Stack.UnmanagedStack->Top = call.argumentBase; } } diff --git a/Source/VSProj/Src/Core/SwitchFlags.cs b/Source/VSProj/Src/Core/SwitchFlags.cs index 256c92a..93e5a50 100644 --- a/Source/VSProj/Src/Core/SwitchFlags.cs +++ b/Source/VSProj/Src/Core/SwitchFlags.cs @@ -1,6 +1,6 @@ /* * Tencent is pleased to support the open source community by making InjectFix available. - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2019 Tencent. All rights reserved. * InjectFix is licensed under the MIT License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. */ @@ -9,19 +9,19 @@ namespace IFix { - //切换到解析执行 - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] + //鍒囨崲鍒拌В鏋愭墽琛 + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Field)] public class InterpretAttribute : Attribute { } - //直接在要做成补丁的方法上打标签 + //鐩存帴鍦ㄨ鍋氭垚琛ヤ竵鐨勬柟娉曚笂鎵撴爣绛 [AttributeUsage(AttributeTargets.Method)] public class PatchAttribute : Attribute { } - //可以手动指定要生成delegate(主要用于闭包)、interface(比如迭代器语法糖)的桥接 + //鍙互鎵嬪姩鎸囧畾瑕佺敓鎴恉elegate锛堜富瑕佺敤浜庨棴鍖咃級銆乮nterface锛堟瘮濡傝凯浠e櫒璇硶绯栵級鐨勬ˉ鎺 [AttributeUsage(AttributeTargets.Class)] public class CustomBridgeAttribute : Attribute { diff --git a/Source/VSProj/Src/Core/Utils.cs b/Source/VSProj/Src/Core/Utils.cs index 1fe4a16..370b98c 100644 --- a/Source/VSProj/Src/Core/Utils.cs +++ b/Source/VSProj/Src/Core/Utils.cs @@ -1,6 +1,6 @@ /* * Tencent is pleased to support the open source community by making InjectFix available. - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2019 Tencent. All rights reserved. * InjectFix is licensed under the MIT License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. */ @@ -11,15 +11,15 @@ namespace IFix.Core { - //虚拟机使用给工具类 + //铏氭嫙鏈轰娇鐢ㄧ粰宸ュ叿绫 public static class Utils { /// - /// 判断一个方法是否能赋值到一个delegate变量 + /// 鍒ゆ柇涓涓柟娉曟槸鍚﹁兘璧嬪煎埌涓涓猟elegate鍙橀噺 /// - /// delegate变量的类型里头的invoke方法 - /// 待赋值的方法 - /// 是否能赋值 + /// delegate鍙橀噺鐨勭被鍨嬮噷澶寸殑invoke鏂规硶 + /// 寰呰祴鍊肩殑鏂规硶 + /// 鏄惁鑳借祴鍊 public static bool IsAssignable(MethodInfo delegateMethod, MethodInfo method) { if (delegateMethod == null || method == null) @@ -49,15 +49,15 @@ public static bool IsAssignable(MethodInfo delegateMethod, MethodInfo method) return true; } - //适配器的缓存,如果不做缓存,每次都调用IsAssignable一个个的取匹配会非常慢 + //閫傞厤鍣ㄧ殑缂撳瓨锛屽鏋滀笉鍋氱紦瀛橈紝姣忔閮借皟鐢↖sAssignable涓涓釜鐨勫彇鍖归厤浼氶潪甯告參 static Dictionary delegateAdptCache = new Dictionary(); /// - /// 从一个wrapper对象里头,查找能够适配到特定delegate的方法 + /// 浠庝竴涓獁rapper瀵硅薄閲屽ご锛屾煡鎵捐兘澶熼傞厤鍒扮壒瀹歞elegate鐨勬柟娉 /// - /// wrapper对象 - /// delegate类型 - /// 方法前缀,能够排除掉一些方法,比如构造函数 + /// wrapper瀵硅薄 + /// delegate绫诲瀷 + /// 鏂规硶鍓嶇紑锛岃兘澶熸帓闄ゆ帀涓浜涙柟娉曪紝姣斿鏋勯犲嚱鏁 /// public static Delegate TryAdapterToDelegate(object obj, Type delegateType, string perfix) { diff --git a/Source/VSProj/Src/Core/VirtualMachine.cs b/Source/VSProj/Src/Core/VirtualMachine.cs index 12c3982..351cac6 100644 --- a/Source/VSProj/Src/Core/VirtualMachine.cs +++ b/Source/VSProj/Src/Core/VirtualMachine.cs @@ -1,6 +1,6 @@ /* * Tencent is pleased to support the open source community by making InjectFix available. - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2019 Tencent. All rights reserved. * InjectFix is licensed under the MIT License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. */ @@ -12,6 +12,7 @@ namespace IFix.Core using System.Collections.Generic; using System.Reflection; using System.IO; + using System.Linq; class RuntimeException : Exception { @@ -26,6 +27,159 @@ internal class FieldAddr public int[] FieldIdList; } + public class Cleanner + { + private static bool start = false; + + public static void Start() + { + if(!start) + { + start = true; + new Cleanner(); + } + } + + public static void Stop() + { + start = false; + } + + ~Cleanner() + { + if(start) + { + NewFieldInfo.Sweep(); + Start(); + } + } + } + + public class NewFieldInfo + { + public string Name; + public Type DeclaringType; + public Type FieldType; + public int MethodId; + + static readonly int staticObjectKey = 0; + + static readonly ThreadStackInfo stack = new ThreadStackInfo(); + + static readonly Dictionary> newFieldValues = new Dictionary>(); + + static readonly Dictionary objList = new Dictionary(); + + private object SetDefaultValue(object obj) + { + if(FieldType.IsValueType) + { + var ret = Activator.CreateInstance(FieldType); + SetValue(obj, ret); + return ret; + } + else + { + SetValue(obj, null); + return null; + } + } + + public static void Sweep() + { + foreach (var item in objList.ToList()) + { + if(!item.Value.IsAlive) + { + newFieldValues.Remove(item.Key); + objList.Remove(item.Key); + } + } + } + + public unsafe void CheckInit(VirtualMachine virtualMachine, object obj) + { + if( MethodId >= 0 && !HasInitialize(obj) ) + { + Call call = Call.BeginForStack(stack); + virtualMachine.Execute(MethodId, ref call, 0, 0); + SetValue(obj, call.GetObject()); + } + } + + public int ObjectToIndex(object obj) + { + if(obj == null) + { + return staticObjectKey; + } + + return obj.GetHashCode(); + } + + public bool HasInitialize(object obj) + { + var index = ObjectToIndex(obj); + + if(!newFieldValues.ContainsKey(index)) + { + return false; + } + + Dictionary fieldValues = null; + newFieldValues.TryGetValue(index, out fieldValues); + if(!fieldValues.ContainsKey(Name)) + { + return false; + } + + return true; + } + + public object GetValue(object obj) + { + var index = ObjectToIndex(obj); + + Dictionary fieldValues = null; + newFieldValues.TryGetValue(index, out fieldValues); + if(fieldValues != null) + { + object val = null; + if(!fieldValues.TryGetValue(Name, out val)) + { + return SetDefaultValue(obj); + } + + return val; + } + else + { + return SetDefaultValue(obj); + } + } + + public void SetValue(object obj, object value) + { + var index = ObjectToIndex(obj); + + if(!newFieldValues.ContainsKey(index)) + { + newFieldValues.Add(index, new Dictionary()); + + if(obj != null) + { + objList.Add(index, new WeakReference(obj)); + } + } + + newFieldValues[index][Name] = value; + + // if(obj.GetType() != DeclaringType || (value != null && value.GetType() != FieldType)) + // { + // } + } + } + unsafe public class VirtualMachine { Instruction tellUnity4IncludeInstructionFisrt; @@ -50,6 +204,8 @@ unsafe public class VirtualMachine internal FieldInfo[] fieldInfos; + internal Dictionary newFieldInfos; + AnonymousStoreyInfo[] anonymousStoreyInfos; Dictionary> overrideCache @@ -124,6 +280,18 @@ public FieldInfo[] FieldInfos } } + public Dictionary NewFieldInfos + { + get + { + return newFieldInfos; + } + set + { + newFieldInfos = value; + } + } + public AnonymousStoreyInfo[] AnonymousStoreyInfos { get @@ -177,11 +345,15 @@ internal VirtualMachine(Instruction** unmanaged_codes, Action on_dispose) { unmanagedCodes = unmanaged_codes; onDispose = on_dispose; + + Cleanner.Start(); } ~VirtualMachine() { onDispose(); + + Cleanner.Stop(); unmanagedCodes = null; } @@ -211,7 +383,8 @@ void store(Value* stackBase, Value* dst, Value* src, object[] managedStack) *dst = *src; if (dst->Type >= ValueType.Object) { - var obj = (dst->Type == ValueType.ValueType) ? objectClone.Clone(managedStack[src->Value1]) + var obj = (dst->Type == ValueType.ValueType && managedStack[src->Value1] != null) //Nullable box鍚庡彲鑳戒负绌 + ? objectClone.Clone(managedStack[src->Value1]) : managedStack[src->Value1]; var dstPos = dst->Value1 = (int)(dst - stackBase); managedStack[dstPos] = obj; @@ -230,7 +403,9 @@ void copy(Value* stackBase, Value* dst, Value* src, object[] managedStack) *dst = *src; if (dst->Type == ValueType.ValueType) { - var obj = objectClone.Clone(managedStack[src->Value1]); + object obj = null; + if (managedStack[src->Value1] != null) //Nullable box鍚庡彲鑳戒负绌 + obj = objectClone.Clone(managedStack[src->Value1]); var dstPos = dst->Value1 = (int)(dst - stackBase); managedStack[dstPos] = obj; } @@ -633,6 +808,40 @@ public static void _Info(string a) //printStack("ret", evaluationStackPointer - 1); } break; + + case Code.Callvirtvirt: + { + int narg = pc->Operand >> 16; + var arg0 = evaluationStackPointer - narg; + if (arg0->Type != ValueType.Object) + { + throwRuntimeException(new InvalidProgramException(arg0->Type.ToString() + + " for Callvirtvirt"), true); + } + if (managedStack[arg0->Value1] == null) + { + throw new NullReferenceException("this is null"); + } + var anonObj = managedStack[arg0->Value1] as AnonymousStorey; + int[] vTable = anonymousStoreyInfos[anonObj.typeId].VTable; + int methodIndexToCall = vTable[pc->Operand & 0xFFFF]; + evaluationStackPointer = Execute(unmanagedCodes[methodIndexToCall], + evaluationStackPointer - narg, managedStack, evaluationStackBase, + narg, methodIndexToCall); + } + break; + + case Code.Ldvirtftn2: + { + int slot = pc->Operand & 0xFFFF; + var pm = evaluationStackPointer - 1; + var po = pm - 1; + var anonObj = managedStack[po->Value1] as AnonymousStorey; + pm->Value1 = anonymousStoreyInfos[anonObj.typeId].VTable[slot]; + pm->Type = ValueType.Integer; + } + break; + case Code.CallExtern://閮ㄥ垎鏉ヨ嚜Call閮ㄥ垎鏉ヨ嚜Callvirt case Code.Newobj: // 2.334642% int methodId = pc->Operand & 0xFFFF; @@ -820,21 +1029,50 @@ public static void _Info(string a) { var fieldInfo = fieldInfos[fieldIndex]; //_Info("Ldfld fieldInfo:" + fieldInfo); + + Type declaringType = null; + Type fieldType = null; + string fieldName = null; + + if(fieldInfo == null) + { + fieldName = newFieldInfos[fieldIndex].Name; + fieldType = newFieldInfos[fieldIndex].FieldType; + declaringType = newFieldInfos[fieldIndex].DeclaringType; + } + else + { + fieldName = fieldInfo.Name; + fieldType = fieldInfo.FieldType; + declaringType = fieldInfo.DeclaringType; + } object obj = EvaluationStackOperation.ToObject(evaluationStackBase, ptr, - managedStack, fieldInfo.DeclaringType, this, false); + managedStack, declaringType, this, false); if (obj == null) { - throw new NullReferenceException(); + throw new NullReferenceException(declaringType + "." + fieldName); } //_Info("Ldfld:" + fieldInfo + ",obj=" + obj.GetType()); - var fieldValue = fieldInfo.GetValue(obj); + object fieldValue = null; + + if(fieldInfo == null) + { + newFieldInfos[fieldIndex].CheckInit(this, obj); + + fieldValue = newFieldInfos[fieldIndex].GetValue(obj); + } + else + { + fieldValue = fieldInfo.GetValue(obj); + } + //_Info("fieldValue:" + fieldValue); //throw new Exception("fieldValue=" + fieldValue); EvaluationStackOperation.PushObject(evaluationStackBase, ptr, managedStack, - fieldValue, fieldInfo.FieldType); + fieldValue, fieldType); } else { @@ -884,25 +1122,51 @@ public static void _Info(string a) { var fieldInfo = fieldInfos[pc->Operand]; + Type declaringType = null; + Type fieldType = null; + string fieldName = null; + + if(fieldInfo == null) + { + fieldName = newFieldInfos[fieldIndex].Name; + fieldType = newFieldInfos[fieldIndex].FieldType; + declaringType = newFieldInfos[fieldIndex].DeclaringType; + } + else + { + fieldName = fieldInfo.Name; + fieldType = fieldInfo.FieldType; + declaringType = fieldInfo.DeclaringType; + } + object obj = EvaluationStackOperation.ToObject(evaluationStackBase, ptr, - managedStack, fieldInfo.DeclaringType, this, false); + managedStack, declaringType, this, false); if (obj == null) { - throw new NullReferenceException(); + throw new NullReferenceException(declaringType + "." + fieldName); + } + + if(fieldInfo != null) + { + fieldInfo.SetValue(obj, EvaluationStackOperation.ToObject(evaluationStackBase, + evaluationStackPointer - 1, managedStack, fieldType, this)); + } + else + { + newFieldInfos[fieldIndex].SetValue(obj, EvaluationStackOperation.ToObject(evaluationStackBase, + evaluationStackPointer - 1, managedStack, fieldType, this)); } - fieldInfo.SetValue(obj, EvaluationStackOperation.ToObject(evaluationStackBase, - evaluationStackPointer - 1, managedStack, fieldInfo.FieldType, this)); //濡傛灉field锛宎rray鍏冪礌鏄肩被鍨嬶紝闇瑕侀噸鏂皍pdate鍥炲幓 if ((ptr->Type == ValueType.FieldReference || ptr->Type == ValueType.ChainFieldReference || ptr->Type == ValueType.StaticFieldReference || ptr->Type == ValueType.ArrayReference) - && fieldInfo.DeclaringType.IsValueType) + && declaringType.IsValueType) { EvaluationStackOperation.UpdateReference(evaluationStackBase, ptr, - managedStack, obj, this, fieldInfo.DeclaringType); + managedStack, obj, this, declaringType); } managedStack[ptr - evaluationStackBase] = null; managedStack[evaluationStackPointer - 1 - evaluationStackBase] = null; @@ -911,7 +1175,9 @@ public static void _Info(string a) else { fieldIndex = -(fieldIndex + 1); - AnonymousStorey anonyObj = managedStack[ptr->Value1] as AnonymousStorey; + object obj = EvaluationStackOperation.ToObject(evaluationStackBase, ptr, + managedStack, ptr->Type.GetType(), this, false); + AnonymousStorey anonyObj = obj as AnonymousStorey; anonyObj.Stfld(fieldIndex, evaluationStackBase, evaluationStackPointer - 1, managedStack); evaluationStackPointer = ptr; @@ -1005,13 +1271,25 @@ public static void _Info(string a) if (fieldIndex >= 0) { var fieldInfo = fieldInfos[fieldIndex]; + Type fieldType = null; + + object fieldValue = null; + if (fieldInfo == null) { - throwRuntimeException(new InvalidProgramException(), true); + newFieldInfos[fieldIndex].CheckInit(this, null); + + fieldType = newFieldInfos[fieldIndex].FieldType; + fieldValue = newFieldInfos[fieldIndex].GetValue(null); + } + else + { + fieldType = fieldInfo.FieldType; + fieldValue = fieldInfo.GetValue(null); } - var fieldValue = fieldInfo.GetValue(null); + EvaluationStackOperation.PushObject(evaluationStackBase, evaluationStackPointer, - managedStack, fieldValue, fieldInfo.FieldType); + managedStack, fieldValue, fieldType); } else { @@ -1193,12 +1471,43 @@ public static void _Info(string a) case Code.Castclass: //0.358122% { var ptr = evaluationStackPointer - 1; - var type = externTypes[pc->Operand]; + var type = pc->Operand >= 0 ? externTypes[pc->Operand] : typeof(AnonymousStorey); var obj = managedStack[ptr->Value1]; - if (obj != null && !type.IsAssignableFrom(obj.GetType())) + if (obj != null) { - throw new InvalidCastException(type + " is not assignable from " - + obj.GetType()); + bool canAssign = type.IsAssignableFrom(obj.GetType()); + if(!canAssign) + { + throw new InvalidCastException(type + " is not assignable from " + + obj.GetType()); + } + + if(canAssign && pc->Operand < 0 && (obj is AnonymousStorey) && (obj as AnonymousStorey).typeId != -(pc->Operand+1) ) + { + var fromInfo = anonymousStoreyInfos[(obj as AnonymousStorey).typeId]; + var targetInfo = anonymousStoreyInfos[-(pc->Operand+1)]; + + if(fromInfo.Slots != null && targetInfo.Slots != null && fromInfo.Slots.Length == targetInfo.Slots.Length) + { + for(int i = 0; i < fromInfo.Slots.Length; ++i) + { + if(fromInfo.Slots[i] != targetInfo.Slots[i]) + { + canAssign = false; + break; + } + } + } + else + { + canAssign = false; + } + + if(!canAssign) + { + throw new InvalidCastException("AnonymousStorey typeid different, " + (obj as AnonymousStorey).typeId + " <-> " + -(pc->Operand+1)); + } + } } } break; @@ -1237,71 +1546,74 @@ public static void _Info(string a) case Code.Box://0.3100877% { var ptr = evaluationStackPointer - 1; - var type = externTypes[pc->Operand]; - var pos = (int)(ptr - evaluationStackBase); - switch (ptr->Type) + if(pc->Operand >= 0) { - case ValueType.ValueType: - case ValueType.Object: - break; - case ValueType.Integer: - if (type.IsEnum) - { - managedStack[pos] = Enum.ToObject(type, ptr->Value1); - } - else if (type == typeof(int)) - { - managedStack[pos] = ptr->Value1; - } - else if (type == typeof(uint)) - { - managedStack[pos] = (uint)ptr->Value1; - } - else - { - managedStack[pos] = Convert.ChangeType(ptr->Value1, type); - } - ptr->Value1 = pos; - break; - case ValueType.Long: - if (type == typeof(long)) - { - managedStack[pos] = *(long*)&ptr->Value1; - } - else if (type == typeof(ulong)) - { - managedStack[pos] = *(ulong*)&ptr->Value1; - } - else if (type.IsEnum) - { - managedStack[pos] = Enum.ToObject(type, *(long*)&ptr->Value1); - } - else if (type == typeof(IntPtr)) - { - managedStack[pos] = new IntPtr(*(long*)&ptr->Value1); - } - else if (type == typeof(UIntPtr)) - { - managedStack[pos] = new UIntPtr(*(ulong*)&ptr->Value1); - } - else - { - managedStack[pos] = Convert.ChangeType(*(long*)&ptr->Value1, type); - } - ptr->Value1 = pos; - break; - case ValueType.Float: - managedStack[pos] = *(float*)&ptr->Value1; - ptr->Value1 = pos; - break; - case ValueType.Double: - managedStack[pos] = *(double*)&ptr->Value1; - ptr->Value1 = pos; - break; - default: - throwRuntimeException(new InvalidProgramException("to box a " + ptr->Type), - true); - break; + var type = externTypes[pc->Operand]; + var pos = (int)(ptr - evaluationStackBase); + switch (ptr->Type) + { + case ValueType.ValueType: + case ValueType.Object: + break; + case ValueType.Integer: + if (type.IsEnum) + { + managedStack[pos] = Enum.ToObject(type, ptr->Value1); + } + else if (type == typeof(int)) + { + managedStack[pos] = ptr->Value1; + } + else if (type == typeof(uint)) + { + managedStack[pos] = (uint)ptr->Value1; + } + else + { + managedStack[pos] = Convert.ChangeType(ptr->Value1, type); + } + ptr->Value1 = pos; + break; + case ValueType.Long: + if (type == typeof(long)) + { + managedStack[pos] = *(long*)&ptr->Value1; + } + else if (type == typeof(ulong)) + { + managedStack[pos] = *(ulong*)&ptr->Value1; + } + else if (type.IsEnum) + { + managedStack[pos] = Enum.ToObject(type, *(long*)&ptr->Value1); + } + else if (type == typeof(IntPtr)) + { + managedStack[pos] = new IntPtr(*(long*)&ptr->Value1); + } + else if (type == typeof(UIntPtr)) + { + managedStack[pos] = new UIntPtr(*(ulong*)&ptr->Value1); + } + else + { + managedStack[pos] = Convert.ChangeType(*(long*)&ptr->Value1, type); + } + ptr->Value1 = pos; + break; + case ValueType.Float: + managedStack[pos] = *(float*)&ptr->Value1; + ptr->Value1 = pos; + break; + case ValueType.Double: + managedStack[pos] = *(double*)&ptr->Value1; + ptr->Value1 = pos; + break; + default: + throwRuntimeException(new InvalidProgramException("to box a " + ptr->Type), + true); + break; + } } ptr->Type = ValueType.Object; } @@ -1309,13 +1621,50 @@ public static void _Info(string a) case Code.Isinst://0.3074192% { var ptr = evaluationStackPointer - 1; - var type = externTypes[pc->Operand]; + var type = pc->Operand >= 0 ? externTypes[pc->Operand] : typeof(AnonymousStorey); var pos = (int)(ptr - evaluationStackBase); var obj = managedStack[ptr->Value1]; ptr->Type = ValueType.Object; ptr->Value1 = pos; - managedStack[pos] = (obj != null && type.IsAssignableFrom(obj.GetType())) - ? obj : null; + if (obj == null) + { + managedStack[pos] = null; + } + else + { + bool canAssign = type.IsAssignableFrom(obj.GetType()); + managedStack[pos] = canAssign + ? obj : null; + if (pc->Operand < 0 && canAssign) + { + if ((obj is AnonymousStorey) && (obj as AnonymousStorey).typeId != -(pc->Operand + 1)) + { + var fromInfo = anonymousStoreyInfos[(obj as AnonymousStorey).typeId]; + var targetInfo = anonymousStoreyInfos[-(pc->Operand + 1)]; + + if (fromInfo.Slots != null && targetInfo.Slots != null && fromInfo.Slots.Length == targetInfo.Slots.Length) + { + for (int i = 0; i < fromInfo.Slots.Length; ++i) + { + if (fromInfo.Slots[i] != targetInfo.Slots[i]) + { + canAssign = false; + break; + } + } + } + else + { + canAssign = false; + } + + if (!canAssign) + { + managedStack[pos] = null; + } + } + } + } } break; case Code.Bge: //Bge_S:0.2954996% Bge:0.005870852% @@ -1468,13 +1817,28 @@ public static void _Info(string a) if (fieldIndex >= 0) { var fieldInfo = fieldInfos[fieldIndex]; + Type filedType = null; + if (fieldInfo == null) { - throwRuntimeException(new InvalidProgramException(), true); + filedType = newFieldInfos[fieldIndex].FieldType; + } + else + { + filedType = fieldInfo.FieldType; } - fieldInfo.SetValue(null, EvaluationStackOperation.ToObject(evaluationStackBase, - evaluationStackPointer - 1, managedStack, fieldInfo.FieldType, this)); + var value = EvaluationStackOperation.ToObject(evaluationStackBase, + evaluationStackPointer - 1, managedStack, filedType, this); + + if (fieldInfo == null) + { + newFieldInfos[fieldIndex].SetValue(null, value); + } + else + { + fieldInfo.SetValue(null, value); + } } else { @@ -1493,18 +1857,36 @@ public static void _Info(string a) case Code.Ldflda: //0.240527% { var fieldInfo = pc->Operand >= 0 ? fieldInfos[pc->Operand] : null; + + Type declaringType = null; + Type fieldType = null; var ptr = evaluationStackPointer - 1; - //鏍堥《涔熸槸瀛楁寮曠敤锛岃屼笖璇ュ瓧娈垫槸鍊肩被鍨嬶紝闇瑕乽pdate涓婂眰瀵硅薄 - if ((ptr->Type == ValueType.FieldReference - || ptr->Type == ValueType.ChainFieldReference - || ptr->Type == ValueType.ArrayReference) && fieldInfo != null - && fieldInfo.FieldType.IsValueType) + + if(pc->Operand >= 0) { - if (pc->Operand < 0) + if(fieldInfo == null) { - throwRuntimeException(new NotSupportedException( - "chain ref for compiler generated object!"), true); + fieldType = newFieldInfos[pc->Operand].FieldType; + declaringType = newFieldInfos[pc->Operand].DeclaringType; } + else + { + fieldType = fieldInfo.FieldType; + declaringType = fieldInfo.DeclaringType; + } + } + + //鏍堥《涔熸槸瀛楁寮曠敤锛岃屼笖璇ュ瓧娈垫槸鍊肩被鍨嬶紝闇瑕乽pdate涓婂眰瀵硅薄 + if ((ptr->Type == ValueType.FieldReference + || ptr->Type == ValueType.ChainFieldReference + || ptr->Type == ValueType.ArrayReference) && pc->Operand >= 0 + && fieldType.IsValueType) + { + // if (pc->Operand < 0) + // { + // throwRuntimeException(new NotSupportedException( + // "chain ref for compiler generated object!"), true); + // } //_Info("mult ref"); //ptr->Value1锛氭寚鍚戝疄闄呭璞 //ptr->Value2锛-1琛ㄧず绗竴涓槸FieldReference锛 -2琛ㄧず鏄疉rrayReference @@ -1542,14 +1924,14 @@ public static void _Info(string a) managedStack[offset] = fieldAddr; ptr->Value2 = ptr->Type == ValueType.FieldReference ? -1 : -2; } - ptr->Type = ValueType.ChainFieldReference; } else { object obj = EvaluationStackOperation.ToObject(evaluationStackBase, ptr, - managedStack, fieldInfo == null ? typeof(AnonymousStorey) - : fieldInfo.DeclaringType, this, false); + managedStack, pc->Operand < 0 ? typeof(AnonymousStorey) + : declaringType, this, false); + ptr->Type = ValueType.FieldReference; ptr->Value1 = (int)(ptr - evaluationStackBase); managedStack[ptr->Value1] = obj; @@ -1732,12 +2114,29 @@ public static void _Info(string a) { var fieldAddr = managedStack[ptr - evaluationStackBase] as FieldAddr; var fieldIdList = fieldAddr.FieldIdList; - fieldType - = fieldInfos[fieldIdList[fieldIdList.Length - 1]].FieldType; + + if(fieldInfos[fieldIdList[fieldIdList.Length - 1]] == null) + { + fieldType + = newFieldInfos[fieldIdList[fieldIdList.Length - 1]].FieldType; + } + else + { + fieldType + = fieldInfos[fieldIdList[fieldIdList.Length - 1]].FieldType; + } + } else { - fieldType = fieldInfos[ptr->Value2].FieldType; + if(fieldInfos[ptr->Value2] == null) + { + fieldType = newFieldInfos[ptr->Value2].FieldType; + } + else + { + fieldType = fieldInfos[ptr->Value2].FieldType; + } } EvaluationStackOperation.UpdateReference(evaluationStackBase, ptr, managedStack, EvaluationStackOperation.ToObject(evaluationStackBase, @@ -1769,9 +2168,28 @@ public static void _Info(string a) if (fieldIndex >= 0) { var fieldInfo = fieldInfos[fieldIndex]; - fieldInfo.SetValue(null, - EvaluationStackOperation.ToObject(evaluationStackBase, src, - managedStack, fieldInfo.FieldType, this)); + Type fieldType = null; + + if(fieldInfo == null) + { + fieldType = newFieldInfos[fieldIndex].FieldType; + } + else + { + fieldType = fieldInfo.FieldType; + } + + var val = EvaluationStackOperation.ToObject(evaluationStackBase, src, + managedStack, fieldType, this); + + if(fieldInfo == null) + { + newFieldInfos[fieldIndex].SetValue(null, val); + } + else + { + fieldInfo.SetValue(null, val); + } } else { @@ -1812,7 +2230,7 @@ public static void _Info(string a) break; default: throwRuntimeException(new InvalidProgramException(code - + " expect ref, but got " + ptr->Type), true); + + " expect ref, but got " + ptr->Type + " value1:" + ptr->Value1 + " value2:" + ptr->Value2), true); break; } } @@ -1835,12 +2253,18 @@ public static void _Info(string a) { case ValueType.FieldReference: { - //_Info("ptr->Value2:" + ptr->Value2); var fieldIndex = ptr->Value2; if (fieldIndex >= 0) { Type fieldType = null; - fieldType = fieldInfos[fieldIndex].FieldType; + if(fieldInfos[fieldIndex] == null) + { + fieldType = newFieldInfos[fieldIndex].FieldType; + } + else + { + fieldType = fieldInfos[fieldIndex].FieldType; + } //var fieldInfo = fieldInfos[ptr->Value2]; var val = EvaluationStackOperation.ToObject(evaluationStackBase, ptr, @@ -1860,18 +2284,22 @@ AnonymousStorey anonyObj break; case ValueType.ChainFieldReference: { - //_Info("ptr->Value2:" + ptr->Value2); Type fieldType = null; var fieldAddr = managedStack[ptr - evaluationStackBase] as FieldAddr; var fieldIdList = fieldAddr.FieldIdList; - //_Info("fieldIdList:" + fieldIdList); - fieldType = fieldInfos[fieldIdList[fieldIdList.Length - 1]].FieldType; + if(fieldInfos[fieldIdList[fieldIdList.Length - 1]] == null) + { + fieldType = newFieldInfos[fieldIdList[fieldIdList.Length - 1]].FieldType; + } + else + { + fieldType = fieldInfos[fieldIdList[fieldIdList.Length - 1]].FieldType; + } //var fieldInfo = fieldInfos[ptr->Value2]; var val = EvaluationStackOperation.ToObject(evaluationStackBase, ptr, managedStack, fieldType, this, false); - //_Info("val = " + val); EvaluationStackOperation.PushObject(evaluationStackBase, ptr, managedStack, val, fieldType); } @@ -1890,8 +2318,23 @@ AnonymousStorey anonyObj if (fieldIndex >= 0) { var fieldInfo = fieldInfos[ptr->Value1]; + object value = null; + Type fieldType = null; + if(fieldInfo == null) + { + newFieldInfos[fieldIndex].CheckInit(this, null); + + fieldType = newFieldInfos[fieldIndex].FieldType; + value = newFieldInfos[fieldIndex].GetValue(null); + } + else + { + fieldType = fieldInfo.FieldType; + value = fieldInfo.GetValue(null); + } + EvaluationStackOperation.PushObject(evaluationStackBase, ptr, - managedStack, fieldInfo.GetValue(null), fieldInfo.FieldType); + managedStack, value, fieldType); } else { @@ -1921,7 +2364,7 @@ AnonymousStorey anonyObj break; default: throwRuntimeException(new InvalidProgramException(code - + " expect ref, but got " + ptr->Type), true); + + " expect ref, but got " + ptr->Type + " value1:" + ptr->Value1 + " value2:" + ptr->Value2), true); break; } } @@ -2044,31 +2487,38 @@ AnonymousStorey anonyObj case Code.Unbox_Any://0.0848605% { var ptr = evaluationStackPointer - 1; - var type = externTypes[pc->Operand]; - //var pos = (int)(ptr - evaluationStackBase); - var obj = managedStack[ptr->Value1]; - if (ptr->Type == ValueType.Object) + if(pc->Operand >= 0) { - if (type.IsValueType) + var type = externTypes[pc->Operand]; + //var pos = (int)(ptr - evaluationStackBase); + var obj = managedStack[ptr->Value1]; + if (ptr->Type == ValueType.Object) { - if (obj == null) - { - throw new NullReferenceException(); - } - else if(type.IsPrimitive) + if (type.IsValueType) { - EvaluationStackOperation.UnboxPrimitive(ptr, obj, type); + if (obj == null) + { + throw new NullReferenceException(); + } + else if(type.IsPrimitive) + { + EvaluationStackOperation.UnboxPrimitive(ptr, obj, type); + } + else if(type.IsEnum) + { + EvaluationStackOperation.UnboxPrimitive(ptr, obj, Enum.GetUnderlyingType(type)); + } + else + { + ptr->Type = ValueType.ValueType; + } } - else + //娉涘瀷鍑芥暟鏄湁鍙兘Unbox_Any涓涓潪鍊肩被鍨嬬殑 + else if (obj != null && !type.IsAssignableFrom(obj.GetType())) { - ptr->Type = ValueType.ValueType; + throw new InvalidCastException(); } } - //娉涘瀷鍑芥暟鏄湁鍙兘Unbox_Any涓涓潪鍊肩被鍨嬬殑 - else if (obj != null && !type.IsAssignableFrom(obj.GetType())) - { - throw new InvalidCastException(); - } } } break; @@ -2139,7 +2589,16 @@ AnonymousStorey anonyObj ptr->Value1 = arr[idx]; } break; - //case Code.Ldelem_Any: //0.05657366% 娉涘瀷涓娇鐢紵娉涘瀷涓嶆敮鎸佽В鏋愶紝鎵浠ヤ笉浼氱鍒拌繖鎸囦护 + case Code.Ldelem_Any: //0.05657366% + { + var arrPtr = evaluationStackPointer - 1 - 1; + int idx = (evaluationStackPointer - 1)->Value1; + var arr = managedStack[arrPtr->Value1] as Array; + EvaluationStackOperation.PushObject(evaluationStackBase, arrPtr, managedStack, + arr.GetValue(idx), arr.GetType().GetElementType()); + evaluationStackPointer = evaluationStackPointer - 1; + } + break; case Code.Ldc_R8: //0.05088072% { *(double*)&evaluationStackPointer->Value1 = *(double*)(pc + 1); @@ -2176,7 +2635,7 @@ AnonymousStorey anonyObj switch (obj->Type) { case ValueType.Integer: - val = (ulong)obj->Value1; + val = *(uint*)&obj->Value1;//Conv_U8鐨勬搷浣滄暟鑲畾鏄痷int break; case ValueType.Long: pc++; @@ -2220,7 +2679,18 @@ AnonymousStorey anonyObj evaluationStackPointer = rhs; } break; - //Constrained//0.04714472% delete + case Code.Constrained://0.04714472% + { + var lastInstruction = pc - 1; + var type = externTypes[pc->Operand]; + var ptr = evaluationStackPointer - 1 - lastInstruction->Operand; + var obj = EvaluationStackOperation.ToObject(evaluationStackBase, ptr, managedStack, type, this); + var pos = (int)(ptr - evaluationStackBase); + managedStack[pos] = obj; + ptr->Value1 = pos; + ptr->Type = ValueType.Object; + } + break; case Code.Switch://0.04518777% { int val = (evaluationStackPointer - 1)->Value1; @@ -2277,8 +2747,8 @@ AnonymousStorey anonyObj var pn = anonymousStoreyInfo.CtorParamNum; //_Info("param count:" + pn + ", ctor id:" + anonymousStoreyInfo.CtorId); AnonymousStorey anonymousStorey = (anonymousStoreyInfo.Slots == null) - ? new AnonymousStorey(anonymousStoreyInfo.FieldNum) - : wrappersManager.CreateBridge(anonymousStoreyInfo.FieldNum, + ? new AnonymousStorey(anonymousStoreyInfo.FieldNum, anonymousStoreyInfo.FieldTypes, pc->Operand, anonymousStoreyInfo.VTable, this) + : wrappersManager.CreateBridge(anonymousStoreyInfo.FieldNum, anonymousStoreyInfo.FieldTypes, pc->Operand, anonymousStoreyInfo.VTable, anonymousStoreyInfo.Slots, this); var pos = evaluationStackPointer; @@ -2287,6 +2757,12 @@ AnonymousStorey anonyObj //var src = pos - 1; //_Info("src t:" + src->Type + ",v:" + src->Value1); copy(evaluationStackBase, pos, pos - 1, managedStack); + if (pos->Type >= ValueType.Object && pos->Type != ValueType.ValueType) + { + var src = pos - 1; + pos->Value1 = (int)(pos - evaluationStackBase); + managedStack[pos->Value1] = managedStack[src->Value1]; + } //_Info("des t:" + pos->Type + ",v:" + pos->Value1); pos = pos - 1; } @@ -2306,7 +2782,20 @@ AnonymousStorey anonyObj evaluationStackPointer = pos + 1; } break; - //case Code.Stelem_Any: //0.03166702% 娉涘瀷涓娇鐢紵娉涘瀷涓嶆敮鎸佽В鏋愶紝鎵浠ヤ笉浼氱鍒拌繖鎸囦护 + case Code.Stelem_Any: //0.03166702% + { + var arrPtr = evaluationStackPointer - 1 - 1 - 1; + int idx = (evaluationStackPointer - 1 - 1)->Value1; + var valPtr = evaluationStackPointer - 1; + var arr = managedStack[arrPtr->Value1] as Array; + var val = EvaluationStackOperation.ToObject(evaluationStackBase, valPtr, + managedStack, arr.GetType().GetElementType(), this, false); + arr.SetValue(val, idx); + managedStack[arrPtr - evaluationStackBase] = null; //娓呯悊锛屽鏋滄湁鐨勮瘽 + managedStack[valPtr - evaluationStackBase] = null; + evaluationStackPointer = arrPtr; + } + break; case Code.Conv_U2: //0.02917635% { var obj = evaluationStackPointer - 1; @@ -3468,6 +3957,11 @@ AnonymousStorey anonyObj } } + public static void Sweep() + { + NewFieldInfo.Sweep(); + } + public string Statistics() { var sb = new System.Text.StringBuilder(); diff --git a/Source/VSProj/Src/Core/WrappersManager.cs b/Source/VSProj/Src/Core/WrappersManager.cs index 94a515f..c92eed0 100644 --- a/Source/VSProj/Src/Core/WrappersManager.cs +++ b/Source/VSProj/Src/Core/WrappersManager.cs @@ -1,6 +1,6 @@ /* * Tencent is pleased to support the open source community by making InjectFix available. - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2019 Tencent. All rights reserved. * InjectFix is licensed under the MIT License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. */ @@ -9,16 +9,16 @@ namespace IFix.Core { - //该接口由注入器自动实现 + //璇ユ帴鍙g敱娉ㄥ叆鍣ㄨ嚜鍔ㄥ疄鐜 public interface WrappersManager { - //创建一个delegate,如果anon非空就是闭包 + //鍒涘缓涓涓猟elegate锛屽鏋渁non闈炵┖灏辨槸闂寘 Delegate CreateDelegate(Type type, int id, object anon); - //创建一个interface桥接器 - AnonymousStorey CreateBridge(int fieldNum, int[] slots, VirtualMachine virtualMachine); - //创建一个wrapper对象(会由补丁加载逻辑调用,创建后放入wrapper数组) + //鍒涘缓涓涓猧nterface妗ユ帴鍣 + AnonymousStorey CreateBridge(int fieldNum, int[] fieldTypes, int typeIndex, int[] vTable, int[] slots, VirtualMachine virtualMachine); + //鍒涘缓涓涓獁rapper瀵硅薄锛堜細鐢辫ˉ涓佸姞杞介昏緫璋冪敤锛屽垱寤哄悗鏀惧叆wrapper鏁扮粍锛 object CreateWrapper(int id); - //初始化wrapper数组 + //鍒濆鍖杦rapper鏁扮粍 object InitWrapperArray(int len); } } diff --git a/Source/VSProj/Src/PerfTest/PerfTest.cs b/Source/VSProj/Src/PerfTest/PerfTest.cs index 37d7afe..2dfbe97 100644 --- a/Source/VSProj/Src/PerfTest/PerfTest.cs +++ b/Source/VSProj/Src/PerfTest/PerfTest.cs @@ -1,6 +1,6 @@ /* * Tencent is pleased to support the open source community by making InjectFix available. - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2019 Tencent. All rights reserved. * InjectFix is licensed under the MIT License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. */ @@ -14,7 +14,7 @@ namespace IFix.Test { public class PerfTest { - //基准测试,空方法调用 + //鍩哄噯娴嬭瘯锛岀┖鏂规硶璋冪敤 static void Base() { int LOOPS = 10000000; @@ -31,12 +31,12 @@ static void Base() } } - //通过Call对象调用add方法,该方法逻辑如下,SimpleVirtualMachineBuilder通过硬编码指令获得 + //閫氳繃Call瀵硅薄璋冪敤add鏂规硶锛岃鏂规硶閫昏緫濡備笅锛孲impleVirtualMachineBuilder閫氳繃纭紪鐮佹寚浠よ幏寰 //int add(int a, int b) //{ // return a + b; //} - //原生方法通过这种方式调用虚拟机方法 + //鍘熺敓鏂规硶閫氳繃杩欑鏂瑰紡璋冪敤铏氭嫙鏈烘柟娉 static void SafeCall() { int LOOPS = 10000000; @@ -55,8 +55,8 @@ static void SafeCall() Console.WriteLine("SafeCall " + " : " + (LOOPS / (int)sw.Elapsed.TotalMilliseconds * 1000) + "\r\n"); } - //直接通过指针操作栈,调用add方法 - //虚拟机内部方法间调用是通过这种方式 + //鐩存帴閫氳繃鎸囬拡鎿嶄綔鏍堬紝璋冪敤add鏂规硶 + //铏氭嫙鏈哄唴閮ㄦ柟娉曢棿璋冪敤鏄氳繃杩欑鏂瑰紡 unsafe static void UnsafeCall() { IntPtr nativePointer = System.Runtime.InteropServices.Marshal.AllocHGlobal(sizeof(Value) diff --git a/Source/VSProj/Src/TestDLL/BaseTest.cs b/Source/VSProj/Src/TestDLL/BaseTest.cs index 797f570..85f19ce 100644 --- a/Source/VSProj/Src/TestDLL/BaseTest.cs +++ b/Source/VSProj/Src/TestDLL/BaseTest.cs @@ -1,6 +1,6 @@ /* * Tencent is pleased to support the open source community by making InjectFix available. - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2019 Tencent. All rights reserved. * InjectFix is licensed under the MIT License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. */ @@ -155,11 +155,11 @@ public string F(List a) return "6"; } - //TODO: 泛型+引用及数组 + //TODO: 娉涘瀷+寮曠敤鍙婃暟缁 - //TODO: 泛型实参同时含函数泛型参数及类泛型参数 + //TODO: 娉涘瀷瀹炲弬鍚屾椂鍚嚱鏁版硾鍨嬪弬鏁板強绫绘硾鍨嬪弬鏁 - //TODO: 由于目前泛型函数不解析执行,所以泛型实参在泛型函数间传递不用考虑,但后续如果要支持泛型函数的解析执行的话,要加入这点的考虑 + //TODO: 鐢变簬鐩墠娉涘瀷鍑芥暟涓嶈В鏋愭墽琛岋紝鎵浠ユ硾鍨嬪疄鍙傚湪娉涘瀷鍑芥暟闂翠紶閫掍笉鐢ㄨ冭檻锛屼絾鍚庣画濡傛灉瑕佹敮鎸佹硾鍨嬪嚱鏁扮殑瑙f瀽鎵ц鐨勮瘽锛岃鍔犲叆杩欑偣鐨勮冭檻 //public void F(List a) //{ @@ -445,14 +445,14 @@ public static void Ref(ref ValueTypeCounter l, ref ValueTypeCounter r) r = t; } - //1、leave的目标不一定紧跟finally block,可以是任何地方 - //2、leave要找到最内层的finally跳转 - //3、endfinally有两种情况,如果是leave跳过来,则跳到leave的目标,如果不是,则重新抛异常 - //4、为了减少注入代码注入侧不try-catch,由解析器清栈,包括引用参数,所以要传入引用参数的个数 - //5、正常leave不应该有查找finally的操作,否则非常慢 - //6、一个finally block里头的leave指令,可以有不同的目标地址,比如:try{}catch{goto} - //7、leave如果在一个finally block内,而目标地址在finally的try block之外,那么这个finally block在跳转前执行, - // 如果有多个这样的finally,则从内到外执行(抛异常其实也可以理解为跳出了finally) + //1銆乴eave鐨勭洰鏍囦笉涓瀹氱揣璺焒inally block锛屽彲浠ユ槸浠讳綍鍦版柟 + //2銆乴eave瑕佹壘鍒版渶鍐呭眰鐨刦inally璺宠浆 + //3銆乪ndfinally鏈変袱绉嶆儏鍐碉紝濡傛灉鏄痩eave璺宠繃鏉ワ紝鍒欒烦鍒發eave鐨勭洰鏍囷紝濡傛灉涓嶆槸锛屽垯閲嶆柊鎶涘紓甯 + //4銆佷负浜嗗噺灏戞敞鍏ヤ唬鐮佹敞鍏ヤ晶涓峵ry-catch锛岀敱瑙f瀽鍣ㄦ竻鏍堬紝鍖呮嫭寮曠敤鍙傛暟锛屾墍浠ヨ浼犲叆寮曠敤鍙傛暟鐨勪釜鏁 + //5銆佹甯竘eave涓嶅簲璇ユ湁鏌ユ壘finally鐨勬搷浣滐紝鍚﹀垯闈炲父鎱 + //6銆佷竴涓猣inally block閲屽ご鐨刲eave鎸囦护锛屽彲浠ユ湁涓嶅悓鐨勭洰鏍囧湴鍧锛屾瘮濡傦細try{}catch{goto} + //7銆乴eave濡傛灉鍦ㄤ竴涓猣inally block鍐咃紝鑰岀洰鏍囧湴鍧鍦╢inally鐨則ry block涔嬪锛岄偅涔堣繖涓猣inally block鍦ㄨ烦杞墠鎵ц锛 + // 濡傛灉鏈夊涓繖鏍风殑finally锛屽垯浠庡唴鍒板鎵ц锛堟姏寮傚父鍏跺疄涔熷彲浠ョ悊瑙d负璺冲嚭浜唂inally锛 public static void ExceptionBase(ref int p) { while (p != 100) diff --git a/Source/VSProj/Src/TestDLL/RedirectBaseTest.cs b/Source/VSProj/Src/TestDLL/RedirectBaseTest.cs index 0dad19a..836cd84 100644 --- a/Source/VSProj/Src/TestDLL/RedirectBaseTest.cs +++ b/Source/VSProj/Src/TestDLL/RedirectBaseTest.cs @@ -1,6 +1,6 @@ /* * Tencent is pleased to support the open source community by making InjectFix available. - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2019 Tencent. All rights reserved. * InjectFix is licensed under the MIT License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. */ @@ -155,11 +155,11 @@ public string F(List a) return "6"; } - //TODO: 泛型+引用及数组 + //TODO: 娉涘瀷+寮曠敤鍙婃暟缁 - //TODO: 泛型实参同时含函数泛型参数及类泛型参数 + //TODO: 娉涘瀷瀹炲弬鍚屾椂鍚嚱鏁版硾鍨嬪弬鏁板強绫绘硾鍨嬪弬鏁 - //TODO: 由于目前泛型函数不解析执行,所以泛型实参在泛型函数间传递不用考虑,但后续如果要支持泛型函数的解析执行的话,要加入这点的考虑 + //TODO: 鐢变簬鐩墠娉涘瀷鍑芥暟涓嶈В鏋愭墽琛岋紝鎵浠ユ硾鍨嬪疄鍙傚湪娉涘瀷鍑芥暟闂翠紶閫掍笉鐢ㄨ冭檻锛屼絾鍚庣画濡傛灉瑕佹敮鎸佹硾鍨嬪嚱鏁扮殑瑙f瀽鎵ц鐨勮瘽锛岃鍔犲叆杩欑偣鐨勮冭檻 //public void F(List a) //{ @@ -445,14 +445,14 @@ public static void Ref(ref ValueTypeCounter l, ref ValueTypeCounter r) r = t; } - //1、leave的目标不一定紧跟finally block,可以是任何地方 - //2、leave要找到最内层的finally跳转 - //3、endfinally有两种情况,如果是leave跳过来,则跳到leave的目标,如果不是,则重新抛异常 - //4、为了减少注入代码注入侧不try-catch,由解析器清栈,包括引用参数,所以要传入引用参数的个数 - //5、正常leave不应该有查找finally的操作,否则非常慢 - //6、一个finally block里头的leave指令,可以有不同的目标地址,比如:try{}catch{goto} - //7、leave如果在一个finally block内,而目标地址在finally的try block之外,那么这个finally block在跳转前执行, - // 如果有多个这样的finally,则从内到外执行(抛异常其实也可以理解为跳出了finally) + //1銆乴eave鐨勭洰鏍囦笉涓瀹氱揣璺焒inally block锛屽彲浠ユ槸浠讳綍鍦版柟 + //2銆乴eave瑕佹壘鍒版渶鍐呭眰鐨刦inally璺宠浆 + //3銆乪ndfinally鏈変袱绉嶆儏鍐碉紝濡傛灉鏄痩eave璺宠繃鏉ワ紝鍒欒烦鍒發eave鐨勭洰鏍囷紝濡傛灉涓嶆槸锛屽垯閲嶆柊鎶涘紓甯 + //4銆佷负浜嗗噺灏戞敞鍏ヤ唬鐮佹敞鍏ヤ晶涓峵ry-catch锛岀敱瑙f瀽鍣ㄦ竻鏍堬紝鍖呮嫭寮曠敤鍙傛暟锛屾墍浠ヨ浼犲叆寮曠敤鍙傛暟鐨勪釜鏁 + //5銆佹甯竘eave涓嶅簲璇ユ湁鏌ユ壘finally鐨勬搷浣滐紝鍚﹀垯闈炲父鎱 + //6銆佷竴涓猣inally block閲屽ご鐨刲eave鎸囦护锛屽彲浠ユ湁涓嶅悓鐨勭洰鏍囧湴鍧锛屾瘮濡傦細try{}catch{goto} + //7銆乴eave濡傛灉鍦ㄤ竴涓猣inally block鍐咃紝鑰岀洰鏍囧湴鍧鍦╢inally鐨則ry block涔嬪锛岄偅涔堣繖涓猣inally block鍦ㄨ烦杞墠鎵ц锛 + // 濡傛灉鏈夊涓繖鏍风殑finally锛屽垯浠庡唴鍒板鎵ц锛堟姏寮傚父鍏跺疄涔熷彲浠ョ悊瑙d负璺冲嚭浜唂inally锛 public static void ExceptionBase(ref int p) { while (p != 100) diff --git a/Source/VSProj/Src/Tools/CSFix.cs b/Source/VSProj/Src/Tools/CSFix.cs index d925d68..615814b 100644 --- a/Source/VSProj/Src/Tools/CSFix.cs +++ b/Source/VSProj/Src/Tools/CSFix.cs @@ -1,6 +1,6 @@ /* * Tencent is pleased to support the open source community by making InjectFix available. - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2019 Tencent. All rights reserved. * InjectFix is licensed under the MIT License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. */ @@ -28,8 +28,8 @@ static void usage() static bool argsValid(string[] args) { - return (args[0] == "-inject" && args.Length >= 6) || (args[0] == "-patch" && args.Length >= 6) - || (args[0] == "-inherit_inject" && args.Length >= 7); + return (args.Length >= 6 && args[0] == "-inject") || (args.Length >= 6 && args[0] == "-patch") + || (args.Length >= 7 && args[0] == "-inherit_inject"); } // #lizard forgives @@ -53,15 +53,15 @@ static void Main(string[] args) bool readSymbols = true; try { - //尝试读取符号 + //灏濊瘯璇诲彇绗﹀彿 assembly = AssemblyDefinition.ReadAssembly(assmeblyPath, new ReaderParameters { ReadSymbols = true }); } catch { - //如果读取不到符号则不读 + //濡傛灉璇诲彇涓嶅埌绗﹀彿鍒欎笉璇 Console.WriteLine("Warning: read " + assmeblyPath + " with symbol fail"); - //写入的时候用这个标志 + //鍐欏叆鐨勬椂鍊欑敤杩欎釜鏍囧織 readSymbols = false; assembly = AssemblyDefinition.ReadAssembly(assmeblyPath, new ReaderParameters { ReadSymbols = false }); @@ -71,7 +71,7 @@ static void Main(string[] args) bool isInheritInject = args[0] == "-inherit_inject"; int searchPathStart = isInheritInject ? 7 : 6; - //往resolver加入程序集搜索路径 + //寰resolver鍔犲叆绋嬪簭闆嗘悳绱㈣矾寰 foreach (var path in args.Skip(searchPathStart)) { try @@ -87,7 +87,7 @@ static void Main(string[] args) if (mode == ProcessMode.Inject) { - //对测试用例特殊处理:测试用例默认全解析执行 + //瀵规祴璇曠敤渚嬬壒娈婂鐞嗭細娴嬭瘯鐢ㄤ緥榛樿鍏ㄨВ鏋愭墽琛 configure = args[3] == "no_cfg" ? GenerateConfigure.Empty() : GenerateConfigure.FromFile(args[3]); @@ -96,8 +96,8 @@ static void Main(string[] args) throw new Exception("Do Not Support already!"); } - //注入逻辑 - //TODO: tranlater的名字不太合适 + //娉ㄥ叆閫昏緫 + //TODO: tranlater鐨勫悕瀛椾笉澶悎閫 if (tranlater.Process(assembly, ilfixAassembly, configure, mode) == CodeTranslator.ProcessResult.Processed) { @@ -112,13 +112,13 @@ static void Main(string[] args) } else { - //补丁生成流程 + //琛ヤ竵鐢熸垚娴佺▼ configure = new PatchGenerateConfigure(assembly, args[4]); if (tranlater.Process(assembly, ilfixAassembly, configure, mode) == CodeTranslator.ProcessResult.Processed) { - //发现程序集已经被注入,主要是防止已经注入的函数包含的注入逻辑会导致死循环 + //鍙戠幇绋嬪簭闆嗗凡缁忚娉ㄥ叆锛屼富瑕佹槸闃叉宸茬粡娉ㄥ叆鐨勫嚱鏁板寘鍚殑娉ㄥ叆閫昏緫浼氬鑷存寰幆 Console.WriteLine("Error: the new assembly must not be inject, please reimport the project!"); return; } @@ -134,8 +134,8 @@ static void Main(string[] args) } finally { - //清理符号读取器 - //如果不清理,在window下会锁定文件 + //娓呯悊绗﹀彿璇诲彇鍣 + //濡傛灉涓嶆竻鐞嗭紝鍦╳indow涓嬩細閿佸畾鏂囦欢 if (assembly != null && assembly.MainModule.SymbolReader != null) { assembly.MainModule.SymbolReader.Dispose(); diff --git a/Source/VSProj/Src/Tools/CecilExtensions.cs b/Source/VSProj/Src/Tools/CecilExtensions.cs index 14a753c..5c278ef 100644 --- a/Source/VSProj/Src/Tools/CecilExtensions.cs +++ b/Source/VSProj/Src/Tools/CecilExtensions.cs @@ -1,6 +1,6 @@ /* * Tencent is pleased to support the open source community by making InjectFix available. - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2019 Tencent. All rights reserved. * InjectFix is licensed under the MIT License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. */ @@ -18,10 +18,10 @@ namespace IFix static internal class CecilExtensions { /// - /// 以contextType为上下文,查找泛型参数对应的实参 + /// 浠ontextType涓轰笂涓嬫枃锛屾煡鎵炬硾鍨嬪弬鏁板搴旂殑瀹炲弬 /// - /// 泛型参数 - /// 上下文类型 + /// 娉涘瀷鍙傛暟 + /// 涓婁笅鏂囩被鍨 /// public static TypeReference ResolveGenericArgument(this GenericParameter gp, TypeReference contextType) { @@ -54,10 +54,10 @@ public static TypeReference ResolveGenericArgument(this GenericParameter gp, Typ } /// - /// 以contextMethod为上下文,查找泛型参数对应的实参 + /// 浠ontextMethod涓轰笂涓嬫枃锛屾煡鎵炬硾鍨嬪弬鏁板搴旂殑瀹炲弬 /// - /// 泛型参数 - /// 上下文函数 + /// 娉涘瀷鍙傛暟 + /// 涓婁笅鏂囧嚱鏁 /// public static TypeReference ResolveGenericArgument(this GenericParameter gp, MethodReference contextMethod) { @@ -70,8 +70,8 @@ public static TypeReference ResolveGenericArgument(this GenericParameter gp, Met } /// - /// 填充泛型参数,如果直接TypeReference取FullName的话,泛型参数(比如名字为T)不会实例化(仍然为T), - /// 这样在补丁查找泛型方法时会有问题 + /// 濉厖娉涘瀷鍙傛暟锛屽鏋滅洿鎺ypeReference鍙朏ullName鐨勮瘽锛屾硾鍨嬪弬鏁帮紙姣斿鍚嶅瓧涓篢锛変笉浼氬疄渚嬪寲锛堜粛鐒朵负T锛夛紝 + /// 杩欐牱鍦ㄨˉ涓佹煡鎵炬硾鍨嬫柟娉曟椂浼氭湁闂 /// /// /// @@ -141,10 +141,10 @@ static string getAssemblyName(TypeReference typeReference) } /// - /// 忽略程序集版本号来对比两个类型是否指向同样的类型 + /// 蹇界暐绋嬪簭闆嗙増鏈彿鏉ュ姣斾袱涓被鍨嬫槸鍚︽寚鍚戝悓鏍风殑绫诲瀷 /// - /// 参数1 - /// 参数2 + /// 鍙傛暟1 + /// 鍙傛暟2 /// public static bool AreEqualIgnoreAssemblyVersion(this TypeReference left, TypeReference right) { @@ -172,12 +172,12 @@ static TypeReference getElementType(TypeReference type, TypeReference contextTyp } /// - /// 获取一个类型的AssemblyQualifiedName + /// 鑾峰彇涓涓被鍨嬬殑AssemblyQualifiedName /// - /// 要获取AssemblyQualifiedName的type - /// 上下文类型,往往是其外层类 - /// 忽略程序集名 - /// 用于泛型类型的递归时,忽略程序集,因为泛型类型是填写完泛型参数后,再填写程序集 + /// 瑕佽幏鍙朅ssemblyQualifiedName鐨則ype + /// 涓婁笅鏂囩被鍨嬶紝寰寰鏄叾澶栧眰绫 + /// 蹇界暐绋嬪簭闆嗗悕 + /// 鐢ㄤ簬娉涘瀷绫诲瀷鐨勯掑綊鏃讹紝蹇界暐绋嬪簭闆嗭紝鍥犱负娉涘瀷绫诲瀷鏄~鍐欏畬娉涘瀷鍙傛暟鍚庯紝鍐嶅~鍐欑▼搴忛泦 /// public static string GetAssemblyQualifiedName(this TypeReference typeReference, TypeReference contextType = null, bool skipAssemblyQualified = false, @@ -261,9 +261,9 @@ public static string GetAssemblyQualifiedName(this TypeReference typeReference, } /// - /// 判断一个类型是否是delegate + /// 鍒ゆ柇涓涓被鍨嬫槸鍚︽槸delegate /// - /// 要判断的类型 + /// 瑕佸垽鏂殑绫诲瀷 /// public static bool IsDelegate(this TypeDefinition typeDefinition) { @@ -275,9 +275,9 @@ public static bool IsDelegate(this TypeDefinition typeDefinition) } /// - /// 判断一个类型是不是泛型 + /// 鍒ゆ柇涓涓被鍨嬫槸涓嶆槸娉涘瀷 /// - /// 要判断的类型 + /// 瑕佸垽鏂殑绫诲瀷 /// public static bool IsGeneric(this TypeReference type) { @@ -307,9 +307,9 @@ public static bool IsGeneric(this TypeReference type) } /// - /// 判断一个类型的泛型实参是否有来自函数的泛型实参 + /// 鍒ゆ柇涓涓被鍨嬬殑娉涘瀷瀹炲弬鏄惁鏈夋潵鑷嚱鏁扮殑娉涘瀷瀹炲弬 /// - /// 要判断的类型 + /// 瑕佸垽鏂殑绫诲瀷 /// public static bool HasGenericArgumentFromMethod(this TypeReference type) { @@ -340,9 +340,9 @@ public static bool HasGenericArgumentFromMethod(this TypeReference type) } /// - /// 判断一个方法是不是泛型 + /// 鍒ゆ柇涓涓柟娉曟槸涓嶆槸娉涘瀷 /// - /// 要判断的方法 + /// 瑕佸垽鏂殑鏂规硶 /// public static bool IsGeneric(this MethodReference method) { @@ -366,9 +366,9 @@ public static bool IsGeneric(this MethodReference method) } /// - /// 判断一个字段的类型是不是泛型 + /// 鍒ゆ柇涓涓瓧娈电殑绫诲瀷鏄笉鏄硾鍨 /// - /// 要判断字段 + /// 瑕佸垽鏂瓧娈 /// public static bool IsGeneric(this FieldReference field) { @@ -376,10 +376,10 @@ public static bool IsGeneric(this FieldReference field) } /// - /// 判断两个类型是不是同一个 + /// 鍒ゆ柇涓や釜绫诲瀷鏄笉鏄悓涓涓 /// - /// 类型1 - /// 类型2 + /// 绫诲瀷1 + /// 绫诲瀷2 /// public static bool IsSameType(this TypeReference left, TypeReference right) { @@ -389,10 +389,10 @@ public static bool IsSameType(this TypeReference left, TypeReference right) } /// - /// 判断两个类型的名字是否相同 + /// 鍒ゆ柇涓や釜绫诲瀷鐨勫悕瀛楁槸鍚︾浉鍚 /// - /// 类型1 - /// 类型2 + /// 绫诲瀷1 + /// 绫诲瀷2 /// public static bool IsSameName(this TypeReference left, TypeReference right) { @@ -400,10 +400,10 @@ public static bool IsSameName(this TypeReference left, TypeReference right) } /// - /// 判断两个方法,如果仅判断其参数类型及返回值类型的名字,是否相等 + /// 鍒ゆ柇涓や釜鏂规硶锛屽鏋滀粎鍒ゆ柇鍏跺弬鏁扮被鍨嬪強杩斿洖鍊肩被鍨嬬殑鍚嶅瓧锛屾槸鍚︾浉绛 /// - /// 方法1 - /// 方法2 + /// 鏂规硶1 + /// 鏂规硶2 /// public static bool IsTheSame(this MethodReference left, MethodReference right) { @@ -411,7 +411,7 @@ public static bool IsTheSame(this MethodReference left, MethodReference right) || left.Name != right.Name || !left.ReturnType.IsSameName(right.ReturnType) || !left.DeclaringType.IsSameName(right.DeclaringType) - || left.HasThis != left.HasThis + || left.HasThis != right.HasThis || left.GenericParameters.Count != right.GenericParameters.Count) { return false; @@ -438,9 +438,9 @@ public static bool IsTheSame(this MethodReference left, MethodReference right) } /// - /// 判断一个方法是否是析构函数 + /// 鍒ゆ柇涓涓柟娉曟槸鍚︽槸鏋愭瀯鍑芥暟 /// - /// 方法 + /// 鏂规硶 /// public static bool IsFinalizer(this MethodDefinition method) { @@ -449,19 +449,20 @@ public static bool IsFinalizer(this MethodDefinition method) } /// - /// 尝试导入一个类型 + /// 灏濊瘯瀵煎叆涓涓被鍨 /// - /// 要导入的类型 - /// 导入到哪个module + /// 瑕佸鍏ョ殑绫诲瀷 + /// 瀵煎叆鍒板摢涓猰odule /// // #lizard forgives public static TypeReference TryImport(this TypeReference toImport, ModuleDefinition module) { + if (toImport == null) return null; if (toImport.Namespace == "System") { if (toImport.Name == "Boolean") { - return module.TypeSystem.Boolean; //用内置类型,否则通过getMap获取不到 + return module.TypeSystem.Boolean; //鐢ㄥ唴缃被鍨嬶紝鍚﹀垯閫氳繃getMap鑾峰彇涓嶅埌 } else if (toImport.Name == "Byte") { @@ -516,7 +517,6 @@ public static TypeReference TryImport(this TypeReference toImport, ModuleDefinit return module.TypeSystem.UIntPtr; } } - if (toImport == null) return null; if (toImport.IsGenericParameter) return toImport; if (toImport.IsGenericInstance) { @@ -540,7 +540,7 @@ public static TypeReference TryImport(this TypeReference toImport, ModuleDefinit } /// - /// 尝试导入一个方法 + /// 灏濊瘯瀵煎叆涓涓柟娉 /// /// /// @@ -560,7 +560,7 @@ public static MethodReference TryImport(this MethodReference toImport, ModuleDef } /// - /// 生成一个泛型引用 + /// 鐢熸垚涓涓硾鍨嬪紩鐢 /// /// /// @@ -651,7 +651,7 @@ public static bool IsMatch(this MethodReference left, MethodDefinition right, Ty } /// - /// 两个方法签名是否相同 + /// 涓や釜鏂规硶绛惧悕鏄惁鐩稿悓 /// /// /// @@ -693,7 +693,7 @@ public static bool AreSignaturesEqual(MethodReference left, MethodReference righ public static bool CheckImplemention(this MethodReference itfMethod, MethodDefinition impl) { - //一个类可能有多个同签名方法,这时应该通过Overrides来检查 + //涓涓被鍙兘鏈夊涓悓绛惧悕鏂规硶锛岃繖鏃跺簲璇ラ氳繃Overrides鏉ユ鏌 if (impl.Overrides.Count > 0) { foreach (var o in impl.Overrides) @@ -811,7 +811,7 @@ public static void AnalysisMethod(MethodDefinition method, out InjectType inject id = -1; } - //如果method是注入函数,返回其注入类型,id,以及对应的新函数 + //濡傛灉method鏄敞鍏ュ嚱鏁帮紝杩斿洖鍏舵敞鍏ョ被鍨嬶紝id锛屼互鍙婂搴旂殑鏂板嚱鏁 public static void AnalysisMethod(Dictionary>> searchData, MethodDefinition method, out InjectType injectType, out int id, out MethodDefinition foundMethod) { @@ -847,7 +847,7 @@ static void addTypeAndNestType(List result, TypeDefinition type) } /// - /// 获取一个程序集里头所有类型,包括其内嵌类型 + /// 鑾峰彇涓涓▼搴忛泦閲屽ご鎵鏈夌被鍨嬶紝鍖呮嫭鍏跺唴宓岀被鍨 /// /// /// diff --git a/Source/VSProj/Src/Tools/CodeTranslator.cs b/Source/VSProj/Src/Tools/CodeTranslator.cs index bb31614..561056b 100644 --- a/Source/VSProj/Src/Tools/CodeTranslator.cs +++ b/Source/VSProj/Src/Tools/CodeTranslator.cs @@ -1,6 +1,6 @@ /* * Tencent is pleased to support the open source community by making InjectFix available. - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2019 Tencent. All rights reserved. * InjectFix is licensed under the MIT License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. */ @@ -42,6 +42,7 @@ class CodeTranslator private List fieldsStoreInVirtualMachine = new List(); private Dictionary fieldToId = new Dictionary(); + private Dictionary virtualMethodToIndex = new Dictionary(); const string Wrap_Perfix = "__Gen_Wrap_"; int nextAllocId = 0; @@ -94,6 +95,15 @@ bool isCompilerGeneratedPlainObject(TypeReference type) && td.BaseType.IsSameType(objType); } + bool isCustomClassPlainObject(TypeReference type) + { + var td = type as TypeDefinition; + return td != null + && !td.IsInterface + && isNewClass(td) + && (td.BaseType.IsSameType(objType) || isCustomClassPlainObject(td.BaseType as TypeReference)); + } + bool isCompilerGeneratedByNotPlainObject(TypeReference type) { var td = type as TypeDefinition; @@ -109,9 +119,10 @@ Dictionary> typeToSpecialGeneratedField = new Dictionary>(); Dictionary typeToCctor = new Dictionary(); + Dictionary newFieldToCtor = new Dictionary(); /// - /// 获取简写属性(例如public int a{get;set;}),事件等所生成的字段 + /// 鑾峰彇绠鍐欏睘鎬э紙渚嬪public int a{get;set;}锛夛紝浜嬩欢绛夋墍鐢熸垚鐨勫瓧娈 /// /// /// @@ -128,7 +139,7 @@ HashSet getSpecialGeneratedFields(TypeDefinition type) var cctor = type.Methods.FirstOrDefault(m => m.Name == ".cctor"); if (cctor != null) { - var cctorInfo = getMethodId(cctor, null, false, InjectType.Redirect); + var cctorInfo = getMethodId(cctor, null,false, false, InjectType.Redirect); typeToCctor[type] = cctorInfo.Type == CallType.Internal ? cctorInfo.Id : -2; } } @@ -149,17 +160,28 @@ where isCompilerGenerated(instruction.Operand as FieldReference) return ret; } - //再补丁新增一个对原生方法的引用 + //鍐嶈ˉ涓佹柊澧炰竴涓鍘熺敓鏂规硶鐨勫紩鐢 int addExternType(TypeReference type, TypeReference contextType = null) { + if (type.IsRequiredModifier) return addExternType((type as RequiredModifierType).ElementType, contextType); if (type.IsGenericParameter || type.HasGenericArgumentFromMethod()) { - throw new InvalidProgramException("try to use a generic type definition"); + var genericTypeInfo = "{None}"; + try { + var genericType = (GenericParameter)type; + var owner = (TypeDefinition)genericType.Owner; + genericTypeInfo = string.Format("{{Owner: {0}, Scope: {1}}}", owner.FullName, genericType.Scope.Name); + } catch { } + throw new InvalidProgramException("try to use a generic type definition: " + type + ", generic type info: " + genericTypeInfo); } if (externTypeToId.ContainsKey(type)) { return externTypeToId[type]; } + if (isNewClass(type as TypeDefinition)) + { + throw new Exception(type + " is new class, cannot be treated as extern type"); + } if (isCompilerGenerated(type)) { throw new Exception(type + " is CompilerGenerated"); @@ -179,8 +201,8 @@ int addExternType(TypeReference type, TypeReference contextType = null) return externTypes.Count - 1; } - //假如是注入模式,而且该函数配置是IFix的话,不需要真的为其访问的资源分配id - //TODO: 更理想的做法是剥离一个分析代码流程,仅分析要生产哪些适配器,反向适配器,反剪裁配置 + //鍋囧鏄敞鍏ユā寮忥紝鑰屼笖璇ュ嚱鏁伴厤缃槸IFix鐨勮瘽锛屼笉闇瑕佺湡鐨勪负鍏惰闂殑璧勬簮鍒嗛厤id + //TODO: 鏇寸悊鎯崇殑鍋氭硶鏄墺绂讳竴涓垎鏋愪唬鐮佹祦绋嬶紝浠呭垎鏋愯鐢熶骇鍝簺閫傞厤鍣紝鍙嶅悜閫傞厤鍣紝鍙嶅壀瑁侀厤缃 bool doNoAdd(MethodDefinition caller) { InjectType injectType; @@ -188,7 +210,7 @@ bool doNoAdd(MethodDefinition caller) out injectType) && injectType == InjectType.Switch; } - //原生字段 + //鍘熺敓瀛楁 int addRefField(FieldReference field, MethodDefinition caller) { if (doNoAdd(caller)) @@ -207,7 +229,7 @@ int addRefField(FieldReference field, MethodDefinition caller) return id; } - //虚拟机存储字段 + //铏氭嫙鏈哄瓨鍌ㄥ瓧娈 int addStoreField(FieldDefinition field, MethodDefinition caller) { if (doNoAdd(caller)) @@ -221,12 +243,12 @@ int addStoreField(FieldDefinition field, MethodDefinition caller) id = -(fieldsStoreInVirtualMachine.Count + 1); fieldToId.Add(field, id); fieldsStoreInVirtualMachine.Add(field); - addExternType(isCompilerGenerated(field.FieldType) ? objType : field.FieldType); + addExternType((isCompilerGenerated(field.FieldType) || isNewClass(field.FieldType as TypeDefinition)) ? objType : field.FieldType); } return id; } - //新增一个字符串字面值 + //鏂板涓涓瓧绗︿覆瀛楅潰鍊 int addInternString(string str, MethodDefinition caller) { if (doNoAdd(caller)) @@ -244,7 +266,7 @@ int addInternString(string str, MethodDefinition caller) return id; } - //原生方法的引用 + //鍘熺敓鏂规硶鐨勫紩鐢 int addExternMethod(MethodReference callee, MethodDefinition caller) { if (doNoAdd(caller)) @@ -252,6 +274,15 @@ int addExternMethod(MethodReference callee, MethodDefinition caller) return ushort.MaxValue; } + if (callee.Name == "AwaitUnsafeOnCompleted") + { + + if (!awaitUnsafeOnCompletedMethods.Any(m => ((GenericInstanceMethod)callee).GenericArguments[0] == ((GenericInstanceMethod)m).GenericArguments[0])) + { + awaitUnsafeOnCompletedMethods.Add(callee); + } + } + if (externMethodToId.ContainsKey(callee)) { return externMethodToId[callee]; @@ -272,7 +303,11 @@ int addExternMethod(MethodReference callee, MethodDefinition caller) { foreach (var typeArg in ((GenericInstanceMethod)callee).GenericArguments) { - addExternType(typeArg); + if (!isCompilerGenerated(typeArg)) + { + addExternType(typeArg); + } + } } @@ -331,7 +366,7 @@ bool checkILAndGetOffset(MethodDefinition method, } /// - /// 判断一个名字是否是一个合法id + /// 鍒ゆ柇涓涓悕瀛楁槸鍚︽槸涓涓悎娉昳d /// /// /// @@ -391,7 +426,7 @@ bool checkILAndGetOffset(MethodDefinition method, //Console.WriteLine(i + " instruction:" + instructions[i].OpCode + " offset:" + offset); switch (instructions[i].OpCode.Code) { - case Code.Nop://先忽略 + case Code.Nop://鍏堝拷鐣 break; case Code.Constrained: { @@ -443,11 +478,11 @@ bool checkILAndGetOffset(MethodDefinition method, case Code.Ldflda: { FieldReference fr = instructions[i].Operand as FieldReference; - //如果是生成的字段,而且不是Getter/Setter/Adder/Remover + //濡傛灉鏄敓鎴愮殑瀛楁锛岃屼笖涓嶆槸Getter/Setter/Adder/Remover if (isCompilerGenerated(fr) && !method.IsSpecialName) { - if (!IsVaildIdentifierName(fr.Name)//不是合法名字,就肯定是随机变量 - //如果是合法名字,但不被任何SpecialName方法引用,也归为随机变量 + if (!IsVaildIdentifierName(fr.Name)//涓嶆槸鍚堟硶鍚嶅瓧锛屽氨鑲畾鏄殢鏈哄彉閲 + //濡傛灉鏄悎娉曞悕瀛楋紝浣嗕笉琚换浣昐pecialName鏂规硶寮曠敤锛屼篃褰掍负闅忔満鍙橀噺 || !isRefBySpecialMethod(fr as FieldDefinition)) { @@ -471,7 +506,7 @@ bool checkILAndGetOffset(MethodDefinition method, case Code.Ldsflda: { FieldReference fr = instructions[i].Operand as FieldReference; - //如果访问了生成的静态字段,而且不能存到虚拟机,不是Getter/Setter/Adder/Remover + //濡傛灉璁块棶浜嗙敓鎴愮殑闈欐佸瓧娈碉紝鑰屼笖涓嶈兘瀛樺埌铏氭嫙鏈猴紝涓嶆槸Getter/Setter/Adder/Remover //if ((isCompilerGenerated(fr) || isCompilerGenerated(fr.DeclaringType)) // && !isFieldStoreInVitualMachine(fr) && !method.IsSpecialName) //{ @@ -493,28 +528,33 @@ bool checkILAndGetOffset(MethodDefinition method, case Code.Ldftn: case Code.Ldvirtftn: { - //LINQ通常是ldftn,要验证ldftn所加载的函数是否含非法指令(不支持,或者引用了个生成字段, - //或者一个生成NotPlainObject) + //LINQ閫氬父鏄痩dftn锛岃楠岃瘉ldftn鎵鍔犺浇鐨勫嚱鏁版槸鍚﹀惈闈炴硶鎸囦护锛堜笉鏀寔锛屾垨鑰呭紩鐢ㄤ簡涓敓鎴愬瓧娈碉紝 + //鎴栬呬竴涓敓鎴怤otPlainObject锛 MethodReference mr = instructions[i].Operand as MethodReference; - if (mr != null && !mr.IsGeneric() + if (mr != null && !mr.IsGeneric() && !isCompilerGeneratedByNotPlainObject(mr.DeclaringType)) { if (isCompilerGenerated(mr) || (/*instructions[i].OpCode.Code != Code.Newobj && */ - isCompilerGeneratedPlainObject(mr.DeclaringType))) + isCompilerGeneratedPlainObject(mr.DeclaringType)) + || isCustomClassPlainObject(mr.DeclaringType)) { var md = mr as MethodDefinition; + if (md == null)//闂寘涓皟鐢ㄤ竴涓硾鍨嬶紝鍦╱nity2018鐨.net 3.5璁剧疆涓嬶紝缂栬瘧鍣ㄦ槸鍏堢敓鎴愪竴涓硾鍨嬬殑闂寘瀹炵幇锛岀劧鍚庡疄渚嬪寲锛屽緢濂囨殑鍋氭硶锛岃佺増鏈瑄nity锛屾柊unity鐨.net 4.0璁剧疆閮戒笉浼氳繖鏍凤紝鍏堣繑鍥瀎alse锛屼笉鏀寔杩欑缂栬瘧鍣 + { + return false; + } if (md.Body != null && !checkILAndGetOffset(md, md.Body.Instructions)) { //Console.WriteLine("check " + md + " fail il = " + md.Body.Instructions[p] // + ",caller=" + method); return false; } - //编译器生成类要检查所有实现方法 + //缂栬瘧鍣ㄧ敓鎴愮被瑕佹鏌ユ墍鏈夊疄鐜版柟娉 if (instructions[i].OpCode.Code == Code.Newobj - && isCompilerGeneratedPlainObject(mr.DeclaringType)) + && (isCompilerGeneratedPlainObject(mr.DeclaringType) || isCustomClassPlainObject(mr.DeclaringType))) { - foreach(var m in mr.DeclaringType.Resolve().Methods + foreach (var m in mr.DeclaringType.Resolve().Methods .Where(m => !m.IsConstructor)) { if (m.Body != null && !checkILAndGetOffset(m, m.Body.Instructions)) @@ -565,7 +605,7 @@ bool checkILAndGetOffset(MethodDefinition method, void processMethod(MethodDefinition method) { - getMethodId(method, null); + getMethodId(method, null,true); } Core.ExceptionHandler findExceptionHandler(Core.ExceptionHandler[] ehs, Core.ExceptionHandlerType type, @@ -576,12 +616,12 @@ Core.ExceptionHandler findExceptionHandler(Core.ExceptionHandler[] ehs, Core.Exc } /// - /// 查找一个指令异常时的异常处理块 + /// 鏌ユ壘涓涓寚浠ゅ紓甯告椂鐨勫紓甯稿鐞嗗潡 /// - /// 当前函数的所有异常处理块 - /// 异常类型 - /// 指令偏移 - /// 异常处理块的索引 + /// 褰撳墠鍑芥暟鐨勬墍鏈夊紓甯稿鐞嗗潡 + /// 寮傚父绫诲瀷 + /// 鎸囦护鍋忕Щ + /// 寮傚父澶勭悊鍧楃殑绱㈠紩 /// Core.ExceptionHandler findExceptionHandler(Core.ExceptionHandler[] ehs, Core.ExceptionHandlerType type, int offset, out int idx) @@ -603,11 +643,11 @@ Core.ExceptionHandler findExceptionHandler(Core.ExceptionHandler[] ehs, Core.Exc return ret; } - MethodDefinition findOverride(TypeDefinition type, MethodReference vmethod) + MethodDefinition findOverride(TypeDefinition type, MethodReference vmethod, bool allowAbstractMethod = false) { foreach (var method in type.Methods) { - if (method.IsVirtual && !method.IsAbstract && isTheSameDeclare(method, vmethod)) + if (method.IsVirtual && (allowAbstractMethod || !method.IsAbstract) && isTheSameDeclare(method, vmethod)) { return method; } @@ -658,13 +698,65 @@ MethodReference _findBase(TypeReference type, MethodDefinition method) return _findBase(td.BaseType, method); } + MethodReference _findInitDefineVirtualMethod(TypeReference type, MethodDefinition method) + { + TypeDefinition td = type.Resolve(); + if (td == null) + { + return null; + } + MethodReference baseM = null; + if (td.BaseType != null && isNewClass(td.BaseType as TypeDefinition)) + { + baseM = _findInitDefineVirtualMethod(td.BaseType, method); + } + if (baseM != null) + { + return baseM; + } + + var m = findOverride(td, method, true); + if (m != null) + { + if (type.IsGenericInstance) + { + return m.MakeGeneric(method.DeclaringType); + } + else + { + return m.TryImport(method.DeclaringType.Module); + } + } + return null; + } + + MethodReference findInitDefineVirtualMethod(TypeDefinition type, MethodDefinition method) + { + if (method.IsVirtual) + { + foreach (var objVirtualMethod in ObjectVirtualMethodDefinitionList) + { + if (isTheSameDeclare(objVirtualMethod,method)) + { + return objVirtualMethod; + } + } + if (method.IsNewSlot) + { + return method; + } + return _findInitDefineVirtualMethod(type.BaseType, method); + } + return null; + } + MethodReference findBase(TypeDefinition type, MethodDefinition method) { - if (method.IsVirtual && !method.IsNewSlot) //表明override + if (method.IsVirtual && !method.IsNewSlot) //琛ㄦ槑override { try { - //TODO: 如果后续支持泛型解析,需要考虑这块的实现,xlua目前泛型直接不支持base调用 + //TODO: 濡傛灉鍚庣画鏀寔娉涘瀷瑙f瀽锛岄渶瑕佽冭檻杩欏潡鐨勫疄鐜帮紝xlua鐩墠娉涘瀷鐩存帴涓嶆敮鎸乥ase璋冪敤 return _findBase(type.BaseType, method); } catch { } @@ -674,46 +766,83 @@ MethodReference findBase(TypeDefinition type, MethodDefinition method) const string BASE_RPOXY_PERFIX = "<>iFixBaseProxy_"; - //方案2 + Dictionary> baseProxys = new Dictionary>(); + + //鏂规2 //var method = typeof(object).GetMethod("ToString"); //var ftn = method.MethodHandle.GetFunctionPointer(); //var func = (Func)Activator.CreateInstance(typeof(Func), obj, ftn); - MethodDefinition tryAddBaseProxy(TypeDefinition type, MethodDefinition method) + MethodReference tryAddBaseProxy(TypeDefinition type, MethodDefinition method) { var mbase = findBase(type, method); if (mbase != null) { - var proxyMethod = new MethodDefinition(BASE_RPOXY_PERFIX + method.Name, MethodAttributes.Private, - method.ReturnType); - for(int i = 0; i < method.Parameters.Count; i++) + if (!isNewClass(type)) { - proxyMethod.Parameters.Add(new ParameterDefinition("P" + i, method.Parameters[i].IsOut - ? ParameterAttributes.Out : ParameterAttributes.None, method.Parameters[i].ParameterType)); + var proxyMethod = new MethodDefinition(BASE_RPOXY_PERFIX + method.Name, MethodAttributes.Public, + method.ReturnType); + for (int i = 0; i < method.Parameters.Count; i++) + { + proxyMethod.Parameters.Add(new ParameterDefinition("P" + i, method.Parameters[i].IsOut + ? ParameterAttributes.Out : ParameterAttributes.None, method.Parameters[i].ParameterType)); + } + var instructions = proxyMethod.Body.Instructions; + var ilProcessor = proxyMethod.Body.GetILProcessor(); + int paramCount = method.Parameters.Count + 1; + for (int i = 0; i < paramCount; i++) + { + emitLdarg(instructions, ilProcessor, i); + if (i == 0 && type.IsValueType) + { + instructions.Add(Instruction.Create(OpCodes.Ldobj, type)); + instructions.Add(Instruction.Create(OpCodes.Box, type)); + } + } + instructions.Add(Instruction.Create(OpCodes.Call, mbase)); + instructions.Add(Instruction.Create(OpCodes.Ret)); + type.Methods.Add(proxyMethod); + + Dictionary typeToProxy; + if (!baseProxys.TryGetValue(mbase, out typeToProxy)) + { + typeToProxy = new Dictionary(); + baseProxys.Add(mbase, typeToProxy); + } + typeToProxy.Add(type, proxyMethod); + return proxyMethod; } - var instructions = proxyMethod.Body.Instructions; - var ilProcessor = proxyMethod.Body.GetILProcessor(); - int paramCount = method.Parameters.Count + 1; - for(int i = 0; i < paramCount; i++) + else if(isNewClass(type) && !isNewClass(type.BaseType as TypeDefinition)) { - emitLdarg(instructions, ilProcessor, i); - if (i == 0 && type.IsValueType) + return objectVirtualMethodReferenceList.FirstOrDefault( m => m.Name == ("Object" + method.Name)); + } + } + return null; + } + + MethodReference findProxy(TypeDefinition type, MethodReference methodToCall) + { + Dictionary typeToProxy; + if (baseProxys.TryGetValue(methodToCall, out typeToProxy)) + { + TypeDefinition ptype = type; + while (ptype != null) + { + if (typeToProxy.ContainsKey(ptype)) { - instructions.Add(Instruction.Create(OpCodes.Ldobj, type)); - instructions.Add(Instruction.Create(OpCodes.Box, type)); + return typeToProxy[ptype]; } + ptype = ptype.DeclaringType; } - instructions.Add(Instruction.Create(OpCodes.Call, mbase)); - instructions.Add(Instruction.Create(OpCodes.Ret)); - type.Methods.Add(proxyMethod); - return proxyMethod; } return null; + } enum CallType { Extern, Internal, + InteralVirtual, Invalid } @@ -741,7 +870,7 @@ bool isFieldStoreInVitualMachine(FieldReference field) return false; } - if (!isCompilerGenerated(field) && !isCompilerGenerated(field.DeclaringType)) + if ((!isCompilerGenerated(field) && !isCompilerGenerated(field.DeclaringType)) || !isNewClass(field.DeclaringType as TypeDefinition)) { return false; } @@ -761,6 +890,15 @@ bool isNewMethod(MethodDefinition method) return configure.IsNewMethod(method); } + bool isNewClass(TypeDefinition type) + { + return configure.IsNewClass(type); + } + + bool isNewField(FieldDefinition field) + { + return configure.isNewField(field); + } Dictionary interpretMethods = new Dictionary(); void addInterpretMethod(MethodDefinition method, int methodId) @@ -783,8 +921,8 @@ bool isFieldAccessInject(MethodDefinition method, int methodId) return false; } - //字段注入方式处理逻辑 - //目前用不上,但后续支持泛型修复需要用到 + //瀛楁娉ㄥ叆鏂瑰紡澶勭悊閫昏緫 + //鐩墠鐢ㄤ笉涓婏紝浣嗗悗缁敮鎸佹硾鍨嬩慨澶嶉渶瑕佺敤鍒 void fieldAccessInject(InjectType injectType, MethodDefinition method, int methodId) { var redirectBridge = getRedirectField(method); @@ -837,7 +975,7 @@ void fieldAccessInject(InjectType injectType, MethodDefinition method, int metho ilProcessor.InsertBefore(insertPoint, Instruction.Create(OpCodes.Callvirt, redirectTo)); } - //id注入方式处理逻辑 + //id娉ㄥ叆鏂瑰紡澶勭悊閫昏緫 void idAccessInject(InjectType injectType, MethodDefinition method, int methodId) { addRedirectIdInfo(method, methodId); @@ -914,7 +1052,7 @@ int allocMethodId(MethodDefinition method) methodToId.Add(method, methodId); - if (methodId > ushort.MaxValue) + if (mode == ProcessMode.Patch && methodId > ushort.MaxValue) { throw new OverflowException("too many internal methods"); } @@ -922,17 +1060,17 @@ int allocMethodId(MethodDefinition method) } /// - /// 获取一个函数的id - /// 该函数会触发指令序列生成 + /// 鑾峰彇涓涓嚱鏁扮殑id + /// 璇ュ嚱鏁颁細瑙﹀彂鎸囦护搴忓垪鐢熸垚 /// - /// 被调用函数 - /// 调用者 - /// 是个虚函数,会生成指令序列, - /// 但是调用通过反射来调用 - /// 调用者的注入类型 - /// 负数表示需要反射访问原生,0或正数是指令数组下标 + /// 琚皟鐢ㄥ嚱鏁 + /// 璋冪敤鑰 + /// 鏄釜铏氬嚱鏁帮紝浼氱敓鎴愭寚浠ゅ簭鍒楋紝 + /// 浣嗘槸璋冪敤閫氳繃鍙嶅皠鏉ヨ皟鐢 + /// 璋冪敤鑰呯殑娉ㄥ叆绫诲瀷 + /// 璐熸暟琛ㄧず闇瑕佸弽灏勮闂師鐢燂紝0鎴栨鏁版槸鎸囦护鏁扮粍涓嬫爣 // #lizard forgives - unsafe MethodIdInfo getMethodId(MethodReference callee, MethodDefinition caller, + unsafe MethodIdInfo getMethodId(MethodReference callee, MethodDefinition caller, bool isCallvirt, bool directCallVirtual = false, InjectType callerInjectType = InjectType.Switch) { //Console.WriteLine("callee:" + callee + ", caller:" + caller); @@ -946,9 +1084,35 @@ unsafe MethodIdInfo getMethodId(MethodReference callee, MethodDefinition caller, Type = CallType.Extern }; } + if (method != null) + { + if (method.IsAbstract && isCallvirt) + { + if (isCompilerGeneratedPlainObject(method.DeclaringType) || isCustomClassPlainObject(method.DeclaringType)) + { + return new MethodIdInfo() + { + Id = virtualMethodInVTableIndex(method), + Type = CallType.InteralVirtual + }; + } + } + } if (methodToId.ContainsKey(callee)) { + if (isCallvirt && isNewClass(callee.DeclaringType as TypeDefinition)) + { + getVirtualMethodForType(method.DeclaringType); + if (virtualMethodToIndex.ContainsKey(callee)) + { + return new MethodIdInfo() + { + Id = virtualMethodToIndex[callee], + Type = CallType.InteralVirtual + }; + } + } return new MethodIdInfo() { Id = methodToId[callee], @@ -956,12 +1120,12 @@ unsafe MethodIdInfo getMethodId(MethodReference callee, MethodDefinition caller, }; } - //如果是dll之外的方法,或者是构造函数,析构函数,作为虚拟机之外(extern)的方法 - if (method == null || (method.IsConstructor && !isCompilerGeneratedPlainObject(method.DeclaringType)) + //濡傛灉鏄痙ll涔嬪鐨勬柟娉曪紝鎴栬呮槸鏋勯犲嚱鏁帮紝鏋愭瀯鍑芥暟锛屼綔涓鸿櫄鎷熸満涔嬪锛坋xtern锛夌殑鏂规硶 + if (method == null || (method.IsConstructor && !(isCompilerGeneratedPlainObject(method.DeclaringType) || isCustomClassPlainObject(method.DeclaringType))) || method.IsFinalizer() || method.IsAbstract || method.IsPInvokeImpl || method.Body == null || method.DeclaringType.IsInterface - || (!methodToInjectType.ContainsKey(method) && !isCompilerGenerated(method.DeclaringType) + || (!methodToInjectType.ContainsKey(method) && !(isCompilerGenerated(method.DeclaringType) || isNewClass(method.DeclaringType)) && !isCompilerGenerated(method) && !(mode == ProcessMode.Patch && isNewMethod(method)))) { //Console.WriteLine("do no tranlater:" + callee + "," + callee.GetType()); @@ -984,13 +1148,12 @@ unsafe MethodIdInfo getMethodId(MethodReference callee, MethodDefinition caller, }; } - if (method.IsGeneric())//暂时不支持解析泛型 + if (method.IsGeneric())//鏆傛椂涓嶆敮鎸佽В鏋愭硾鍨 { return new MethodIdInfo() { Id = 0, Type = CallType.Invalid }; } - - var baseProxy = tryAddBaseProxy(method.DeclaringType, method); - + + tryAddBaseProxy(method.DeclaringType, method); var body = method.Body; var msIls = body.Instructions; var ilOffset = new Dictionary(); @@ -1001,7 +1164,7 @@ unsafe MethodIdInfo getMethodId(MethodReference callee, MethodDefinition caller, // || methodToInjectType[method] == InjectType.Redirect) { int stopPos; - //包含不支持指令的方法,作为虚拟机之外(extern)的方法 + //鍖呭惈涓嶆敮鎸佹寚浠ょ殑鏂规硶锛屼綔涓鸿櫄鎷熸満涔嬪锛坋xtern锛夌殑鏂规硶 if (!checkILAndGetOffset(method, msIls, ilOffset, out stopPos)) { InjectType it; @@ -1009,7 +1172,7 @@ unsafe MethodIdInfo getMethodId(MethodReference callee, MethodDefinition caller, { if (mode == ProcessMode.Patch || it == InjectType.Redirect) { - // 打patch发现不支持指令应该报错 + // 鎵損atch鍙戠幇涓嶆敮鎸佹寚浠ゅ簲璇ユ姤閿 throw new InvalidDataException("not support il[" + msIls[stopPos] + "] in " + method + ", caller is " + caller); } @@ -1044,15 +1207,16 @@ unsafe MethodIdInfo getMethodId(MethodReference callee, MethodDefinition caller, } //if (!methodToInjectType.TryGetValue(method, out injectType) // || injectType == InjectType.Redirect || mode == ProcessMode.Patch) + try { var code = new List(); codes.Add(methodId, code); if (!codeMustWriteToPatch.Contains(methodId) && ( - mode == ProcessMode.Patch || //patch阶段无论哪种类型都要写入补丁 + mode == ProcessMode.Patch || //patch闃舵鏃犺鍝绫诲瀷閮借鍐欏叆琛ヤ竵 (methodToInjectType.TryGetValue(method, out injectType) - && injectType == InjectType.Redirect) || //注入阶段,重定向类型需要写入补丁 - (callerInjectType == InjectType.Redirect) //被重定向类型函数调用,也需要写入补丁 + && injectType == InjectType.Redirect) || //娉ㄥ叆闃舵锛岄噸瀹氬悜绫诲瀷闇瑕佸啓鍏ヨˉ涓 + (callerInjectType == InjectType.Redirect) //琚噸瀹氬悜绫诲瀷鍑芥暟璋冪敤锛屼篃闇瑕佸啓鍏ヨˉ涓 )) { codeMustWriteToPatch.Add(methodId); @@ -1061,7 +1225,51 @@ unsafe MethodIdInfo getMethodId(MethodReference callee, MethodDefinition caller, code.Add(new Core.Instruction { Code = Core.Code.StackSpace, Operand = (body.Variables.Count << 16) | body.MaxStackSize }); // local | maxstack - //TODO: locals init,复杂值类型要new,引用类型要留空位 + int offsetAdd = 0; + + foreach(var variable in body.Variables) + { + if (variable.VariableType.IsValueType && !variable.VariableType.IsPrimitive) + { + if (isCompilerGenerated(variable.VariableType)) + { + code.Add(new Core.Instruction + { + Code = Core.Code.Newanon, + Operand = addAnonymousCtor(null, variable.VariableType) + }); + code.Add(new Core.Instruction + { + Code = Core.Code.Stloc, + Operand = variable.Index + }); + } + else + { + code.Add(new Core.Instruction + { + Code = Core.Code.Ldloca, + Operand = variable.Index, + }); + code.Add(new Core.Instruction + { + Code = Core.Code.Initobj, + Operand = addExternType(variable.VariableType) + }); + } + offsetAdd += 2; + } + } + + if (offsetAdd > 0) + { + var ilNewOffset = new Dictionary(); + foreach (var kv in ilOffset) + { + ilNewOffset[kv.Key] = kv.Value + offsetAdd; + } + ilOffset = ilNewOffset; + } Core.ExceptionHandler[] exceptionHandlers = new Core.ExceptionHandler[body.ExceptionHandlers.Count]; @@ -1096,7 +1304,7 @@ unsafe MethodIdInfo getMethodId(MethodReference callee, MethodDefinition caller, bool typeofDetected = false; - Core.Instruction operand; + Core.Instruction operand = new Core.Instruction(); for (int i = 0; i < msIls.Count; i++) { var msIl = msIls[i]; @@ -1116,7 +1324,7 @@ unsafe MethodIdInfo getMethodId(MethodReference callee, MethodDefinition caller, //case Code.Conv_Ovf_I8: //case Code.Conv_Ovf_I8_Un: //case Code.Conv_Ovf_U8: - //case Code.Conv_Ovf_U8_Un: // 指令合并 + //case Code.Conv_Ovf_U8_Un: // 鎸囦护鍚堝苟 // code.Add(new Core.Instruction // { // Code = Core.Code.Conv_I8, @@ -1130,14 +1338,14 @@ unsafe MethodIdInfo getMethodId(MethodReference callee, MethodDefinition caller, int leaveTo = ilOffset[msIl.Operand as Instruction]; if (exceptionHandler == null || (exceptionHandler.TryStart <= leaveTo - && exceptionHandler.TryEnd > leaveTo)) // 退化成Br + && exceptionHandler.TryEnd > leaveTo)) // 閫鍖栨垚Br { code.Add(new Core.Instruction { Code = Core.Code.Br, Operand = leaveTo - ilOffset[msIl] }); - code.Add(new Core.Instruction //补指令 + code.Add(new Core.Instruction //琛ユ寚浠 { Code = Core.Code.Nop, Operand = 0 @@ -1178,7 +1386,7 @@ unsafe MethodIdInfo getMethodId(MethodReference callee, MethodDefinition caller, code.Add(new Core.Instruction { Code = Core.Code.Endfinally, - Operand = nextIdx // -1表示最外层 + Operand = nextIdx // -1琛ㄧず鏈澶栧眰 }); } break; @@ -1361,11 +1569,17 @@ unsafe MethodIdInfo getMethodId(MethodReference callee, MethodDefinition caller, break; } var methodToCall = msIl.Operand as MethodReference; - if (msIl.OpCode.Code == Code.Newobj && isCompilerGeneratedPlainObject( - methodToCall.DeclaringType)) + if (methodToCall.ReturnType.IsByReference) + { + Console.WriteLine("Warning: method returning ByRef type is not supported. caller={0} callee={1}", + method.FullName, + methodToCall.FullName); + } + if (msIl.OpCode.Code == Code.Newobj && (isCompilerGeneratedPlainObject( + methodToCall.DeclaringType) || isCustomClassPlainObject(methodToCall.DeclaringType))) { TypeDefinition td = methodToCall.DeclaringType as TypeDefinition; - var anonymousCtorInfo = getMethodId(methodToCall, method, false, + var anonymousCtorInfo = getMethodId(methodToCall, method, false, false, injectTypePassToNext); if (anonymousCtorInfo.Type != CallType.Internal) { @@ -1404,25 +1618,21 @@ unsafe MethodIdInfo getMethodId(MethodReference callee, MethodDefinition caller, { code[code.Count - 2] = new Core.Instruction { - Code = Core.Code.Ldobj, - Operand = lastInstruction.Operand, - }; - code[code.Count - 1] = new Core.Instruction - { - Code = Core.Code.Box, - Operand = lastInstruction.Operand, + Code = Core.Code.Nop, + Operand = methodToCall.Parameters.Count, }; } //code.RemoveAt(code.Count - 1); } int paramCount = (methodToCall.Parameters.Count + (msIl.OpCode.Code != Code.Newobj && methodToCall.HasThis ? 1 : 0)); - var methodIdInfo = getMethodId(methodToCall, method, or != null || directCallVirtual, + var methodIdInfo = getMethodId(methodToCall, method, msIl.OpCode.Code == Code.Callvirt, or != null || directCallVirtual, injectTypePassToNext); - if (msIl.OpCode.Code == Code.Call && baseProxy != null - && isTheSameDeclare(methodToCall, method)) + if (msIl.OpCode.Code == Code.Call && baseProxys.ContainsKey(methodToCall)) { + var baseProxy = findProxy(method.DeclaringType, methodToCall); + if (baseProxy == null) throw new Exception("can not find the proxy for " + methodToCall + ", in " + method.DeclaringType); code.Add(new Core.Instruction { Code = Core.Code.CallExtern, @@ -1451,6 +1661,35 @@ unsafe MethodIdInfo getMethodId(MethodReference callee, MethodDefinition caller, Operand = (paramCount << 16) | methodIdInfo.Id }); } + else if (methodIdInfo.Type == CallType.InteralVirtual) + { + if ((methodToCall as MethodDefinition).IsVirtual) + { + int idx = -1; + if(!virtualMethodToIndex.TryGetValue(methodToCall,out idx)) + { + idx = virtualMethodInVTableIndex(methodToCall as MethodDefinition); + } + code.Add(new Core.Instruction + { + Code = Core.Code.Callvirtvirt, + Operand = (paramCount << 16) | idx + }); + } + else + { + code.Add(new Core.Instruction + { + Code = (or != null) ? Core.Code.Call : + (Core.Code)Enum.Parse(typeof(Core.Code), strCode), + Operand = (paramCount << 16) | methodToId[method] + }); + if (msIl.OpCode.Code == Code.Newobj) + { + throw new InvalidProgramException("Newobj's Operand is not a constructor?"); + } + } + } else { throw new InvalidProgramException("call a generic method definition"); @@ -1461,10 +1700,25 @@ unsafe MethodIdInfo getMethodId(MethodReference callee, MethodDefinition caller, case Code.Ldvirtftn: { var methodToCall = msIl.Operand as MethodReference; - var methodIdInfo = getMethodId(methodToCall, method, false, injectTypePassToNext); + var methodIdInfo = getMethodId(methodToCall, method, msIl.OpCode.Code == Code.Ldvirtftn, false, injectTypePassToNext); if (methodIdInfo.Type == CallType.Internal - && isCompilerGeneratedPlainObject(methodToCall.DeclaringType)) // closure + && (isCompilerGeneratedPlainObject(methodToCall.DeclaringType) || isCustomClassPlainObject(methodToCall.DeclaringType))) // closure { + if ((methodToCall as MethodDefinition).IsVirtual) + { + int methodIndex = -1; + if (!virtualMethodToIndex.TryGetValue(methodToCall,out methodIndex)) + { + methodIndex = virtualMethodInVTableIndex(methodToCall as MethodDefinition); + } + code.Add(new Core.Instruction + { + Code = Core.Code.Ldvirtftn2, + Operand = methodIndex + }); + break; + + } //Console.WriteLine("closure: " + methodToCall); getWrapperMethod(wrapperType, anonObjOfWrapper, methodToCall as MethodDefinition, true, true); @@ -1475,7 +1729,7 @@ unsafe MethodIdInfo getMethodId(MethodReference callee, MethodDefinition caller, }); break; } - //TODO: 如果生成代码做了delegate的cache怎么办呢? + //TODO锛 濡傛灉鐢熸垚浠g爜鍋氫簡delegate鐨刢ache鎬庝箞鍔炲憿锛 else if (methodIdInfo.Type == CallType.Internal && (isCompilerGenerated(methodToCall as MethodDefinition) || isNewMethod(methodToCall as MethodDefinition)) ) @@ -1490,7 +1744,7 @@ unsafe MethodIdInfo getMethodId(MethodReference callee, MethodDefinition caller, }); break; } - else //TODO:如果闭包含不支持的指令怎么办? + else //TODO锛氬鏋滈棴鍖呭惈涓嶆敮鎸佺殑鎸囦护鎬庝箞鍔烇紵 { code.Add(new Core.Instruction { @@ -1526,15 +1780,23 @@ unsafe MethodIdInfo getMethodId(MethodReference callee, MethodDefinition caller, } break; case Code.Box: - case Code.Isinst: case Code.Unbox_Any: case Code.Unbox: + case Code.Castclass: + case Code.Isinst: + code.Add(new Core.Instruction + { + Code = (Core.Code)Enum.Parse(typeof(Core.Code), strCode), + Operand = isNewClass(msIl.Operand as TypeDefinition) ? + -addAnonymousCtor(null, msIl.Operand as TypeReference) - 1 + : addExternType(msIl.Operand as TypeReference) + }); + break; case Code.Newarr: case Code.Ldelema: case Code.Initobj: case Code.Ldobj: case Code.Stobj: - case Code.Castclass: code.Add(new Core.Instruction { Code = (Core.Code)Enum.Parse(typeof(Core.Code), strCode), @@ -1546,13 +1808,20 @@ unsafe MethodIdInfo getMethodId(MethodReference callee, MethodDefinition caller, case Code.Ldflda: { var field = msIl.Operand as FieldReference; - if (isCompilerGeneratedPlainObject(field.DeclaringType)) + if (isCompilerGenerated(field.DeclaringType) || isCompilerGeneratedPlainObject(field.DeclaringType) || isCustomClassPlainObject(field.DeclaringType)) { var declaringType = field.DeclaringType as TypeDefinition; + int baseFieldCount = 0; + var temp = declaringType; + while (temp.BaseType != null && isNewClass(temp.BaseType as TypeDefinition)) + { + baseFieldCount += (temp.BaseType as TypeDefinition).Fields.Count; + temp = temp.BaseType as TypeDefinition; + } code.Add(new Core.Instruction { Code = (Core.Code)Enum.Parse(typeof(Core.Code), strCode), - Operand = -(declaringType.Fields.IndexOf(field as FieldDefinition) + 1) + Operand = -(declaringType.Fields.IndexOf(field as FieldDefinition) + baseFieldCount + 1) }); //Console.WriteLine("anon obj field:" + field + ",idx:" + // declaringType.Fields.IndexOf(field as FieldDefinition)); @@ -1573,7 +1842,7 @@ unsafe MethodIdInfo getMethodId(MethodReference callee, MethodDefinition caller, var fr = msIl.Operand as FieldReference; var fd = fr.Resolve(); bool storeInVitualMachine = (isCompilerGenerated(fr) - || isCompilerGenerated(fr.DeclaringType)) && + || isCompilerGenerated(fr.DeclaringType) || isNewClass(fr.DeclaringType as TypeDefinition)) && !getSpecialGeneratedFields(fr.DeclaringType.Resolve()).Contains(fd) && typeToCctor[fd.DeclaringType] > -2; if (!storeInVitualMachine && isCompilerGenerated(fr) && fd.Name.IndexOf("$cache") >= 0 @@ -1665,6 +1934,17 @@ unsafe MethodIdInfo getMethodId(MethodReference callee, MethodDefinition caller, Console.WriteLine("patched: " + method); } } + catch(Exception e) + { + if (mode == ProcessMode.Inject) + { + Console.WriteLine("Warning: process " + method + " il throw " + e); + } + else + { + throw e; + } + } //Console.WriteLine("process finish:" + method); if (mode == ProcessMode.Inject) @@ -1672,13 +1952,24 @@ unsafe MethodIdInfo getMethodId(MethodReference callee, MethodDefinition caller, injectMethod(method, methodId); } - if (!directCallVirtual && method.IsVirtual) + if (!directCallVirtual && method.IsVirtual && isCallvirt) { - return new MethodIdInfo() + if (isNewClass(method.DeclaringType)) { - Id = addExternMethod(callee, caller), - Type = CallType.Extern - }; + return new MethodIdInfo() + { + Id = virtualMethodInVTableIndex(method), + Type = CallType.InteralVirtual + }; + } + else + { + return new MethodIdInfo() + { + Id = addExternMethod(callee, caller), + Type = CallType.Extern + }; + } } else { @@ -1702,13 +1993,15 @@ public enum ProcessResult private TypeReference voidType; private TypeDefinition wrapperType; private TypeDefinition idMapType; + private TypeReference enumType; + private List idMapList; private TypeDefinition itfBridgeType; private int bridgeMethodId; private TypeReference anonymousStoreyTypeRef; private MethodReference anonymousStoreyCtorRef; private FieldDefinition virualMachineFieldOfWrapper; - private FieldDefinition virualMachineFieldOfBridge; + private FieldReference virualMachineFieldOfBridge; private FieldDefinition methodIdFieldOfWrapper; private FieldDefinition anonObjOfWrapper; private FieldDefinition wrapperArray; @@ -1752,18 +2045,7 @@ TypeReference wrapperParamerterType(TypeReference type) } if (type.IsValueType) { - if (type.IsPrimitive) - { - return type; - } - try - { - if (type.Resolve().IsEnum) - { - return type; - } - } - catch { } + return type; } return objType; } @@ -1776,21 +2058,42 @@ TypeReference getRawType(TypeReference type) private List anonymousTypeInfos = new List(); private Dictionary anonymousTypeToId = new Dictionary(); - int addAnonymousCtor(MethodDefinition ctor) + int addAnonymousCtor(MethodDefinition ctor, TypeReference variableType = null) { int id; - if (anonymousTypeToId.TryGetValue(ctor, out id)) + var ctorOrMethod = ctor != null ? ctor : (variableType as TypeDefinition).Methods[0]; + if (anonymousTypeToId.TryGetValue(ctorOrMethod, out id)) { return id; } - addInterfacesOfTypeToBridge(ctor.DeclaringType as TypeDefinition); - foreach(var method in ctor.DeclaringType.Methods.Where(m => !m.IsConstructor)) + var typeDefinition = ctor != null ? (ctor.DeclaringType as TypeDefinition) : (variableType as TypeDefinition); + addInterfacesOfTypeToBridge(typeDefinition); + var methods = typeDefinition.Methods.Where(m => !m.IsConstructor).ToList(); + if(variableType != null || ctor.DeclaringType.Fields.Count > 0) + { + for (int field = 0; field < typeDefinition.Fields.Count; field++) + { + if (typeDefinition.Fields[field].FieldType.IsValueType) + { + if (!isCompilerGenerated(typeDefinition.Fields[field].FieldType)) + { + if (!externTypes.Contains(typeDefinition.Fields[field].FieldType)) + { + addExternType(typeDefinition.Fields[field].FieldType); + } + } + } + } + } + + foreach (var method in methods) { - getMethodId(method, null, true, InjectType.Redirect); + getMethodId(method, null,true, true, InjectType.Redirect); } id = anonymousTypeInfos.Count; - anonymousTypeInfos.Add(ctor); - anonymousTypeToId[ctor] = id; + var methodDefinition = ctor != null ? ctor : methods[0]; + anonymousTypeInfos.Add(methodDefinition); + anonymousTypeToId[methodDefinition] = id; return id; } @@ -1808,9 +2111,9 @@ void bridgeImplement(TypeReference itf) } /// - /// 桥接器实现一个类的所有接口,一般来说是个匿名类 + /// 妗ユ帴鍣ㄥ疄鐜颁竴涓被鐨勬墍鏈夋帴鍙o紝涓鑸潵璇存槸涓尶鍚嶇被 /// - /// 要实现桥接的匿名类 + /// 瑕佸疄鐜版ˉ鎺ョ殑鍖垮悕绫 void addInterfacesOfTypeToBridge(TypeDefinition anonType) { if (anonType.Interfaces.Count == 0) @@ -1842,27 +2145,26 @@ void addInterfacesOfTypeToBridge(TypeDefinition anonType) } //implementMap[method] = matchItfMethod; - if (itfBridgeType.Interfaces.Any(ii => ii.InterfaceType.IsSameType(matchItfMethod.DeclaringType))) + if (matchItfMethod == null || itfBridgeType.Interfaces.Any(ii => ii.InterfaceType.IsSameType(matchItfMethod.DeclaringType))) { continue; } } - else //Enumerator 语法糖里头再有个闭包语法糖,会在类那生成一个非私有,非公有的函数 + else //Enumerator 璇硶绯栭噷澶村啀鏈変釜闂寘璇硶绯栵紝浼氬湪绫婚偅鐢熸垚涓涓潪绉佹湁锛岄潪鍏湁鐨勫嚱鏁 { continue; } - if (matchItfMethod == null) + if (matchItfMethod != null) { - throw new Exception("can not find base method for " + method); + toImplement.Add(matchItfMethod.DeclaringType); + //Console.WriteLine("add slot " + matchItfMethod + ",m=" + method); + interfaceSlot.Add(matchItfMethod, bridgeMethodId); + var impl = getWrapperMethod(itfBridgeType, null, method, false, true, true, bridgeMethodId); + addIDTag(impl, bridgeMethodId++); + impl.Overrides.Add(matchItfMethod); } - toImplement.Add(matchItfMethod.DeclaringType); - //Console.WriteLine("add slot " + matchItfMethod + ",m=" + method); - interfaceSlot.Add(matchItfMethod, bridgeMethodId); - var impl = getWrapperMethod(itfBridgeType, null, method, false, true, true, bridgeMethodId); - addIDTag(impl, bridgeMethodId++); - impl.Overrides.Add(matchItfMethod); } //Console.WriteLine("end type:" + anonType); @@ -1911,16 +2213,121 @@ void addInterfaceToBridge(TypeReference itf) } } + void EmitRefAwaitUnsafeOnCompletedMethod() + { + MethodDefinition targetMethod = new MethodDefinition("RefAwaitUnsafeOnCompleteMethod", + Mono.Cecil.MethodAttributes.Public, assembly.MainModule.TypeSystem.Void); + var instructions = targetMethod.Body.Instructions; + var localBridge = new VariableDefinition(itfBridgeType); + targetMethod.Body.Variables.Add(localBridge); + for (int j = 0;j < awaitUnsafeOnCompletedMethods.Count;j++) + { + var localTaskAwaiter = new VariableDefinition(((GenericInstanceMethod)awaitUnsafeOnCompletedMethods[j]).GenericArguments[0]); + targetMethod.Body.Variables.Add(localTaskAwaiter); + var localAsync = new VariableDefinition(awaitUnsafeOnCompletedMethods[j].DeclaringType); + targetMethod.Body.Variables.Add(localAsync); + instructions.Add(Instruction.Create(OpCodes.Ldloca_S, localAsync)); + instructions.Add(Instruction.Create(OpCodes.Ldloca_S, localTaskAwaiter)); + instructions.Add(Instruction.Create(OpCodes.Ldloca_S, localBridge)); + instructions.Add(Instruction.Create(OpCodes.Call, makeGenericMethod(awaitUnsafeOnCompletedMethods[j].GetElementMethod(), ((GenericInstanceMethod)awaitUnsafeOnCompletedMethods[j]).GenericArguments[0], itfBridgeType))); + } + instructions.Add(Instruction.Create(OpCodes.Ret)); + itfBridgeType.Methods.Add(targetMethod); + } + + private void EmitAsyncBuilderStartMethod(IEnumerable allTypes) + { + var builders = new List(); + var genericBuilders = new List(); + + // 鎵惧埌鎵鏈夊紓姝ユ柟娉曠殑builder + foreach(var typeDefinition in allTypes) + { + foreach(var nestedType in typeDefinition.NestedTypes) + { + try + { + var isStateMachine = + nestedType.Interfaces.Any(e => e.InterfaceType.Name == "IAsyncStateMachine"); + + if(!isStateMachine) + continue; + + var builder = nestedType.Fields.First(e => e.Name.EndsWith("builder")); + var builderType = builder.FieldType; + + if(builderType.ContainsGenericParameter) + continue; + + if(!builderType.IsValueType) + continue; + + if(builderType.IsGenericInstance) + { + if(genericBuilders.Any(e => ((GenericInstanceType) e).GenericArguments[0] + == ((GenericInstanceType) builderType).GenericArguments[0])) + continue; + + genericBuilders.Add(builderType); + } + else + { + if(builders.Contains(builderType)) + continue; + + builders.Add(builderType); + } + } + catch (Exception e) + { + Console.WriteLine("Warning: get builder in " + typeDefinition + " throw: " + e); + } + } + } + + // 鐢熸垚Start鍑芥暟寮曠敤 + builders.AddRange(genericBuilders); + + var targetMethod = new MethodDefinition("RefAsyncBuilderStartMethod", MethodAttributes.Public, + assembly.MainModule.TypeSystem.Void); + var instructions = targetMethod.Body.Instructions; + var localBridge = new VariableDefinition(itfBridgeType); + targetMethod.Body.Variables.Add(localBridge); + + foreach(var builder in builders) + { + var start = new MethodReference("Start", voidType, builder) + { + HasThis = true, CallingConvention = MethodCallingConvention.Generic + }; + var genericParameter = new GenericParameter("!!0", start); + start.GenericParameters.Add(genericParameter); + var byReferenceType = new ByReferenceType(genericParameter); + start.Parameters.Add(new ParameterDefinition(byReferenceType)); + + var localBuilder = new VariableDefinition(builder); + targetMethod.Body.Variables.Add(localBuilder); + + instructions.Add(Instruction.Create(OpCodes.Ldloca_S, localBuilder)); + instructions.Add(Instruction.Create(OpCodes.Ldloca_S, localBridge)); + instructions.Add(Instruction.Create(OpCodes.Call, makeGenericMethod(start, itfBridgeType))); + } + + instructions.Add(Instruction.Create(OpCodes.Ret)); + itfBridgeType.Methods.Add(targetMethod); + } + + /// - /// 获取一个方法的适配器 + /// 鑾峰彇涓涓柟娉曠殑閫傞厤鍣 /// - /// 方法适配器的放置类 - /// 适配器所绑定的匿名对象 - /// 要适配的方法 - /// 是不是闭包 - /// 是否向基类收敛(如果是delegate适配器,就不能收敛) - /// 是否是接口桥接器 - /// 方法id + /// 鏂规硶閫傞厤鍣ㄧ殑鏀剧疆绫 + /// 閫傞厤鍣ㄦ墍缁戝畾鐨勫尶鍚嶅璞 + /// 瑕侀傞厤鐨勬柟娉 + /// 鏄笉鏄棴鍖 + /// 鏄惁鍚戝熀绫绘敹鏁涳紙濡傛灉鏄痙elegate閫傞厤鍣紝灏变笉鑳芥敹鏁涳級 + /// 鏄惁鏄帴鍙fˉ鎺ュ櫒 + /// 鏂规硶id /// // #lizard forgives MethodDefinition getWrapperMethod(TypeDefinition type, FieldDefinition anonObj, MethodReference method, @@ -1931,15 +2338,17 @@ MethodDefinition getWrapperMethod(TypeDefinition type, FieldDefinition anonObj, { md = method.Resolve(); } - //原始参数类型 + //鍘熷鍙傛暟绫诲瀷 List parameterTypes = new List(); - //适配器参数类型,不是强制noBaselize的话,引用类型,复杂非引用值类型,均转为object + //閫傞厤鍣ㄥ弬鏁扮被鍨嬶紝涓嶆槸寮哄埗noBaselize鐨勮瘽锛屽紩鐢ㄧ被鍨嬶紝澶嶆潅闈炲紩鐢ㄥ肩被鍨嬶紝鍧囪浆涓簅bject List wrapperParameterTypes = new List(); List isOut = new List(); + List isIn = new List(); //List paramAttrs = new List(); - if (!md.IsStatic && !isClosure && !isInterfaceBridge) //匿名类闭包的this是自动传,不需要显式参数 + if (!md.IsStatic && !isClosure && !isInterfaceBridge) //鍖垮悕绫婚棴鍖呯殑this鏄嚜鍔ㄤ紶锛屼笉闇瑕佹樉寮忓弬鏁 { isOut.Add(false); + isIn.Add(false); //paramAttrs.Add(Mono.Cecil.ParameterAttributes.None); if (method.DeclaringType.IsValueType) { @@ -1958,12 +2367,17 @@ MethodDefinition getWrapperMethod(TypeDefinition type, FieldDefinition anonObj, for (int i = 0; i < method.Parameters.Count; i++) { isOut.Add(method.Parameters[i].IsOut); + isIn.Add(method.Parameters[i].IsIn); //paramAttrs.Add(method.Parameters[i].Attributes); var paramType = method.Parameters[i].ParameterType; if (paramType.IsGenericParameter) { paramType = (paramType as GenericParameter).ResolveGenericArgument(method.DeclaringType); } + if (paramType.IsRequiredModifier) + { + paramType = (paramType as RequiredModifierType).ElementType; + } parameterTypes.Add(paramType); wrapperParameterTypes.Add(noBaselize ? paramType : wrapperParamerterType(paramType)); } @@ -2001,7 +2415,7 @@ MethodDefinition getWrapperMethod(TypeDefinition type, FieldDefinition anonObj, for (int j = 0; j < wrapperParameterTypes.Count; j++) { if (!wrapperParameterTypes[j].IsSameType(wrapperMethod.Parameters[j].ParameterType) - || isOut[j] != wrapperMethod.Parameters[j].IsOut) + || isOut[j] != wrapperMethod.Parameters[j].IsOut || isIn[j] != wrapperMethod.Parameters[j].IsIn) { paramMatch = false; break; @@ -2024,9 +2438,11 @@ MethodDefinition getWrapperMethod(TypeDefinition type, FieldDefinition anonObj, for (int i = 0; i < parameterTypes.Count; i++) { - refPos[i] = parameterTypes[i].IsByReference ? refCount++ : -1; - wrapperMethod.Parameters.Add(new ParameterDefinition("P" + i, isOut[i] ? ParameterAttributes.Out - : ParameterAttributes.None, wrapperParameterTypes[i].TryImport(assembly.MainModule))); + refPos[i] = (parameterTypes[i].IsByReference) ? refCount++ : -1; + var parameterAttributes = ParameterAttributes.None; + if (isOut[i]) parameterAttributes |= ParameterAttributes.Out; + if (isIn[i]) parameterAttributes |= ParameterAttributes.In; + wrapperMethod.Parameters.Add(new ParameterDefinition("P" + i, parameterAttributes, wrapperParameterTypes[i].TryImport(assembly.MainModule))); } var ilProcessor = wrapperMethod.Body.GetILProcessor(); @@ -2049,6 +2465,7 @@ MethodDefinition getWrapperMethod(TypeDefinition type, FieldDefinition anonObj, instructions.Add(Instruction.Create(OpCodes.Ldloca_S, call)); MethodReference push; var wpt = wrapperParamerterType(paramRawType); + wpt = (wpt.IsValueType && !wpt.IsPrimitive) ? objType : wpt; if (pushMap.TryGetValue(wpt, out push)) { if (wpt == assembly.MainModule.TypeSystem.Object) @@ -2221,16 +2638,16 @@ MethodDefinition getWrapperMethod(TypeDefinition type, FieldDefinition anonObj, // Ref param for (int i = 0; i < parameterTypes.Count; i++) { - if (parameterTypes[i].IsByReference) + if (parameterTypes[i].IsByReference && ! isIn[i]) { emitLdarg(instructions, ilProcessor, i + 1); var paramRawType = tryGetUnderlyingType(getRawType(parameterTypes[i])); instructions.Add(Instruction.Create(OpCodes.Ldloca_S, call)); emitLdcI4(instructions, refPos[i]); - if (getMap.ContainsKey(paramRawType)) + if (paramRawType.IsPrimitive && getMap.ContainsKey(paramRawType.Resolve())) { - instructions.Add(Instruction.Create(OpCodes.Callvirt, getMap[paramRawType])); + instructions.Add(Instruction.Create(OpCodes.Callvirt, getMap[paramRawType.Resolve()])); } else { @@ -2247,7 +2664,8 @@ MethodDefinition getWrapperMethod(TypeDefinition type, FieldDefinition anonObj, instructions.Add(Instruction.Create(OpCodes.Ldloca_S, call)); MethodReference get; emitLdcI4(instructions, refCount); - if (getMap.TryGetValue(tryGetUnderlyingType(returnType), out get)) + var returnRawType = tryGetUnderlyingType(returnType); + if (returnRawType.IsPrimitive && getMap.TryGetValue(returnRawType.Resolve(), out get)) { instructions.Add(Instruction.Create(OpCodes.Callvirt, get)); } @@ -2312,6 +2730,7 @@ TypeReference tryGetUnderlyingType(TypeReference type) { try { + if (type.IsArray) return type; TypeDefinition typeDefinition = type.Resolve(); if (typeDefinition.IsEnum) { @@ -2327,7 +2746,7 @@ TypeReference tryGetUnderlyingType(TypeReference type) return type; } - //不能直接栈上表示的值类型,都boxing + //涓嶈兘鐩存帴鏍堜笂琛ㄧず鐨勫肩被鍨嬶紝閮絙oxing void emitLoadRef(Mono.Collections.Generic.Collection instructions, TypeReference type) { var underlyingTypetype = tryGetUnderlyingType(type); @@ -2381,11 +2800,24 @@ void emitStoreRef(Mono.Collections.Generic.Collection instructions, OpCodes.Ldc_I4_3,OpCodes.Ldc_I4_4, OpCodes.Ldc_I4_5, OpCodes.Ldc_I4_6, OpCodes.Ldc_I4_7 }; private Dictionary ldinds = null; private Dictionary stinds = null; + private List ObjectVirtualMethodDefinitionList = null; + private List objectVirtualMethodReferenceList = null; void init(AssemblyDefinition assembly, AssemblyDefinition ilfixAassembly) { this.assembly = assembly; objType = assembly.MainModule.TypeSystem.Object; + List supportedMethods = new List() { "Equals", "Finalize","GetHashCode", "ToString"}; + ObjectVirtualMethodDefinitionList = (from method in objType.Resolve().Methods where method.IsVirtual && supportedMethods.Contains(method.Name) select method).ToList(); + if (ObjectVirtualMethodDefinitionList.Count != 4) + { + throw new InvalidProgramException(); + } + ObjectVirtualMethodDefinitionList.OrderBy(t => t.FullName); + for (int methodIdx = 0; methodIdx < ObjectVirtualMethodDefinitionList.Count; methodIdx++) + { + virtualMethodToIndex.Add(ObjectVirtualMethodDefinitionList[methodIdx], methodIdx); + } voidType = assembly.MainModule.TypeSystem.Void; wrapperType = new TypeDefinition("IFix", DYNAMICWRAPPER, Mono.Cecil.TypeAttributes.Class @@ -2449,24 +2881,23 @@ void init(AssemblyDefinition assembly, AssemblyDefinition ilfixAassembly) var anonymousStoreyType = ilfixAassembly.MainModule.Types.Single(t => t.Name == "AnonymousStorey"); anonymousStoreyTypeRef = assembly.MainModule.ImportReference(anonymousStoreyType); anonymousStoreyCtorRef = assembly.MainModule.ImportReference( - anonymousStoreyType.Methods.Single(m => m.Name == ".ctor" && m.Parameters.Count == 1)); + anonymousStoreyType.Methods.Single(m => m.Name == ".ctor" && m.Parameters.Count == 5)); + + objectVirtualMethodReferenceList = anonymousStoreyType.Methods.Where(m => m.Name.StartsWith("Object")). + Select(m => assembly.MainModule.ImportReference(m)).ToList(); itfBridgeType = new TypeDefinition("IFix", INTERFACEBRIDGE, TypeAttributes.Class | TypeAttributes.Public, anonymousStoreyTypeRef); - virualMachineFieldOfBridge = new FieldDefinition("virtualMachine", Mono.Cecil.FieldAttributes.Private, - VirtualMachineType); - itfBridgeType.Fields.Add(virualMachineFieldOfBridge); + virualMachineFieldOfBridge = assembly.MainModule.ImportReference(anonymousStoreyType.Fields.Single(f => f.Name == "virtualMachine")); assembly.MainModule.Types.Add(itfBridgeType); + addExternType(itfBridgeType); //end init itfBridgeType //begin init idMapper - var enumType = assembly.MainModule.ImportReference(typeof(System.Enum)); - idMapType = new TypeDefinition("IFix", "IDMAP", TypeAttributes.Public | TypeAttributes.Sealed, - enumType); - assembly.MainModule.Types.Add(idMapType); - idMapType.Fields.Add(new FieldDefinition("value__", FieldAttributes.Public | FieldAttributes.SpecialName - | FieldAttributes.RTSpecialName, assembly.MainModule.TypeSystem.Int32)); + enumType = assembly.MainModule.ImportReference(typeof(System.Enum)); + idMapList = new List(); + idMapType = null; //end init idMapper wrapperMethods = new List(); @@ -2539,10 +2970,28 @@ void init(AssemblyDefinition assembly, AssemblyDefinition ilfixAassembly) initStackOp(Call, assembly.MainModule.TypeSystem.UIntPtr); } + const int MAX_ID_MAP_FIELD_COUNT = 32760; + + void idMapTypeCheck() + { + if (idMapType == null || idMapType.Fields.Count >= MAX_ID_MAP_FIELD_COUNT) + { + if (idMapType != null) + { + idMapList.Add(idMapType); + } + idMapType = new TypeDefinition("IFix", "IDMAP" + idMapList.Count, TypeAttributes.Public | TypeAttributes.Sealed, + enumType); + assembly.MainModule.Types.Add(idMapType); + idMapType.Fields.Add(new FieldDefinition("value__", FieldAttributes.Public | FieldAttributes.SpecialName + | FieldAttributes.RTSpecialName, assembly.MainModule.TypeSystem.Int32)); + } + } + void initStackOp(TypeDefinition call, TypeReference type) { pushMap[type] = importMethodReference(call, "Push" + type.Name); - getMap[type] = importMethodReference(call, "Get" + type.Name); + getMap[type.Resolve()] = importMethodReference(call, "Get" + type.Name); nameToTypeReference[type.FullName] = type; } @@ -2787,6 +3236,7 @@ void addRedirectIdInfo(MethodDefinition method, int id) { throw new Exception("try inject method twice: " + method); } + idMapTypeCheck(); var redirectIdField = new FieldDefinition("tmp_r_field_" + redirectIdMap.Count, FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.Literal | FieldAttributes.HasDefault, idMapType); idMapType.Fields.Add(redirectIdField); @@ -2794,7 +3244,7 @@ void addRedirectIdInfo(MethodDefinition method, int id) redirectIdMap.Add(method, redirectIdField); } - //先加字段,建立关联关系 + //鍏堝姞瀛楁锛屽缓绔嬪叧鑱斿叧绯 FieldDefinition getRedirectField(MethodDefinition method) { if (redirectMemberMap.ContainsKey(method)) @@ -2841,7 +3291,7 @@ void redirectFieldRename() { kv.Value.Name = "_rf_" + methodName + (id++); } - if (id > 1) //有重载 + if (id > 1) //鏈夐噸杞 { id = 0; foreach(var kv in methodGroup) @@ -2867,7 +3317,7 @@ void redirectFieldRename() } duplicateCheck.Add(kv.Value.Name); } - if (id > 1) //有重载 + if (id > 1) //鏈夐噸杞 { id = 0; foreach (var kv in methodGroup) @@ -2877,21 +3327,24 @@ void redirectFieldRename() } } } + idMapList.Add(idMapType); + idMapType = null; } - //1、构造函数及析构函数不转,不支持的指令不转,转的函数留下函数定义,所以支持反射 - //2、不转的函数通过反射调用,已转函数调用已转函数在虚拟机内部完成 - //3、已转函数可以支持直接重定向并删除原实现(减包场景),以及保留原实现增加切换代码(修bug场景) - //4、已转函数需要生成wrap - //5、TODO:虚函数用base如何处理?私有函数是否需要保留入口?简单函数(比如getter/setter)是否要转? - //6、应该为基本值类型生成出入栈函数,防止过大GC - //7、Callvirt分析其是否真的是虚函数,虚函数反射调用,非虚并且动态方法直接调用 - //8、泛型等同多了一个Type[]参数 - //工具输入一个dll,输出dll+dif + //1銆佹瀯閫犲嚱鏁板強鏋愭瀯鍑芥暟涓嶈浆锛屼笉鏀寔鐨勬寚浠や笉杞紝杞殑鍑芥暟鐣欎笅鍑芥暟瀹氫箟锛屾墍浠ユ敮鎸佸弽灏 + //2銆佷笉杞殑鍑芥暟閫氳繃鍙嶅皠璋冪敤锛屽凡杞嚱鏁拌皟鐢ㄥ凡杞嚱鏁板湪铏氭嫙鏈哄唴閮ㄥ畬鎴 + //3銆佸凡杞嚱鏁板彲浠ユ敮鎸佺洿鎺ラ噸瀹氬悜骞跺垹闄ゅ師瀹炵幇锛堝噺鍖呭満鏅級锛屼互鍙婁繚鐣欏師瀹炵幇澧炲姞鍒囨崲浠g爜锛堜慨bug鍦烘櫙锛 + //4銆佸凡杞嚱鏁伴渶瑕佺敓鎴恮rap + //5銆乀ODO锛氳櫄鍑芥暟鐢╞ase濡備綍澶勭悊锛熺鏈夊嚱鏁版槸鍚﹂渶瑕佷繚鐣欏叆鍙o紵绠鍗曞嚱鏁帮紙姣斿getter/setter锛夋槸鍚﹁杞紵 + //6銆佸簲璇ヤ负鍩烘湰鍊肩被鍨嬬敓鎴愬嚭鍏ユ爤鍑芥暟锛岄槻姝㈣繃澶C + //7銆丆allvirt鍒嗘瀽鍏舵槸鍚︾湡鐨勬槸铏氬嚱鏁帮紝铏氬嚱鏁板弽灏勮皟鐢紝闈炶櫄骞朵笖鍔ㄦ佹柟娉曠洿鎺ヨ皟鐢 + //8銆佹硾鍨嬬瓑鍚屽浜嗕竴涓猅ype[]鍙傛暟 + //宸ュ叿杈撳叆涓涓猟ll锛岃緭鍑篸ll+dif Dictionary methodToInjectType = new Dictionary(); bool hasRedirect = false; ProcessMode mode; GenerateConfigure configure; + List awaitUnsafeOnCompletedMethods = new List(); public ProcessResult Process(AssemblyDefinition assembly, AssemblyDefinition ilfixAassembly, GenerateConfigure configure, ProcessMode mode) @@ -2911,14 +3364,14 @@ public ProcessResult Process(AssemblyDefinition assembly, AssemblyDefinition ilf //makeCloneFast(ilfixAassembly); var allTypes = (from type in assembly.GetAllType() - where type.Namespace != "IFix" && !type.IsGeneric() && !isCompilerGenerated(type) - select type); + where type.Namespace != "IFix" && !type.IsGeneric() && !(isCompilerGenerated(type) || isNewClass(type)) + select type).ToList(); foreach (var method in ( from type in allTypes - where !isCompilerGenerated(type) && !type.HasGenericParameters + where !(isCompilerGenerated(type) || isNewClass(type)) && !type.HasGenericParameters from method in type.Methods - where !method.IsConstructor && !isCompilerGenerated(method) && !method.HasGenericParameters + where !method.IsConstructor && !isCompilerGenerated(method) && !method.HasGenericParameters && !method.ReturnType.IsRequiredModifier select method)) { int flag; @@ -2933,6 +3386,58 @@ from method in type.Methods } } + foreach (var cls in ( + from type in allTypes + where type.IsClass select type)) + { + foreach (var field in cls.Fields) + { + if(isNewField(field)) + { + var ctor = + (from method in cls.Methods + where method.IsConstructor && (field.IsStatic ? method.Name == ".cctor" : method.Name == ".ctor") + select method).FirstOrDefault(); + + if(ctor != null) + { + var ret = new Mono.Collections.Generic.Collection(); + foreach (var instruction in ctor.Body.Instructions) + { + + var code = instruction.OpCode.Code; + + if((code == Code.Stsfld || code == Code.Stfld) && (instruction.Operand as FieldDefinition) == field) + { + emitFieldCtor(field, ret); + break; + } + else + { + ret.Add(instruction); + } + + if(field.IsStatic) + { + if(code == Code.Stsfld) + { + ret.Clear(); + } + } + else + { + if(code == Code.Stfld) + { + ret.Clear(); + } + } + } + } + } + } + } + + foreach(var kv in methodToInjectType) { processMethod(kv.Key); @@ -2947,11 +3452,60 @@ from method in type.Methods if (mode == ProcessMode.Inject) { redirectFieldRename(); - } + if (awaitUnsafeOnCompletedMethods.Count != 0) + { + EmitRefAwaitUnsafeOnCompletedMethod(); + } + + EmitAsyncBuilderStartMethod(allTypes); + } return ProcessResult.OK; } + void emitFieldCtor(FieldDefinition field, Mono.Collections.Generic.Collection insertInstructions) + { + var staticConstructorAttributes = + MethodAttributes.Private | + MethodAttributes.Static | + MethodAttributes.HideBySig | + MethodAttributes.SpecialName | + MethodAttributes.RTSpecialName; + + MethodDefinition fieldDefaultValue = new MethodDefinition("<>__ctor_" + field.Name, staticConstructorAttributes, assembly.MainModule.TypeSystem.Object); + + var instructions = fieldDefaultValue.Body.Instructions; + + foreach (var instruction in insertInstructions) + { + if(!instruction.OpCode.Code.ToString().Contains("Ldarg")) + { + instructions.Add(instruction); + } + } + + if(field.FieldType.IsValueType) + { + instructions.Add(Instruction.Create(OpCodes.Box, field.FieldType)); + } + + instructions.Add(Instruction.Create(OpCodes.Ret)); + + field.DeclaringType.Methods.Add(fieldDefaultValue); + + configure.AddNewMethod(fieldDefaultValue); + + methodToInjectType[fieldDefaultValue] = InjectType.Redirect; + hasRedirect = true; + + if (!newFieldToCtor.ContainsKey(field)) + { + var cctorInfo = getMethodId(fieldDefaultValue, null,false, false, InjectType.Redirect); + newFieldToCtor[field] = cctorInfo.Id; + } + } + + void postProcessInterfaceBridge() { //foreach(var g in itfBridgeType.Methods.GroupBy(m => m.Name).Select(g => g.ToList())) @@ -2962,7 +3516,7 @@ void postProcessInterfaceBridge() // } //} - //为getter setter增加对应的property + //涓篻etter setter澧炲姞瀵瑰簲鐨刾roperty foreach (var m in itfBridgeType.Methods) { if (m.IsSpecialName && !m.IsConstructor) @@ -2976,7 +3530,7 @@ void postProcessInterfaceBridge() if (!name.StartsWith("get_") && !name.StartsWith("set_")) { - throw new NotImplementedException("do not support special method: " + m); + continue; } var propName = name.Substring(4); @@ -3009,7 +3563,7 @@ void postProcessInterfaceBridge() } } - //bridge的构造函数 + //bridge鐨勬瀯閫犲嚱鏁 var methodIdFields = itfBridgeType.Fields.Where(f => f.Name.StartsWith(METHODIDPERFIX)).ToList(); int methodIdPerfixLen = METHODIDPERFIX.Length; methodIdFields.Sort((l, r) => int.Parse(l.Name.Substring(methodIdPerfixLen)) @@ -3020,6 +3574,12 @@ void postProcessInterfaceBridge() | MethodAttributes.RTSpecialName, voidType); ctorOfItfBridgeType.Parameters.Add(new ParameterDefinition("fieldNum", Mono.Cecil.ParameterAttributes.None, assembly.MainModule.TypeSystem.Int32)); + ctorOfItfBridgeType.Parameters.Add(new ParameterDefinition("fieldTypes", + Mono.Cecil.ParameterAttributes.None, new ArrayType(assembly.MainModule.TypeSystem.Int32))); + ctorOfItfBridgeType.Parameters.Add(new ParameterDefinition("typeIndex", + Mono.Cecil.ParameterAttributes.None, assembly.MainModule.TypeSystem.Int32)); + ctorOfItfBridgeType.Parameters.Add(new ParameterDefinition("vTable", + Mono.Cecil.ParameterAttributes.None, new ArrayType(assembly.MainModule.TypeSystem.Int32))); ctorOfItfBridgeType.Parameters.Add(new ParameterDefinition("methodIdArray", Mono.Cecil.ParameterAttributes.None, new ArrayType(assembly.MainModule.TypeSystem.Int32))); ctorOfItfBridgeType.Parameters.Add(new ParameterDefinition("virtualMachine", @@ -3027,17 +3587,17 @@ void postProcessInterfaceBridge() var instructions = ctorOfItfBridgeType.Body.Instructions; instructions.Add(Instruction.Create(OpCodes.Ldarg_0)); instructions.Add(Instruction.Create(OpCodes.Ldarg_1)); + instructions.Add(Instruction.Create(OpCodes.Ldarg_2)); + instructions.Add(Instruction.Create(OpCodes.Ldarg_3)); + instructions.Add(createLdarg(ctorOfItfBridgeType.Body.GetILProcessor(), 4)); + instructions.Add(createLdarg(ctorOfItfBridgeType.Body.GetILProcessor(), 6)); var callBaseCtor = Instruction.Create(OpCodes.Call, anonymousStoreyCtorRef); instructions.Add(callBaseCtor); - instructions.Add(Instruction.Create(OpCodes.Ldarg_0)); - instructions.Add(Instruction.Create(OpCodes.Ldarg_3)); - instructions.Add(Instruction.Create(OpCodes.Stfld, virualMachineFieldOfBridge)); - for (int i = 0; i < methodIdFields.Count; i++) { instructions.Add(Instruction.Create(OpCodes.Ldarg_0)); - instructions.Add(Instruction.Create(OpCodes.Ldarg_2)); + instructions.Add(createLdarg(ctorOfItfBridgeType.Body.GetILProcessor(), 5)); emitLdcI4(instructions, i); instructions.Add(Instruction.Create(OpCodes.Ldelem_I4)); instructions.Add(Instruction.Create(OpCodes.Stfld, methodIdFields[i])); @@ -3047,7 +3607,7 @@ void postProcessInterfaceBridge() var insertPoint = callBaseCtor.Next; var processor = ctorOfItfBridgeType.Body.GetILProcessor(); - processor.InsertBefore(insertPoint, Instruction.Create(OpCodes.Ldarg_2)); + processor.InsertBefore(insertPoint, createLdarg(ctorOfItfBridgeType.Body.GetILProcessor(), 5)); processor.InsertBefore(insertPoint, Instruction.Create(OpCodes.Ldlen)); processor.InsertBefore(insertPoint, Instruction.Create(OpCodes.Conv_I4)); processor.InsertBefore(insertPoint, createLdcI4(methodIdFields.Count)); @@ -3060,7 +3620,7 @@ void postProcessInterfaceBridge() itfBridgeType.Methods.Add(ctorOfItfBridgeType); - //在WrappersManagerImpl增加创建接口 + //鍦╓rappersManagerImpl澧炲姞鍒涘缓鎺ュ彛 var createBridge = new MethodDefinition("CreateBridge", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot @@ -3068,6 +3628,12 @@ void postProcessInterfaceBridge() | MethodAttributes.Final, anonymousStoreyTypeRef); createBridge.Parameters.Add(new ParameterDefinition("fieldNum", Mono.Cecil.ParameterAttributes.None, assembly.MainModule.TypeSystem.Int32)); + createBridge.Parameters.Add(new ParameterDefinition("fieldTypes", Mono.Cecil.ParameterAttributes.None, + new ArrayType(assembly.MainModule.TypeSystem.Int32))); + createBridge.Parameters.Add(new ParameterDefinition("typeIndex", Mono.Cecil.ParameterAttributes.None, + assembly.MainModule.TypeSystem.Int32)); + createBridge.Parameters.Add(new ParameterDefinition("vTable", Mono.Cecil.ParameterAttributes.None, + new ArrayType(assembly.MainModule.TypeSystem.Int32))); createBridge.Parameters.Add(new ParameterDefinition("slots", Mono.Cecil.ParameterAttributes.None, new ArrayType(assembly.MainModule.TypeSystem.Int32))); createBridge.Parameters.Add(new ParameterDefinition("virtualMachine", Mono.Cecil.ParameterAttributes.None, @@ -3076,6 +3642,9 @@ void postProcessInterfaceBridge() instructions.Add(Instruction.Create(OpCodes.Ldarg_1)); instructions.Add(Instruction.Create(OpCodes.Ldarg_2)); instructions.Add(Instruction.Create(OpCodes.Ldarg_3)); + instructions.Add(createLdarg(createBridge.Body.GetILProcessor(), 4)); + instructions.Add(createLdarg(createBridge.Body.GetILProcessor(), 5)); + instructions.Add(createLdarg(createBridge.Body.GetILProcessor(), 6)); instructions.Add(Instruction.Create(OpCodes.Newobj, ctorOfItfBridgeType)); instructions.Add(Instruction.Create(OpCodes.Ret)); @@ -3107,7 +3676,7 @@ from instruction in method.Body.Instructions } else { - getWrapperMethod(wrapperType, anonObjOfWrapper, invoke, true, true); + getWrapperMethod(wrapperType, anonObjOfWrapper, invoke.TryImport(t.Module), true, true); } } else if (td.IsInterface) @@ -3127,9 +3696,13 @@ void writeMethod(BinaryWriter writer, MethodReference method) writer.Write(method.Name); var typeArgs = ((GenericInstanceMethod)method).GenericArguments; writer.Write(typeArgs.Count); - foreach (var typeArg in typeArgs) + for (int typeArg = 0;typeArg < typeArgs.Count;typeArg++) { - writer.Write(externTypeToId[typeArg]); + if (isCompilerGenerated(typeArgs[typeArg])) + { + typeArgs[typeArg] = itfBridgeType; + } + writer.Write(externTypeToId[typeArgs[typeArg]]); } writer.Write(method.Parameters.Count); //var genericParameters = ((GenericInstanceMethod)externMethod).ElementMethod.GenericParameters; @@ -3214,6 +3787,10 @@ void writeMethod(BinaryWriter writer, MethodReference method) { paramType = (paramType as GenericParameter).ResolveGenericArgument(method.DeclaringType); } + if (paramType.IsRequiredModifier) + { + paramType = (paramType as RequiredModifierType).ElementType; + } if (!externTypeToId.ContainsKey(paramType)) { throw new Exception("externTypeToId do not exist key: " + paramType @@ -3270,7 +3847,7 @@ public void Serialize(string filename) } } - //TODO: 预分析,生成link.xml之类的文件 + //TODO: 棰勫垎鏋愶紝鐢熸垚link.xml涔嬬被鐨勬枃浠 public void Serialize(Stream output) { using (BinaryWriter writer = new BinaryWriter(output)) @@ -3278,6 +3855,27 @@ public void Serialize(Stream output) writer.Write(IFix.Core.Instruction.INSTRUCTION_FORMAT_MAGIC); writer.Write(itfBridgeType.GetAssemblyQualifiedName()); + // add field type + for (int i = 0; i < fields.Count; i++) + { + var fieldType = fields[i].FieldType; + if (fieldType.IsGenericParameter) + { + var resolveType = ((GenericParameter)fieldType).ResolveGenericArgument(fields[i].DeclaringType); + if (resolveType != null) + { + addExternType(resolveType); + continue; + } + } + + if (isCompilerGenerated(fieldType) || isNewClass(fieldType as TypeDefinition)) + { + fieldType = objType; + } + addExternType(fieldType); + } + //---------------extern type--------------- writer.Write(externTypes.Count); for (int i = 0; i < externTypes.Count; i++) @@ -3343,20 +3941,40 @@ public void Serialize(Stream output) writer.Write(fields.Count); for (int i = 0; i < fields.Count; i++) { + bool newField = isNewField(fields[i] as FieldDefinition); + writer.Write(newField); writer.Write(addExternType(fields[i].DeclaringType)); writer.Write(fields[i].Name); + + if(newField) + { + var fieldType = fields[i].FieldType; + if (isCompilerGenerated(fieldType) || isNewClass(fieldType as TypeDefinition)) + { + fieldType = objType; + } + writer.Write(addExternType(fieldType)); + if(newFieldToCtor.ContainsKey(fields[i] as FieldDefinition)) + { + writer.Write(newFieldToCtor[fields[i] as FieldDefinition]); + } + else + { + writer.Write(-1); + } + } } writer.Write(fieldsStoreInVirtualMachine.Count); for (int i = 0; i < fieldsStoreInVirtualMachine.Count; i++) { var fieldType = fieldsStoreInVirtualMachine[i].FieldType; - if (isCompilerGenerated(fieldType)) + if (isCompilerGenerated(fieldType) || isNewClass(fieldType as TypeDefinition)) { fieldType = objType; } writer.Write(addExternType(fieldType)); - //字段静态构造函数 + //瀛楁闈欐佹瀯閫犲嚱鏁 writer.Write(typeToCctor[fieldsStoreInVirtualMachine[i].DeclaringType]); } @@ -3366,14 +3984,79 @@ public void Serialize(Stream output) { //Console.WriteLine("anonymous type: " + anonymousTypeInfos[i]); var anonymousType = anonymousTypeInfos[i].DeclaringType as TypeDefinition; - writer.Write(anonymousType.Fields.Count); + List anonymousTypeFields = new List(); + if (isNewClass(anonymousTypeInfos[i].DeclaringType as TypeDefinition)) + { + var temp = anonymousType; + while (temp != null && isNewClass(temp as TypeDefinition)) + { + if (temp.Fields != null) + { + foreach (var fi in temp.Fields) + { + anonymousTypeFields.Add(fi); + } + } + temp = temp.BaseType as TypeDefinition; + } + } + else + { + anonymousTypeFields.AddRange(anonymousTypeInfos[i].DeclaringType.Fields); + } + writer.Write(anonymousTypeFields.Count); + + for (int field = 0; field < anonymousTypeFields.Count; field++) + { + if (anonymousTypeFields[field].FieldType.IsPrimitive) + { + writer.Write(0); + } + else if (anonymousTypeFields[field].FieldType.IsValueType) + { + writer.Write(externTypeToId[anonymousTypeFields[field].FieldType] + 1); + } + else + { + writer.Write(-2); + } + } writer.Write(methodToId[anonymousTypeInfos[i]]); writer.Write(anonymousTypeInfos[i].Parameters.Count); writeSlotInfo(writer, anonymousType); + List vT = getVirtualMethodForType(anonymousType); + writer.Write(vT.Count); + int[] vTables = new int[vT.Count]; + for (int s = 0; s < vTables.Length; s++) + { + vTables[s] = -1; + } + writeVTable(anonymousType,vTables,vT); + + for (int k = 0; k < vTables.Length; k++) + { + writer.Write(vTables[k]); + } } writer.Write(wrapperMgrImpl.GetAssemblyQualifiedName()); - writer.Write(idMapType.GetAssemblyQualifiedName()); + + TypeDefinition idMap0 = null; + if (idMapList.Count == 0) + { + if (idMapType == null) + { + idMapTypeCheck(); + } + idMap0 = idMapType; + idMapType = null; + } + else + { + idMap0 = idMapList[0]; + } + var idMap0Name = idMap0.GetAssemblyQualifiedName(); + writer.Write(idMap0Name.Substring("IFix.IDMAP0".Length)); writer.Write(interpretMethods.Count); //Console.WriteLine("interpretMethods.Count:" + interpretMethods.Count); @@ -3384,7 +4067,18 @@ public void Serialize(Stream output) writeMethod(writer, kv.Key); writer.Write(kv.Value); } - } + var newClassTypes = (from type in assembly.GetAllType() + where type.Namespace != "IFix" && !type.IsGeneric() && isNewClass(type) + select type); + + var newClassList = newClassTypes.ToList(); + writer.Write(newClassList.Count); + foreach (var n in newClassList) + { + var str = n.GetAssemblyQualifiedName(); + writer.Write(str); + } + } //var allTypes = (from module in assembly.Modules // from type in module.Types @@ -3400,6 +4094,75 @@ public void Serialize(Stream output) // Console.WriteLine("hgp:" + method + ",type:" + method.GetType()); //} } + Dictionary> InternalTypeToVirtualMethods = new Dictionary>(); + public List getVirtualMethodForType(TypeDefinition type) + { + List virtualMethods; + if (InternalTypeToVirtualMethods.TryGetValue(type, out virtualMethods)) + { + return virtualMethods; + } + + if (type.BaseType != null && isNewClass(type.BaseType as TypeDefinition)) + { + virtualMethods = new List(getVirtualMethodForType(type.BaseType as TypeDefinition)); + } + else + { + virtualMethods = new List(ObjectVirtualMethodDefinitionList); + } + int index = virtualMethods.Count; + foreach (var method in type.Methods) + { + if (method.IsVirtual && method.IsNewSlot) + { + virtualMethods.Add(method); + } + } + + InternalTypeToVirtualMethods.Add(type, virtualMethods); + foreach (var vmethod in virtualMethods) + { + if (!virtualMethodToIndex.ContainsKey(vmethod)) + { + virtualMethodToIndex.Add(vmethod, index++); + } + } + return virtualMethods; + } + + public int virtualMethodInVTableIndex(MethodDefinition method) + { + var list = getVirtualMethodForType(method.DeclaringType); + var baseMethod = findInitDefineVirtualMethod(method.DeclaringType as TypeDefinition, method); + return list.FindIndex(li => li == baseMethod || li == method); + } + + public void writeVTable(TypeDefinition type,int[] vTables,List vT) + { + if (type.BaseType != null && isNewClass(type.BaseType as TypeDefinition)) + { + writeVTable(type.BaseType as TypeDefinition, vTables,vT); + } + foreach (var an in (from method in type.Methods + where method.IsVirtual + where !method.IsAbstract + select method)) + { + int index = 0; + if (!virtualMethodToIndex.TryGetValue(an,out index)) + { + index = virtualMethodInVTableIndex(an); + } + if (!methodToId.ContainsKey(an)) + { + vTables[index] = getMethodId(an, null, false).Id; + } + else + vTables[index] = methodToId[an]; + } + + } } } \ No newline at end of file diff --git a/Source/VSProj/Src/Tools/GenerateConfigure.cs b/Source/VSProj/Src/Tools/GenerateConfigure.cs index 91cc013..3fe8915 100644 --- a/Source/VSProj/Src/Tools/GenerateConfigure.cs +++ b/Source/VSProj/Src/Tools/GenerateConfigure.cs @@ -1,247 +1,512 @@ -/* - * Tencent is pleased to support the open source community by making InjectFix available. - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. - * InjectFix is licensed under the MIT License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. - * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. - */ - -using System.Collections.Generic; -using System.IO; -using Mono.Cecil; -using Mono.Cecil.Cil; -using System; -using System.Linq; - -namespace IFix -{ - public abstract class GenerateConfigure - { - public static GenerateConfigure Empty() - { - return new EmptyGenerateConfigure(); - } - - //仅仅简单的从文件加载类名而已 - public static GenerateConfigure FromFile(string filename) - { - DefaultGenerateConfigure generateConfigure = new DefaultGenerateConfigure(); - - using (BinaryReader reader = new BinaryReader(File.Open(filename, FileMode.Open))) - { - int configureNum = reader.ReadInt32(); - for (int i = 0; i < configureNum; i++) - { - string configureName = reader.ReadString(); - Dictionary configure = new Dictionary(); - int cfgItemCount = reader.ReadInt32(); - for (int j = 0; j < cfgItemCount; j++) - { - string typeName = reader.ReadString(); - int flag = reader.ReadInt32(); - configure[typeName] = flag; - } - generateConfigure.configures[configureName] = configure; - } - } - - return generateConfigure; - } - - /// - /// 如果一个方法打了指定的标签,返回其配置的标志位 - /// - /// 标签 - /// 要查询的方法 - /// 输出参数,用户配置的标志位 - /// - public abstract bool TryGetConfigure(string tag, MethodReference method, out int flag); - - /// - /// 判断一个方法是否是新增方法 - /// - /// 要查询的方法 - /// - public abstract bool IsNewMethod(MethodReference method); - } - - //内部测试专用 - public class EmptyGenerateConfigure : GenerateConfigure - { - public override bool TryGetConfigure(string tag, MethodReference method, out int flag) - { - flag = 0; - return true; - } - - public override bool IsNewMethod(MethodReference method) - { - return false; - } - } - - //注入配置使用 - public class DefaultGenerateConfigure : GenerateConfigure - { - internal Dictionary> configures - = new Dictionary>(); - - public override bool TryGetConfigure(string tag, MethodReference method, out int flag) - { - Dictionary configure; - flag = 0; - return (configures.TryGetValue(tag, out configure) - && configure.TryGetValue(method.DeclaringType.FullName, out flag)); - } - - public override bool IsNewMethod(MethodReference method) - { - return false; - } - } - - //patch配置使用 - public class PatchGenerateConfigure : GenerateConfigure - { - public override bool TryGetConfigure(string tag, MethodReference method, out int flag) - { - flag = 0; - if (tag == "IFix.InterpretAttribute") - { - return redirectMethods.Contains(method); - } - else if (tag == "IFix.IFixAttribute") - { - return switchMethods.Contains(method); - } - return false; - } - - public override bool IsNewMethod(MethodReference method) - { - return newMethods.Contains(method); - } - - //暂时不支持redirect类型的方法 - HashSet redirectMethods = new HashSet(); - HashSet switchMethods = new HashSet(); - HashSet newMethods = new HashSet(); - - MethodDefinition findMatchMethod(Dictionary>> searchData, - MethodDefinition method) - { - Dictionary> methodsOfType; - List overloads; - if (searchData.TryGetValue(method.DeclaringType.FullName, out methodsOfType) - && methodsOfType.TryGetValue(method.Name, out overloads)) - { - foreach (var overload in overloads) - { - if (overload.IsTheSame(method)) - { - return overload; - } - } - } - return null; - } - //参数类型信息 - class ParameterMatchInfo - { - public bool IsOut; - public string ParameterType; - } - - //方法签名信息 - class MethodMatchInfo - { - public string Name; - public string ReturnType; - public ParameterMatchInfo[] Parameters; - } - - //判断一个方法是否能够在matchInfo里头能查询到 - bool isMatch(Dictionary matchInfo, MethodReference method) - { - MethodMatchInfo[] mmis; - if (matchInfo.TryGetValue(method.DeclaringType.FullName, out mmis)) - { - foreach(var mmi in mmis) - { - if (mmi.Name == method.Name && mmi.ReturnType == method.ReturnType.FullName - && mmi.Parameters.Length == method.Parameters.Count) - { - bool paramMatch = true; - for(int i = 0; i < mmi.Parameters.Length; i++) - { - if (mmi.Parameters[i].IsOut != method.Parameters[i].IsOut - || mmi.Parameters[i].ParameterType != method.Parameters[i].ParameterType.FullName) - { - paramMatch = false; - break; - } - } - if (paramMatch) return true; - } - } - } - return false; - } - - //读取方法信息,主要是方法的签名信息,名字+参数类型+返回值类型 - static Dictionary readMatchInfo(BinaryReader reader) - { - Dictionary matchInfo = new Dictionary(); - - int typeCount = reader.ReadInt32(); - for (int k = 0; k < typeCount; k++) - { - string typeName = reader.ReadString(); - int methodCount = reader.ReadInt32(); - MethodMatchInfo[] methodMatchInfos = new MethodMatchInfo[methodCount]; - for (int i = 0; i < methodCount; i++) - { - MethodMatchInfo mmi = new MethodMatchInfo(); - mmi.Name = reader.ReadString(); - mmi.ReturnType = reader.ReadString(); - int parameterCount = reader.ReadInt32(); - mmi.Parameters = new ParameterMatchInfo[parameterCount]; - for (int p = 0; p < parameterCount; p++) - { - mmi.Parameters[p] = new ParameterMatchInfo(); - mmi.Parameters[p].IsOut = reader.ReadBoolean(); - mmi.Parameters[p].ParameterType = reader.ReadString(); - } - methodMatchInfos[i] = mmi; - } - matchInfo[typeName] = methodMatchInfos; - } - - return matchInfo; - } - - //读取配置信息(要patch的方法列表,新增方法列表) - public PatchGenerateConfigure(AssemblyDefinition newAssembly, string cfgPath) - { - Dictionary patchMethodInfo = null; - Dictionary newMethodInfo = null; - - using (BinaryReader reader = new BinaryReader(File.Open(cfgPath, FileMode.Open))) - { - patchMethodInfo = readMatchInfo(reader); - newMethodInfo = readMatchInfo(reader); - } - - foreach (var method in (from type in newAssembly.GetAllType() from method in type.Methods select method )) - { - if (isMatch(patchMethodInfo, method)) - { - switchMethods.Add(method); - } - if (isMatch(newMethodInfo, method)) - { - newMethods.Add(method); - } - } - } - } +/* + * Tencent is pleased to support the open source community by making InjectFix available. + * Copyright (C) 2019 Tencent. All rights reserved. + * InjectFix is licensed under the MIT License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. + * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. + */ + +using System.Collections.Generic; +using System.IO; +using Mono.Cecil; +using Mono.Cecil.Cil; +using System; +using System.Linq; + +namespace IFix +{ + public abstract class GenerateConfigure + { + public static GenerateConfigure Empty() + { + return new EmptyGenerateConfigure(); + } + + //浠呬粎绠鍗曠殑浠庢枃浠跺姞杞界被鍚嶈屽凡 + public static GenerateConfigure FromFile(string filename) + { + DefaultGenerateConfigure generateConfigure = new DefaultGenerateConfigure(); + + using (BinaryReader reader = new BinaryReader(File.Open(filename, FileMode.Open))) + { + int configureNum = reader.ReadInt32(); + for (int i = 0; i < configureNum; i++) + { + string configureName = reader.ReadString(); + Dictionary configure = new Dictionary(); + int cfgItemCount = reader.ReadInt32(); + for (int j = 0; j < cfgItemCount; j++) + { + string typeName = reader.ReadString(); + int flag = reader.ReadInt32(); + configure[typeName] = flag; + } + generateConfigure.configures[configureName] = configure; + } + generateConfigure.blackListMethodInfo = readMatchInfo(reader); + } + + return generateConfigure; + } + + /// + /// 濡傛灉涓涓柟娉曟墦浜嗘寚瀹氱殑鏍囩锛岃繑鍥炲叾閰嶇疆鐨勬爣蹇椾綅 + /// + /// 鏍囩 + /// 瑕佹煡璇㈢殑鏂规硶 + /// 杈撳嚭鍙傛暟锛岀敤鎴烽厤缃殑鏍囧織浣 + /// + public abstract bool TryGetConfigure(string tag, MethodReference method, out int flag); + + /// + /// 鍒ゆ柇涓涓柟娉曟槸鍚︽槸鏂板鏂规硶 + /// + /// 瑕佹煡璇㈢殑鏂规硶 + /// + public abstract bool IsNewMethod(MethodReference method); + + public abstract bool IsNewClass(TypeReference type); + + public abstract bool isNewField(FieldReference field); + + public abstract void AddNewMethod(MethodReference method); + + public abstract void AddNewClass(TypeReference type); + + public abstract void AddNewField(FieldReference field); + + //鍙傛暟绫诲瀷淇℃伅 + internal class ParameterMatchInfo + { + public bool IsOut; + public string ParameterType; + } + + //鏂规硶绛惧悕淇℃伅 + internal class MethodMatchInfo + { + public string Name; + public string ReturnType; + public ParameterMatchInfo[] Parameters; + } + + internal class FieldMatchInfo + { + public string Name; + public string FieldType; + } + + internal class PropertyMatchInfo + { + public string Name; + public string PropertyType; + } + + //鍒ゆ柇涓涓柟娉曟槸鍚﹁兘澶熷湪matchInfo閲屽ご鑳芥煡璇㈠埌 + internal static bool isMatch(Dictionary matchInfo, MethodReference method) + { + MethodMatchInfo[] mmis; + if (matchInfo.TryGetValue(method.DeclaringType.FullName, out mmis)) + { + foreach (var mmi in mmis) + { + if (mmi.Name == method.Name && mmi.ReturnType == method.ReturnType.FullName + && mmi.Parameters.Length == method.Parameters.Count) + { + bool paramMatch = true; + for (int i = 0; i < mmi.Parameters.Length; i++) + { + var paramType = method.Parameters[i].ParameterType; + if (paramType.IsRequiredModifier) + { + paramType = (paramType as RequiredModifierType).ElementType; + } + if (mmi.Parameters[i].IsOut != method.Parameters[i].IsOut + || mmi.Parameters[i].ParameterType != paramType.FullName) + { + paramMatch = false; + break; + } + } + if (paramMatch) return true; + } + } + } + return false; + } + + internal static bool isMatchForField(Dictionary matchInfo, FieldReference field) + { + FieldMatchInfo[] mmis; + if (matchInfo.TryGetValue(field.DeclaringType.FullName, out mmis)) + { + foreach (var mmi in mmis) + { + if (mmi.Name == field.Name && mmi.FieldType == field.FieldType.FullName) + { + return true; + } + } + } + return false; + } + + internal static bool isMatchForProperty(Dictionary matchInfo, PropertyReference property) + { + PropertyMatchInfo[] mmis; + if (matchInfo.TryGetValue(property.DeclaringType.FullName, out mmis)) + { + foreach (var mmi in mmis) + { + if (mmi.Name == property.Name && mmi.PropertyType == property.PropertyType.FullName) + { + return true; + } + } + } + return false; + } + + internal static bool isMatchForClass(HashSet matchInfo, TypeReference type) + { + if (matchInfo.Contains(type.ToString())) + { + return true; + } + return false; + } + + //璇诲彇鏂规硶淇℃伅锛屼富瑕佹槸鏂规硶鐨勭鍚嶄俊鎭紝鍚嶅瓧+鍙傛暟绫诲瀷+杩斿洖鍊肩被鍨 + internal static Dictionary readMatchInfo(BinaryReader reader) + { + Dictionary matchInfo = new Dictionary(); + + int typeCount = reader.ReadInt32(); + for (int k = 0; k < typeCount; k++) + { + string typeName = reader.ReadString(); + int methodCount = reader.ReadInt32(); + MethodMatchInfo[] methodMatchInfos = new MethodMatchInfo[methodCount]; + for (int i = 0; i < methodCount; i++) + { + MethodMatchInfo mmi = new MethodMatchInfo(); + mmi.Name = reader.ReadString(); + mmi.ReturnType = reader.ReadString(); + int parameterCount = reader.ReadInt32(); + mmi.Parameters = new ParameterMatchInfo[parameterCount]; + for (int p = 0; p < parameterCount; p++) + { + mmi.Parameters[p] = new ParameterMatchInfo(); + mmi.Parameters[p].IsOut = reader.ReadBoolean(); + mmi.Parameters[p].ParameterType = reader.ReadString(); + } + methodMatchInfos[i] = mmi; + } + matchInfo[typeName] = methodMatchInfos; + } + + return matchInfo; + } + + internal static Dictionary readFieldInfo(BinaryReader reader) + { + Dictionary matchInfo = new Dictionary(); + + int typeCount = reader.ReadInt32(); + for (int k = 0; k < typeCount; k++) + { + string typeName = reader.ReadString(); + int methodCount = reader.ReadInt32(); + FieldMatchInfo[] fieldMatchInfos = new FieldMatchInfo[methodCount]; + for (int i = 0; i < methodCount; i++) + { + FieldMatchInfo fmi = new FieldMatchInfo(); + fmi.Name = reader.ReadString(); + fmi.FieldType = reader.ReadString(); + fieldMatchInfos[i] = fmi; + } + matchInfo[typeName] = fieldMatchInfos; + } + + return matchInfo; + } + + internal static Dictionary readPropertyInfo(BinaryReader reader) + { + Dictionary matchInfo = new Dictionary(); + + int typeCount = reader.ReadInt32(); + for (int k = 0; k < typeCount; k++) + { + string typeName = reader.ReadString(); + int methodCount = reader.ReadInt32(); + PropertyMatchInfo[] propertyMatchInfos = new PropertyMatchInfo[methodCount]; + for (int i = 0; i < methodCount; i++) + { + PropertyMatchInfo pmi = new PropertyMatchInfo(); + pmi.Name = reader.ReadString(); + pmi.PropertyType = reader.ReadString(); + propertyMatchInfos[i] = pmi; + } + matchInfo[typeName] = propertyMatchInfos; + } + + return matchInfo; + } + + internal static HashSet readMatchInfoForClass(BinaryReader reader) + { + HashSet setMatchInfoForClass = new HashSet(); + int typeCount = reader.ReadInt32(); + for (int k = 0; k < typeCount; k++) + { + string className = reader.ReadString(); + setMatchInfoForClass.Add(className); + } + return setMatchInfoForClass; + } + } + + //鍐呴儴娴嬭瘯涓撶敤 + public class EmptyGenerateConfigure : GenerateConfigure + { + public override bool TryGetConfigure(string tag, MethodReference method, out int flag) + { + flag = 0; + return true; + } + + public override bool IsNewMethod(MethodReference method) + { + return false; + } + public override bool IsNewClass(TypeReference type) + { + return false; + } + + public override bool isNewField(FieldReference field) + { + return false; + } + + public override void AddNewMethod(MethodReference method) + { + + } + + public override void AddNewClass(TypeReference type) + { + + } + + public override void AddNewField(FieldReference field) + { + + } + } + + //娉ㄥ叆閰嶇疆浣跨敤 + public class DefaultGenerateConfigure : GenerateConfigure + { + internal Dictionary> configures + = new Dictionary>(); + + internal Dictionary blackListMethodInfo = null; + + public override bool TryGetConfigure(string tag, MethodReference method, out int flag) + { + Dictionary configure; + flag = 0; + if(tag == "IFix.IFixAttribute" && blackListMethodInfo != null) + { + if(isMatch(blackListMethodInfo, method)) + { + return false; + } + } + return (configures.TryGetValue(tag, out configure) + && configure.TryGetValue(method.DeclaringType.FullName, out flag)); + } + + public override bool IsNewMethod(MethodReference method) + { + return false; + } + public override bool IsNewClass(TypeReference type) + { + return false; + } + public override bool isNewField(FieldReference field) + { + return false; + } + public override void AddNewMethod(MethodReference method) + { + + } + public override void AddNewClass(TypeReference type) + { + + } + public override void AddNewField(FieldReference field) + { + + } + } + + //patch閰嶇疆浣跨敤 + public class PatchGenerateConfigure : GenerateConfigure + { + public override bool TryGetConfigure(string tag, MethodReference method, out int flag) + { + flag = 0; + if (tag == "IFix.InterpretAttribute") + { + return redirectMethods.Contains(method); + } + else if (tag == "IFix.IFixAttribute") + { + return switchMethods.Contains(method); + } + return false; + } + + public override bool IsNewMethod(MethodReference method) + { + return newMethods.Contains(method); + } + + public override bool IsNewClass(TypeReference type) + { + return newClasses.Contains(type); + } + + public override bool isNewField(FieldReference field) + { + return newFields.Contains(field); + } + + public override void AddNewMethod(MethodReference method) + { + newMethods.Add(method); + } + + public override void AddNewClass(TypeReference type) + { + newClasses.Add(type); + } + + public override void AddNewField(FieldReference field) + { + newFields.Add(field); + } + + //鏆傛椂涓嶆敮鎸乺edirect绫诲瀷鐨勬柟娉 + HashSet redirectMethods = new HashSet(); + HashSet switchMethods = new HashSet(); + HashSet newMethods = new HashSet(); + HashSet newClasses = new HashSet(); + HashSet newFields = new HashSet(); + MethodDefinition findMatchMethod(Dictionary>> searchData, + MethodDefinition method) + { + Dictionary> methodsOfType; + List overloads; + if (searchData.TryGetValue(method.DeclaringType.FullName, out methodsOfType) + && methodsOfType.TryGetValue(method.Name, out overloads)) + { + foreach (var overload in overloads) + { + if (overload.IsTheSame(method)) + { + return overload; + } + } + } + return null; + } + + private static bool isCompilerGenerated(FieldReference field) + { + var fd = field as FieldDefinition; + return fd != null && fd.CustomAttributes.Any(ca => ca.AttributeType.FullName + == "System.Runtime.CompilerServices.CompilerGeneratedAttribute"); + } + + //璇诲彇閰嶇疆淇℃伅锛堣patch鐨勬柟娉曞垪琛紝鏂板鏂规硶鍒楄〃锛 + public PatchGenerateConfigure(AssemblyDefinition newAssembly, string cfgPath) + { + Dictionary patchMethodInfo = null; + Dictionary newMethodInfo = null; + Dictionary newFieldsInfo = null; + Dictionary newPropertiesInfo = null; + HashSet newClassInfo = null; + + using (BinaryReader reader = new BinaryReader(File.Open(cfgPath, FileMode.Open))) + { + patchMethodInfo = readMatchInfo(reader); + newMethodInfo = readMatchInfo(reader); + newFieldsInfo = readFieldInfo(reader); + newPropertiesInfo = readPropertyInfo(reader); + newClassInfo = readMatchInfoForClass(reader); + } + + foreach (var method in (from type in newAssembly.GetAllType() from method in type.Methods select method )) + { + if (isMatch(patchMethodInfo, method)) + { + switchMethods.Add(method); + } + if (isMatch(newMethodInfo, method)) + { + AddNewMethod(method); + } + } + foreach (var clas in newAssembly.GetAllType()) + { + if (isMatchForClass(newClassInfo, clas)) + { + AddNewClass(clas); + } + } + foreach (var property in (from type in newAssembly.GetAllType() from property in type.Properties select property)) + { + if (isMatchForProperty(newPropertiesInfo, property)) + { + AddNewMethod(property.SetMethod); + AddNewMethod(property.GetMethod); + + var methods = new List{property.GetMethod, property.SetMethod}; + + var defination = newAssembly.MainModule.GetType(property.DeclaringType.FullName); + foreach (var field in ( from method in methods + where method != null && method.IsSpecialName && method.Body != null + && method.Body.Instructions != null + from instruction in method.Body.Instructions + where instruction.OpCode.Code == Mono.Cecil.Cil.Code.Ldsfld + || instruction.OpCode.Code == Mono.Cecil.Cil.Code.Stsfld + || instruction.OpCode.Code == Mono.Cecil.Cil.Code.Ldsflda + || instruction.OpCode.Code == Mono.Cecil.Cil.Code.Ldfld + || instruction.OpCode.Code == Mono.Cecil.Cil.Code.Stfld + || instruction.OpCode.Code == Mono.Cecil.Cil.Code.Ldflda + where isCompilerGenerated(instruction.Operand as Mono.Cecil.FieldReference) + select (instruction.Operand as Mono.Cecil.FieldReference).Resolve()).Distinct()) + { + var backingField = property.DeclaringType.Fields.FirstOrDefault(f => f.FullName == field.FullName); + if(backingField != null) + { + AddNewField(backingField); + } + } + } + } + foreach (var field in (from type in newAssembly.GetAllType() from field in type.Fields select field )) + { + if (isMatchForField(newFieldsInfo, field)) + { + AddNewField(field); + } + } + } + } } \ No newline at end of file diff --git a/Source/VSProj/Src/UnitTest/VirtualMachineTest.cs b/Source/VSProj/Src/UnitTest/VirtualMachineTest.cs index 4f44926..4a5ebf5 100644 --- a/Source/VSProj/Src/UnitTest/VirtualMachineTest.cs +++ b/Source/VSProj/Src/UnitTest/VirtualMachineTest.cs @@ -1,6 +1,6 @@ /* * Tencent is pleased to support the open source community by making InjectFix available. - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2019 Tencent. All rights reserved. * InjectFix is licensed under the MIT License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. */ diff --git a/Source/VSProj/Src/Version.cs b/Source/VSProj/Src/Version.cs index 990518b..fa127ec 100644 --- a/Source/VSProj/Src/Version.cs +++ b/Source/VSProj/Src/Version.cs @@ -1,6 +1,6 @@ /* * Tencent is pleased to support the open source community by making InjectFix available. - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2019 Tencent. All rights reserved. * InjectFix is licensed under the MIT License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms. * This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package. */ @@ -9,6 +9,6 @@ namespace IFix { public class Version { - public const string FILE_FORMAT = "0.0.6";//文件格式版本号 + public const string FILE_FORMAT = "0.0.6";//鏂囦欢鏍煎紡鐗堟湰鍙 } } \ No newline at end of file diff --git a/Source/VSProj/build_for_unity.bat b/Source/VSProj/build_for_unity.bat index 5049dde..d0ee967 100644 --- a/Source/VSProj/build_for_unity.bat +++ b/Source/VSProj/build_for_unity.bat @@ -1,5 +1,9 @@ @set UNITY_HOME=D:\Program Files\Unity523f1 -@set GMCS="%UNITY_HOME%\Editor\Data\Mono\bin\gmcs" +if exist "%UNITY_HOME%\Editor\Data\Mono\bin\gmcs" ( + @set GMCS="%UNITY_HOME%\Editor\Data\Mono\bin\gmcs" +) else ( + @set GMCS="%UNITY_HOME%\Editor\Data\MonoBleedingEdge\bin\mcs.bat" +) @set MONO="%UNITY_HOME%\Editor\Data\MonoBleedingEdge\bin\mono" @set DLL_OUTPUT=..\UnityProj\Assets\Plugins\IFix.Core.dll @set TOOL_KIT_PATH=..\UnityProj\IFixToolKit diff --git a/Source/VSProj/build_for_unity.sh b/Source/VSProj/build_for_unity.sh index bed5f41..1938cfb 100755 --- a/Source/VSProj/build_for_unity.sh +++ b/Source/VSProj/build_for_unity.sh @@ -1,6 +1,9 @@ #!/usr/bin/env sh UNITY_HOME="/Applications/Unity2017/Unity.app" GMCS="$UNITY_HOME/Contents/Mono/bin/gmcs" +if [ ! -d $GMCS ]; then + GMCS="$UNITY_HOME/Contents/MonoBleedingEdge/bin/mcs" +fi MONO="$UNITY_HOME/Contents/MonoBleedingEdge/bin/mono" DLL_OUTPUT="../UnityProj/Assets/Plugins/IFix.Core.dll" TOOL_KIT_PATH="../UnityProj/IFixToolKit"