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或者Patch for ios时,报“"please put template file for android/ios in IFixToolKit directory!” -������������Ҫ����һ������ģ���ļ��ŵ�IFixToolKitĿ¼�� +解决这个错误需要制作一个编译模版文件放到IFixToolKit目录: -* ��������������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��װĿ¼�޸ġ� +* 假如你制作的是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安装目录修改。 -## 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执行过注入,不能对注入过的dll生成patch,在Unit选工程根目录,右键选reimport -## ���������������������δ��� +## 补丁制作的条件编译宏如何处理 -�����Unity2018.3�汾�����ϣ�����Unity������C#����ӿڣ�����InjectFix��Unity2018.3�汾ֱ��֧��Android��iOS�IJ������ɣ�ֱ��ִ�ж�Ӧ�˵����ɡ� +如果是Unity2018.3版本及以上,由于Unity开放了C#编译接口,所以InjectFix在Unity2018.3版本直接支持Android和iOS的补丁生成,直接执行对应菜单即可。 -���������Unity2018.3�汾����Ҫ�ñȽ��鷳�ķ�ʽ������Ӧƽ̨�ı��������Assembly-CSharp.dll���������Ȼ�����IFix.Editor.IFixEditor.GenPatchȥ���ɲ����� +但如果低于Unity2018.3版本,则要用比较麻烦的方式:按对应平台的编译参数把Assembly-CSharp.dll编译出来,然后调用IFix.Editor.IFixEditor.GenPatch去生成补丁。 -Unity�������ڹ��̵�TempĿ¼�½�һ���ļ����������в����ŵ��Ǹ��ļ���Ȼ��ִ�����ƣ�Ŀ¼�����Լ���unity��װ�������������������б��룺 +Unity编译是在工程的Temp目录新建一个文件,把命令行参数放到那个文件,然后执行类似(目录根据自己的unity安装情况而定)如下命令进行编译: ~~~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] + +##### 用途 + +​ 在补丁阶段使用;原生代码修复。如果发现某个函数有错误,就可以使用该标签给函数打补丁,打上这个标签的函数,童鞋们就可以随意修改该函数。 + +##### 用法 + +​ 该标签只能用在方法上,直接在要修改的函数上面标注一下这个标签即可。 + +##### 举例 + +​ 这个函数本来的意思是两个值相加,但现在写错了,所以可以给该函数打上[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] + +##### 用途 + +​ 在补丁阶段使用;新增代码。在补丁阶段,童鞋们还有新的需求,想新增个字段,函数或者类,可以用[IFix.Interpret]标签实现。 + +##### 用法 + +​ 该标签可以用在字段,属性,方法,类型上,直接在要新增的代码上面标注一下这个标签即可。 + +##### 举例 + +​ 新增一个字段 + +```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] + +##### 用途 + +​ 在注入阶段使用; 把一个虚拟机的类适配到原生interface或者把一个虚拟机的函数适配到原生delegate。 + +​ 什么时候需要用到呢? + +- 修复代码赋值一个闭包到一个delegate变量; +- 修复代码的Unity协程用了yield return; +- 新增一个函数,赋值到一个delegate变量; +- 新增一个类,赋值到一个原生interface变量; +- 新增函数,用了yield return; + +##### 用法 + +​ 该标签只能用在类上,在童鞋们程序的某个地方,写上一个静态类,里面有一个静态字段,值就是interface和delegate的类型集合 + + !!注意,该配置类不能放到Editor目录,且不能内嵌到另外一个类里头。 + +##### 举例 + +​ 新增一个类,该类实现了一个接口 + +```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]的Unity协程),用到了 yield return + +```c# +[IFix.Interpret] +public IEnumerator TestInterface() +{ + yield return new WaitForSeconds(1); + UnityEngine.Debug.Log("wait one second"); +} +``` + +​ 新增函数(或者修复代码[IFix.Patch]),赋值到一个delegate变量 + +```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] + +##### 用途 + +​ 在注入阶段使用;配置类,里面存储的是一些注入时需要注入或过滤的东西。 + +##### 用法 + +​ 该标签只能用在类上,该类必须在Editor文件夹下 。 + +##### 举例 + +```c# +[Configure] +public class TestCfg +{ + +} +``` + +### [IFix] + +##### 用途 + +​ 在注入阶段使用;用来存储所有你认为将来可能会需要修复的类的集合。该标签和[IFix.Patch]有关联,因为如果发现某个函数需要修复,直接打上[IFix.Patch]标签就可以了,但是前提是,这个需要修复的函数的类必须在[IFix]下。 + +##### 用法 + +​ 该标签只能用在属性上,Configure类中的一个静态属性,get得到的是可能会需要修复的函数所有类的集合 + +##### 举例 + +​ 认为Test类里面的函数可能会出错,所以把它们放到[IFix]标签下,当Test类中的Add函数需要修复,直接打标签修改即可。 + +```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]标签下的属性里面的值,都会被注入适配代码,但是如果不想对某个函数进行注入,可以用该标签进行过滤。 + +##### 用法 + +​ 该标签只能用在方法上,Configure类中的一个静态方法。 + +##### 举例 + +​ 觉得Test类里的函数可能会需要修复,但是Test类里面的Div和Mult不可能有问题,可以把这两个函数过滤掉。 + +```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"); + } +} + +``` + + + +### 注意事项 + +- 如果觉得某个类的函数可能会需要修复,那么一定要把该类放到Editor目录下[Configure]类的[IFix]静态字段里;然后才可以对某个函数进行[IFix.Patch]。 +- 涉及到interface和delegate,如果把一个虚拟机的类适配到原生interface或者把一个虚拟机的函数适配到原生delegate ,一定要放到[IFix.CustomBridge]类的静态字段里。 +- 打上[Configure]标签的类,必须放在Editor目录下。 +- [IFix],[Filter]这些标签必须放在打上[Configure]标签的类里。 +- 在[IFix.Patch]时,不支持修复泛型函数,不支持修复构造函数,不支持在原生类中新增字段。 +- 在[IFix.Interpret]时,不支持新增类继承原生类,不支持新增类是泛型类。 + + + + + +### 总结 + +| 标签 | 使用阶段 | 用途 | 用法 | +| :-----------------: | :------: | :------------------------: | :----------------------------------------------------------: | +| [IFix.Patch] | 补丁 | 修复函数 | 只能放在函数上 | +| [IFix.Interpret] | 补丁 | 新增字段,属性,函数,类型 | 放在字段,属性,函数,类型上 | +| [IFix.CustomBridge] | 注入 | interface和delegate桥接 | 只能放在单独写一个静态类上,存储虚拟机的类适配到原生interface或者虚拟机的函数适配到原生delegate,该类不能放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拷贝到Unity项目的Assets同级目录 * Assets/IFix,Assets/Plugins拷贝到Unity项目的Assets下 -## 快速入门 +## 文档 -[如何使用?](./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’s 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、android) + // 2、手机的ip地址 + // 3、端口信息,端口信息要和PatchReceiver配置对应上 public class LiveDotNet : EditorWindow { private int platformIndex = 0; @@ -41,8 +41,8 @@ void OnGUI() doPatch(); } - //1��IFixEditor.GenPlatformPatch���������ɲ����ļ� - //2�����͸��ֻ� + //1、IFixEditor.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 { - //Ĭ�ϵIJ����ļ������� + //默认的补丁文件保存名 const string PERSISTENT_FILE_NAME = "__LAST_RUN_SAVED_PATCH"; - //���ջ������Ĵ�С + //接收缓冲区的大小 const int BUFFER_SIZE = 1024; Stream patch = null; - //��Ҫ��LiveDotNet.cs�Ķ˿����� + //需要跟LiveDotNet.cs的端口配套 public int Port = 8080; - //�������Ϊtrue�Ļ��������ļ��ᱣ�浽�ļ�������Ӧ�ú���Ȼ��Ч + //这个设置为true的话,补丁文件会保存到文件,重启应用后仍然生效 public bool Persistent = false; Socket listener = null; string lastRunSavePath; - //1������ - //2��accpet�����Ӻ󣬽��ո������������� - //3����������Ϊ���������� + //1、监听 + //2、accpet到链接后,接收该链接所有数据 + //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ʱ�����ϴα���IJ����ļ� + //如果设置了持久化,在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; -//������ʾ�޸Ĵ��������ˢ�µ���� +//用来演示修改代码后,立即刷新到真机 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 { - //�޸ij���ȷ���߼��󣬴�����ע�ͣ����ɵIJ����������ú��� + //修改成正确的逻辑后,打开如下注释,生成的补丁将修正该函数 //[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在Pro Standard Assets下,会编译到Assembly-CSharp-firstpass.dll下,用来演示多dll的修复 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 @@ +using 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 @@ +using System; +using System.Collections.Generic; +using UnityEngine; +using IFix.Core; +using System.IO; + +//1、执行菜单“InjectFix/Fix”生成补丁; +//2、注释“NewBehaviourScript”,“SubSystem2”两个类,以及NewClassTest的Init函数里头new SubSystem2的那行语句; +//3、执行菜单“InjectFix/Inject”,模拟线上没有“NewBehaviourScript”,“SubSystem2”的版本; +//4、NewClassTest.cs拖到场景,运行看下效果,此时只加载SubSystem1; +//5、把生成的补丁拷贝到Resources下,再次运行看下效果; + +public interface IMonoBehaviour +{ + void Start();//简单demo,只定义了Start方法,实际Awake,Update,OnDestroy。。。都类似 + + 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 @@ +using 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会分析闭包,针对闭包对应的delegate生成适配器 +//但是有的情况是原来那个地方不是用闭包,修复时用了闭包,这时会报找不到适配器的错误。 +//这种问题可以通过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 + //虚拟机方法id int methodId; - //�󶨵��������� + //绑定的匿名对象 object anonObj; - //Ԥ���㣬�Ƿ�Ҫ��anonObj push�ı�־δ + //预计算,是否要把anonObj push的标志未 bool pushSelf; - //Ԥ���㣬�����anonObj����������Ҫ+1 + //预计算,如果有anonObj参数个数则要+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 + //如果有绑定的匿名对象,push 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: 需要用到jit么? } 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创建用Delegate.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的标志位,仍然通过传地址的方式进行方法调用, + //但这在反射调用行不通,参数是object类型,boxing到object就是null,所以会触发 + //“Non-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会Crash, + //Unity文档:https://docs.unity3d.com/Manual/Attributes.html + //相关issue链接:https://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�� + //未初始化的local引用可能作为out参数反射调用 + //TODO: 验证值类型out参数,对应参数位置是否可以是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://更新完毕,直接return { 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������������﷨�ǣ����Ž� + //可以手动指定要生成delegate(主要用于闭包)、interface(比如迭代器语法糖)的桥接 [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���� + /// 判断一个方法是否能赋值到一个delegate变量 /// - /// 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һ������ȡƥ���dz��� + //适配器的缓存,如果不做缓存,每次都调用IsAssignable一个个的取匹配会非常慢 static Dictionary delegateAdptCache = new Dictionary(); /// - /// ��һ��wrapper������ͷ�������ܹ����䵽�ض�delegate�ķ��� + /// 从一个wrapper对象里头,查找能够适配到特定delegate的方法 /// - /// 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,array元素是值类型,需要重新update回去 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; - //栈顶也是字段引用,而且该字段是值类型,需要update上层对象 - 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; + } + } + + //栈顶也是字段引用,而且该字段是值类型,需要update上层对象 + 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表示是ArrayReference @@ -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的操作数肯定是uint 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 { - //�ýӿ���ע�����Զ�ʵ�� + //该接口由注入器自动实现 public interface WrappersManager { - //����һ��delegate�����anon�ǿվ��DZհ� + //创建一个delegate,如果anon非空就是闭包 Delegate CreateDelegate(Type type, int id, object anon); - //����һ��interface�Ž��� - AnonymousStorey CreateBridge(int fieldNum, int[] slots, VirtualMachine virtualMachine); - //����һ��wrapper���󣨻��ɲ��������߼����ã����������wrapper���飩 + //创建一个interface桥接器 + AnonymousStorey CreateBridge(int fieldNum, int[] fieldTypes, int typeIndex, int[] vTable, int[] slots, VirtualMachine virtualMachine); + //创建一个wrapper对象(会由补丁加载逻辑调用,创建后放入wrapper数组) object CreateWrapper(int id); - //��ʼ��wrapper���� + //初始化wrapper数组 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方法,该方法逻辑如下,SimpleVirtualMachineBuilder通过硬编码指令获得 //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: 由于目前泛型函数不解析执行,所以泛型实参在泛型函数间传递不用考虑,但后续如果要支持泛型函数的解析执行的话,要加入这点的考虑 //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�IJ���������dz��� - //6��һ��finally block��ͷ��leaveָ������в�ͬ��Ŀ���ַ�����磺try{}catch{goto} - //7��leave�����һ��finally block�ڣ���Ŀ���ַ��finally��try block֮�⣬��ô���finally block����תǰִ�У� - // ����ж��������finally������ڵ���ִ�У����쳣��ʵҲ��������Ϊ������finally�� + //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) 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: 由于目前泛型函数不解析执行,所以泛型实参在泛型函数间传递不用考虑,但后续如果要支持泛型函数的解析执行的话,要加入这点的考虑 //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�IJ���������dz��� - //6��һ��finally block��ͷ��leaveָ������в�ͬ��Ŀ���ַ�����磺try{}catch{goto} - //7��leave�����һ��finally block�ڣ���Ŀ���ַ��finally��try block֮�⣬��ô���finally block����תǰִ�У� - // ����ж��������finally������ڵ���ִ�У����쳣��ʵҲ��������Ϊ������finally�� + //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) 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�»������ļ� + //清理符号读取器 + //如果不清理,在window下会锁定文件 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Ϊ�����ģ����ҷ��Ͳ�����Ӧ��ʵ�� + /// 以contextType为上下文,查找泛型参数对应的实参 /// - /// ���Ͳ��� - /// ���������� + /// 泛型参数 + /// 上下文类型 /// public static TypeReference ResolveGenericArgument(this GenericParameter gp, TypeReference contextType) { @@ -54,10 +54,10 @@ public static TypeReference ResolveGenericArgument(this GenericParameter gp, Typ } /// - /// ��contextMethodΪ�����ģ����ҷ��Ͳ�����Ӧ��ʵ�� + /// 以contextMethod为上下文,查找泛型参数对应的实参 /// - /// ���Ͳ��� - /// �����ĺ��� + /// 泛型参数 + /// 上下文函数 /// 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���� - /// �����ڲ������ҷ��ͷ���ʱ�������� + /// 填充泛型参数,如果直接TypeReference取FullName的话,泛型参数(比如名字为T)不会实例化(仍然为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 - /// ���������ͣ�������������� - /// ���Գ����� - /// ���ڷ������͵ĵݹ�ʱ�����Գ��򼯣���Ϊ������������д�귺�Ͳ���������д���� + /// 要获取AssemblyQualifiedName的type + /// 上下文类型,往往是其外层类 + /// 忽略程序集名 + /// 用于泛型类型的递归时,忽略程序集,因为泛型类型是填写完泛型参数后,再填写程序集 /// 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) } /// - /// �ж�һ�������Dz��Ƿ��� + /// 判断一个类型是不是泛型 /// - /// Ҫ�жϵ����� + /// 要判断的类型 /// 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) } /// - /// �ж�һ�������Dz��Ƿ��� + /// 判断一个方法是不是泛型 /// - /// Ҫ�жϵķ��� + /// 要判断的方法 /// public static bool IsGeneric(this MethodReference method) { @@ -366,9 +366,9 @@ public static bool IsGeneric(this MethodReference method) } /// - /// �ж�һ���ֶε������Dz��Ƿ��� + /// 判断一个字段的类型是不是泛型 /// - /// Ҫ�ж��ֶ� + /// 要判断字段 /// public static bool IsGeneric(this FieldReference field) { @@ -376,10 +376,10 @@ public static bool IsGeneric(this FieldReference field) } /// - /// �ж����������Dz���ͬһ�� + /// 判断两个类型是不是同一个 /// - /// ����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 + /// 要导入的类型 + /// 导入到哪个module /// // #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 + /// 判断一个名字是否是一个合法id /// /// /// @@ -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)//不是合法名字,就肯定是随机变量 + //如果是合法名字,但不被任何SpecialName方法引用,也归为随机变量 || !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通常是ldftn,要验证ldftn所加载的函数是否含非法指令(不支持,或者引用了个生成字段, + //或者一个生成NotPlainObject) 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)//闭包中调用一个泛型,在unity2018的.net 3.5设置下,编译器是先生成一个泛型的闭包实现,然后实例化,很奇怪的做法,老版本unity,新unity的.net 4.0设置都不会这样,先返回false,不支持这种编译器 + { + 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: 如果后续支持泛型解析,需要考虑这块的实现,xlua目前泛型直接不支持base调用 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)) + //如果是dll之外的方法,或者是构造函数,析构函数,作为虚拟机之外(extern)的方法 + 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���ķ��� + //包含不支持指令的方法,作为虚拟机之外(extern)的方法 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���ֲ�֧��ָ��Ӧ�ñ��� + // 打patch发现不支持指令应该报错 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: 如果生成代码做了delegate的cache怎么办呢? 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) } /// - /// �Ž���ʵ��һ��������нӿڣ�һ����˵�Ǹ������� + /// 桥接器实现一个类的所有接口,一般来说是个匿名类 /// - /// Ҫʵ���Žӵ������� + /// 要实现桥接的匿名类 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); + } + + /// - /// ��ȡһ�������������� + /// 获取一个方法的适配器 /// - /// �����������ķ����� - /// ���������󶨵��������� - /// Ҫ����ķ��� - /// �Dz��DZհ� - /// �Ƿ�����������������delegate���������Ͳ��������� - /// �Ƿ��ǽӿ��Ž��� - /// ����id + /// 方法适配器的放置类 + /// 适配器所绑定的匿名对象 + /// 要适配的方法 + /// 是不是闭包 + /// 是否向基类收敛(如果是delegate适配器,就不能收敛) + /// 是否是接口桥接器 + /// 方法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的话,引用类型,复杂非引用值类型,均转为object 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 + //不能直接栈上表示的值类型,都boxing 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、已转函数可以支持直接重定向并删除原实现(减包场景),以及保留原实现增加切换代码(修bug场景) + //4、已转函数需要生成wrap + //5、TODO:虚函数用base如何处理?私有函数是否需要保留入口?简单函数(比如getter/setter)是否要转? + //6、应该为基本值类型生成出入栈函数,防止过大GC + //7、Callvirt分析其是否真的是虚函数,虚函数反射调用,非虚并且动态方法直接调用 + //8、泛型等同多了一个Type[]参数 + //工具输入一个dll,输出dll+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 + //为getter setter增加对应的property 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���Ӵ����ӿ� + //在WrappersManagerImpl增加创建接口 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); + } + + //暂时不支持redirect类型的方法 + 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"