Example #1
0
        /// <summary>
        /// Implements an interface method in a duck proxy type using a given type builder.
        /// If successful, the implemented method will be added to the given proxy member dictionary.
        /// </summary>
        /// <param name="proxyType">Type builder for the duck proxy type.</param>
        /// <param name="proxyMembers">Dictionary of members of the proxy type.</param>
        /// <param name="duckField">Field that holds a reference to the duck object to forward calls to.</param>
        /// <param name="interfaceMethod">The interface method to implement.</param>
        private void ImplementMethod(TypeBuilder proxyType, ProxyMemberDictionary proxyMembers, FieldInfo duckField, MethodInfo interfaceMethod)
        {
            MethodInfo duckMethod = FindDuckMethod(interfaceMethod);

            if (duckMethod == null)
            {
                throw new NotImplementedException("Duck type does not implement a method named \"" + interfaceMethod.Name + "\" with the same parameters and return type.");
            }

            ParameterInfo[] interfaceMethodParameters = interfaceMethod.GetParameters();
            ParameterInfo[] duckMethodParameters      = duckMethod.GetParameters();

            MethodBuilder proxyMethod   = proxyType.DefineMethod(interfaceMethod.Name, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final, CallingConventions.HasThis, interfaceMethod.ReturnType, GetParameterTypes(interfaceMethodParameters));
            ILGenerator   proxyMethodIL = proxyMethod.GetILGenerator();

            // Emit IL to load the proxy instance, then load the value of its duck field
            proxyMethodIL.Emit(OpCodes.Ldarg_0);
            proxyMethodIL.Emit(OpCodes.Ldfld, duckField);

            // Emit IL to load method arguments
            for (int i = 0; i < interfaceMethodParameters.Length; i++)
            {
                // Emit IL to load the argument
                proxyMethodIL.Emit(OpCodes.Ldarg, i + 1);

                // Emit IL to cast the argument if necessary
                DuckTyping.EmitCastIL(proxyMethodIL, duckMethodParameters[i].ParameterType, interfaceMethodParameters[i].ParameterType);
            }

            // Emit IL to call the method
            proxyMethodIL.Emit(OpCodes.Callvirt, duckMethod);

            // If we are returning something...
            if (duckMethod.ReturnType != typeof(void))
            {
                // Emit IL to cast the return value if necessary
                DuckTyping.EmitCastIL(proxyMethodIL, interfaceMethod.ReturnType, duckMethod.ReturnType);
            }

            // Emit IL to return.
            proxyMethodIL.Emit(OpCodes.Ret);

            // Add proxy method to proxy member dictionary
            // (This is so that any associated properties or events can refer to it later)
            proxyMembers.Add(duckMethod, proxyMethod);
        }
Example #2
0
        /// <summary>
        /// Implements the method of the proxy type that invokes the delegate given in its constructor.
        /// </summary>
        /// <param name="proxyType">Type builder to define the invoke method in.</param>
        /// <param name="duckDelegateField">Field that stores a reference to the delegate to call.</param>
        /// <returns>The implemented invoke method.</returns>
        private MethodBuilder ImplementInvokeMethod(TypeBuilder proxyType, FieldInfo duckDelegateField)
        {
            MethodInfo variantMethod = m_ToDelegateType.GetMethod("Invoke");
            MethodInfo duckMethod    = m_FromDelegateType.GetMethod("Invoke");

            ParameterInfo[] variantMethodParameters = variantMethod.GetParameters();
            ParameterInfo[] duckMethodParameters    = duckMethod.GetParameters();

            MethodBuilder proxyMethod   = proxyType.DefineMethod("Invoke", MethodAttributes.Public | MethodAttributes.HideBySig, CallingConventions.HasThis, variantMethod.ReturnType, GetParameterTypes(variantMethodParameters));
            ILGenerator   proxyMethodIL = proxyMethod.GetILGenerator();

            // Emit IL to load the proxy instance, then load the value of its duck delegate field
            proxyMethodIL.Emit(OpCodes.Ldarg_0);
            proxyMethodIL.Emit(OpCodes.Ldfld, duckDelegateField);

            // Emit IL to load method arguments
            for (int i = 0; i < variantMethodParameters.Length; i++)
            {
                // Emit IL to load the argument
                proxyMethodIL.Emit(OpCodes.Ldarg, i + 1);

                // Emit IL to cast the argument if necessary
                DuckTyping.EmitCastIL(proxyMethodIL, duckMethodParameters[i].ParameterType, variantMethodParameters[i].ParameterType);
            }

            // Emit IL to call the delegate
            proxyMethodIL.Emit(OpCodes.Callvirt, duckMethod);

            // If we are returning something...
            if (duckMethod.ReturnType != typeof(void))
            {
                // Emit IL to cast the return value if necessary
                DuckTyping.EmitCastIL(proxyMethodIL, variantMethod.ReturnType, duckMethod.ReturnType);
            }

            // Emit IL to return.
            proxyMethodIL.Emit(OpCodes.Ret);

            return(proxyMethod);
        }
