Browse Source

Add support for C# 11 scoped parameter modifier.

pull/2749/head
Siegfried Pammer 3 years ago
parent
commit
2ed9ad6b51
  1. 10
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
  2. 32
      ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs
  3. 2
      ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
  4. 22
      ICSharpCode.Decompiler/DecompilerSettings.cs
  5. 9
      ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs
  6. 13
      ICSharpCode.Decompiler/TypeSystem/IParameter.cs
  7. 3
      ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs
  8. 2
      ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultParameter.cs
  9. 2
      ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs
  10. 32
      ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs
  11. 2
      ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedParameter.cs
  12. 1
      ILSpy/Languages/CSharpHighlightingTokenWriter.cs
  13. 2
      ILSpy/Languages/CSharpLanguage.cs
  14. 20
      ILSpy/Properties/Resources.Designer.cs
  15. 8
      ILSpy/Properties/Resources.resx

10
ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs

@ -2565,6 +2565,11 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
WriteKeyword(ParameterDeclaration.ThisModifierRole); WriteKeyword(ParameterDeclaration.ThisModifierRole);
Space(); Space();
} }
if (parameterDeclaration.IsRefScoped)
{
WriteKeyword(ParameterDeclaration.RefScopedRole);
Space();
}
switch (parameterDeclaration.ParameterModifier) switch (parameterDeclaration.ParameterModifier)
{ {
case ParameterModifier.Ref: case ParameterModifier.Ref:
@ -2584,6 +2589,11 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
Space(); Space();
break; break;
} }
if (parameterDeclaration.IsValueScoped)
{
WriteKeyword(ParameterDeclaration.ValueScopedRole);
Space();
}
parameterDeclaration.Type.AcceptVisitor(this); parameterDeclaration.Type.AcceptVisitor(this);
if (!parameterDeclaration.Type.IsNull && !string.IsNullOrEmpty(parameterDeclaration.Name)) if (!parameterDeclaration.Type.IsNull && !string.IsNullOrEmpty(parameterDeclaration.Name))
{ {

32
ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs

@ -34,17 +34,20 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
Ref, Ref,
Out, Out,
Params, Params,
In In,
Scoped
} }
public class ParameterDeclaration : AstNode public class ParameterDeclaration : AstNode
{ {
public static readonly Role<AttributeSection> AttributeRole = EntityDeclaration.AttributeRole; public static readonly Role<AttributeSection> AttributeRole = EntityDeclaration.AttributeRole;
public static readonly TokenRole ThisModifierRole = new TokenRole("this");
public static readonly TokenRole RefScopedRole = new TokenRole("scoped");
public static readonly TokenRole RefModifierRole = new TokenRole("ref"); public static readonly TokenRole RefModifierRole = new TokenRole("ref");
public static readonly TokenRole OutModifierRole = new TokenRole("out"); 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 static readonly TokenRole InModifierRole = new TokenRole("in");
public static readonly TokenRole ValueScopedRole = new TokenRole("scoped");
public static readonly TokenRole ParamsModifierRole = new TokenRole("params");
#region PatternPlaceholder #region PatternPlaceholder
public static implicit operator ParameterDeclaration?(PatternMatching.Pattern pattern) public static implicit operator ParameterDeclaration?(PatternMatching.Pattern pattern)
@ -92,17 +95,14 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
} }
#endregion #endregion
public override NodeType NodeType { public override NodeType NodeType => NodeType.Unknown;
get {
return NodeType.Unknown;
}
}
public AstNodeCollection<AttributeSection> Attributes { public AstNodeCollection<AttributeSection> Attributes {
get { return GetChildrenByRole(AttributeRole); } get { return GetChildrenByRole(AttributeRole); }
} }
bool hasThisModifier; bool hasThisModifier;
bool isRefScoped, isValueScoped;
public CSharpTokenNode ThisKeyword { public CSharpTokenNode ThisKeyword {
get { get {
@ -122,6 +122,22 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
} }
} }
public bool IsRefScoped {
get { return isRefScoped; }
set {
ThrowIfFrozen();
isRefScoped = value;
}
}
public bool IsValueScoped {
get { return isValueScoped; }
set {
ThrowIfFrozen();
isValueScoped = value;
}
}
ParameterModifier parameterModifier; ParameterModifier parameterModifier;
public ParameterModifier ParameterModifier { public ParameterModifier ParameterModifier {

2
ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs

@ -1649,6 +1649,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{ {
decl.ParameterModifier = ParameterModifier.Params; decl.ParameterModifier = ParameterModifier.Params;
} }
decl.IsRefScoped = parameter.Lifetime.RefScoped;
decl.IsValueScoped = parameter.Lifetime.ValueScoped;
if (ShowAttributes) if (ShowAttributes)
{ {
decl.Attributes.AddRange(ConvertAttributes(parameter.GetAttributes())); decl.Attributes.AddRange(ConvertAttributes(parameter.GetAttributes()));

22
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -150,12 +150,13 @@ namespace ICSharpCode.Decompiler
if (languageVersion < CSharp.LanguageVersion.CSharp11_0) if (languageVersion < CSharp.LanguageVersion.CSharp11_0)
{ {
parameterNullCheck = false; parameterNullCheck = false;
lifetimeAnnotations = false;
} }
} }
public CSharp.LanguageVersion GetMinimumRequiredVersion() public CSharp.LanguageVersion GetMinimumRequiredVersion()
{ {
if (parameterNullCheck) if (parameterNullCheck || lifetimeAnnotations)
return CSharp.LanguageVersion.CSharp11_0; return CSharp.LanguageVersion.CSharp11_0;
if (fileScopedNamespaces || recordStructs) if (fileScopedNamespaces || recordStructs)
return CSharp.LanguageVersion.CSharp10_0; return CSharp.LanguageVersion.CSharp10_0;
@ -336,6 +337,25 @@ namespace ICSharpCode.Decompiler
} }
} }
bool lifetimeAnnotations = true;
/// <summary>
/// Use C# 9 <c>delegate* unmanaged</c> types.
/// If this option is disabled, function pointers will instead be decompiled with type `IntPtr`.
/// </summary>
[Category("C# 11.0 / VS 2022.4")]
[Description("DecompilerSettings.LifetimeAnnotations")]
public bool LifetimeAnnotations {
get { return lifetimeAnnotations; }
set {
if (lifetimeAnnotations != value)
{
lifetimeAnnotations = value;
OnPropertyChanged();
}
}
}
bool switchExpressions = true; bool switchExpressions = true;
/// <summary> /// <summary>

