Browse Source

Merge pull request #2992 from icsharpcode/ref-fields

pull/2994/head
Siegfried Pammer 2 years ago committed by GitHub
parent
commit
5cb1fe32f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  2. 6
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  3. 3
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.cs
  4. 4
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Records.cs
  5. 87
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefFields.cs
  6. 8
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  7. 9
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
  8. 3
      ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertRequiredSpacesDecorator.cs
  9. 6
      ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterTokenWriter.cs
  10. 1
      ICSharpCode.Decompiler/CSharp/Syntax/Expressions/PrimitiveExpression.cs
  11. 28
      ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs
  12. 7
      ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
  13. 1
      ICSharpCode.Decompiler/CSharp/Transforms/EscapeInvalidIdentifiers.cs
  14. 47
      ICSharpCode.Decompiler/DecompilerSettings.cs
  15. 65
      ICSharpCode.Decompiler/IL/Instructions.cs
  16. 2
      ICSharpCode.Decompiler/IL/Instructions.tt
  17. 4
      ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs
  18. 2
      ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
  19. 2
      ICSharpCode.Decompiler/IL/Transforms/ParameterNullCheckTransform.cs
  20. 39
      ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs
  21. 2
      ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs
  22. 12
      ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs
  23. 5
      ICSharpCode.Decompiler/TypeSystem/IField.cs
  24. 14
      ICSharpCode.Decompiler/TypeSystem/IParameter.cs
  25. 5
      ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs
  26. 1
      ICSharpCode.Decompiler/TypeSystem/Implementation/FakeMember.cs
  27. 4
      ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs
  28. 7
      ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataField.cs
  29. 21
      ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs
  30. 10
      ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedField.cs
  31. 28
      ILSpy/Properties/Resources.Designer.cs
  32. 9
      ILSpy/Properties/Resources.resx

7
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -9,7 +9,7 @@ @@ -9,7 +9,7 @@
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<NoWarn>1701;1702;1705,67,169,1058,728,1720,649,168,251,660,661,675;1998;162;8632</NoWarn>
<NoWarn>1701;1702;1705,67,169,1058,728,1720,649,168,251,660,661,675;1998;162;8632;626;8618;8714;8602</NoWarn>
<GenerateAssemblyVersionAttribute>False</GenerateAssemblyVersionAttribute>
<GenerateAssemblyFileVersionAttribute>False</GenerateAssemblyFileVersionAttribute>
@ -33,11 +33,11 @@ @@ -33,11 +33,11 @@
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DefineConstants>TRACE;DEBUG;ROSLYN;CS60;CS70;CS71;CS72;CS73;CS80;CS90;CS100</DefineConstants>
<DefineConstants>TRACE;DEBUG;ROSLYN;NET60;CS60;CS70;CS71;CS72;CS73;CS80;CS90;CS100</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DefineConstants>TRACE;ROSLYN;CS60;CS70;CS71;CS72;CS73;CS80;CS90;CS100</DefineConstants>
<DefineConstants>TRACE;ROSLYN;NET60;CS60;CS70;CS71;CS72;CS73;CS80;CS90;CS100</DefineConstants>
</PropertyGroup>
<Import Project="..\packages.props" />
@ -220,6 +220,7 @@ @@ -220,6 +220,7 @@
<None Include="TestCases\Pretty\MemberTests.cs" />
<Compile Include="TestCases\Pretty\NamedArguments.cs" />
<Compile Include="TestCases\Pretty\QualifierTests.cs" />
<None Include="TestCases\Pretty\RefFields.cs" />
<Compile Include="TestCases\Pretty\RefLocalsAndReturns.cs" />
<Compile Include="TestCases\Pretty\TupleTests.cs" />
<Compile Include="TestCases\Pretty\WellKnownConstants.cs" />

