From f3069b99f9ddb6b116ccaa01965564346183f4b5 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Wed, 9 Mar 2011 10:59:11 +0100 Subject: [PATCH] Add support for decompiling automatic events. --- ICSharpCode.Decompiler/Ast/AstBuilder.cs | 18 +++- .../ConvertConstructorCallIntoInitializer.cs | 6 +- .../Transforms/PatternStatementTransform.cs | 97 +++++++++++++++++++ ICSharpCode.Decompiler/DecompilerSettings.cs | 15 +++ .../Tests/PropertiesAndEvents.cs | 3 + 5 files changed, 132 insertions(+), 7 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index c11a9c945..07f9064ed 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -56,6 +56,9 @@ namespace ICSharpCode.Decompiler.Ast if (settings.AutomaticProperties && field.Name.StartsWith("<", StringComparison.Ordinal) && field.Name.EndsWith("BackingField", StringComparison.Ordinal)) return true; } + // event-fields are not [CompilerGenerated] + if (field != null && settings.AutomaticEvents && field.DeclaringType.Events.Any(ev => ev.Name == field.Name)) + return true; return false; } @@ -922,20 +925,27 @@ namespace ICSharpCode.Decompiler.Ast } } - void ConvertAttributes(AttributedNode attributedNode, FieldDefinition fieldDefinition) + internal static void ConvertAttributes(AttributedNode attributedNode, FieldDefinition fieldDefinition, AttributeTarget target = AttributeTarget.None) { ConvertCustomAttributes(attributedNode, fieldDefinition); #region FieldOffsetAttribute if (fieldDefinition.HasLayoutInfo) { - Ast.Attribute fieldOffset = CreateNonCustomAttribute(typeof(FieldOffsetAttribute)); + Ast.Attribute fieldOffset = CreateNonCustomAttribute(typeof(FieldOffsetAttribute), fieldDefinition.Module); fieldOffset.Arguments.Add(new PrimitiveExpression(fieldDefinition.Offset)); - attributedNode.Attributes.Add(new AttributeSection(fieldOffset)); + attributedNode.Attributes.Add(new AttributeSection(fieldOffset) { AttributeTarget = target }); + } + #endregion + + #region NonSerializedAttribute + if (fieldDefinition.IsNotSerialized) { + Ast.Attribute nonSerialized = CreateNonCustomAttribute(typeof(NonSerializedAttribute), fieldDefinition.Module); + attributedNode.Attributes.Add(new AttributeSection(nonSerialized) { AttributeTarget = target }); } #endregion if (fieldDefinition.HasMarshalInfo) { - attributedNode.Attributes.Add(new AttributeSection(ConvertMarshalInfo(fieldDefinition, fieldDefinition.Module))); + attributedNode.Attributes.Add(new AttributeSection(ConvertMarshalInfo(fieldDefinition, fieldDefinition.Module)) { AttributeTarget = target }); } } diff --git a/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs b/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs index 0a4602084..977867036 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs @@ -65,8 +65,8 @@ namespace ICSharpCode.Decompiler.Ast.Transforms FieldDefinition fieldDef = m.Get("fieldAccess").Single().Annotation(); if (fieldDef == null) break; - FieldDeclaration fieldDecl = typeDeclaration.Members.OfType().FirstOrDefault(f => f.Annotation() == fieldDef); - if (fieldDecl == null) + AttributedNode fieldOrEventDecl = typeDeclaration.Members.FirstOrDefault(f => f.Annotation() == fieldDef); + if (fieldOrEventDecl == null) break; allSame = true; @@ -77,7 +77,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms if (allSame) { foreach (var ctor in instanceCtors) ctor.Body.First().Remove(); - fieldDecl.Variables.Single().Initializer = m.Get("initializer").Single().Detach(); + fieldOrEventDecl.GetChildrenByRole(AstNode.Roles.Variable).Single().Initializer = m.Get("initializer").Single().Detach(); } } while (allSame); } diff --git a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs index 48f53da25..4dd384831 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs @@ -33,6 +33,8 @@ namespace ICSharpCode.Decompiler.Ast.Transforms TransformDoWhile(compilationUnit); if (context.Settings.AutomaticProperties) TransformAutomaticProperties(compilationUnit); + if (context.Settings.AutomaticEvents) + TransformAutomaticEvents(compilationUnit); } /// @@ -357,5 +359,100 @@ namespace ICSharpCode.Decompiler.Ast.Transforms } } #endregion + + #region Automatic Events + Accessor automaticEventPatternV4 = new Accessor { + Body = new BlockStatement { + new VariableDeclarationStatement { + Type = new AnyNode("type"), + Variables = { + new NamedNode( + "var1", new VariableInitializer { + Initializer = new NamedNode("field", new MemberReferenceExpression { Target = new ThisReferenceExpression() }) + })} + }, + new VariableDeclarationStatement { + Type = new Backreference("type"), + Variables = { new NamedNode("var2", new VariableInitializer()) } + }, + new DoWhileStatement { + EmbeddedStatement = new BlockStatement { + new AssignmentExpression(new IdentifierExpressionBackreference("var2"), new IdentifierExpressionBackreference("var1")), + new VariableDeclarationStatement { + Type = new Backreference("type"), + Variables = { + new NamedNode( + "var3", new VariableInitializer { + Initializer = new AnyNode("delegateCombine").ToExpression().Invoke( + new IdentifierExpressionBackreference("var2"), + new IdentifierExpression("value") + ).CastTo(new Backreference("type")) + }) + }}, + new AssignmentExpression { + Left = new IdentifierExpressionBackreference("var1"), + Right = new AnyNode("Interlocked").ToType().Invoke( + "CompareExchange", + new AstType[] { new Backreference("type") }, // type argument + new Expression[] { // arguments + new DirectionExpression { FieldDirection = FieldDirection.Ref, Expression = new Backreference("field") }, + new IdentifierExpressionBackreference("var3"), + new IdentifierExpressionBackreference("var2") + } + )} + }, + Condition = new BinaryOperatorExpression { + Left = new IdentifierExpressionBackreference("var1"), + Operator = BinaryOperatorType.InEquality, + Right = new IdentifierExpressionBackreference("var2") + }} + }}; + + bool CheckAutomaticEventV4Match(Match m, CustomEventDeclaration ev, bool isAddAccessor) + { + if (m == null) + return false; + if (m.Get("field").Single().MemberName != ev.Name) + return false; // field name must match event name + if (ev.ReturnType.Match(m.Get("type").Single()) == null) + return false; // variable types must match event type + var combineMethod = m.Get("delegateCombine").Single().Parent.Annotation(); + if (combineMethod == null || combineMethod.Name != (isAddAccessor ? "Combine" : "Remove")) + return false; + if (combineMethod.DeclaringType.FullName != "System.Delegate") + return false; + var ice = m.Get("Interlocked").Single().Annotation(); + return ice != null && ice.FullName == "System.Threading.Interlocked"; + } + + void TransformAutomaticEvents(AstNode compilationUnit) + { + foreach (var ev in compilationUnit.Descendants.OfType().ToArray()) { + Match m1 = automaticEventPatternV4.Match(ev.AddAccessor); + if (!CheckAutomaticEventV4Match(m1, ev, true)) + continue; + Match m2 = automaticEventPatternV4.Match(ev.RemoveAccessor); + if (!CheckAutomaticEventV4Match(m2, ev, false)) + continue; + EventDeclaration ed = new EventDeclaration(); + ev.Attributes.MoveTo(ed.Attributes); + ed.ReturnType = ev.ReturnType.Detach(); + ed.Modifiers = ev.Modifiers; + ed.Variables.Add(new VariableInitializer(ev.Name)); + ed.CopyAnnotationsFrom(ev); + + EventDefinition eventDef = ev.Annotation(); + if (eventDef != null) { + FieldDefinition field = eventDef.DeclaringType.Fields.FirstOrDefault(f => f.Name == ev.Name); + if (field != null) { + ed.AddAnnotation(field); + AstBuilder.ConvertAttributes(ed, field, AttributeTarget.Field); + } + } + + ev.ReplaceWith(ed); + } + } + #endregion } } diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs index 541b95a53..82e6797b6 100644 --- a/ICSharpCode.Decompiler/DecompilerSettings.cs +++ b/ICSharpCode.Decompiler/DecompilerSettings.cs @@ -56,6 +56,21 @@ namespace ICSharpCode.Decompiler } } + bool automaticEvents = true; + + /// + /// Decompile automatic events + /// + public bool AutomaticEvents { + get { return automaticEvents; } + set { + if (automaticEvents != value) { + automaticEvents = value; + OnPropertyChanged("AutomaticEvents"); + } + } + } + bool usingStatement = true; /// diff --git a/ICSharpCode.Decompiler/Tests/PropertiesAndEvents.cs b/ICSharpCode.Decompiler/Tests/PropertiesAndEvents.cs index c610bc7ae..0241c217a 100644 --- a/ICSharpCode.Decompiler/Tests/PropertiesAndEvents.cs +++ b/ICSharpCode.Decompiler/Tests/PropertiesAndEvents.cs @@ -39,6 +39,9 @@ public class PropertiesAndEvents public event EventHandler AutomaticEvent; + [field: NonSerialized] + public event EventHandler AutomaticEventWithInitializer = delegate {}; + public event EventHandler CustomEvent { add { this.AutomaticEvent += value;