Search code examples
c#reflectioncil

Add objects to list in loop in IL Emit - Common Language Runtime detected an invalid program


The following is my C# code:

List<int> list = new List<int>();
for(int Count = 0; Count < 5; Count++)
    list.Add(Count);
return list;

My corresponding emitted code is as follows:

LocalBuilder list = ILout.DeclareLocal(typeof(List<int>));
LocalBuilder Count = ILout.DeclareLocal(typeof(int));
LocalBuilder CmpRes = ILout.DeclareLocal(typeof(bool));
ConstructorInfo DictConstrctor = typeof(List<int>).GetConstructor(new Type[] { });
MethodInfo methodinfo_add = typeof(List<int>).GetMethod("Add", new[] { typeof(int) });
Label IL_001C = ILout.DefineLabel();
Label IL_000B = ILout.DefineLabel();

ILout.Emit(OpCodes.Newobj, DictConstrctor);
ILout.Emit(OpCodes.Stloc_0, list);
ILout.Emit(OpCodes.Ldc_I4_0);
ILout.Emit(OpCodes.Stloc_1, Count);

ILout.Emit(OpCodes.Br_S, IL_001C);
ILout.MarkLabel(IL_000B);
ILout.Emit(OpCodes.Ldloc_0, list);
ILout.Emit(OpCodes.Ldloc_1, Count);
ILout.Emit(OpCodes.Call, methodinfo_add);

ILout.Emit(OpCodes.Ldloc_1, Count);
ILout.Emit(OpCodes.Ldc_I4_1);
ILout.Emit(OpCodes.Add);

ILout.Emit(OpCodes.Stloc_1, Count);
ILout.MarkLabel(IL_001C);
ILout.Emit(OpCodes.Ldloc_1, Count);
ILout.Emit(OpCodes.Ldc_I4_2);
ILout.Emit(OpCodes.Clt);
ILout.Emit(OpCodes.Stloc_3, CmpRes);
ILout.Emit(OpCodes.Ldloc_3, CmpRes);
ILout.Emit(OpCodes.Brtrue_S, IL_000B);

ILout.Emit(OpCodes.Ldloc_0, list);
ILout.Emit(OpCodes.Ret);

It is throwing an exception - "Common Language Runtime detected an invalid program.".

What is it that I am doing wrong here? Any help is appreciated.


Solution

  • ILout.Emit(OpCodes.Stloc_1, Count);
    

    and

    ILout.Emit(OpCodes.Ldloc_1, Count);
    

    make no sense. No additional parameter is needed if you are explicitly saying "use local 1"

    Likewise:

    ILout.Emit(OpCodes.Stloc_3, CmpRes); 
    ILout.Emit(OpCodes.Ldloc_3, CmpRes);
    

    although frankly I'm not sure that CmpRes serves any useful purpose; no point storing and loading - just leave it on the stack

    Note: if Count is "local 1", then CmpRes is "local 2"; there is no "local 3", so Stloc_3 and Ldloc_3 are malformed.

    And again here:

    ILout.Emit(OpCodes.Ldloc_0, list);
    ILout.Emit(OpCodes.Ldloc_1, Count);
    

    --

    Next we get to the call; you're doing a static call:

    ILout.Emit(OpCodes.Call, methodinfo_add);
    

    but that is an instance method on an object, so should be a virtual call.

    and again another local fumble here:

    ILout.Emit(OpCodes.Ldloc_1, Count);
    

    and here:

    ILout.Emit(OpCodes.Stloc_1, Count);
    

    and here:

    ILout.Emit(OpCodes.Ldloc_0, list);
    

    However, I also have severe doubts that this loop (even if fixed) does what you expect it to do - if I read it right, it is actually:

    var list = new List<int>();
    for(int i [= 0] ; i < 2 ; i++) // note the 0 here implicit not explicit
    {
        list.Add(i);
    }
    return list;