6
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -538,6 +538,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -538,6 +538,12 @@ namespace ICSharpCode.Decompiler.Tests
await RunForLibrary(cscOptions: cscOptions);
}
[Test]
public async Task RefFields([ValueSource(nameof(roslyn4OrNewerOptions))] CompilerOptions cscOptions)
{
await RunForLibrary(cscOptions: cscOptions);
}
[Test]
public async Task ThrowExpressions([ValueSource(nameof(roslyn2OrNewerOptions))] CompilerOptions cscOptions)
{

3
ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.cs

@ -382,6 +382,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.InitializerTests @@ -382,6 +382,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.InitializerTests
public static ReadOnlySpan<byte> StaticData3 => new byte[3] { 1, 2, 3 };
public static Span<byte> StaticData3Span => new byte[3] { 1, 2, 3 };
#endif
#if CS110 && !NET40
public static ReadOnlySpan<byte> UTF8Literal => "Hello, world!"u8;
#endif
#endregion

4
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Records.cs

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
using System;
#if ROSLYN4
#if CS100
using System.Runtime.InteropServices;
#endif
@ -242,6 +242,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -242,6 +242,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
#endif
}
#if !NET60
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)]
@ -261,3 +262,4 @@ namespace System.Runtime.CompilerServices @@ -261,3 +262,4 @@ namespace System.Runtime.CompilerServices
{
}
}
#endif

87
ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefFields.cs

@ -0,0 +1,87 @@ @@ -0,0 +1,87 @@
using System;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
internal class LifetimeTests
{
private static int staticField;
public Span<int> CreateWithoutCapture(scoped ref int value)
{
// Okay: value is not captured
return new Span<int>(ref staticField);
}
public Span<int> CreateAndCapture(ref int value)
{
// Okay: value Rule 3 specifies that the safe-to-escape be limited to the ref-safe-to-escape
// of the ref argument. That is the *calling method* for value hence this is not allowed.
return new Span<int>(ref value);
}
public Span<int> ScopedRefSpan(scoped ref Span<int> span)
{
return span;
}
public Span<int> ScopedSpan(scoped Span<int> span)
{
return default(Span<int>);
}
public void OutSpan(out Span<int> span)
{
span = default(Span<int>);
}
public void Calls()
{
int value = 0;
Span<int> span = CreateWithoutCapture(ref value);
//span = CreateAndCapture(ref value); -- would need scoped local, not yet implemented
span = ScopedRefSpan(ref span);
span = ScopedSpan(span);
OutSpan(out span);
}
}
internal ref struct RefFields
{
public ref int Field0;
public ref readonly int Field1;
public readonly ref int Field2;
public readonly ref readonly int Field3;
public int PropertyAccessingRefFieldByValue {
get {
return Field0;
}
set {
Field0 = value;
}
}
public ref int PropertyReturningRefFieldByReference => ref Field0;
public void Uses(int[] array)
{
Field1 = ref array[0];
Field2 = array[0];
}
public void ReadonlyLocal()
{
ref readonly int field = ref Field1;
Console.WriteLine("No inlining");
field.ToString();
}
public RefFields(ref int v)
{
Field0 = ref v;
Field1 = ref v;
Field2 = ref v;
Field3 = ref v;
}
}
}

8
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -617,6 +617,14 @@ namespace ICSharpCode.Decompiler.CSharp @@ -617,6 +617,14 @@ namespace ICSharpCode.Decompiler.CSharp
.WithRR(new ConstantResolveResult(compilation.FindType(KnownTypeCode.String), inst.Value));
}
protected internal override TranslatedExpression VisitLdStrUtf8(LdStrUtf8 inst, TranslationContext context)
{
var type = new ParameterizedType(compilation.FindType(KnownTypeCode.ReadOnlySpanOfT), new[] { compilation.FindType(KnownTypeCode.Byte) });
return new PrimitiveExpression(inst.Value, LiteralFormat.Utf8Literal)
.WithILInstruction(inst)
.WithRR(new ConstantResolveResult(type, inst.Value));
}
protected internal override TranslatedExpression VisitLdNull(LdNull inst, TranslationContext context)
{
return GetDefaultValueExpression(SpecialType.NullType).WithILInstruction(inst);

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

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

3
ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertRequiredSpacesDecorator.cs

@ -159,6 +159,9 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -159,6 +159,9 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
return;
if (value is string)
{
if (format == LiteralFormat.VerbatimStringLiteral)
lastWritten = LastWritten.KeywordOrIdentifier;
else
lastWritten = LastWritten.Other;
}
else if (value is char)

6
ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterTokenWriter.cs

@ -269,6 +269,12 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -269,6 +269,12 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
textWriter.Write('"');
textWriter.Write(tmp);
textWriter.Write('"');
if (format == LiteralFormat.Utf8Literal)
{
textWriter.Write("u8");
column += 2;
Length += 2;
}
}
else if (value is char)
{

1
ICSharpCode.Decompiler/CSharp/Syntax/Expressions/PrimitiveExpression.cs

@ -42,6 +42,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -42,6 +42,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
StringLiteral,
VerbatimStringLiteral,
CharLiteral,
Utf8Literal,
}
/// <summary>

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

