Olson.dev
Olson.dev

Reputation: 1836

What's wrong with the IL I'm emitting in a Reflection.Emit.DynamicMethod?

Ideally I want to be using the CreateObject delegate because the code generating these dynamic methods is for a deserializer that should be able to handle any type (at least, primitives, structs, & class instances). However, I ran into a problem with the CreateObject delegate type so I decided to try the CreateRectangle delegate to debug things. I got a little closer to a working solution but something else isn't right. What's wrong with my code for both cases? That is, how can I make the dynamic method work for both CreateObject and CreateRectangle? Or, is my calling code the culprit?

Output:

{X=0,Y=0,Width=0,Height=0}

Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type.

Exception has been thrown by the target of an invocation.
    Common Language Runtime detected an invalid program.

Code:

using System;
using System.Drawing;
using System.Reflection.Emit;

namespace Experiments {
    public class Program {
        private delegate object CreateObject();
        private delegate Rectangle CreateRectangle();

        static void Main() {
            Console.WriteLine(new Rectangle());
            Console.WriteLine();
            var dm = BuildDynamicMethod();
            TryCreateDelegate<CreateObject>(dm);
            Console.WriteLine();
            TryCreateDelegate<CreateRectangle>(dm);
            Console.WriteLine();
            Console.ReadKey();
        }

        private static void TryCreateDelegate<T>(DynamicMethod dm) {
            try {
                var co = dm.CreateDelegate(typeof (T));
                var value = co.DynamicInvoke(null);
                Console.WriteLine(value);
            } catch (Exception ex) {
                Console.WriteLine(ex.Message);
                var indent = 0;
                while (ex.InnerException != null) {
                    indent++;
                    ex = ex.InnerException;
                    Console.WriteLine(new string('\t', indent) + ex.Message);
                }
            }
        }

        private static DynamicMethod BuildDynamicMethod() {
            var tr = typeof(Rectangle);
            var dm = new DynamicMethod("buildNewRectangle", tr, Type.EmptyTypes);
            var il = dm.GetILGenerator();
            il.Emit(OpCodes.Ldloca_S, (byte)0);
            il.Emit(OpCodes.Initobj, tr);
            il.Emit(OpCodes.Ldloc_0);
            il.Emit(OpCodes.Box, tr);
            il.Emit(OpCodes.Ret);
            return dm;
        }
    }
}

Upvotes: 3

Views: 1213

Answers (2)

Serj-Tm
Serj-Tm

Reputation: 16981

private delegate object CreateObject();
private delegate Rectangle CreateRectangle();

static void Main()
{
  Console.WriteLine(new Rectangle());
  Console.WriteLine();
  TryCreateDelegate<CreateObject>(BuildDynamicMethod_Boxed());
  Console.WriteLine();
  TryCreateDelegate<CreateRectangle>(BuildDynamicMethod());
  Console.WriteLine();
}

private static DynamicMethod BuildDynamicMethod_Boxed()
{
  var TRect = typeof(Rectangle);
  var dm = new DynamicMethod("buildNewRectangle", typeof(object), Type.EmptyTypes);
  var il = dm.GetILGenerator();
  il.Emit(OpCodes.Ldloca_S, il.DeclareLocal(TRect));
  il.Emit(OpCodes.Initobj, TRect);
  il.Emit(OpCodes.Ldloc_0);      
  il.Emit(OpCodes.Box, TRect);
  il.Emit(OpCodes.Ret);
  return dm;
}

private static DynamicMethod BuildDynamicMethod()
{
  var TRect = typeof(Rectangle);
  var dm = new DynamicMethod("buildNewRectangle", TRect, Type.EmptyTypes);
  var il = dm.GetILGenerator();
  il.Emit(OpCodes.Ldloca_S, il.DeclareLocal(TRect));
  il.Emit(OpCodes.Initobj, TRect);
  il.Emit(OpCodes.Ldloc_0);
  il.Emit(OpCodes.Ret);
  return dm;
}

private static void TryCreateDelegate<T>(DynamicMethod dm)
{
  try
  {
    var co = dm.CreateDelegate(typeof(T));
    var value = co.DynamicInvoke();
  }
  catch (Exception ex)
  {
    Console.WriteLine(ex.Message);
    var indent = 0;
    while (ex.InnerException != null)
    {
      indent++;
      ex = ex.InnerException;
      Console.WriteLine(new string('\t', indent) + ex.Message);
    }
  }
}

Upvotes: 3

Bryce Wagner
Bryce Wagner

Reputation: 2650

I've found the easiest way to understand what is valid ILCode is to start by writing C# code, compile it, and then try reading the decompiled ILCode. It can be very informative.

But it looks like you're trying to reference local variables without declaring address space for any. LdLoca_S is referencing a local variable, and before you can do that, you need to call .DeclareLocal(tr). Not sure if that's the only problem, but it's the most obvious one.

Edit: OK, I ran it myself, and it really does work once you add the .DeclareLocal(tr) in there, but there was also an issue with the method signature.

But you're trying to call it with two different method signatures. If you return object, you need to box it, but if you return Rectangle, you can't box it. But your return type is hardcoded to typeof(Rectangle). So you're trying to box a Rectangle and then return the boxed rectangle as a rectangle struct. Either don't box it, or change your return type.

Upvotes: 3

Related Questions