Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to save a MethodInfo pointer and later call that function?

C#/CIL/Reflection.Emit question:

I'm trying to define a type that has a function pointer, instantiate that type, create a static method on another type (because I don't know how else to make "just a function"), give a pointer to this static method to the instance, and later use that pointer to call the function.

I've had little success. :-(

Here's the type:

Thunk = modb.DefineType("Thunk");

Thunk.DefineField("Env" , Env.AsType(), FieldAttributes.Public);
Thunk.DefineField("Expr", typeof(int), FieldAttributes.Public); // int is the correct type according to http://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.ldftn.aspx

Thunk.CreateType();

So far so good (I think). Then I create one of these guys and assign the function pointer:

var methodBuilder = MainType.DefineMethod("my_other_little_function", MethodAttributes.Static, typeof(Int64), new[] {Env.AsType()});
{
    var il2 = methodBuilder.GetILGenerator();
    il2.Emit(OpCodes.Ldarg_0);
    il2.Emit(OpCodes.Stloc_0);
    binding.Expr.Compile(il2);
    il2.Emit(OpCodes.Ret);
}
il.Emit(OpCodes.Newobj, ThunkCtor);
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Stfld, Thunk.GetField("Env"));
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldftn, methodBuilder);
il.Emit(OpCodes.Stfld, Thunk.GetField("Expr"));

As far as I can tell, this part is working just fine. The problem is elsewhere, when I try to call it:

var func = il.DeclareLocal(typeof(int));
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldfld, Thunk.GetField("Expr"));
il.Emit(OpCodes.Stloc, func);
il.Emit(OpCodes.Ldfld, Thunk.GetField("Env"));
il.Emit(OpCodes.Ldloc, func);

il.EmitCalli(OpCodes.Calli, CallingConventions.Standard, typeof(Int64), new[] { Env.AsType() }, null);

If I try to run the program created by this, I get at error before any of the bytecode is executed (or at least that's how it appears):

Unhandled Exception: System.InvalidProgramException: Common Language Runtime detected an invalid program.

If instead I replace the EmitCalli() with code to simply pop the function pointer and the argument and push a number, the rest of the program works fine. So how am I supposed to call this function?

Thanks so much. :-)

like image 318
Chris Pine Avatar asked Nov 03 '22 08:11

Chris Pine


1 Answers

Unless you like solving problems like this just for the fun of it, another option is to use a library to generate the desired IL.

Fasterflect is a library with lots of reflection helpers. For instance, to get a delegate for a method, just write:

var delegate = typeof(YourClass).DelegateForCallStaticMethod( "MyStaticMethod" );
delegate( args );

The library uses DynamicMethod and IL generation behind the scenes, and also has extensions that do not require you to declare and use a variable for the delegate (although that will be much faster if you intend to call it repeatedly, e.g. in a tight loop). The simple variant looks like this:

var result = typeof(YourClass).CallStaticMethod( "MyStaticMethod", args );

Fasterflect caches generated delegates (as it's quite expensive to compile the generated IL), so the added performance cost for each invocation in the simple scenario equates to the cost of doing the cache lookup.

Disclaimer: I'm involved in said project. That said, writing IL by hand really is no fun task. Another good option for IL generation is the Mono.Cecil library, although I'm not familiar with the particulars of what it offers.

like image 54
Morten Mertner Avatar answered Nov 14 '22 16:11

Morten Mertner