From d0a73d1a5eb282e550457ada0943123fc753562b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Artur=20Zgodzi=C5=84ski?= Date: Sat, 5 Mar 2011 15:07:00 +0100 Subject: [PATCH 01/16] Updated test sample files. --- .../Tests/CustomAttributes/S_CustomAttributeSamples.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributeSamples.cs b/ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributeSamples.cs index 866d9aa06..8c54c97d7 100644 --- a/ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributeSamples.cs +++ b/ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributeSamples.cs @@ -134,7 +134,6 @@ namespace AppliedToPropertySet [MyAttribute] set { - return; } } } @@ -177,7 +176,7 @@ namespace AppliedToParameter } public class MyClass { - public void Method([MyAttribute]int val) + public void Method([MyAttribute] int val) { } } @@ -204,7 +203,6 @@ namespace NamedInitializerPropertyString } set { - return; } } } @@ -227,7 +225,6 @@ namespace NamedInitializerPropertyType } set { - return; } } } @@ -250,7 +247,6 @@ namespace NamedInitializerPropertyEnum } set { - return; } } } @@ -321,7 +317,6 @@ namespace TargetPropertySetParam [param: MyAttribute] set { - return; } } } @@ -344,7 +339,6 @@ namespace TargetPropertySetReturn [return: MyAttribute] set { - return; } } } From ebd20b88c9c16719b5443cda294d057c6c700692 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Artur=20Zgodzi=C5=84ski?= Date: Mon, 7 Mar 2011 10:18:38 +0100 Subject: [PATCH 02/16] Indexer decompilation support. Conflicts: ICSharpCode.Decompiler/Ast/AstBuilder.cs --- ICSharpCode.Decompiler/Ast/AstBuilder.cs | 54 ++++++++++++++++++- .../S_CustomAttributeSamples.cs | 17 +++++- .../CSharp/Ast/TypeMembers/Accessor.cs | 8 ++- .../CSharp/OutputVisitor/OutputVisitor.cs | 17 +++++- 4 files changed, 88 insertions(+), 8 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index 0c249b6c6..aaeb49d84 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -153,6 +153,7 @@ namespace ICSharpCode.Decompiler.Ast TypeDefinition oldCurrentType = context.CurrentType; context.CurrentType = typeDef; TypeDeclaration astType = new TypeDeclaration(); + ConvertAttributes(astType, typeDef); astType.AddAnnotation(typeDef); astType.Modifiers = ConvertModifiers(typeDef); astType.Name = CleanName(typeDef.Name); @@ -216,7 +217,6 @@ namespace ICSharpCode.Decompiler.Ast AddTypeMembers(astType, typeDef); } - ConvertAttributes(astType, typeDef); context.CurrentType = oldCurrentType; return astType; } @@ -506,7 +506,25 @@ namespace ICSharpCode.Decompiler.Ast // Add properties foreach(PropertyDefinition propDef in typeDef.Properties) { - astType.AddChild(CreateProperty(propDef), TypeDeclaration.MemberRole); + MemberDeclaration astProp = CreateProperty(propDef); + + if (astProp.Name == "Item" && propDef.HasParameters) + { + var defaultMember = GetDefaultMember(astType.Annotation()); + if (defaultMember.Item1 == "Item") + { + astProp = ConvertPropertyToIndexer((PropertyDeclaration)astProp, propDef); + + var astAttr = astType.Attributes.SelectMany(sec => sec.Attributes).First(attr => attr.Annotation() == defaultMember.Item2); + var attrSection = (AttributeSection)astAttr.Parent; + if (attrSection.Attributes.Count == 1) + attrSection.Remove(); + else + astAttr.Remove(); + } + } + + astType.AddChild(astProp, TypeDeclaration.MemberRole); } // Add constructors @@ -595,6 +613,19 @@ namespace ICSharpCode.Decompiler.Ast return astMethod; } + IndexerDeclaration ConvertPropertyToIndexer(PropertyDeclaration astProp, PropertyDefinition propDef) + { + var astIndexer = new IndexerDeclaration(); + astIndexer.Name = astProp.Name; + astIndexer.CopyAnnotationsFrom(astProp); + astIndexer.Modifiers = astProp.Modifiers; + astIndexer.ReturnType = astProp.ReturnType.Detach(); + astIndexer.Getter = astProp.Getter.Detach(); + astIndexer.Setter = astProp.Setter.Detach(); + astIndexer.Parameters.AddRange(MakeParameters(propDef.Parameters)); + return astIndexer; + } + PropertyDeclaration CreateProperty(PropertyDefinition propDef) { PropertyDeclaration astProp = new PropertyDeclaration(); @@ -894,6 +925,7 @@ namespace ICSharpCode.Decompiler.Ast var attributes = new List(); foreach (var customAttribute in customAttributeProvider.CustomAttributes) { var attribute = new ICSharpCode.NRefactory.CSharp.Attribute(); + attribute.AddAnnotation(customAttribute); attribute.Type = ConvertType(customAttribute.AttributeType); attributes.Add(attribute); @@ -1024,5 +1056,23 @@ namespace ICSharpCode.Decompiler.Ast return type.CustomAttributes.Any(attr => attr.AttributeType.FullName == "System.FlagsAttribute"); } + + /// + /// Gets the name of the default member of the type pointed by the attribute. + /// + /// The type definition. + /// The name of the default member or null if no attribute has been found. + private static Tuple GetDefaultMember(TypeDefinition type) + { + foreach (CustomAttribute ca in type.CustomAttributes) + { + if (ca.Constructor.FullName == "System.Void System.Reflection.DefaultMemberAttribute::.ctor(System.String)") + { + return Tuple.Create(ca.ConstructorArguments.Single().Value as string, ca); + } + } + return new Tuple(null, null); + } + } } diff --git a/ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributeSamples.cs b/ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributeSamples.cs index 8c54c97d7..d219bfee0 100644 --- a/ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributeSamples.cs +++ b/ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributeSamples.cs @@ -138,6 +138,21 @@ namespace AppliedToPropertySet } } } +//$$ AppliedToIndexer +namespace AppliedToIndexer +{ + public class TestClass + { + [Obsolete("reason")] + public int this[int i] + { + get + { + return 0; + } + } + } +} //$$ AppliedToDelegate [Obsolete("reason")] public delegate int AppliedToDelegate(); @@ -361,7 +376,6 @@ namespace TargetPropertyIndexSetParam [param: MyAttribute] set { - return; } } } @@ -385,7 +399,6 @@ namespace TargetPropertyIndexSetMultiParam [param: MyAttribute] set { - return; } } } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/Accessor.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/Accessor.cs index c487032eb..cc5651d87 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/Accessor.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/Accessor.cs @@ -56,8 +56,12 @@ namespace ICSharpCode.NRefactory.CSharp get { return GetChildByRole (Roles.Body); } set { SetChildByRole (Roles.Body, value); } } - - public override S AcceptVisitor (IAstVisitor visitor, T data) + + public AstNodeCollection Parameters { + get { return GetChildrenByRole(Roles.Parameter); } + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) { return visitor.VisitAccessor (this, data); } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs index da02c47d5..5a5d01d09 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs @@ -202,8 +202,21 @@ namespace ICSharpCode.NRefactory.CSharp { WriteCommaSeparatedListInParenthesis(list.SafeCast(), spaceWithin); } + #endif - + + void WriteCommaSeparatedListInBrackets(IEnumerable list, bool spaceWithin) + { + WriteToken("[", AstNode.Roles.LBracket); + if (list.Any()) + { + Space(spaceWithin); + WriteCommaSeparatedList(list.SafeCast()); + Space(spaceWithin); + } + WriteToken("]", AstNode.Roles.RBracket); + } + void WriteCommaSeparatedListInBrackets(IEnumerable list) { WriteToken("[", AstNode.Roles.LBracket); @@ -1815,7 +1828,7 @@ namespace ICSharpCode.NRefactory.CSharp WritePrivateImplementationType(indexerDeclaration.PrivateImplementationType); WriteKeyword("this"); Space(policy.BeforeMethodDeclarationParentheses); - WriteCommaSeparatedListInParenthesis(indexerDeclaration.Parameters, policy.WithinMethodDeclarationParentheses); + WriteCommaSeparatedListInBrackets(indexerDeclaration.Parameters, policy.WithinMethodDeclarationParentheses); OpenBrace(policy.PropertyBraceStyle); // output get/set in their original order foreach (AstNode node in indexerDeclaration.Children) { From 16fd9d07e3cc58f8349f8b67a882852d12bd254a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Artur=20Zgodzi=C5=84ski?= Date: Mon, 7 Mar 2011 10:26:39 +0100 Subject: [PATCH 03/16] Indexers support. Explicit implementation of methods and parameters. Conflicts: ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj ICSharpCode.Decompiler/Tests/Types/TypeTests.cs --- ICSharpCode.Decompiler/Ast/AstBuilder.cs | 72 +++-- .../S_CustomAttributeSamples.cs | 60 +++- .../Tests/DelegateConstruction.cs | 6 +- .../Tests/ICSharpCode.Decompiler.Tests.csproj | 1 + .../Tests/Types/S_TypeMemberDeclarations.cs | 305 ++++++++++++++++++ .../Tests/Types/TypeTests.cs | 5 + 6 files changed, 419 insertions(+), 30 deletions(-) create mode 100644 ICSharpCode.Decompiler/Tests/Types/S_TypeMemberDeclarations.cs diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index aaeb49d84..328a55e30 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -231,9 +231,12 @@ namespace ICSharpCode.Decompiler.Ast int pos = name.LastIndexOf('`'); if (pos >= 0) name = name.Substring(0, pos); + pos = name.LastIndexOf('.'); + if (pos >= 0) + name = name.Substring(pos + 1); return name; } - + #region Convert Type Reference /// /// Converts a type reference. @@ -489,6 +492,7 @@ namespace ICSharpCode.Decompiler.Ast return modifiers; } + #endregion void AddTypeMembers(TypeDeclaration astType, TypeDefinition typeDef) @@ -503,29 +507,9 @@ namespace ICSharpCode.Decompiler.Ast foreach(EventDefinition eventDef in typeDef.Events) { astType.AddChild(CreateEvent(eventDef), TypeDeclaration.MemberRole); } - - // Add properties - foreach(PropertyDefinition propDef in typeDef.Properties) { - MemberDeclaration astProp = CreateProperty(propDef); - - if (astProp.Name == "Item" && propDef.HasParameters) - { - var defaultMember = GetDefaultMember(astType.Annotation()); - if (defaultMember.Item1 == "Item") - { - astProp = ConvertPropertyToIndexer((PropertyDeclaration)astProp, propDef); - var astAttr = astType.Attributes.SelectMany(sec => sec.Attributes).First(attr => attr.Annotation() == defaultMember.Item2); - var attrSection = (AttributeSection)astAttr.Parent; - if (attrSection.Attributes.Count == 1) - attrSection.Remove(); - else - astAttr.Remove(); - } - } - - astType.AddChild(astProp, TypeDeclaration.MemberRole); - } + // Add properties + CreateProperties(astType, typeDef); // Add constructors foreach(MethodDefinition methodDef in typeDef.Methods) { @@ -542,6 +526,35 @@ namespace ICSharpCode.Decompiler.Ast } } + private void CreateProperties(TypeDeclaration astType, TypeDefinition typeDef) + { + CustomAttribute attributeToRemove = null; + foreach (PropertyDefinition propDef in typeDef.Properties) { + MemberDeclaration astProp = CreateProperty(propDef); + + if (astProp.Name == "Item" && propDef.HasParameters) { + var defaultMember = GetDefaultMember(astType.Annotation()); + if (defaultMember.Item1 == "Item") { + astProp = ConvertPropertyToIndexer((PropertyDeclaration)astProp, propDef); + attributeToRemove = defaultMember.Item2; + } else if ((propDef.GetMethod ?? propDef.SetMethod).HasOverrides) { + astProp = ConvertPropertyToIndexer((PropertyDeclaration)astProp, propDef); + } + } + + astType.AddChild(astProp, TypeDeclaration.MemberRole); + } + + if (attributeToRemove != null) { + var astAttr = astType.Attributes.SelectMany(sec => sec.Attributes).First(attr => attr.Annotation() == attributeToRemove); + var attrSection = (AttributeSection)astAttr.Parent; + if (attrSection.Attributes.Count == 1) + attrSection.Remove(); + else + astAttr.Remove(); + } + } + MethodDeclaration CreateMethod(MethodDefinition methodDef) { MethodDeclaration astMethod = new MethodDeclaration(); @@ -552,7 +565,10 @@ namespace ICSharpCode.Decompiler.Ast astMethod.Parameters.AddRange(MakeParameters(methodDef.Parameters)); astMethod.Constraints.AddRange(MakeConstraints(methodDef.GenericParameters)); if (!methodDef.DeclaringType.IsInterface) { - astMethod.Modifiers = ConvertModifiers(methodDef); + if (!methodDef.HasOverrides) + astMethod.Modifiers = ConvertModifiers(methodDef); + else + astMethod.PrivateImplementationType = ConvertType(methodDef.Overrides.First().DeclaringType); astMethod.Body = AstMethodBodyBuilder.CreateMethodBody(methodDef, context); } ConvertAttributes(astMethod, methodDef); @@ -618,7 +634,9 @@ namespace ICSharpCode.Decompiler.Ast var astIndexer = new IndexerDeclaration(); astIndexer.Name = astProp.Name; astIndexer.CopyAnnotationsFrom(astProp); + astProp.Attributes.MoveTo(astIndexer.Attributes); astIndexer.Modifiers = astProp.Modifiers; + astIndexer.PrivateImplementationType = astProp.PrivateImplementationType.Detach(); astIndexer.ReturnType = astProp.ReturnType.Detach(); astIndexer.Getter = astProp.Getter.Detach(); astIndexer.Setter = astProp.Setter.Detach(); @@ -630,7 +648,11 @@ namespace ICSharpCode.Decompiler.Ast { PropertyDeclaration astProp = new PropertyDeclaration(); astProp.AddAnnotation(propDef); - astProp.Modifiers = ConvertModifiers(propDef.GetMethod ?? propDef.SetMethod); + var accessor = propDef.SetMethod ?? propDef.GetMethod; + if (!propDef.DeclaringType.IsInterface && !accessor.HasOverrides) { + astProp.Modifiers = ConvertModifiers(accessor); + } else if (accessor.HasOverrides) + astProp.PrivateImplementationType = ConvertType(accessor.Overrides.First().DeclaringType); astProp.Name = CleanName(propDef.Name); astProp.ReturnType = ConvertType(propDef.PropertyType, propDef); if (propDef.GetMethod != null) { diff --git a/ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributeSamples.cs b/ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributeSamples.cs index d219bfee0..1a05566b4 100644 --- a/ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributeSamples.cs +++ b/ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributeSamples.cs @@ -358,8 +358,62 @@ namespace TargetPropertySetReturn } } } -//$$ TargetPropertyIndexSetParam -namespace TargetPropertyIndexSetParam +//$$ TargetPropertyIndexGetReturn +namespace TargetPropertyIndexGetReturn +{ + [AttributeUsage(AttributeTargets.All)] + public class MyAttributeAttribute : Attribute + { + } + public class MyClass + { + public int this[string s] + { + [return: MyAttribute] + get + { + return 3; + } + } + } +} +//$$ TargetPropertyIndexParamOnlySet +namespace TargetPropertyIndexParamOnlySet +{ + [AttributeUsage(AttributeTargets.All)] + public class MyAttributeAttribute : Attribute + { + } + public class MyClass + { + public int this[[MyAttribute] string s] + { + set + { + } + } + } +} +//$$ TargetPropertyIndexParamOnlyGet +namespace TargetPropertyIndexParamOnlyGet +{ + [AttributeUsage(AttributeTargets.All)] + public class MyAttributeAttribute : Attribute + { + } + public class MyClass + { + public int this[[MyAttribute] string s] + { + get + { + return 3; + } + } + } +} +//$$ TargetPropertyIndexSetReturn +namespace TargetPropertyIndexSetReturn { [AttributeUsage(AttributeTargets.All)] public class MyAttributeAttribute : Attribute @@ -373,7 +427,7 @@ namespace TargetPropertyIndexSetParam { return ""; } - [param: MyAttribute] + [return: MyAttribute] set { } diff --git a/ICSharpCode.Decompiler/Tests/DelegateConstruction.cs b/ICSharpCode.Decompiler/Tests/DelegateConstruction.cs index 223247bf5..240e6b0d1 100644 --- a/ICSharpCode.Decompiler/Tests/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/Tests/DelegateConstruction.cs @@ -43,7 +43,8 @@ public static class DelegateConstruction public static List> AnonymousMethodStoreWithinLoop() { List> list = new List>(); - for (int i = 0; i < 10; i++) { + for (int i = 0; i < 10; i++) + { int counter; list.Add(x => counter = x); } @@ -54,7 +55,8 @@ public static class DelegateConstruction { List> list = new List>(); int counter; - for (int i = 0; i < 10; i++) { + for (int i = 0; i < 10; i++) + { list.Add(x => counter = x); } return list; diff --git a/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj index 42ee0a0cd..1f8fbd5be 100644 --- a/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj @@ -57,6 +57,7 @@ + diff --git a/ICSharpCode.Decompiler/Tests/Types/S_TypeMemberDeclarations.cs b/ICSharpCode.Decompiler/Tests/Types/S_TypeMemberDeclarations.cs new file mode 100644 index 000000000..08193844a --- /dev/null +++ b/ICSharpCode.Decompiler/Tests/Types/S_TypeMemberDeclarations.cs @@ -0,0 +1,305 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under MIT X11 license (for details please see \doc\license.txt) + +//$CS +using System; +//$CE + +//$$ IndexerWithGetOnly +namespace IndexerWithGetOnly +{ + public class MyClass + { + public int this[int i] + { + get + { + return i; + } + } + } +} +//$$ IndexerWithSetOnly +namespace IndexerWithSetOnly +{ + public class MyClass + { + public int this[int i] + { + set + { + } + } + } +} +//$$ IndexerWithMoreParameters +namespace IndexerWithMoreParameters +{ + public class MyClass + { + public int this[int i, string s, Type t] + { + get + { + return 0; + } + } + } +} +//$$ IndexerInGenericClass +namespace IndexerInGenericClass +{ + public class MyClass + { + public int this[T t] + { + get + { + return 0; + } + } + } +} +//$$ OverloadedIndexer +namespace OverloadedIndexer +{ + public class MyClass + { + public int this[int t] + { + get + { + return 0; + } + } + public int this[string s] + { + get + { + return 0; + } + set + { + Console.WriteLine(value + " " + s); + } + } + } +} +//$$ IndexerInInterface +namespace IndexerInInterface +{ + public interface IInterface + { + int this[string s, string s2] + { + set; + } + } +} +//$$ IndexerInterfaceExplicitImplementation +namespace IndexerInterfaceExplicitImplementation +{ + public interface IMyInterface + { + int this[string s] + { + get; + } + } + public class MyClass : IMyInterface + { + int IMyInterface.this[string s] + { + get + { + return 3; + } + } + } +} +//$$ IndexerInterfaceImplementation +namespace IndexerInterfaceImplementation +{ + public interface IMyInterface + { + int this[string s] + { + get; + } + } + public class MyClass : IMyInterface + { + public int this[string s] + { + get + { + return 3; + } + } + } +} +//$$ IndexerAbstract +namespace IndexerAbstract +{ + public abstract class MyClass + { + public abstract int this[string s, string s2] + { + set; + } + protected abstract string this[int index] + { + get; + } + } +} +//$$ MethodExplicit +namespace MethodExplicit +{ + public interface IMyInterface + { + void MyMethod(); + } + public class MyClass : IMyInterface + { + void IMyInterface.MyMethod() + { + } + } +} +//$$ MethodFromInterfaceVirtual +namespace MethodFromInterfaceVirtual +{ + public interface IMyInterface + { + void MyMethod(); + } + public class MyClass : IMyInterface + { + public virtual void MyMethod() + { + } + } +} +//$$ MethodFromInterface +namespace MethodFromInterface +{ + public interface IMyInterface + { + void MyMethod(); + } + public class MyClass : IMyInterface + { + public void MyMethod() + { + } + } +} +//$$ MethodFromInterfaceAbstract +namespace MethodFromInterfaceAbstract +{ + public interface IMyInterface + { + void MyMethod(); + } + public abstract class MyClass : IMyInterface + { + public abstract void MyMethod(); + } +} +//$$ PropertyInterface +namespace PropertyInterface +{ + public interface IMyInterface + { + int MyProperty + { + get; + set; + } + } +} +//$$ PropertyInterfaceExplicitImplementation +namespace PropertyInterfaceExplicitImplementation +{ + public interface IMyInterface + { + int MyProperty + { + get; + set; + } + } + public class MyClass : IMyInterface + { + int IMyInterface.MyProperty + { + get + { + return 0; + } + set + { + } + } + } +} +//$$ PropertyInterfaceImplementation +namespace PropertyInterfaceImplementation +{ + public interface IMyInterface + { + int MyProperty + { + get; + set; + } + } + public class MyClass : IMyInterface + { + public int MyProperty + { + get + { + return 0; + } + set + { + } + } + } +} +//$$ PropertyPrivateGetPublicSet +namespace PropertyPrivateGetPublicSet +{ + public class MyClass + { + public int MyProperty + { + private get + { + return 3; + } + set + { + } + } + } +} +//$$ PropertyPublicGetProtectedSet +namespace PropertyPublicGetProtectedSet +{ + public class MyClass + { + public int MyProperty + { + get + { + return 3; + } + protected set + { + } + } + } +} diff --git a/ICSharpCode.Decompiler/Tests/Types/TypeTests.cs b/ICSharpCode.Decompiler/Tests/Types/TypeTests.cs index 56d7db39f..398144b2e 100644 --- a/ICSharpCode.Decompiler/Tests/Types/TypeTests.cs +++ b/ICSharpCode.Decompiler/Tests/Types/TypeTests.cs @@ -8,5 +8,10 @@ namespace ICSharpCode.Decompiler.Tests.Types { public class TypeTests : DecompilerTestBase { + [Test] + public void TypeMemberDeclarations() + { + ValidateFileRoundtrip(@"Types\S_TypeMemberDeclarations.cs"); + } } } From 1e9de3c359fd8f0bc04c7c231655773e07bdc4ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Artur=20Zgodzi=C5=84ski?= Date: Mon, 7 Mar 2011 11:00:39 +0100 Subject: [PATCH 04/16] small code reformatting --- ICSharpCode.Decompiler/Ast/AstBuilder.cs | 4 ---- .../CSharp/OutputVisitor/OutputVisitor.cs | 3 +-- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index 328a55e30..ad4d213d8 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -1087,12 +1087,8 @@ namespace ICSharpCode.Decompiler.Ast private static Tuple GetDefaultMember(TypeDefinition type) { foreach (CustomAttribute ca in type.CustomAttributes) - { if (ca.Constructor.FullName == "System.Void System.Reflection.DefaultMemberAttribute::.ctor(System.String)") - { return Tuple.Create(ca.ConstructorArguments.Single().Value as string, ca); - } - } return new Tuple(null, null); } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs index 5a5d01d09..bcab463d7 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs @@ -208,8 +208,7 @@ namespace ICSharpCode.NRefactory.CSharp void WriteCommaSeparatedListInBrackets(IEnumerable list, bool spaceWithin) { WriteToken("[", AstNode.Roles.LBracket); - if (list.Any()) - { + if (list.Any()) { Space(spaceWithin); WriteCommaSeparatedList(list.SafeCast()); Space(spaceWithin); From 49a702f0cf2d03875a6c3f00fb9702d4f8dbc8b9 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Tue, 8 Mar 2011 04:19:31 +0100 Subject: [PATCH 05/16] Don't use ".Invoke" for delegate invocations. --- ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index a85b773f9..9b3d43ef3 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -595,6 +595,9 @@ namespace ICSharpCode.Decompiler.Ast }; } } + } else if (cecilMethodDef.Name == "Invoke" && cecilMethodDef.DeclaringType.BaseType.FullName == "System.MulticastDelegate") { + AdjustArgumentsForMethodCall(cecilMethod, methodArgs); + return target.Invoke(methodArgs); } } // Default invocation From 87a654c45179754053f7a77cc7d3d8f32e90298c Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Tue, 8 Mar 2011 04:56:32 +0100 Subject: [PATCH 06/16] Output accessibility of getter/setter when it differs from the accessibility of the property. --- ICSharpCode.Decompiler/Ast/AstBuilder.cs | 42 +++++++++++++++++++----- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index ad4d213d8..32d3bf9d2 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -644,29 +644,53 @@ namespace ICSharpCode.Decompiler.Ast return astIndexer; } + Modifiers FixUpVisibility(Modifiers m) + { + Modifiers v = m & Modifiers.VisibilityMask; + // If any of the modifiers is public, use that + if ((v & Modifiers.Public) == Modifiers.Public) + return Modifiers.Public | (m & ~Modifiers.VisibilityMask); + // If both modifiers are private, no need to fix anything + if (v == Modifiers.Private) + return m; + // Otherwise, use the other modifiers (internal and/or protected) + return m & ~Modifiers.Private; + } + PropertyDeclaration CreateProperty(PropertyDefinition propDef) { PropertyDeclaration astProp = new PropertyDeclaration(); astProp.AddAnnotation(propDef); - var accessor = propDef.SetMethod ?? propDef.GetMethod; + var accessor = propDef.GetMethod ?? propDef.SetMethod; + Modifiers getterModifiers = Modifiers.None; + Modifiers setterModifiers = Modifiers.None; if (!propDef.DeclaringType.IsInterface && !accessor.HasOverrides) { - astProp.Modifiers = ConvertModifiers(accessor); - } else if (accessor.HasOverrides) + getterModifiers = ConvertModifiers(propDef.GetMethod); + setterModifiers = ConvertModifiers(propDef.SetMethod); + astProp.Modifiers = FixUpVisibility(getterModifiers | setterModifiers); + } else if (accessor.HasOverrides) { astProp.PrivateImplementationType = ConvertType(accessor.Overrides.First().DeclaringType); + } astProp.Name = CleanName(propDef.Name); astProp.ReturnType = ConvertType(propDef.PropertyType, propDef); if (propDef.GetMethod != null) { - astProp.Getter = new Accessor { - Body = AstMethodBodyBuilder.CreateMethodBody(propDef.GetMethod, context) - }.WithAnnotation(propDef.GetMethod); + astProp.Getter = new Accessor(); + astProp.Getter.Body = AstMethodBodyBuilder.CreateMethodBody(propDef.GetMethod, context); + astProp.AddAnnotation(propDef.GetMethod); ConvertAttributes(astProp.Getter, propDef.GetMethod); + + if ((getterModifiers & Modifiers.VisibilityMask) != (astProp.Modifiers & Modifiers.VisibilityMask)) + astProp.Getter.Modifiers = getterModifiers & Modifiers.VisibilityMask; } if (propDef.SetMethod != null) { - astProp.Setter = new Accessor { - Body = AstMethodBodyBuilder.CreateMethodBody(propDef.SetMethod, context) - }.WithAnnotation(propDef.SetMethod); + astProp.Setter = new Accessor(); + astProp.Setter.Body = AstMethodBodyBuilder.CreateMethodBody(propDef.SetMethod, context); + astProp.Setter.AddAnnotation(propDef.SetMethod); ConvertAttributes(astProp.Setter, propDef.SetMethod); ConvertCustomAttributes(astProp.Setter, propDef.SetMethod.Parameters.Last(), AttributeTarget.Param); + + if ((setterModifiers & Modifiers.VisibilityMask) != (astProp.Modifiers & Modifiers.VisibilityMask)) + astProp.Setter.Modifiers = setterModifiers & Modifiers.VisibilityMask; } ConvertCustomAttributes(astProp, propDef); return astProp; From fe345d5e0c5ad8337b0c0c2b5106a283184f3564 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Tue, 8 Mar 2011 05:03:43 +0100 Subject: [PATCH 07/16] Support explicit interface implementation for events. --- ICSharpCode.Decompiler/Ast/AstBuilder.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index 32d3bf9d2..de016ae24 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -702,7 +702,10 @@ namespace ICSharpCode.Decompiler.Ast astEvent.AddAnnotation(eventDef); astEvent.Name = CleanName(eventDef.Name); astEvent.ReturnType = ConvertType(eventDef.EventType, eventDef); - astEvent.Modifiers = ConvertModifiers(eventDef.AddMethod); + if (eventDef.AddMethod == null || !eventDef.AddMethod.HasOverrides) + astEvent.Modifiers = ConvertModifiers(eventDef.AddMethod); + else + astEvent.PrivateImplementationType = ConvertType(eventDef.AddMethod.Overrides.First().DeclaringType); if (eventDef.AddMethod != null) { astEvent.AddAccessor = new Accessor { Body = AstMethodBodyBuilder.CreateMethodBody(eventDef.AddMethod, context) From 9421c4591a2a9da1b6222dd03c82f072a657a640 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Tue, 8 Mar 2011 05:44:22 +0100 Subject: [PATCH 08/16] Fix decompiling iterator blocks in debug builds when there is no try-finally block and the "ret(0)" is reached by fall-through (instead of by a jump instruction). Closes #68. --- .../ILAst/YieldReturnDecompiler.cs | 15 ++++++++------- ICSharpCode.Decompiler/Tests/YieldReturn.cs | 8 ++++++++ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs b/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs index 9e653d374..d7b83381c 100644 --- a/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs +++ b/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs @@ -690,18 +690,19 @@ namespace ICSharpCode.Decompiler.ILAst // Now verify that the last instruction in the body is 'ret(false)' if (returnVariable != null) { // If we don't have a return variable, we already verified that above. - if (bodyLength < 2) - throw new YieldAnalysisFailedException(); - ILExpression leave = body[bodyLength - 1] as ILExpression; - if (leave == null || leave.Operand != returnLabel || !(leave.Code == ILCode.Br || leave.Code == ILCode.Leave)) - throw new YieldAnalysisFailedException(); - ILExpression store0 = body[bodyLength - 2] as ILExpression; + // If we do have one, check for 'stloc(returnVariable, ldc.i4(0))' + + // Maybe might be a jump to the return label after the stloc: + ILExpression leave = body.ElementAtOrDefault(bodyLength - 1) as ILExpression; + if (leave != null && (leave.Code == ILCode.Br || leave.Code == ILCode.Leave) && leave.Operand == returnLabel) + bodyLength--; + ILExpression store0 = body.ElementAtOrDefault(bodyLength - 1) as ILExpression; if (store0 == null || store0.Code != ILCode.Stloc || store0.Operand != returnVariable) throw new YieldAnalysisFailedException(); if (store0.Arguments[0].Code != ILCode.Ldc_I4 || (int)store0.Arguments[0].Operand != 0) throw new YieldAnalysisFailedException(); - bodyLength -= 2; // don't conside the 'ret(false)' part of the body + bodyLength--; // don't conside the stloc instruction to be part of the body } // verify that the last element in the body is a label pointing to the 'ret(false)' returnFalseLabel = body.ElementAtOrDefault(bodyLength - 1) as ILLabel; diff --git a/ICSharpCode.Decompiler/Tests/YieldReturn.cs b/ICSharpCode.Decompiler/Tests/YieldReturn.cs index de9bd1efb..77e6a28d0 100644 --- a/ICSharpCode.Decompiler/Tests/YieldReturn.cs +++ b/ICSharpCode.Decompiler/Tests/YieldReturn.cs @@ -100,4 +100,12 @@ public static class YieldReturn yield return () => copy; } } + + public static IEnumerable GetEvenNumbers(int n) + { + for (int i = 0; i < n; i++) { + if (i % 2 == 0) + yield return i; + } + } } From 8484a217672314de88d4b78c3d732ebf23edd1b1 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Tue, 8 Mar 2011 09:06:19 +0100 Subject: [PATCH 09/16] Add support for Modifiers.Any (for pattern-matching) and for AttributeSection patterns. --- .../CSharp/Ast/CSharpModifierToken.cs | 5 +++- .../CSharp/Ast/Modifiers.cs | 5 ++++ .../CSharp/Ast/PatternMatching/Pattern.cs | 5 ++++ .../CSharp/Ast/PatternMatching/Placeholder.cs | 29 +++++++++++++++++++ .../CSharp/Ast/TypeMembers/AttributedNode.cs | 2 +- 5 files changed, 44 insertions(+), 2 deletions(-) diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/CSharpModifierToken.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/CSharpModifierToken.cs index ec0a85705..3fc7fa291 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/CSharpModifierToken.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/CSharpModifierToken.cs @@ -72,7 +72,10 @@ namespace ICSharpCode.NRefactory.CSharp new KeyValuePair(Modifiers.Volatile, "volatile".Length), new KeyValuePair(Modifiers.Extern, "extern".Length), new KeyValuePair(Modifiers.Partial, "partial".Length), - new KeyValuePair(Modifiers.Const, "const".Length) + new KeyValuePair(Modifiers.Const, "const".Length), + + // even though it's used for patterns only, it needs to be in this table to be usable in the AST + new KeyValuePair(Modifiers.Any, "any".Length) }; public static IEnumerable AllModifiers { diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Modifiers.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Modifiers.cs index a05a817aa..700a3c04d 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Modifiers.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Modifiers.cs @@ -66,4 +66,9 @@ namespace ICSharpCode.NRefactory.CSharp //Final = 0x400000, //Literal = 0x800000, VisibilityMask = Private | Internal | Protected | Public, + + /// + /// Special value used to match any modifiers during pattern matching. + /// + Any = unchecked((int)0x80000000) }} diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Pattern.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Pattern.cs index 23f5c2ea5..85fd861bb 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Pattern.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Pattern.cs @@ -64,6 +64,11 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching return p != null ? new VariablePlaceholder(p) : null; } + public static implicit operator AttributeSection(Pattern p) + { + return p != null ? new AttributeSectionPlaceholder(p) : null; + } + // Make debugging easier by giving Patterns a ToString() implementation public override string ToString() { diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Placeholder.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Placeholder.cs index f68feb443..67987595f 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Placeholder.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Placeholder.cs @@ -152,4 +152,33 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching return child.DoMatchCollection(role, pos, match, backtrackingStack); } } + + sealed class AttributeSectionPlaceholder : AttributeSection + { + readonly AstNode child; + + public AttributeSectionPlaceholder(AstNode child) + { + this.child = child; + } + + public override NodeType NodeType { + get { return NodeType.Placeholder; } + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return ((IPatternAstVisitor)visitor).VisitPlaceholder(this, child, data); + } + + protected internal override bool DoMatch(AstNode other, Match match) + { + return child.DoMatch(other, match); + } + + internal override bool DoMatchCollection(Role role, AstNode pos, Match match, Stack backtrackingStack) + { + return child.DoMatchCollection(role, pos, match, backtrackingStack); + } + } } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/AttributedNode.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/AttributedNode.cs index 41eec380d..af5e584dc 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/AttributedNode.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/AttributedNode.cs @@ -60,7 +60,7 @@ namespace ICSharpCode.NRefactory.CSharp protected bool MatchAttributesAndModifiers(AttributedNode o, PatternMatching.Match match) { - return this.Modifiers == o.Modifiers && this.Attributes.DoMatch(o.Attributes, match); + return (this.Modifiers == Modifiers.Any || this.Modifiers == o.Modifiers) && this.Attributes.DoMatch(o.Attributes, match); } } } From 41d6c3f6ebf18c54933c14d36629b4167b4f05c9 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Tue, 8 Mar 2011 09:06:50 +0100 Subject: [PATCH 10/16] Don't show compiler-generated code for automatic properties. Closes #69. --- ICSharpCode.Decompiler/Ast/AstBuilder.cs | 6 +- .../Transforms/PatternStatementTransform.cs | 79 ++++++++++++++++++- .../Ast/Transforms/TransformationPipeline.cs | 2 +- ICSharpCode.Decompiler/DecompilerSettings.cs | 45 +++++++++++ 4 files changed, 126 insertions(+), 6 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index de016ae24..c11a9c945 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -50,8 +50,10 @@ namespace ICSharpCode.Decompiler.Ast return true; } FieldDefinition field = member as FieldDefinition; - if (field != null) { - if (settings.AnonymousMethods && field.Name.StartsWith("CS$<>", StringComparison.Ordinal) && field.IsCompilerGenerated()) + if (field != null && field.IsCompilerGenerated()) { + if (settings.AnonymousMethods && field.Name.StartsWith("CS$<>", StringComparison.Ordinal)) + return true; + if (settings.AutomaticProperties && field.Name.StartsWith("<", StringComparison.Ordinal) && field.Name.EndsWith("BackingField", StringComparison.Ordinal)) return true; } return false; diff --git a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs index f41fe43ec..641640e6f 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs @@ -14,11 +14,24 @@ namespace ICSharpCode.Decompiler.Ast.Transforms /// public class PatternStatementTransform : IAstTransform { + DecompilerContext context; + + public PatternStatementTransform(DecompilerContext context) + { + if (context == null) + throw new ArgumentNullException("context"); + this.context = context; + } + public void Run(AstNode compilationUnit) { + if (context.Settings.UsingStatement) TransformUsings(compilationUnit); - TransformForeach(compilationUnit); + if (context.Settings.ForEachStatement) + TransformForeach(compilationUnit); TransformFor(compilationUnit); + if (context.Settings.AutomaticProperties) + TransformAutomaticProperties(compilationUnit); } /// @@ -99,7 +112,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms #endregion #region foreach - UsingStatement foreachPattern = new UsingStatement { + static readonly UsingStatement foreachPattern = new UsingStatement { ResourceAcquisition = new VariableDeclarationStatement { Type = new AnyNode("enumeratorType"), Variables = { @@ -188,7 +201,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms #endregion #region for - WhileStatement forPattern = new WhileStatement { + static readonly WhileStatement forPattern = new WhileStatement { Condition = new BinaryOperatorExpression { Left = new NamedNode("ident", new IdentifierExpression()), Operator = BinaryOperatorType.Any, @@ -237,5 +250,65 @@ namespace ICSharpCode.Decompiler.Ast.Transforms } } #endregion + + #region Automatic Properties + static readonly PropertyDeclaration automaticPropertyPattern = new PropertyDeclaration { + Attributes = { new Repeat(new AnyNode()) }, + Modifiers = Modifiers.Any, + ReturnType = new AnyNode(), + Getter = new Accessor { + Attributes = { new Repeat(new AnyNode()) }, + Modifiers = Modifiers.Any, + Body = new BlockStatement { + new ReturnStatement { + Expression = new NamedNode("fieldReference", new MemberReferenceExpression { Target = new ThisReferenceExpression() }) + } + } + }, + Setter = new Accessor { + Attributes = { new Repeat(new AnyNode()) }, + Modifiers = Modifiers.Any, + Body = new BlockStatement { + new AssignmentExpression { + Left = new Backreference("fieldReference"), + Right = new IdentifierExpression("value") + } + }}}; + + void TransformAutomaticProperties(AstNode compilationUnit) + { + foreach (var property in compilationUnit.Descendants.OfType()) { + PropertyDefinition cecilProperty = property.Annotation(); + if (cecilProperty == null || cecilProperty.GetMethod == null || cecilProperty.SetMethod == null) + continue; + if (!(cecilProperty.GetMethod.IsCompilerGenerated() && cecilProperty.SetMethod.IsCompilerGenerated())) + continue; + Match m = automaticPropertyPattern.Match(property); + if (m != null) { + FieldDefinition field = m.Get("fieldReference").Single().Annotation(); + if (field.IsCompilerGenerated()) { + RemoveCompilerGeneratedAttribute(property.Getter.Attributes); + RemoveCompilerGeneratedAttribute(property.Setter.Attributes); + property.Getter.Body = null; + property.Setter.Body = null; + } + } + } + } + + void RemoveCompilerGeneratedAttribute(AstNodeCollection attributeSections) + { + foreach (AttributeSection section in attributeSections) { + foreach (var attr in section.Attributes) { + TypeReference tr = attr.Type.Annotation(); + if (tr != null && tr.Namespace == "System.Runtime.CompilerServices" && tr.Name == "CompilerGeneratedAttribute") { + attr.Remove(); + } + } + if (section.Attributes.Count == 0) + section.Remove(); + } + } + #endregion } } diff --git a/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs b/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs index d240c7583..b4aedf175 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs @@ -19,7 +19,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms return new IAstTransform[] { new PushNegation(), new DelegateConstruction(context), - new PatternStatementTransform(), + new PatternStatementTransform(context), new ConvertConstructorCallIntoInitializer(), new ReplaceMethodCallsWithOperators(), }; diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs index a1770d771..541b95a53 100644 --- a/ICSharpCode.Decompiler/DecompilerSettings.cs +++ b/ICSharpCode.Decompiler/DecompilerSettings.cs @@ -41,6 +41,51 @@ namespace ICSharpCode.Decompiler } } + bool automaticProperties = true; + + /// + /// Decompile automatic properties + /// + public bool AutomaticProperties { + get { return automaticProperties; } + set { + if (automaticProperties != value) { + automaticProperties = value; + OnPropertyChanged("AutomaticProperties"); + } + } + } + + bool usingStatement = true; + + /// + /// Decompile using statements. + /// + public bool UsingStatement { + get { return usingStatement; } + set { + if (usingStatement != value) { + usingStatement = value; + OnPropertyChanged("UsingStatement"); + } + } + } + + bool forEachStatement = true; + + /// + /// Decompile foreach statements. + /// + public bool ForEachStatement { + get { return forEachStatement; } + set { + if (forEachStatement != value) { + forEachStatement = value; + OnPropertyChanged("ForEachStatement"); + } + } + } + public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) From 530d6ace6c0c73b5af8ef3c8423ca4ce30fbd4c3 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Tue, 8 Mar 2011 21:56:05 +0100 Subject: [PATCH 11/16] XAML streams inside Silverlight's .resources-files can be displayed --- ILSpy/ILSpy.csproj | 1 + ILSpy/XamlResourceNode.cs | 67 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 ILSpy/XamlResourceNode.cs diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index b094e107b..fae100be4 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -120,6 +120,7 @@ Code + README.txt diff --git a/ILSpy/XamlResourceNode.cs b/ILSpy/XamlResourceNode.cs new file mode 100644 index 000000000..7610ca6b7 --- /dev/null +++ b/ILSpy/XamlResourceNode.cs @@ -0,0 +1,67 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under MIT X11 license (for details please see \doc\license.txt) + +using System; +using System.ComponentModel.Composition; +using System.IO; +using System.Threading.Tasks; + +using ICSharpCode.AvalonEdit.Highlighting; +using ICSharpCode.ILSpy.TextView; +using ICSharpCode.ILSpy.TreeNodes; + +namespace ICSharpCode.ILSpy.Xaml +{ + [Export(typeof(IResourceNodeFactory))] + sealed class XamlResourceNodeFactory : IResourceNodeFactory + { + public ILSpyTreeNode CreateNode(Mono.Cecil.Resource resource) + { + return null; + } + + public ILSpyTreeNode CreateNode(string key, Stream data) + { + if (key.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase)) + return new XamlResourceEntryNode(key, data); + else + return null; + } + } + + sealed class XamlResourceEntryNode : ResourceEntryNode + { + string xaml; + + public XamlResourceEntryNode(string key, Stream data) : base(key, data) + { + } + + internal override bool View(DecompilerTextView textView) + { + AvalonEditTextOutput output = new AvalonEditTextOutput(); + IHighlightingDefinition highlighting = null; + + textView.RunWithCancellation( + token => Task.Factory.StartNew( + () => { + try { + // cache read XAML because stream will be closed after first read + if (xaml == null) { + using (var reader = new StreamReader(data)) { + xaml = reader.ReadToEnd(); + } + } + output.Write(xaml); + highlighting = HighlightingManager.Instance.GetDefinitionByExtension(".xml"); + } catch (Exception ex) { + output.Write(ex.ToString()); + } + return output; + }), + t => textView.Show(t.Result, highlighting) + ); + return true; + } + } +} From f4da816fc469789f92de78016a61568b6638cae2 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Wed, 9 Mar 2011 10:03:17 +0100 Subject: [PATCH 12/16] NRefactory AstNode: add DescendantsAndSelf property. --- NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstNode.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstNode.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstNode.cs index 2dab5a5ad..dafcdc253 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstNode.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstNode.cs @@ -157,6 +157,15 @@ namespace ICSharpCode.NRefactory.CSharp } } + /// + /// Gets all descendants of this node (including this node itself). + /// + public IEnumerable DescendantsAndSelf { + get { + return Utils.TreeTraversal.PreOrder(this, n => n.Children); + } + } + /// /// Gets the first child with the specified role. /// Returns the role's null object if the child is not found. From 8ffb317cc4b6ce68c0efa64418dc4bf8d3680141 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Wed, 9 Mar 2011 10:04:04 +0100 Subject: [PATCH 13/16] Add support for decompiling do-while loops. --- .../Transforms/PatternStatementTransform.cs | 53 +++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs index 641640e6f..48f53da25 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs @@ -26,10 +26,11 @@ namespace ICSharpCode.Decompiler.Ast.Transforms public void Run(AstNode compilationUnit) { if (context.Settings.UsingStatement) - TransformUsings(compilationUnit); + TransformUsings(compilationUnit); if (context.Settings.ForEachStatement) TransformForeach(compilationUnit); TransformFor(compilationUnit); + TransformDoWhile(compilationUnit); if (context.Settings.AutomaticProperties) TransformAutomaticProperties(compilationUnit); } @@ -219,8 +220,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms Right = new AnyNode() })) } - } - }; + }}; public void TransformFor(AstNode compilationUnit) { @@ -251,6 +251,53 @@ namespace ICSharpCode.Decompiler.Ast.Transforms } #endregion + #region doWhile + static readonly WhileStatement doWhilePattern = new WhileStatement { + Condition = new PrimitiveExpression(true), + EmbeddedStatement = new BlockStatement { + Statements = { + new Repeat(new AnyNode("statement")), + new IfElseStatement { + Condition = new AnyNode("condition"), + TrueStatement = new BlockStatement { new BreakStatement() } + } + } + }}; + + public void TransformDoWhile(AstNode compilationUnit) + { + foreach (WhileStatement whileLoop in compilationUnit.Descendants.OfType().ToArray()) { + Match m = doWhilePattern.Match(whileLoop); + if (m != null) { + DoWhileStatement doLoop = new DoWhileStatement(); + doLoop.Condition = new UnaryOperatorExpression(UnaryOperatorType.Not, m.Get("condition").Single().Detach()); + doLoop.Condition.AcceptVisitor(new PushNegation(), null); + BlockStatement block = (BlockStatement)whileLoop.EmbeddedStatement; + block.Statements.Last().Remove(); // remove if statement + doLoop.EmbeddedStatement = block.Detach(); + whileLoop.ReplaceWith(doLoop); + + // we may have to extract variable definitions out of the loop if they were used in the condition: + foreach (var varDecl in block.Statements.OfType()) { + VariableInitializer v = varDecl.Variables.Single(); + if (doLoop.Condition.DescendantsAndSelf.OfType().Any(i => i.Identifier == v.Name)) { + AssignmentExpression assign = new AssignmentExpression(new IdentifierExpression(v.Name), v.Initializer.Detach()); + // move annotations from v to assign: + assign.CopyAnnotationsFrom(v); + v.RemoveAnnotations(); + // remove varDecl with assignment; and move annotations from varDecl to the ExpressionStatement: + varDecl.ReplaceWith(new ExpressionStatement(assign).CopyAnnotationsFrom(varDecl)); + varDecl.RemoveAnnotations(); + + // insert the varDecl above the do-while loop: + doLoop.Parent.InsertChildBefore(doLoop, varDecl, BlockStatement.StatementRole); + } + } + } + } + } + #endregion + #region Automatic Properties static readonly PropertyDeclaration automaticPropertyPattern = new PropertyDeclaration { Attributes = { new Repeat(new AnyNode()) }, From ca720c105d34566ff9838e9b86a9357cdb58544c Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Wed, 9 Mar 2011 10:10:15 +0100 Subject: [PATCH 14/16] Type analysis for "yield return" --- ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs | 11 +++++++++++ ICSharpCode.Decompiler/Tests/YieldReturn.cs | 7 +++++++ 2 files changed, 18 insertions(+) diff --git a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs index b7afed8d4..e09d14e33 100644 --- a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs +++ b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs @@ -458,11 +458,22 @@ namespace ICSharpCode.Decompiler.ILAst case ILCode.Rethrow: case ILCode.LoopOrSwitchBreak: case ILCode.LoopContinue: + case ILCode.YieldBreak: return null; case ILCode.Ret: if (forceInferChildren && expr.Arguments.Count == 1) InferTypeForExpression(expr.Arguments[0], context.CurrentMethod.ReturnType); return null; + case ILCode.YieldReturn: + if (forceInferChildren) { + GenericInstanceType genericType = context.CurrentMethod.ReturnType as GenericInstanceType; + if (genericType != null) { // IEnumerable or IEnumerator + InferTypeForExpression(expr.Arguments[0], genericType.GenericArguments[0]); + } else { // non-generic IEnumerable or IEnumerator + InferTypeForExpression(expr.Arguments[0], typeSystem.Object); + } + } + return null; #endregion case ILCode.Pop: return null; diff --git a/ICSharpCode.Decompiler/Tests/YieldReturn.cs b/ICSharpCode.Decompiler/Tests/YieldReturn.cs index 77e6a28d0..f7c7f73f6 100644 --- a/ICSharpCode.Decompiler/Tests/YieldReturn.cs +++ b/ICSharpCode.Decompiler/Tests/YieldReturn.cs @@ -108,4 +108,11 @@ public static class YieldReturn yield return i; } } + + public static IEnumerable YieldChars() + { + yield return 'a'; + yield return 'b'; + yield return 'c'; + } } From 5fc5d7c16c96038e41bf8e37c2f64509bc0d86c9 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Wed, 9 Mar 2011 10:58:48 +0100 Subject: [PATCH 15/16] AstNode: add Invoke() methods to AstType (builds InvocationExpression for static methods) --- .../CSharp/Ast/AstType.cs | 24 +++++++++++++++++++ .../CSharp/Ast/PatternMatching/Pattern.cs | 5 ++++ 2 files changed, 29 insertions(+) diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstType.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstType.cs index a35204e3c..5582b9012 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstType.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstType.cs @@ -53,6 +53,30 @@ namespace ICSharpCode.NRefactory.CSharp return new TypeReferenceExpression { Type = this }.Member(memberName); } + /// + /// Builds an invocation expression using this type as target. + /// + public InvocationExpression Invoke(string methodName, IEnumerable arguments) + { + return new TypeReferenceExpression { Type = this }.Invoke(methodName, arguments); + } + + /// + /// Builds an invocation expression using this type as target. + /// + public InvocationExpression Invoke(string methodName, params Expression[] arguments) + { + return new TypeReferenceExpression { Type = this }.Invoke(methodName, arguments); + } + + /// + /// Builds an invocation expression using this type as target. + /// + public InvocationExpression Invoke(string methodName, IEnumerable typeArguments, IEnumerable arguments) + { + return new TypeReferenceExpression { Type = this }.Invoke(methodName, typeArguments, arguments); + } + public static AstType Create(Type type) { switch (Type.GetTypeCode(type)) { diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Pattern.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Pattern.cs index 85fd861bb..ed2122d19 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Pattern.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Pattern.cs @@ -34,6 +34,11 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching return p != null ? new TypePlaceholder(p) : null; } + public AstType ToType() + { + return new TypePlaceholder(this); + } + public static implicit operator Expression(Pattern p) { return p != null ? new ExpressionPlaceholder(p) : null; From f3069b99f9ddb6b116ccaa01965564346183f4b5 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Wed, 9 Mar 2011 10:59:11 +0100 Subject: [PATCH 16/16] 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;