@ -26,6 +26,8 @@ @@ -26,6 +26,8 @@
#nullable enable
using System;
namespace ICSharpCode.Decompiler.CSharp.Syntax
{
public enum ParameterModifier
@ -42,10 +44,13 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -42,10 +44,13 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{
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 ScopedRefRole = new TokenRole("scoped");
[Obsolete("Renamed to ScopedRefRole")]
public static readonly TokenRole RefScopedRole = ScopedRefRole;
public static readonly TokenRole RefModifierRole = new TokenRole("ref");
public static readonly TokenRole OutModifierRole = new TokenRole("out");
public static readonly TokenRole InModifierRole = new TokenRole("in");
[Obsolete("C# 11 preview: \"ref scoped\" no longer supported")]
public static readonly TokenRole ValueScopedRole = new TokenRole("scoped");
public static readonly TokenRole ParamsModifierRole = new TokenRole("params");
@ -102,7 +107,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -102,7 +107,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
}
bool hasThisModifier;
bool isRefScoped, isValueScoped;
bool isScopedRef;
public CSharpTokenNode ThisKeyword {
get {
@ -122,22 +127,29 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -122,22 +127,29 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
}
}
public bool IsRefScoped {
get { return isRefScoped; }
public bool IsScopedRef {
get { return isScopedRef; }
set {
ThrowIfFrozen();
isRefScoped = value;
isScopedRef = value;
}
}
public bool IsValueScoped {
get { return isValueScoped; }
[Obsolete("Renamed to IsScopedRef")]
public bool IsRefScoped {
get { return isScopedRef; }
set {
ThrowIfFrozen();
isValueScoped = value;
isScopedRef = value;
}
}
[Obsolete("C# 11 preview: \"ref scoped\" no longer supported")]
public bool IsValueScoped {
get { return false; }
set { }
}
ParameterModifier parameterModifier;
public ParameterModifier ParameterModifier {

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

@ -1654,8 +1654,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1654,8 +1654,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{
decl.ParameterModifier = ParameterModifier.Params;
}
decl.IsRefScoped = parameter.Lifetime.RefScoped;
decl.IsValueScoped = parameter.Lifetime.ValueScoped;
decl.IsScopedRef = parameter.Lifetime.ScopedRef;
if (ShowAttributes)
{
decl.Attributes.AddRange(ConvertAttributes(parameter.GetAttributes()));
@ -1970,6 +1969,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1970,6 +1969,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
decl.AddAnnotation(new MemberResolveResult(null, field));
}
decl.ReturnType = ConvertType(field.ReturnType);
if (decl.ReturnType is ComposedType ct && ct.HasRefSpecifier && field.ReturnTypeIsRefReadOnly)
{
ct.HasReadOnlySpecifier = true;
}
Expression initializer = null;
if (field.IsConst && this.ShowConstantValues)
{

1
ICSharpCode.Decompiler/CSharp/Transforms/EscapeInvalidIdentifiers.cs

@ -167,6 +167,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -167,6 +167,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
"System.Runtime.CompilerServices.NullableContextAttribute",
"System.Runtime.CompilerServices.NativeIntegerAttribute",
"System.Runtime.CompilerServices.RefSafetyRulesAttribute",
"System.Runtime.CompilerServices.ScopedRefAttribute",
"Microsoft.CodeAnalysis.EmbeddedAttribute",
};

47
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -150,15 +150,16 @@ namespace ICSharpCode.Decompiler @@ -150,15 +150,16 @@ namespace ICSharpCode.Decompiler
if (languageVersion < CSharp.LanguageVersion.CSharp11_0)
{
parameterNullCheck = false;
lifetimeAnnotations = false;
scopedRef = false;
requiredMembers = false;
numericIntPtr = false;
utf8StringLiterals = false;
}
}
public CSharp.LanguageVersion GetMinimumRequiredVersion()
{
if (parameterNullCheck || lifetimeAnnotations || requiredMembers || numericIntPtr)
if (parameterNullCheck || scopedRef || requiredMembers || numericIntPtr || utf8StringLiterals)
return CSharp.LanguageVersion.CSharp11_0;
if (fileScopedNamespaces || recordStructs)
return CSharp.LanguageVersion.CSharp10_0;
@ -357,25 +358,30 @@ namespace ICSharpCode.Decompiler @@ -357,25 +358,30 @@ namespace ICSharpCode.Decompiler
}
}
bool lifetimeAnnotations = true;
bool scopedRef = true;
/// <summary>
/// Use C# 9 <c>delegate* unmanaged</c> types.
/// If this option is disabled, function pointers will instead be decompiled with type `IntPtr`.
/// Use C# 11 <c>scoped</c> modifier.
/// </summary>
[Category("C# 11.0 / VS 2022.4")]
[Description("DecompilerSettings.LifetimeAnnotations")]
public bool LifetimeAnnotations {
get { return lifetimeAnnotations; }
[Description("DecompilerSettings.ScopedRef")]
public bool ScopedRef {
get { return scopedRef; }
set {
if (lifetimeAnnotations != value)
if (scopedRef != value)
{
lifetimeAnnotations = value;
scopedRef = value;
OnPropertyChanged();
}
}
}
[Obsolete("Renamed to ScopedRef. This property will be removed in a future version of the decompiler.")]
public bool LifetimeAnnotations {
get { return ScopedRef; }
set { ScopedRef = value; }
}
bool requiredMembers = true;
/// <summary>
@ -435,9 +441,10 @@ namespace ICSharpCode.Decompiler @@ -435,9 +441,10 @@ namespace ICSharpCode.Decompiler
/// <summary>
/// Use C# 11 preview parameter null-checking (<code>string param!!</code>).
/// </summary>
[Category("C# 11.0 / VS 2022.1")]
[Category("C# 11.0 / VS 2022.4")]
[Description("DecompilerSettings.ParameterNullCheck")]
[Browsable(false)]
[Obsolete("This feature did not make it into C# 11, and may be removed in a future version of the decompiler.")]
public bool ParameterNullCheck {
get { return parameterNullCheck; }
set {
@ -1174,6 +1181,24 @@ namespace ICSharpCode.Decompiler @@ -1174,6 +1181,24 @@ namespace ICSharpCode.Decompiler
}
}
bool utf8StringLiterals = true;
/// <summary>
/// Gets/Sets whether to use C# 11.0 UTF-8 string literals
/// </summary>
[Category("C# 11.0 / VS 2022.4")]
[Description("DecompilerSettings.Utf8StringLiterals")]
public bool Utf8StringLiterals {
get { return utf8StringLiterals; }
set {
if (utf8StringLiterals != value)
{
utf8StringLiterals = value;
OnPropertyChanged();
}
}
}
bool showXmlDocumentation = true;
/// <summary>

65
ICSharpCode.Decompiler/IL/Instructions.cs

@ -19,6 +19,7 @@ @@ -19,6 +19,7 @@
#nullable enable
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
@ -121,6 +122,8 @@ namespace ICSharpCode.Decompiler.IL @@ -121,6 +122,8 @@ namespace ICSharpCode.Decompiler.IL
NullableRewrap,
/// <summary>Loads a constant string.</summary>
LdStr,
/// <summary>Loads a constant byte string (as ReadOnlySpan&lt;byte&gt;).</summary>
LdStrUtf8,
/// <summary>Loads a constant 32-bit integer.</summary>
LdcI4,
/// <summary>Loads a constant 64-bit integer.</summary>
@ -2838,6 +2841,43 @@ namespace ICSharpCode.Decompiler.IL @@ -2838,6 +2841,43 @@ namespace ICSharpCode.Decompiler.IL
}
}
namespace ICSharpCode.Decompiler.IL
{
/// <summary>Loads a constant byte string (as ReadOnlySpan&lt;byte&gt;).</summary>
public sealed partial class LdStrUtf8 : SimpleInstruction
{
public LdStrUtf8(string value) : base(OpCode.LdStrUtf8)
{
this.Value = value;
}
public readonly string Value;
public override StackType ResultType { get { return StackType.O; } }
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
WriteILRange(output, options);
output.Write(OpCode);
output.Write(' ');
Disassembler.DisassemblerHelpers.WriteOperand(output, Value);
}
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitLdStrUtf8(this);
}
public override T AcceptVisitor<T>(ILVisitor<T> visitor)
{
return visitor.VisitLdStrUtf8(this);
}
public override T AcceptVisitor<C, T>(ILVisitor<C, T> visitor, C context)
{
return visitor.VisitLdStrUtf8(this, context);
}
protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match)
{
var o = other as LdStrUtf8;
return o != null && this.Value == o.Value;
}
}
}
namespace ICSharpCode.Decompiler.IL
{
/// <summary>Loads a constant 32-bit integer.</summary>
public sealed partial class LdcI4 : SimpleInstruction
@ -6947,6 +6987,10 @@ namespace ICSharpCode.Decompiler.IL @@ -6947,6 +6987,10 @@ namespace ICSharpCode.Decompiler.IL
{
Default(inst);
}
protected internal virtual void VisitLdStrUtf8(LdStrUtf8 inst)
{
Default(inst);
}
protected internal virtual void VisitLdcI4(LdcI4 inst)
{
Default(inst);
@ -7345,6 +7389,10 @@ namespace ICSharpCode.Decompiler.IL @@ -7345,6 +7389,10 @@ namespace ICSharpCode.Decompiler.IL
{
return Default(inst);
}
protected internal virtual T VisitLdStrUtf8(LdStrUtf8 inst)
{
return Default(inst);
}
protected internal virtual T VisitLdcI4(LdcI4 inst)
{
return Default(inst);
@ -7743,6 +7791,10 @@ namespace ICSharpCode.Decompiler.IL @@ -7743,6 +7791,10 @@ namespace ICSharpCode.Decompiler.IL
{
return Default(inst, context);
}
protected internal virtual T VisitLdStrUtf8(LdStrUtf8 inst, C context)
{
return Default(inst, context);
}
protected internal virtual T VisitLdcI4(LdcI4 inst, C context)
{
return Default(inst, context);
@ -8013,6 +8065,7 @@ namespace ICSharpCode.Decompiler.IL @@ -8013,6 +8065,7 @@ namespace ICSharpCode.Decompiler.IL
"nullable.unwrap",
"nullable.rewrap",
"ldstr",
"ldstr.utf8",
"ldc.i4",
"ldc.i8",
"ldc.f4",
@ -8285,6 +8338,17 @@ namespace ICSharpCode.Decompiler.IL @@ -8285,6 +8338,17 @@ namespace ICSharpCode.Decompiler.IL
value = default(string);
return false;
}
public bool MatchLdStrUtf8([NotNullWhen(true)] out string? value)
{
var inst = this as LdStrUtf8;
if (inst != null)
{
value = inst.Value;
return true;
}
value = default(string);
return false;
}
public bool MatchLdcI4(out int value)
{
var inst = this as LdcI4;
@ -8751,3 +8815,4 @@ namespace ICSharpCode.Decompiler.IL @@ -8751,3 +8815,4 @@ namespace ICSharpCode.Decompiler.IL
}
}
}

2
ICSharpCode.Decompiler/IL/Instructions.tt

@ -204,6 +204,8 @@ @@ -204,6 +204,8 @@
new OpCode("ldstr", "Loads a constant string.",
CustomClassName("LdStr"), LoadConstant("string"), ResultType("O")),
new OpCode("ldstr.utf8", "Loads a constant byte string (as ReadOnlySpan&lt;byte&gt;).",
CustomClassName("LdStrUtf8"), LoadConstant("string"), ResultType("O")),
new OpCode("ldc.i4", "Loads a constant 32-bit integer.",
LoadConstant("int"), ResultType("I4")),
new OpCode("ldc.i8", "Loads a constant 64-bit integer.",

4
ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs

@ -313,10 +313,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -313,10 +313,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
ILInlining.InlineIfPossible(block, stmt.ChildIndex, context);
return;
}
if (TransformArrayInitializers.TransformSpanTArrayInitialization(inst, context, out block))
if (TransformArrayInitializers.TransformSpanTArrayInitialization(inst, context, out var replacement))
{
context.Step("TransformSpanTArrayInitialization: single-dim", inst);
inst.ReplaceWith(block);
inst.ReplaceWith(replacement);
return;
}
if (TransformDelegateCtorLdVirtFtnToLdVirtDelegate(inst, out LdVirtDelegate ldVirtDelegate))

2
ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs

@ -494,6 +494,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -494,6 +494,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// C# doesn't allow mutation of value-type temporaries
return true;
default:
if (addr.MatchLdFld(out _, out var field))
return field.ReturnTypeIsRefReadOnly;
return false;
}
}

