Alecu
Alecu

Reputation: 2708

C# Reflection Emit Invalid program exception when implementing generic interface

I am implementing a dynamic proxy class that implements an interface and exposes the methods inside that interface giving the user the option to call the method on the original object.

The proxy class contains implementations of the interface methods. The thing is that when I try to call any methods from the proxy class I get an Invalid Program exception. I tried calling even basic methods like Clear() that don't have any kind of arguments and it didn't work.

I don't know what I am doing wrong, I checked the code 10 times, when I implement the generic interface, I create a realized generic interface type from it with the generic type arguments from my generic proxy class like bellow:

        if (interfaceType.IsGenericTypeDefinition)
        {
            if (!wrappedType.IsGenericTypeDefinition)
            {
                throw new InvalidOperationException("Wrapped type is not an open generic type whereas the interface is");
            }
            var interfaceGenericArguments = interfaceType.GetGenericArguments();
            serviceInterceptorGenericParameters = serviceInterceptorTypeBuilder.DefineGenericParameters(interfaceGenericArguments.Select(arg => $"{arg.Name}W").ToArray());
            serviceInterceptorGenericParametersMap = new Dictionary<Type, GeneraticParameterTypeWithInitialization>();
            wrappedTypeGenericParametersMap = new Dictionary<Type, GeneraticParameterTypeWithInitialization>();
            for (int i = 0; i < serviceInterceptorGenericParameters.Length; i++)
            {
                serviceInterceptorGenericParametersMap[interfaceGenericArguments[i]] = new GeneraticParameterTypeWithInitialization { GenericTypeParameterBuilder = serviceInterceptorGenericParameters[i] };
            }
            var wrappedTypeGenericArguments = wrappedType.GetGenericArguments();
            for (int i = 0; i < wrappedTypeGenericArguments.Length; i++)
            {
                wrappedTypeGenericParametersMap[wrappedTypeGenericArguments[i]] = new GeneraticParameterTypeWithInitialization { GenericTypeParameterBuilder = serviceInterceptorGenericParameters[i] };
            }
            serviceInterceptorTypeBuilder.AddInterfaceImplementation(ReplaceGenericArgumentsAndConstraintsFromType(interfaceType, serviceInterceptorGenericParametersMap));
        }
        else
        {
            serviceInterceptorTypeBuilder.AddInterfaceImplementation(interfaceType);
        }

And when I generate the methods I generate them like this:

var arrayObjectConstructor = arrayObject.GetConstructors().First();
var arrayObjectSetValueMethod = arrayObject.GetMethod("SetValue", new Type[] { typeof(object), typeof(int) }) ?? throw new InvalidOperationException("Could not get array object set value method");
var invokeMethod = typeof(ServiceInterceptor).GetMethod(nameof(Invoke), BindingFlags.Instance | BindingFlags.NonPublic) ?? throw new InvalidOperationException("Could not get current method");
var getCurrentMethod = typeof(MethodBase).GetMethod(nameof(MethodBase.GetCurrentMethod), BindingFlags.Static | BindingFlags.Public) ?? throw new InvalidOperationException("Could not get current method");
var interfaceMethods = GetAllInterfaceMethods(interfaceType);
var objectGetTypeMethod = typeof(ServiceInterceptor).GetType().GetMethod(nameof(GetType), BindingFlags.Public | BindingFlags.Instance) ?? throw new InvalidOperationException("Could not get get type object method");
var objectTypeIsValueTypeGetMethod = typeof(ServiceInterceptor).GetType().GetProperty(nameof(Type.IsValueType), BindingFlags.Public | BindingFlags.Instance)?.GetMethod ?? throw new InvalidOperationException("Could not get the method is value type");

