Question

So I wanted to answer https://codegolf.stackexchange.com/q/22921/12097 and decided to emit MSIL code to do the integer addition. Since this was successful, I then decided to emit MSIL code, which emits my first code. So calling the code constructs a method, which constructs a method that calls int.op_Addition. This fails miserably with the JIT complaining that I have gone too far! Duh!

The exception is System.SystemException: {"JIT Compiler encountered an internal limitation."} on the last line when the dynamic method is called.

My question is, I am correct in assuming that emitting code to emit code is somehow disallowed by the JIT. The alternative is that I made a misake, which is likely, but I checked my code against MSIL code generated by Reflector.

Here is the code, for your amusement:

class Program
{
    static void Main(string[] args)
    {
        int z2=Add2(1, 2);
        // z2 = "JIT Compiler encountered an internal limitation."
    }

    // Emit MSIL to emit MSIL
    public static int Add2(int x, int y)
    {
        Type delegate_type=typeof(Func<int, int, int>);
        DynamicMethod method=new DynamicMethod(typeof(Program).ToString()+".GenAdd",
            typeof(int),
            new Type[] { typeof(int), typeof(int) }, typeof(Program));
        ILGenerator generator=method.GetILGenerator();

        LocalBuilder method1=generator.DeclareLocal(typeof(DynamicMethod));
        LocalBuilder generator1=generator.DeclareLocal(typeof(ILGenerator));
        LocalBuilder add1=generator.DeclareLocal(typeof(Func<int, int, int>));
        LocalBuilder args1=generator.DeclareLocal(typeof(Type[]));
        generator.Emit(OpCodes.Ldtoken, typeof(int));            

        generator.Emit(OpCodes.Call, 
            typeof(Type).GetMethod("GetTypeFromHandle", 
                System.Reflection.BindingFlags.Public | 
                System.Reflection.BindingFlags.Static));
        generator.Emit(OpCodes.Callvirt,
            typeof(object).GetMethod("ToString", 
                System.Reflection.BindingFlags.Public|
                System.Reflection.BindingFlags.Instance));
        generator.Emit(OpCodes.Ldstr, ".op_Addition");                        
        generator.Emit(OpCodes.Call,                
            typeof(string).GetMethod("Concat",
                new Type[] { typeof(string), typeof(string) } ));
        generator.Emit(OpCodes.Ldtoken, typeof(int));
        generator.Emit(OpCodes.Call,
            typeof(Type).GetMethod("GetTypeFromHandle",
                System.Reflection.BindingFlags.Public|
                System.Reflection.BindingFlags.Static));
        generator.Emit(OpCodes.Ldc_I4, 2);
        generator.Emit(OpCodes.Newarr, typeof(Type));
        generator.Emit(OpCodes.Stloc_3);
        generator.Emit(OpCodes.Ldloc_3);
        generator.Emit(OpCodes.Ldc_I4, 0);
        generator.Emit(OpCodes.Ldtoken, typeof(int));
        generator.Emit(OpCodes.Call,
            typeof(Type).GetMethod("GetTypeFromHandle",
                System.Reflection.BindingFlags.Public|
                System.Reflection.BindingFlags.Static));
        generator.Emit(OpCodes.Stelem_Ref);
        generator.Emit(OpCodes.Ldloc_3);
        generator.Emit(OpCodes.Ldc_I4, 1);
        generator.Emit(OpCodes.Ldtoken, typeof(int));
        generator.Emit(OpCodes.Call,
            typeof(Type).GetMethod("GetTypeFromHandle",
                System.Reflection.BindingFlags.Public|
                System.Reflection.BindingFlags.Static));
        generator.Emit(OpCodes.Stelem_Ref);
        generator.Emit(OpCodes.Ldloc_3);
        generator.Emit(OpCodes.Ldtoken, typeof(Program));
        generator.Emit(OpCodes.Call,
            typeof(Type).GetMethod("GetTypeFromHandle",
                System.Reflection.BindingFlags.Public|
                System.Reflection.BindingFlags.Static));

        generator.Emit(OpCodes.Newobj,
            typeof(DynamicMethod).GetConstructor(
                new Type[] { typeof(string), typeof(Type), typeof(Type[]) }));

        generator.Emit(OpCodes.Stloc_0);
        generator.Emit(OpCodes.Ldloc_0);
        generator.Emit(OpCodes.Callvirt,
            typeof(DynamicMethod).GetMethod("GetILGenerator",
                Type.EmptyTypes));
        generator.Emit(OpCodes.Stloc_1);
        generator.Emit(OpCodes.Ldloc_1);
        generator.Emit(OpCodes.Ldtoken, typeof(int));
        generator.Emit(OpCodes.Call,
            typeof(Type).GetMethod("GetTypeFromHandle",
                System.Reflection.BindingFlags.Public|
                System.Reflection.BindingFlags.Static));
        generator.Emit(OpCodes.Callvirt,
            typeof(ILGenerator).GetMethod("DeclareLocal",
                new Type[] { typeof(Type) }));
        generator.Emit(OpCodes.Pop);
        generator.Emit(OpCodes.Ldloc_1);
        generator.Emit(OpCodes.Ldsfld,
            typeof(OpCodes).GetField("Ldarg_0",
                System.Reflection.BindingFlags.Public|System.Reflection.BindingFlags.Static));
        generator.Emit(OpCodes.Callvirt,
            typeof(ILGenerator).GetMethod("Emit", new Type[] { typeof(OpCode) }));
        generator.Emit(OpCodes.Ldloc_1);
        generator.Emit(OpCodes.Ldsfld,
            typeof(OpCodes).GetField("Ldarg_1",
                System.Reflection.BindingFlags.Public|System.Reflection.BindingFlags.Static));
        generator.Emit(OpCodes.Callvirt,
            typeof(ILGenerator).GetMethod("Emit", new Type[] { typeof(OpCode) }));
        generator.Emit(OpCodes.Ldloc_1);
        generator.Emit(OpCodes.Ldsfld,
            typeof(OpCodes).GetField("Add",
                System.Reflection.BindingFlags.Public|System.Reflection.BindingFlags.Static));
        generator.Emit(OpCodes.Callvirt,
            typeof(ILGenerator).GetMethod("Emit", new Type[] { typeof(OpCode) }));
        generator.Emit(OpCodes.Ldloc_1);
        generator.Emit(OpCodes.Ldsfld,
            typeof(OpCodes).GetField("Stloc_0",
                System.Reflection.BindingFlags.Public|System.Reflection.BindingFlags.Static));
        generator.Emit(OpCodes.Callvirt,
            typeof(ILGenerator).GetMethod("Emit", new Type[] { typeof(OpCode) }));
        generator.Emit(OpCodes.Ldloc_1);
        generator.Emit(OpCodes.Ldsfld,
            typeof(OpCodes).GetField("Ldloc_0",
                System.Reflection.BindingFlags.Public|System.Reflection.BindingFlags.Static));
        generator.Emit(OpCodes.Callvirt,
            typeof(ILGenerator).GetMethod("Emit", new Type[] { typeof(OpCode) }));
        generator.Emit(OpCodes.Ldloc_1);
        generator.Emit(OpCodes.Ldsfld,
            typeof(OpCodes).GetField("Ret",
                System.Reflection.BindingFlags.Public|System.Reflection.BindingFlags.Static));
        generator.Emit(OpCodes.Callvirt,
            typeof(ILGenerator).GetMethod("Emit", new Type[] { typeof(OpCode) }));
        generator.Emit(OpCodes.Ldloc_0);
        generator.Emit(OpCodes.Ldtoken, typeof(Func<int, int, int>));
        generator.Emit(OpCodes.Call,
            typeof(Type).GetMethod("GetTypeFromHandle",
                System.Reflection.BindingFlags.Public|
                System.Reflection.BindingFlags.Static));
        generator.Emit(OpCodes.Callvirt,
            typeof(DynamicMethod).GetMethod("CreateDelegate",
                new Type[] { typeof(Type)} ));
        generator.Emit(OpCodes.Isinst, typeof(Func<int, int, int>));
        generator.Emit(OpCodes.Stloc_2);
        generator.Emit(OpCodes.Ldloc_2);
        generator.Emit(OpCodes.Ldarg_0);
        generator.Emit(OpCodes.Ldarg_1);
        generator.Emit(OpCodes.Callvirt,
            typeof(Func<int, int, int>).GetMethod("Invoke",
                System.Reflection.BindingFlags.Public|System.Reflection.BindingFlags.Instance));
        generator.Emit(OpCodes.Ret);

        Func<int, int, int> add2=method.CreateDelegate(typeof(Func<int, int, int>)) as Func<int, int, int>;

        return add2(x, y);
    }
}

