ILGenerator在int上发出调用GetHashCode

时间:2019-05-13 21:42:08

标签: c# .net-core

当我运行此代码时:

var il = getHashCode.GetILGenerator();
il.Emit(OpCodes.Ldc_I4_S, 17); // put "17" on the stack
il.Emit(OpCodes.Call, typeof(Int32).GetMethod("GetHashCode", new Type[] { })); 
il.Emit(OpCodes.Ret);

我正在获取System.NullReferenceException:对象引用未设置为对象的实例。

当我输入值时:

var il = getHashCode.GetILGenerator();
il.Emit(OpCodes.Ldc_I4_S, 17); // put "17" on the stack
il.Emit(OpCodes.Box, typeof(Int32));
il.Emit(OpCodes.Call, typeof(Int32).GetMethod("GetHashCode", new Type[] { })); 
il.Emit(OpCodes.Ret);

返回值为-1875039000,但应为17。

如何发出正确的电话?

1 个答案:

答案 0 :(得分:2)

GetHashCode()是一个实例方法,因此您需要在“引用”上调用它。虽然不需要装箱整数,但需要压入堆栈的this参数不是整数值本身,而是指向整数值的指针。

由于这个原因,您需要一个局部变量,可以在其中存储整数值,然后将指向该局部值的指针推入堆栈(ldloca.s)并调用实例方法:

static void Main(string[] args)
{
    var method = new DynamicMethod("Get17HashCode", typeof(int), new Type[0], typeof(Program).Module);
    var ilGenerator = method.GetILGenerator();
    ilGenerator.DeclareLocal(typeof(int));
    ilGenerator.Emit(OpCodes.Ldc_I4_S, 17);
    ilGenerator.Emit(OpCodes.Stloc_0);
    ilGenerator.Emit(OpCodes.Ldloca_S, 0);
    ilGenerator.Emit(OpCodes.Call, typeof(int).GetMethod(nameof(int.GetHashCode)));
    ilGenerator.Emit(OpCodes.Ret);

    var delegateFunction = (Func<int>)method.CreateDelegate(typeof(Func<int>));

    var result = delegateFunction();
    Console.WriteLine($"Got {result}");
}

获得NullReferenceException的原因可能是因为地址17仍在CLR为空引用注册处理程序的地址0上注册的虚拟内存页中。较大的值应导致AccessViolationException: Attempted to read or write protected memory(假设堆栈上使用简短形式表示)。