tags:

views:

167

answers:

2

I discovered the reason I was getting "Operation could destabilize the runtime" in a DynamicMethod I'm producing, and though I easily fixed it, it left me with a seemingly simple question:

  • How do I cast an object reference of type "Object" into a specific type, so that I can call methods from that type on the object reference?

Below is a sample program. When running this, it will crash with an "Operation could destabilize the runtime" exception when compiling the method.

The problem is fixed by just changing the type of the variable being declared to be of type TestClass instead of Object, but I still want to know how I can cast the reference to the appropriate type in the code.

In the code I've marked a line with asterixes. What can I emit of code at that point that will make the Object reference on the stack into a TestClass reference instead, so that the method call will go through?

Note that I know that it is the method call that produces the problem, if I comment out the lines altogether, it doesn't matter which type the variable is, the method is compiled and executes fine.

Here's the code.

using System;
using System.Reflection.Emit;

namespace ConsoleApplication9
{
    public class TestClass
    {
        public void TestMethod() { }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Type type = typeof(TestClass);
            DynamicMethod method = new DynamicMethod("", typeof(Object), null);
            ILGenerator il = method.GetILGenerator();
            LocalBuilder variable = il.DeclareLocal(typeof(Object));

            // Construct object
            il.Emit(OpCodes.Newobj, type.GetConstructor(new Type[0]));
            il.Emit(OpCodes.Stloc, variable);

            // Call Test method
            il.Emit(OpCodes.Ldloc, variable);
            // ***************************************** what do I do here?
            il.Emit(OpCodes.Call, type.GetMethod("TestMethod"));

            // Return object
            il.Emit(OpCodes.Ldloc, variable);
            il.Emit(OpCodes.Ret);

            // Create and call delegate
            Func<Object> fn = (Func<Object>)method.CreateDelegate(
                typeof(Func<Object>));
            Object instance = fn();
        }
    }
}
A: 

Have you tried

(variable as TestClass)
junmats
I have to emit the code to do that, that is my question, what does that code look like. "variable" here is a LocalBuilder object, used to define the variable for the compiled code.
Lasse V. Karlsen
+2  A: 

The short answer:

// Call Test method
il.Emit(OpCodes.Ldloc, variable);
il.Emit(OpCodes.Castclass, type);
il.Emit(OpCodes.Call, type.GetMethod("TestMethod"));

How to come by that though? Well, the method I used was Reflector. Firstly, write up a method that does what you want to do. I came up with the following:

private static object PrecompiledTest()
{
    object variable = new TestClass();
    ((TestClass) variable).TestMethod();
    return variable;
}

Now, compile that, and open Reflector, and open your assembly. Navigate to your function and look at the MSIL for it. The function above decompiles to the following:

.method private hidebysig static object PrecompiledTest() cil managed
{
    .maxstack 1
    .locals init (
        [0] object variable,
        [1] object CS$1$0000)
    L_0000: nop 
    L_0001: newobj instance void EmitTest.TestClass::.ctor()
    L_0006: stloc.0 
    L_0007: ldloc.0 
    L_0008: castclass EmitTest.TestClass
    L_000d: callvirt instance void EmitTest.TestClass::TestMethod()
    L_0012: nop 
    L_0013: ldloc.0 
    L_0014: stloc.1 
    L_0015: br.s L_0017
    L_0017: ldloc.1 
    L_0018: ret 
}

The above uses callvirt instead of call. I'm not really all that proficient in IL, so I'm not sure of the difference, but call does work in your example. One last thing, while we're on the topic of Reflector. You can use the ReflectionEmitLanguage addin to great effect to generate your Emit code for you. This addin generates the following code for you:

public MethodBuilder BuildMethodPrecompiledTest(TypeBuilder type)
{
    // Declaring method builder
    // Method attributes
    System.Reflection.MethodAttributes methodAttributes = 
          System.Reflection.MethodAttributes.Private
        | System.Reflection.MethodAttributes.HideBySig
        | System.Reflection.MethodAttributes.Static;
    MethodBuilder method = type.DefineMethod("PrecompiledTest", methodAttributes);
    // Preparing Reflection instances
    ConstructorInfo ctor1 = typeof(TestClass).GetConstructor(
        BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, 
        null, 
        new Type[]{
            }, 
        null
        );
    MethodInfo method2 = typeof(TestClass).GetMethod(
        "TestMethod", 
        BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, 
        null, 
        new Type[]{
            }, 
        null
        );
    // Setting return type
    method.SetReturnType(typeof(Object));
    // Adding parameters
    ILGenerator gen =  method.GetILGenerator();
    // Preparing locals
    LocalBuilder variable =  gen.DeclareLocal(typeof(Object));
    LocalBuilder CS$1$0000 =  gen.DeclareLocal(typeof(Object));
    // Preparing labels
    Label label23 =  gen.DefineLabel();
    // Writing body
    gen.Emit(OpCodes.Nop);
    gen.Emit(OpCodes.Newobj,ctor1);
    gen.Emit(OpCodes.Stloc_0);
    gen.Emit(OpCodes.Ldloc_0);
    gen.Emit(OpCodes.Castclass,TestClass);
    gen.Emit(OpCodes.Callvirt,method2);
    gen.Emit(OpCodes.Nop);
    gen.Emit(OpCodes.Ldloc_0);
    gen.Emit(OpCodes.Stloc_1);
    gen.Emit(OpCodes.Br_S,label23);
    gen.MarkLabel(label23);
    gen.Emit(OpCodes.Ldloc_1);
    gen.Emit(OpCodes.Ret);
    // finished
    return method;
}
Matthew Scharley
Sorry for ignoring your answer for so long, I had a bug in my code which made it look like Castclass wasn't the right choice, but when I found that out, I promptly forgot about my SO question. Thanks for the effort.
Lasse V. Karlsen