diff --git a/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/MemberInitializerEvents.cs b/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/MemberInitializerEvents.cs index bbda36917..245556605 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/MemberInitializerEvents.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/MemberInitializerEvents.cs @@ -6,12 +6,12 @@ internal class MemberInitializerEvents public MemberInitializerEvents() { - this.Changed?.Invoke(this, EventArgs.Empty); + Changed?.Invoke(this, EventArgs.Empty); } public MemberInitializerEvents(int value) { - this.Changed?.Invoke(this, EventArgs.Empty); + Changed?.Invoke(this, EventArgs.Empty); } private static void Handler(object sender, EventArgs e) diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 947e4d5a9..c828ad48a 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -609,20 +609,6 @@ namespace ICSharpCode.Decompiler.CSharp return metadata.GetString(field.Name).StartsWith("<>f__switch", StringComparison.Ordinal); } - internal static bool IsEventBackingFieldName(string fieldName, string eventName, out int suffixLength) - { - suffixLength = 0; - if (fieldName == eventName) - return true; - var vbSuffixLength = "Event".Length; - if (fieldName.Length == eventName.Length + vbSuffixLength && fieldName.StartsWith(eventName, StringComparison.Ordinal) && fieldName.EndsWith("Event", StringComparison.Ordinal)) - { - suffixLength = vbSuffixLength; - return true; - } - return false; - } - static bool IsAnonymousMethodCacheField(SRM.FieldDefinition field, MetadataReader metadata) { var name = metadata.GetString(field.Name); @@ -2539,10 +2525,11 @@ namespace ICSharpCode.Decompiler.CSharp bool isAutomaticEvent = adderHasBody && removerHasBody && decompileRun.Settings.AutomaticEvents && AutoEventDecompiler.IsAutomaticEvent(typeSystem, ev, decompileRun, CancellationToken, out backingField); // A recognized automatic event is built in field-like form directly; its - // compiler-generated accessor bodies are never decompiled. + // compiler-generated accessor bodies are never decompiled. Accessors without + // bodies (abstract, extern, interface members) cannot be expressed as custom + // accessors in C#, so only the field-like form is valid for them as well. typeSystemAstBuilder.UseCustomEvents = !isAutomaticEvent - && (ev.DeclaringTypeDefinition!.Kind != TypeKind.Interface - || ev.IsExplicitInterfaceImplementation + && (ev.IsExplicitInterfaceImplementation || adderHasBody || removerHasBody); var eventDecl = typeSystemAstBuilder.ConvertEntity(ev); diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs index 2f868b6d4..41e7a28b8 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs @@ -121,19 +121,6 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms return base.VisitPropertyDeclaration(propertyDeclaration); } - public override AstNode VisitCustomEventDeclaration(CustomEventDeclaration eventDeclaration) - { - // first apply transforms to the accessor bodies - base.VisitCustomEventDeclaration(eventDeclaration); - if (context.Settings.AutomaticEvents) - { - AstNode? result = TransformAutomaticEvents(eventDeclaration); - if (result != null) - return result; - } - return eventDeclaration; - } - public override AstNode VisitEventDeclaration(EventDeclaration eventDeclaration) { // A field-like event declaration hides its backing field; remove the field declaration @@ -922,276 +909,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms } #region Automatic Events - static readonly Expression fieldReferencePattern = new Choice { - new IdentifierExpression(Pattern.AnyString), - new MemberReferenceExpression { - Target = new Choice { new ThisReferenceExpression(), new TypeReferenceExpression { Type = new AnyNode() } }, - MemberName = Pattern.AnyString - } - }; - - static readonly Accessor automaticEventPatternV2 = new Accessor { - Attributes = { new Repeat(new AnyNode()) }, - Body = new BlockStatement { - new AssignmentExpression { - Left = new NamedNode("field", fieldReferencePattern), - Operator = AssignmentOperatorType.Assign, - Right = new CastExpression( - new AnyNode("type"), - new InvocationExpression(new AnyNode("delegateCombine").ToExpression(), new Backreference("field"), new IdentifierExpression("value")) - ) - }, - } - }; - - static readonly Accessor automaticEventPatternV4 = new Accessor { - Attributes = { new Repeat(new AnyNode()) }, - Body = new BlockStatement { - new AssignmentExpression { - Left = new NamedNode("var1", new IdentifierExpression(Pattern.AnyString)), - Operator = AssignmentOperatorType.Assign, - Right = new NamedNode("field", fieldReferencePattern) - }, - new DoWhileStatement { - EmbeddedStatement = new BlockStatement { - new AssignmentExpression(new NamedNode("var2", new IdentifierExpression(Pattern.AnyString)), new IdentifierExpressionBackreference("var1")), - new AssignmentExpression { - Left = new NamedNode("var3", new IdentifierExpression(Pattern.AnyString)), - Operator = AssignmentOperatorType.Assign, - Right = new CastExpression(new AnyNode("type"), new InvocationExpression(new AnyNode("delegateCombine").ToExpression(), new IdentifierExpressionBackreference("var2"), new IdentifierExpression("value"))) - }, - new AssignmentExpression { - Left = new IdentifierExpressionBackreference("var1"), - Right = new InvocationExpression(new MemberReferenceExpression(new TypeReferenceExpression(new TypePattern(typeof(System.Threading.Interlocked)).ToType()), - "CompareExchange"), - new Expression[] { // arguments - new DirectionExpression { FieldDirection = FieldDirection.Ref, Expression = new Backreference("field") }, - new IdentifierExpressionBackreference("var3"), - new IdentifierExpressionBackreference("var2") - } - )} - }, - Condition = new BinaryOperatorExpression { - Left = new CastExpression(new TypePattern(typeof(object)), new IdentifierExpressionBackreference("var1")), - Operator = BinaryOperatorType.InEquality, - Right = new IdentifierExpressionBackreference("var2") - }, - } - } - }; - - static readonly Accessor automaticEventPatternV4AggressivelyInlined = new Accessor { - Attributes = { new Repeat(new AnyNode()) }, - Body = new BlockStatement { - new AssignmentExpression { - Left = new NamedNode("var1", new IdentifierExpression(Pattern.AnyString)), - Operator = AssignmentOperatorType.Assign, - Right = new NamedNode("field", fieldReferencePattern) - }, - new DoWhileStatement { - EmbeddedStatement = new BlockStatement { - new AssignmentExpression(new NamedNode("var2", new IdentifierExpression(Pattern.AnyString)), new IdentifierExpressionBackreference("var1")), - new AssignmentExpression { - Left = new IdentifierExpressionBackreference("var1"), - Right = new InvocationExpression(new MemberReferenceExpression(new TypeReferenceExpression(new TypePattern(typeof(System.Threading.Interlocked)).ToType()), - "CompareExchange"), - new Expression[] { // arguments - new NamedArgumentExpression("value", new CastExpression(new AnyNode("type"), new InvocationExpression(new AnyNode("delegateCombine").ToExpression(), new IdentifierExpressionBackreference("var2"), new IdentifierExpression("value")))), - new NamedArgumentExpression("location1", new DirectionExpression { FieldDirection = FieldDirection.Ref, Expression = new Backreference("field") }), - new NamedArgumentExpression("comparand", new IdentifierExpressionBackreference("var2")) - } - )} - }, - Condition = new BinaryOperatorExpression { - Left = new CastExpression(new TypePattern(typeof(object)), new IdentifierExpressionBackreference("var1")), - Operator = BinaryOperatorType.InEquality, - Right = new IdentifierExpressionBackreference("var2") - }, - } - } - }; - - static readonly Accessor automaticEventPatternV4MCS = new Accessor { - Attributes = { new Repeat(new AnyNode()) }, - Body = new BlockStatement { - new AssignmentExpression { - Left = new NamedNode("var1", new IdentifierExpression(Pattern.AnyString)), - Operator = AssignmentOperatorType.Assign, - Right = new NamedNode( - "field", - new MemberReferenceExpression { - Target = new Choice { new ThisReferenceExpression(), new TypeReferenceExpression { Type = new AnyNode() } }, - MemberName = Pattern.AnyString - } - ) - }, - new DoWhileStatement { - EmbeddedStatement = new BlockStatement { - new AssignmentExpression(new NamedNode("var2", new IdentifierExpression(Pattern.AnyString)), new IdentifierExpressionBackreference("var1")), - new AssignmentExpression { - Left = new IdentifierExpressionBackreference("var1"), - Right = new InvocationExpression(new MemberReferenceExpression(new TypeReferenceExpression(new TypePattern(typeof(System.Threading.Interlocked)).ToType()), - "CompareExchange", - new AstType[] { new Repeat(new AnyNode()) }), // optional type arguments - new Expression[] { // arguments - new DirectionExpression { FieldDirection = FieldDirection.Ref, Expression = new Backreference("field") }, - new CastExpression(new AnyNode("type"), new InvocationExpression(new AnyNode("delegateCombine").ToExpression(), new IdentifierExpressionBackreference("var2"), new IdentifierExpression("value"))), - new IdentifierExpressionBackreference("var1") - } - ) - } - }, - Condition = new BinaryOperatorExpression { - Left = new CastExpression(new TypePattern(typeof(object)), new IdentifierExpressionBackreference("var1")), - Operator = BinaryOperatorType.InEquality, - Right = new IdentifierExpressionBackreference("var2") - }, - } - } - }; - - bool CheckAutomaticEventMatch(Match m, CustomEventDeclaration ev, bool isAddAccessor) - { - if (!m.Success) - return false; - Expression fieldExpression = m.Get("field").Single(); - IField? eventField = fieldExpression.GetSymbol() as IField; - if (eventField == null) - return false; - var module = eventField.ParentModule as MetadataModule; - if (module == null) - return false; - if (!module.MetadataFile.PropertyAndEventBackingFieldLookup.IsEventBackingField((FieldDefinitionHandle)eventField.MetadataToken, out _)) - return false; - var returnType = ev.ReturnType.GetResolveResult().Type; - var eventType = m.Get("type").Single().GetResolveResult().Type; - // ignore tuple element names, dynamic and nullability - if (!NormalizeTypeVisitor.TypeErasure.EquivalentTypes(returnType, eventType)) - return false; - var combineMethod = m.Get("delegateCombine").Single().Parent!.GetSymbol() as IMethod; - if (combineMethod == null || combineMethod.Name != (isAddAccessor ? "Combine" : "Remove")) - return false; - return combineMethod.DeclaringType.FullName == "System.Delegate"; - } - - static readonly string[] attributeTypesToRemoveFromAutoEvents = new[] { - "System.Runtime.CompilerServices.CompilerGeneratedAttribute", - "System.Diagnostics.DebuggerBrowsableAttribute", - "System.Runtime.CompilerServices.MethodImplAttribute" - }; - internal static readonly string[] attributeTypesToRemoveFromAutoProperties = new[] { "System.Runtime.CompilerServices.CompilerGeneratedAttribute", "System.Diagnostics.DebuggerBrowsableAttribute" }; - bool CheckAutomaticEventV4(CustomEventDeclaration ev) - { - Match addMatch = automaticEventPatternV4.Match(ev.AddAccessor); - if (!CheckAutomaticEventMatch(addMatch, ev, isAddAccessor: true)) - return false; - Match removeMatch = automaticEventPatternV4.Match(ev.RemoveAccessor); - if (!CheckAutomaticEventMatch(removeMatch, ev, isAddAccessor: false)) - return false; - return true; - } - - bool CheckAutomaticEventV4AggressivelyInlined(CustomEventDeclaration ev) - { - if (!context.Settings.AggressiveInlining) - return false; - Match addMatch = automaticEventPatternV4AggressivelyInlined.Match(ev.AddAccessor); - if (!CheckAutomaticEventMatch(addMatch, ev, isAddAccessor: true)) - return false; - Match removeMatch = automaticEventPatternV4AggressivelyInlined.Match(ev.RemoveAccessor); - if (!CheckAutomaticEventMatch(removeMatch, ev, isAddAccessor: false)) - return false; - return true; - } - - bool CheckAutomaticEventV2(CustomEventDeclaration ev) - { - Match addMatch = automaticEventPatternV2.Match(ev.AddAccessor); - if (!CheckAutomaticEventMatch(addMatch, ev, isAddAccessor: true)) - return false; - Match removeMatch = automaticEventPatternV2.Match(ev.RemoveAccessor); - if (!CheckAutomaticEventMatch(removeMatch, ev, isAddAccessor: false)) - return false; - return true; - } - - bool CheckAutomaticEventV4MCS(CustomEventDeclaration ev) - { - Match addMatch = automaticEventPatternV4MCS.Match(ev.AddAccessor); - if (!CheckAutomaticEventMatch(addMatch, ev, true)) - return false; - Match removeMatch = automaticEventPatternV4MCS.Match(ev.RemoveAccessor); - if (!CheckAutomaticEventMatch(removeMatch, ev, false)) - return false; - return true; - } - - EventDeclaration? TransformAutomaticEvents(CustomEventDeclaration ev) - { - if (ev.PrivateImplementationType is not null) - return null; - const Modifiers withoutBody = Modifiers.Abstract | Modifiers.Extern; - if (ev.GetSymbol() is not IEvent symbol) - return null; - if ((ev.Modifiers & withoutBody) == 0) - { - if (!CheckAutomaticEventV4AggressivelyInlined(ev) && !CheckAutomaticEventV4(ev) && !CheckAutomaticEventV2(ev) && !CheckAutomaticEventV4MCS(ev)) - return null; - } - if (ev.AddAccessor is not { }) - return null; - context.Step("Convert custom event to field-like event", ev); - var fieldDecl = ev.Parent?.Children.OfType() - .FirstOrDefault(fd => IsEventBackingFieldDeclaration(fd, symbol)); - fieldDecl?.Remove(); - EventDeclaration ed = ConvertToFieldLikeEvent(ev, fieldDecl); - ev.ReplaceWith(ed); - context.EndStep(ed); - return ed; - } - - /// - /// Builds a field-like event declaration from a custom event declaration whose accessors - /// are compiler-generated: moves the event attributes, the add-accessor attributes - /// (as "method:" sections) and the backing-field attributes (as "field:" sections) over, - /// dropping the attributes the compiler puts on automatic events. - /// The caller is responsible for detaching from the - /// syntax tree and for replacing with the returned declaration. - /// - internal static EventDeclaration ConvertToFieldLikeEvent(CustomEventDeclaration ev, EntityDeclaration? backingFieldDecl) - { - var addAccessor = ev.AddAccessor!; - RemoveCompilerGeneratedAttribute(addAccessor.Attributes, attributeTypesToRemoveFromAutoEvents); - EventDeclaration ed = new EventDeclaration(); - ev.Attributes.MoveTo(ed.Attributes); - foreach (var attr in addAccessor.Attributes) - { - attr.AttributeTarget = "method"; - ed.Attributes.Add(attr.Detach()); - } - ed.ReturnType = ev.ReturnType.Detach(); - ed.Modifiers = ev.Modifiers; - ed.Variables.Add(new VariableInitializer(ev.Name)); - ed.CopyAnnotationsFrom(ev); - - if (backingFieldDecl != null) - { - CSharpDecompiler.RemoveAttribute(backingFieldDecl, KnownAttribute.CompilerGenerated); - CSharpDecompiler.RemoveAttribute(backingFieldDecl, KnownAttribute.DebuggerBrowsable); - foreach (var section in backingFieldDecl.Attributes) - { - section.AttributeTarget = "field"; - ed.Attributes.Add(section.Detach()); - } - } - return ed; - } - static bool IsEventBackingFieldDeclaration(FieldDeclaration fd, IEvent ev) { if (fd.Variables.Count > 1)