Search code examples
c#reflectioncompiler-constructionclril

System.Reflection.Emit - If Statement Generation


I'm trying to learn how If statements are generated with ILGenerator in C#.

Here's my code: (ilg is an ILGenerator)

ilg.Emit(OpCodes.Ldc_I4_1);
Label start = ilg.DefineLabel();
ilg.Emit(OpCodes.Brfalse, start);
ilg.Emit(OpCodes.Ldstr, "Hello");
ilg.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }));
ilg.MarkLabel(start);

Some notes:

  • Alone, calling Console.WriteLine works perfectly.
  • This is the error that I get when I run the generated exe:

Unhandled Exception: System.InvalidProgramException: Common Language Runtime detected an invalid program. at Testing.Test.Main(String[] )


Solution

  • You code works almost fine, you just forgot to put OpCodes.Ret at end, see the code that works:

    public static void Main(string[] args)
    {
        var dynamicMethod = new DynamicMethod("PrintHello", typeof(void), null);
        var ilGenerator = dynamicMethod.GetILGenerator();
        ilGenerator.Emit(OpCodes.Ldc_I4_1);
        var toEnd = ilGenerator.DefineLabel();
        ilGenerator.Emit(OpCodes.Brfalse, toEnd);
        ilGenerator.Emit(OpCodes.Ldstr, "Hello");
        ilGenerator.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }));
        ilGenerator.MarkLabel(toEnd);
        ilGenerator.Emit(OpCodes.Ret);
    
        var @delegate = (Action)dynamicMethod.CreateDelegate(typeof(Action));
        @delegate();
    }
    

    Then you'll have this output

    Hello

    If you comment this line ilGenerator.Emit(OpCodes.Ret); you will get this exception

    System.InvalidProgramException: Common Language Runtime detected an invalid program.

    Remember, every method in C# must have the OpCode.Ret

    If your Main method is empty, like this

    public static void Main(string[] args)
    {
    }
    

    The IL generated (in debug mode, without optimization) will be this

    .method public hidebysig static void Main (string[] args) cil managed 
    {
        // Method begins at RVA 0x2050
        // Code size 2 (0x2)
        .maxstack 8
        .entrypoint
    
        IL_0000: nop
        IL_0001: ret
    } // end of method Program::Main
    

    The IL generated (in release mode, with optimization) will be this

    .method public hidebysig static void Main (string[] args) cil managed 
    {
        // Method begins at RVA 0x2050
        // Code size 1 (0x1)
        .maxstack 8
        .entrypoint
    
        IL_0000: ret
    } // end of method Program::Main