Browse Source

Merge branch 'master' of https://github.com/icsharpcode/ILSpy into deconstruction-pattern-matching-foundation

pull/2119/head
Siegfried Pammer 5 years ago
parent
commit
3080ca0768
  1. 16
      .github/workflows/lock.yml
  2. 18
      BuildTools/sort-resx.ps1
  3. 2
      DecompilerNuGetDemos.workbook
  4. 8
      ICSharpCode.Decompiler.Console/ICSharpCode.Decompiler.Console.csproj
  5. 2
      ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj
  6. 12
      ICSharpCode.Decompiler.Tests/DataFlowTest.cs
  7. 6
      ICSharpCode.Decompiler.Tests/Helpers/Tester.cs
  8. 5
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  9. 4
      ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs
  10. 15
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  11. 43
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.cs
  12. 119
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.il
  13. 12
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs
  14. 18
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs
  15. 143
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/SwitchExpressions.cs
  16. 13
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/YieldReturn.cs
  17. 42
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  18. 20
      ICSharpCode.Decompiler/CSharp/CallBuilder.cs
  19. 72
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  20. 3
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs
  21. 49
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
  22. 9
      ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs
  23. 3
      ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs
  24. 17
      ICSharpCode.Decompiler/CSharp/RequiredNamespaceCollector.cs
  25. 4
      ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolution.cs
  26. 43
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  27. 36
      ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs
  28. 118
      ICSharpCode.Decompiler/CSharp/Syntax/Expressions/SwitchExpression.cs
  29. 6
      ICSharpCode.Decompiler/CSharp/Syntax/IAstVisitor.cs
  30. 67
      ICSharpCode.Decompiler/CSharp/Syntax/Statements/LocalFunctionDeclarationStatement.cs
  31. 5
      ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/Accessor.cs
  32. 1
      ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/PropertyDeclaration.cs
  33. 63
      ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
  34. 20
      ICSharpCode.Decompiler/CSharp/Transforms/ContextTrackingVisitor.cs
  35. 5
      ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs
  36. 54
      ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs
  37. 59
      ICSharpCode.Decompiler/DecompilerSettings.cs
  38. 1
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  39. 33
      ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs
  40. 76
      ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs
  41. 27
      ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs
  42. 2
      ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs
  43. 2
      ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs
  44. 8
      ICSharpCode.Decompiler/IL/ILReader.cs
  45. 13
      ICSharpCode.Decompiler/IL/ILVariable.cs
  46. 3
      ICSharpCode.Decompiler/IL/Instructions.cs
  47. 5
      ICSharpCode.Decompiler/IL/Instructions.tt
  48. 1
      ICSharpCode.Decompiler/IL/Instructions/DynamicInstructions.cs
  49. 17
      ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
  50. 24
      ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs
  51. 24
      ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs
  52. 2
      ICSharpCode.Decompiler/IL/Transforms/CopyPropagation.cs
  53. 4
      ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs
  54. 2
      ICSharpCode.Decompiler/IL/Transforms/EarlyExpressionTransforms.cs
  55. 167
      ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs
  56. 2
      ICSharpCode.Decompiler/IL/Transforms/FixLoneIsInst.cs
  57. 86
      ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
  58. 4
      ICSharpCode.Decompiler/IL/Transforms/IndexRangeTransform.cs
  59. 2
      ICSharpCode.Decompiler/IL/Transforms/IntroduceDynamicTypeOnLocals.cs
  60. 2
      ICSharpCode.Decompiler/IL/Transforms/IntroduceRefReadOnlyModifierOnLocals.cs
  61. 24
      ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs
  62. 2
      ICSharpCode.Decompiler/IL/Transforms/LockTransform.cs
  63. 6
      ICSharpCode.Decompiler/IL/Transforms/NamedArgumentTransform.cs
  64. 2
      ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs
  65. 2
      ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs
  66. 2
      ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs
  67. 2
      ICSharpCode.Decompiler/IL/Transforms/SwitchOnNullableTransform.cs
  68. 2
      ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs
  69. 21
      ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs
  70. 5
      ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs
  71. 4
      ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs
  72. 2
      ICSharpCode.Decompiler/IL/Transforms/UserDefinedLogicTransform.cs
  73. 2
      ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs
  74. 55
      ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs
  75. 107
      ICSharpCode.Decompiler/Metadata/DotNetCorePathFinderExtensions.cs
  76. 4
      ICSharpCode.Decompiler/Metadata/MetadataExtensions.cs
  77. 4
      ICSharpCode.Decompiler/Metadata/PEFile.cs
  78. 33
      ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs
  79. 63
      ICSharpCode.Decompiler/Output/TextTokenWriter.cs
  80. 18
      ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs
  81. 6
      ICSharpCode.Decompiler/TypeSystem/IMethod.cs
  82. 1
      ICSharpCode.Decompiler/TypeSystem/Implementation/FakeMember.cs
  83. 7
      ICSharpCode.Decompiler/TypeSystem/Implementation/GetClassTypeReference.cs
  84. 41
      ICSharpCode.Decompiler/TypeSystem/Implementation/LocalFunctionMethod.cs
  85. 22
      ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs
  86. 3
      ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataProperty.cs
  87. 32
      ICSharpCode.Decompiler/TypeSystem/Implementation/MinimalCorlib.cs
  88. 1
      ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs
  89. 1
      ICSharpCode.Decompiler/TypeSystem/Implementation/SyntheticRangeIndexer.cs
  90. 4
      ICSharpCode.Decompiler/TypeSystem/Implementation/UnknownType.cs
  91. 12
      ICSharpCode.Decompiler/TypeSystem/KnownTypeReference.cs
  92. 60
      ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs
  93. 12
      ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs
  94. 59
      ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs
  95. 1
      ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs
  96. 8
      ICSharpCode.Decompiler/Util/CollectionExtensions.cs
  97. 23
      ILSpy.AddIn/AssemblyFileFinder.cs
  98. 14
      ILSpy.AddIn/Commands/OpenCodeItemCommand.cs
  99. 5
      ILSpy.AddIn/Commands/OpenILSpyCommand.cs
  100. 84
      ILSpy.AddIn/ILSpy.AddIn.csproj
  101. Some files were not shown because too many files have changed in this diff Show More

16
.github/workflows/lock.yml

@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
name: 'Lock threads'
on:
schedule:
- cron: '0 0 * * *'
jobs:
lock:
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v2.0.1
with:
github-token: ${{ github.token }}
issue-lock-inactive-days: '90'
issue-lock-reason: 'resolved'
process-only: 'issues'

18
BuildTools/sort-resx.ps1

@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
$ErrorActionPreference = "Stop";
[Reflection.Assembly]::LoadWithPartialName("System.Xml.Linq") | Out-Null
Write-Host "Sorting .resx files...";
Get-ChildItem -Include *.resx -Recurse | foreach ($_) {
Write-Host $_.FullName;
$doc = [System.Xml.Linq.XDocument]::Load($_.FullName);
$descendants = [System.Linq.Enumerable]::ToArray($doc.Descendants("data"));
[System.Xml.Linq.Extensions]::Remove($descendants);
$ordered = [System.Linq.Enumerable]::OrderBy($descendants, [System.Func[System.Xml.Linq.XElement,string]] { param ($e) $e.Attribute("name").Value }, [System.StringComparer]::Ordinal);
$doc.Root.Add($ordered);
$doc.Save($_.FullName);
}

2
DecompilerNuGetDemos.workbook

@ -6,7 +6,7 @@ platforms: @@ -6,7 +6,7 @@ platforms:
- DotNetCore
packages:
- id: ICSharpCode.Decompiler
version: 6.0.0.5836
version: 6.1.0.5902
---
Setup: load the references required to work with the decompiler

8
ICSharpCode.Decompiler.Console/ICSharpCode.Decompiler.Console.csproj

@ -8,7 +8,7 @@ @@ -8,7 +8,7 @@
<PackAsTool>true</PackAsTool>
<AssemblyName>ilspycmd</AssemblyName>
<ToolCommandName>ilspycmd</ToolCommandName>
<Version>6.0.0.5836</Version>
<Version>6.1.0.5902</Version>
<Description>Command-line decompiler using the ILSpy decompilation engine</Description>
<Copyright>Copyright 2011-2020 AlphaSierraPapa</Copyright>
<PackageProjectUrl>https://github.com/icsharpcode/ILSpy/</PackageProjectUrl>
@ -16,8 +16,8 @@ @@ -16,8 +16,8 @@
<PackageIcon>ILSpyCmdNuGetPackageIcon.png</PackageIcon>
<RepositoryUrl>https://github.com/icsharpcode/ILSpy/</RepositoryUrl>
<Company />
<AssemblyVersion>6.0.0.0</AssemblyVersion>
<FileVersion>6.0.0.0</FileVersion>
<AssemblyVersion>6.1.0.0</AssemblyVersion>
<FileVersion>6.1.0.0</FileVersion>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Authors>ILSpy Team</Authors>
</PropertyGroup>
@ -42,7 +42,7 @@ @@ -42,7 +42,7 @@
</ItemGroup>
<ItemGroup Condition="'$(Configuration)' == 'Release'">
<PackageReference Include="ICSharpCode.Decompiler" Version="6.0.0.5836" />
<PackageReference Include="ICSharpCode.Decompiler" Version="6.1.0.5902" />
</ItemGroup>
<ItemGroup>

2
ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj

@ -17,7 +17,7 @@ @@ -17,7 +17,7 @@
</ItemGroup>
<ItemGroup Condition="'$(Configuration)' == 'Release'">
<PackageReference Include="ICSharpCode.Decompiler" Version="6.0.0.5836" />
<PackageReference Include="ICSharpCode.Decompiler" Version="6.1.0.5902" />
</ItemGroup>
<ItemGroup>

12
ICSharpCode.Decompiler.Tests/DataFlowTest.cs

@ -55,10 +55,14 @@ namespace ICSharpCode.Decompiler.Tests @@ -55,10 +55,14 @@ namespace ICSharpCode.Decompiler.Tests
public void TryFinallyWithAssignmentInFinally()
{
ILVariable v = new ILVariable(VariableKind.Local, SpecialType.UnknownType, 0);
ILFunction f = new ILFunction((IMethod)null, 0, new GenericContext(), new TryFinally(
new Nop(),
new StLoc(v, new LdcI4(0))
));
ILFunction f = new ILFunction(
returnType: SpecialType.UnknownType,
parameters: new IParameter[0],
genericContext: new GenericContext(),
body: new TryFinally(
new Nop(),
new StLoc(v, new LdcI4(0))
));
f.AddRef();
f.Variables.Add(v);
f.Body.AcceptVisitor(new RDTest(f, v));

6
ICSharpCode.Decompiler.Tests/Helpers/Tester.cs

@ -185,7 +185,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers @@ -185,7 +185,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
return Regex.Replace(il, @"'<PrivateImplementationDetails>\{[0-9A-F-]+\}'", "'<PrivateImplementationDetails>'");
}
static readonly string coreRefAsmPath = new DotNetCorePathFinder(new Version(3, 1)).GetReferenceAssemblyPath(".NETCoreApp, Version = v3.1");
static readonly string coreRefAsmPath = new DotNetCorePathFinder(TargetFrameworkIdentifier.NETCoreApp, new Version(3, 1)).GetReferenceAssemblyPath(".NETCoreApp, Version = v3.1");
static readonly string refAsmPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86),
@"Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2");
@ -251,6 +251,10 @@ namespace ICSharpCode.Decompiler.Tests.Helpers @@ -251,6 +251,10 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
preprocessorSymbols.Add("VB11");
preprocessorSymbols.Add("VB14");
preprocessorSymbols.Add("VB15");
if (flags.HasFlag(CompilerOptions.Preview)) {
preprocessorSymbols.Add("CS90");
}
} else if (flags.HasFlag(CompilerOptions.UseMcs)) {
preprocessorSymbols.Add("MCS");
} else {

5
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -42,8 +42,8 @@ @@ -42,8 +42,8 @@
<ItemGroup>
<PackageReference Include="DiffLib" Version="2017.7.26.1241" />
<PackageReference Include="Microsoft.Build.Locator" Version="1.2.2" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.7.0-4.final" />
<PackageReference Include="Microsoft.CodeAnalysis.VisualBasic" Version="3.7.0-4.final" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.7.0" />
<PackageReference Include="Microsoft.CodeAnalysis.VisualBasic" Version="3.7.0" />
<PackageReference Include="Microsoft.DiaSymReader.Converter.Xml" Version="1.1.0-beta1-63314-01" />
<PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
<PackageReference Include="System.Collections.Immutable" Version="1.5.0" />
@ -93,6 +93,7 @@ @@ -93,6 +93,7 @@
<Compile Include="TestAssemblyResolver.cs" />
<Compile Include="TestCases\Correctness\StringConcat.cs" />
<Compile Include="TestCases\Pretty\DeconstructionTests.cs" />
<Compile Include="TestCases\Pretty\SwitchExpressions.cs" />
<None Include="TestCases\Pretty\NativeInts.cs" />
<None Include="TestCases\ILPretty\CallIndirect.cs" />
<Compile Include="TestCases\ILPretty\Issue1681.cs" />

4
ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs

@ -127,13 +127,13 @@ namespace ICSharpCode.Decompiler.Tests @@ -127,13 +127,13 @@ namespace ICSharpCode.Decompiler.Tests
[Test]
public void CS1xSwitch_Debug()
{
Run();
Run(settings: new DecompilerSettings { SwitchExpressions = false });
}
[Test]
public void CS1xSwitch_Release()
{
Run();
Run(settings: new DecompilerSettings { SwitchExpressions = false });
}
[Test]

15
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -135,10 +135,17 @@ namespace ICSharpCode.Decompiler.Tests @@ -135,10 +135,17 @@ namespace ICSharpCode.Decompiler.Tests
{
RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings {
// legacy csc generates a dead store in debug builds
RemoveDeadStores = (cscOptions == CompilerOptions.None)
RemoveDeadStores = (cscOptions == CompilerOptions.None),
SwitchExpressions = false,
});
}
[Test]
public void SwitchExpressions([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void ReduceNesting([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions)
{
@ -148,7 +155,7 @@ namespace ICSharpCode.Decompiler.Tests @@ -148,7 +155,7 @@ namespace ICSharpCode.Decompiler.Tests
[Test]
public void DelegateConstruction([ValueSource(nameof(defaultOptionsWithMcs))] CompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
RunForLibrary(cscOptions: cscOptions | CompilerOptions.Preview);
}
[Test]
@ -199,7 +206,7 @@ namespace ICSharpCode.Decompiler.Tests @@ -199,7 +206,7 @@ namespace ICSharpCode.Decompiler.Tests
[Test]
public void LocalFunctions([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
RunForLibrary(cscOptions: cscOptions | CompilerOptions.Preview);
}
[Test]
@ -282,7 +289,7 @@ namespace ICSharpCode.Decompiler.Tests @@ -282,7 +289,7 @@ namespace ICSharpCode.Decompiler.Tests
}
[Test]
public void ValueTypes([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions)
public void ValueTypes([ValueSource(nameof(defaultOptionsWithMcs))] CompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}

43
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.cs

@ -25,6 +25,47 @@ internal sealed class ExtraUnsafeTests @@ -25,6 +25,47 @@ internal sealed class ExtraUnsafeTests
{
return (uint*)Unsafe.AsPointer(ref managedPtr);
}
public static ref ulong RefAssignTypeMismatch(ref uint a, ref uint b)
{
ref ushort reference = ref Unsafe.As<uint, ushort>(ref a);
if (a != 0) {
reference = ref Unsafe.As<uint, ushort>(ref b);
}
Console.WriteLine(reference);
return ref Unsafe.As<ushort, ulong>(ref reference);
}
public unsafe static byte[] Issue1292(int val, byte[] arr)
{
//The blocks IL_0019 are reachable both inside and outside the pinned region starting at IL_0013. ILSpy has duplicated these blocks in order to place them both within and outside the `fixed` statement.
byte[] array;
if ((array = arr) != null && array.Length != 0) {
fixed (byte* ptr = &array[0]) {
*(int*)ptr = val;
}
} else {
/*pinned*/ref byte reference = ref *(byte*)null;
*(int*)Unsafe.AsPointer(ref reference) = val;
}
return arr;
}
public unsafe void pin_ptr_test(int[] a, int[] b)
{
//The blocks IL_0016 are reachable both inside and outside the pinned region starting at IL_0007. ILSpy has duplicated these blocks in order to place them both within and outside the `fixed` statement.
ref int reference;
fixed (int* ptr = &a[0]) {
if (*ptr <= 0) {
ptr[4 * 0] = 1;
return;
}
reference = ref *ptr;
}
fixed (int* ptr = &b[reference]) {
ptr[4 * 0] = 1;
}
}
}
namespace System.Runtime.CompilerServices
@ -228,7 +269,7 @@ namespace System.Runtime.CompilerServices @@ -228,7 +269,7 @@ namespace System.Runtime.CompilerServices
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IntPtr ByteOffset<T>(ref T origin, ref T target)
{
return Unsafe.ByteOffset(ref target, ref origin);
return Unsafe.ByteOffset(target: ref target, origin: ref origin);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]

119
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.il

@ -474,4 +474,123 @@ @@ -474,4 +474,123 @@
ldarg.0
ret
}
.method public hidebysig static uint64& RefAssignTypeMismatch(uint32& a, uint32& b)
{
.maxstack 8
.locals (
[0] uint16&
)
ldarg.0
stloc.0
ldarg.0
ldind.i4
brfalse lbl
ldarg.1
stloc.0
lbl:
ldloc.0
ldind.i2
call void [mscorlib]System.Console::WriteLine(uint16)
ldloc.0
ret
}
.method public hidebysig static
uint8[] Issue1292 (
int32 val,
uint8[] arr
) cil managed
{
.maxstack 2
.locals init (
[0] uint8[],
[1] uint8[],
[2] uint8& pinned
)
IL_0000: ldarg.1
IL_0001: stloc.0
IL_0002: ldloc.0
IL_0003: dup
IL_0004: stloc.1
IL_0005: brfalse.s IL_0016
IL_0007: ldloc.1
IL_0008: ldlen
IL_0009: conv.i4
IL_000a: brfalse.s IL_0016
IL_000c: ldloc.1
IL_000d: ldc.i4.0
IL_000e: ldelema [mscorlib]System.Byte
IL_0013: stloc.2
IL_0014: br.s IL_0019
IL_0016: ldc.i4.0
IL_0017: conv.u
IL_0018: stloc.2
IL_0019: ldloc.2
IL_001a: conv.i
IL_001b: ldarg.0
IL_001c: stind.i4
IL_001d: ldc.i4.0
IL_001e: conv.u
IL_001f: stloc.2
IL_0020: ldloc.0
IL_0021: ret
} // end of method Issue1292
.method /* 06000066 */ public hidebysig
instance void pin_ptr_test (
int32[] a,
int32[] b
) cil managed
{
/* From C++/CLI:
void pin_ptr_test(array<int>^ a, array<int>^ b)
{
pin_ptr<int> p = &a[0];
if (*p > 0)
{
p = &b[*p];
}
p[0] = 1;
}
*/
.maxstack 3
.locals /* 11000004 */ (
[0] int32& pinned modopt([mscorlib]System.Runtime.CompilerServices.IsExplicitlyDereferenced) p
)
IL_0000: ldarg.1
IL_0001: ldc.i4.0
IL_0002: ldelema [mscorlib]System.Int32 /* 01000016 */
IL_0007: stloc.0
IL_0008: ldloc.0
IL_0009: ldind.i4
IL_000a: ldc.i4.0
IL_000b: ble.s IL_0016
IL_000d: ldarg.2
IL_000e: ldloc.0
IL_000f: ldind.i4
IL_0010: ldelema [mscorlib]System.Int32 /* 01000016 */
IL_0015: stloc.0
IL_0016: ldloc.0
IL_0017: ldc.i4.4
IL_0018: ldc.i4.0
IL_0019: mul
IL_001a: add
IL_001b: ldc.i4.1
IL_001c: stind.i4
IL_001d: ret
} // end of method pin_ptr_test
}

12
ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs

@ -428,6 +428,18 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -428,6 +428,18 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
return () => valueTuple.RenamedString;
}
#endif
public static Func<T, T> Identity<T>()
{
return (T _) => _;
}
#if CS90
public static Func<int, int, int, int> LambdaParameterDiscard()
{
return (int _, int _, int _) => 0;
}
#endif
}
public class Issue1867

18
ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs

@ -23,6 +23,12 @@ namespace LocalFunctions @@ -23,6 +23,12 @@ namespace LocalFunctions
{
internal class LocalFunctions
{
[AttributeUsage(AttributeTargets.All)]
internal class MyAttribute : Attribute
{
}
public class Generic<T1> where T1 : struct, ICloneable, IConvertible
{
public int MixedLocalFunction<T2>() where T2 : ICloneable, IConvertible
@ -31,15 +37,21 @@ namespace LocalFunctions @@ -31,15 +37,21 @@ namespace LocalFunctions
object z = this;
for (int j = 0; j < 10; j++) {
int i = 0;
i += NonStaticMethod6<object>();
int NonStaticMethod6<T3>()
i += NonStaticMethod6<object>(0);
#if CS90
[My]
[return: My]
int NonStaticMethod6<[My] T3>([My] int unused)
#else
int NonStaticMethod6<T3>(int unused)
#endif
{
t2 = default(T2);
int l = 0;
return NonStaticMethod6_1<T1>() + NonStaticMethod6_1<T2>() + z.GetHashCode();
int NonStaticMethod6_1<T4>()
{
return i + l + NonStaticMethod6<T4>() + StaticMethod1<decimal>();
return i + l + NonStaticMethod6<T4>(0) + StaticMethod1<decimal>();
}
}
}

143
ICSharpCode.Decompiler.Tests/TestCases/Pretty/SwitchExpressions.cs

@ -0,0 +1,143 @@ @@ -0,0 +1,143 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
public static class SwitchExpressions
{
public enum State
{
False,
True,
Null
}
public static bool? SwitchOverNullableEnum(State? state)
{
return state switch {
State.False => false,
State.True => true,
State.Null => null,
_ => throw new InvalidOperationException(),
};
}
public static string SparseIntegerSwitch(int i)
{
Console.WriteLine("SparseIntegerSwitch: " + i);
return i switch {
-10000000 => "-10 mln",
-100 => "-hundred",
-1 => "-1",
0 => "0",
1 => "1",
2 => "2",
4 => "4",
100 => "hundred",
10000 => "ten thousand",
10001 => "ten thousand and one",
int.MaxValue => "int.MaxValue",
_ => "something else",
};
}
public static bool SparseIntegerSwitch3(int i)
{
// not using a switch expression because we'd have to duplicate the 'true' branch
switch (i) {
case 0:
case 10:
case 11:
case 12:
case 100:
case 101:
case 200:
return true;
default:
return false;
}
}
public static string SwitchOverNullableInt(int? i, int? j)
{
return (i + j) switch {
null => "null",
0 => "zero",
5 => "five",
10 => "ten",
_ => "large",
};
}
public static void SwitchOverInt(int i)
{
Console.WriteLine(i switch {
0 => "zero",
5 => "five",
10 => "ten",
15 => "fifteen",
20 => "twenty",
25 => "twenty-five",
30 => "thirty",
_ => throw new NotImplementedException(),
});
}
public static string SwitchOverString1(string text)
{
Console.WriteLine("SwitchOverString1: " + text);
return text switch {
"First case" => "Text1",
"Second case" => "Text2",
"Third case" => "Text3",
"Fourth case" => "Text4",
"Fifth case" => "Text5",
"Sixth case" => "Text6",
null => null,
_ => "Default",
};
}
public static string SwitchOverString2(string text)
{
Console.WriteLine("SwitchOverString1: " + text);
// Cannot use switch expression, because "return Text2;" would need to be duplicated
switch (text) {
case "First case":
return "Text1";
case "Second case":
case "2nd case":
return "Text2";
case "Third case":
return "Text3";
case "Fourth case":
return "Text4";
case "Fifth case":
return "Text5";
case "Sixth case":
return "Text6";
case null:
return null;
default:
return "Default";
}
}
}
}

13
ICSharpCode.Decompiler.Tests/TestCases/Pretty/YieldReturn.cs

@ -356,5 +356,18 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -356,5 +356,18 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
Console.WriteLine("normal exit");
}
internal IEnumerable<int> ForLoopWithYieldReturn(int end, int evil)
{
// This loop needs to pick the implicit "yield break;" as exit point
// in order to produce pretty code; not the "throw" which would
// be a less-pretty option.
for (int i = 0; i < end; i++) {
if (i == evil) {
throw new InvalidOperationException("Found evil number");
}
yield return i;
}
}
}
}

