diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
index 19bb00805..15aa15071 100644
--- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
+++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
@@ -911,16 +911,10 @@ namespace ICSharpCode.Decompiler.CSharp
// Remove the [DefaultMember] attribute if the class contains indexers
RemoveAttribute(typeDecl, KnownAttribute.DefaultMember);
}
- if (settings.IntroduceRefAndReadonlyModifiersOnStructs && typeDecl.ClassType == ClassType.Struct) {
- if (RemoveAttribute(typeDecl, KnownAttribute.IsByRefLike)) {
- typeDecl.Modifiers |= Modifiers.Ref;
- }
- if (RemoveAttribute(typeDecl, KnownAttribute.IsReadOnly)) {
- typeDecl.Modifiers |= Modifiers.Readonly;
- }
+ if (settings.IntroduceRefModifiersOnStructs) {
if (FindAttribute(typeDecl, KnownAttribute.Obsolete, out var attr)) {
if (obsoleteAttributePattern.IsMatch(attr)) {
- if (attr.Parent is Syntax.AttributeSection section && section.Attributes.Count == 1)
+ if (attr.Parent is AttributeSection section && section.Attributes.Count == 1)
section.Remove();
else
attr.Remove();
diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
index 52445918c..158911cb5 100644
--- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
+++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
@@ -2170,6 +2170,9 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
case ParameterModifier.This:
WriteKeyword(ParameterDeclaration.ThisModifierRole);
break;
+ case ParameterModifier.In:
+ WriteKeyword(ParameterDeclaration.InModifierRole);
+ break;
}
parameterDeclaration.Type.AcceptVisitor(this);
if (!parameterDeclaration.Type.IsNull && !string.IsNullOrEmpty(parameterDeclaration.Name)) {
diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs
index fad67c387..a3be5547a 100644
--- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs
+++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs
@@ -32,7 +32,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
Ref,
Out,
Params,
- This
+ This,
+ In
}
public class ParameterDeclaration : AstNode
@@ -42,7 +43,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public static readonly TokenRole OutModifierRole = new TokenRole("out");
public static readonly TokenRole ParamsModifierRole = new TokenRole("params");
public static readonly TokenRole ThisModifierRole = new TokenRole("this");
-
+ public static readonly TokenRole InModifierRole = new TokenRole("in");
+
public override NodeType NodeType {
get {
return NodeType.Unknown;
diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
index 54a6021dd..e2e627d05 100644
--- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
+++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
@@ -918,6 +918,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
decl.ParameterModifier = ParameterModifier.Ref;
} else if (parameter.IsOut) {
decl.ParameterModifier = ParameterModifier.Out;
+ } else if (parameter.IsIn) {
+ decl.ParameterModifier = ParameterModifier.In;
} else if (parameter.IsParams) {
decl.ParameterModifier = ParameterModifier.Params;
}
@@ -1014,6 +1016,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
case TypeKind.Struct:
classType = ClassType.Struct;
modifiers &= ~Modifiers.Sealed;
+ if (typeDefinition.IsReadOnly) {
+ modifiers |= Modifiers.Readonly;
+ }
+ if (typeDefinition.IsByRefLike) {
+ modifiers |= Modifiers.Ref;
+ }
break;
case TypeKind.Enum:
classType = ClassType.Enum;
diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs
index ef08fa1ac..39c24387f 100644
--- a/ICSharpCode.Decompiler/DecompilerSettings.cs
+++ b/ICSharpCode.Decompiler/DecompilerSettings.cs
@@ -83,7 +83,8 @@ namespace ICSharpCode.Decompiler
discards = false;
}
if (languageVersion < CSharp.LanguageVersion.CSharp7_2) {
- introduceRefAndReadonlyModifiersOnStructs = false;
+ introduceReadonlyAndInModifiers = false;
+ introduceRefModifiersOnStructs = false;
nonTrailingNamedArguments = false;
}
if (languageVersion < CSharp.LanguageVersion.CSharp7_3) {
@@ -96,7 +97,7 @@ namespace ICSharpCode.Decompiler
{
if (tupleComparisons)
return CSharp.LanguageVersion.CSharp7_3;
- if (IntroduceRefAndReadonlyModifiersOnStructs || nonTrailingNamedArguments)
+ if (introduceRefModifiersOnStructs || introduceReadonlyAndInModifiers || nonTrailingNamedArguments)
return CSharp.LanguageVersion.CSharp7_2;
// C# 7.1 missing
if (outVariables || tupleTypes || tupleConversions || discards)
@@ -650,16 +651,32 @@ namespace ICSharpCode.Decompiler
}
}
- bool introduceRefAndReadonlyModifiersOnStructs = true;
+ bool introduceRefModifiersOnStructs = true;
///
- /// Gets/Sets whether IsByRefLikeAttribute and IsReadOnlyAttribute should be replaced with 'ref' and 'readonly' modifiers on structs.
+ /// Gets/Sets whether IsByRefLikeAttribute should be replaced with 'ref' modifiers on structs.
///
- public bool IntroduceRefAndReadonlyModifiersOnStructs {
- get { return introduceRefAndReadonlyModifiersOnStructs; }
+ public bool IntroduceRefModifiersOnStructs {
+ get { return introduceRefModifiersOnStructs; }
set {
- if (introduceRefAndReadonlyModifiersOnStructs != value) {
- introduceRefAndReadonlyModifiersOnStructs = value;
+ if (introduceRefModifiersOnStructs != value) {
+ introduceRefModifiersOnStructs = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ bool introduceReadonlyAndInModifiers = true;
+
+ ///
+ /// Gets/Sets whether IsReadOnlyAttribute should be replaced with 'readonly' modifiers on structs
+ /// and with the 'in' modifier on parameters.
+ ///
+ public bool IntroduceReadonlyAndInModifiers {
+ get { return introduceReadonlyAndInModifiers; }
+ set {
+ if (introduceReadonlyAndInModifiers != value) {
+ introduceReadonlyAndInModifiers = value;
OnPropertyChanged();
}
}
diff --git a/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs b/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs
index 602290f10..71bc5a4e7 100644
--- a/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs
+++ b/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs
@@ -84,9 +84,19 @@ namespace ICSharpCode.Decompiler.TypeSystem
///
KeepModifiers = 0x40,
///
+ /// If this option is active, [IsReadOnlyAttribute] is removed and parameters are marked as in, structs as readonly.
+ /// Otherwise, the attribute is preserved but the parameters and structs are not marked.
+ ///
+ ReadOnlyStructsAndParameters = 0x80,
+ ///
+ /// If this option is active, [IsByRefLikeAttribute] is removed and structs are marked as ref.
+ /// Otherwise, the attribute is preserved but the structs are not marked.
+ ///
+ RefStructs = 0x100,
+ ///
/// Default settings: typical options for the decompiler, with all C# languages features enabled.
///
- Default = Dynamic | Tuple | ExtensionMethods | DecimalConstants
+ Default = Dynamic | Tuple | ExtensionMethods | DecimalConstants | ReadOnlyStructsAndParameters | RefStructs
}
///
@@ -108,6 +118,10 @@ namespace ICSharpCode.Decompiler.TypeSystem
typeSystemOptions |= TypeSystemOptions.ExtensionMethods;
if (settings.DecimalConstants)
typeSystemOptions |= TypeSystemOptions.DecimalConstants;
+ if (settings.IntroduceRefModifiersOnStructs)
+ typeSystemOptions |= TypeSystemOptions.RefStructs;
+ if (settings.IntroduceReadonlyAndInModifiers)
+ typeSystemOptions |= TypeSystemOptions.ReadOnlyStructsAndParameters;
return typeSystemOptions;
}
diff --git a/ICSharpCode.Decompiler/TypeSystem/IParameter.cs b/ICSharpCode.Decompiler/TypeSystem/IParameter.cs
index 553d8f029..68f10c532 100644
--- a/ICSharpCode.Decompiler/TypeSystem/IParameter.cs
+++ b/ICSharpCode.Decompiler/TypeSystem/IParameter.cs
@@ -37,7 +37,12 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// Gets whether this parameter is a C# 'out' parameter.
///
bool IsOut { get; }
-
+
+ ///
+ /// Gets whether this parameter is a C# 'in' parameter.
+ ///
+ bool IsIn { get; }
+
///
/// Gets whether this parameter is a C# 'params' parameter.
///
diff --git a/ICSharpCode.Decompiler/TypeSystem/ITypeDefinition.cs b/ICSharpCode.Decompiler/TypeSystem/ITypeDefinition.cs
index dbb2e1162..2a3d750ed 100644
--- a/ICSharpCode.Decompiler/TypeSystem/ITypeDefinition.cs
+++ b/ICSharpCode.Decompiler/TypeSystem/ITypeDefinition.cs
@@ -44,6 +44,12 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// For all other types: returns .
///
IType EnumUnderlyingType { get; }
+
+ ///
+ /// For structs: returns whether this is a readonly struct.
+ /// For all other types: returns false.
+ ///
+ bool IsReadOnly { get; }
///
/// Gets the full name of this type.
diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs
index 5e53daafb..867521eb7 100644
--- a/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs
+++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs
@@ -200,6 +200,10 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
return (options & TypeSystemOptions.ExtensionMethods) != 0;
case "DecimalConstantAttribute":
return (options & TypeSystemOptions.DecimalConstants) != 0;
+ case "IsReadOnlyAttribute":
+ return (options & TypeSystemOptions.ReadOnlyStructsAndParameters) != 0;
+ case "IsByRefLikeAttribute":
+ return (options & TypeSystemOptions.RefStructs) != 0;
default:
return false;
}
diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultParameter.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultParameter.cs
index 7642ad98c..00ebd3088 100644
--- a/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultParameter.cs
+++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultParameter.cs
@@ -31,7 +31,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
readonly IType type;
readonly string name;
readonly IReadOnlyList attributes;
- readonly bool isRef, isOut, isParams, isOptional;
+ readonly bool isRef, isOut, isIn, isParams, isOptional;
readonly object defaultValue;
readonly IParameterizedMember owner;
@@ -47,7 +47,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
}
public DefaultParameter(IType type, string name, IParameterizedMember owner = null, IReadOnlyList attributes = null,
- bool isRef = false, bool isOut = false, bool isParams = false, bool isOptional = false, object defaultValue = null)
+ bool isRef = false, bool isOut = false, bool isIn = false, bool isParams = false, bool isOptional = false, object defaultValue = null)
{
if (type == null)
throw new ArgumentNullException("type");
@@ -59,6 +59,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
this.attributes = attributes ?? EmptyList.Instance;
this.isRef = isRef;
this.isOut = isOut;
+ this.isIn = isIn;
this.isParams = isParams;
this.isOptional = isOptional;
this.defaultValue = defaultValue;
@@ -81,6 +82,10 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public bool IsOut {
get { return isOut; }
}
+
+ public bool IsIn {
+ get { return IsIn; }
+ }
public bool IsParams {
get { return isParams; }
diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs
index 18871d920..eabc198f4 100644
--- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs
+++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs
@@ -66,7 +66,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
if (IsOptional && !HasConstantValueInSignature)
b.Add(KnownAttribute.Optional);
- if (!IsOut) {
+ if (!IsOut && !IsIn) {
if ((attributes & ParameterAttributes.In) == ParameterAttributes.In)
b.Add(KnownAttribute.In);
if ((attributes & ParameterAttributes.Out) == ParameterAttributes.Out)
@@ -80,17 +80,28 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
#endregion
const ParameterAttributes inOut = ParameterAttributes.In | ParameterAttributes.Out;
- public bool IsRef => Type.Kind == TypeKind.ByReference && (attributes & inOut) != ParameterAttributes.Out;
+ public bool IsRef => Type.Kind == TypeKind.ByReference && (attributes & inOut) == 0;
public bool IsOut => Type.Kind == TypeKind.ByReference && (attributes & inOut) == ParameterAttributes.Out;
public bool IsOptional => (attributes & ParameterAttributes.Optional) != 0;
+ public bool IsIn {
+ get {
+ if ((module.TypeSystemOptions & TypeSystemOptions.ReadOnlyStructsAndParameters) == 0 ||
+ Type.Kind != TypeKind.ByReference || (attributes & inOut) != ParameterAttributes.In)
+ return false;
+ var metadata = module.metadata;
+ var parameterDef = metadata.GetParameter(handle);
+ return parameterDef.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.IsReadOnly);
+ }
+ }
+
public bool IsParams {
get {
if (Type.Kind != TypeKind.Array)
return false;
var metadata = module.metadata;
- var propertyDef = metadata.GetParameter(handle);
- return propertyDef.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.ParamArray);
+ var parameterDef = metadata.GetParameter(handle);
+ return parameterDef.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.ParamArray);
}
}
@@ -100,8 +111,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
if (name != null)
return name;
var metadata = module.metadata;
- var propertyDef = metadata.GetParameter(handle);
- return LazyInit.GetOrSet(ref this.name, metadata.GetString(propertyDef.Name));
+ var parameterDef = metadata.GetParameter(handle);
+ return LazyInit.GetOrSet(ref this.name, metadata.GetString(parameterDef.Name));
}
}
diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs
index b3121892d..d053a6bdb 100644
--- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs
+++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs
@@ -45,6 +45,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
readonly TypeAttributes attributes;
public TypeKind Kind { get; }
public bool IsByRefLike { get; }
+ public bool IsReadOnly { get; }
public ITypeDefinition DeclaringTypeDefinition { get; }
public IReadOnlyList TypeParameters { get; }
public KnownTypeCode KnownTypeCode { get; }
@@ -101,12 +102,14 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
} else {
this.Kind = TypeKind.Struct;
this.IsByRefLike = td.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.IsByRefLike);
+ this.IsReadOnly = td.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.IsReadOnly);
}
} else if (td.IsDelegate(metadata)) {
this.Kind = TypeKind.Delegate;
} else {
this.Kind = TypeKind.Class;
this.HasExtensionMethods = this.IsStatic
+ && (module.TypeSystemOptions & TypeSystemOptions.ExtensionMethods) == TypeSystemOptions.ExtensionMethods
&& td.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.Extension);
}
}
diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MinimalCorlib.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MinimalCorlib.cs
index 704daf99a..2cb4332a6 100644
--- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MinimalCorlib.cs
+++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MinimalCorlib.cs
@@ -156,6 +156,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
IType IEntity.DeclaringType => null;
bool ITypeDefinition.HasExtensionMethods => false;
+ bool ITypeDefinition.IsReadOnly => false;
TypeKind IType.Kind => typeKind;
diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedParameter.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedParameter.cs
index bd34dbe92..40459fb48 100644
--- a/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedParameter.cs
+++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedParameter.cs
@@ -38,6 +38,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
IEnumerable IParameter.GetAttributes() => baseParameter.GetAttributes();
bool IParameter.IsRef => baseParameter.IsRef;
bool IParameter.IsOut => baseParameter.IsOut;
+ bool IParameter.IsIn => baseParameter.IsIn;
bool IParameter.IsParams => baseParameter.IsParams;
bool IParameter.IsOptional => baseParameter.IsOptional;
bool IParameter.HasConstantValueInSignature => baseParameter.HasConstantValueInSignature;