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;
}
}