Search code examples
c#reflectioncilmethodinfo

Convert class method into IL and execute it at runtime


I want to convert a method into an IL codes from a class and then execute it by invoke it. The example I am following is from msdn: https://msdn.microsoft.com/en-us/library/system.reflection.emit.methodbuilder.createmethodbody(v=vs.110).aspx.

It shows exactly what I need, my problem is to generate the ILcodes from a class method.

So basically I need to fill the following

byte[] ILcodes = new byte[] {
  0x02,   /* 02h is the opcode for ldarg.0 */
  0x03,   /* 03h is the opcode for ldarg.1 */
  0x58,   /* 58h is the opcode for add     */
  0x2A    /* 2Ah is the opcode for ret     */
};

from a method defined in a class for example:

public class MethodBodyDemo
{ 
    public int Add(int x, int y)
    {
        return x + y;
    }
}

I tried the following call to fill the byte array:

var ILcodes = typeof(MethodBodyDemo).GetMethod("Add").GetMethodBody().GetILAsByteArray();

The following is the example I am creating but it gives an exception: "Common Language Runtime detected an invalid program."

public class MethodBodyDemo
{ 
    public int Add(int x, int y)
    {
        return x + y;
    }
    // This class will demonstrate how to create a method body using 
    // the MethodBuilder.CreateMethodBody(byte[], int) method.
    public static Type BuildDynType()
    {
        Type addType = null;
        AppDomain currentDom = Thread.GetDomain();
        AssemblyName myAsmName = new AssemblyName();
        myAsmName.Name = "MyDynamicAssembly";
        AssemblyBuilder myAsmBldr = currentDom.DefineDynamicAssembly(
                           myAsmName,
                           AssemblyBuilderAccess.RunAndSave);
        // The dynamic assembly space has been created. Next, create a module
        // within it. The type Point will be reflected into this module.
        ModuleBuilder myModuleBldr = myAsmBldr.DefineDynamicModule("MyModule");
        TypeBuilder myTypeBldr = myModuleBldr.DefineType("Adder");
        MethodBuilder myMthdBldr = myTypeBldr.DefineMethod("Add",
                                MethodAttributes.Public |
                                MethodAttributes.Static,
                                typeof(int),
                                new Type[]
                                {typeof(int), typeof(int)});
        // Build the array of Bytes holding the MSIL instructions.
        // byte[] ILcodes = new byte[] {
        //  0x02,   /* 02h is the opcode for ldarg.0 */
        //  0x03,   /* 03h is the opcode for ldarg.1 */
        //  0x58,   /* 58h is the opcode for add     */
        //  0x2A    /* 2Ah is the opcode for ret     */
        // };
        var ILcodes = typeof(MethodBodyDemo).GetMethod("Add").GetMethodBody().GetILAsByteArray();
        myMthdBldr.CreateMethodBody(ILcodes, ILcodes.Length);
        addType = myTypeBldr.CreateType();
        return addType;
    }
    public static void TestExecMethod()
    {
        Type myType = BuildDynType();
        Console.WriteLine("---");
        Console.Write("Enter the first integer to add: ");
        int aVal = Convert.ToInt32(Console.ReadLine());
        Console.Write("Enter the second integer to add: ");
        int bVal = Convert.ToInt32(Console.ReadLine());
        object adderInst = Activator.CreateInstance(myType, new object[0]);
        Console.WriteLine("The value of adding {0} to {1} is: {2}.", aVal, bVal, myType.InvokeMember("Add", BindingFlags.InvokeMethod, null, adderInst, new object[] { aVal, bVal }));
    }
}

Execute by calling:

MethodBodyDemo.TestExecMethod();

Any help please?


Solution

  • Build your project in Release mode, and do one of the following:

    Remove the MethodAttributes.Static attribute here

    MethodBuilder myMthdBldr = myTypeBldr.DefineMethod("Add",
                               MethodAttributes.Public |
                               MethodAttributes.Static,
                               typeof(int),
                               new Type[]
                               {typeof(int), typeof(int)});
    

    Or change this method to be static

    public int Add(int x, int y)
    {
        return x + y;
    }
    

    And this should work for you.

    enter image description here

    But, I saw in comments that you want it to pass bytes to other system and run it, my answer is just for the example you have in the question. In reality this is not going to work because the reason that Simon Svensson wrote in comment. (Unless all methods are statics and do pure operations (like return 3+4) without any reference to other methods\types\fields.)

    You still can (theoretically) do some magic to make it work but it's not practically.