December 23, 2016

Invoking a delegate from inside a dynamic method

As always, one question at StackOverflow attracted my attention and made me program all the night. The question was actually about a way to refer to a predefined object in a dynamically emitted method in a dynamic assembly. That is actually quite an easy task, provided you aren't going to save the assembly and use it later in a different program. In this case, simply use some way to refer to the object using a number (add it to a list, or use a GCHandle) and there's nothing easier than emitting an integer in CIL.

If you want to save the assembly, this approach obviously will not work, as the object will be lost when you quit the runtime. In this case, you'd have to find an optimal way to serialize the desired object, either directly calling its constructor in method, or use a serializer and put the result in the assembly resource space.

However, for some strange reason, I didn't immediately think about those two ways, probably because it was late night. Instead, I thought about compiled lambdas in Linq.Expressions and how closures work there (mainly Expression.Constant). Behind all that emitting, it's actually quite simple - if you know how delegates work. Someone coming over from the land of C will think about delegates as wrappers for function pointers, but it's more than that. You can bind a function pointer to a specific target object, which is then internally used as the first argument to the method (you can actually even bind static methods to target objects, and they behave as expected).

LINQ Expressions work the same way - the compiled lambda is dynamically emitted, and non-primitive constants are placed in a special container, which is then used as the target object of the created delegate. The compiled method would then access the container as easily as using this in an instance method. Provided I wouldn't have to save the assembly, what if I were to bind the desired object to a delegate and then call that delegate in the dynamic assembly?

The immediate problem here is obvious - the delegate has a target object, and I still have no way (ignore the first paragraph) to refer to it from inside the dynamic method. What I would want is a way to generate a piece of code that somehow stores the delegate and its target object, and would allow me to call it as a raw function pointer. Hmm, is there something in .NET that does that?

Connecting emitting and interop

Actually, I recalled there is - Marshal.GetFunctionPointerForDelegate. A bit of an overkill, having to generate all that marshalling code for a method that is never intended to be called from an unmanaged code, but whatever, it works. And I get a function pointer from a delegate, without any additional data. However, it's an unmanaged function pointer, so I have to convert it back to a managed one. Naturally, the method has a counterpart - Marshal.GetDelegateForFunctionPointer, but unfortunatelly, it is too smart for me to use it. It traverses an internal hash table and checks it there's some delegate that has been already converted to an unmanaged function pointer, and finds the original one, returning it. I have to tell the method not to do that and simply generate a new thunk, but how...?

Marshal.GetManagedThunkForUnmanagedMethodPtr comes to my mind, me having seen that method often when browsing the Marshal class. It sounds like it should accomplish what I seek, although the naming seems strange at first (function pointer vs. method pointer). There is a "small" caveat, though. The arguments are "IntPtr pfnMethodToWrap, IntPtr pbSignature, int cbSignature", and MSDN does nothing helpful than retelling their names in plain English. The first argument should be obviously the unmanaged function pointer in question, but what about the rest? In C#, the signature of a method is basically the list of all its parameter types, but that's not what the method needs. I have to obtain the CIL signature of the metod.

Coming back to CIL

ECMA-335 is an excellent reading, if you want to know more about CIL and signatures. In short, it's a byte array containing information about the method's return type, parameter types, and the managed calling convention (like varargs). For a plain method without any return type or parameter types, the signature is "00 00 01". I am not going to describe it in detail; read the ECMA specification for that.

But how to actually obtain the correct signature of the method, other than generating it via using the specification and reflection? Pardon me for promoting my own library, but there are already means there to accomplish this. Using the ReflectionTools.GetSignature method, I have a quick way of obtaining the signature of a method (albeit in the context of its own module), so I simply get a pointer to the array, and the whole call is easy.

Action m = TestMethod; //The delegate I want to call
var sig = ReflectionTools.GetSignature(m.Method);

var ptr = Marshal.GetFunctionPointerForDelegate(m);

unsafe{
    fixed(byte* sigptr = sig)
    {
        var thunk = Marshal.GetManagedThunkForUnmanagedMethodPtr(ptr, (IntPtr)sigptr, sig.Length);
      
        var del = (Action)FormatterServices.GetUninitializedObject(typeof(Action));
        typeof(Delegate).GetField("_methodPtr", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(del, thunk);
        del();
    }



The second part of the code is a quick and ugly way of initializing a delegate directly from the method pointer. And guess what - it actually works. Although the resulting delegate can be only invoked and other actions produce an exception, it's not a problem, as I only wanted to see it it can be called by the function pointer. The only thing left is to see whether it is possible to emit a code that calls the function pointer:

public static Delegate WrapFunctionPointer(IntPtr fnPtr, MethodSignature signature)
{
    var dyn = new DynamicMethod("_FnPtrCall", signature.ReturnType, signature.ParameterTypes, typeof(Program).Module, true);
    var il = dyn.GetILGenerator();
    for(int i = 0; i < signature.ParameterTypes.Length; i++)
    {
        il.EmitLdarg(i);
    }
    if((ulong)fnPtr <= UInt32.MaxValue)
    {
        il.Emit(OpCodes.Ldc_I4, (uint)fnPtr);
    }else{
        il.Emit(OpCodes.Ldc_I8, (ulong)fnPtr);
    }
    il.Emit(OpCodes.Conv_I);
    il.EmitCalli(OpCodes.Calli, signature.CallingConvention, signature.ReturnType, signature.ParameterTypes, null/*signature.OptionalParameterTypes*/);
    il.Emit(OpCodes.Ret);
    var delType = ReflectionTools.GetDelegateType(signature.ReturnType, signature.ParameterTypes);
    return dyn.CreateDelegate(delType);
}


Ignore the MethodSignature type, it's just an another type in my library, and it simply stores information necessary to describe a method signature (types and calling convention). The calli opcode is the black magic that does the rest - it is the way to call a function pointer without having to specify the method, probably used heavily in C++/CLI code. ReflectionTools.GetDelegateType is another nice method from my library, but you can imagine typeof(Action)instead of it, in the context of a simple method.

So yes, it is possible to "bake" the target object in a delegate and then call it without specifying one, but having to step into unmanaged code in order to do it makes this way unfit for production code. What's nice though is that I have accurately recreated GetDelegateForFunctionPointer using GetManagedThunkForUnmanagedMethodPtr and WrapFunctionPointer and a bit of reflection hacks.

No comments:

Post a Comment