none
Generated MSIL throws “Common Language Runtime detected an invalid program.” RRS feed

  • Question

  • I'm trying to write dynamic method that make a clone of the Dictionary<,>.
    Code presented below throws Exception:

    Unhandled Exception: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
    ---> System.InvalidProgramException: Common Language Runtime detected an invalid program.
       at Clone(Dictionary`2 )
       --- End of inner exception stack trace ---
       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

    This exception occurs when i added line:

    generator.Emit(OpCodes.Callvirt, enumeratorType.GetMethod("MoveNext"));

    I have no idea why this Exception is thrown. If someone can help solve this problem, I will be very grateful.

        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.Ldloc, enumerator);
    /*[1] -1+1*/generator.Emit(OpCodes.Callvirt, enumeratorType.GetProperty("Current").GetGetMethod());
    /*[0] -1  */generator.Emit(OpCodes.Stloc, pair);
    
    /*[1] +1  */generator.Emit(OpCodes.Ldloc, pair);
    /*[1] -1+1*/generator.Emit(OpCodes.Callvirt, pairType.GetProperty("Key").GetGetMethod());
    /*[0] -1  */generator.Emit(OpCodes.Stloc, key);
    
    /*[1] +1  */generator.Emit(OpCodes.Ldloc, pair);
    /*[1] -1+1*/generator.Emit(OpCodes.Callvirt, pairType.GetProperty("Value").GetGetMethod());
    /*[0] -1  */generator.Emit(OpCodes.Stloc, value);
    
    /*[1] +1  */generator.Emit(OpCodes.Ldloc, value);
    /*[2] +1  */generator.Emit(OpCodes.Ldloc, key);
    /*[1] -2+1*/generator.Emit(OpCodes.Newobj, pairType.GetConstructor(new []{ keyType, valueType }));
    
    /*[2] +1  */generator.Emit(OpCodes.Ldloc, target);
    /*[0] -2  */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.Ldloc, 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);
            }
        }
    Sample project : https://mega.co.nz/#!sVRCEKJD!Ry5E_gwtVyK0tn8q9-b0kXooT3AymDqhuR9D7p_eGWM


    Monday, January 28, 2013 3:22 PM

Answers

  • You have a few wrong things in your code:

    1) Dictionary<TKey,Tvalue>.Enumerator and KeyValuePair<TKey,TValue> are value-types. You need to use ldloca to call methods on them.

    2) Dictionary<TKey,TValue>.Add takes two parameters: the key and the value. It doesn't take a KeyValuePair.

    3) You cannot check a Dictionary<TKey, TValue>.Enumerator using brfalse. It's a value-type. It cannot be null.

    Monday, January 28, 2013 5:49 PM