Search code examples
c#dynamicmethodilgenerator

DynamicMethod call terminated due to a StackOverFlowException


I have this class (simplified example)

public class Foo
{
    public object Bar(Type type)
    {
        return new object();
    }
}

and I want to call the Bar method on an instance of Bar using DynamicMethod as it is shown below:

MethodInfo methodInfo = typeof(Foo).GetMethod(nameof(Foo.Bar), new[] { typeof(Type) });
DynamicMethod method = new DynamicMethod("Dynamic Bar", 
                                         typeof(object), 
                                         new []{ typeof(Type) }, 
                                         typeof(Foo).Module);

ILGenerator ilGenerator = method.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.EmitCall(OpCodes.Call, method, null); // I feel like this is wrong...
ilGenerator.Emit(OpCodes.Ret);

Func<Type, object> func = (Func<Type, object>) method.CreateDelegate(typeof(Func<Type, object>));

// Attempt to call the function:
func(typeof(Foo));

However, it does not work as wanted but rather aborts with

Process is terminated due to a StackOverFlowException.


Can someone please tell me what I am doing wrong? Is it a mismatch of the parameters? How can I call the Func on a specific instance of Bar?


Solution

  • ilGenerator.EmitCall(OpCodes.Call, method, null); // I feel like this is wrong...
    

    You are currently writing method; you probably intended to call methodInfo here. Note that this will need to be a static method to use Call - if it is an instance method, you should probably be using CallVirt. Since you aren't passing in an instance of Foo, it is unclear where the target instance is going to come from; you need to load two values onto the stack to call the instance method Foo.Bar(Type type) - and you're currently only loading one.

    To show Delegate.CreateDelegate usage:

    var methodInfo = typeof(Foo).GetMethod(nameof(Foo.Bar), new[] { typeof(Type) });
    var foo = new Foo();
    
    // if Foo is known ahead of time:
    var f1 = (Func<Type, object>)Delegate.CreateDelegate(
        typeof(Func<Type, object>), foo, methodInfo);
    
    // if Foo is only known per-call:
    var f2 = (Func<Foo, Type, object>)Delegate.CreateDelegate(
        typeof(Func<Foo, Type, object>), null, methodInfo);
    
    Console.WriteLine(f1(typeof(string)));
    
    Console.WriteLine(f2(foo, typeof(string)));