Search code examples
c#reflection.emit

How to translate IL to Emit statements?


Here is the C# code:

public class Calc1 : ICalculator
{
    public int Calculate(int x, int y)
    {
        return x + y;
    }
}

Here is the IL:

.method public hidebysig newslot virtual final 
    instance int32  Calculate(int32 x,
                              int32 y) cil managed
{
  // Code size       9 (0x9)
  .maxstack  2
  .locals init ([0] int32 CS$1$0000)
  IL_0000:  nop
  IL_0001:  ldarg.1
  IL_0002:  ldarg.2
  IL_0003:  add
  IL_0004:  stloc.0
  IL_0005:  br.s       IL_0007
  IL_0007:  ldloc.0
  IL_0008:  ret
} // end of method Calc1::Calculate

How can I translate the IL above to Emit statements? Here is my try of translating lines one by one, but got an 'System.InvalidProgramException'.

        string methodName = "Calculate";

        MethodBuilder getFieldMethod = typeBuilder.DefineMethod(methodName, MethodAttributes.Public | MethodAttributes.Virtual, typeof(int), new Type[] { typeof(int), typeof(int) });
        ILGenerator methodIL = getFieldMethod.GetILGenerator();

        Label iL0007Label = methodIL.DefineLabel();

        methodIL.Emit(OpCodes.Nop);
        methodIL.Emit(OpCodes.Ldarg_1);
        methodIL.Emit(OpCodes.Ldarg_2);
        methodIL.Emit(OpCodes.Add);
        methodIL.Emit(OpCodes.Stloc_0);
        methodIL.Emit(OpCodes.Br_S, iL0007Label);

        methodIL.MarkLabel(iL0007Label);
        methodIL.Emit(OpCodes.Ldloc_0);
        methodIL.Emit(OpCodes.Ret);

What's wrong with my Emit statements?


Solution

  • You forgot to declare the local variable:

    ILGenerator methodIL = getFieldMethod.GetILGenerator();
    
    methodIL.DeclareLocal(typeof(int)); // THIS ONE!
    
    Label iL0007Label = methodIL.DefineLabel();
    

    And technically you forgot the MethodAttributes.Final, but I don't know if you want to add it.

    Note that the opcodes you got are probably for a debug build. If you want to look at the opcodes, I suggest using release builds that use less opcodes.

    ILGenerator methodIL = getFieldMethod.GetILGenerator();
    methodIL.Emit(OpCodes.Ldarg_1);
    methodIL.Emit(OpCodes.Ldarg_2);
    methodIL.Emit(OpCodes.Add);
    methodIL.Emit(OpCodes.Ret);
    

    is enough for what you want, without even using a local variable.