From afa4db00b65b493c73ada852fd534ef227d4f381 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Tue, 16 Feb 2021 23:27:41 +0100 Subject: [PATCH] Fix #2297: Add an option to disable decompilation of getter-only auto properties. --- .../CSharp/CSharpDecompiler.cs | 38 +++++++++++++++++-- .../CSharp/ExpressionBuilder.cs | 3 +- .../Transforms/PatternStatementTransform.cs | 5 ++- ICSharpCode.Decompiler/DecompilerSettings.cs | 22 ++++++++++- ILSpy/Properties/Resources.Designer.cs | 9 +++++ ILSpy/Properties/Resources.resx | 3 ++ 6 files changed, 74 insertions(+), 6 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 30220b51e..e1d135139 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -23,6 +23,7 @@ using System.IO; using System.Linq; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; +using System.Text.RegularExpressions; using System.Threading; using ICSharpCode.Decompiler.CSharp.OutputVisitor; @@ -329,8 +330,29 @@ namespace ICSharpCode.Decompiler.CSharp { if (settings.AnonymousMethods && IsAnonymousMethodCacheField(field, metadata)) return true; - if (settings.AutomaticProperties && IsAutomaticPropertyBackingField(field, metadata)) + if (settings.AutomaticProperties && IsAutomaticPropertyBackingField(field, metadata, out var propertyName)) + { + if (!settings.GetterOnlyAutomaticProperties && IsGetterOnlyProperty(propertyName)) + return false; + + bool IsGetterOnlyProperty(string propertyName) + { + var properties = metadata.GetTypeDefinition(field.GetDeclaringType()).GetProperties(); + foreach (var p in properties) + { + var pd = metadata.GetPropertyDefinition(p); + string name = metadata.GetString(pd.Name); + if (!metadata.StringComparer.Equals(pd.Name, propertyName)) + continue; + PropertyAccessors accessors = pd.GetAccessors(); + return !accessors.Getter.IsNil && accessors.Setter.IsNil; + } + return false; + } + return true; + } + if (settings.SwitchStatementOnString && IsSwitchOnStringCache(field, metadata)) return true; } @@ -359,10 +381,20 @@ namespace ICSharpCode.Decompiler.CSharp return metadata.GetString(field.Name).StartsWith("<>f__switch", StringComparison.Ordinal); } - static bool IsAutomaticPropertyBackingField(SRM.FieldDefinition field, MetadataReader metadata) + static readonly Regex automaticPropertyBackingFieldRegex = new Regex(@"^<(.*)>k__BackingField$", + RegexOptions.Compiled | RegexOptions.CultureInvariant); + + static bool IsAutomaticPropertyBackingField(SRM.FieldDefinition field, MetadataReader metadata, out string propertyName) { + propertyName = null; var name = metadata.GetString(field.Name); - return name.StartsWith("<", StringComparison.Ordinal) && name.EndsWith("BackingField", StringComparison.Ordinal); + var m = automaticPropertyBackingFieldRegex.Match(name); + if (m.Success) + { + propertyName = m.Groups[1].Value; + return true; + } + return false; } static bool IsAnonymousMethodCacheField(SRM.FieldDefinition field, MetadataReader metadata) diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index e37343051..49e523971 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -292,7 +292,8 @@ namespace ICSharpCode.Decompiler.CSharp // It feels a bit hacky, though. if (settings.AutomaticProperties && PatternStatementTransform.IsBackingFieldOfAutomaticProperty(field, out var property) - && decompilationContext.CurrentMember != property) + && decompilationContext.CurrentMember != property + && (property.CanSet || settings.GetterOnlyAutomaticProperties)) { requireTarget = RequiresQualifier(property, target); } diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs index 92ef7b02c..f4a3a040e 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs @@ -103,7 +103,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms public override AstNode VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) { - if (context.Settings.AutomaticProperties) + if (context.Settings.AutomaticProperties + && (!propertyDeclaration.Setter.IsNull || context.Settings.GetterOnlyAutomaticProperties)) { AstNode result = TransformAutomaticProperty(propertyDeclaration); if (result != null) @@ -738,6 +739,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms if (field != null && IsBackingFieldOfAutomaticProperty(field, out var property) && CanTransformToAutomaticProperty(property) && currentMethod.AccessorOwner != property) { + if (!property.CanSet && !context.Settings.GetterOnlyAutomaticProperties) + return null; parent.RemoveAnnotations(); parent.AddAnnotation(new MemberResolveResult(mrr.TargetResult, property)); return Identifier.Create(property.Name); diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs index d8c8b700c..b7c467223 100644 --- a/ICSharpCode.Decompiler/DecompilerSettings.cs +++ b/ICSharpCode.Decompiler/DecompilerSettings.cs @@ -93,6 +93,7 @@ namespace ICSharpCode.Decompiler dictionaryInitializers = false; extensionMethodsInCollectionInitializers = false; useRefLocalsForAccurateOrderOfEvaluation = false; + getterOnlyAutomaticProperties = false; } if (languageVersion < CSharp.LanguageVersion.CSharp7) { @@ -159,7 +160,8 @@ namespace ICSharpCode.Decompiler || discards || localFunctions) return CSharp.LanguageVersion.CSharp7; if (awaitInCatchFinally || useExpressionBodyForCalculatedGetterOnlyProperties || nullPropagation - || stringInterpolation || dictionaryInitializers || extensionMethodsInCollectionInitializers || useRefLocalsForAccurateOrderOfEvaluation) + || stringInterpolation || dictionaryInitializers || extensionMethodsInCollectionInitializers + || useRefLocalsForAccurateOrderOfEvaluation || getterOnlyAutomaticProperties) return CSharp.LanguageVersion.CSharp6; if (asyncAwait) return CSharp.LanguageVersion.CSharp5; @@ -572,6 +574,24 @@ namespace ICSharpCode.Decompiler } } + bool getterOnlyAutomaticProperties = true; + + /// + /// Decompile getter-only automatic properties + /// + [Category("C# 6.0 / VS 2015")] + [Description("DecompilerSettings.GetterOnlyAutomaticProperties")] + public bool GetterOnlyAutomaticProperties { + get { return getterOnlyAutomaticProperties; } + set { + if (getterOnlyAutomaticProperties != value) + { + getterOnlyAutomaticProperties = value; + OnPropertyChanged(); + } + } + } + bool automaticEvents = true; /// diff --git a/ILSpy/Properties/Resources.Designer.cs b/ILSpy/Properties/Resources.Designer.cs index 5702efa5f..a2d18e3c7 100644 --- a/ILSpy/Properties/Resources.Designer.cs +++ b/ILSpy/Properties/Resources.Designer.cs @@ -927,6 +927,15 @@ namespace ICSharpCode.ILSpy.Properties { } } + /// + /// Looks up a localized string similar to Decompile getter-only automatic properties. + /// + public static string DecompilerSettings_GetterOnlyAutomaticProperties { + get { + return ResourceManager.GetString("DecompilerSettings.GetterOnlyAutomaticProperties", resourceCulture); + } + } + /// /// Looks up a localized string similar to Include XML documentation comments in the decompiled code. /// diff --git a/ILSpy/Properties/Resources.resx b/ILSpy/Properties/Resources.resx index 56be5cf81..20023a581 100644 --- a/ILSpy/Properties/Resources.resx +++ b/ILSpy/Properties/Resources.resx @@ -339,6 +339,9 @@ Are you sure you want to continue? Function pointers + + Decompile getter-only automatic properties + Include XML documentation comments in the decompiled code