42
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -406,6 +406,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -406,6 +406,7 @@ namespace ICSharpCode.Decompiler.CSharp
typeSystemAstBuilder.AlwaysUseShortTypeNames = true;
typeSystemAstBuilder.AddResolveResultAnnotations = true;
typeSystemAstBuilder.UseNullableSpecifierForValueTypes = settings.LiftNullables;
typeSystemAstBuilder.SupportInitAccessors = settings.InitAccessors;
return typeSystemAstBuilder;
}
@ -1346,30 +1347,35 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1346,30 +1347,35 @@ namespace ICSharpCode.Decompiler.CSharp
}
entityDecl.AddAnnotation(function);
if (function.IsIterator) {
if (localSettings.DecompileMemberBodies && !body.Descendants.Any(d => d is YieldReturnStatement || d is YieldBreakStatement)) {
body.Add(new YieldBreakStatement());
}
if (function.IsAsync) {
RemoveAttribute(entityDecl, KnownAttribute.AsyncIteratorStateMachine);
} else {
RemoveAttribute(entityDecl, KnownAttribute.IteratorStateMachine);
}
if (function.StateMachineCompiledWithMono) {
RemoveAttribute(entityDecl, KnownAttribute.DebuggerHidden);
}
CleanUpMethodDeclaration(entityDecl, body, function, localSettings.DecompileMemberBodies);
} catch (Exception innerException) when (!(innerException is OperationCanceledException || innerException is DecompilerException)) {
throw new DecompilerException(module, method, innerException);
}
}
internal static void CleanUpMethodDeclaration(EntityDeclaration entityDecl, BlockStatement body, ILFunction function, bool decompileBody = true)
{
if (function.IsIterator) {
if (decompileBody && !body.Descendants.Any(d => d is YieldReturnStatement || d is YieldBreakStatement)) {
body.Add(new YieldBreakStatement());
}
if (function.IsAsync) {
entityDecl.Modifiers |= Modifiers.Async;
RemoveAttribute(entityDecl, KnownAttribute.AsyncStateMachine);
RemoveAttribute(entityDecl, KnownAttribute.DebuggerStepThrough);
RemoveAttribute(entityDecl, KnownAttribute.AsyncIteratorStateMachine);
} else {
RemoveAttribute(entityDecl, KnownAttribute.IteratorStateMachine);
}
} catch (Exception innerException) when (!(innerException is OperationCanceledException || innerException is DecompilerException)) {
throw new DecompilerException(module, method, innerException);
if (function.StateMachineCompiledWithMono) {
RemoveAttribute(entityDecl, KnownAttribute.DebuggerHidden);
}
}
if (function.IsAsync) {
entityDecl.Modifiers |= Modifiers.Async;
RemoveAttribute(entityDecl, KnownAttribute.AsyncStateMachine);
RemoveAttribute(entityDecl, KnownAttribute.DebuggerStepThrough);
}
}
bool RemoveAttribute(EntityDeclaration entityDecl, KnownAttribute attributeType)
internal static bool RemoveAttribute(EntityDeclaration entityDecl, KnownAttribute attributeType)
{
bool found = false;
foreach (var section in entityDecl.Attributes) {

20
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

@ -197,8 +197,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -197,8 +197,7 @@ namespace ICSharpCode.Decompiler.CSharp
} else if (localFunction != null) {
var ide = new IdentifierExpression(localFunction.Name);
if (method.TypeArguments.Count > 0) {
int skipCount = localFunction.ReducedMethod.NumberOfCompilerGeneratedTypeParameters;
ide.TypeArguments.AddRange(method.TypeArguments.Skip(skipCount).Select(expressionBuilder.ConvertType));
ide.TypeArguments.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType));
}
ide.AddAnnotation(localFunction);
target = ide.WithoutILInstruction()
@ -812,7 +811,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -812,7 +811,7 @@ namespace ICSharpCode.Decompiler.CSharp
// that are no longer required once we add the type arguments.
// We lend overload resolution a hand by detecting such cases beforehand and requiring type arguments,
// if necessary.
if (!CanInferTypeArgumentsFromParameters(method, argumentList.Arguments.SelectArray(a => a.ResolveResult), expressionBuilder.typeInference)) {
if (!CanInferTypeArgumentsFromArguments(method, argumentList, expressionBuilder.typeInference)) {
requireTypeArguments = true;
typeArguments = method.TypeArguments.ToArray();
appliedRequireTypeArgumentsShortcut = true;
@ -896,14 +895,14 @@ namespace ICSharpCode.Decompiler.CSharp @@ -896,14 +895,14 @@ namespace ICSharpCode.Decompiler.CSharp
return method.IsExtensionMethod && arguments.Count > 0 && arguments[0].Expression is NullReferenceExpression;
}
public static bool CanInferTypeArgumentsFromParameters(IMethod method, IReadOnlyList<ResolveResult> arguments,
TypeInference typeInference)
static bool CanInferTypeArgumentsFromArguments(IMethod method, ArgumentList argumentList, TypeInference typeInference)
{
if (method.TypeParameters.Count == 0)
return true;
// always use unspecialized member, otherwise type inference fails
method = (IMethod)method.MemberDefinition;
typeInference.InferTypeArguments(method.TypeParameters, arguments, method.Parameters.SelectReadOnlyArray(p => p.Type),
var parametersInArgumentOrder = argumentList.ArgumentToParameterMap == null ? method.Parameters : argumentList.ArgumentToParameterMap.SelectReadOnlyArray(index => method.Parameters[index]);
typeInference.InferTypeArguments(method.TypeParameters, argumentList.Arguments.SelectReadOnlyArray(a => a.ResolveResult), parametersInArgumentOrder.SelectReadOnlyArray(p => p.Type),
out bool success);
return success;
}
@ -1327,7 +1326,6 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1327,7 +1326,6 @@ namespace ICSharpCode.Decompiler.CSharp
target = default;
targetType = default;
methodName = localFunction.Name;
// TODO : think about how to handle generic local functions
} else if (method.IsExtensionMethod && invokeMethod != null && method.Parameters.Count - 1 == invokeMethod.Parameters.Count) {
step = 5;
targetType = method.Parameters[0].Type;
@ -1406,11 +1404,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1406,11 +1404,7 @@ namespace ICSharpCode.Decompiler.CSharp
} else {
var ide = new IdentifierExpression(methodName);
if ((step & 2) != 0) {
int skipCount = 0;
if (localFunction != null && method.TypeArguments.Count > 0) {
skipCount = localFunction.ReducedMethod.NumberOfCompilerGeneratedTypeParameters;
}
ide.TypeArguments.AddRange(method.TypeArguments.Skip(skipCount).Select(expressionBuilder.ConvertType));
ide.TypeArguments.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType));
}
targetExpression = ide.WithRR(result);
}
@ -1469,7 +1463,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1469,7 +1463,7 @@ namespace ICSharpCode.Decompiler.CSharp
method.DeclaringType,
new IParameterizedMember[] { method }
)
}, method.TypeArguments.Skip(localFunction.ReducedMethod.NumberOfCompilerGeneratedTypeParameters).ToArray()
}, method.TypeArguments
);
}

72
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -92,6 +92,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -92,6 +92,7 @@ namespace ICSharpCode.Decompiler.CSharp
this.astBuilder = new TypeSystemAstBuilder(resolver);
this.astBuilder.AlwaysUseShortTypeNames = true;
this.astBuilder.AddResolveResultAnnotations = true;
this.astBuilder.ShowAttributes = true;
this.astBuilder.UseNullableSpecifierForValueTypes = settings.LiftNullables;
this.typeInference = new TypeInference(compilation) { Algorithm = TypeInferenceAlgorithm.Improved };
}
@ -639,6 +640,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -639,6 +640,7 @@ namespace ICSharpCode.Decompiler.CSharp
if (lhs.Expression is DirectionExpression dirExpr && lhs.ResolveResult is ByReferenceResolveResult lhsRefRR) {
// ref (re-)assignment, emit "ref (a = ref b)".
lhs = lhs.UnwrapChild(dirExpr.Expression);
translatedValue = translatedValue.ConvertTo(lhsRefRR.Type, this, allowImplicitConversion: true);
var assign = new AssignmentExpression(lhs.Expression, translatedValue.Expression)
.WithRR(new OperatorResolveResult(lhs.Type, ExpressionType.Assign, lhsRefRR, translatedValue.ResolveResult));
return new DirectionExpression(FieldDirection.Ref, assign)
@ -1075,7 +1077,11 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1075,7 +1077,11 @@ namespace ICSharpCode.Decompiler.CSharp
return null;
if (inst.Operator == BinaryNumericOperator.Sub && inst.LeftInputType == StackType.Ref && inst.RightInputType == StackType.Ref) {
// ref - ref => i
return CallUnsafeIntrinsic("ByteOffset", new[] { left.Expression, right.Expression }, compilation.FindType(KnownTypeCode.IntPtr), inst);
return CallUnsafeIntrinsic("ByteOffset", new[] {
// ByteOffset() expects the parameters the wrong way around, so order using named arguments
new NamedArgumentExpression("target", left.Expression),
new NamedArgumentExpression("origin", right.Expression)
}, compilation.FindType(KnownTypeCode.IntPtr), inst);
}
if (inst.LeftInputType == StackType.Ref && inst.RightInputType.IsIntegerType()
&& left.Type is ByReferenceType brt) {
@ -1788,6 +1794,15 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1788,6 +1794,15 @@ namespace ICSharpCode.Decompiler.CSharp
// Case 4 (left-over extension from implicit conversion) can also be handled by our caller.
return arg.WithILInstruction(inst);
}
case ConversionKind.Invalid:
if (inst.InputType == StackType.Unknown && inst.TargetType == IL.PrimitiveType.None && arg.Type.Kind == TypeKind.Unknown) {
// Unknown -> O conversion.
// Our post-condition allows us to also use expressions with unknown type where O is expected,
// so avoid introducing an `(object)` cast because we're likely to cast back to the same unknown type,
// just in a signature context where we know that it's a class type.
return arg.WithILInstruction(inst);
}
goto default;
default: {
// We need to convert to inst.TargetType, or to an equivalent type.
IType targetType;
@ -1983,7 +1998,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1983,7 +1998,7 @@ namespace ICSharpCode.Decompiler.CSharp
return SpecialType.UnknownType;
}
internal IEnumerable<ParameterDeclaration> MakeParameters(IReadOnlyList<IParameter> parameters, ILFunction function)
IEnumerable<ParameterDeclaration> MakeParameters(IReadOnlyList<IParameter> parameters, ILFunction function)
{
var variables = function.Variables.Where(v => v.Kind == VariableKind.Parameter).ToDictionary(v => v.Index);
int i = 0;
@ -1992,9 +2007,6 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1992,9 +2007,6 @@ namespace ICSharpCode.Decompiler.CSharp
if (string.IsNullOrEmpty(pd.Name) && !pd.Type.IsArgList()) {
// needs to be consistent with logic in ILReader.CreateILVarable(ParameterDefinition)
pd.Name = "P_" + i;
// if this is a local function, we have to skip the parameters for closure references
if (settings.LocalFunctions && function.Kind == ILFunctionKind.LocalFunction && IL.Transforms.LocalFunctionDecompiler.IsClosureParameter(parameter, decompilationContext))
break;
}
if (settings.AnonymousTypes && parameter.Type.ContainsAnonymousType())
pd.Type = null;
@ -2922,6 +2934,56 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2922,6 +2934,56 @@ namespace ICSharpCode.Decompiler.CSharp
}
}
protected internal override TranslatedExpression VisitSwitchInstruction(SwitchInstruction inst, TranslationContext context)
{
TranslatedExpression value;
if (inst.Value is StringToInt strToInt) {
value = Translate(strToInt.Argument);
} else {
strToInt = null;
value = Translate(inst.Value);
}
IL.SwitchSection defaultSection = inst.GetDefaultSection();
SwitchExpression switchExpr = new SwitchExpression();
switchExpr.Expression = value;
IType resultType;
if (context.TypeHint.Kind != TypeKind.Unknown && context.TypeHint.GetStackType() == inst.ResultType) {
resultType = context.TypeHint;
} else {
resultType = compilation.FindType(inst.ResultType.ToKnownTypeCode());
}
foreach (var section in inst.Sections) {
if (section == defaultSection)
continue;
var ses = new SwitchExpressionSection();
if (section.HasNullLabel) {
Debug.Assert(section.Labels.IsEmpty);
ses.Pattern = new NullReferenceExpression();
} else {
long val = section.Labels.Values.Single();
var rr = statementBuilder.CreateTypedCaseLabel(val, value.Type, strToInt?.Map).Single();
ses.Pattern = astBuilder.ConvertConstantValue(rr);
}
ses.Body = TranslateSectionBody(section);
switchExpr.SwitchSections.Add(ses);
}
var defaultSES = new SwitchExpressionSection();
defaultSES.Pattern = new IdentifierExpression("_");
defaultSES.Body = TranslateSectionBody(defaultSection);
switchExpr.SwitchSections.Add(defaultSES);
return switchExpr.WithILInstruction(inst).WithRR(new ResolveResult(resultType));
Expression TranslateSectionBody(IL.SwitchSection section)
{
var body = Translate(section.Body, resultType);
return body.ConvertTo(resultType, this, allowImplicitConversion: true);
}
}
protected internal override TranslatedExpression VisitAddressOf(AddressOf inst, TranslationContext context)
{
// HACK: this is only correct if the argument is an R-value; otherwise we're missing the copy to the temporary

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

@ -24,6 +24,7 @@ using System.Linq; @@ -24,6 +24,7 @@ using System.Linq;
using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.Output;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
{
@ -222,7 +223,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -222,7 +223,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
{
TypeSystemAstBuilder astBuilder = CreateAstBuilder();
EntityDeclaration node = astBuilder.ConvertEntity(member);
if ((ConversionFlags & ConversionFlags.ShowDeclaringType) == ConversionFlags.ShowDeclaringType && member.DeclaringType != null) {
if ((ConversionFlags & ConversionFlags.ShowDeclaringType) == ConversionFlags.ShowDeclaringType && member.DeclaringType != null && !(member is LocalFunctionMethod)) {
ConvertType(member.DeclaringType, writer, formattingPolicy);
writer.WriteToken(Roles.Dot, ".");
}

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

@ -1770,6 +1770,33 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -1770,6 +1770,33 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
EndNode(caseLabel);
}
public virtual void VisitSwitchExpression(SwitchExpression switchExpression)
{
StartNode(switchExpression);
switchExpression.Expression.AcceptVisitor(this);
Space();
WriteKeyword(SwitchExpression.SwitchKeywordRole);
OpenBrace(BraceStyle.EndOfLine);
foreach (AstNode node in switchExpression.SwitchSections) {
node.AcceptVisitor(this);
Comma(node);
NewLine();
}
CloseBrace(BraceStyle.EndOfLine);
EndNode(switchExpression);
}
public virtual void VisitSwitchExpressionSection(SwitchExpressionSection switchExpressionSection)
{
StartNode(switchExpressionSection);
switchExpressionSection.Pattern.AcceptVisitor(this);
Space();
WriteToken(Roles.Arrow);
Space();
switchExpressionSection.Body.AcceptVisitor(this);
EndNode(switchExpressionSection);
}
public virtual void VisitThrowStatement(ThrowStatement throwStatement)
{
StartNode(throwStatement);
@ -1887,19 +1914,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -1887,19 +1914,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
public virtual void VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement)
{
StartNode(localFunctionDeclarationStatement);
WriteModifiers(localFunctionDeclarationStatement.ModifierTokens);
localFunctionDeclarationStatement.ReturnType.AcceptVisitor(this);
Space();
WriteIdentifier(localFunctionDeclarationStatement.NameToken);
WriteTypeParameters(localFunctionDeclarationStatement.TypeParameters);
Space(policy.SpaceBeforeMethodDeclarationParentheses);
WriteCommaSeparatedListInParenthesis(localFunctionDeclarationStatement.Parameters, policy.SpaceWithinMethodDeclarationParentheses);
foreach (Constraint constraint in localFunctionDeclarationStatement.Constraints) {
constraint.AcceptVisitor(this);
}
WriteMethodBody(localFunctionDeclarationStatement.Body, policy.MethodBraceStyle);
localFunctionDeclarationStatement.Declaration.AcceptVisitor(this);
EndNode(localFunctionDeclarationStatement);
}
@ -1950,7 +1965,11 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -1950,7 +1965,11 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
WriteKeyword("get", PropertyDeclaration.GetKeywordRole);
style = policy.PropertyGetBraceStyle;
} else if (accessor.Role == PropertyDeclaration.SetterRole) {
WriteKeyword("set", PropertyDeclaration.SetKeywordRole);
if (accessor.Keyword.Role == PropertyDeclaration.InitKeywordRole) {
WriteKeyword("init", PropertyDeclaration.InitKeywordRole);
} else {
WriteKeyword("set", PropertyDeclaration.SetKeywordRole);
}
style = policy.PropertySetBraceStyle;
} else if (accessor.Role == CustomEventDeclaration.AddAccessorRole) {
WriteKeyword("add", CustomEventDeclaration.AddKeywordRole);
@ -2596,7 +2615,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -2596,7 +2615,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
} else if (childNode is Repeat) {
VisitRepeat((Repeat)childNode);
} else {
TextWriterTokenWriter.PrintPrimitiveValue(childNode);
writer.WritePrimitiveValue(childNode);
}
}
#endregion

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

@ -52,6 +52,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -52,6 +52,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
Shift, // << >>
Additive, // binary + -
Multiplicative, // * / %
Switch, // C# 8 switch expression
Range, // ..
Unary,
QueryOrLambda,
@ -145,6 +146,8 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -145,6 +146,8 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
return PrecedenceLevel.Conditional;
if (expr is AssignmentExpression || expr is LambdaExpression)
return PrecedenceLevel.Assignment;
if (expr is SwitchExpression)
return PrecedenceLevel.Switch;
// anything else: primary expression
return PrecedenceLevel.Primary;
}
@ -414,5 +417,11 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -414,5 +417,11 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
}
base.VisitNamedExpression (namedExpression);
}
public override void VisitSwitchExpression(SwitchExpression switchExpression)
{
ParenthesizeIfRequired(switchExpression.Expression, PrecedenceLevel.Switch + 1);
base.VisitSwitchExpression(switchExpression);
}
}
}

3
ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs

