Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

InvalidProgramException (Invalid IL Code)?

I was trying to emit the following code as IL code at runtime.

    class TestObject {
        public int Hello {get;set;}
        public int Test {get;set;}
    }

    static TestObject test(BinaryReader reader) {
        var a = new TestObject();
        a.Hello = reader.ReadInt32();
        a.Test = reader.ReadInt32();
        return a;
    }

LINQPad shows:

test:
IL_0000:  nop         
IL_0001:  newobj      UserQuery+TestObject..ctor
IL_0006:  stloc.0     // a
IL_0007:  ldloc.0     // a
IL_0008:  ldarg.0     
IL_0009:  callvirt    System.IO.BinaryReader.ReadInt32
IL_000E:  callvirt    UserQuery+TestObject.set_Hello
IL_0013:  nop         
IL_0014:  ldloc.0     // a
IL_0015:  ldarg.0     
IL_0016:  callvirt    System.IO.BinaryReader.ReadInt32
IL_001B:  callvirt    UserQuery+TestObject.set_Test
IL_0020:  nop         
IL_0021:  ldloc.0     // a
IL_0022:  stloc.1     
IL_0023:  br.s        IL_0025
IL_0025:  ldloc.1     
IL_0026:  ret         

Trying to reproduce it with C#:

        var method = new DynamicMethod("DynamicCreate", typeof(TestSubject), new Type[] {typeof(BinaryReader)},
            typeof(TestSubject).Module);
        var il = method.GetILGenerator();

        var properties = from property in typeof(TestSubject).GetProperties()
            let orderAttribute =
                property.GetCustomAttributes(typeof(OrderAttribute), false).SingleOrDefault() as OrderAttribute
            orderby orderAttribute.Order
            select property;

        il.Emit(OpCodes.Nop);
        il.Emit(OpCodes.Newobj, typeof(TestSubject).GetConstructors()[0]); // pushes a new instance of testsubject on the stack
        il.Emit(OpCodes.Stloc_0); // pop the instance to local variable 0
        foreach (var prop in properties)
        {
            il.Emit(OpCodes.Ldloc_0); // load local variable 0
            il.Emit(OpCodes.Ldarg_0); // load argument 0 (the binary reader)
            il.Emit(OpCodes.Callvirt, typeof(BinaryReader).GetMethod("ReadInt32", BindingFlags.Public | BindingFlags.Instance)); // call the binary reader method (ReadInt32)
            il.Emit(OpCodes.Callvirt, prop.SetMethod); // call the setter of the property (instance of local variable 0 and return value of readint32)
            il.Emit(OpCodes.Nop);
        }
        il.Emit(OpCodes.Ldloc_0);
        il.Emit(OpCodes.Stloc_1);
        var label = il.DefineLabel();
        il.Emit(OpCodes.Br_S, label);
        il.MarkLabel(label);
        il.Emit(OpCodes.Ldloc_1); // push the test subject instance
        il.Emit(OpCodes.Ret); // and return

        var generator = (Load)method.CreateDelegate(typeof(Load));
        var reader = new BinaryReader(new MemoryStream(new byte[] {1, 2, 3, 4, 0, 0, 0, 1}));
        var test = generator(reader); // exception here

The TestSubject class:

public class TestSubject
{

    [Order]
    public int Test1 { get; set; }

    [Order]
    public int Test2 { get; set; }

}

Giving me the following exception:

System.InvalidProgramException {"Die Common Language Runtime hat ein ungültiges Programm gefunden."}

What's wrong with that?

like image 890
AmazingTurtle Avatar asked Jul 31 '16 14:07

AmazingTurtle


2 Answers

You need to declare locals before you use them. Also, the code can be simplified (the IL was produced in a debug build).

il.DeclareLocal(typeof(TestSubject));

il.Emit(OpCodes.Newobj, typeof(TestSubject).GetConstructors()[0]); // pushes a new instance of testsubject on the stack
il.Emit(OpCodes.Stloc_0); // store the instance in local variable 0
foreach (var prop in properties)
{
    il.Emit(OpCodes.Ldloc_0); // load local variable 0
    il.Emit(OpCodes.Ldarg_0); // load argument 0 (the binary reader)
    il.Emit(OpCodes.Callvirt, typeof(BinaryReader).GetMethod("ReadInt32", BindingFlags.Public | BindingFlags.Instance)); // call the binary reader method (ReadInt32)
    il.Emit(OpCodes.Callvirt, prop.SetMethod); // call the setter of the property (instance of local variable 0 and return value of readint32)
}
il.Emit(OpCodes.Ldloc_0); // push the test subject instance
il.Emit(OpCodes.Ret); // and return
like image 58
Eli Arbel Avatar answered Oct 17 '22 03:10

Eli Arbel


You did not define the locals.

Also, the IL here is from debug mode output. It's a bit more convoluted than necessary.

Reflection emit is nearly obsolete (still needed for some cases). It is much easier to generate code like this using expression trees. Delete all of this code and replace it with expression trees.

like image 40
usr Avatar answered Oct 17 '22 04:10

usr