9
ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs

@ -120,11 +120,16 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// </summary> /// </summary>
FunctionPointers = 0x2000, FunctionPointers = 0x2000,
/// <summary> /// <summary>
/// Allow C# 11 scoped annotation. If this option is not enabled, LifetimeAnnotationAttribute
/// will be reported as custom attribute.
/// </summary>
LifetimeAnnotations = 0x4000,
/// <summary>
/// Default settings: typical options for the decompiler, with all C# languages features enabled. /// Default settings: typical options for the decompiler, with all C# languages features enabled.
/// </summary> /// </summary>
Default = Dynamic | Tuple | ExtensionMethods | DecimalConstants | ReadOnlyStructsAndParameters Default = Dynamic | Tuple | ExtensionMethods | DecimalConstants | ReadOnlyStructsAndParameters
| RefStructs | UnmanagedConstraints | NullabilityAnnotations | ReadOnlyMethods | RefStructs | UnmanagedConstraints | NullabilityAnnotations | ReadOnlyMethods
| NativeIntegers | FunctionPointers | NativeIntegers | FunctionPointers | LifetimeAnnotations
} }
/// <summary> /// <summary>
@ -160,6 +165,8 @@ namespace ICSharpCode.Decompiler.TypeSystem
typeSystemOptions |= TypeSystemOptions.NativeIntegers; typeSystemOptions |= TypeSystemOptions.NativeIntegers;
if (settings.FunctionPointers) if (settings.FunctionPointers)
typeSystemOptions |= TypeSystemOptions.FunctionPointers; typeSystemOptions |= TypeSystemOptions.FunctionPointers;
if (settings.LifetimeAnnotations)
typeSystemOptions |= TypeSystemOptions.LifetimeAnnotations;
return typeSystemOptions; return typeSystemOptions;
} }

13
ICSharpCode.Decompiler/TypeSystem/IParameter.cs