@ -33,6 +33,7 @@ using static ICSharpCode.Decompiler.Metadata.MetadataExtensions; @@ -33,6 +33,7 @@ using static ICSharpCode.Decompiler.Metadata.MetadataExtensions;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Solution;
using ICSharpCode.Decompiler.DebugInfo;
using System.Collections.Concurrent;
namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
{
@ -205,7 +206,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -205,7 +206,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
var progress = ProgressIndicator;
DecompilerTypeSystem ts = new DecompilerTypeSystem(module, AssemblyResolver, Settings);
Parallel.ForEach(
files,
Partitioner.Create(files, loadBalance: true),
new ParallelOptions {
MaxDegreeOfParallelism = this.MaxDegreeOfParallelism,
CancellationToken = cancellationToken

17
ICSharpCode.Decompiler/CSharp/RequiredNamespaceCollector.cs

@ -93,17 +93,18 @@ namespace ICSharpCode.Decompiler.CSharp @@ -93,17 +93,18 @@ namespace ICSharpCode.Decompiler.CSharp
CollectNamespacesForTypeReference(field.ReturnType);
break;
case IMethod method:
HandleAttributes(method.GetAttributes());
HandleAttributes(method.GetReturnTypeAttributes());
CollectNamespacesForTypeReference(method.ReturnType);
foreach (var param in method.Parameters) {
HandleAttributes(param.GetAttributes());
CollectNamespacesForTypeReference(param.Type);
}
HandleTypeParameters(method.TypeParameters);
var reader = module.PEFile.Reader;
var parts = mappingInfo.GetMethodParts((MethodDefinitionHandle)method.MetadataToken).ToList();
foreach (var part in parts) {
var partMethod = module.ResolveMethod(part, genericContext);
HandleAttributes(partMethod.GetAttributes());
HandleAttributes(partMethod.GetReturnTypeAttributes());
CollectNamespacesForTypeReference(partMethod.ReturnType);
foreach (var param in partMethod.Parameters) {
HandleAttributes(param.GetAttributes());
CollectNamespacesForTypeReference(param.Type);
}
HandleTypeParameters(partMethod.TypeParameters);
HandleOverrides(part.GetMethodImplementations(module.metadata), module);
var methodDef = module.metadata.GetMethodDefinition(part);
if (method.HasBody) {

4
ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolution.cs

@ -423,8 +423,10 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -423,8 +423,10 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
}
} else {
TypeInference ti = new TypeInference(compilation, conversions);
IType[] parameterTypes = candidate.ArgumentToParameterMap
.SelectReadOnlyArray(parameterIndex => parameterIndex >= 0 ? candidate.ParameterTypes[parameterIndex] : SpecialType.UnknownType);
bool success;
candidate.InferredTypes = ti.InferTypeArguments(candidate.TypeParameters, arguments, candidate.ParameterTypes, out success, classTypeArguments);
candidate.InferredTypes = ti.InferTypeArguments(candidate.TypeParameters, arguments, parameterTypes, out success, classTypeArguments);
if (!success)
candidate.AddError(OverloadResolutionErrors.TypeInferenceFailed);
}

43
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -120,7 +120,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -120,7 +120,7 @@ namespace ICSharpCode.Decompiler.CSharp
return new IfElseStatement(condition, trueStatement, falseStatement).WithILInstruction(inst);
}
IEnumerable<ConstantResolveResult> CreateTypedCaseLabel(long i, IType type, List<(string Key, int Value)> map = null)
internal IEnumerable<ConstantResolveResult> CreateTypedCaseLabel(long i, IType type, List<(string Key, int Value)> map = null)
{
object value;
// unpack nullable type, if necessary:
@ -165,20 +165,14 @@ namespace ICSharpCode.Decompiler.CSharp @@ -165,20 +165,14 @@ namespace ICSharpCode.Decompiler.CSharp
caseLabelMapping = new Dictionary<Block, ConstantResolveResult>();
TranslatedExpression value;
var strToInt = inst.Value as StringToInt;
if (strToInt != null) {
if (inst.Value is StringToInt strToInt) {
value = exprBuilder.Translate(strToInt.Argument);
} else {
strToInt = null;
value = exprBuilder.Translate(inst.Value);
}
// Pick the section with the most labels as default section.
IL.SwitchSection defaultSection = inst.Sections.First();
foreach (var section in inst.Sections) {
if (section.Labels.Count() > defaultSection.Labels.Count()) {
defaultSection = section;
}
}
IL.SwitchSection defaultSection = inst.GetDefaultSection();
var stmt = new SwitchStatement() { Expression = value };
Dictionary<IL.SwitchSection, Syntax.SwitchSection> translationDictionary = new Dictionary<IL.SwitchSection, Syntax.SwitchSection>();
@ -1002,34 +996,19 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1002,34 +996,19 @@ namespace ICSharpCode.Decompiler.CSharp
LocalFunctionDeclarationStatement TranslateFunction(ILFunction function)
{
var stmt = new LocalFunctionDeclarationStatement();
var nestedBuilder = new StatementBuilder(typeSystem, exprBuilder.decompilationContext, function, settings, cancellationToken);
stmt.Name = function.Name;
stmt.Parameters.AddRange(exprBuilder.MakeParameters(function.Parameters, function));
stmt.ReturnType = exprBuilder.ConvertType(function.Method.ReturnType);
stmt.Body = nestedBuilder.ConvertAsBlock(function.Body);
var astBuilder = exprBuilder.astBuilder;
var method = (MethodDeclaration)astBuilder.ConvertEntity(function.ReducedMethod);
method.Body = nestedBuilder.ConvertAsBlock(function.Body);
Comment prev = null;
foreach (string warning in function.Warnings) {
stmt.Body.InsertChildAfter(prev, prev = new Comment(warning), Roles.Comment);
method.Body.InsertChildAfter(prev, prev = new Comment(warning), Roles.Comment);
}
if (function.Method.TypeParameters.Count > 0) {
var astBuilder = exprBuilder.astBuilder;
if (astBuilder.ShowTypeParameters) {
int skipCount = function.ReducedMethod.NumberOfCompilerGeneratedTypeParameters;
stmt.TypeParameters.AddRange(function.Method.TypeParameters.Skip(skipCount).Select(t => astBuilder.ConvertTypeParameter(t)));
if (astBuilder.ShowTypeParameterConstraints) {
stmt.Constraints.AddRange(function.Method.TypeParameters.Skip(skipCount).Select(t => astBuilder.ConvertTypeParameterConstraint(t)).Where(c => c != null));
}
}
}
if (function.IsAsync) {
stmt.Modifiers |= Modifiers.Async;
}
if (settings.StaticLocalFunctions && function.ReducedMethod.IsStaticLocalFunction) {
stmt.Modifiers |= Modifiers.Static;
}
CSharpDecompiler.CleanUpMethodDeclaration(method, method.Body, function);
CSharpDecompiler.RemoveAttribute(method, KnownAttribute.CompilerGenerated);
var stmt = new LocalFunctionDeclarationStatement(method);
stmt.AddAnnotation(new MemberResolveResult(null, function.ReducedMethod));
stmt.WithILInstruction(function);
return stmt;

36
ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs

@ -345,7 +345,17 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -345,7 +345,17 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{
VisitChildren (caseLabel);
}
public virtual void VisitSwitchExpression(SwitchExpression switchExpression)
{
VisitChildren(switchExpression);
}
public virtual void VisitSwitchExpressionSection(SwitchExpressionSection switchExpressionSection)
{
VisitChildren(switchExpressionSection);
}
public virtual void VisitThrowStatement (ThrowStatement throwStatement)
{
VisitChildren (throwStatement);
@ -1002,7 +1012,17 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1002,7 +1012,17 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{
return VisitChildren (caseLabel);
}
public virtual T VisitSwitchExpression(SwitchExpression switchExpression)
{
return VisitChildren(switchExpression);
}
public virtual T VisitSwitchExpressionSection(SwitchExpressionSection switchExpressionSection)
{
return VisitChildren(switchExpressionSection);
}
public virtual T VisitThrowStatement (ThrowStatement throwStatement)
{
return VisitChildren (throwStatement);
@ -1659,7 +1679,17 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1659,7 +1679,17 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{
return VisitChildren (caseLabel, data);
}
public virtual S VisitSwitchExpression(SwitchExpression switchExpression, T data)
{
return VisitChildren(switchExpression, data);
}
public virtual S VisitSwitchExpressionSection(SwitchExpressionSection switchExpressionSection, T data)
{
return VisitChildren(switchExpressionSection, data);
}
public virtual S VisitThrowStatement (ThrowStatement throwStatement, T data)
{
return VisitChildren (throwStatement, data);

118
ICSharpCode.Decompiler/CSharp/Syntax/Expressions/SwitchExpression.cs

@ -0,0 +1,118 @@ @@ -0,0 +1,118 @@
// Copyright (c) 2020 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
namespace ICSharpCode.Decompiler.CSharp.Syntax
{
/// <summary>
/// Expression switch { SwitchSections }
/// </summary>
public class SwitchExpression : Expression
{
public static readonly TokenRole SwitchKeywordRole = new TokenRole("switch");
public static readonly Role<SwitchExpressionSection> SwitchSectionRole = new Role<SwitchExpressionSection>("SwitchSection");
public Expression Expression {
get { return GetChildByRole(Roles.Expression); }
set { SetChildByRole(Roles.Expression, value); }
}
public CSharpTokenNode SwitchToken {
get { return GetChildByRole(SwitchKeywordRole); }
}
public CSharpTokenNode LBraceToken {
get { return GetChildByRole(Roles.LBrace); }
}
public AstNodeCollection<SwitchExpressionSection> SwitchSections {
get { return GetChildrenByRole(SwitchSectionRole); }
}
public CSharpTokenNode RBraceToken {
get { return GetChildByRole(Roles.RBrace); }
}
public override void AcceptVisitor(IAstVisitor visitor)
{
visitor.VisitSwitchExpression(this);
}
public override T AcceptVisitor<T>(IAstVisitor<T> visitor)
{
return visitor.VisitSwitchExpression(this);
}
public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data)
{
return visitor.VisitSwitchExpression(this, data);
}
protected internal override bool DoMatch(AstNode other, PatternMatching.Match match)
{
SwitchExpression o = other as SwitchExpression;
return o != null && this.Expression.DoMatch(o.Expression, match) && this.SwitchSections.DoMatch(o.SwitchSections, match);
}
}
/// <summary>
/// Pattern => Expression
/// </summary>
public class SwitchExpressionSection : AstNode
{
public static readonly Role<Expression> PatternRole = new Role<Expression>("Pattern", Expression.Null);
public static readonly Role<Expression> BodyRole = new Role<Expression>("Body", Expression.Null);
public Expression Pattern {
get { return GetChildByRole(PatternRole); }
set { SetChildByRole(PatternRole, value); }
}
public CSharpTokenNode ArrowToken {
get { return GetChildByRole(Roles.Arrow); }
}
public Expression Body {
get { return GetChildByRole(BodyRole); }
set { SetChildByRole(BodyRole, value); }
}
public override NodeType NodeType => NodeType.Unknown;
public override void AcceptVisitor(IAstVisitor visitor)
{
visitor.VisitSwitchExpressionSection(this);
}
public override T AcceptVisitor<T>(IAstVisitor<T> visitor)
{
return visitor.VisitSwitchExpressionSection(this);
}
public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data)
{
return visitor.VisitSwitchExpressionSection(this, data);
}
protected internal override bool DoMatch(AstNode other, PatternMatching.Match match)
{
SwitchExpressionSection o = other as SwitchExpressionSection;
return o != null && this.Pattern.DoMatch(o.Pattern, match) && this.Body.DoMatch(o.Body, match);
}
}
}

6
ICSharpCode.Decompiler/CSharp/Syntax/IAstVisitor.cs

@ -104,6 +104,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -104,6 +104,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
void VisitSwitchStatement(SwitchStatement switchStatement);
void VisitSwitchSection(SwitchSection switchSection);
void VisitCaseLabel(CaseLabel caseLabel);
void VisitSwitchExpression(SwitchExpression switchExpression);
void VisitSwitchExpressionSection(SwitchExpressionSection switchExpressionSection);
void VisitThrowStatement(ThrowStatement throwStatement);
void VisitTryCatchStatement(TryCatchStatement tryCatchStatement);
void VisitCatchClause(CatchClause catchClause);
@ -248,6 +250,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -248,6 +250,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
S VisitSwitchStatement(SwitchStatement switchStatement);
S VisitSwitchSection(SwitchSection switchSection);
S VisitCaseLabel(CaseLabel caseLabel);
S VisitSwitchExpression(SwitchExpression switchExpression);
S VisitSwitchExpressionSection(SwitchExpressionSection switchExpressionSection);
S VisitThrowStatement(ThrowStatement throwStatement);
S VisitTryCatchStatement(TryCatchStatement tryCatchStatement);
S VisitCatchClause(CatchClause catchClause);
@ -392,6 +396,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -392,6 +396,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
S VisitSwitchStatement(SwitchStatement switchStatement, T data);
S VisitSwitchSection(SwitchSection switchSection, T data);
S VisitCaseLabel(CaseLabel caseLabel, T data);
S VisitSwitchExpression(SwitchExpression switchExpression, T data);
S VisitSwitchExpressionSection(SwitchExpressionSection switchExpressionSection, T data);
S VisitThrowStatement(ThrowStatement throwStatement, T data);
S VisitTryCatchStatement(TryCatchStatement tryCatchStatement, T data);
S VisitCatchClause(CatchClause catchClause, T data);

67
ICSharpCode.Decompiler/CSharp/Syntax/Statements/LocalFunctionDeclarationStatement.cs

@ -16,69 +16,22 @@ @@ -16,69 +16,22 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System.Collections.Generic;
using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching;
namespace ICSharpCode.Decompiler.CSharp.Syntax
{
public class LocalFunctionDeclarationStatement : Statement
{
public AstNodeCollection<TypeParameterDeclaration> TypeParameters {
get { return GetChildrenByRole(Roles.TypeParameter); }
}
public CSharpTokenNode LParToken {
get { return GetChildByRole(Roles.LPar); }
}
public AstNodeCollection<ParameterDeclaration> Parameters {
get { return GetChildrenByRole(Roles.Parameter); }
}
public static readonly Role<MethodDeclaration> MethodDeclarationRole = new Role<MethodDeclaration>("Method");
public CSharpTokenNode RParToken {
get { return GetChildByRole(Roles.RPar); }
public MethodDeclaration Declaration {
get { return GetChildByRole(MethodDeclarationRole); }
set { SetChildByRole(MethodDeclarationRole, value); }
}
public AstNodeCollection<Constraint> Constraints {
get { return GetChildrenByRole(Roles.Constraint); }
}
public BlockStatement Body {
get { return GetChildByRole(Roles.Body); }
set { SetChildByRole(Roles.Body, value); }
}
public Modifiers Modifiers {
get { return EntityDeclaration.GetModifiers(this); }
set { EntityDeclaration.SetModifiers(this, value); }
}
public bool HasModifier(Modifiers mod)
public LocalFunctionDeclarationStatement(MethodDeclaration methodDeclaration)
{
return (Modifiers & mod) == mod;
}
public IEnumerable<CSharpModifierToken> ModifierTokens {
get { return GetChildrenByRole(EntityDeclaration.ModifierRole); }
}
public virtual string Name {
get {
return GetChildByRole(Roles.Identifier).Name;
}
set {
SetChildByRole(Roles.Identifier, Identifier.Create(value, TextLocation.Empty));
}
}
public virtual Identifier NameToken {
get { return GetChildByRole(Roles.Identifier); }
set { SetChildByRole(Roles.Identifier, value); }
}
public virtual AstType ReturnType {
get { return GetChildByRole(Roles.Type); }
set { SetChildByRole(Roles.Type, value); }
AddChild(methodDeclaration, MethodDeclarationRole);
}
public override void AcceptVisitor(IAstVisitor visitor)
@ -98,13 +51,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -98,13 +51,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
protected internal override bool DoMatch(AstNode other, Match match)
{
LocalFunctionDeclarationStatement o = other as LocalFunctionDeclarationStatement;
return o != null && MatchString(this.Name, o.Name)
&& (this.Modifiers == Modifiers.Any || this.Modifiers == o.Modifiers)
&& this.ReturnType.DoMatch(o.ReturnType, match)
&& this.TypeParameters.DoMatch(o.TypeParameters, match)
&& this.Parameters.DoMatch(o.Parameters, match) && this.Constraints.DoMatch(o.Constraints, match)
&& this.Body.DoMatch(o.Body, match);
return other is LocalFunctionDeclarationStatement o && Declaration.DoMatch(o.Declaration, match);
}
}
}

5
ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/Accessor.cs

@ -72,13 +72,14 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -72,13 +72,14 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
}
/// <summary>
/// Gets the 'get'/'set'/'add'/'remove' keyword
/// Gets the 'get'/'set'/'init'/'add'/'remove' keyword
/// </summary>
public CSharpTokenNode Keyword {
get {
for (AstNode child = this.FirstChild; child != null; child = child.NextSibling) {
if (child.Role == PropertyDeclaration.GetKeywordRole || child.Role == PropertyDeclaration.SetKeywordRole
|| child.Role == CustomEventDeclaration.AddKeywordRole || child.Role == CustomEventDeclaration.RemoveKeywordRole)
|| child.Role == PropertyDeclaration.InitKeywordRole
|| child.Role == CustomEventDeclaration.AddKeywordRole || child.Role == CustomEventDeclaration.RemoveKeywordRole)
{
return (CSharpTokenNode)child;
}

1
ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/PropertyDeclaration.cs

@ -32,6 +32,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -32,6 +32,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{
public static readonly TokenRole GetKeywordRole = new TokenRole ("get");
public static readonly TokenRole SetKeywordRole = new TokenRole ("set");
public static readonly TokenRole InitKeywordRole = new TokenRole ("init");
public static readonly Role<Accessor> GetterRole = new Role<Accessor>("Getter", Accessor.Null);
public static readonly Role<Accessor> SetterRole = new Role<Accessor>("Setter", Accessor.Null);
public static readonly Role<Expression> ExpressionBodyRole = new Role<Expression>("ExpressionBody", Expression.Null);

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

@ -21,6 +21,7 @@ using System.Collections.Generic; @@ -21,6 +21,7 @@ using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using ICSharpCode.Decompiler.CSharp.Resolver;
using ICSharpCode.Decompiler.CSharp.TypeSystem;
using ICSharpCode.Decompiler.Semantics;
@ -205,6 +206,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -205,6 +206,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
/// The default value is <see langword="false" />.
/// </summary>
public bool PrintIntegralValuesAsHex { get; set; }
/// <summary>
/// Controls whether C# 9 "init;" accessors are supported.
/// If disabled, emits "set /*init*/;" instead.
/// </summary>
public bool SupportInitAccessors { get; set; }
#endregion
#region Convert Type
@ -1363,7 +1370,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1363,7 +1370,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return ConvertDestructor((IMethod)entity);
case SymbolKind.Accessor:
IMethod accessor = (IMethod)entity;
return ConvertAccessor(accessor, accessor.AccessorOwner != null ? accessor.AccessorOwner.Accessibility : Accessibility.None, false);
Accessibility ownerAccessibility = accessor.AccessorOwner?.Accessibility ?? Accessibility.None;
return ConvertAccessor(accessor, accessor.AccessorKind, ownerAccessibility, false);
default:
throw new ArgumentException("Invalid value for SymbolKind: " + entity.SymbolKind);
}
@ -1554,15 +1562,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1554,15 +1562,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
}
}
Accessor ConvertAccessor(IMethod accessor, Accessibility ownerAccessibility, bool addParameterAttribute)
Accessor ConvertAccessor(IMethod accessor, MethodSemanticsAttributes kind, Accessibility ownerAccessibility, bool addParameterAttribute)
{
if (accessor == null)
return Accessor.Null;
Accessor decl = new Accessor();
if (this.ShowAccessibility && accessor.Accessibility != ownerAccessibility)
decl.Modifiers = ModifierFromAccessibility(accessor.Accessibility);
if (accessor.HasReadonlyModifier())
decl.Modifiers |= Modifiers.Readonly;
if (ShowAttributes) {
decl.Attributes.AddRange(ConvertAttributes(accessor.GetAttributes()));
decl.Attributes.AddRange(ConvertAttributes(accessor.GetReturnTypeAttributes(), "return"));
@ -1570,10 +1574,35 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1570,10 +1574,35 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
decl.Attributes.AddRange(ConvertAttributes(accessor.Parameters.Last().GetAttributes(), "param"));
}
}
if (this.ShowAccessibility && accessor.Accessibility != ownerAccessibility)
decl.Modifiers = ModifierFromAccessibility(accessor.Accessibility);
if (accessor.HasReadonlyModifier())
decl.Modifiers |= Modifiers.Readonly;
TokenRole keywordRole = kind switch
{
MethodSemanticsAttributes.Getter => PropertyDeclaration.GetKeywordRole,
MethodSemanticsAttributes.Setter => PropertyDeclaration.SetKeywordRole,
MethodSemanticsAttributes.Adder => CustomEventDeclaration.AddKeywordRole,
MethodSemanticsAttributes.Remover => CustomEventDeclaration.RemoveKeywordRole,
_ => null
};
if (kind == MethodSemanticsAttributes.Setter && SupportInitAccessors && accessor.IsInitOnly) {
keywordRole = PropertyDeclaration.InitKeywordRole;
}
if (keywordRole != null) {
decl.AddChild(new CSharpTokenNode(TextLocation.Empty, keywordRole), keywordRole);
}
if (accessor.IsInitOnly && keywordRole != PropertyDeclaration.InitKeywordRole) {
decl.AddChild(new Comment("init", CommentType.MultiLine), Roles.Comment);
}
if (AddResolveResultAnnotations) {
decl.AddAnnotation(new MemberResolveResult(null, accessor));
}
decl.Body = GenerateBodyBlock();
if (GenerateBody) {
decl.Body = GenerateBodyBlock();
} else {
decl.AddChild(new CSharpTokenNode(TextLocation.Empty, Roles.Semicolon), Roles.Semicolon);
}
return decl;
}
@ -1592,8 +1621,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1592,8 +1621,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
ct.HasReadOnlySpecifier = true;
}
decl.Name = property.Name;
decl.Getter = ConvertAccessor(property.Getter, property.Accessibility, false);
decl.Setter = ConvertAccessor(property.Setter, property.Accessibility, true);
decl.Getter = ConvertAccessor(property.Getter, MethodSemanticsAttributes.Getter, property.Accessibility, false);
decl.Setter = ConvertAccessor(property.Setter, MethodSemanticsAttributes.Setter, property.Accessibility, true);
decl.PrivateImplementationType = GetExplicitInterfaceType (property);
MergeReadOnlyModifiers(decl, decl.Getter, decl.Setter);
return decl;
@ -1624,8 +1653,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1624,8 +1653,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
foreach (IParameter p in indexer.Parameters) {
decl.Parameters.Add(ConvertParameter(p));
}
decl.Getter = ConvertAccessor(indexer.Getter, indexer.Accessibility, false);
decl.Setter = ConvertAccessor(indexer.Setter, indexer.Accessibility, true);
decl.Getter = ConvertAccessor(indexer.Getter, MethodSemanticsAttributes.Getter, indexer.Accessibility, false);
decl.Setter = ConvertAccessor(indexer.Setter, MethodSemanticsAttributes.Setter, indexer.Accessibility, true);
decl.PrivateImplementationType = GetExplicitInterfaceType (indexer);
MergeReadOnlyModifiers(decl, decl.Getter, decl.Setter);
return decl;
@ -1644,8 +1673,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1644,8 +1673,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
}
decl.ReturnType = ConvertType(ev.ReturnType);
decl.Name = ev.Name;
decl.AddAccessor = ConvertAccessor(ev.AddAccessor, ev.Accessibility, true);
decl.RemoveAccessor = ConvertAccessor(ev.RemoveAccessor, ev.Accessibility, true);
decl.AddAccessor = ConvertAccessor(ev.AddAccessor, MethodSemanticsAttributes.Adder, ev.Accessibility, true);
decl.RemoveAccessor = ConvertAccessor(ev.RemoveAccessor, MethodSemanticsAttributes.Remover, ev.Accessibility, true);
decl.PrivateImplementationType = GetExplicitInterfaceType (ev);
MergeReadOnlyModifiers(decl, decl.AddAccessor, decl.RemoveAccessor);
return decl;
@ -1799,6 +1828,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1799,6 +1828,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return !member.IsStatic;
case SymbolKind.Destructor:
return false;
case SymbolKind.Method:
return !((IMethod)member).IsLocalFunction;
default:
return true;
}
@ -1811,7 +1842,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1811,7 +1842,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
m |= ModifierFromAccessibility (member.Accessibility);
}
if (this.ShowModifiers) {
if (member.IsStatic) {
if (member is LocalFunctionMethod localFunction) {
if (localFunction.IsStaticLocalFunction) {
m |= Modifiers.Static;
}
} else if (member.IsStatic) {
m |= Modifiers.Static;
} else {
var declaringType = member.DeclaringType;

20
ICSharpCode.Decompiler/CSharp/Transforms/ContextTrackingVisitor.cs

@ -55,56 +55,56 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -55,56 +55,56 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
public override TResult VisitMethodDeclaration(MethodDeclaration methodDeclaration)
{
Debug.Assert(currentMethod == null);
var oldMethod = currentMethod;
try {
currentMethod = methodDeclaration.GetSymbol() as IMethod;
return base.VisitMethodDeclaration(methodDeclaration);
} finally {
currentMethod = null;
currentMethod = oldMethod;
}
}
public override TResult VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration)
{
Debug.Assert(currentMethod == null);
var oldMethod = currentMethod;
try {
currentMethod = constructorDeclaration.GetSymbol() as IMethod;
return base.VisitConstructorDeclaration(constructorDeclaration);
} finally {
currentMethod = null;
currentMethod = oldMethod;
}
}
public override TResult VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration)
{
Debug.Assert(currentMethod == null);
var oldMethod = currentMethod;
try {
currentMethod = destructorDeclaration.GetSymbol() as IMethod;
return base.VisitDestructorDeclaration(destructorDeclaration);
} finally {
currentMethod = null;
currentMethod = oldMethod;
}
}
public override TResult VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration)
{
Debug.Assert(currentMethod == null);
var oldMethod = currentMethod;
try {
currentMethod = operatorDeclaration.GetSymbol() as IMethod;
return base.VisitOperatorDeclaration(operatorDeclaration);
} finally {
currentMethod = null;
currentMethod = oldMethod;
}
}
public override TResult VisitAccessor(Accessor accessor)
{
Debug.Assert(currentMethod == null);
var oldMethod = currentMethod;
try {
currentMethod = accessor.GetSymbol() as IMethod;
return base.VisitAccessor(accessor);
} finally {
currentMethod = null;
currentMethod = oldMethod;
}
}
}

5
ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs

@ -308,7 +308,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -308,7 +308,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
internal static bool VariableNeedsDeclaration(VariableKind kind)
{
switch (kind) {
case VariableKind.PinnedLocal:
case VariableKind.PinnedRegionLocal:
case VariableKind.Parameter:
case VariableKind.ExceptionLocal:
case VariableKind.ExceptionStackSlot:
@ -468,6 +468,9 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -468,6 +468,9 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
if (v.ILVariable.IsRefReadOnly && type is ComposedType composedType && composedType.HasRefSpecifier) {
composedType.HasReadOnlySpecifier = true;
}
if (v.ILVariable.Kind == VariableKind.PinnedLocal) {
type.InsertChildAfter(null, new Comment("pinned", CommentType.MultiLine), Roles.Comment);
}
var vds = new VariableDeclarationStatement(type, v.Name, assignment.Right.Detach());
var init = vds.Variables.Single();
init.AddAnnotation(assignment.Left.GetResolveResult());

54
ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs

@ -755,6 +755,37 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -755,6 +755,37 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
}
};
static readonly Accessor automaticEventPatternV4AggressivelyInlined = new Accessor {
Attributes = { new Repeat(new AnyNode()) },
Body = new BlockStatement {
new AssignmentExpression {
Left = new NamedNode("var1", new IdentifierExpression(Pattern.AnyString)),
Operator = AssignmentOperatorType.Assign,
Right = new NamedNode("field", fieldReferencePattern)
},
new DoWhileStatement {
EmbeddedStatement = new BlockStatement {
new AssignmentExpression(new NamedNode("var2", new IdentifierExpression(Pattern.AnyString)), new IdentifierExpressionBackreference("var1")),
new AssignmentExpression {
Left = new IdentifierExpressionBackreference("var1"),
Right = new InvocationExpression(new MemberReferenceExpression(new TypeReferenceExpression(new TypePattern(typeof(System.Threading.Interlocked)).ToType()),
"CompareExchange"),
new Expression[] { // arguments
new NamedArgumentExpression("value", new CastExpression(new AnyNode("type"), new InvocationExpression(new AnyNode("delegateCombine").ToExpression(), new IdentifierExpressionBackreference("var2"), new IdentifierExpression("value")))),
new NamedArgumentExpression("location1", new DirectionExpression { FieldDirection = FieldDirection.Ref, Expression = new Backreference("field") }),
new NamedArgumentExpression("comparand", new IdentifierExpressionBackreference("var2"))
}
)}
},
Condition = new BinaryOperatorExpression {
Left = new CastExpression(new TypePattern(typeof(object)), new IdentifierExpressionBackreference("var1")),
Operator = BinaryOperatorType.InEquality,
Right = new IdentifierExpressionBackreference("var2")
},
}
}
};
static readonly Accessor automaticEventPatternV4MCS = new Accessor {
Attributes = { new Repeat(new AnyNode()) },
Body = new BlockStatement {
@ -837,10 +868,23 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -837,10 +868,23 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
bool CheckAutomaticEventV4(CustomEventDeclaration ev)
{
Match addMatch = automaticEventPatternV4.Match(ev.AddAccessor);
if (!CheckAutomaticEventMatch(addMatch, ev, true))
if (!CheckAutomaticEventMatch(addMatch, ev, isAddAccessor: true))
return false;
Match removeMatch = automaticEventPatternV4.Match(ev.RemoveAccessor);
if (!CheckAutomaticEventMatch(removeMatch, ev, false))
if (!CheckAutomaticEventMatch(removeMatch, ev, isAddAccessor: false))
return false;
return true;
}
bool CheckAutomaticEventV4AggressivelyInlined(CustomEventDeclaration ev)
{
if (!context.Settings.AggressiveInlining)
return false;
Match addMatch = automaticEventPatternV4AggressivelyInlined.Match(ev.AddAccessor);
if (!CheckAutomaticEventMatch(addMatch, ev, isAddAccessor: true))
return false;
Match removeMatch = automaticEventPatternV4AggressivelyInlined.Match(ev.RemoveAccessor);
if (!CheckAutomaticEventMatch(removeMatch, ev, isAddAccessor: false))
return false;
return true;
}
@ -848,10 +892,10 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -848,10 +892,10 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
bool CheckAutomaticEventV2(CustomEventDeclaration ev)
{
Match addMatch = automaticEventPatternV2.Match(ev.AddAccessor);
if (!CheckAutomaticEventMatch(addMatch, ev, true))
if (!CheckAutomaticEventMatch(addMatch, ev, isAddAccessor: true))
return false;
Match removeMatch = automaticEventPatternV2.Match(ev.RemoveAccessor);
if (!CheckAutomaticEventMatch(removeMatch, ev, false))
if (!CheckAutomaticEventMatch(removeMatch, ev, isAddAccessor: false))
return false;
return true;
}
@ -872,7 +916,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -872,7 +916,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
if (!ev.PrivateImplementationType.IsNull)
return null;
if (!ev.Modifiers.HasFlag(Modifiers.Abstract)) {
if (!CheckAutomaticEventV4(ev) && !CheckAutomaticEventV2(ev) && !CheckAutomaticEventV4MCS(ev))
if (!CheckAutomaticEventV4AggressivelyInlined(ev) && !CheckAutomaticEventV4(ev) && !CheckAutomaticEventV2(ev) && !CheckAutomaticEventV4MCS(ev))
return null;
}
RemoveCompilerGeneratedAttribute(ev.AddAccessor.Attributes, attributeTypesToRemoveFromAutoEvents);

59
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -114,17 +114,19 @@ namespace ICSharpCode.Decompiler @@ -114,17 +114,19 @@ namespace ICSharpCode.Decompiler
asyncEnumerator = false;
staticLocalFunctions = false;
ranges = false;
switchExpressions = false;
}
if (languageVersion < CSharp.LanguageVersion.Preview) {
nativeIntegers = false;
initAccessors = false;
}
}
public CSharp.LanguageVersion GetMinimumRequiredVersion()
{
if (nativeIntegers)
if (nativeIntegers || initAccessors)
return CSharp.LanguageVersion.Preview;
if (nullableReferenceTypes || readOnlyMethods || asyncEnumerator || asyncUsingAndForEachStatement || staticLocalFunctions || ranges)
if (nullableReferenceTypes || readOnlyMethods || asyncEnumerator || asyncUsingAndForEachStatement || staticLocalFunctions || ranges || switchExpressions)
return CSharp.LanguageVersion.CSharp8_0;
if (introduceUnmanagedConstraint || tupleComparisons || stackAllocInitializers || patternBasedFixedStatement)
return CSharp.LanguageVersion.CSharp7_3;
@ -164,6 +166,40 @@ namespace ICSharpCode.Decompiler @@ -164,6 +166,40 @@ namespace ICSharpCode.Decompiler
}
}
bool initAccessors = true;
/// <summary>
/// Use C# 9 <c>init;</c> property accessors.
/// </summary>
[Category("C# 9.0 (experimental)")]
[Description("DecompilerSettings.InitAccessors")]
public bool InitAccessors {
get { return initAccessors; }
set {
if (initAccessors != value) {
initAccessors = value;
OnPropertyChanged();
}
}
}
bool switchExpressions = true;
/// <summary>
/// Use C# 8 switch expressions.
/// </summary>
[Category("C# 8.0 / VS 2019")]
[Description("DecompilerSettings.SwitchExpressions")]
public bool SwitchExpressions {
get { return switchExpressions; }
set {
if (switchExpressions != value) {
switchExpressions = value;
OnPropertyChanged();
}
}
}
bool anonymousMethods = true;
/// <summary>
@ -1405,6 +1441,25 @@ namespace ICSharpCode.Decompiler @@ -1405,6 +1441,25 @@ namespace ICSharpCode.Decompiler
}
}
bool aggressiveInlining = false;
/// <summary>
/// If set to false (the default), the decompiler will inline local variables only when they occur
/// in a context where the C# compiler is known to emit compiler-generated locals.
/// If set to true, the decompiler will inline local variables whenever possible.
/// </summary>
[Category("DecompilerSettings.Other")]
[Description("DecompilerSettings.AggressiveInlining")]
public bool AggressiveInlining {
get { return aggressiveInlining; }
set {
if (aggressiveInlining != value) {
aggressiveInlining = value;
OnPropertyChanged();
}
}
}
CSharpFormattingOptions csharpFormattingOptions;
[Browsable(false)]

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -71,6 +71,7 @@ @@ -71,6 +71,7 @@
<Compile Include="CSharp\ProjectDecompiler\TargetFramework.cs" />
<Compile Include="CSharp\ProjectDecompiler\TargetServices.cs" />
<Compile Include="CSharp\Syntax\Expressions\DeclarationExpression.cs" />
<Compile Include="CSharp\Syntax\Expressions\SwitchExpression.cs" />
<Compile Include="CSharp\Syntax\FunctionPointerType.cs" />
<Compile Include="CSharp\Syntax\VariableDesignation.cs" />
<Compile Include="IL\Transforms\DeconstructionTransform.cs" />