Example #3
0
        /// <summary>
        /// Implements an interface event in a duck proxy type using a given type builder.
        /// If successful, the implemented event will be added to the given proxy member dictionary.
        /// </summary>
        /// <param name="proxyType">Type builder for the duck proxy type.</param>
        /// <param name="proxyMembers">Dictionary of members of the proxy type.</param>
        /// <param name="duckField">Field that holds a reference to the duck object to forward calls to.</param>
        /// <param name="constructorIL">IL generator to use to add code to the constructor if necessary.</param>
        /// <param name="interfaceEvent">The interface event to implement.</param>
        private void ImplementEvent(TypeBuilder proxyType, ProxyMemberDictionary proxyMembers, FieldInfo duckField, ILGenerator constructorIL, EventInfo interfaceEvent)
        {
            EventInfo duckEvent = FindDuckEvent(interfaceEvent);

            if (duckEvent == null)
            {
                throw new NotImplementedException("Duck type does not implement an event named \"" + interfaceEvent.Name + "\" with the same event handler type.");
            }

            EventBuilder proxyEvent = proxyType.DefineEvent(interfaceEvent.Name, EventAttributes.None, interfaceEvent.EventHandlerType);

            Type interfaceEventHandlerType = interfaceEvent.EventHandlerType;
            Type duckEventHandlerType      = duckEvent.EventHandlerType;

            // Define event add method
            MethodBuilder addMethod   = proxyType.DefineMethod("add_" + interfaceEvent.Name, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final, CallingConventions.HasThis, typeof(void), new Type[] { interfaceEventHandlerType });
            ILGenerator   addMethodIL = addMethod.GetILGenerator();

            // Define event remove method
            MethodBuilder removeMethod   = proxyType.DefineMethod("remove_" + interfaceEvent.Name, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final, CallingConventions.HasThis, typeof(void), new Type[] { interfaceEventHandlerType });
            ILGenerator   removeMethodIL = removeMethod.GetILGenerator();

            if (interfaceEventHandlerType == duckEventHandlerType)
            {
                // If the event handler types are the same, we can just forward calls to add and remove like normal.

                MethodInfo duckAddMethod = duckEvent.GetAddMethod();
                if (!duckAddMethod.IsStatic)
                {
                    addMethodIL.Emit(OpCodes.Ldarg_0);
                    addMethodIL.Emit(OpCodes.Ldfld, duckField);
                    addMethodIL.Emit(OpCodes.Ldarg_1);
                    addMethodIL.Emit(OpCodes.Callvirt, duckAddMethod);
                }
                else
                {
                    addMethodIL.Emit(OpCodes.Ldarg_1);
                    addMethodIL.Emit(OpCodes.Call, duckAddMethod);
                }

                MethodInfo duckRemoveMethod = duckEvent.GetRemoveMethod();
                if (!duckRemoveMethod.IsStatic)
                {
                    removeMethodIL.Emit(OpCodes.Ldarg_0);
                    removeMethodIL.Emit(OpCodes.Ldfld, duckField);
                    removeMethodIL.Emit(OpCodes.Ldarg_1);
                    removeMethodIL.Emit(OpCodes.Callvirt, duckRemoveMethod);
                }
                else
                {
                    removeMethodIL.Emit(OpCodes.Ldarg_1);
                    removeMethodIL.Emit(OpCodes.Call, duckRemoveMethod);
                }
            }
            else
            {
                // If the event handler types are different, we need some special behavior.  Specifically, the event handlers
                // added using the interface are kept as a seperate multicast delegate in the proxy class and an event handler
                // is added to the duck that calls the proxy event delegate.

                // Define the event multicast delegate field
                FieldBuilder eventHandlerField = proxyType.DefineField(interfaceEvent.Name, interfaceEventHandlerType, FieldAttributes.Private);

                // Implement the add method
                addMethodIL.Emit(OpCodes.Ldarg_0);
                addMethodIL.Emit(OpCodes.Ldarg_0);
                addMethodIL.Emit(OpCodes.Ldfld, eventHandlerField);
                addMethodIL.Emit(OpCodes.Ldarg_1);
                addMethodIL.Emit(OpCodes.Call, typeof(Delegate).GetMethod("Combine", new Type[] { typeof(Delegate), typeof(Delegate) }));
                addMethodIL.Emit(OpCodes.Castclass, interfaceEventHandlerType);
                addMethodIL.Emit(OpCodes.Stfld, eventHandlerField);

                // Implement the remove method
                removeMethodIL.Emit(OpCodes.Ldarg_0);
                removeMethodIL.Emit(OpCodes.Ldarg_0);
                removeMethodIL.Emit(OpCodes.Ldfld, eventHandlerField);
                removeMethodIL.Emit(OpCodes.Ldarg_1);
                removeMethodIL.Emit(OpCodes.Call, typeof(Delegate).GetMethod("Remove", new Type[] { typeof(Delegate), typeof(Delegate) }));
                removeMethodIL.Emit(OpCodes.Castclass, interfaceEventHandlerType);
                removeMethodIL.Emit(OpCodes.Stfld, eventHandlerField);

                // Set some local variables for later use...
                MethodInfo interfaceInvokeMethod = interfaceEventHandlerType.GetMethod("Invoke");
                Type[]     interfaceInvokeMethodParameterTypes = GetParameterTypes(interfaceInvokeMethod.GetParameters());

                MethodInfo duckInvokeMethod = duckEventHandlerType.GetMethod("Invoke");
                Type[]     duckInvokeMethodParameterTypes = GetParameterTypes(duckInvokeMethod.GetParameters());


                // Define the method that will serve as an event handler to the duck which will invoke the proxy's delegate
                MethodBuilder proxyInvokeMethod   = proxyType.DefineMethod("Invoke" + interfaceEvent.Name, MethodAttributes.Private | MethodAttributes.Virtual | MethodAttributes.Final, CallingConventions.HasThis, duckInvokeMethod.ReturnType, duckInvokeMethodParameterTypes);
                ILGenerator   proxyInvokeMethodIL = proxyInvokeMethod.GetILGenerator();

                // First, check if the proxy event handler is null
                Label ifEventHandlerIsNullLabel = proxyInvokeMethodIL.DefineLabel();
                proxyInvokeMethodIL.Emit(OpCodes.Ldarg_0);
                proxyInvokeMethodIL.Emit(OpCodes.Ldfld, eventHandlerField);
                proxyInvokeMethodIL.Emit(OpCodes.Ldnull);
                proxyInvokeMethodIL.Emit(OpCodes.Ceq);
                proxyInvokeMethodIL.Emit(OpCodes.Brtrue_S, ifEventHandlerIsNullLabel);

                // If the proxy event handler is not null, invoke it
                proxyInvokeMethodIL.Emit(OpCodes.Ldarg_0);
                proxyInvokeMethodIL.Emit(OpCodes.Ldfld, eventHandlerField);
                for (int i = 0; i < interfaceInvokeMethodParameterTypes.Length; i++)
                {
                    proxyInvokeMethodIL.Emit(OpCodes.Ldarg, i + 1);
                    DuckTyping.EmitCastIL(proxyInvokeMethodIL, interfaceInvokeMethodParameterTypes[i], duckInvokeMethodParameterTypes[i]);
                }
                proxyInvokeMethodIL.Emit(OpCodes.Callvirt, interfaceInvokeMethod);

                // If the proxy event handler is null, execution jumps here
                proxyInvokeMethodIL.MarkLabel(ifEventHandlerIsNullLabel);

                // Return
                proxyInvokeMethodIL.Emit(OpCodes.Ret);


                // Add code to the constructor to add the event handler to the duck.
                MethodInfo duckAddMethod = duckEvent.GetAddMethod();
                if (!duckAddMethod.IsStatic)
                {
                    constructorIL.Emit(OpCodes.Ldarg_0);
                    constructorIL.Emit(OpCodes.Ldfld, duckField);
                }
                constructorIL.Emit(OpCodes.Ldarg_0);
                constructorIL.Emit(OpCodes.Ldftn, proxyInvokeMethod);
                constructorIL.Emit(OpCodes.Newobj, duckEventHandlerType.GetConstructor(new Type[] { typeof(object), typeof(IntPtr) }));
                if (!duckAddMethod.IsStatic)
                {
                    constructorIL.Emit(OpCodes.Callvirt, duckAddMethod);
                }
                else
                {
                    constructorIL.Emit(OpCodes.Call, duckAddMethod);
                }
            }

            // Finish add method and set it for the event
            addMethodIL.Emit(OpCodes.Ret);
            proxyEvent.SetAddOnMethod(addMethod);

            // Finish remove method and set it for the event
            removeMethodIL.Emit(OpCodes.Ret);
            proxyEvent.SetRemoveOnMethod(removeMethod);

            // Other associated methods appear before the event in the member array; thus, they have already been
            // defined and implemented.  The proxy member dictionary is used to refer to these.
            MethodInfo raiseMethod = duckEvent.GetRaiseMethod();

            if (raiseMethod != null)
            {
                proxyEvent.SetRaiseMethod((MethodBuilder)(proxyMembers[raiseMethod]));
            }

            MethodInfo[] otherDuckMethods = duckEvent.GetOtherMethods();
            foreach (MethodInfo otherDuckMethod in otherDuckMethods)
            {
                if (proxyMembers.ContainsKey(otherDuckMethod))
                {
                    proxyEvent.AddOtherMethod((MethodBuilder)(proxyMembers[otherDuckMethod]));
                }
            }

            // Add proxy event to the proxy member dictionary
            // (This is not really necessary, but good to keep things consistent)
            // if (proxyMembers != null) proxyMembers[duckEvent] =  proxyEvent;
            // For some reason, EventBuilder does not inherit MemberInfo, so it cannot be added.
        }
