撤消IL CLR方法注入?

时间:2018-01-05 16:11:18

标签: c# unit-testing pointers clr jit

我有一些代码需要两个方法并注入一个(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;注射在这里完成了吗?

1 个答案:

答案 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;
           }

        }