/// <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); }
/// <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); }
/// <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. }
/// <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; } } }