2
ICSharpCode.Decompiler/IL/Transforms/ParameterNullCheckTransform.cs

@ -31,8 +31,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -31,8 +31,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{
void IILTransform.Run(ILFunction function, ILTransformContext context)
{
#pragma warning disable 618 // ParameterNullCheck is obsolete
if (!context.Settings.ParameterNullCheck)
return;
#pragma warning restore 618
// we only need to look at the entry-point as parameter null-checks
// do not produce any IL control-flow instructions
Block entryPoint = ((BlockContainer)function.Body).EntryPoint;

39
ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs

@ -20,6 +20,7 @@ using System; @@ -20,6 +20,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Metadata;
using System.Text;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
@ -113,9 +114,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -113,9 +114,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false;
}
internal static bool TransformSpanTArrayInitialization(NewObj inst, StatementTransformContext context, out Block block)
internal static bool TransformSpanTArrayInitialization(NewObj inst, StatementTransformContext context, out ILInstruction replacement)
{
block = null;
replacement = null;
if (!context.Settings.ArrayInitializers)
return false;
if (MatchSpanTCtorWithPointerAndSize(inst, context, out var elementType, out var field, out var size))
@ -124,10 +125,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -124,10 +125,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{
var valuesList = new List<ILInstruction>();
var initialValue = field.GetInitialValue(context.PEFile.Reader, context.TypeSystem);
if (context.Settings.Utf8StringLiterals &&
elementType.IsKnownType(KnownTypeCode.Byte) &&
DecodeUTF8String(initialValue, size, out string text))
{
replacement = new LdStrUtf8(text);
return true;
}
if (DecodeArrayInitializer(elementType, initialValue, new[] { size }, valuesList))
{
var tempStore = context.Function.RegisterVariable(VariableKind.InitializerTarget, new ArrayType(context.TypeSystem, elementType));
block = BlockFromInitializer(tempStore, elementType, new[] { size }, valuesList.ToArray());
replacement = BlockFromInitializer(tempStore, elementType, new[] { size }, valuesList.ToArray());
return true;
}
}
@ -135,13 +143,36 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -135,13 +143,36 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false;
}
private static unsafe bool DecodeUTF8String(BlobReader blob, int size, out string text)
{
if (size > blob.RemainingBytes)
{
text = null;
return false;
}
for (int i = 0; i < size; i++)
{
byte val = blob.CurrentPointer[i];
// If the string has control characters, it's probably binary data and not a string.
if (val < 0x20 && val is not ((byte)'\r' or (byte)'\n' or (byte)'\t'))
{
text = null;
return false;
}
}
text = Encoding.UTF8.GetString(blob.CurrentPointer, size);
// Only use UTF8 string literal if we can perfectly roundtrip the data
byte[] bytes = Encoding.UTF8.GetBytes(text);
return MemoryExtensions.SequenceEqual(bytes, new ReadOnlySpan<byte>(blob.CurrentPointer, size));
}
static bool MatchSpanTCtorWithPointerAndSize(NewObj newObj, StatementTransformContext context, out IType elementType, out FieldDefinition field, out int size)
{
field = default;
size = default;
elementType = null;
IType type = newObj.Method.DeclaringType;
if (!type.IsKnownType(KnownTypeCode.SpanOfT) && !type.IsKnownType(KnownTypeCode.ReadOnlySpanOfT))
if (!type.IsKnownType(KnownTypeCode.ReadOnlySpanOfT))
return false;
if (newObj.Arguments.Count != 2 || type.TypeArguments.Count != 1)
return false;

2
ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs

@ -390,7 +390,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -390,7 +390,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{
if (left.MatchStLoc(out var inlineAssignVar, out var inlineAssignVal))
{
if (!inlineAssignVal.MatchIsInst(out var arg, out var type) && type.IsKnownType(disposeType))
if (!inlineAssignVal.MatchIsInst(out var arg, out var type) || !type.IsKnownType(disposeType))
return false;
if (!inlineAssignVar.IsSingleDefinition || inlineAssignVar.LoadCount != 1)
return false;

12
ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs

@ -120,10 +120,12 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -120,10 +120,12 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// </summary>
FunctionPointers = 0x2000,
/// <summary>
/// Allow C# 11 scoped annotation. If this option is not enabled, LifetimeAnnotationAttribute
/// Allow C# 11 scoped annotation. If this option is not enabled, ScopedRefAttribute
/// will be reported as custom attribute.
/// </summary>
LifetimeAnnotations = 0x4000,
ScopedRef = 0x4000,
[Obsolete("Use ScopedRef instead")]
LifetimeAnnotations = ScopedRef,
/// <summary>
/// Replace 'IntPtr' types with the 'nint' type even in absence of [NativeIntegerAttribute].
/// Note: DecompilerTypeSystem constructor removes this setting from the options if
@ -135,7 +137,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -135,7 +137,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// </summary>
Default = Dynamic | Tuple | ExtensionMethods | DecimalConstants | ReadOnlyStructsAndParameters
| RefStructs | UnmanagedConstraints | NullabilityAnnotations | ReadOnlyMethods
| NativeIntegers | FunctionPointers | LifetimeAnnotations | NativeIntegersWithoutAttribute
| NativeIntegers | FunctionPointers | ScopedRef | NativeIntegersWithoutAttribute
}
/// <summary>
@ -171,8 +173,8 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -171,8 +173,8 @@ namespace ICSharpCode.Decompiler.TypeSystem
typeSystemOptions |= TypeSystemOptions.NativeIntegers;
if (settings.FunctionPointers)
typeSystemOptions |= TypeSystemOptions.FunctionPointers;
if (settings.LifetimeAnnotations)
typeSystemOptions |= TypeSystemOptions.LifetimeAnnotations;
if (settings.ScopedRef)
typeSystemOptions |= TypeSystemOptions.ScopedRef;
if (settings.NumericIntPtr)
typeSystemOptions |= TypeSystemOptions.NativeIntegersWithoutAttribute;
return typeSystemOptions;

5
ICSharpCode.Decompiler/TypeSystem/IField.cs

@ -35,6 +35,11 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -35,6 +35,11 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// </summary>
bool IsReadOnly { get; }
/// <summary>
/// Gets whether the field type is 'ref readonly'.
/// </summary>
bool ReturnTypeIsRefReadOnly { get; }
/// <summary>
/// Gets whether this field is volatile.
/// </summary>

14
ICSharpCode.Decompiler/TypeSystem/IParameter.cs

@ -18,6 +18,7 @@ @@ -18,6 +18,7 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
@ -36,7 +37,20 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -36,7 +37,20 @@ namespace ICSharpCode.Decompiler.TypeSystem
public struct LifetimeAnnotation
{
/// <summary>
/// C# 11 scoped annotation: "scoped ref" (ScopedRefAttribute)
/// </summary>
public bool ScopedRef {
#pragma warning disable 618
get { return RefScoped; }
set { RefScoped = value; }
#pragma warning restore 618
}
[Obsolete("Use ScopedRef property instead of directly accessing this field")]
public bool RefScoped;
[Obsolete("C# 11 preview: \"ref scoped\" no longer supported")]
public bool ValueScoped;
}

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

@ -238,6 +238,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -238,6 +238,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
case SymbolKind.ReturnType:
case SymbolKind.Property:
case SymbolKind.Indexer:
case SymbolKind.Field:
return true; // "ref readonly" is currently always active
default:
return false;
@ -251,8 +252,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -251,8 +252,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
case "NullableContextAttribute":
return (options & TypeSystemOptions.NullabilityAnnotations) != 0
&& (target == SymbolKind.TypeDefinition || IsMethodLike(target));
case "LifetimeAnnotationAttribute":
return (options & TypeSystemOptions.LifetimeAnnotations) != 0
case "ScopedRefAttribute":
return (options & TypeSystemOptions.ScopedRef) != 0
&& (target == SymbolKind.Parameter);
default:
return false;

1
ICSharpCode.Decompiler/TypeSystem/Implementation/FakeMember.cs

@ -109,6 +109,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -109,6 +109,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
}
bool IField.IsReadOnly => false;
bool IField.ReturnTypeIsRefReadOnly => false;
bool IField.IsVolatile => false;
bool IVariable.IsConst => false;

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

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

7
ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataField.cs

@ -189,6 +189,13 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -189,6 +189,13 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
return b.GetAttribute(metadata, def.GetCustomAttributes(), attribute, SymbolKind.Field);
}
public bool ReturnTypeIsRefReadOnly {
get {
var def = module.metadata.GetFieldDefinition(handle);
return def.GetCustomAttributes().HasKnownAttribute(module.metadata, KnownAttribute.IsReadOnly);
}
}
public string FullName => $"{DeclaringType?.FullName}.{Name}";
public string ReflectionName => $"{DeclaringType?.ReflectionName}.{Name}";
public string Namespace => DeclaringType?.Namespace ?? string.Empty;

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

@ -117,32 +117,17 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -117,32 +117,17 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public LifetimeAnnotation Lifetime {
get {
if ((module.TypeSystemOptions & TypeSystemOptions.LifetimeAnnotations) == 0)
if ((module.TypeSystemOptions & TypeSystemOptions.ScopedRef) == 0)
{
return default;
}
var metadata = module.metadata;
var parameterDef = metadata.GetParameter(handle);
foreach (var h in parameterDef.GetCustomAttributes())
if (parameterDef.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.ScopedRef))
{
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 new LifetimeAnnotation { ScopedRef = true };
}
}
return default;
}
}

