我有一些代码需要两个方法并注入一个(injectionMethod)来代替IL中的另一个(replacementMethod)。这用于操作被替换的方法的结果,以便在单元测试中给出injectMethod提供的结果。
public static void Inject<T>( Func<T> replacedMethod, Func<T> injectionMethod )
{
string replacedMethodName = replacedMethod.Method.Name;
Type replacedMethodType = replacedMethod.Method.ReflectedType;
string injectionMethodName = injectionMethod.Method.Name;
Type injectionMethodType = injectionMethod.Method.ReflectedType;
MethodInfo methodToReplace = replacedMethodType.GetMethod( replacedMethodName );
MethodInfo methodToInject = injectionMethodType.GetMethod( injectionMethodName );
RuntimeHelpers.PrepareMethod( methodToReplace.MethodHandle );
RuntimeHelpers.PrepareMethod( methodToInject.MethodHandle );
ReplacedMethod = methodToReplace;
unsafe {
if( IntPtr.Size == 4 ) {
int* inj = (int*) methodToInject.MethodHandle.Value.ToPointer() + 2;
int* tar = (int*) methodToReplace.MethodHandle.Value.ToPointer() + 2;
if( System.Diagnostics.Debugger.IsAttached ) {
//Version x86 Debug
byte* injInst = (byte*) *inj;
byte* tarInst = (byte*) *tar;
int* injSrc = (int*) (injInst + 1);
int* tarSrc = (int*) (tarInst + 1);
*tarSrc = (((int) injInst + 5) + *injSrc) - ((int) tarInst + 5);
}
else {
//Version x86 Release
*tar = *inj;
}
}
else {
long* inj = (long*) methodToInject.MethodHandle.Value.ToPointer() + 1;
long* tar = (long*) methodToReplace.MethodHandle.Value.ToPointer() + 1;
if( System.Diagnostics.Debugger.IsAttached ) {
//Version x64 Debug
byte* injInst = (byte*) *inj;
byte* tarInst = (byte*) *tar;
int* injSrc = (int*) (injInst + 1);
int* tarSrc = (int*) (tarInst + 1);
*tarSrc = (((int) injInst + 5) + *injSrc) - ((int) tarInst + 5);
}
else {
//Version x64 Release
*tar = *inj;
}
}
}
}
代码有效,但我不完全理解它是如何工作的。一旦IL被jitted,注入的方法似乎永久缓存,并且引用原始方法的任何其他代码都会失败,因为它引用了注入的方法。
我不打算重构测试中的原始代码,而是不想在使用Inject方法的时候运行一个测试。有没有办法去&#34;撤消&#34;注射在这里完成了吗?
答案 0 :(得分:1)
我曾尝试保存目标源,但是一旦替换了目标方法,我就不能简单地重新分配前一个源代码,而代码不会进入中断模式。 事实证明这是一个相当简单的答案。在我完成交换(注入)方法之后,我所要做的就是第二次运行相同的命令。使用ToLongString()方法对对象运行NUnit测试,这将通过:
public string InterceptorToString()
{
return "Injected Text";
}
[Test]
[Category( "Interceptor" )]
public void InjectionTest()
{
MyObject obj = new MyObject();
string objString1 = obj.ToLongString();
string intString1 = InterceptorToString(); //returns "Injected Text"
Interceptor.Inject( obj.ToLongString, InterceptorToString );
string objString2 = obj.ToLongString(); //returns "Injected Text"
string intString2 = InterceptorToString();
Interceptor.Inject( obj.ToLongString, InterceptorToString );
string objString3 = obj.ToLongString();
string intString3 = InterceptorToString(); //returns "Injected Text"
Assert.That( objString2, Is.Not.EqualTo( intString2 ) );
Assert.That( objString3, Is.Not.EqualTo( intString3 ) );
Assert.That( objString2, Is.EqualTo( "Injected Text" ) );
}
必须对Interceptor类进行的更改:
unsafe {
if( IntPtr.Size == 4 ) {
int* inj = (int*) methodToInject.MethodHandle.Value.ToPointer() + 2;
int* tar = (int*) methodToReplace.MethodHandle.Value.ToPointer() + 2;
if( System.Diagnostics.Debugger.IsAttached ) {
//Version x86 Debug
byte* injInst = (byte*) *inj;
byte* tarInst = (byte*) *tar;
int* injSrc = (int*) (injInst + 1);
int* tarSrc = (int*) (tarInst + 1);
Replaced = (((int) tarInst + 5) + *tarSrc) - ((int) injInst + 5);
*tarSrc = (((int) injInst + 5) + *injSrc) - ((int) tarInst + 5);
*injSrc = Replaced;
}
else {
//Version x86 Release
Replaced = *tar;
*tar = *inj;
*inj = Replaced;
}
}