/// <summary> /// Implements the add or remove accessor of the event. /// </summary> /// <param name="isAdd">true to create an 'add' method; false to create a 'remove' method.</param> /// <param name="evnt">Event to implement.</param> /// <param name="backingField">Multicast delegate field backing the event.</param> private static void ImplementAccessor(bool isAdd, GeneratedEvent evnt, IGeneratedField backingField) { Type backingFieldType = backingField.FieldBuilder.FieldType; // get the Delegate.Combine() method when adding a handler and Delegate.Remove() when removing a handler MethodInfo delegateMethod = typeof(Delegate).GetMethod(isAdd ? "Combine" : "Remove", new[] { typeof(Delegate), typeof(Delegate) }); // get the System.Threading.Interlocked.CompareExchange(ref object, object, object) method MethodInfo interlockedCompareExchangeGenericMethod = typeof(Interlocked) .GetMethods() .Single(m => m.Name == "CompareExchange" && m.GetGenericArguments().Length == 1); MethodInfo interlockedCompareExchangeMethod = interlockedCompareExchangeGenericMethod.MakeGenericMethod(backingFieldType); // emit code to combine the handler with the multicast delegate in the backing field respectively remove the handler from it ILGenerator msil = isAdd ? evnt.AddAccessor.MethodBuilder.GetILGenerator() : evnt.RemoveAccessor.MethodBuilder.GetILGenerator(); LocalBuilder local0 = msil.DeclareLocal(backingFieldType); LocalBuilder local1 = msil.DeclareLocal(backingFieldType); LocalBuilder local2 = msil.DeclareLocal(backingFieldType); Label retryLabel = msil.DefineLabel(); if (evnt.Kind == EventKind.Static) { msil.Emit(OpCodes.Ldsfld, backingField.FieldBuilder); msil.Emit(OpCodes.Stloc_0); msil.MarkLabel(retryLabel); msil.Emit(OpCodes.Ldloc_0); msil.Emit(OpCodes.Stloc_1); msil.Emit(OpCodes.Ldloc_1); msil.Emit(OpCodes.Ldarg_0); msil.EmitCall(OpCodes.Call, delegateMethod, null); msil.Emit(OpCodes.Castclass, backingFieldType); msil.Emit(OpCodes.Stloc_2); msil.Emit(OpCodes.Ldsflda, backingField.FieldBuilder); msil.Emit(OpCodes.Ldloc_2); msil.Emit(OpCodes.Ldloc_1); msil.Emit(OpCodes.Call, interlockedCompareExchangeMethod); msil.Emit(OpCodes.Stloc_0); msil.Emit(OpCodes.Ldloc_0); msil.Emit(OpCodes.Ldloc_1); msil.Emit(OpCodes.Bne_Un_S, retryLabel); msil.Emit(OpCodes.Ret); } else { msil.Emit(OpCodes.Ldarg_0); msil.Emit(OpCodes.Ldfld, backingField.FieldBuilder); msil.Emit(OpCodes.Stloc_0); msil.MarkLabel(retryLabel); msil.Emit(OpCodes.Ldloc_0); msil.Emit(OpCodes.Stloc_1); msil.Emit(OpCodes.Ldloc_1); msil.Emit(OpCodes.Ldarg_1); msil.EmitCall(OpCodes.Call, delegateMethod, null); msil.Emit(OpCodes.Castclass, backingFieldType); msil.Emit(OpCodes.Stloc_2); msil.Emit(OpCodes.Ldarg_0); msil.Emit(OpCodes.Ldflda, backingField.FieldBuilder); msil.Emit(OpCodes.Ldloc_2); msil.Emit(OpCodes.Ldloc_1); msil.Emit(OpCodes.Call, interlockedCompareExchangeMethod); msil.Emit(OpCodes.Stloc_0); msil.Emit(OpCodes.Ldloc_0); msil.Emit(OpCodes.Ldloc_1); msil.Emit(OpCodes.Bne_Un_S, retryLabel); msil.Emit(OpCodes.Ret); } }
/// <summary> /// Reviews the default declaration of the event and adds additional type declarations, if necessary. /// </summary> /// <param name="engine">The <see cref="CodeGenEngine"/> assembling the type in creation.</param> /// <param name="evnt">The event to review.</param> public void Declare(CodeGenEngine engine, GeneratedEvent evnt) { // declare the backing field storing event handlers mBackingField = engine.AddField(evnt.Type, null, evnt.Kind == EventKind.Static, evnt.Visibility, null); }
/// <summary> /// Implements the event. /// </summary> /// <param name="engine">The <see cref="CodeGenEngine"/> assembling the type in creation.</param> /// <param name="evnt">The event to implement.</param> public void Implement(CodeGenEngine engine, GeneratedEvent evnt) { ImplementAccessor(true, evnt, mBackingField); ImplementAccessor(false, evnt, mBackingField); ImplementEventRaiser(evnt, mBackingField); }
/// <summary> /// Adds the event raiser method. /// </summary> /// <param name="evnt">Event to implement.</param> /// <param name="backingField">Multicast delegate field backing the event.</param> private static void ImplementEventRaiser(GeneratedEvent evnt, IGeneratedField backingField) { ILGenerator msil = evnt.Raiser.MethodBuilder.GetILGenerator(); if (evnt.Type == typeof(EventHandler) || evnt.Type == typeof(EventHandler <EventArgs>)) { // System.EventHandler // System.EventHandler<EventArgs> FieldInfo EventArgsEmpty = typeof(EventArgs).GetField("Empty"); LocalBuilder handlerLocalBuilder = msil.DeclareLocal(backingField.FieldBuilder.FieldType); Label label = msil.DefineLabel(); if (evnt.Kind == EventKind.Static) { msil.Emit(OpCodes.Ldsfld, backingField.FieldBuilder); msil.Emit(OpCodes.Stloc, handlerLocalBuilder); msil.Emit(OpCodes.Ldloc, handlerLocalBuilder); msil.Emit(OpCodes.Brfalse_S, label); msil.Emit(OpCodes.Ldloc, handlerLocalBuilder); msil.Emit(OpCodes.Ldnull); // load sender (null) msil.Emit(OpCodes.Ldsfld, EventArgsEmpty); // load event arguments } else { msil.Emit(OpCodes.Ldarg_0); msil.Emit(OpCodes.Ldfld, backingField.FieldBuilder); msil.Emit(OpCodes.Stloc, handlerLocalBuilder); msil.Emit(OpCodes.Ldloc, handlerLocalBuilder); msil.Emit(OpCodes.Brfalse_S, label); msil.Emit(OpCodes.Ldloc, handlerLocalBuilder); msil.Emit(OpCodes.Ldarg_0); // load sender (this) msil.Emit(OpCodes.Ldsfld, EventArgsEmpty); // load event arguments } MethodInfo invokeMethod = backingField.FieldBuilder.FieldType.GetMethod("Invoke"); msil.Emit(OpCodes.Callvirt, invokeMethod); msil.MarkLabel(label); msil.Emit(OpCodes.Ret); return; } else if (evnt.Type.IsGenericType && evnt.Type.GetGenericTypeDefinition() == typeof(EventHandler <>)) { // EventHandler<T> with T derived from System.EventArgs LocalBuilder handlerLocalBuilder = msil.DeclareLocal(backingField.FieldBuilder.FieldType); Label label = msil.DefineLabel(); if (evnt.Kind == EventKind.Static) { msil.Emit(OpCodes.Ldsfld, backingField.FieldBuilder); msil.Emit(OpCodes.Stloc, handlerLocalBuilder); msil.Emit(OpCodes.Ldloc, handlerLocalBuilder); msil.Emit(OpCodes.Brfalse_S, label); msil.Emit(OpCodes.Ldloc, handlerLocalBuilder); msil.Emit(OpCodes.Ldnull); // load sender (null) msil.Emit(OpCodes.Ldarg_0); // load event arguments } else { msil.Emit(OpCodes.Ldarg_0); msil.Emit(OpCodes.Ldfld, backingField.FieldBuilder); msil.Emit(OpCodes.Stloc, handlerLocalBuilder); msil.Emit(OpCodes.Ldloc, handlerLocalBuilder); msil.Emit(OpCodes.Brfalse_S, label); msil.Emit(OpCodes.Ldloc, handlerLocalBuilder); msil.Emit(OpCodes.Ldarg_0); // load sender (this) msil.Emit(OpCodes.Ldarg_1); // load event arguments } MethodInfo invokeMethod = backingField.FieldBuilder.FieldType.GetMethod("Invoke"); msil.Emit(OpCodes.Callvirt, invokeMethod); msil.MarkLabel(label); msil.Emit(OpCodes.Ret); return; } else if (typeof(Delegate).IsAssignableFrom(evnt.Type)) { MethodInfo invokeMethod = backingField.FieldBuilder.FieldType.GetMethod("Invoke"); LocalBuilder handlerLocalBuilder = msil.DeclareLocal(backingField.FieldBuilder.FieldType); Label label = msil.DefineLabel(); if (evnt.Kind == EventKind.Static) { msil.Emit(OpCodes.Ldsfld, backingField.FieldBuilder); } else { msil.Emit(OpCodes.Ldarg_0); msil.Emit(OpCodes.Ldfld, backingField.FieldBuilder); } msil.Emit(OpCodes.Stloc, handlerLocalBuilder); msil.Emit(OpCodes.Ldloc, handlerLocalBuilder); msil.Emit(OpCodes.Brfalse_S, label); msil.Emit(OpCodes.Ldloc, handlerLocalBuilder); int argumentOffset = evnt.Kind == EventKind.Static ? 0 : 1; for (int i = 0; i < evnt.Raiser.ParameterTypes.Length; i++) { CodeGenHelpers.EmitLoadArgument(msil, argumentOffset + i); } msil.Emit(OpCodes.Callvirt, invokeMethod); msil.MarkLabel(label); msil.Emit(OpCodes.Ret); return; } throw new NotSupportedException("The event type is not supported."); }