@ -26,7 +26,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// <remarks> /// <remarks>
/// Should match order in <see cref="CSharp.Syntax.FieldDirection"/>. /// Should match order in <see cref="CSharp.Syntax.FieldDirection"/>.
/// </remarks> /// </remarks>
public enum ReferenceKind public enum ReferenceKind : byte
{ {
None, None,
Out, Out,
@ -34,6 +34,12 @@ namespace ICSharpCode.Decompiler.TypeSystem
In In
} }
public struct LifetimeAnnotation
{
public bool RefScoped;
public bool ValueScoped;
}
public interface IParameter : IVariable public interface IParameter : IVariable
{ {
/// <summary> /// <summary>
@ -46,6 +52,11 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// </summary> /// </summary>
ReferenceKind ReferenceKind { get; } ReferenceKind ReferenceKind { get; }
/// <summary>
/// C# 11 scoped annotation.
/// </summary>
LifetimeAnnotation Lifetime { get; }
/// <summary> /// <summary>
/// Gets whether this parameter is a C# 'ref' parameter. /// Gets whether this parameter is a C# 'ref' parameter.
/// </summary> /// </summary>

3
ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs

@ -246,6 +246,9 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
case "NullableContextAttribute": case "NullableContextAttribute":
return (options & TypeSystemOptions.NullabilityAnnotations) != 0 return (options & TypeSystemOptions.NullabilityAnnotations) != 0
&& (target == SymbolKind.TypeDefinition || IsMethodLike(target)); && (target == SymbolKind.TypeDefinition || IsMethodLike(target));
case "LifetimeAnnotationAttribute":
return (options & TypeSystemOptions.LifetimeAnnotations) != 0
&& (target == SymbolKind.Parameter);
default: default:
return false; return false;
} }

2
ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultParameter.cs

@ -100,6 +100,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
get { return IsOptional; } get { return IsOptional; }
} }
public LifetimeAnnotation Lifetime => default;
public object GetConstantValue(bool throwOnInvalidMetadata) public object GetConstantValue(bool throwOnInvalidMetadata)
{ {
return defaultValue; return defaultValue;

2
ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs

@ -91,6 +91,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
CallerMemberName, CallerMemberName,
CallerFilePath, CallerFilePath,
CallerLineNumber, CallerLineNumber,
LifetimeAnnotation,
// Type parameter attributes: // Type parameter attributes:
IsUnmanaged, IsUnmanaged,
@ -162,6 +163,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
new TopLevelTypeName("System.Runtime.CompilerServices", nameof(CallerMemberNameAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", nameof(CallerMemberNameAttribute)),
new TopLevelTypeName("System.Runtime.CompilerServices", nameof(CallerFilePathAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", nameof(CallerFilePathAttribute)),
new TopLevelTypeName("System.Runtime.CompilerServices", nameof(CallerLineNumberAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", nameof(CallerLineNumberAttribute)),
new TopLevelTypeName("System.Runtime.CompilerServices", "LifetimeAnnotationAttribute"),
// Type parameter attributes: // Type parameter attributes:
new TopLevelTypeName("System.Runtime.CompilerServices", "IsUnmanagedAttribute"), new TopLevelTypeName("System.Runtime.CompilerServices", "IsUnmanagedAttribute"),
// Marshalling attributes: // Marshalling attributes:

32
ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs

@ -106,6 +106,38 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
return ReferenceKind.Ref; return ReferenceKind.Ref;
} }
public LifetimeAnnotation Lifetime {
get {
if ((module.TypeSystemOptions & TypeSystemOptions.LifetimeAnnotations) == 0)
{
return default;
}
var metadata = module.metadata;
var parameterDef = metadata.GetParameter(handle);
foreach (var h in parameterDef.GetCustomAttributes())
{
var custom = metadata.GetCustomAttribute(h);
if (!custom.IsKnownAttribute(metadata, KnownAttribute.LifetimeAnnotation))
continue;
var value = custom.DecodeValue(module.TypeProvider);
if (value.FixedArguments.Length != 2)
continue;
if (value.FixedArguments[0].Value is bool refScoped
&& value.FixedArguments[1].Value is bool valueScoped)
{
return new LifetimeAnnotation {
RefScoped = refScoped,
ValueScoped = valueScoped
};
}
}
return default;
}
}
public bool IsParams { public bool IsParams {
get { get {
if (Type.Kind != TypeKind.Array) if (Type.Kind != TypeKind.Array)

2
ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedParameter.cs

@ -51,6 +51,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
object IVariable.GetConstantValue(bool throwOnInvalidMetadata) => baseParameter.GetConstantValue(throwOnInvalidMetadata); object IVariable.GetConstantValue(bool throwOnInvalidMetadata) => baseParameter.GetConstantValue(throwOnInvalidMetadata);
SymbolKind ISymbol.SymbolKind => SymbolKind.Parameter; SymbolKind ISymbol.SymbolKind => SymbolKind.Parameter;
public LifetimeAnnotation Lifetime => baseParameter.Lifetime;
public override string ToString() public override string ToString()
{ {
return DefaultParameter.ToString(this); return DefaultParameter.ToString(this);

1
ILSpy/Languages/CSharpHighlightingTokenWriter.cs

@ -262,6 +262,7 @@ namespace ICSharpCode.ILSpy
case "params": case "params":
case "ref": case "ref":
case "out": case "out":
case "scoped":
color = parameterModifierColor; color = parameterModifierColor;
break; break;
case "break": case "break":

2
ILSpy/Languages/CSharpLanguage.cs

@ -115,7 +115,7 @@ namespace ICSharpCode.ILSpy
new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp8_0.ToString(), "C# 8.0 / VS 2019"), new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp8_0.ToString(), "C# 8.0 / VS 2019"),
new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp9_0.ToString(), "C# 9.0 / VS 2019.8"), new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp9_0.ToString(), "C# 9.0 / VS 2019.8"),
new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp10_0.ToString(), "C# 10.0 / VS 2022"), new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp10_0.ToString(), "C# 10.0 / VS 2022"),
new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp11_0.ToString(), "C# 11.0 / VS 2022.1"), new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp11_0.ToString(), "C# 11.0 / VS 2022.4"),
}; };
} }
return versions; return versions;

