Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reflection.emit System.InvalidProgramException: Common Language Runtime detected an invalid program

I'm new to reflection.emit and have been trying to produce the following c# code:

public class RepositoryWrapper
{
    public void CallRepositoryMethod(IAddressRepository repository, Address address)
    {
        repository.NODE_I_NodeExtendedDetails_Address3(address.NodeId);
    }
}

Here is the il Representation of it:

    IL_0000: nop
    IL_0001: ldarg.1
    IL_0002: ldarg.2
    IL_0003: callvirt instance int32 ReflectionServices.Node::get_NodeId()
    IL_0008: callvirt instance void ReflectionServices.IAddressRepository::NODE_I_NodeExtendedDetails_Address3(int32)
    IL_000d: nop
    IL_000e: ret

And here is my code used to create it:

 internal static void Generate(this System.Reflection.Emit.ILGenerator @this, Type target,string method,Type instance)
        {

        var methodToCall = target.GetMethod(method);
        var methodParams = methodToCall.GetParameters();
        var instanceProperties = instance.GetProperties(BindingFlags.Public | BindingFlags.Instance);

        var orderedProperties = (from mp in methodParams
                              join p in instanceProperties
                              on mp.Name.ToLower() equals p.Name.ToLower()
                              select p).ToArray();

        //add properties to the string builder

        //load the object reference onto the stack sothat we can access its methods
        @this.Emit(OpCodes.Nop);
        @this.Emit(OpCodes.Ldarg_1);
        @this.Emit(OpCodes.Ldarg_2);

            var property = orderedProperties.FirstOrDefault(x => x.Name == "NodeId");
            if (property != null)
            {
                var getMethod = property.GetGetMethod();
                @this.Emit(getMethod.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, getMethod);

            }


        //call method 
            @this.Emit(methodToCall.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, methodToCall);
            @this.Emit(OpCodes.Nop);
        //return from function
        @this.Emit(OpCodes.Ret);
        }

Here is the error I am getting:

System.InvalidProgramException: Common Language Runtime detected an invalid program.
Result StackTrace:  
at ReflectionServices.Repository.NODE_I_NodeExtendedDetails3_Address40807399(Repository      target, Address )

here is the generated il:

nop
ldarg.1
ldarg.2
call instance int32 ReflectionServices.Node::get_NodeId()
callvirt instance void            
ReflectionServices.Repository::
                               NODE_I_NodeExtendedDetails3_Address(int32)
nop

ret

Can anybody see what the issue is i'm stuck?

thanks

here is my dll and method as requested:

 public sealed class ReflectionEmitWithDebuggingMethodGenerator
{
    private AssemblyBuilder Assembly { get; set; }
    private ModuleBuilder Module { get; set; }
    private AssemblyName Name { get; set; }

    public ReflectionEmitWithDebuggingMethodGenerator()
        : base()
    {
        this.Name = new AssemblyName() { Name = Guid.NewGuid().ToString("N") };
        this.Assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(
            this.Name, AssemblyBuilderAccess.RunAndSave,@"C:\Users\darren\Documents\Visual Studio 2012\Projects\UnityInjection");
        this.AddDebuggingAttribute(this.Assembly);
        this.Module = this.Assembly.DefineDynamicModule(this.Name.Name + ".dll", true);
    }
   public Action<TObject, TInstance> Generate<TObject, TInstance>(Type target, string methodN, Type instanceType)
    {
        var type = this.Module.DefineType(target.Namespace + "." + target.Name);
        var methodName = methodN + target.GetHashCode().ToString();
        var method = type.DefineMethod(methodName, MethodAttributes.Static | MethodAttributes.Public, typeof(void), new Type[] { target, instanceType });
        method.DefineParameter(1, ParameterAttributes.In, "target");
        method.DefineParameter(2, ParameterAttributes.In, "instance");

        ILGenerator.Generate(method.GetILGenerator(), target,methodN,instanceType);

        var createdType = type.CreateType();

        var createdMethod = createdType.GetMethod(methodName);
        return (Action<TObject, TInstance>)Delegate.CreateDelegate(typeof(Action<TObject, TInstance>), createdMethod);
    }


}
like image 583
Code Junkie Avatar asked Oct 22 '22 08:10

Code Junkie


1 Answers

Comparing the compiled and the emitted output, there's only one difference:

Compiled:

callvirt instance int32 ReflectionServices.Node::get_NodeId()

Emitted:

call instance int32 ReflectionServices.Node::get_NodeId()

The type you're calling int32 get_NodeId() on is ReflectionServices.Node, but the type of the object you're passing into the method you're trying to replicate is Address. This leads me to believe that the property accessor defined on ReflectionServices.Node must be called virtually, perhaps because it inherits from another class (or implements an interface) that declares that property before ReflectionServices.Node implements it.

When you're emitting that line of code, simply call it virtually:

@this.Emit(OpCodes.Callvirt, getMethod);

EDIT: In light of the further provided code, here's the real solution.

So, you have a problem with the fundamentals of how you're implementing the interface:

var method = type.DefineMethod(methodName, MethodAttributes.Static | Method...
//                                                          ^^^^^^

Interface methods are not static; they're instance members. Thus, you first need to remove MethodAttributes.Static from the attributes flag upon creating the MethodBuilder.

Second, when you go to return this function, you're going to have to include a target object, which is the instance upon which the method is called. To do that, you can use Activator.CreateInstance to call the default-generated constructor and give you an instantiated object to use as the target. Replace the final line of your Generate method with these lines to achieve that.

var activatedObject = Activator.CreateInstance(type);

return (Action<TObject, TInstance>)Delegate.CreateDelegate(
    typeof(Action<TObject, TInstance>), activatedObject, createdMethod);
like image 114
Adam Maras Avatar answered Nov 03 '22 17:11

Adam Maras