33
ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs

@ -20,6 +20,7 @@ using ICSharpCode.Decompiler.CSharp; @@ -20,6 +20,7 @@ using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.DebugInfo;
using ICSharpCode.Decompiler.IL.Transforms;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
using ICSharpCode.Decompiler.Util;
using System;
using System.Collections.Generic;
@ -33,9 +34,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -33,9 +34,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// <summary>
/// Decompiler step for C# 5 async/await.
/// </summary>
class AsyncAwaitDecompiler : IILTransform
public class AsyncAwaitDecompiler : IILTransform
{
public static bool IsCompilerGeneratedStateMachine(TypeDefinitionHandle type, MetadataReader metadata)
internal static bool IsCompilerGeneratedStateMachine(TypeDefinitionHandle type, MetadataReader metadata)
{
TypeDefinition td;
if (type.IsNil || (td = metadata.GetTypeDefinition(type)).GetDeclaringType().IsNil)
@ -50,7 +51,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -50,7 +51,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
return false;
}
public static bool IsCompilerGeneratedMainMethod(Metadata.PEFile module, MethodDefinitionHandle method)
internal static bool IsCompilerGeneratedMainMethod(Metadata.PEFile module, MethodDefinitionHandle method)
{
var metadata = module.Metadata;
var definition = metadata.GetMethodDefinition(method);
@ -74,7 +75,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -74,7 +75,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
IType underlyingReturnType; // return type of the method (only the "T" for Task{T}), for async enumerators this is the type being yielded
AsyncMethodType methodType;
ITypeDefinition stateMachineType;
ITypeDefinition builderType;
IType builderType;
IField builderField;
IField stateField;
int initialState;
@ -218,24 +219,32 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -218,24 +219,32 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
if (startCall.Method.Name != "Start")
return false;
taskType = function.Method.ReturnType;
builderType = startCall.Method.DeclaringTypeDefinition;
builderType = startCall.Method.DeclaringType;
FullTypeName builderTypeName;
if (builderType?.GetDefinition() is { } builderTypeDef) {
builderTypeName = builderTypeDef.FullTypeName;
} else if (builderType is UnknownType unknownBuilderType) {
builderTypeName = unknownBuilderType.FullTypeName;
} else {
return false;
}
if (taskType.IsKnownType(KnownTypeCode.Void)) {
methodType = AsyncMethodType.Void;
underlyingReturnType = taskType;
if (builderType?.FullTypeName != new TopLevelTypeName("System.Runtime.CompilerServices", "AsyncVoidMethodBuilder"))
if (builderTypeName != new TopLevelTypeName("System.Runtime.CompilerServices", "AsyncVoidMethodBuilder"))
return false;
} else if (TaskType.IsNonGenericTaskType(taskType, out var builderTypeName)) {
} else if (TaskType.IsNonGenericTaskType(taskType, out var builderTypeNameFromTask)) {
methodType = AsyncMethodType.Task;
underlyingReturnType = context.TypeSystem.FindType(KnownTypeCode.Void);
if (builderType?.FullTypeName != builderTypeName)
if (builderTypeNameFromTask != builderTypeName)
return false;
} else if (TaskType.IsGenericTaskType(taskType, out builderTypeName)) {
} else if (TaskType.IsGenericTaskType(taskType, out builderTypeNameFromTask)) {
methodType = AsyncMethodType.TaskOfT;
if (taskType.IsKnownType(KnownTypeCode.TaskOfT))
underlyingReturnType = TaskType.UnpackTask(context.TypeSystem, taskType);
else
underlyingReturnType = startCall.Method.DeclaringType.TypeArguments[0];
if (builderType?.FullTypeName != builderTypeName)
if (builderTypeNameFromTask != builderTypeName)
return false;
} else {
return false;
@ -484,7 +493,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -484,7 +493,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
return false;
}
static void AnalyzeEnumeratorCtor(IMethod ctor, ILTransformContext context, out IField builderField, out ITypeDefinition builderType, out IField stateField)
static void AnalyzeEnumeratorCtor(IMethod ctor, ILTransformContext context, out IField builderField, out IType builderType, out IField stateField)
{
builderField = null;
stateField = null;
@ -515,7 +524,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -515,7 +524,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
if (stateField == null || builderField == null)
throw new SymbolicAnalysisFailedException();
builderType = builderField.Type.GetDefinition();
builderType = builderField.Type;
if (builderType == null)
throw new SymbolicAnalysisFailedException();
}

76
ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs

@ -83,7 +83,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -83,7 +83,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
this.context = null;
}
/// <summary>
/// Ensures that every write to a pinned local is followed by a branch instruction.
/// This ensures the 'pinning region' does not involve any half blocks, which makes it easier to extract.
@ -94,7 +94,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -94,7 +94,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
var block = container.Blocks[i];
for (int j = 0; j < block.Instructions.Count - 1; j++) {
var inst = block.Instructions[j];
if (inst.MatchStLoc(out ILVariable v) && v.Kind == VariableKind.PinnedLocal) {
if (inst.MatchStLoc(out ILVariable v, out var value) && v.Kind == VariableKind.PinnedLocal) {
if (block.Instructions[j + 1].OpCode != OpCode.Branch) {
// split block after j:
context.Step("Split block after pinned local write", inst);
@ -107,6 +107,19 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -107,6 +107,19 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
block.Instructions.Add(new Branch(newBlock));
container.Blocks.Insert(i + 1, newBlock);
}
// in case of re-pinning (e.g. C++/CLI assignment to pin_ptr variable),
// it's possible for the new value to be dependent on the old.
if (v.IsUsedWithin(value)) {
// In this case, we need to un-inline the uses of the pinned local
// so that they are split off into the block prior to the pinned local write
var temp = context.Function.RegisterVariable(VariableKind.StackSlot, v.Type);
block.Instructions.Insert(j++, new StLoc(temp, new LdLoc(v)));
foreach (var descendant in value.Descendants) {
if (descendant.MatchLdLoc(v)) {
descendant.ReplaceWith(new LdLoc(temp).WithILRange(descendant));
}
}
}
if (j > 0) {
// split block before j:
context.Step("Split block before pinned local write", inst);
@ -349,7 +362,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -349,7 +362,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
return false;
return block.Instructions[1].MatchBranch(nullOrEmptyBlock);
}
bool IsNullSafeArrayToPointerNotNullAndNotEmptyBlock(Block block, ILVariable v, ILVariable p, Block targetBlock)
{
// Block B_not_null_and_not_empty {
@ -380,7 +393,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -380,7 +393,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
return false;
return block.Instructions[1].MatchBranch(targetBlock);
}
bool IsNullSafeArrayToPointerNullOrEmptyBlock(Block block, out ILVariable p, out Block targetBlock)
{
p = null;
@ -490,10 +503,23 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -490,10 +503,23 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
body.Blocks.Add(innerBlock); // move block into body
if (!cloneBlocks) {
sourceContainer.Blocks[i] = new Block(); // replace with dummy block
// we'll delete the dummy block later
// we'll delete the dummy block later
}
}
}
if (body.Blocks.Count == 0) {
// empty body, the entryBlock itself doesn't belong into the pinned region
Debug.Assert(reachedEdgesPerBlock[entryBlock.ChildIndex] == 0);
var bodyBlock = new Block();
bodyBlock.SetILRange(stLoc);
bodyBlock.Instructions.Add(new Branch(entryBlock));
body.Blocks.Add(bodyBlock);
}
var pinnedRegion = new PinnedRegion(stLoc.Variable, stLoc.Value, body).WithILRange(stLoc);
stLoc.ReplaceWith(pinnedRegion);
block.Instructions.RemoveAt(block.Instructions.Count - 1); // remove branch into body
if (cloneBlocks) {
// Adjust branches between cloned blocks.
foreach (var branch in body.Descendants.OfType<Branch>()) {
@ -506,27 +532,26 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -506,27 +532,26 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
// Replace unreachable blocks in sourceContainer with dummy blocks:
bool[] isAlive = new bool[sourceContainer.Blocks.Count];
List<int> duplicatedBlockStartOffsets = new List<int>();
foreach (var remainingBlock in sourceContainer.TopologicalSort(deleteUnreachableBlocks: true)) {
isAlive[remainingBlock.ChildIndex] = true;
if (clonedBlocks[remainingBlock.ChildIndex] != null) {
duplicatedBlockStartOffsets.Add(remainingBlock.StartILOffset);
}
}
for (int i = 0; i < isAlive.Length; i++) {
if (!isAlive[i])
sourceContainer.Blocks[i] = new Block();
}
// we'll delete the dummy blocks later
}
if (body.Blocks.Count == 0) {
// empty body, the entryBlock itself doesn't belong into the pinned region
Debug.Assert(reachedEdgesPerBlock[entryBlock.ChildIndex] == 0);
var bodyBlock = new Block();
bodyBlock.SetILRange(stLoc);
bodyBlock.Instructions.Add(new Branch(entryBlock));
body.Blocks.Add(bodyBlock);
Debug.Assert(duplicatedBlockStartOffsets.Count > 0);
duplicatedBlockStartOffsets.Sort();
context.Function.Warnings.Add("The blocks "
+ string.Join(", ", duplicatedBlockStartOffsets.Select(o => $"IL_{o:x4}"))
+ $" are reachable both inside and outside the pinned region starting at IL_{stLoc.StartILOffset:x4}."
+ " ILSpy has duplicated these blocks in order to place them both within and outside the `fixed` statement.");
}
var pinnedRegion = new PinnedRegion(stLoc.Variable, stLoc.Value, body).WithILRange(stLoc);
stLoc.ReplaceWith(pinnedRegion);
block.Instructions.RemoveAt(block.Instructions.Count - 1); // remove branch into body
ProcessPinnedRegion(pinnedRegion);
return true;
}
@ -539,7 +564,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -539,7 +564,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
return inst.MatchLdcI4(0) || inst.MatchLdNull();
}
#endregion
#region ProcessPinnedRegion
/// <summary>
/// After a pinned region was detected; process its body; replacing the pin variable
@ -553,15 +578,14 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -553,15 +578,14 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
ILVariable oldVar = pinnedRegion.Variable;
IType elementType = ((ByReferenceType)oldVar.Type).ElementType;
if (elementType.Kind == TypeKind.Pointer && pinnedRegion.Init.MatchLdFlda(out _, out var field)
&& ((PointerType)elementType).ElementType.Equals(field.Type))
{
&& ((PointerType)elementType).ElementType.Equals(field.Type)) {
// Roslyn 2.6 (C# 7.2) uses type "int*&" for the pinned local referring to a
// fixed field of type "int".
// Remove the extra level of indirection.
elementType = ((PointerType)elementType).ElementType;
}
ILVariable newVar = new ILVariable(
VariableKind.PinnedLocal,
VariableKind.PinnedRegionLocal,
new PointerType(elementType),
oldVar.Index);
newVar.Name = oldVar.Name;
@ -583,6 +607,10 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -583,6 +607,10 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
DetectPinnedRegion(block);
body.Blocks.RemoveAll(b => b.Instructions.Count == 0); // remove dummy blocks
body.SetILRange(body.EntryPoint);
if (pinnedRegion.Variable.Kind != VariableKind.PinnedRegionLocal) {
Debug.Assert(pinnedRegion.Variable.Kind == VariableKind.PinnedLocal);
pinnedRegion.Variable.Kind = VariableKind.PinnedRegionLocal;
}
}
private void MoveArrayToPointerToPinnedRegionInit(PinnedRegion pinnedRegion)
@ -610,7 +638,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -610,7 +638,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
Debug.Assert(arrayToPointer.IsDescendantOf(pinnedRegion));
ILVariable oldVar = pinnedRegion.Variable;
ILVariable newVar = new ILVariable(
VariableKind.PinnedLocal,
VariableKind.PinnedRegionLocal,
new PointerType(((ArrayType)oldVar.Type).ElementType),
oldVar.Index);
newVar.Name = oldVar.Name;
@ -680,7 +708,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -680,7 +708,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// potentially a special case with legacy csc and an unused pinned variable:
if (pinnedRegion.Variable.AddressCount == 0 && pinnedRegion.Variable.LoadCount == 0) {
var charPtr = new PointerType(context.TypeSystem.FindType(KnownTypeCode.Char));
newVar = new ILVariable(VariableKind.PinnedLocal, charPtr, pinnedRegion.Variable.Index);
newVar = new ILVariable(VariableKind.PinnedRegionLocal, charPtr, pinnedRegion.Variable.Index);
newVar.Name = pinnedRegion.Variable.Name;
newVar.HasGeneratedName = pinnedRegion.Variable.HasGeneratedName;
pinnedRegion.Variable.Function.Variables.Add(newVar);
@ -732,7 +760,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -732,7 +760,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
nativeVar = otherVar;
}
if (nativeVar.Kind == VariableKind.Local) {
newVar = new ILVariable(VariableKind.PinnedLocal, nativeVar.Type, nativeVar.Index);
newVar = new ILVariable(VariableKind.PinnedRegionLocal, nativeVar.Type, nativeVar.Index);
newVar.Name = nativeVar.Name;
newVar.HasGeneratedName = nativeVar.HasGeneratedName;
nativeVar.Function.Variables.Add(newVar);
@ -801,7 +829,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -801,7 +829,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
if (v.Kind != VariableKind.Local)
return;
// replace V_1 with V_0
v.Kind = VariableKind.PinnedLocal;
v.Kind = VariableKind.PinnedRegionLocal;
pinnedRegion.Variable = v;
body.EntryPoint.Instructions.RemoveAt(0);
}

27
ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs

@ -200,11 +200,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -200,11 +200,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// * use only a single exit point if at all possible
/// * minimize the amount of code in the in-loop partition
/// (thus: maximize the amount of code in the out-of-loop partition)
/// "amount of code" could be measured as:
/// * number of basic blocks
/// * number of instructions directly in those basic blocks (~= number of statements)
/// * number of instructions in those basic blocks (~= number of expressions)
/// (we currently use the number of statements)
///
/// Observations:
/// * If a node is in-loop, so are all its ancestors in the dominator tree (up to the loop entry point)
@ -315,6 +310,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -315,6 +310,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
ControlFlowNode exitPoint = null;
int exitPointILOffset = -1;
ConsiderReturnAsExitPoint((Block)loopHead.UserData, ref exitPoint, ref exitPointILOffset);
foreach (var node in loopHead.DominatorTreeChildren) {
PickExitPoint(node, ref exitPoint, ref exitPointILOffset);
}
@ -457,12 +453,31 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -457,12 +453,31 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
exitPoint = node;
exitPointILOffset = block.StartILOffset;
return; // don't visit children, they are likely to have even later IL offsets and we'd end up
// moving almost all of the code into the loop.
// moving almost all of the code into the loop.
}
ConsiderReturnAsExitPoint(block, ref exitPoint, ref exitPointILOffset);
foreach (var child in node.DominatorTreeChildren) {
PickExitPoint(child, ref exitPoint, ref exitPointILOffset);
}
}
private static void ConsiderReturnAsExitPoint(Block block, ref ControlFlowNode exitPoint, ref int exitPointILOffset)
{
// It's possible that the real exit point of the loop is a "return;" that has been combined (by ControlFlowSimplification)
// with the condition block.
if (!block.MatchIfAtEndOfBlock(out _, out var trueInst, out var falseInst))
return;
if (trueInst.StartILOffset > exitPointILOffset && trueInst is Leave { IsLeavingFunction: true, Value: Nop _ }) {
// By using NoExitPoint, everything (including the "return;") becomes part of the loop body
// Then DetectExitPoint will move the "return;" out of the loop body.
exitPoint = NoExitPoint;
exitPointILOffset = trueInst.StartILOffset;
}
if (falseInst.StartILOffset > exitPointILOffset && falseInst is Leave { IsLeavingFunction: true, Value: Nop _ }) {
exitPoint = NoExitPoint;
exitPointILOffset = falseInst.StartILOffset;
}
}
/// <summary>
/// Constructs a new control flow graph.

2
ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs

@ -35,7 +35,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -35,7 +35,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// Detect sequences of conditional branches that all test a single integer value,
/// and simplify them into a ILAst switch instruction (which like C# does not require contiguous values).
/// </summary>
class SwitchDetection : IILTransform
public class SwitchDetection : IILTransform
{
private readonly SwitchAnalysis analysis = new SwitchAnalysis();

2
ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs

@ -28,7 +28,7 @@ using System.Reflection.Metadata; @@ -28,7 +28,7 @@ using System.Reflection.Metadata;
namespace ICSharpCode.Decompiler.IL.ControlFlow
{
class YieldReturnDecompiler : IILTransform
public class YieldReturnDecompiler : IILTransform
{
// For a description on the code generated by the C# compiler for yield return:
// http://csharpindepth.com/Articles/Chapter6/IteratorBlockImplementation.aspx

8
ICSharpCode.Decompiler/IL/ILReader.cs

@ -515,10 +515,10 @@ namespace ICSharpCode.Decompiler.IL @@ -515,10 +515,10 @@ namespace ICSharpCode.Decompiler.IL
var blockBuilder = new BlockBuilder(body, variableByExceptionHandler);
blockBuilder.CreateBlocks(mainContainer, instructionBuilder, isBranchTarget, cancellationToken);
var function = new ILFunction(this.method, body.GetCodeSize(), this.genericContext, mainContainer, kind);
CollectionExtensions.AddRange(function.Variables, parameterVariables);
CollectionExtensions.AddRange(function.Variables, localVariables);
CollectionExtensions.AddRange(function.Variables, stackVariables);
CollectionExtensions.AddRange(function.Variables, variableByExceptionHandler.Values);
function.Variables.AddRange(parameterVariables);
function.Variables.AddRange(localVariables);
function.Variables.AddRange(stackVariables);
function.Variables.AddRange(variableByExceptionHandler.Values);
function.AddRef(); // mark the root node
var removedBlocks = new List<Block>();
foreach (var c in function.Descendants.OfType<BlockContainer>()) {

13
ICSharpCode.Decompiler/IL/ILVariable.cs

@ -31,10 +31,14 @@ namespace ICSharpCode.Decompiler.IL @@ -31,10 +31,14 @@ namespace ICSharpCode.Decompiler.IL
/// </summary>
Local,
/// <summary>
/// A pinned local variable
/// A pinned local variable (not associated with a pinned region)
/// </summary>
PinnedLocal,
/// <summary>
/// A pinned local variable (associated with a pinned region)
/// </summary>
PinnedRegionLocal,
/// <summary>
/// A local variable used as using-resource variable.
/// </summary>
UsingLocal,
@ -92,6 +96,7 @@ namespace ICSharpCode.Decompiler.IL @@ -92,6 +96,7 @@ namespace ICSharpCode.Decompiler.IL
case VariableKind.ForeachLocal:
case VariableKind.UsingLocal:
case VariableKind.PinnedLocal:
case VariableKind.PinnedRegionLocal:
case VariableKind.DisplayClassLocal:
return true;
default:
@ -161,6 +166,7 @@ namespace ICSharpCode.Decompiler.IL @@ -161,6 +166,7 @@ namespace ICSharpCode.Decompiler.IL
case VariableKind.Local:
case VariableKind.ForeachLocal:
case VariableKind.PinnedLocal:
case VariableKind.PinnedRegionLocal:
case VariableKind.UsingLocal:
case VariableKind.ExceptionLocal:
case VariableKind.DisplayClassLocal:
@ -401,6 +407,9 @@ namespace ICSharpCode.Decompiler.IL @@ -401,6 +407,9 @@ namespace ICSharpCode.Decompiler.IL
case VariableKind.PinnedLocal:
output.Write("pinned local ");
break;
case VariableKind.PinnedRegionLocal:
output.Write("PinnedRegion local ");
break;
case VariableKind.Parameter:
output.Write("param ");
break;
@ -438,7 +447,7 @@ namespace ICSharpCode.Decompiler.IL @@ -438,7 +447,7 @@ namespace ICSharpCode.Decompiler.IL
output.Write(" : ");
Type.WriteTo(output);
output.Write('(');
if (Kind == VariableKind.Parameter || Kind == VariableKind.Local || Kind == VariableKind.PinnedLocal) {
if (Kind == VariableKind.Parameter || Kind == VariableKind.Local || Kind == VariableKind.PinnedLocal || Kind == VariableKind.PinnedRegionLocal) {
output.Write("Index={0}, ", Index);
}
output.Write("LoadCount={0}, AddressCount={1}, StoreCount={2})", LoadCount, AddressCount, StoreCount);

3
ICSharpCode.Decompiler/IL/Instructions.cs

@ -1019,6 +1019,7 @@ namespace ICSharpCode.Decompiler.IL @@ -1019,6 +1019,7 @@ namespace ICSharpCode.Decompiler.IL
base.CheckInvariant(phase);
Debug.Assert(phase <= ILPhase.InILReader || this.IsDescendantOf(variable.Function));
Debug.Assert(phase <= ILPhase.InILReader || variable.Function.Variables[variable.IndexInFunction] == variable);
Debug.Assert(Variable.Kind == VariableKind.PinnedRegionLocal);
}
}
}
@ -1503,7 +1504,7 @@ namespace ICSharpCode.Decompiler.IL @@ -1503,7 +1504,7 @@ namespace ICSharpCode.Decompiler.IL
/// <summary>Switch statement</summary>
public sealed partial class SwitchInstruction : ILInstruction
{
public override StackType ResultType { get { return StackType.Void; } }
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitSwitchInstruction(this);

5
ICSharpCode.Decompiler/IL/Instructions.tt

@ -67,7 +67,8 @@ @@ -67,7 +67,8 @@
CustomChildren(new []{
new ChildInfo("init") { CanInlineInto = true },
new ChildInfo("body")
})),
}),
CustomInvariant("Debug.Assert(Variable.Kind == VariableKind.PinnedRegionLocal);")),
new OpCode("binary", "Common instruction for add, sub, mul, div, rem, bit.and, bit.or, bit.xor, shl and shr.",
CustomClassName("BinaryNumericInstruction"), Binary, CustomWriteTo, CustomConstructor, CustomComputeFlags,
MatchCondition("CheckForOverflow == o.CheckForOverflow && Sign == o.Sign && Operator == o.Operator && IsLifted == o.IsLifted")),
@ -116,7 +117,7 @@ @@ -116,7 +117,7 @@
new ChildInfo("fallbackInst"),
}), CustomConstructor, CustomComputeFlags, CustomWriteTo),
new OpCode("switch", "Switch statement",
CustomClassName("SwitchInstruction"), CustomConstructor, CustomComputeFlags, CustomWriteTo, ResultType("Void"),
CustomClassName("SwitchInstruction"), CustomConstructor, CustomComputeFlags, CustomWriteTo,
MatchCondition("IsLifted == o.IsLifted && Value.PerformMatch(o.Value, ref match) && Patterns.ListMatch.DoMatch(this.Sections, o.Sections, ref match)")),
new OpCode("switch.section", "Switch section within a switch statement",
CustomClassName("SwitchSection"), CustomChildren(new [] { new ChildInfo("body") }),

1
ICSharpCode.Decompiler/IL/Instructions/DynamicInstructions.cs

@ -18,6 +18,7 @@ @@ -18,6 +18,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using ICSharpCode.Decompiler.IL.Patterns;
using ICSharpCode.Decompiler.TypeSystem;

17
ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs

@ -166,13 +166,11 @@ namespace ICSharpCode.Decompiler.IL @@ -166,13 +166,11 @@ namespace ICSharpCode.Decompiler.IL
/// <summary>
/// Return type of this function.
/// Might be null, if this function was not created from metadata.
/// </summary>
public readonly IType ReturnType;
/// <summary>
/// List of parameters of this function.
/// Might be null, if this function was not created from metadata.
/// </summary>
public readonly IReadOnlyList<IParameter> Parameters;
@ -188,17 +186,16 @@ namespace ICSharpCode.Decompiler.IL @@ -188,17 +186,16 @@ namespace ICSharpCode.Decompiler.IL
/// </summary>
/// <remarks>
/// Use <see cref="ILReader"/> to create ILAst.
/// <paramref name="method"/> may be null.
/// </remarks>
public ILFunction(IMethod method, int codeSize, GenericContext genericContext, ILInstruction body, ILFunctionKind kind = ILFunctionKind.TopLevelFunction) : base(OpCode.ILFunction)
{
this.Method = method;
this.Name = Method?.Name;
this.Name = method.Name;
this.CodeSize = codeSize;
this.GenericContext = genericContext;
this.Body = body;
this.ReturnType = Method?.ReturnType;
this.Parameters = Method?.Parameters;
this.ReturnType = method.ReturnType;
this.Parameters = method.Parameters;
this.Variables = new ILVariableCollection(this);
this.LocalFunctions = new InstructionCollection<ILFunction>(this, 1);
this.kind = kind;
@ -207,15 +204,15 @@ namespace ICSharpCode.Decompiler.IL @@ -207,15 +204,15 @@ namespace ICSharpCode.Decompiler.IL
/// <summary>
/// This constructor is only to be used by the TransformExpressionTrees step.
/// </summary>
internal ILFunction(IType returnType, IReadOnlyList<IParameter> parameters, GenericContext genericContext, ILInstruction body) : base(OpCode.ILFunction)
internal ILFunction(IType returnType, IReadOnlyList<IParameter> parameters, GenericContext genericContext, ILInstruction body, ILFunctionKind kind = ILFunctionKind.TopLevelFunction) : base(OpCode.ILFunction)
{
this.GenericContext = genericContext;
this.Body = body;
this.ReturnType = returnType;
this.Parameters = parameters;
this.ReturnType = returnType ?? throw new ArgumentNullException(nameof(returnType));
this.Parameters = parameters ?? throw new ArgumentNullException(nameof(parameters));
this.Variables = new ILVariableCollection(this);
this.LocalFunctions = new InstructionCollection<ILFunction>(this, 1);
this.kind = ILFunctionKind.ExpressionTree;
this.kind = kind;
}
internal override void CheckInvariant(ILPhase phase)

24
ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs

@ -130,7 +130,16 @@ namespace ICSharpCode.Decompiler.IL @@ -130,7 +130,16 @@ namespace ICSharpCode.Decompiler.IL
clone.Sections.AddRange(this.Sections.Select(h => (SwitchSection)h.Clone()));
return clone;
}
StackType resultType = StackType.Void;
public override StackType ResultType => resultType;
public void SetResultType(StackType resultType)
{
this.resultType = resultType;
}
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
@ -143,12 +152,25 @@ namespace ICSharpCode.Decompiler.IL @@ -143,12 +152,25 @@ namespace ICSharpCode.Decompiler.IL
}
Debug.Assert(!section.Labels.IsEmpty || section.HasNullLabel);
Debug.Assert(!section.Labels.Overlaps(sets));
Debug.Assert(section.Body.ResultType == this.ResultType);
sets = sets.UnionWith(section.Labels);
}
Debug.Assert(sets.SetEquals(LongSet.Universe), "switch does not handle all possible cases");
Debug.Assert(!expectNullSection, "Lifted switch is missing 'case null'");
Debug.Assert(this.IsLifted ? (value.ResultType == StackType.O) : (value.ResultType == StackType.I4 || value.ResultType == StackType.I8));
}
public SwitchSection GetDefaultSection()
{
// Pick the section with the most labels as default section.
IL.SwitchSection defaultSection = Sections.First();
foreach (var section in Sections) {
if (section.Labels.Count() > defaultSection.Labels.Count()) {
defaultSection = section;
}
}
return defaultSection;
}
}
partial class SwitchSection