20
ILSpy/Properties/Resources.Designer.cs generated

@ -1073,6 +1073,15 @@ namespace ICSharpCode.ILSpy.Properties {
} }
} }
/// <summary>
/// Looks up a localized string similar to &apos;scoped&apos; lifetime annotation.
/// </summary>
public static string DecompilerSettings_LifetimeAnnotations {
get {
return ResourceManager.GetString("DecompilerSettings.LifetimeAnnotations", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Use nint/nuint types. /// Looks up a localized string similar to Use nint/nuint types.
/// </summary> /// </summary>
@ -1164,7 +1173,7 @@ namespace ICSharpCode.ILSpy.Properties {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Records. /// Looks up a localized string similar to Record classes.
/// </summary> /// </summary>
public static string DecompilerSettings_RecordClasses { public static string DecompilerSettings_RecordClasses {
get { get {
@ -1172,6 +1181,15 @@ namespace ICSharpCode.ILSpy.Properties {
} }
} }
/// <summary>
/// Looks up a localized string similar to Record structs.
/// </summary>
public static string DecompilerSettings_RecordStructs {
get {
return ResourceManager.GetString("DecompilerSettings.RecordStructs", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Remove dead and side effect free code (use with caution!). /// Looks up a localized string similar to Remove dead and side effect free code (use with caution!).
/// </summary> /// </summary>

8
ILSpy/Properties/Resources.resx

@ -381,6 +381,9 @@ Are you sure you want to continue?</value>
<data name="DecompilerSettings.IsUnmanagedAttributeOnTypeParametersShouldBeReplacedWithUnmanagedConstraints" xml:space="preserve"> <data name="DecompilerSettings.IsUnmanagedAttributeOnTypeParametersShouldBeReplacedWithUnmanagedConstraints" xml:space="preserve">
<value>IsUnmanagedAttribute on type parameters should be replaced with 'unmanaged' constraints</value> <value>IsUnmanagedAttribute on type parameters should be replaced with 'unmanaged' constraints</value>
</data> </data>
<data name="DecompilerSettings.LifetimeAnnotations" xml:space="preserve">
<value>'scoped' lifetime annotation</value>
</data>
<data name="DecompilerSettings.NativeIntegers" xml:space="preserve"> <data name="DecompilerSettings.NativeIntegers" xml:space="preserve">
<value>Use nint/nuint types</value> <value>Use nint/nuint types</value>
</data> </data>
@ -412,7 +415,10 @@ Are you sure you want to continue?</value>
<value>Read-only methods</value> <value>Read-only methods</value>
</data> </data>
<data name="DecompilerSettings.RecordClasses" xml:space="preserve"> <data name="DecompilerSettings.RecordClasses" xml:space="preserve">
<value>Records</value> <value>Record classes</value>
</data>
<data name="DecompilerSettings.RecordStructs" xml:space="preserve">
<value>Record structs</value>
</data> </data>
<data name="DecompilerSettings.RemoveDeadAndSideEffectFreeCodeUseWithCaution" xml:space="preserve"> <data name="DecompilerSettings.RemoveDeadAndSideEffectFreeCodeUseWithCaution" xml:space="preserve">
<value>Remove dead and side effect free code (use with caution!)</value> <value>Remove dead and side effect free code (use with caution!)</value>

Loading…
Cancel
Save