foreach (var interfaceMethod in interfaceMethods)
{
    var interfaceMethodTypeBuilder = serviceInterceptorTypeBuilder.DefineMethod(interfaceMethod.Name, interfaceMethod.Attributes & ~MethodAttributes.Abstract & ~MethodAttributes.NewSlot, interfaceMethod.CallingConvention);
    builtMethods.Add(interfaceMethodTypeBuilder);
    var interfaceMethodArgumentsType = new List<Type>();
    var interfaceMethodGenericArguments = interfaceMethod.GetGenericArguments();
    var interfaceMethodGenericParametersMap = new Dictionary<Type, GeneraticParameterTypeWithInitialization>();
    if (serviceInterceptorGenericParametersMap != null && serviceInterceptorGenericParametersMap.Any())
    {
        foreach (var parameterMap in serviceInterceptorGenericParametersMap)
        {
            interfaceMethodGenericParametersMap[parameterMap.Key] = parameterMap.Value;
        }
    }
    if (interfaceMethodGenericArguments.Any())
    {
        var genericMethodParameters = interfaceMethodTypeBuilder.DefineGenericParameters(interfaceMethodGenericArguments.Select(arg => arg.Name).ToArray());
        for (int i = 0; i < genericMethodParameters.Length; i++)
        {
            interfaceMethodGenericParametersMap[interfaceMethodGenericArguments[i]] = new GeneraticParameterTypeWithInitialization { GenericTypeParameterBuilder = genericMethodParameters[i] };
        }
        foreach (var interfaceMethodGenericArgument in interfaceMethodGenericArguments)
        {
            foreach (var contraint in interfaceMethodGenericArgument.GetGenericParameterConstraints())
            {
                var replacedConstraint = ReplaceGenericArgumentsAndConstraintsFromType(contraint, interfaceMethodGenericParametersMap);
                if (replacedConstraint.IsInterface)
                {
                    interfaceMethodGenericParametersMap[interfaceMethodGenericArgument].GenericTypeParameterBuilder.SetInterfaceConstraints(replacedConstraint);
                    continue;
                }
                interfaceMethodGenericParametersMap[interfaceMethodGenericArgument].GenericTypeParameterBuilder.SetBaseTypeConstraint(replacedConstraint);
                interfaceMethodGenericParametersMap[interfaceMethodGenericArgument].GenericTypeParameterBuilder.SetGenericParameterAttributes(contraint.GenericParameterAttributes & ~GenericParameterAttributes.Covariant & ~GenericParameterAttributes.Contravariant);
            }
        }
    }
    var interfaceParameters = interfaceMethod.GetParameters();
    interfaceMethodTypeBuilder.SetParameters(interfaceParameters.Select(x => ReplaceGenericArgumentsFromType(x.ParameterType, serviceInterceptorGenericParametersMap)).ToArray());
    interfaceMethodTypeBuilder.SetReturnType(ReplaceGenericArgumentsFromType(interfaceMethod.ReturnType, serviceInterceptorGenericParametersMap));

    var ilGenerator = interfaceMethodTypeBuilder.GetILGenerator();
    var localOjectParamList = ilGenerator.DeclareLocal(typeof(object[]));
    var localObjectParam = ilGenerator.DeclareLocal(typeof(object));
    var localMethodInfo = ilGenerator.DeclareLocal(typeof(MethodInfo));
    ilGenerator.EmitWriteLine("Calling generated interface method");
    ilGenerator.Emit(OpCodes.Call, getCurrentMethod);
    ilGenerator.Emit(OpCodes.Castclass, typeof(MethodInfo));
    ilGenerator.Emit(OpCodes.Stloc, localMethodInfo);
    if (!interfaceMethod.GetParameters().Any())
    {
        ilGenerator.Emit(OpCodes.Ldarg_0);
        ilGenerator.Emit(OpCodes.Ldloc, localMethodInfo);
        ilGenerator.Emit(OpCodes.Ldnull);
        ilGenerator.Emit(OpCodes.Callvirt, invokeMethod);
        ilGenerator.Emit(OpCodes.Ret);
        continue;
    }

    ilGenerator.Emit(OpCodes.Ldc_I4, interfaceParameters.Length);
    ilGenerator.Emit(OpCodes.Call, arrayObjectConstructor);
    ilGenerator.Emit(OpCodes.Stloc, localOjectParamList);
    var loadArgumentLabel = ilGenerator.DefineLabel();
    var loadNextArgumentLabel = ilGenerator.DefineLabel();
    if (interfaceParameters.Length >= 1)
    {
        ilGenerator.Emit(OpCodes.Ldarg_1);
        ilGenerator.Emit(OpCodes.Brfalse, loadArgumentLabel);
        ilGenerator.Emit(OpCodes.Ldarg_1);
        ilGenerator.Emit(OpCodes.Callvirt, objectGetTypeMethod);
        ilGenerator.Emit(OpCodes.Callvirt, objectTypeIsValueTypeGetMethod);
        ilGenerator.Emit(OpCodes.Ldc_I4_0);
        ilGenerator.Emit(OpCodes.Beq, loadArgumentLabel);
        ilGenerator.Emit(OpCodes.Ldarg_1);
        ilGenerator.Emit(OpCodes.Box);
        ilGenerator.Emit(OpCodes.Stloc, localObjectParam);
        ilGenerator.Emit(OpCodes.Ldloc, localOjectParamList);
        ilGenerator.Emit(OpCodes.Ldloc, localObjectParam);
        ilGenerator.Emit(OpCodes.Ldind_I4, 0);
        ilGenerator.Emit(OpCodes.Callvirt, arrayObjectSetValueMethod);
        ilGenerator.Emit(OpCodes.Br, loadNextArgumentLabel);
        ilGenerator.MarkLabel(loadArgumentLabel);
        ilGenerator.Emit(OpCodes.Ldloc, localOjectParamList);
        ilGenerator.Emit(OpCodes.Castclass, typeof(object));
        ilGenerator.Emit(OpCodes.Ldind_I4, 0);
        ilGenerator.Emit(OpCodes.Ldarg_1);
        ilGenerator.Emit(OpCodes.Callvirt, arrayObjectSetValueMethod);
    }

    ilGenerator.MarkLabel(loadNextArgumentLabel);

    if (interfaceParameters.Length >= 2)
    {
        loadArgumentLabel = ilGenerator.DefineLabel();
        loadNextArgumentLabel = ilGenerator.DefineLabel();

        ilGenerator.Emit(OpCodes.Ldarg_2);
        ilGenerator.Emit(OpCodes.Brfalse, loadArgumentLabel);
        ilGenerator.Emit(OpCodes.Ldarg_2);
        ilGenerator.Emit(OpCodes.Callvirt, objectGetTypeMethod);
        ilGenerator.Emit(OpCodes.Callvirt, objectTypeIsValueTypeGetMethod);
        ilGenerator.Emit(OpCodes.Brfalse, loadArgumentLabel);
        ilGenerator.Emit(OpCodes.Ldarg_2);
        ilGenerator.Emit(OpCodes.Box);
        ilGenerator.Emit(OpCodes.Stloc, localObjectParam);
        ilGenerator.Emit(OpCodes.Ldloc, localOjectParamList);
        ilGenerator.Emit(OpCodes.Ldloc, localObjectParam);
        ilGenerator.Emit(OpCodes.Ldind_I4, 1);
        ilGenerator.Emit(OpCodes.Callvirt, arrayObjectSetValueMethod);
        ilGenerator.Emit(OpCodes.Br, loadNextArgumentLabel);
        ilGenerator.MarkLabel(loadArgumentLabel);
        ilGenerator.Emit(OpCodes.Ldloc, localOjectParamList);
        ilGenerator.Emit(OpCodes.Ldarg_2);
        ilGenerator.Emit(OpCodes.Castclass, typeof(object));
        ilGenerator.Emit(OpCodes.Ldind_I4, 1);
        ilGenerator.Emit(OpCodes.Callvirt, arrayObjectSetValueMethod);

        ilGenerator.MarkLabel(loadNextArgumentLabel);
    }

    if (interfaceParameters.Length >= 3)
    {
        loadArgumentLabel = ilGenerator.DefineLabel();
        loadNextArgumentLabel = ilGenerator.DefineLabel();

        ilGenerator.Emit(OpCodes.Ldarg_3);
        ilGenerator.Emit(OpCodes.Brfalse, loadArgumentLabel);
        ilGenerator.Emit(OpCodes.Ldarg_3);
        ilGenerator.Emit(OpCodes.Callvirt, objectGetTypeMethod);
        ilGenerator.Emit(OpCodes.Callvirt, objectTypeIsValueTypeGetMethod);
        ilGenerator.Emit(OpCodes.Brfalse, loadArgumentLabel);
        ilGenerator.Emit(OpCodes.Ldarg_3);
        ilGenerator.Emit(OpCodes.Box);
        ilGenerator.Emit(OpCodes.Stloc, localObjectParam);
        ilGenerator.Emit(OpCodes.Ldloc, localOjectParamList);
        ilGenerator.Emit(OpCodes.Ldloc, localObjectParam);
        ilGenerator.Emit(OpCodes.Ldind_I4, 2);
        ilGenerator.Emit(OpCodes.Callvirt, arrayObjectSetValueMethod);
        ilGenerator.Emit(OpCodes.Br, loadNextArgumentLabel);
        ilGenerator.MarkLabel(loadArgumentLabel);
        ilGenerator.Emit(OpCodes.Ldloc, localOjectParamList);
        ilGenerator.Emit(OpCodes.Ldarg_3);
        ilGenerator.Emit(OpCodes.Castclass, typeof(object));
        ilGenerator.Emit(OpCodes.Ldind_I4, 2);
        ilGenerator.Emit(OpCodes.Callvirt, arrayObjectSetValueMethod);

        ilGenerator.MarkLabel(loadNextArgumentLabel);
    }

    for (short parameterIndex = 3; parameterIndex < interfaceParameters.Length; parameterIndex++)
    {
        loadArgumentLabel = ilGenerator.DefineLabel();
        loadNextArgumentLabel = ilGenerator.DefineLabel();

        ilGenerator.Emit(OpCodes.Ldarg, (short)(parameterIndex + 1));
        ilGenerator.Emit(OpCodes.Brfalse, loadArgumentLabel);
        ilGenerator.Emit(OpCodes.Ldarg, (short)(parameterIndex + 1));
        ilGenerator.Emit(OpCodes.Callvirt, objectGetTypeMethod);
        ilGenerator.Emit(OpCodes.Callvirt, objectTypeIsValueTypeGetMethod);
        ilGenerator.Emit(OpCodes.Brfalse, loadArgumentLabel);
        ilGenerator.Emit(OpCodes.Ldarg, (short)(parameterIndex + 1));
        ilGenerator.Emit(OpCodes.Box);
        ilGenerator.Emit(OpCodes.Stloc, localObjectParam);
        ilGenerator.Emit(OpCodes.Ldloc, localOjectParamList);
        ilGenerator.Emit(OpCodes.Ldloc, localObjectParam);
        ilGenerator.Emit(OpCodes.Ldind_I4, (int)parameterIndex);
        ilGenerator.Emit(OpCodes.Callvirt, arrayObjectSetValueMethod);
        ilGenerator.Emit(OpCodes.Br, loadNextArgumentLabel);
        ilGenerator.MarkLabel(loadArgumentLabel);
        ilGenerator.Emit(OpCodes.Ldloc, localOjectParamList);
        ilGenerator.Emit(OpCodes.Ldarg, (short)(parameterIndex + 1));
        ilGenerator.Emit(OpCodes.Castclass, typeof(object));
        ilGenerator.Emit(OpCodes.Ldind_I4, (int)parameterIndex);
        ilGenerator.Emit(OpCodes.Callvirt, arrayObjectSetValueMethod);

        ilGenerator.MarkLabel(loadNextArgumentLabel);
    }

    ilGenerator.Emit(OpCodes.Ldarg_0);
    ilGenerator.Emit(OpCodes.Ldloc, localMethodInfo);
    ilGenerator.Emit(OpCodes.Ldloc, localOjectParamList);
    ilGenerator.Emit(OpCodes.Callvirt, invokeMethod);
    ilGenerator.Emit(OpCodes.Ret);
}

I don't get any kind of errors when creating the dynamic type and it even calls the constructor of the dynamic type. It's just that I can't call the interface methods. I tried to define an override for the interface method but I get an exception that the method from the interface is already overwriten.

Upvotes: 0

Views: 39

Answers (0)

Related Questions