24
ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs

@ -20,6 +20,8 @@ using System; @@ -20,6 +20,8 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
using Humanizer;
using ICSharpCode.Decompiler.CSharp.OutputVisitor;
using ICSharpCode.Decompiler.TypeSystem;
@ -51,6 +53,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -51,6 +53,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
ILTransformContext context;
string[] currentFieldNames;
Dictionary<string, int> reservedVariableNames;
Dictionary<MethodDefinitionHandle, string> localFunctionMapping;
HashSet<ILVariable> loopCounters;
const char maxLoopVariableName = 'n';
@ -59,6 +62,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -59,6 +62,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
this.context = context;
currentFieldNames = function.Method.DeclaringTypeDefinition.Fields.Select(f => f.Name).ToArray();
reservedVariableNames = new Dictionary<string, int>();
localFunctionMapping = new Dictionary<MethodDefinitionHandle, string>();
loopCounters = CollectLoopCounters(function);
foreach (var f in function.Descendants.OfType<ILFunction>()) {
if (f.Method != null) {
@ -180,6 +184,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -180,6 +184,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (!LocalFunctionDecompiler.ParseLocalFunctionName(localFunction.Name, out _, out var newName) || !IsValidName(newName))
newName = null;
localFunction.Name = newName;
localFunction.ReducedMethod.Name = newName;
}
// Now generate names:
var mapping = new Dictionary<ILVariable, string>(ILVariableEqualityComparer.Instance);
@ -199,6 +204,25 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -199,6 +204,25 @@ namespace ICSharpCode.Decompiler.IL.Transforms
newName = GetAlternativeName("f");
}
localFunction.Name = newName;
localFunction.ReducedMethod.Name = newName;
localFunctionMapping[(MethodDefinitionHandle)localFunction.ReducedMethod.MetadataToken] = newName;
}
foreach (var inst in function.Descendants) {
LocalFunctionMethod localFunction;
switch (inst) {
case Call call:
localFunction = call.Method as LocalFunctionMethod;
break;
case LdFtn ldftn:
localFunction = ldftn.Method as LocalFunctionMethod;
break;
default:
localFunction = null;
break;
}
if (localFunction == null || !localFunctionMapping.TryGetValue((MethodDefinitionHandle)localFunction.MetadataToken, out var name))
continue;
localFunction.Name = name;
}
}

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