Example #4
0
        /// <summary>
        /// Implements an interface method in a duck proxy type using a given type builder.
        /// If successful, the implemented method will be added to the given proxy member dictionary.
        /// </summary>
        /// <param name="proxyType">Type builder for the duck proxy type.</param>
        /// <param name="proxyMembers">Dictionary of members of the proxy type.</param>
        /// <param name="duckField">Field that holds a reference to the duck object to forward calls to.</param>
        /// <param name="constructorIL">IL generator to use to add code to the constructor if necessary.</param>
        /// <param name="interfaceMethod">The interface method to implement.</param>
        private void ImplementMethod(TypeBuilder proxyType, ProxyMemberDictionary proxyMembers, FieldInfo duckField, ILGenerator constructorIL, MethodInfo interfaceMethod)
        {
            MethodInfo duckMethod = FindDuckMethod(interfaceMethod);

            if (duckMethod == null)
            {
                throw new NotImplementedException("Duck type does not implement a method named \"" + interfaceMethod.Name + "\" with compatible parameters and return type.");
            }

            if (!duckMethod.IsSpecialName || (!duckMethod.Name.StartsWith("add_") && !duckMethod.Name.StartsWith("remove_")))
            {
                ParameterInfo[] interfaceMethodParameters = interfaceMethod.GetParameters();
                ParameterInfo[] duckMethodParameters      = duckMethod.GetParameters();

                MethodBuilder proxyMethod = proxyType.DefineMethod(interfaceMethod.Name, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final, CallingConventions.HasThis, interfaceMethod.ReturnType, GetParameterTypes(interfaceMethodParameters));

                GenericTypeParameterBuilder[] genericParameters;

                if (interfaceMethod.IsGenericMethodDefinition)
                {
                    Type[]   interfaceMethodGenericArguments = interfaceMethod.GetGenericArguments();
                    string[] genericParameterNames           = new string[interfaceMethodGenericArguments.Length];
                    for (int i = 0; i < interfaceMethodGenericArguments.Length; i++)
                    {
                        genericParameterNames[i] = interfaceMethodGenericArguments[i].Name;
                    }
                    genericParameters = proxyMethod.DefineGenericParameters(genericParameterNames);
                }
                else
                {
                    genericParameters = new GenericTypeParameterBuilder[0];
                }

                ILGenerator proxyMethodIL = proxyMethod.GetILGenerator();

                // Emit IL to load the duck object if the method is not static
                if (!duckMethod.IsStatic)
                {
                    // Emit IL to load the proxy instance, then load the value of its duck field
                    proxyMethodIL.Emit(OpCodes.Ldarg_0);
                    proxyMethodIL.Emit(OpCodes.Ldfld, duckField);
                }

                // Emit IL to load method arguments
                for (int i = 0; i < interfaceMethodParameters.Length; i++)
                {
                    // Emit IL to load the argument
                    proxyMethodIL.Emit(OpCodes.Ldarg, 1 + i);

                    // Emit IL to cast the argument if necessary
                    DuckTyping.EmitCastIL(proxyMethodIL, duckMethodParameters[i].ParameterType, interfaceMethodParameters[i].ParameterType);
                }

                MethodInfo methodToCall;
                if (!duckMethod.IsGenericMethodDefinition)
                {
                    methodToCall = duckMethod;
                }
                else
                {
                    methodToCall = duckMethod.MakeGenericMethod((Type[])(genericParameters));
                }

                // Emit IL to call the method
                if (!duckMethod.IsStatic)
                {
                    proxyMethodIL.Emit(OpCodes.Callvirt, methodToCall);
                }
                else
                {
                    proxyMethodIL.Emit(OpCodes.Call, methodToCall);
                }

                // If we are returning something...
                if (duckMethod.ReturnType != typeof(void))
                {
                    // Emit IL to cast the return value if necessary
                    DuckTyping.EmitCastIL(proxyMethodIL, interfaceMethod.ReturnType, duckMethod.ReturnType);
                }

                // Emit IL to return.
                proxyMethodIL.Emit(OpCodes.Ret);

                // Add proxy method to proxy member dictionary
                // (This is so that any associated properties or events can refer to it later)
                if (proxyMembers != null)
                {
                    proxyMembers[duckMethod] = proxyMethod;
                }
            }
        }