From bc5d078febc5ea54412b080d6cc1f54aebb16fa9 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 6 Jun 2021 20:18:18 +0200 Subject: [PATCH] Fix #1698: Readonly auto properties from VB.NET are not properly decompiled --- .../ICSharpCode.Decompiler.Tests.csproj | 2 + .../TestCases/ILPretty/Issue1325.cs | 6 +-- .../TestCases/VBPretty/VBPropertiesTest.cs | 39 +++++++++++++++++++ .../TestCases/VBPretty/VBPropertiesTest.vb | 37 ++++++++++++++++++ .../VBPrettyTestRunner.cs | 8 +++- .../CSharp/CSharpDecompiler.cs | 7 +++- .../Transforms/PatternStatementTransform.cs | 11 +++--- 7 files changed, 99 insertions(+), 11 deletions(-) create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBPropertiesTest.cs create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBPropertiesTest.vb diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index 386914ee3..e0334ffcb 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -107,6 +107,7 @@ + @@ -308,6 +309,7 @@ + diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1325.cs b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1325.cs index 6c618ec71..02b8066a5 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1325.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1325.cs @@ -1,6 +1,5 @@ using System; using System.ComponentModel; -using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; @@ -13,6 +12,8 @@ using Microsoft.VisualBasic.CompilerServices; [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] +#pragma warning disable format + namespace Issue1325 { [StandardModule] @@ -34,9 +35,6 @@ namespace Issue1325 internal class Test { - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - [CompilerGenerated] - private string _Unparameterized; public string Parameterized { get { throw new NotImplementedException(); diff --git a/ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBPropertiesTest.cs b/ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBPropertiesTest.cs new file mode 100644 index 000000000..0c65e65c9 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBPropertiesTest.cs @@ -0,0 +1,39 @@ +using System; + +public class VBPropertiesTest +{ + private int _fullProperty; + + public int FullProperty { + get { + return _fullProperty; + } + set { + _fullProperty = value; + } + } + + public int AutoProperty { get; set; } + +#if ROSLYN + public int ReadOnlyAutoProperty { get; } + public VBPropertiesTest() + { + ReadOnlyAutoProperty = 32; + } +#endif + + public void TestMethod() + { + FullProperty = 42; + _fullProperty = 24; + AutoProperty = 4711; + + Console.WriteLine(AutoProperty); + Console.WriteLine(_fullProperty); + Console.WriteLine(FullProperty); +#if ROSLYN + Console.WriteLine(ReadOnlyAutoProperty); +#endif + } +} diff --git a/ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBPropertiesTest.vb b/ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBPropertiesTest.vb new file mode 100644 index 000000000..f5d142265 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBPropertiesTest.vb @@ -0,0 +1,37 @@ +Imports System +Public Class VBPropertiesTest + Private _fullProperty As Integer + + Property FullProperty As Integer + Get + Return _fullProperty + End Get + Set(value As Integer) + _fullProperty = value + End Set + End Property + + Property AutoProperty As Integer + +#If ROSLYN Then + ReadOnly Property ReadOnlyAutoProperty As Integer + + Sub New() + Me.ReadOnlyAutoProperty = 32 + End Sub +#End If + + Sub TestMethod() + Me.FullProperty = 42 + Me._fullProperty = 24 + Me.AutoProperty = 4711 + + Console.WriteLine(Me.AutoProperty) + Console.WriteLine(Me._fullProperty) + Console.WriteLine(Me.FullProperty) + +#If ROSLYN Then + Console.WriteLine(Me.ReadOnlyAutoProperty) +#End If + End Sub +End Class \ No newline at end of file diff --git a/ICSharpCode.Decompiler.Tests/VBPrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/VBPrettyTestRunner.cs index 7c8dbde4d..a0559a39c 100644 --- a/ICSharpCode.Decompiler.Tests/VBPrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/VBPrettyTestRunner.cs @@ -102,6 +102,12 @@ namespace ICSharpCode.Decompiler.Tests Run(options: options | CompilerOptions.Library); } + [Test] + public void VBPropertiesTest([ValueSource(nameof(defaultOptions))] CompilerOptions options) + { + Run(options: options | CompilerOptions.Library); + } + void Run([CallerMemberName] string testName = null, CompilerOptions options = CompilerOptions.UseDebug, DecompilerSettings settings = null) { var vbFile = Path.Combine(TestCasePath, testName + ".vb"); @@ -115,7 +121,7 @@ namespace ICSharpCode.Decompiler.Tests var executable = Tester.CompileVB(vbFile, options | CompilerOptions.ReferenceVisualBasic, exeFile); var decompiled = Tester.DecompileCSharp(executable.PathToAssembly, settings); - CodeAssert.FilesAreEqual(csFile, decompiled); + CodeAssert.FilesAreEqual(csFile, decompiled, Tester.GetPreprocessorSymbols(options).ToArray()); } } } diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 896a70eb8..90b6dfcab 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -386,7 +386,7 @@ namespace ICSharpCode.Decompiler.CSharp static readonly Regex automaticPropertyBackingFieldRegex = new Regex(@"^<(.*)>k__BackingField$", RegexOptions.Compiled | RegexOptions.CultureInvariant); - static bool IsAutomaticPropertyBackingField(SRM.FieldDefinition field, MetadataReader metadata, out string propertyName) + static bool IsAutomaticPropertyBackingField(FieldDefinition field, MetadataReader metadata, out string propertyName) { propertyName = null; var name = metadata.GetString(field.Name); @@ -396,6 +396,11 @@ namespace ICSharpCode.Decompiler.CSharp propertyName = m.Groups[1].Value; return true; } + if (name.StartsWith("_", StringComparison.Ordinal)) + { + propertyName = name.Substring(1); + return field.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.CompilerGenerated); + } return false; } diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs index 02978e5e4..e61bbe41c 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs @@ -592,15 +592,15 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms } }; - bool CanTransformToAutomaticProperty(IProperty property) + bool CanTransformToAutomaticProperty(IProperty property, bool accessorsMustBeCompilerGenerated) { if (!property.CanGet) return false; - if (!property.Getter.IsCompilerGenerated()) + if (accessorsMustBeCompilerGenerated && !property.Getter.IsCompilerGenerated()) return false; if (property.Setter is IMethod setter) { - if (!setter.IsCompilerGenerated()) + if (accessorsMustBeCompilerGenerated && !setter.IsCompilerGenerated()) return false; if (setter.HasReadonlyModifier()) return false; @@ -611,7 +611,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms PropertyDeclaration TransformAutomaticProperty(PropertyDeclaration propertyDeclaration) { IProperty property = propertyDeclaration.GetSymbol() as IProperty; - if (!CanTransformToAutomaticProperty(property)) + if (!CanTransformToAutomaticProperty(property, !property.DeclaringTypeDefinition.Fields.Any(f => f.Name == "_" + property.Name && f.IsCompilerGenerated()))) return null; IField field = null; Match m = automaticPropertyPattern.Match(propertyDeclaration); @@ -741,7 +741,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms var mrr = parent.Annotation(); var field = mrr?.Member as IField; if (field != null && IsBackingFieldOfAutomaticProperty(field, out var property) - && CanTransformToAutomaticProperty(property) && currentMethod.AccessorOwner != property) + && CanTransformToAutomaticProperty(property, !(field.IsCompilerGenerated() && field.Name == "_" + property.Name)) + && currentMethod.AccessorOwner != property) { if (!property.CanSet && !context.Settings.GetterOnlyAutomaticProperties) return null;