diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs index b8be46721..895efc1f1 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs @@ -62,6 +62,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public struct NormalStruct { private readonly int dummy; + private int[] arr; public int Property { get { @@ -96,8 +97,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } + public ref int RefProperty => ref arr[0]; + public ref readonly int RefReadonlyProperty => ref arr[0]; + public readonly ref int ReadonlyRefProperty => ref arr[0]; + public readonly ref readonly int ReadonlyRefReadonlyProperty => ref arr[0]; +#endif + + public ref readonly int this[in int index] => ref arr[index]; + public event EventHandler NormalEvent; +#if CS80 public readonly event EventHandler ReadOnlyEvent { add { } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs index 6776082c5..c6b563bec 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs @@ -1574,6 +1574,9 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax decl.AddAnnotation(new MemberResolveResult(null, property)); } decl.ReturnType = ConvertType(property.ReturnType); + if (property.ReturnTypeIsRefReadOnly && decl.ReturnType is ComposedType ct && ct.HasRefSpecifier) { + ct.HasReadOnlySpecifier = true; + } decl.Name = property.Name; decl.Getter = ConvertAccessor(property.Getter, property.Accessibility, false); decl.Setter = ConvertAccessor(property.Setter, property.Accessibility, true); @@ -1601,6 +1604,9 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax decl.AddAnnotation(new MemberResolveResult(null, indexer)); } decl.ReturnType = ConvertType(indexer.ReturnType); + if (indexer.ReturnTypeIsRefReadOnly && decl.ReturnType is ComposedType ct && ct.HasRefSpecifier) { + ct.HasReadOnlySpecifier = true; + } foreach (IParameter p in indexer.Parameters) { decl.Parameters.Add(ConvertParameter(p)); } @@ -1695,6 +1701,9 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax decl.Modifiers = GetMemberModifiers(op); decl.OperatorType = opType.Value; decl.ReturnType = ConvertType(op.ReturnType); + if (op.ReturnTypeIsRefReadOnly && decl.ReturnType is ComposedType ct && ct.HasRefSpecifier) { + ct.HasReadOnlySpecifier = true; + } foreach (IParameter p in op.Parameters) { decl.Parameters.Add(ConvertParameter(p)); } diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs b/ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs index 306892735..b4b88cbc1 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs @@ -144,7 +144,10 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms Name = Pattern.AnyString, PrivateImplementationType = new AnyNodeOrNull(), ReturnType = new AnyNode(), - Getter = new Accessor() { Body = new BlockStatement() { new ReturnStatement(new AnyNode("expression")) } } + Getter = new Accessor() { + Modifiers = Modifiers.Any, + Body = new BlockStatement() { new ReturnStatement(new AnyNode("expression")) } + } }; static readonly IndexerDeclaration CalculatedGetterOnlyIndexerPattern = new IndexerDeclaration() { @@ -153,14 +156,25 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms PrivateImplementationType = new AnyNodeOrNull(), Parameters = { new Repeat(new AnyNode()) }, ReturnType = new AnyNode(), - Getter = new Accessor() { Body = new BlockStatement() { new ReturnStatement(new AnyNode("expression")) } } + Getter = new Accessor() { + Modifiers = Modifiers.Any, + Body = new BlockStatement() { new ReturnStatement(new AnyNode("expression")) } + } }; + /// + /// Modifiers that are emitted on accessors, but can be moved to the property declaration. + /// + const Modifiers movableModifiers = Modifiers.Readonly; + void SimplifyPropertyDeclaration(PropertyDeclaration propertyDeclaration) { var m = CalculatedGetterOnlyPropertyPattern.Match(propertyDeclaration); if (!m.Success) return; + if ((propertyDeclaration.Getter.Modifiers & ~movableModifiers) != 0) + return; + propertyDeclaration.Modifiers |= propertyDeclaration.Getter.Modifiers; propertyDeclaration.ExpressionBody = m.Get("expression").Single().Detach(); propertyDeclaration.Getter.Remove(); } @@ -170,6 +184,9 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms var m = CalculatedGetterOnlyIndexerPattern.Match(indexerDeclaration); if (!m.Success) return; + if ((indexerDeclaration.Getter.Modifiers & ~movableModifiers) != 0) + return; + indexerDeclaration.Modifiers |= indexerDeclaration.Getter.Modifiers; indexerDeclaration.ExpressionBody = m.Get("expression").Single().Detach(); indexerDeclaration.Getter.Remove(); } diff --git a/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs b/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs index 74409be17..769b5d33c 100644 --- a/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs @@ -228,7 +228,7 @@ namespace ICSharpCode.Decompiler.CSharp } w.WriteElementString("WarningLevel", "4"); w.WriteElementString("AllowUnsafeBlocks", "True"); - + if (StrongNameKeyFile != null) { w.WriteElementString("SignAssembly", "True"); w.WriteElementString("AssemblyOriginatorKeyFile", Path.GetFileName(StrongNameKeyFile)); @@ -463,7 +463,7 @@ namespace ICSharpCode.Decompiler.CSharp } catch (EndOfStreamException) { // if the .resources can't be decoded, just save them as-is } - } + } using (FileStream fs = new FileStream(Path.Combine(targetDirectory, fileName), FileMode.Create, FileAccess.Write)) { entryStream.CopyTo(fs); } @@ -515,7 +515,10 @@ namespace ICSharpCode.Decompiler.CSharp string name = b.ToString(); if (IsReservedFileSystemName(name)) return name + "_"; - return name; + else if (name == ".") + return "_"; + else + return name; } static bool IsReservedFileSystemName(string name) diff --git a/ICSharpCode.Decompiler/TypeSystem/IProperty.cs b/ICSharpCode.Decompiler/TypeSystem/IProperty.cs index 495f7c22c..388c770e0 100644 --- a/ICSharpCode.Decompiler/TypeSystem/IProperty.cs +++ b/ICSharpCode.Decompiler/TypeSystem/IProperty.cs @@ -30,5 +30,10 @@ namespace ICSharpCode.Decompiler.TypeSystem IMethod Setter { get; } bool IsIndexer { get; } + + /// + /// Gets whether the return type is 'ref readonly'. + /// + bool ReturnTypeIsRefReadOnly { get; } } } diff --git a/ICSharpCode.Decompiler/TypeSystem/ISymbol.cs b/ICSharpCode.Decompiler/TypeSystem/ISymbol.cs index b1b59c404..b269c785a 100644 --- a/ICSharpCode.Decompiler/TypeSystem/ISymbol.cs +++ b/ICSharpCode.Decompiler/TypeSystem/ISymbol.cs @@ -73,6 +73,10 @@ namespace ICSharpCode.Decompiler.TypeSystem /// Constraint on a type parameter. /// Constraint, + /// + /// Return type. Not actually an ISymbol implementation; but can appear as attribut target. + /// + ReturnType, } /// diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs index 920106c37..46cb1c99d 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs @@ -208,6 +208,10 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation case SymbolKind.Method: case SymbolKind.Accessor: return (options & TypeSystemOptions.ReadOnlyMethods) != 0; + case SymbolKind.ReturnType: + case SymbolKind.Property: + case SymbolKind.Indexer: + return true; // "ref readonly" is currently always active default: return false; } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs index 881933acd..87f7eb4c9 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs @@ -400,7 +400,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation var retParam = metadata.GetParameter(parameters.First()); if (retParam.SequenceNumber == 0) { b.AddMarshalInfo(retParam.GetMarshallingDescriptor()); - b.Add(retParam.GetCustomAttributes(), symbolKind); + b.Add(retParam.GetCustomAttributes(), SymbolKind.ReturnType); } } return b.Build(); diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataProperty.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataProperty.cs index cd98cf907..7ba165859 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataProperty.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataProperty.cs @@ -113,6 +113,13 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation } } + public bool ReturnTypeIsRefReadOnly { + get { + var propertyDef = module.metadata.GetPropertyDefinition(propertyHandle); + return propertyDef.GetCustomAttributes().HasKnownAttribute(module.metadata, KnownAttribute.IsReadOnly); + } + } + private void DecodeSignature() { var propertyDef = module.metadata.GetPropertyDefinition(propertyHandle); diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedProperty.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedProperty.cs index 6f7ea7ed4..3cd66fe33 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedProperty.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedProperty.cs @@ -65,5 +65,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public bool IsIndexer { get { return propertyDefinition.IsIndexer; } } + + public bool ReturnTypeIsRefReadOnly => propertyDefinition.ReturnTypeIsRefReadOnly; } }