【发布时间】:2018-06-15 11:35:31
【问题描述】:
我有一段代码需要两个方法,并在 IL 中注入一个(injectionMethod)代替另一个(replacedMethod)。这被用来操纵replacedMethod的结果,以在单元测试中给出injectionMethod提供的结果。
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 方法的测试。有没有办法“撤消”这里的注入?
【问题讨论】:
-
为什么不坚持原来的方法参考,在你完成后把那只小狗注入回去?
-
我想到了这个想法。我在一个单独的类中有这段代码,我尝试将替换的方法保存为属性,并在测试完成后执行“Undo_Inject”方法。不过,它似乎仍然引用了新注入的代码。
-
唯一的写入发生在
*tarSrc = (((int) injInst + 5) + *injSrc) - ((int) tarInst + 5);。在此之前保存*tarSrc的值,稍后使用它来恢复它。
标签: c# unit-testing pointers clr jit