Search code examples
c#reflectionreflection.emitil

Reflection.Emit error Operation can destabilize runtime


I'm currently playing with reflection and I have problem with my short code:

public class Test
{
    public Test()
    { 

    }
    public string Call()
    {
        string called = "Called";
        return called;
    }
}

And usage:

var method = new DynamicMethod("dummy", null, Type.EmptyTypes);
var g = method.GetILGenerator();

g.DeclareLocal(typeof(Object));
g.Emit(OpCodes.Newobj, typeof(Test).GetConstructor(Type.EmptyTypes));
g.Emit(OpCodes.Stloc, 0);
g.Emit(OpCodes.Nop);
g.Emit(OpCodes.Ldloc, 0);
g.Emit(OpCodes.Call, typeof(Test).GetMethod("Call", new Type[]{}));
g.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[]{ typeof(string) }));
g.Emit(OpCodes.Nop);
//g.Emit(OpCodes.Pop); - used in debugging
g.Emit(OpCodes.Ret);

var action = (Action)method.CreateDelegate(typeof(Action));
action();

Console.Read();

So. I 'm trying to create new method in runtime. In that method I'm creating new empty Test instance. Then, I'm trying to set it to location (0) which's type if Object. Then I'm loading it and I'm calling it's method Call to get string. On the end I'm trying to put string result on screen. My code works to 'Ldloc_0'. There when 'Call' method is being called error occurs. Does anybody know how to solve this problem? Please help.


Solution

  • Call(...) and is an instance methods; try using CallVirt instead of OpCodes.Call. Console.WriteLine is a static method, so should use Call.

    If in doubt, just write the C# for what you want to emit, and look at it in reflector.

    Note that Ldloc / 0 followed by a call/callvirt is going to be unverifiable - should have a cast in there too:

    g.DeclareLocal(typeof(Object));
    g.Emit(OpCodes.Newobj, typeof(Test).GetConstructor(Type.EmptyTypes));
    g.Emit(OpCodes.Stloc, 0);
    g.Emit(OpCodes.Ldloc, 0);
    g.Emit(OpCodes.Castclass, typeof(Test));
    g.Emit(OpCodes.Callvirt, typeof(Test).GetMethod("Call", new Type[] { }));
    g.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine",
           new Type[] { typeof(string) }));
    g.Emit(OpCodes.Ret);
    

    or better:

    g.DeclareLocal(typeof(Test));
    g.Emit(OpCodes.Newobj, typeof(Test).GetConstructor(Type.EmptyTypes));
    g.Emit(OpCodes.Stloc_0);
    g.Emit(OpCodes.Ldloc_0);
    g.Emit(OpCodes.Callvirt, typeof(Test).GetMethod("Call", new Type[] { }));
    g.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine",
               new Type[] { typeof(string) }));
    g.Emit(OpCodes.Ret);
    

    or best:

    g.Emit(OpCodes.Newobj, typeof(Test).GetConstructor(Type.EmptyTypes));
    g.Emit(OpCodes.Callvirt, typeof(Test).GetMethod("Call", new Type[] { }));
    g.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine",
              new Type[] { typeof(string) }));
    g.Emit(OpCodes.Ret);