@ -138,7 +138,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -138,7 +138,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
};
block.Instructions.Insert(i++, new StLoc(uninlinedArgs[j], arg));
}
CollectionExtensions.AddRange(v.Function.Variables, uninlinedArgs);
v.Function.Variables.AddRange(uninlinedArgs);
// perform copy propagation:
foreach (var expr in v.LoadInstructions.ToArray()) {
var clone = copiedExpr.Clone();

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

@ -25,9 +25,9 @@ using ICSharpCode.Decompiler.TypeSystem; @@ -25,9 +25,9 @@ using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL.Transforms
{
/// <summary>
///
/// Transforms anonymous methods and lambdas by creating nested ILFunctions.
/// </summary>
class DelegateConstruction : IILTransform
public class DelegateConstruction : IILTransform
{
ILTransformContext context;
ITypeResolveContext decompilationContext;

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

@ -25,7 +25,7 @@ using ICSharpCode.Decompiler.TypeSystem; @@ -25,7 +25,7 @@ using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL.Transforms
{
class EarlyExpressionTransforms : ILVisitor, IILTransform
public class EarlyExpressionTransforms : ILVisitor, IILTransform
{
ILTransformContext context;

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

@ -65,8 +65,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -65,8 +65,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{
if (container.Kind == ContainerKind.Switch) {
// Special case for switch: Only visit the switch condition block.
var switchInst = (SwitchInstruction)container.EntryPoint.Instructions[0];
var switchInst = (SwitchInstruction)container.EntryPoint.Instructions[0];
switchInst.Value.AcceptVisitor(this);
HandleSwitchExpression(container, switchInst);
}
// No need to call base.VisitBlockContainer, see comment in VisitBlock.
}
@ -121,12 +123,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -121,12 +123,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
inst.Kind = ComparisonKind.Equality;
}
}
var rightWithoutConv = inst.Right.UnwrapConv(ConversionKind.SignExtend).UnwrapConv(ConversionKind.ZeroExtend);
if (rightWithoutConv.MatchLdcI4(0)
&& inst.Sign == Sign.Unsigned
&& (inst.Kind == ComparisonKind.GreaterThan || inst.Kind == ComparisonKind.LessThanOrEqual))
{
&& inst.Sign == Sign.Unsigned
&& (inst.Kind == ComparisonKind.GreaterThan || inst.Kind == ComparisonKind.LessThanOrEqual)) {
if (inst.Kind == ComparisonKind.GreaterThan) {
context.Step("comp.unsigned(left > ldc.i4 0) => comp(left != ldc.i4 0)", inst);
inst.Kind = ComparisonKind.Inequality;
@ -169,21 +170,19 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -169,21 +170,19 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
}
}
protected internal override void VisitConv(Conv inst)
{
inst.Argument.AcceptVisitor(this);
if (inst.Argument.MatchLdLen(StackType.I, out ILInstruction array) && inst.TargetType.IsIntegerType()
&& (!inst.CheckForOverflow || context.Settings.AssumeArrayLengthFitsIntoInt32))
{
&& (!inst.CheckForOverflow || context.Settings.AssumeArrayLengthFitsIntoInt32)) {
context.Step("conv.i4(ldlen array) => ldlen.i4(array)", inst);
inst.AddILRange(inst.Argument);
inst.ReplaceWith(new LdLen(inst.TargetType.GetStackType(), array).WithILRange(inst));
return;
}
if (inst.TargetType.IsFloatType() && inst.Argument is Conv conv
&& conv.Kind == ConversionKind.IntToFloat && conv.TargetType == PrimitiveType.R)
{
if (inst.TargetType.IsFloatType() && inst.Argument is Conv conv
&& conv.Kind == ConversionKind.IntToFloat && conv.TargetType == PrimitiveType.R) {
// IL conv.r.un does not indicate whether to convert the target type to R4 or R8,
// so the C# compiler usually follows it with an explicit conv.r4 or conv.r8.
// To avoid emitting '(float)(double)val', we combine these two conversions:
@ -270,7 +269,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -270,7 +269,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
arg.AcceptVisitor(this);
}
}
protected internal override void VisitCall(Call inst)
{
var expr = EarlyExpressionTransforms.HandleCall(inst, context);
@ -430,9 +429,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -430,9 +429,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} else if (args.Count == 5) {
int lo, mid, hi, isNegative, scale;
if (args[0].MatchLdcI4(out lo) && args[1].MatchLdcI4(out mid) &&
args[2].MatchLdcI4(out hi) && args[3].MatchLdcI4(out isNegative) &&
args[4].MatchLdcI4(out scale))
{
args[2].MatchLdcI4(out hi) && args[3].MatchLdcI4(out isNegative) &&
args[4].MatchLdcI4(out scale)) {
result = new LdcDecimal(new decimal(lo, mid, hi, isNegative != 0, (byte)scale));
return true;
}
@ -501,8 +499,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -501,8 +499,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// Be careful: when both LHS and RHS are the constant 1, we must not
// swap the arguments as it would lead to an infinite transform loop.
if (inst.TrueInst.MatchLdcI4(0) && !inst.FalseInst.MatchLdcI4(0)
|| inst.FalseInst.MatchLdcI4(1) && !inst.TrueInst.MatchLdcI4(1))
{
|| inst.FalseInst.MatchLdcI4(1) && !inst.TrueInst.MatchLdcI4(1)) {
context.Step("canonicalize logic and/or", inst);
var t = inst.TrueInst;
inst.TrueInst = inst.FalseInst;
@ -531,6 +528,112 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -531,6 +528,112 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
}
IfInstruction HandleConditionalOperator(IfInstruction inst)
{
// if (cond) stloc (A, V1) else stloc (A, V2) --> stloc (A, if (cond) V1 else V2)
Block trueInst = inst.TrueInst as Block;
if (trueInst == null || trueInst.Instructions.Count != 1)
return inst;
Block falseInst = inst.FalseInst as Block;
if (falseInst == null || falseInst.Instructions.Count != 1)
return inst;
ILVariable v;
ILInstruction value1, value2;
if (trueInst.Instructions[0].MatchStLoc(out v, out value1) && falseInst.Instructions[0].MatchStLoc(v, out value2)) {
context.Step("conditional operator", inst);
var newIf = new IfInstruction(Comp.LogicNot(inst.Condition), value2, value1);
newIf.AddILRange(inst);
inst.ReplaceWith(new StLoc(v, newIf));
context.RequestRerun(); // trigger potential inlining of the newly created StLoc
return newIf;
}
return inst;
}
private void HandleSwitchExpression(BlockContainer container, SwitchInstruction switchInst)
{
if (!context.Settings.SwitchExpressions)
return;
Debug.Assert(container.Kind == ContainerKind.Switch);
Debug.Assert(container.ResultType == StackType.Void);
var defaultSection = switchInst.GetDefaultSection();
StackType resultType = StackType.Void;
BlockContainer leaveTarget = null;
ILVariable resultVariable = null;
foreach (var section in switchInst.Sections) {
if (section != defaultSection) {
// every section except for the default must have exactly 1 label
if (section.Labels.Count() != (section.HasNullLabel ? 0u : 1u))
return;
}
if (!section.Body.MatchBranch(out var sectionBlock))
return;
if (sectionBlock.IncomingEdgeCount != 1)
return;
if (sectionBlock.Parent != container)
return;
if (sectionBlock.Instructions.Count == 1) {
if (sectionBlock.Instructions[0] is Throw) {
// OK
} else if (sectionBlock.Instructions[0] is Leave leave) {
if (!leave.IsLeavingFunction)
return;
leaveTarget ??= leave.TargetContainer;
Debug.Assert(leaveTarget == leave.TargetContainer);
resultType = leave.Value.ResultType;
} else {
return;
}
} else if (sectionBlock.Instructions.Count == 2) {
if (!sectionBlock.Instructions[0].MatchStLoc(out var v, out _))
return;
if (!sectionBlock.Instructions[1].MatchLeave(container))
return;
resultVariable ??= v;
if (resultVariable != v)
return;
resultType = resultVariable.StackType;
} else {
return;
}
}
// Exactly one of resultVariable/leaveTarget must be null
if ((resultVariable == null) == (leaveTarget == null))
return;
if (switchInst.Value is StringToInt str2int) {
// validate that each integer is used for exactly one value
var integersUsed = new HashSet<int>();
foreach ((string key, int val) in str2int.Map) {
if (!integersUsed.Add(val))
return;
}
}
context.Step("Switch Expression", switchInst);
switchInst.SetResultType(resultType);
foreach (var section in switchInst.Sections) {
var block = ((Branch)section.Body).TargetBlock;
if (block.Instructions.Count == 1) {
if (block.Instructions[0] is Throw t) {
t.resultType = resultType;
section.Body = t;
} else if (block.Instructions[0] is Leave leave) {
section.Body = leave.Value;
} else {
throw new InvalidOperationException();
}
} else {
section.Body = ((StLoc)block.Instructions[0]).Value;
}
}
if (resultVariable != null) {
container.ReplaceWith(new StLoc(resultVariable, switchInst));
} else {
container.ReplaceWith(new Leave(leaveTarget, switchInst));
}
context.RequestRerun(); // new StLoc might trigger inlining
}
/// <summary>
/// op is either add or remove/subtract:
/// if (dynamic.isevent (target)) {
@ -641,28 +744,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -641,28 +744,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms
inst.ReplaceWith(new DynamicCompoundAssign(binaryOp.Operation, binaryOp.BinderFlags, binaryOp.Left, binaryOp.LeftArgumentInfo, binaryOp.Right, binaryOp.RightArgumentInfo));
}
IfInstruction HandleConditionalOperator(IfInstruction inst)
{
// if (cond) stloc (A, V1) else stloc (A, V2) --> stloc (A, if (cond) V1 else V2)
Block trueInst = inst.TrueInst as Block;
if (trueInst == null || trueInst.Instructions.Count != 1)
return inst;
Block falseInst = inst.FalseInst as Block;
if (falseInst == null || falseInst.Instructions.Count != 1)
return inst;
ILVariable v;
ILInstruction value1, value2;
if (trueInst.Instructions[0].MatchStLoc(out v, out value1) && falseInst.Instructions[0].MatchStLoc(v, out value2)) {
context.Step("conditional operator", inst);
var newIf = new IfInstruction(Comp.LogicNot(inst.Condition), value2, value1);
newIf.AddILRange(inst);
inst.ReplaceWith(new StLoc(v, newIf));
context.RequestRerun(); // trigger potential inlining of the newly created StLoc
return newIf;
}
return inst;
}
protected internal override void VisitBinaryNumericInstruction(BinaryNumericInstruction inst)
{
base.VisitBinaryNumericInstruction(inst);
@ -670,8 +751,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -670,8 +751,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
case BinaryNumericOperator.ShiftLeft:
case BinaryNumericOperator.ShiftRight:
if (inst.Right.MatchBinaryNumericInstruction(BinaryNumericOperator.BitAnd, out var lhs, out var rhs)
&& rhs.MatchLdcI4(inst.ResultType == StackType.I8 ? 63 : 31))
{
&& rhs.MatchLdcI4(inst.ResultType == StackType.I8 ? 63 : 31)) {
// a << (b & 31) => a << b
context.Step("Combine bit.and into shift", inst);
inst.Right = lhs;
@ -679,8 +759,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -679,8 +759,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
break;
case BinaryNumericOperator.BitAnd:
if (inst.Left.InferType(context.TypeSystem).IsKnownType(KnownTypeCode.Boolean)
&& inst.Right.InferType(context.TypeSystem).IsKnownType(KnownTypeCode.Boolean))
{
&& inst.Right.InferType(context.TypeSystem).IsKnownType(KnownTypeCode.Boolean)) {
if (new NullableLiftingTransform(context).Run(inst)) {
// e.g. "(a.GetValueOrDefault() == b.GetValueOrDefault()) & (a.HasValue & b.HasValue)"
}
@ -688,7 +767,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -688,7 +767,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
break;
}
}
protected internal override void VisitTryCatchHandler(TryCatchHandler inst)
{
base.VisitTryCatchHandler(inst);

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

@ -28,7 +28,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -28,7 +28,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// This transform un-inlines the argument of `isinst` instructions that can't be directly translated to C#,
/// thus allowing the emulation via "expr is T ? (T)expr : null".
/// </summary>
class FixLoneIsInst : IILTransform
public class FixLoneIsInst : IILTransform
{
void IILTransform.Run(ILFunction function, ILTransformContext context)
{

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

@ -53,15 +53,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -53,15 +53,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms
public void Run(Block block, int pos, StatementTransformContext context)
{
InlineOneIfPossible(block, pos, OptionsForBlock(block, pos), context: context);
InlineOneIfPossible(block, pos, OptionsForBlock(block, pos, context), context: context);
}
internal static InliningOptions OptionsForBlock(Block block, int pos)
internal static InliningOptions OptionsForBlock(Block block, int pos, ILTransformContext context)
{
InliningOptions options = InliningOptions.None;
if (IsCatchWhenBlock(block))
if (context.Settings.AggressiveInlining || IsCatchWhenBlock(block)) {
options |= InliningOptions.Aggressive;
else {
} else {
var function = block.Ancestors.OfType<ILFunction>().FirstOrDefault();
var inst = block.Instructions[pos];
if (IsInConstructorInitializer(function, inst))
@ -77,7 +77,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -77,7 +77,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
for (int i = instructions.Count - 1; i >= 0; i--) {
if (instructions[i] is StLoc inst) {
InliningOptions options = InliningOptions.None;
if (IsCatchWhenBlock(block) || IsInConstructorInitializer(function, inst))
if (context.Settings.AggressiveInlining || IsCatchWhenBlock(block) || IsInConstructorInitializer(function, inst))
options = InliningOptions.Aggressive;
if (InlineOneIfPossible(block, i, options, context)) {
modified = true;
@ -250,21 +250,26 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -250,21 +250,26 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// Thus, we have to ensure we're operating on an r-value.
// Additionally, we cannot inline in cases where the C# compiler prohibits the direct use
// of the rvalue (e.g. M(ref (MyStruct)obj); is invalid).
if (!IsUsedAsThisPointerInCall(loadInst, out var method))
if (IsUsedAsThisPointerInCall(loadInst, out var method)) {
switch (ClassifyExpression(inlinedExpression)) {
case ExpressionClassification.RValue:
// For struct method calls on rvalues, the C# compiler always generates temporaries.
return true;
case ExpressionClassification.MutableLValue:
// For struct method calls on mutable lvalues, the C# compiler never generates temporaries.
return false;
case ExpressionClassification.ReadonlyLValue:
// For struct method calls on readonly lvalues, the C# compiler
// only generates a temporary if it isn't a "readonly struct"
return MethodRequiresCopyForReadonlyLValue(method);
default:
throw new InvalidOperationException("invalid expression classification");
}
} else if (IsUsedAsThisPointerInFieldRead(loadInst)) {
// mcs generated temporaries for field reads on rvalues (#1555)
return ClassifyExpression(inlinedExpression) == ExpressionClassification.RValue;
} else {
return false;
switch (ClassifyExpression(inlinedExpression)) {
case ExpressionClassification.RValue:
// For struct method calls on rvalues, the C# compiler always generates temporaries.
return true;
case ExpressionClassification.MutableLValue:
// For struct method calls on mutable lvalues, the C# compiler never generates temporaries.
return false;
case ExpressionClassification.ReadonlyLValue:
// For struct method calls on readonly lvalues, the C# compiler
// only generates a temporary if it isn't a "readonly struct"
return MethodRequiresCopyForReadonlyLValue(method);
default:
throw new InvalidOperationException("invalid expression classification");
}
}
@ -288,14 +293,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -288,14 +293,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms
static bool IsUsedAsThisPointerInCall(LdLoca ldloca, out IMethod method)
{
method = null;
if (ldloca.ChildIndex != 0)
return false;
if (ldloca.Variable.Type.IsReferenceType ?? false)
return false;
ILInstruction inst = ldloca;
while (inst.Parent is LdFlda ldflda) {
inst = ldflda;
}
if (inst.ChildIndex != 0)
return false;
switch (inst.Parent.OpCode) {
case OpCode.Call:
case OpCode.CallVirt:
@ -321,7 +326,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -321,7 +326,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false;
}
}
static bool IsUsedAsThisPointerInFieldRead(LdLoca ldloca)
{
if (ldloca.Variable.Type.IsReferenceType ?? false)
return false;
ILInstruction inst = ldloca;
while (inst.Parent is LdFlda ldflda) {
inst = ldflda;
}
return inst != ldloca && inst.Parent is LdObj;
}
internal enum ExpressionClassification
{
RValue,
@ -418,6 +434,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -418,6 +434,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
case OpCode.NumericCompoundAssign:
case OpCode.UserDefinedCompoundAssign:
case OpCode.Await:
case OpCode.SwitchInstruction:
return true;
case OpCode.LdLoc:
if (v.StateMachineField == null && ((LdLoc)inlinedExpression).Variable.StateMachineField != null) {
@ -479,32 +496,21 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -479,32 +496,21 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true;
}
break;
case OpCode.Leave:
case OpCode.YieldReturn:
return true;
case OpCode.SwitchInstruction:
//case OpCode.BinaryNumericInstruction when parent.SlotInfo == SwitchInstruction.ValueSlot:
case OpCode.StringToInt when parent.SlotInfo == SwitchInstruction.ValueSlot:
return true;
}
// decide based on the top-level target instruction into which we are inlining:
switch (next.OpCode) {
case OpCode.Leave:
case OpCode.YieldReturn:
return parent == next;
case OpCode.IfInstruction:
while (parent.MatchLogicNot(out _)) {
parent = parent.Parent;
}
return parent == next;
case OpCode.BlockContainer:
if (((BlockContainer)next).EntryPoint.Instructions[0] is SwitchInstruction switchInst) {
next = switchInst;
goto case OpCode.SwitchInstruction;
} else {
return false;
}
case OpCode.SwitchInstruction:
if (parent == next)
return true;
if (parent.MatchBinaryNumericInstruction(BinaryNumericOperator.Sub) && parent.Parent == next)
return true;
if (parent is StringToInt stringToInt && stringToInt.Parent == next)
return true;
return false;
default:
return false;
}

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

@ -205,6 +205,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -205,6 +205,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (!CheckContainerLengthVariableUseCount(containerLengthVar, startIndexKind)) {
return;
}
if (!call.IsDescendantOf(block.Instructions[pos]))
return;
// startOffsetVar might be used deep inside a complex statement, ensure we can inline up to that point:
for (int i = startPos; i < pos; i++) {
if (!ILInlining.CanInlineInto(block.Instructions[pos], startOffsetVar, block.Instructions[i]))
@ -275,6 +277,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -275,6 +277,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
if (!(sliceLengthVar.LoadInstructions.Single().Parent is CallInstruction call))
return;
if (!call.IsDescendantOf(block.Instructions[pos]))
return;
if (!IsSlicingMethod(call.Method))
return;
if (call.Arguments.Count != 3)

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

@ -24,7 +24,7 @@ using ICSharpCode.Decompiler.TypeSystem; @@ -24,7 +24,7 @@ using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL
{
class IntroduceDynamicTypeOnLocals : IILTransform
public class IntroduceDynamicTypeOnLocals : IILTransform
{
public void Run(ILFunction function, ILTransformContext context)
{

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

@ -25,7 +25,7 @@ using ICSharpCode.Decompiler.TypeSystem; @@ -25,7 +25,7 @@ using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL
{
class IntroduceRefReadOnlyModifierOnLocals : IILTransform
public class IntroduceRefReadOnlyModifierOnLocals : IILTransform
{
public void Run(ILFunction function, ILTransformContext context)
{

24
ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs

@ -36,7 +36,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -36,7 +36,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// <summary>
/// Decompiler step for C# 7.0 local functions
/// </summary>
class LocalFunctionDecompiler : IILTransform
public class LocalFunctionDecompiler : IILTransform
{
ILTransformContext context;
ITypeResolveContext resolveContext;
@ -484,7 +484,27 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -484,7 +484,27 @@ namespace ICSharpCode.Decompiler.IL.Transforms
break;
parametersToRemove++;
}
return new LocalFunctionMethod(method, parametersToRemove, typeParametersToRemove);
return new LocalFunctionMethod(method, method.Name, CanBeStaticLocalFunction(), parametersToRemove, typeParametersToRemove);
bool CanBeStaticLocalFunction()
{
if (!context.Settings.StaticLocalFunctions)
return false;
// Cannot be static because there are closure parameters that will be removed
if (parametersToRemove > 0)
return false;
// no closure parameters, but static:
// we can safely assume, this local function can be declared static
if (method.IsStatic)
return true;
// the local function is used in conjunction with a lambda, which means,
// it is defined inside the display-class type
var declaringType = method.DeclaringTypeDefinition;
if (!declaringType.IsCompilerGenerated())
return false;
// if there are no instance fields, we can make it a static local function
return !declaringType.GetFields(f => !f.IsStatic).Any();
}
}
static void TransformToLocalFunctionReference(ILFunction function, CallInstruction useSite)

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

@ -25,7 +25,7 @@ using ICSharpCode.Decompiler.TypeSystem; @@ -25,7 +25,7 @@ using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL.Transforms
{
class LockTransform : IBlockTransform
public class LockTransform : IBlockTransform
{
BlockTransformContext context;

6
ICSharpCode.Decompiler/IL/Transforms/NamedArgumentTransform.cs

@ -10,9 +10,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -10,9 +10,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
using FindResult = ILInlining.FindResult;
using FindResultType = ILInlining.FindResultType;
class NamedArgumentTransform : IStatementTransform
public class NamedArgumentTransform : IStatementTransform
{
public static FindResult CanIntroduceNamedArgument(CallInstruction call, ILInstruction child, ILVariable v, ILInstruction expressionBeingMoved)
internal static FindResult CanIntroduceNamedArgument(CallInstruction call, ILInstruction child, ILVariable v, ILInstruction expressionBeingMoved)
{
Debug.Assert(child.Parent == call);
if (call.IsInstanceCall && child.ChildIndex == 0)
@ -106,7 +106,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -106,7 +106,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{
if (!context.Settings.NamedArguments)
return;
var options = ILInlining.OptionsForBlock(block, pos);
var options = ILInlining.OptionsForBlock(block, pos, context);
options |= InliningOptions.IntroduceNamedArguments;
ILInlining.InlineOneIfPossible(block, pos, options, context: context);
}

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

@ -31,7 +31,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -31,7 +31,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
///
/// The ?? operator for nullable value types is handled by NullableLiftingTransform.
/// </summary>
class NullCoalescingTransform : IStatementTransform
public class NullCoalescingTransform : IStatementTransform
{
public void Run(Block block, int pos, StatementTransformContext context)
{

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

@ -489,7 +489,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -489,7 +489,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
}
class NullPropagationStatementTransform : IStatementTransform
public class NullPropagationStatementTransform : IStatementTransform
{
public void Run(Block block, int pos, StatementTransformContext context)
{

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

@ -908,7 +908,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -908,7 +908,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
#endregion
}
class NullableLiftingStatementTransform : IStatementTransform
public class NullableLiftingStatementTransform : IStatementTransform
{
public void Run(Block block, int pos, StatementTransformContext context)
{

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

@ -28,7 +28,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -28,7 +28,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// <summary>
/// Detects switch-on-nullable patterns employed by the C# compiler and transforms them to an ILAst-switch-instruction.
/// </summary>
class SwitchOnNullableTransform : IILTransform
public class SwitchOnNullableTransform : IILTransform
{
public void Run(ILFunction function, ILTransformContext context)
{

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

@ -31,7 +31,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -31,7 +31,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// <summary>
/// Detects switch-on-string patterns employed by the C# compiler and transforms them to an ILAst-switch-instruction.
/// </summary>
class SwitchOnStringTransform : IILTransform
public class SwitchOnStringTransform : IILTransform
{
ILTransformContext context;

21
ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs

@ -378,9 +378,24 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -378,9 +378,24 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false;
if (!targetType.GetAllBaseTypes().Any(i => i.IsKnownType(KnownTypeCode.IEnumerable) || i.IsKnownType(KnownTypeCode.IEnumerableOfT)))
return false;
return CSharp.CallBuilder.CanInferTypeArgumentsFromParameters(
method, method.Parameters.SelectReadOnlyArray(p => new ResolveResult(p.Type)),
new TypeInference(resolveContext.Compilation));
return CanInferTypeArgumentsFromParameters(method);
bool CanInferTypeArgumentsFromParameters(IMethod method)
{
if (method.TypeParameters.Count == 0)
return true;
// always use unspecialized member, otherwise type inference fails
method = (IMethod)method.MemberDefinition;
new TypeInference(resolveContext.Compilation)
.InferTypeArguments(
method.TypeParameters,
// TODO : this is not entirely correct... we need argument type information to resolve Add methods properly
method.Parameters.SelectReadOnlyArray(p => new ResolveResult(p.Type)),
method.Parameters.SelectReadOnlyArray(p => p.Type),
out bool success
);
return success;
}
}
static IType GetReturnTypeFromInstruction(ILInstruction instruction)

5
ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs

@ -43,7 +43,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -43,7 +43,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// Note that 2) and 3) apply because declarations and uses of lambdas and local functions
/// are already transformed by the time this transform is applied.
/// </summary>
class TransformDisplayClassUsage : ILVisitor, IILTransform
public class TransformDisplayClassUsage : ILVisitor, IILTransform
{
class VariableToDeclare
{
@ -68,7 +68,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -68,7 +68,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms
public void Propagate(ILVariable variable)
{
Debug.Assert(declaredVariable == null || (variable == null && declaredVariable.StateMachineField == null));
this.declaredVariable = variable;
this.CanPropagate = variable != null;
}
@ -456,7 +455,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -456,7 +455,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{
ILVariable v;
switch (value) {
case LdLoc load when load.Variable.StateMachineField == null:
case LdLoc load:
v = load.Variable;
if (v.Kind == VariableKind.Parameter) {
if (v.LoadCount != 1 && !v.IsThis()) {

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

@ -156,8 +156,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -156,8 +156,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
var container = new BlockContainer();
container.AddILRange(instruction);
var functionType = instruction.Method.ReturnType.TypeArguments[0];
var returnType = functionType.GetDelegateInvokeMethod()?.ReturnType;
var function = new ILFunction(returnType, parameterList, context.Function.GenericContext, container);
var returnType = functionType.GetDelegateInvokeMethod()?.ReturnType ?? SpecialType.UnknownType;
var function = new ILFunction(returnType, parameterList, context.Function.GenericContext, container, ILFunctionKind.ExpressionTree);
function.DelegateType = functionType;
function.Kind = IsExpressionTree(functionType) ? ILFunctionKind.ExpressionTree : ILFunctionKind.Delegate;
function.Variables.AddRange(parameterVariablesList);

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

@ -26,7 +26,7 @@ using ICSharpCode.Decompiler.Util; @@ -26,7 +26,7 @@ using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.IL.Transforms
{
class UserDefinedLogicTransform : IStatementTransform
public class UserDefinedLogicTransform : IStatementTransform
{
void IStatementTransform.Run(Block block, int pos, StatementTransformContext context)
{

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

@ -25,7 +25,7 @@ using ICSharpCode.Decompiler.TypeSystem; @@ -25,7 +25,7 @@ using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL.Transforms
{
class UsingTransform : IBlockTransform
public class UsingTransform : IBlockTransform
{
BlockTransformContext context;

55
ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs

@ -61,29 +61,30 @@ namespace ICSharpCode.Decompiler.Metadata @@ -61,29 +61,30 @@ namespace ICSharpCode.Decompiler.Metadata
};
readonly DotNetCorePackageInfo[] packages;
ISet<string> packageBasePaths = new HashSet<string>(StringComparer.Ordinal);
readonly Version version;
readonly List<string> searchPaths = new List<string>();
readonly List<string> packageBasePaths = new List<string>();
readonly Version targetFrameworkVersion;
readonly string dotnetBasePath = FindDotNetExeDirectory();
public DotNetCorePathFinder(Version version)
public DotNetCorePathFinder(TargetFrameworkIdentifier targetFramework, Version targetFrameworkVersion)
{
this.version = version;
}
public DotNetCorePathFinder(string parentAssemblyFileName, string targetFrameworkIdString, TargetFrameworkIdentifier targetFramework, Version version, ReferenceLoadInfo loadInfo = null)
{
string assemblyName = Path.GetFileNameWithoutExtension(parentAssemblyFileName);
string basePath = Path.GetDirectoryName(parentAssemblyFileName);
this.version = version;
this.targetFrameworkVersion = targetFrameworkVersion;
if (targetFramework == TargetFrameworkIdentifier.NETStandard) {
// .NET Standard 2.1 is implemented by .NET Core 3.0 or higher
if (version.Major == 2 && version.Minor == 1) {
this.version = new Version(3, 0, 0);
if (targetFrameworkVersion.Major == 2 && targetFrameworkVersion.Minor == 1) {
this.targetFrameworkVersion = new Version(3, 0, 0);
}
}
}
public DotNetCorePathFinder(string parentAssemblyFileName, string targetFrameworkIdString, TargetFrameworkIdentifier targetFramework, Version targetFrameworkVersion, ReferenceLoadInfo loadInfo = null)
: this(targetFramework, targetFrameworkVersion)
{
string assemblyName = Path.GetFileNameWithoutExtension(parentAssemblyFileName);
string basePath = Path.GetDirectoryName(parentAssemblyFileName);
packageBasePaths.Add(basePath);
searchPaths.Add(basePath);
var depsJsonFileName = Path.Combine(basePath, $"{assemblyName}.deps.json");
if (File.Exists(depsJsonFileName)) {
@ -106,17 +107,17 @@ namespace ICSharpCode.Decompiler.Metadata @@ -106,17 +107,17 @@ namespace ICSharpCode.Decompiler.Metadata
public void AddSearchDirectory(string path)
{
this.packageBasePaths.Add(path);
this.searchPaths.Add(path);
}
public void RemoveSearchDirectory(string path)
{
this.packageBasePaths.Remove(path);
this.searchPaths.Remove(path);
}
public string TryResolveDotNetCore(IAssemblyReference name)
{
foreach (var basePath in packageBasePaths) {
foreach (var basePath in searchPaths.Concat(packageBasePaths)) {
if (File.Exists(Path.Combine(basePath, name.Name + ".dll"))) {
return Path.Combine(basePath, name.Name + ".dll");
} else if (File.Exists(Path.Combine(basePath, name.Name + ".exe"))) {
@ -124,7 +125,7 @@ namespace ICSharpCode.Decompiler.Metadata @@ -124,7 +125,7 @@ namespace ICSharpCode.Decompiler.Metadata
}
}
return FallbackToDotNetSharedDirectory(name, version);
return FallbackToDotNetSharedDirectory(name);
}
internal string GetReferenceAssemblyPath(string targetFramework)
@ -169,7 +170,7 @@ namespace ICSharpCode.Decompiler.Metadata @@ -169,7 +170,7 @@ namespace ICSharpCode.Decompiler.Metadata
}
}
string FallbackToDotNetSharedDirectory(IAssemblyReference name, Version version)
string FallbackToDotNetSharedDirectory(IAssemblyReference name)
{
if (dotnetBasePath == null)
return null;
@ -177,7 +178,7 @@ namespace ICSharpCode.Decompiler.Metadata @@ -177,7 +178,7 @@ namespace ICSharpCode.Decompiler.Metadata
foreach (var basePath in basePaths) {
if (!Directory.Exists(basePath))
continue;
var closestVersion = GetClosestVersionFolder(basePath, version);
var closestVersion = GetClosestVersionFolder(basePath, targetFrameworkVersion);
if (File.Exists(Path.Combine(basePath, closestVersion, name.Name + ".dll"))) {
return Path.Combine(basePath, closestVersion, name.Name + ".dll");
} else if (File.Exists(Path.Combine(basePath, closestVersion, name.Name + ".exe"))) {
@ -189,15 +190,17 @@ namespace ICSharpCode.Decompiler.Metadata @@ -189,15 +190,17 @@ namespace ICSharpCode.Decompiler.Metadata
static string GetClosestVersionFolder(string basePath, Version version)
{
string result = null;
foreach (var folder in new DirectoryInfo(basePath).GetDirectories().Select(d => ConvertToVersion(d.Name)).Where(v => v.Item1 != null).OrderByDescending(v => v.Item1)) {
if (folder.Item1 >= version)
result = folder.Item2;
var foundVersions = new DirectoryInfo(basePath).GetDirectories()
.Select(d => ConvertToVersion(d.Name))
.Where(v => v.version != null);
foreach (var folder in foundVersions.OrderBy(v => v.Item1)) {
if (folder.version >= version)
return folder.directoryName;
}
return result ?? version.ToString();
return version.ToString();
}
internal static (Version, string) ConvertToVersion(string name)
internal static (Version version, string directoryName) ConvertToVersion(string name)
{
string RemoveTrailingVersionInfo()
{

107
ICSharpCode.Decompiler/Metadata/DotNetCorePathFinderExtensions.cs

@ -20,20 +20,26 @@ using System; @@ -20,20 +20,26 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.Metadata
{
public static class DotNetCorePathFinderExtensions
{
static readonly string RefPathPattern =
static readonly string PathPattern =
@"(Reference Assemblies[/\\]Microsoft[/\\]Framework[/\\](?<type>.NETFramework)[/\\]v(?<version>[^/\\]+)[/\\])" +
@"|((?<type>Microsoft\.NET)[/\\]assembly[/\\]GAC_(MSIL|32|64)[/\\])" +
@"|((?<type>Microsoft\.NET)[/\\]Framework(64)?[/\\](?<version>[^/\\]+)[/\\])" +
@"|(NuGetFallbackFolder[/\\](?<type>[^/\\]+)\\(?<version>[^/\\]+)([/\\].*)?[/\\]ref[/\\])" +
@"|(shared[/\\](?<type>[^/\\]+)\\(?<version>[^/\\]+)([/\\].*)?[/\\])";
@"|(shared[/\\](?<type>[^/\\]+)\\(?<version>[^/\\]+)([/\\].*)?[/\\])" +
@"|(packs[/\\](?<type>[^/\\]+)\\(?<version>[^/\\]+)\\ref([/\\].*)?[/\\])";
static readonly string RefPathPattern =
@"(Reference Assemblies[/\\]Microsoft[/\\]Framework[/\\](?<type>.NETFramework)[/\\]v(?<version>[^/\\]+)[/\\])" +
@"|(NuGetFallbackFolder[/\\](?<type>[^/\\]+)\\(?<version>[^/\\]+)([/\\].*)?[/\\]ref[/\\])" +
@"|(packs[/\\](?<type>[^/\\]+)\\(?<version>[^/\\]+)\\ref([/\\].*)?[/\\])";
public static string DetectTargetFrameworkId(this PEFile assembly)
{
@ -49,44 +55,52 @@ namespace ICSharpCode.Decompiler.Metadata @@ -49,44 +55,52 @@ namespace ICSharpCode.Decompiler.Metadata
var reader = assembly.GetMetadataReader();
foreach (var h in reader.GetCustomAttributes(Handle.AssemblyDefinition)) {
var attribute = reader.GetCustomAttribute(h);
if (attribute.GetAttributeType(reader).GetFullTypeName(reader).ToString() != TargetFrameworkAttributeName)
continue;
var blobReader = reader.GetBlobReader(attribute.Value);
if (blobReader.ReadUInt16() == 0x0001) {
return blobReader.ReadSerializedString();
try {
var attribute = reader.GetCustomAttribute(h);
if (attribute.GetAttributeType(reader).GetFullTypeName(reader).ToString() != TargetFrameworkAttributeName)
continue;
var blobReader = reader.GetBlobReader(attribute.Value);
if (blobReader.ReadUInt16() == 0x0001) {
return blobReader.ReadSerializedString();
}
} catch (BadImageFormatException) {
// ignore malformed attributes
}
}
foreach (var h in reader.AssemblyReferences) {
var r = reader.GetAssemblyReference(h);
if (r.PublicKeyOrToken.IsNil)
continue;
string version;
switch (reader.GetString(r.Name)) {
case "netstandard":
version = r.Version.ToString(3);
return $".NETStandard,Version=v{version}";
case "System.Runtime":
// System.Runtime.dll uses the following scheme:
// 4.2.0 => .NET Core 2.0
// 4.2.1 => .NET Core 2.1 / 3.0
// 4.2.2 => .NET Core 3.1
if (r.Version >= new Version(4, 2, 0)) {
version = "2.0";
if (r.Version >= new Version(4, 2, 1)) {
version = "3.0";
}
if (r.Version >= new Version(4, 2, 2)) {
version = "3.1";
try {
var r = reader.GetAssemblyReference(h);
if (r.PublicKeyOrToken.IsNil)
continue;
string version;
switch (reader.GetString(r.Name)) {
case "netstandard":
version = r.Version.ToString(3);
return $".NETStandard,Version=v{version}";
case "System.Runtime":
// System.Runtime.dll uses the following scheme:
// 4.2.0 => .NET Core 2.0
// 4.2.1 => .NET Core 2.1 / 3.0
// 4.2.2 => .NET Core 3.1
if (r.Version >= new Version(4, 2, 0)) {
version = "2.0";
if (r.Version >= new Version(4, 2, 1)) {
version = "3.0";
}
if (r.Version >= new Version(4, 2, 2)) {
version = "3.1";
}
return $".NETCoreApp,Version=v{version}";
} else {
continue;
}
return $".NETCoreApp,Version=v{version}";
} else {
continue;
}
case "mscorlib":
version = r.Version.ToString(2);
return $".NETFramework,Version=v{version}";
case "mscorlib":
version = r.Version.ToString(2);
return $".NETFramework,Version=v{version}";
}
} catch (BadImageFormatException) {
// ignore malformed references
}
}
@ -99,7 +113,7 @@ namespace ICSharpCode.Decompiler.Metadata @@ -99,7 +113,7 @@ namespace ICSharpCode.Decompiler.Metadata
* - .NETCore -> C:\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.netcore.app\2.1.0\ref\netcoreapp2.1\System.Console.dll
* - .NETStandard -> C:\Program Files\dotnet\sdk\NuGetFallbackFolder\netstandard.library\2.0.3\build\netstandard2.0\ref\netstandard.dll
*/
var pathMatch = Regex.Match(assemblyPath, RefPathPattern,
var pathMatch = Regex.Match(assemblyPath, PathPattern,
RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.ExplicitCapture);
if (pathMatch.Success) {
var type = pathMatch.Groups["type"].Value;
@ -121,6 +135,25 @@ namespace ICSharpCode.Decompiler.Metadata @@ -121,6 +135,25 @@ namespace ICSharpCode.Decompiler.Metadata
return string.Empty;
}
public static bool IsReferenceAssembly(this PEFile assembly)
{
return IsReferenceAssembly(assembly.Reader, assembly.FileName);
}
public static bool IsReferenceAssembly(this PEReader assembly, string assemblyPath)
{
if (assembly == null)
throw new ArgumentNullException(nameof(assembly));
var metadata = assembly.GetMetadataReader();
if (metadata.GetCustomAttributes(Handle.AssemblyDefinition).HasKnownAttribute(metadata, KnownAttribute.ReferenceAssembly))
return true;
// Try to detect reference assembly through specific path pattern
var refPathMatch = Regex.Match(assemblyPath, RefPathPattern, RegexOptions.IgnoreCase | RegexOptions.Compiled);
return refPathMatch.Success;
}
}
public class ReferenceLoadInfo

4
ICSharpCode.Decompiler/Metadata/MetadataExtensions.cs

@ -95,6 +95,9 @@ namespace ICSharpCode.Decompiler.Metadata @@ -95,6 +95,9 @@ namespace ICSharpCode.Decompiler.Metadata
public static string ToHexString(this IEnumerable<byte> bytes, int estimatedLength)
{
if (bytes == null)
throw new ArgumentNullException(nameof(bytes));
StringBuilder sb = new StringBuilder(estimatedLength * 2);
foreach (var b in bytes)
sb.AppendFormat("{0:x2}", b);
@ -147,6 +150,7 @@ namespace ICSharpCode.Decompiler.Metadata @@ -147,6 +150,7 @@ namespace ICSharpCode.Decompiler.Metadata
return Disassembler.DisassemblerHelpers.Escape(name);
}
[Obsolete("Use MetadataModule.GetDeclaringModule() instead")]
public static IModuleReference GetDeclaringModule(this TypeReferenceHandle handle, MetadataReader reader)
{
var tr = reader.GetTypeReference(handle);

4
ICSharpCode.Decompiler/Metadata/PEFile.cs

@ -75,8 +75,12 @@ namespace ICSharpCode.Decompiler.Metadata @@ -75,8 +75,12 @@ namespace ICSharpCode.Decompiler.Metadata
public TargetRuntime GetRuntime()
{
string version = Metadata.MetadataVersion;
if (version == null || version.Length <= 1)
return TargetRuntime.Unknown;
switch (version[1]) {
case '1':
if (version.Length <= 3)
return TargetRuntime.Unknown;
if (version[3] == 1)
return TargetRuntime.Net_1_0;
else

33
ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs

@ -92,16 +92,19 @@ namespace ICSharpCode.Decompiler.Metadata @@ -92,16 +92,19 @@ namespace ICSharpCode.Decompiler.Metadata
public UniversalAssemblyResolver(string mainAssemblyFileName, bool throwOnError, string targetFramework,
PEStreamOptions streamOptions = PEStreamOptions.Default, MetadataReaderOptions metadataOptions = MetadataReaderOptions.Default)
{
this.mainAssemblyFileName = mainAssemblyFileName;
this.throwOnError = throwOnError;
this.streamOptions = streamOptions;
this.metadataOptions = metadataOptions;
this.targetFramework = targetFramework ?? string.Empty;
(targetFrameworkIdentifier, targetFrameworkVersion) = ParseTargetFramework(this.targetFramework);
this.mainAssemblyFileName = mainAssemblyFileName;
this.baseDirectory = Path.GetDirectoryName(mainAssemblyFileName);
this.throwOnError = throwOnError;
if (string.IsNullOrWhiteSpace(this.baseDirectory))
this.baseDirectory = Environment.CurrentDirectory;
AddSearchDirectory(baseDirectory);
if (mainAssemblyFileName != null) {
string baseDirectory = Path.GetDirectoryName(mainAssemblyFileName);
if (string.IsNullOrWhiteSpace(this.baseDirectory))
this.baseDirectory = Environment.CurrentDirectory;
AddSearchDirectory(baseDirectory);
}
}
internal static (TargetFrameworkIdentifier, Version) ParseTargetFramework(string targetFramework)
@ -191,7 +194,10 @@ namespace ICSharpCode.Decompiler.Metadata @@ -191,7 +194,10 @@ namespace ICSharpCode.Decompiler.Metadata
if (IsZeroOrAllOnes(targetFrameworkVersion))
goto default;
if (dotNetCorePathFinder == null) {
dotNetCorePathFinder = new DotNetCorePathFinder(mainAssemblyFileName, targetFramework, targetFrameworkIdentifier, targetFrameworkVersion);
if (mainAssemblyFileName == null)
dotNetCorePathFinder = new DotNetCorePathFinder(targetFrameworkIdentifier, targetFrameworkVersion);
else
dotNetCorePathFinder = new DotNetCorePathFinder(mainAssemblyFileName, targetFramework, targetFrameworkIdentifier, targetFrameworkVersion);
foreach (var directory in directories) {
dotNetCorePathFinder.AddSearchDirectory(directory);
}
@ -327,9 +333,13 @@ namespace ICSharpCode.Decompiler.Metadata @@ -327,9 +333,13 @@ namespace ICSharpCode.Decompiler.Metadata
if (assembly != null)
return assembly;
assembly = SearchDirectory(name, framework_dirs);
if (assembly != null)
return assembly;
// when decompiling assemblies that target frameworks prior to 4.0, we can fall back to the 4.0 assemblies in case the target framework is not installed.
// but when looking for Microsoft.Build.Framework, Version=15.0.0.0 we should not use the version 4.0 assembly here so that the LoadedAssembly logic can instead fall back to version 15.1.0.0
if (name.Version <= new Version(4, 0, 0, 0)) {
assembly = SearchDirectory(name, framework_dirs);
if (assembly != null)
return assembly;
}
if (throwOnError)
throw new AssemblyResolutionException(name);
@ -388,6 +398,9 @@ namespace ICSharpCode.Decompiler.Metadata @@ -388,6 +398,9 @@ namespace ICSharpCode.Decompiler.Metadata
return typeof(object).Module.FullyQualifiedName;
}
if (reference.PublicKeyToken == null)
return null;
string path;
if (decompilerRuntime == DecompilerRuntime.Mono) {
path = GetMonoMscorlibBasePath(version);

63
ICSharpCode.Decompiler/Output/TextTokenWriter.cs

@ -19,6 +19,7 @@ @@ -19,6 +19,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.CSharp.OutputVisitor;
using ICSharpCode.Decompiler.CSharp.Resolver;
@ -26,6 +27,7 @@ using ICSharpCode.Decompiler.CSharp.Syntax; @@ -26,6 +27,7 @@ using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.IL;
using ICSharpCode.Decompiler.Semantics;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
namespace ICSharpCode.Decompiler
{
@ -39,7 +41,7 @@ namespace ICSharpCode.Decompiler @@ -39,7 +41,7 @@ namespace ICSharpCode.Decompiler
bool inDocumentationComment = false;
bool firstUsingDeclaration;
bool lastUsingDeclaration;
public TextTokenWriter(ITextOutput output, DecompilerSettings settings, IDecompilerTypeSystem typeSystem)
{
if (output == null)
@ -52,13 +54,13 @@ namespace ICSharpCode.Decompiler @@ -52,13 +54,13 @@ namespace ICSharpCode.Decompiler
this.settings = settings;
this.typeSystem = typeSystem;
}
public override void WriteIdentifier(Identifier identifier)
{
if (identifier.IsVerbatim || CSharpOutputVisitor.IsKeyword(identifier.Name, identifier)) {
output.Write('@');
}
var definition = GetCurrentDefinition();
string name = TextWriterTokenWriter.EscapeIdentifier(identifier.Name);
switch (definition) {
@ -69,7 +71,7 @@ namespace ICSharpCode.Decompiler @@ -69,7 +71,7 @@ namespace ICSharpCode.Decompiler
output.WriteReference(m, name, true);
return;
}
var member = GetCurrentMemberReference();
switch (member) {
case IType t:
@ -110,6 +112,7 @@ namespace ICSharpCode.Decompiler @@ -110,6 +112,7 @@ namespace ICSharpCode.Decompiler
if (symbol != null && node.Role == Roles.Type && node.Parent is ObjectCreateExpression) {
symbol = node.Parent.GetSymbol();
}
if (node is IdentifierExpression && node.Role == Roles.TargetExpression && node.Parent is InvocationExpression && symbol is IMember member) {
var declaringType = member.DeclaringType;
if (declaringType != null && declaringType.Kind == TypeKind.Delegate)
@ -123,10 +126,8 @@ namespace ICSharpCode.Decompiler @@ -123,10 +126,8 @@ namespace ICSharpCode.Decompiler
if (symbol == null)
return null;
//if (settings.AutomaticEvents && member is FieldDefinition) {
// var field = (FieldDefinition)member;
// return field.DeclaringType.Events.FirstOrDefault(ev => ev.Name == field.Name) ?? member;
//}
if (symbol is LocalFunctionMethod)
return null;
return symbol;
}
@ -142,14 +143,18 @@ namespace ICSharpCode.Decompiler @@ -142,14 +143,18 @@ namespace ICSharpCode.Decompiler
if (letClauseVariable != null)
return letClauseVariable;
var gotoStatement = node as GotoStatement;
if (gotoStatement != null)
{
if (node is GotoStatement gotoStatement) {
var method = nodeStack.Select(nd => nd.GetSymbol() as IMethod).FirstOrDefault(mr => mr != null);
if (method != null)
return method + gotoStatement.Label;
}
if (node.Role == Roles.TargetExpression && node.Parent is InvocationExpression) {
var symbol = node.Parent.GetSymbol();
if (symbol is LocalFunctionMethod)
return symbol;
}
return null;
}
@ -177,29 +182,29 @@ namespace ICSharpCode.Decompiler @@ -177,29 +182,29 @@ namespace ICSharpCode.Decompiler
return method + label.Label;
}
if (node is LocalFunctionDeclarationStatement) {
var localFunction = node.GetResolveResult() as MemberResolveResult;
if (node is MethodDeclaration && node.Parent is LocalFunctionDeclarationStatement) {
var localFunction = node.Parent.GetResolveResult() as MemberResolveResult;
if (localFunction != null)
return localFunction.Member;
}
return null;
}
ISymbol GetCurrentDefinition()
{
if (nodeStack == null || nodeStack.Count == 0)
return null;
var node = nodeStack.Peek();
if (node is Identifier)
node = node.Parent;
if (IsDefinition(ref node))
return node.GetSymbol();
return null;
}
public override void WriteKeyword(Role role, string keyword)
{
//To make reference for 'this' and 'base' keywords in the ClassName():this() expression
@ -211,7 +216,7 @@ namespace ICSharpCode.Decompiler @@ -211,7 +216,7 @@ namespace ICSharpCode.Decompiler
}
output.Write(keyword);
}
public override void WriteToken(Role role, string token)
{
switch (token) {
@ -253,22 +258,22 @@ namespace ICSharpCode.Decompiler @@ -253,22 +258,22 @@ namespace ICSharpCode.Decompiler
break;
}
}
public override void Space()
{
output.Write(' ');
}
public override void Indent()
{
output.Indent();
}
public override void Unindent()
{
output.Unindent();
}
public override void NewLine()
{
if (!firstUsingDeclaration && lastUsingDeclaration) {
@ -277,7 +282,7 @@ namespace ICSharpCode.Decompiler @@ -277,7 +282,7 @@ namespace ICSharpCode.Decompiler
}
output.WriteLine();
}
public override void WriteComment(CommentType commentType, string content)
{
switch (commentType) {
@ -309,7 +314,7 @@ namespace ICSharpCode.Decompiler @@ -309,7 +314,7 @@ namespace ICSharpCode.Decompiler
break;
}
}
public override void WritePreProcessorDirective(PreProcessorDirectiveType type, string argument)
{
// pre-processor directive must start on its own line
@ -321,7 +326,7 @@ namespace ICSharpCode.Decompiler @@ -321,7 +326,7 @@ namespace ICSharpCode.Decompiler
}
output.WriteLine();
}
public override void WritePrimitiveValue(object value, LiteralFormat format = LiteralFormat.None)
{
new TextWriterTokenWriter(new TextOutputWriter(output)).WritePrimitiveValue(value, format);
@ -376,7 +381,7 @@ namespace ICSharpCode.Decompiler @@ -376,7 +381,7 @@ namespace ICSharpCode.Decompiler
break;
}
}
public override void StartNode(AstNode node)
{
if (nodeStack.Count == 0) {
@ -390,7 +395,7 @@ namespace ICSharpCode.Decompiler @@ -390,7 +395,7 @@ namespace ICSharpCode.Decompiler
}
nodeStack.Push(node);
}
private bool IsUsingDeclaration(AstNode node)
{
return node is UsingDeclaration || node is UsingAliasDeclaration;
@ -401,10 +406,10 @@ namespace ICSharpCode.Decompiler @@ -401,10 +406,10 @@ namespace ICSharpCode.Decompiler
if (nodeStack.Pop() != node)
throw new InvalidOperationException();
}
public static bool IsDefinition(ref AstNode node)
{
if (node is EntityDeclaration)
if (node is EntityDeclaration && !(node.Parent is LocalFunctionDeclarationStatement))
return true;
if (node is VariableInitializer && node.Parent is FieldDeclaration) {
node = node.Parent;

18
ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs

@ -224,24 +224,26 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -224,24 +224,26 @@ namespace ICSharpCode.Decompiler.TypeSystem
var mainModuleWithOptions = mainModule.WithOptions(typeSystemOptions);
var referencedAssembliesWithOptions = referencedAssemblies.Select(file => file.WithOptions(typeSystemOptions));
// Primitive types are necessary to avoid assertions in ILReader.
// Fallback to MinimalCorlib to provide the primitive types.
if (!HasType(KnownTypeCode.Void) || !HasType(KnownTypeCode.Int32)) {
Init(mainModule.WithOptions(typeSystemOptions), referencedAssembliesWithOptions.Concat(new[] { MinimalCorlib.Instance }));
// Other known types are necessary in order for transforms to work (e.g. Task<T> for async transform).
// Figure out which known types are missing from our type system so far:
var missingKnownTypes = KnownTypeReference.AllKnownTypes.Where(IsMissing).ToList();
if (missingKnownTypes.Count > 0) {
Init(mainModule.WithOptions(typeSystemOptions), referencedAssembliesWithOptions.Concat(new[] { MinimalCorlib.CreateWithTypes(missingKnownTypes) }));
} else {
Init(mainModuleWithOptions, referencedAssembliesWithOptions);
}
this.MainModule = (MetadataModule)base.MainModule;
bool HasType(KnownTypeCode code)
bool IsMissing(KnownTypeReference knownType)
{
TopLevelTypeName name = KnownTypeReference.Get(code).TypeName;
var name = knownType.TypeName;
if (!mainModule.GetTypeDefinition(name).IsNil)
return true;
return false;
foreach (var file in referencedAssemblies) {
if (!file.GetTypeDefinition(name).IsNil)
return true;
return false;
}
return false;
return true;
}
}

6
ICSharpCode.Decompiler/TypeSystem/IMethod.cs

@ -40,6 +40,12 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -40,6 +40,12 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// </summary>
bool ReturnTypeIsRefReadOnly { get; }
/// <summary>
/// Gets whether this method may only be called on fresh instances.
/// Used with C# 9 `init;` property setters.
/// </summary>
bool IsInitOnly { get; }
/// <summary>
/// Gets whether the method accepts the 'this' reference as ref readonly.
/// This can be either because the method is C# 8.0 'readonly', or because it is within a C# 7.2 'readonly struct'

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

@ -134,6 +134,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -134,6 +134,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
IEnumerable<IAttribute> IMethod.GetReturnTypeAttributes() => EmptyList<IAttribute>.Instance;
bool IMethod.ReturnTypeIsRefReadOnly => false;
bool IMethod.ThisIsRefReadOnly => false;
bool IMethod.IsInitOnly => false;
public IReadOnlyList<ITypeParameter> TypeParameters { get; set; } = EmptyList<ITypeParameter>.Instance;

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

@ -84,9 +84,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -84,9 +84,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
/// </summary>
public FullTypeName FullTypeName { get { return fullTypeName; } }
IType ResolveInAllAssemblies(ITypeResolveContext context)
internal static IType ResolveInAllAssemblies(ICompilation compilation, in FullTypeName fullTypeName)
{
var compilation = context.Compilation;
foreach (var asm in compilation.Modules) {
IType type = asm.GetTypeDefinition(fullTypeName);
if (type != null)
@ -107,7 +106,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -107,7 +106,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
type = context.CurrentModule.GetTypeDefinition(fullTypeName);
}
if (type == null) {
type = ResolveInAllAssemblies(context);
type = ResolveInAllAssemblies(context.Compilation, in fullTypeName);
}
} else {
// Assembly specified: only look in the specified assembly.
@ -118,7 +117,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -118,7 +117,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
if (asm != null) {
type = asm.GetTypeDefinition(fullTypeName);
} else {
type = ResolveInAllAssemblies(context);
type = ResolveInAllAssemblies(context.Compilation, in fullTypeName);
}
}
return type ?? new UnknownType(fullTypeName, isReferenceType);

41
ICSharpCode.Decompiler/TypeSystem/Implementation/LocalFunctionMethod.cs

@ -31,11 +31,13 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -31,11 +31,13 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
{
readonly IMethod baseMethod;
public LocalFunctionMethod(IMethod baseMethod, int numberOfCompilerGeneratedParameters, int numberOfCompilerGeneratedTypeParameters)
public LocalFunctionMethod(IMethod baseMethod, string name, bool isStaticLocalFunction, int numberOfCompilerGeneratedParameters, int numberOfCompilerGeneratedTypeParameters)
{
if (baseMethod == null)
throw new ArgumentNullException(nameof(baseMethod));
this.baseMethod = baseMethod;
this.Name = name;
this.IsStaticLocalFunction = isStaticLocalFunction;
this.NumberOfCompilerGeneratedParameters = numberOfCompilerGeneratedParameters;
this.NumberOfCompilerGeneratedTypeParameters = numberOfCompilerGeneratedTypeParameters;
}
@ -46,7 +48,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -46,7 +48,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
return false;
return baseMethod.Equals(other.baseMethod, typeNormalization)
&& NumberOfCompilerGeneratedParameters == other.NumberOfCompilerGeneratedParameters
&& NumberOfCompilerGeneratedTypeParameters == other.NumberOfCompilerGeneratedTypeParameters;
&& NumberOfCompilerGeneratedTypeParameters == other.NumberOfCompilerGeneratedTypeParameters
&& IsStaticLocalFunction == other.IsStaticLocalFunction;
}
public override bool Equals(object obj)
@ -55,7 +58,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -55,7 +58,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
return false;
return baseMethod.Equals(other.baseMethod)
&& NumberOfCompilerGeneratedParameters == other.NumberOfCompilerGeneratedParameters
&& NumberOfCompilerGeneratedTypeParameters == other.NumberOfCompilerGeneratedTypeParameters;
&& NumberOfCompilerGeneratedTypeParameters == other.NumberOfCompilerGeneratedTypeParameters
&& IsStaticLocalFunction == other.IsStaticLocalFunction;
}
public override int GetHashCode()
@ -65,14 +69,14 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -65,14 +69,14 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public override string ToString()
{
return string.Format("[LocalFunctionMethod: ReducedFrom={0}, NumberOfGeneratedParameters={1}, NumberOfCompilerGeneratedTypeParameters={2}]", ReducedFrom, NumberOfCompilerGeneratedParameters, NumberOfCompilerGeneratedTypeParameters);
return string.Format("[LocalFunctionMethod: ReducedFrom={0}, Name={1}, NumberOfGeneratedParameters={2}, NumberOfCompilerGeneratedTypeParameters={3}, IsStaticLocalFunction={4}]", ReducedFrom, Name, NumberOfCompilerGeneratedParameters, NumberOfCompilerGeneratedTypeParameters, IsStaticLocalFunction);
}
internal int NumberOfCompilerGeneratedParameters { get; }
internal int NumberOfCompilerGeneratedTypeParameters { get; }
internal bool IsStaticLocalFunction => NumberOfCompilerGeneratedParameters == 0 && (baseMethod.IsStatic || (baseMethod.DeclaringTypeDefinition.IsCompilerGenerated() && !baseMethod.DeclaringType.GetFields(f => !f.IsStatic).Any()));
internal bool IsStaticLocalFunction { get; }
public IMember MemberDefinition => this;
@ -88,7 +92,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -88,7 +92,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
{
return new LocalFunctionMethod(
baseMethod.Specialize(substitution),
NumberOfCompilerGeneratedParameters, NumberOfCompilerGeneratedTypeParameters);
Name, IsStaticLocalFunction, NumberOfCompilerGeneratedParameters, NumberOfCompilerGeneratedTypeParameters);
}
IMember IMember.Specialize(TypeParameterSubstitution substitution)
@ -96,7 +100,6 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -96,7 +100,6 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
return Specialize(substitution);
}
public IReadOnlyList<ITypeParameter> TypeParameters => baseMethod.TypeParameters;
public bool IsExtensionMethod => baseMethod.IsExtensionMethod;
public bool IsLocalFunction => true;
public bool IsConstructor => baseMethod.IsConstructor;
@ -107,7 +110,24 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -107,7 +110,24 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public IMember AccessorOwner => baseMethod.AccessorOwner;
public MethodSemanticsAttributes AccessorKind => baseMethod.AccessorKind;
public IMethod ReducedFrom => baseMethod;
public IReadOnlyList<IType> TypeArguments => baseMethod.TypeArguments;
List<ITypeParameter> typeParameters;
public IReadOnlyList<ITypeParameter> TypeParameters {
get {
if (typeParameters == null)
typeParameters = new List<ITypeParameter>(baseMethod.TypeParameters.Skip(NumberOfCompilerGeneratedTypeParameters));
return typeParameters;
}
}
List<IType> typeArguments;
public IReadOnlyList<IType> TypeArguments {
get {
if (typeArguments == null)
typeArguments = new List<IType>(baseMethod.TypeArguments.Skip(NumberOfCompilerGeneratedTypeParameters));
return typeArguments;
}
}
List<IParameter> parameters;
public IReadOnlyList<IParameter> Parameters {
@ -127,6 +147,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -127,6 +147,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
IEnumerable<IAttribute> IMethod.GetReturnTypeAttributes() => baseMethod.GetReturnTypeAttributes();
bool IMethod.ReturnTypeIsRefReadOnly => baseMethod.ReturnTypeIsRefReadOnly;
bool IMethod.ThisIsRefReadOnly => baseMethod.ThisIsRefReadOnly;
bool IMethod.IsInitOnly => baseMethod.IsInitOnly;
/// <summary>
/// We consider local functions as always static, because they do not have a "this parameter".
/// Even local functions in instance methods capture this.
@ -137,8 +158,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -137,8 +158,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public Accessibility Accessibility => baseMethod.Accessibility;
public string FullName => baseMethod.FullName;
public string Name => baseMethod.Name;
public string FullName => Name;
public string Name { get; set; }
public string ReflectionName => baseMethod.ReflectionName;
public string Namespace => baseMethod.Namespace;

22
ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs

@ -50,6 +50,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -50,6 +50,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
IType returnType;
byte returnTypeIsRefReadonly = ThreeState.Unknown;
byte thisIsRefReadonly = ThreeState.Unknown;
bool isInitOnly;
internal MetadataMethod(MetadataModule module, MethodDefinitionHandle handle)
{
@ -150,6 +151,15 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -150,6 +151,15 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
}
}
public bool IsInitOnly {
get {
var returnType = LazyInit.VolatileRead(ref this.returnType);
if (returnType == null)
DecodeSignature();
return this.isInitOnly;
}
}
internal Nullability NullableContext {
get {
var methodDef = module.metadata.GetMethodDefinition(handle);
@ -163,19 +173,23 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -163,19 +173,23 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
var genericContext = new GenericContext(DeclaringType.TypeParameters, this.TypeParameters);
IType returnType;
IParameter[] parameters;
ModifiedType mod;
try {
var nullableContext = methodDef.GetCustomAttributes().GetNullableContext(module.metadata) ?? DeclaringTypeDefinition.NullableContext;
var signature = methodDef.DecodeSignature(module.TypeProvider, genericContext);
(returnType, parameters) = DecodeSignature(module, this, signature, methodDef.GetParameters(), nullableContext, module.OptionsForEntity(this));
(returnType, parameters, mod) = DecodeSignature(module, this, signature, methodDef.GetParameters(), nullableContext, module.OptionsForEntity(this));
} catch (BadImageFormatException) {
returnType = SpecialType.UnknownType;
parameters = Empty<IParameter>.Array;
mod = null;
}
this.isInitOnly = mod is { Modifier: { Name: "IsExternalInit", Namespace: "System.Runtime.CompilerServices" } };
LazyInit.GetOrSet(ref this.returnType, returnType);
LazyInit.GetOrSet(ref this.parameters, parameters);
}
internal static (IType, IParameter[]) DecodeSignature(MetadataModule module, IParameterizedMember owner,
internal static (IType returnType, IParameter[] parameters, ModifiedType returnTypeModifier) DecodeSignature(
MetadataModule module, IParameterizedMember owner,
MethodSignature<IType> signature, ParameterHandleCollection? parameterHandles,
Nullability nullableContext, TypeSystemOptions typeSystemOptions,
CustomAttributeHandleCollection? returnTypeAttributes = null)
@ -231,7 +245,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -231,7 +245,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
Debug.Assert(i == parameters.Length);
var returnType = ApplyAttributeTypeVisitor.ApplyAttributesToType(signature.ReturnType,
module.Compilation, returnTypeAttributes, metadata, typeSystemOptions, nullableContext);
return (returnType, parameters);
return (returnType, parameters, signature.ReturnType as ModifiedType);
}
#endregion
@ -453,7 +467,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -453,7 +467,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
#endregion
public Accessibility Accessibility => GetAccessibility(attributes);
internal static Accessibility GetAccessibility(MethodAttributes attr)
{
switch (attr & MethodAttributes.MemberAccessMask) {

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

@ -153,7 +153,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -153,7 +153,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
// Roslyn uses the same workaround (see the NullableTypeDecoder.TransformType
// call in PEPropertySymbol).
var typeOptions = module.OptionsForEntity(declTypeDef);
(returnType, parameters) = MetadataMethod.DecodeSignature(module, this, signature,
(returnType, parameters, _) = MetadataMethod.DecodeSignature(
module, this, signature,
parameterHandles, nullableContext, typeOptions,
returnTypeAttributes: propertyDef.GetCustomAttributes());
} catch (BadImageFormatException) {

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

@ -31,22 +31,25 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -31,22 +31,25 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
/// </summary>
public sealed class MinimalCorlib : IModule
{
public static readonly IModuleReference Instance = new CorlibModuleReference();
/// <summary>
/// Minimal corlib instance containing all known types.
/// </summary>
public static readonly IModuleReference Instance = new CorlibModuleReference(KnownTypeReference.AllKnownTypes);
public static IModuleReference CreateWithTypes(IEnumerable<KnownTypeReference> types)
{
return new CorlibModuleReference(types);
}
public ICompilation Compilation { get; }
CorlibTypeDefinition[] typeDefinitions;
readonly CorlibNamespace rootNamespace;
private MinimalCorlib(ICompilation compilation)
private MinimalCorlib(ICompilation compilation, IEnumerable<KnownTypeReference> types)
{
this.Compilation = compilation;
this.typeDefinitions = new CorlibTypeDefinition[KnownTypeReference.KnownTypeCodeCount];
this.typeDefinitions = types.Select(ktr => new CorlibTypeDefinition(this, ktr.KnownTypeCode)).ToArray();
this.rootNamespace = new CorlibNamespace(this, null, string.Empty, string.Empty);
for (int i = 0; i < KnownTypeReference.KnownTypeCodeCount; i++) {
if (KnownTypeReference.Get((KnownTypeCode)i) != null) {
typeDefinitions[i] = new CorlibTypeDefinition(this, (KnownTypeCode)i);
}
}
}
bool IModule.IsMainModule => Compilation.MainModule == this;
@ -65,7 +68,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -65,7 +68,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public ITypeDefinition GetTypeDefinition(TopLevelTypeName topLevelTypeName)
{
foreach (var typeDef in typeDefinitions) {
if (typeDef != null && typeDef.FullTypeName == topLevelTypeName)
if (typeDef.FullTypeName == topLevelTypeName)
return typeDef;
}
return null;
@ -81,9 +84,16 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -81,9 +84,16 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
sealed class CorlibModuleReference : IModuleReference
{
readonly IEnumerable<KnownTypeReference> types;
public CorlibModuleReference(IEnumerable<KnownTypeReference> types)
{
this.types = types;
}
IModule IModuleReference.Resolve(ITypeResolveContext context)
{
return new MinimalCorlib(context.Compilation);
return new MinimalCorlib(context.Compilation, types);
}
}
@ -196,7 +206,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -196,7 +206,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
get {
var baseType = KnownTypeReference.Get(typeCode).baseType;
if (baseType != KnownTypeCode.None)
return new[] { corlib.typeDefinitions[(int)baseType] };
return new[] { corlib.Compilation.FindType(baseType) };
else
return EmptyList<IType>.Instance;
}

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

@ -98,6 +98,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -98,6 +98,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public bool ReturnTypeIsRefReadOnly => methodDefinition.ReturnTypeIsRefReadOnly;
bool IMethod.ThisIsRefReadOnly => methodDefinition.ThisIsRefReadOnly;
bool IMethod.IsInitOnly => methodDefinition.IsInitOnly;
public IReadOnlyList<ITypeParameter> TypeParameters {
get {

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

@ -62,6 +62,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -62,6 +62,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
bool IMethod.ReturnTypeIsRefReadOnly => underlyingMethod.ReturnTypeIsRefReadOnly;
bool IMethod.ThisIsRefReadOnly => underlyingMethod.ThisIsRefReadOnly;
bool IMethod.IsInitOnly => underlyingMethod.IsInitOnly;
IReadOnlyList<ITypeParameter> IMethod.TypeParameters => EmptyList<ITypeParameter>.Instance;
IReadOnlyList<IType> IMethod.TypeArguments => EmptyList<IType>.Instance;

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

@ -86,7 +86,9 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -86,7 +86,9 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public override string ReflectionName {
get { return namespaceKnown ? fullTypeName.ReflectionName : "?"; }
}
public FullTypeName FullTypeName => fullTypeName;
public override int TypeParameterCount => fullTypeName.TypeParameterCount;
public override IReadOnlyList<ITypeParameter> TypeParameters => DummyTypeParameter.GetClassTypeParameterList(TypeParameterCount);
public override IReadOnlyList<IType> TypeArguments => TypeParameters;

12
ICSharpCode.Decompiler/TypeSystem/KnownTypeReference.cs

@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
namespace ICSharpCode.Decompiler.TypeSystem
{
@ -235,6 +236,17 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -235,6 +236,17 @@ namespace ICSharpCode.Decompiler.TypeSystem
return knownTypeReferences[(int)typeCode];
}
public static IEnumerable<KnownTypeReference> AllKnownTypes {
get {
for (int i = 0; i < KnownTypeCodeCount; i++) {
var ktr = Get((KnownTypeCode)i);
if (ktr == null)
continue;
yield return ktr;
}
}
}
readonly KnownTypeCode knownTypeCode;
readonly string namespaceName;
readonly string name;

60
ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs

@ -48,6 +48,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -48,6 +48,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
readonly MetadataMethod[] methodDefs;
readonly MetadataProperty[] propertyDefs;
readonly MetadataEvent[] eventDefs;
readonly IModule[] referencedAssemblies;
internal MetadataModule(ICompilation compilation, Metadata.PEFile peFile, TypeSystemOptions options)
{
@ -79,6 +80,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -79,6 +80,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
this.methodDefs = new MetadataMethod[metadata.MethodDefinitions.Count + 1];
this.propertyDefs = new MetadataProperty[metadata.PropertyDefinitions.Count + 1];
this.eventDefs = new MetadataEvent[metadata.EventDefinitions.Count + 1];
this.referencedAssemblies = new IModule[metadata.AssemblyReferences.Count + 1];
}
}
@ -270,6 +272,64 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -270,6 +272,64 @@ namespace ICSharpCode.Decompiler.TypeSystem
}
#endregion
#region Resolve Module
public IModule ResolveModule(AssemblyReferenceHandle handle)
{
if (handle.IsNil)
return null;
if (referencedAssemblies == null)
return ResolveModuleUncached(handle);
int row = MetadataTokens.GetRowNumber(handle);
Debug.Assert(row != 0);
if (row >= referencedAssemblies.Length)
HandleOutOfRange(handle);
var module = LazyInit.VolatileRead(ref referencedAssemblies[row]);
if (module != null)
return module;
module = ResolveModuleUncached(handle);
return LazyInit.GetOrSet(ref referencedAssemblies[row], module);
}
IModule ResolveModuleUncached(AssemblyReferenceHandle handle)
{
var asmRef = new Metadata.AssemblyReference(metadata, handle);
return Compilation.FindModuleByReference(asmRef);
}
public IModule ResolveModule(ModuleReferenceHandle handle)
{
if (handle.IsNil)
return null;
var modRef = metadata.GetModuleReference(handle);
string name = metadata.GetString(modRef.Name);
foreach (var mod in Compilation.Modules) {
if (mod.Name == name) {
return mod;
}
}
return null;
}
public IModule GetDeclaringModule(TypeReferenceHandle handle)
{
if (handle.IsNil)
return null;
var tr = metadata.GetTypeReference(handle);
switch (tr.ResolutionScope.Kind) {
case HandleKind.TypeReference:
return GetDeclaringModule((TypeReferenceHandle)tr.ResolutionScope);
case HandleKind.AssemblyReference:
return ResolveModule((AssemblyReferenceHandle)tr.ResolutionScope);
case HandleKind.ModuleReference:
return ResolveModule((ModuleReferenceHandle)tr.ResolutionScope);
default:
return this;
}
}
#endregion
#region Resolve Type
public IType ResolveType(EntityHandle typeRefDefSpec, GenericContext context, CustomAttributeHandleCollection? typeAttributes = null, Nullability nullableContext = Nullability.Oblivious)
{

12
ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs

@ -131,9 +131,15 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -131,9 +131,15 @@ namespace ICSharpCode.Decompiler.TypeSystem
public IType GetTypeFromReference(SRM.MetadataReader reader, SRM.TypeReferenceHandle handle, byte rawTypeKind)
{
bool? isReferenceType = IsReferenceType(reader, handle, rawTypeKind);
var gctr = new GetClassTypeReference(handle.GetFullTypeName(reader), handle.GetDeclaringModule(reader), isReferenceType);
return gctr.Resolve(module != null ? new SimpleTypeResolveContext(module) : new SimpleTypeResolveContext(compilation));
IModule resolvedModule = module.GetDeclaringModule(handle);
var fullTypeName = handle.GetFullTypeName(reader);
IType type;
if (resolvedModule != null) {
type = resolvedModule.GetTypeDefinition(fullTypeName);
} else {
type = GetClassTypeReference.ResolveInAllAssemblies(compilation, in fullTypeName);
}
return type ?? new UnknownType(fullTypeName, IsReferenceType(reader, handle, rawTypeKind));
}
public IType GetTypeFromSerializedName(string name)

59
ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs

@ -50,7 +50,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -50,7 +50,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
collector.CollectBaseTypes(type);
return collector;
}
/// <summary>
/// Gets all non-interface base types.
/// </summary>
@ -69,7 +69,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -69,7 +69,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
return collector;
}
#endregion
#region GetAllBaseTypeDefinitions
/// <summary>
/// Gets all base type definitions.
@ -82,10 +82,10 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -82,10 +82,10 @@ namespace ICSharpCode.Decompiler.TypeSystem
{
if (type == null)
throw new ArgumentNullException(nameof(type));
return type.GetAllBaseTypes().Select(t => t.GetDefinition()).Where(d => d != null).Distinct();
}
/// <summary>
/// Gets whether this type definition is derived from the base type definition.
/// </summary>
@ -100,7 +100,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -100,7 +100,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
}
return type.GetAllBaseTypeDefinitions().Contains(baseType);
}
/// <summary>
/// Gets whether this type definition is derived from a given known type.
/// </summary>
@ -138,7 +138,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -138,7 +138,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
internal bool isOpen;
internal IEntity typeParameterOwner;
int typeParameterOwnerNestingLevel;
public override IType VisitTypeParameter(ITypeParameter type)
{
isOpen = true;
@ -151,7 +151,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -151,7 +151,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
}
return base.VisitTypeParameter(type);
}
static int GetNestingLevel(IEntity entity)
{
int level = 0;
@ -162,7 +162,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -162,7 +162,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
return level;
}
}
/// <summary>
/// Gets whether the type is an open type (contains type parameters).
/// </summary>
@ -184,7 +184,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -184,7 +184,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
type.AcceptVisitor(v);
return v.isOpen;
}
/// <summary>
/// Gets the entity that owns the type parameters occurring in the specified type.
/// If both class and method type parameters are present, the method is returned.
@ -199,7 +199,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -199,7 +199,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
type.AcceptVisitor(v);
return v.typeParameterOwner;
}
/// <summary>
/// Gets whether the type is unbound (is a generic type, but no type arguments were provided).
/// </summary>
@ -214,7 +214,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -214,7 +214,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
throw new ArgumentNullException(nameof(type));
return (type is ITypeDefinition || type is UnknownType) && type.TypeParameterCount > 0;
}
/// <summary>
/// Gets whether the type is the specified known type.
/// For generic known types, this returns true for any parameterization of the type (and also for the definition itself).
@ -273,7 +273,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -273,7 +273,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
return null;
}
#endregion
public static IType SkipModifiers(this IType ty)
{
while (ty is ModifiedType mt) {
@ -292,7 +292,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -292,7 +292,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// Gets all type definitions in the compilation.
/// This may include types from referenced assemblies that are not accessible in the main assembly.
/// </summary>
public static IEnumerable<ITypeDefinition> GetAllTypeDefinitions (this ICompilation compilation)
public static IEnumerable<ITypeDefinition> GetAllTypeDefinitions(this ICompilation compilation)
{
return compilation.Modules.SelectMany(a => a.TypeDefinitions);
}
@ -301,12 +301,12 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -301,12 +301,12 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// Gets all top level type definitions in the compilation.
/// This may include types from referenced assemblies that are not accessible in the main assembly.
/// </summary>
public static IEnumerable<ITypeDefinition> GetTopLevelTypeDefinitions (this ICompilation compilation)
public static IEnumerable<ITypeDefinition> GetTopLevelTypeDefinitions(this ICompilation compilation)
{
return compilation.Modules.SelectMany(a => a.TopLevelTypeDefinitions);
}
#endregion
#region Resolve on collections
public static IReadOnlyList<IType> Resolve(this IList<ITypeReference> typeReferences, ITypeResolveContext context)
{
@ -317,11 +317,11 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -317,11 +317,11 @@ namespace ICSharpCode.Decompiler.TypeSystem
else
return new ProjectedList<ITypeResolveContext, ITypeReference, IType>(context, typeReferences, (c, t) => t.Resolve(c));
}
// There is intentionally no Resolve() overload for IList<IMemberReference>: the resulting IList<Member> would
// contains nulls when there are resolve errors.
#endregion
#region IAssembly.GetTypeDefinition()
/// <summary>
/// Retrieves the specified type in this compilation.
@ -345,7 +345,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -345,7 +345,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
}
return new UnknownType(fullTypeName);
}
/// <summary>
/// Gets the type definition for the specified unresolved type.
/// Returns null if the unresolved type does not belong to this assembly.
@ -368,7 +368,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -368,7 +368,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
}
return typeDef;
}
static ITypeDefinition FindNestedType(ITypeDefinition typeDef, string name, int typeParameterCount)
{
foreach (var nestedType in typeDef.NestedTypes) {
@ -378,7 +378,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -378,7 +378,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
return null;
}
#endregion
#region IEntity.GetAttribute
/// <summary>
/// Gets whether the entity has an attribute of the specified attribute type (or derived attribute types).
@ -390,7 +390,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -390,7 +390,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// (if the given <paramref name="entity"/> in an <c>override</c>)
/// should be returned.
/// </param>
public static bool HasAttribute(this IEntity entity, KnownAttribute attributeType, bool inherit=false)
public static bool HasAttribute(this IEntity entity, KnownAttribute attributeType, bool inherit = false)
{
return GetAttribute(entity, attributeType, inherit) != null;
}
@ -410,7 +410,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -410,7 +410,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// If inherit is true, an from the entity itself will be returned if possible;
/// and the base entity will only be searched if none exists.
/// </returns>
public static IAttribute GetAttribute(this IEntity entity, KnownAttribute attributeType, bool inherit=false)
public static IAttribute GetAttribute(this IEntity entity, KnownAttribute attributeType, bool inherit = false)
{
return GetAttributes(entity, inherit).FirstOrDefault(a => a.AttributeType.IsKnownType(attributeType));
}
@ -478,11 +478,11 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -478,11 +478,11 @@ namespace ICSharpCode.Decompiler.TypeSystem
public static ITypeDefinition GetTypeDefinition(this IModule module, string namespaceName, string name, int typeParameterCount = 0)
{
if (module == null)
throw new ArgumentNullException ("assembly");
return module.GetTypeDefinition (new TopLevelTypeName (namespaceName, name, typeParameterCount));
throw new ArgumentNullException("assembly");
return module.GetTypeDefinition(new TopLevelTypeName(namespaceName, name, typeParameterCount));
}
#endregion
#region ResolveResult
public static ISymbol GetSymbol(this ResolveResult rr)
{
@ -495,7 +495,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -495,7 +495,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
} else if (rr is ConversionResolveResult) {
return ((ConversionResolveResult)rr).Input.GetSymbol();
}
return null;
}
#endregion
@ -560,7 +560,12 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -560,7 +560,12 @@ namespace ICSharpCode.Decompiler.TypeSystem
public static IModule FindModuleByReference(this ICompilation compilation, IAssemblyReference assemblyName)
{
foreach (var module in compilation.Modules) {
if (module.FullAssemblyName == assemblyName.FullName) {
if (string.Equals(module.FullAssemblyName, assemblyName.FullName, StringComparison.OrdinalIgnoreCase)) {
return module;
}
}
foreach (var module in compilation.Modules) {
if (string.Equals(module.Name, assemblyName.Name, StringComparison.OrdinalIgnoreCase)) {
return module;
}
}

1
ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs

@ -115,6 +115,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -115,6 +115,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
IEnumerable<IAttribute> IMethod.GetReturnTypeAttributes() => baseMethod.GetReturnTypeAttributes();
bool IMethod.ReturnTypeIsRefReadOnly => baseMethod.ReturnTypeIsRefReadOnly;
bool IMethod.ThisIsRefReadOnly => baseMethod.ThisIsRefReadOnly;
bool IMethod.IsInitOnly => baseMethod.IsInitOnly;
public IReadOnlyList<ITypeParameter> TypeParameters {
get { return baseMethod.TypeParameters; }

8
ICSharpCode.Decompiler/Util/CollectionExtensions.cs

@ -12,10 +12,12 @@ namespace ICSharpCode.Decompiler.Util @@ -12,10 +12,12 @@ namespace ICSharpCode.Decompiler.Util
value = pair.Value;
}
#if !NETCORE
public static IEnumerable<(A, B)> Zip<A, B>(this IEnumerable<A> input1, IEnumerable<B> input2)
{
return input1.Zip(input2, (a, b) => (a, b));
}
#endif
public static IEnumerable<(A, B)> ZipLongest<A, B>(this IEnumerable<A> input1, IEnumerable<B> input2)
{
@ -51,10 +53,12 @@ namespace ICSharpCode.Decompiler.Util @@ -51,10 +53,12 @@ namespace ICSharpCode.Decompiler.Util
}
}
#if !NETCORE
public static HashSet<T> ToHashSet<T>(this IEnumerable<T> input)
{
return new HashSet<T>(input);
}
#endif
public static IEnumerable<T> SkipLast<T>(this IReadOnlyCollection<T> input, int count)
{
@ -323,7 +327,7 @@ namespace ICSharpCode.Decompiler.Util @@ -323,7 +327,7 @@ namespace ICSharpCode.Decompiler.Util
return first;
}
#region Aliases/shortcuts for Enumerable extension methods
#region Aliases/shortcuts for Enumerable extension methods
public static bool Any<T>(this ICollection<T> list) => list.Count > 0;
public static bool Any<T>(this T[] array, Predicate<T> match) => Array.Exists(array, match);
public static bool Any<T>(this List<T> list, Predicate<T> match) => list.Exists(match);
@ -335,6 +339,6 @@ namespace ICSharpCode.Decompiler.Util @@ -335,6 +339,6 @@ namespace ICSharpCode.Decompiler.Util
public static T FirstOrDefault<T>(this List<T> list, Predicate<T> predicate) => list.Find(predicate);
public static T Last<T>(this IList<T> list) => list[list.Count - 1];
#endregion
#endregion
}
}

23
ILSpy.AddIn/AssemblyFileFinder.cs

@ -1,32 +1,29 @@ @@ -1,32 +1,29 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Util;
using Mono.Cecil;
namespace ICSharpCode.ILSpy.AddIn
{
public class AssemblyFileFinder
{
public static string FindAssemblyFile(AssemblyDefinition assemblyDefinition, string assemblyFile)
public static string FindAssemblyFile(Mono.Cecil.AssemblyDefinition assemblyDefinition, string assemblyFile)
{
var assemblyResolver = new UniversalAssemblyResolver(assemblyFile, false,
DetectTargetFrameworkId(assemblyDefinition, assemblyFile));
string tfi = DetectTargetFrameworkId(assemblyDefinition, assemblyFile);
UniversalAssemblyResolver assemblyResolver;
if (IsReferenceAssembly(assemblyDefinition, assemblyFile)) {
assemblyResolver.RemoveSearchDirectory(Path.GetDirectoryName(assemblyFile));
assemblyResolver = new UniversalAssemblyResolver(null, throwOnError: false, tfi);
} else {
assemblyResolver = new UniversalAssemblyResolver(assemblyFile, throwOnError: false, tfi);
}
return assemblyResolver.FindAssemblyFile(
ICSharpCode.Decompiler.Metadata.AssemblyNameReference.Parse(assemblyDefinition.Name.FullName));
return assemblyResolver.FindAssemblyFile(AssemblyNameReference.Parse(assemblyDefinition.Name.FullName));
}
static readonly string RefPathPattern = @"NuGetFallbackFolder[/\\][^/\\]+[/\\][^/\\]+[/\\]ref[/\\]";
public static bool IsReferenceAssembly(AssemblyDefinition assemblyDef, string assemblyFile)
public static bool IsReferenceAssembly(Mono.Cecil.AssemblyDefinition assemblyDef, string assemblyFile)
{
if (assemblyDef.CustomAttributes.Any(ca => ca.AttributeType.FullName == "System.Runtime.CompilerServices.ReferenceAssemblyAttribute"))
return true;
@ -40,7 +37,7 @@ namespace ICSharpCode.ILSpy.AddIn @@ -40,7 +37,7 @@ namespace ICSharpCode.ILSpy.AddIn
@"(Reference Assemblies[/\\]Microsoft[/\\]Framework[/\\](?<1>.NETFramework)[/\\]v(?<2>[^/\\]+)[/\\])" +
@"|((NuGetFallbackFolder|packs)[/\\](?<1>[^/\\]+)\\(?<2>[^/\\]+)([/\\].*)?[/\\]ref[/\\])";
public static string DetectTargetFrameworkId(AssemblyDefinition assembly, string assemblyPath = null)
public static string DetectTargetFrameworkId(Mono.Cecil.AssemblyDefinition assembly, string assemblyPath = null)
{
if (assembly == null)
throw new ArgumentNullException(nameof(assembly));

14
ILSpy.AddIn/Commands/OpenCodeItemCommand.cs

@ -38,10 +38,7 @@ namespace ICSharpCode.ILSpy.AddIn.Commands @@ -38,10 +38,7 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
var document = owner.DTE.ActiveDocument;
menuItem.Visible =
(document != null) &&
(document.ProjectItem != null) &&
(document.ProjectItem.ContainingProject != null) &&
(document.ProjectItem.ContainingProject.ConfigurationManager != null) &&
(document?.ProjectItem?.ContainingProject?.ConfigurationManager != null) &&
!string.IsNullOrEmpty(document.ProjectItem.ContainingProject.FileName);
}
}
@ -51,7 +48,6 @@ namespace ICSharpCode.ILSpy.AddIn.Commands @@ -51,7 +48,6 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
ThreadHelper.ThrowIfNotOnUIThread();
var document = owner.DTE.ActiveDocument;
var selection = (EnvDTE.TextPoint)((EnvDTE.TextSelection)document.Selection).ActivePoint;
var id = owner.Workspace.CurrentSolution.GetDocumentIdsWithFilePath(document.FullName).FirstOrDefault();
if (id == null)
return null;
@ -59,14 +55,6 @@ namespace ICSharpCode.ILSpy.AddIn.Commands @@ -59,14 +55,6 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
return owner.Workspace.CurrentSolution.GetDocument(id);
}
EnvDTE.TextPoint GetEditorSelection()
{
ThreadHelper.ThrowIfNotOnUIThread();
var document = owner.DTE.ActiveDocument;
return ((EnvDTE.TextSelection)document.Selection).ActivePoint;
}
protected override async void OnExecute(object sender, EventArgs e)
{
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();

5
ILSpy.AddIn/Commands/OpenILSpyCommand.cs

@ -50,7 +50,7 @@ namespace ICSharpCode.ILSpy.AddIn.Commands @@ -50,7 +50,7 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
protected string GetILSpyPath()
{
var basePath = Path.GetDirectoryName(typeof(ILSpyAddInPackage).Assembly.Location);
return Path.Combine(basePath, "ILSpy.exe");
return Path.Combine(basePath, "ILSpy", "ILSpy.exe");
}
protected void OpenAssembliesInILSpy(ILSpyParameters parameters)
@ -80,8 +80,7 @@ namespace ICSharpCode.ILSpy.AddIn.Commands @@ -80,8 +80,7 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
using (var assemblyDef = AssemblyDefinition.ReadAssembly(reference.Display)) {
string assemblyName = assemblyDef.Name.Name;
string resolvedAssemblyFile = AssemblyFileFinder.FindAssemblyFile(assemblyDef, reference.Display);
dict.Add(assemblyName,
new DetectedReference(assemblyName, resolvedAssemblyFile, false));
dict.Add(assemblyName, new DetectedReference(assemblyName, resolvedAssemblyFile, false));
}
}
foreach (var projectReference in parentProject.ProjectReferences) {

84
ILSpy.AddIn/ILSpy.AddIn.csproj

@ -12,7 +12,6 @@ @@ -12,7 +12,6 @@
<Company>IC#Code</Company>
<Description>ILSpy</Description>
<NeutralLanguage>en-US</NeutralLanguage>
<Version>1.7.1.0</Version>
<FileVersion>1.7.1.0</FileVersion>
@ -51,10 +50,15 @@ @@ -51,10 +50,15 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ILSpy\ILSpy.csproj" />
<ProjectReference Include="..\ICSharpCode.Decompiler\ICSharpCode.Decompiler.csproj" />
<ProjectReference Include="..\ILSpy\ILSpy.csproj">
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
<Private>false</Private>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Compile Include="..\ILSpy\NativeMethods.cs" Link="NativeMethods.cs" />
<Compile Include="Commands\AssemblyReferenceForILSpy.cs" />
<Compile Include="Commands\NuGetReferenceForILSpy.cs" />
<Compile Include="Commands\ProjectItemForILSpy.cs" />
@ -85,10 +89,20 @@ @@ -85,10 +89,20 @@
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="VSPackage.en-US.resx">
<MergeWithCTO>true</MergeWithCTO>
<LogicalName>VSPackage.en-US.resources</LogicalName>
<DependentUpon>VSPackage.resx</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="VSPackage.resx">
<MergeWithCTO>true</MergeWithCTO>
<ManifestResourceName>VSPackage</ManifestResourceName>
</EmbeddedResource>
<EmbeddedResource Include="VSPackage.zh-Hans.resx">
<MergeWithCTO>true</MergeWithCTO>
<LogicalName>VSPackage.zh-Hans.resources</LogicalName>
<DependentUpon>VSPackage.resx</DependentUpon>
</EmbeddedResource>
</ItemGroup>
<!--
@ -101,33 +115,7 @@ @@ -101,33 +115,7 @@
</PropertyGroup>
<ItemGroup>
<AdditionalDependencies Include="$(ILSpyBuildPath)System.Collections.Immutable.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)System.Composition.AttributedModel.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)System.Composition.Convention.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)System.Composition.Hosting.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)System.Composition.Runtime.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)System.Composition.TypedParts.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)System.Threading.Tasks.Dataflow.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)ICSharpCode.AvalonEdit.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)Humanizer.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)System.Reflection.Metadata.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)Microsoft.VisualStudio.Composition.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)Microsoft.VisualStudio.Validation.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)Microsoft.Xaml.Behaviors.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)Mono.Cecil.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)Mono.Cecil.Pdb.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)ILSpy.BamlDecompiler.Plugin.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)Microsoft.DiaSymReader*.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)OSVersionHelper.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)Xceed.Wpf.AvalonDock.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)DataGridExtensions.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)Iced.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)ILCompiler.Reflection.ReadyToRun.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)ILSpy.ReadyToRun.Plugin.dll" />
</ItemGroup>
<ItemGroup>
<Content Include="@(AdditionalDependencies)">
<Content Include="$(OutputPath)Mono.Cecil.dll">
<IncludeInVSIX>true</IncludeInVSIX>
<VSIXSubPath>\</VSIXSubPath>
</Content>
@ -135,22 +123,46 @@ @@ -135,22 +123,46 @@
<!-- END WORKAROUND -->
<ItemGroup>
<None Include="ILSpyAddIn.vsct" />
<None Include="source.extension.vsixmanifest.template" />
<None Include="source.extension.vsixmanifest">
<SubType>Designer</SubType>
</None>
<Content Include="$(ILSpyBuildPath)*.dll;$(ILSpyBuildPath)ILSpy.exe;$(ILSpyBuildPath)ILSpy.exe.config">
<IncludeInVSIX>true</IncludeInVSIX>
<VSIXSubPath>\ILSpy</VSIXSubPath>
</Content>
</ItemGroup>
<ItemGroup>
<None Include="Key.snk" />
<Content Include="$(ILSpyBuildPath)zh-Hans\ILSpy.resources.dll;$(ILSpyBuildPath)zh-Hans\ILSpy.ReadyToRun.Plugin.resources.dll;">
<IncludeInVSIX>true</IncludeInVSIX>
<VSIXSubPath>\ILSpy\zh-Hans\</VSIXSubPath>
</Content>
</ItemGroup>
<ItemGroup>
<VSCTCompile Include="ILSpyAddIn.vsct">
<None Include="source.extension.vsixmanifest.template" />
<None Include="source.extension.vsixmanifest">
<SubType>Designer</SubType>
</None>
</ItemGroup>
<ItemGroup>
<None Include="ILSpyAddIn.vsct">
<SubType>Designer</SubType>
</None>
<Content Include="zh-Hans\extension.vsixlangpack">
<IncludeInVSIX>true</IncludeInVSIX>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<VSCTCompile Include="ILSpyAddIn.en-US.vsct">
<ResourceName>Menus.ctmenu</ResourceName>
<SubType>Designer</SubType>
<DependentUpon>ILSpyAddIn.vsct</DependentUpon>
</VSCTCompile>
<VSCTCompile Include="ILSpyAddIn.zh-Hans.vsct">
<ResourceName>Menus.ctmenu</ResourceName>
<SubType>Designer</SubType>
<DependentUpon>ILSpyAddIn.vsct</DependentUpon>
</VSCTCompile>
</ItemGroup>
<ItemGroup>
<None Include="Key.snk" />
</ItemGroup>
<ItemGroup>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save