I expect Add2 to generate code that looks like this, which works 100% correctly:

    // Emit MSIL directly
    public static int Add1(int x, int y)
    {
        Type delegate_type=typeof(Func<int, int, int>);
        DynamicMethod method = new DynamicMethod(typeof(int).ToString()+".op_Addition",
            typeof(int),
            new Type[] { typeof(int), typeof(int) }, typeof(Program));

        ILGenerator generator=method.GetILGenerator();
        LocalBuilder result=generator.DeclareLocal(typeof(int));

        generator.Emit(OpCodes.Ldarg_0);
        generator.Emit(OpCodes.Ldarg_1);
        generator.Emit(OpCodes.Add);
        generator.Emit(OpCodes.Stloc_0);
        generator.Emit(OpCodes.Ldloc_0);
        generator.Emit(OpCodes.Ret);

        Func<int, int, int> add=method.CreateDelegate(typeof(Func<int, int, int>)) as Func<int, int, int>;

        return add(x, y);
    }
Was it helpful?

Solution

I'm quite certain that the JIT is able to compile code that uses Reflection. If not, you wouldn't be able to use Reflection.Emit, ever. The MSIL you generate goes through the same JIT process as MSIL generated by the C# compiler.

There is a bit of a difference when it comes to CAS checks, but I see nothing to suggest that you're operating in a partial trust scenario to begin with.

I see this line:

generator.Emit(OpCodes.Newobj,
typeof(DynamicMethod).GetConstructor(
    new Type[] { typeof(string), typeof(Type), typeof(Type[]) }));

which locates the DynamicMethod(string, Type, Type[]) constructor that creates an anonymously-hosted method. But in Add1, you have

DynamicMethod method = new DynamicMethod(typeof(int).ToString()+".op_Addition",
                                         typeof(int),
                                         new Type[] { typeof(int), typeof(int) }, 
                                         typeof(Program));

which calls the four-argument DynamicMethod constructor, that adds a method to the type Program.

As a result, there's a complete mismatch of the types on the operand stack when you use newobj.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top