10
ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedField.cs

@ -45,13 +45,9 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -45,13 +45,9 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
AddSubstitution(substitution);
}
public bool IsReadOnly {
get { return fieldDefinition.IsReadOnly; }
}
public bool IsVolatile {
get { return fieldDefinition.IsVolatile; }
}
public bool IsReadOnly => fieldDefinition.IsReadOnly;
public bool ReturnTypeIsRefReadOnly => fieldDefinition.ReturnTypeIsRefReadOnly;
public bool IsVolatile => fieldDefinition.IsVolatile;
IType IVariable.Type {
get { return this.ReturnType; }

28
ILSpy/Properties/Resources.Designer.cs generated

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@ -1072,15 +1073,6 @@ namespace ICSharpCode.ILSpy.Properties { @@ -1072,15 +1073,6 @@ 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>
/// Looks up a localized string similar to Use nint/nuint types.
/// </summary>
@ -1234,6 +1226,15 @@ namespace ICSharpCode.ILSpy.Properties { @@ -1234,6 +1226,15 @@ namespace ICSharpCode.ILSpy.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to &apos;scoped&apos; lifetime annotation.
/// </summary>
public static string DecompilerSettings_ScopedRef {
get {
return ResourceManager.GetString("DecompilerSettings.ScopedRef", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Separate local variable declarations and initializers (int x = 5; -&gt; int x; x = 5;), if possible.
/// </summary>
@ -1477,6 +1478,15 @@ namespace ICSharpCode.ILSpy.Properties { @@ -1477,6 +1478,15 @@ namespace ICSharpCode.ILSpy.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to UTF-8 string literals.
/// </summary>
public static string DecompilerSettings_Utf8StringLiterals {
get {
return ResourceManager.GetString("DecompilerSettings.Utf8StringLiterals", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to VB-specific options.
/// </summary>

9
ILSpy/Properties/Resources.resx

@ -381,9 +381,6 @@ Are you sure you want to continue?</value> @@ -381,9 +381,6 @@ Are you sure you want to continue?</value>
<data name="DecompilerSettings.IsUnmanagedAttributeOnTypeParametersShouldBeReplacedWithUnmanagedConstraints" xml:space="preserve">
<value>IsUnmanagedAttribute on type parameters should be replaced with 'unmanaged' constraints</value>
</data>
<data name="DecompilerSettings.LifetimeAnnotations" xml:space="preserve">
<value>'scoped' lifetime annotation</value>
</data>
<data name="DecompilerSettings.NativeIntegers" xml:space="preserve">
<value>Use nint/nuint types</value>
</data>
@ -435,6 +432,9 @@ Are you sure you want to continue?</value> @@ -435,6 +432,9 @@ Are you sure you want to continue?</value>
<data name="DecompilerSettings.RequiredMembers" xml:space="preserve">
<value>Required members</value>
</data>
<data name="DecompilerSettings.ScopedRef" xml:space="preserve">
<value>'scoped' lifetime annotation</value>
</data>
<data name="DecompilerSettings.SeparateLocalVariableDeclarations" xml:space="preserve">
<value>Separate local variable declarations and initializers (int x = 5; -&gt; int x; x = 5;), if possible</value>
</data>
@ -516,6 +516,9 @@ Are you sure you want to continue?</value> @@ -516,6 +516,9 @@ Are you sure you want to continue?</value>
<data name="DecompilerSettings.UseVariableNamesFromDebugSymbolsIfAvailable" xml:space="preserve">
<value>Use variable names from debug symbols, if available</value>
</data>
<data name="DecompilerSettings.Utf8StringLiterals" xml:space="preserve">
<value>UTF-8 string literals</value>
</data>
<data name="DecompilerSettings.VBSpecificOptions" xml:space="preserve">
<value>VB-specific options</value>
</data>

Loading…
Cancel
Save