生成的 MSIL 引发“公共语言运行时检测到无效程序”
本文关键字:检测 无效 程序 运行时 公共语言 MSIL 引发 | 更新日期: 2023-09-27 17:56:24
我正在尝试编写动态方法来克隆字典<,>。
下面提供的代码引发异常:
未处理的异常:System.Reflection.TargetInvocationException:调用的目标引发了异常。---> System.InvalidProgramException: 公共语言运行库检测到无效程序。 在克隆(字典'2) ---内部异常堆栈跟踪结束--- at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor) at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments) at System.Delegate.DynamicInvokeImpl(Object[] args) at MsilTests.DynamicHelper.Clone[TKey,TValue](Dictionary'2 source) in x:''MsilTests''MsilTests''DynamicHelper.cs:line 17 at MsilTests.Program.Main(String[] args) in x:''MsilTests''MsilTests''Program.cs:line 11
当我添加行时会发生此异常:
generator.Emit(OpCodes.Callvirt, enumeratorType.GetMethod("MoveNext"));
我不知道为什么抛出这个异常。如果有人能帮助解决这个问题,我将不胜感激。
public static class DynamicHelper
{
public static Dictionary<TKey, TValue> Clone<TKey, TValue>(Dictionary<TKey, TValue> source)
{
var type = typeof (Dictionary<,>).MakeGenericType(typeof (TKey), typeof (TValue));
var genericFunc = typeof(Func<,>);
genericFunc = genericFunc.MakeGenericType(type, type);
var method = new DynamicMethod("Clone", type, new[] { type }, Assembly.GetExecutingAssembly().ManifestModule, true);
GenerateMsil(method, type);
return (Dictionary<TKey, TValue>)method.CreateDelegate(genericFunc).DynamicInvoke(source);
}
private static void GenerateMsil(DynamicMethod method, Type type)
{
var genArgs = type.GetGenericArguments();
var keyType = genArgs[0];
var valueType = genArgs[0];
var pairType = typeof(KeyValuePair<,>).MakeGenericType(keyType, valueType);
var enumeratorType = typeof(Dictionary<,>.Enumerator).MakeGenericType(keyType, valueType);
var enumerableType = typeof (IEnumerable<>).MakeGenericType(pairType);
var generator = method.GetILGenerator();
var labelRet = generator.DefineLabel();
var labelEndFinally = generator.DefineLabel();
var labelMove = generator.DefineLabel();
var labelWhile = generator.DefineLabel();
var source = generator.DeclareLocal(type); //local_0
var target = generator.DeclareLocal(type); //local_1
var enumerator = generator.DeclareLocal(enumeratorType); //local_2
var pair = generator.DeclareLocal(pairType); //local_3
var key = generator.DeclareLocal(keyType); //local_4
var value = generator.DeclareLocal(valueType); //local_5
/*Stack */
/*[0] */
/*[1] +1 */generator.Emit(OpCodes.Newobj, type.GetConstructor(Type.EmptyTypes));
/*[0] -1 */generator.Emit(OpCodes.Stloc, target);
/*[1] +1 */generator.Emit(OpCodes.Ldarg_0);
/*[0] -1 */generator.Emit(OpCodes.Stloc, source);
/*[1] +1 */generator.Emit(OpCodes.Ldloc, source);
/*[1] -1+1*/generator.Emit(OpCodes.Callvirt, type.GetMethod("GetEnumerator"));
/*[0] -1 */generator.Emit(OpCodes.Stloc, enumerator);
var tryFinally = generator.BeginExceptionBlock();
// try {
generator.Emit(OpCodes.Br_S, labelMove);
generator.MarkLabel(labelWhile);
/*[1] +1 */generator.Emit(OpCodes.Ldloca, enumerator);
/*[1] -1+1*/generator.Emit(OpCodes.Callvirt, enumeratorType.GetProperty("Current").GetGetMethod());
/*[0] -1 */generator.Emit(OpCodes.Stloc, pair);
/*[1] +1 */generator.Emit(OpCodes.Ldloca, pair);
/*[1] -1+1*/generator.Emit(OpCodes.Callvirt, pairType.GetProperty("Key").GetGetMethod());
/*[0] -1 */generator.Emit(OpCodes.Stloc, key);
/*[1] +1 */generator.Emit(OpCodes.Ldloca, pair);
/*[1] -1+1*/generator.Emit(OpCodes.Callvirt, pairType.GetProperty("Value").GetGetMethod());
/*[0] -1 */generator.Emit(OpCodes.Stloc, value);
/*[2] +1 */generator.Emit(OpCodes.Ldloc, target);
/*[1] +1 */generator.Emit(OpCodes.Ldloc, key);
/*[3] +1 */generator.Emit(OpCodes.Ldloc, value);
/*[0] -3 */generator.Emit(OpCodes.Callvirt, type.GetMethod("Add"));
generator.MarkLabel(labelMove);
/*[1] +1 */generator.Emit(OpCodes.Ldloc, enumerator);
/*[1] -1+1*/generator.Emit(OpCodes.Callvirt, enumeratorType.GetMethod("MoveNext"));
/*[0] -1 */generator.Emit(OpCodes.Brtrue_S, labelWhile);
generator.Emit(OpCodes.Leave_S, labelRet);
// } finally {
generator.BeginFinallyBlock();
/*[1] +1 */generator.Emit(OpCodes.Ldloca, enumerator);
/*[0] -1 */generator.Emit(OpCodes.Brfalse_S, labelEndFinally);
/*[1] +1 */generator.Emit(OpCodes.Ldloc, enumerator);
/*[0] -1 */generator.Emit(OpCodes.Callvirt, enumeratorType.GetMethod("Dispose"));
generator.MarkLabel(labelEndFinally);
generator.Emit(OpCodes.Endfinally);
// }
generator.EndExceptionBlock();
generator.MarkLabel(labelRet);
/*[1] +1 */generator.Emit(OpCodes.Ldloc, target);
/*[0] -1 */generator.Emit(OpCodes.Ret);
}
}
更新:感谢大家对我问题的帮助和关注。我已经解决了这个问题,它包括将 Ldloc 与字典一起使用<,>。枚举器,但是字典<,>。枚举器是一种值类型,使用 Ldloca 将局部变量的地址传递到 Call 或 Callvirt 中非常重要。我用正确的代码更新了源代码。
GetEnumerator
应该使用CallVirt
。在底部,您有:
generator.Emit(OpCodes.Ldloc, enumerator); // +1
generator.Emit(OpCodes.Callvirt, enumeratorType.GetMethod("MoveNext")); // -1+1
generator.Emit(OpCodes.Ldloc, target); // +1
generator.Emit(OpCodes.Ret); // should be 0 if void, or 1 for non-void
这是堆栈上的两个值,您尚未弹出。应该只有一个。
如果不打算使用它,请删除对MoveNext
的调用;这意味着您还需要删除它之前的Ldloc
。
以下工作(但没有做任何有趣的事情):
// Create instance of Dictionaty<,>
generator.Emit(OpCodes.Newobj, type.GetConstructor(Type.EmptyTypes));
// Store instance in local variable "target"
generator.Emit(OpCodes.Stloc, target);
// Load first method argument to the stack (for static method - argument, for non-static - instance for with method called)
generator.Emit(OpCodes.Ldarg_0);
// Store argument in local variable "source"
generator.Emit(OpCodes.Stloc, source);
// Load value of local variable "source" to the stack
generator.Emit(OpCodes.Ldloc, source);
// Call method GetEnumerator of type IEnumerable<>
generator.Emit(OpCodes.Callvirt, type.GetMethod("GetEnumerator"));
// Store value returned from method in local variable "enumerator"
generator.Emit(OpCodes.Stloc, enumerator);
generator.Emit(OpCodes.Ldloc, target);
generator.Emit(OpCodes.Ret);
但是,它在语义上等效于:
generator.Emit(OpCodes.Newobj, type.GetConstructor(Type.EmptyTypes));
generator.Emit(OpCodes.Ret);
(但不泄漏枚举器)