0

I'm just curious on how I would call a method on a field using Emit.

I have this class generated

public class AClass : IDynamicProxyTestInterface
{
    private DynamicProxy<IDynamicProxyTestInterface> proxy;
    public AClass(DynamicProxy<IDynamicProxyTestInterface> p)
    {
        proxy = p;
    }

    public void Hello()
    {
        // Here I want to do the following
        Int32 code = 123456;
        proxy.HandleCall(code);
    }
}

The commented part is what I've been struggling to figure out.

I have this code(commented out stuff is stuff I've been turning on and off, trying to make it work)

var methodBuilder = typeBuilder.DefineMethod(
    methodInfo.Name,
    MethodAttributes.Public | MethodAttributes.Virtual,
    methodInfo.ReturnType, 
    methodInfo.GetParameters().Select(p => p.GetType()).ToArray()
    );
var methodIlGen = methodBuilder.GetILGenerator();

// Add proxy call to method
var emptyAction = new DynamicProxyOnBuilder();
ProxyCalls.Add(methodInfo.GetHashCode(), emptyAction);

// Drop method info hash into local variable 0
//var hash = methodInfo.GetHashCode();
//methodIlGen.Emit(OpCodes.Ldc_I4, hash);
//methodIlGen.DeclareLocal(typeof(Int32), false);
//methodIlGen.Emit(OpCodes.Stloc_0);

// Create local variable for instance of this DynamicProxy
methodIlGen.Emit(OpCodes.Ldarg_0);
methodIlGen.Emit(OpCodes.Ldflda, proxyField);
var var2 = methodIlGen.DeclareLocal(this.GetType());
methodIlGen.Emit(OpCodes.Stloc,var2);
////methodIlGen.EmitWriteLine(proxyField);
////methodIlGen.Emit(OpCodes.St);
//methodIlGen.Emit(OpCodes.Ldloc,var2);
methodIlGen.Emit(OpCodes.Call, this.GetType().GetMethods().First(x => x.Name == "HandleCall"));

Any ideas what I'm doing wrong here?

BTW the errors that I get are Common Language Runtime detected an invalid program. and JIT Compiler encountered an internal limitation.

Kelly Elton
  • 4,373
  • 10
  • 53
  • 97
  • What's with all the commented out code? Also seems to be a lot of irrelevant code. – leppie Feb 16 '13 at 07:30
  • 2
    Why dont you just compile a C# snippet in release mode, and see what IL is produced. This should be trivial. – leppie Feb 16 '13 at 07:31
  • `(commented out stuff is stuff I've been turning on and off, trying to make it work)` – Kelly Elton Feb 16 '13 at 07:32
  • this is my first attempt playing with IL and emit, and I haven't touched assembly in years, so this type of stuff is a bit foreign to me. Just figured someone would be able to look at it and provide a quick answer. Been fighting with this for hours. – Kelly Elton Feb 16 '13 at 07:33
  • @leppie Yeah, I guess you were right. I dropped it into IL, browsed through the code, and figured it out pretty quickly. Thanks for the tip. – Kelly Elton Feb 16 '13 at 07:43
  • No problem :) It is easier than it looks sometimes. – leppie Feb 16 '13 at 07:53

1 Answers1

3

Usually the best thing to do in these circumstances is to write the code in c#, compile it and then examine the compiled IL.

In your case though the main thing you are missing is that your method must finish with a "ret" instruction, and the HandleCall should be virtual. So you just need (assuming HandleCall has a void return type)

Ldflda [proxyField]   // push the this pointer
Ldc_I4 123            // push the argument
Callvirt [HandleCode] // call the method
Ret                   // return
Oliver Hallam
  • 4,242
  • 1
  • 24
  • 30
  • BTW, if the method is not virtual, it's not necessary to use `callvirt` instead of `call`. But it's usually a good idea, because `call` doesn't perform a `null` check. – svick Feb 16 '13 at 14:17
  • The ret is there, it was just out of scope, sorry. – Kelly Elton Feb 16 '13 at 17:55
  • @svick - you're right of course; I didn't read the sample carefully enough! I assumed this was a call on an interface, not on a non-virtual method. – Oliver Hallam Feb 18 '13 at 10:56