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 @@
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 @@
$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:
- DotNetCore - DotNetCore
packages: packages:
- id: ICSharpCode.Decompiler - id: ICSharpCode.Decompiler
version: 6.0.0.5836 version: 6.1.0.5902
--- ---
Setup: load the references required to work with the decompiler Setup: load the references required to work with the decompiler

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

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

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

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

12
ICSharpCode.Decompiler.Tests/DataFlowTest.cs

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

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

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

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

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

4
ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs

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

15
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

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

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

@ -25,6 +25,47 @@ internal sealed class ExtraUnsafeTests
{ {
return (uint*)Unsafe.AsPointer(ref managedPtr); 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 namespace System.Runtime.CompilerServices
@ -228,7 +269,7 @@ namespace System.Runtime.CompilerServices
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IntPtr ByteOffset<T>(ref T origin, ref T target) 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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]

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

@ -474,4 +474,123 @@
ldarg.0 ldarg.0
ret 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
return () => valueTuple.RenamedString; return () => valueTuple.RenamedString;
} }
#endif #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 public class Issue1867

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

@ -23,6 +23,12 @@ namespace LocalFunctions
{ {
internal class LocalFunctions internal class LocalFunctions
{ {
[AttributeUsage(AttributeTargets.All)]
internal class MyAttribute : Attribute
{
}
public class Generic<T1> where T1 : struct, ICloneable, IConvertible public class Generic<T1> where T1 : struct, ICloneable, IConvertible
{ {
public int MixedLocalFunction<T2>() where T2 : ICloneable, IConvertible public int MixedLocalFunction<T2>() where T2 : ICloneable, IConvertible
@ -31,15 +37,21 @@ namespace LocalFunctions
object z = this; object z = this;
for (int j = 0; j < 10; j++) { for (int j = 0; j < 10; j++) {
int i = 0; int i = 0;
i += NonStaticMethod6<object>(); i += NonStaticMethod6<object>(0);
int NonStaticMethod6<T3>() #if CS90
[My]
[return: My]
int NonStaticMethod6<[My] T3>([My] int unused)
#else
int NonStaticMethod6<T3>(int unused)
#endif
{ {
t2 = default(T2); t2 = default(T2);
int l = 0; int l = 0;
return NonStaticMethod6_1<T1>() + NonStaticMethod6_1<T2>() + z.GetHashCode(); return NonStaticMethod6_1<T1>() + NonStaticMethod6_1<T2>() + z.GetHashCode();
int NonStaticMethod6_1<T4>() 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 @@
// 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
} }
Console.WriteLine("normal exit"); 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
typeSystemAstBuilder.AlwaysUseShortTypeNames = true; typeSystemAstBuilder.AlwaysUseShortTypeNames = true;
typeSystemAstBuilder.AddResolveResultAnnotations = true; typeSystemAstBuilder.AddResolveResultAnnotations = true;
typeSystemAstBuilder.UseNullableSpecifierForValueTypes = settings.LiftNullables; typeSystemAstBuilder.UseNullableSpecifierForValueTypes = settings.LiftNullables;
typeSystemAstBuilder.SupportInitAccessors = settings.InitAccessors;
return typeSystemAstBuilder; return typeSystemAstBuilder;
} }
@ -1346,30 +1347,35 @@ namespace ICSharpCode.Decompiler.CSharp
} }
entityDecl.AddAnnotation(function); entityDecl.AddAnnotation(function);
if (function.IsIterator) { CleanUpMethodDeclaration(entityDecl, body, function, localSettings.DecompileMemberBodies);
if (localSettings.DecompileMemberBodies && !body.Descendants.Any(d => d is YieldReturnStatement || d is YieldBreakStatement)) { } catch (Exception innerException) when (!(innerException is OperationCanceledException || innerException is DecompilerException)) {
body.Add(new YieldBreakStatement()); throw new DecompilerException(module, method, innerException);
} }
if (function.IsAsync) { }
RemoveAttribute(entityDecl, KnownAttribute.AsyncIteratorStateMachine);
} else { internal static void CleanUpMethodDeclaration(EntityDeclaration entityDecl, BlockStatement body, ILFunction function, bool decompileBody = true)
RemoveAttribute(entityDecl, KnownAttribute.IteratorStateMachine); {
} if (function.IsIterator) {
if (function.StateMachineCompiledWithMono) { if (decompileBody && !body.Descendants.Any(d => d is YieldReturnStatement || d is YieldBreakStatement)) {
RemoveAttribute(entityDecl, KnownAttribute.DebuggerHidden); body.Add(new YieldBreakStatement());
}
} }
if (function.IsAsync) { if (function.IsAsync) {
entityDecl.Modifiers |= Modifiers.Async; RemoveAttribute(entityDecl, KnownAttribute.AsyncIteratorStateMachine);
RemoveAttribute(entityDecl, KnownAttribute.AsyncStateMachine); } else {
RemoveAttribute(entityDecl, KnownAttribute.DebuggerStepThrough); RemoveAttribute(entityDecl, KnownAttribute.IteratorStateMachine);
} }
} catch (Exception innerException) when (!(innerException is OperationCanceledException || innerException is DecompilerException)) { if (function.StateMachineCompiledWithMono) {
throw new DecompilerException(module, method, innerException); 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; bool found = false;
foreach (var section in entityDecl.Attributes) { foreach (var section in entityDecl.Attributes) {

20
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

@ -197,8 +197,7 @@ namespace ICSharpCode.Decompiler.CSharp
} else if (localFunction != null) { } else if (localFunction != null) {
var ide = new IdentifierExpression(localFunction.Name); var ide = new IdentifierExpression(localFunction.Name);
if (method.TypeArguments.Count > 0) { if (method.TypeArguments.Count > 0) {
int skipCount = localFunction.ReducedMethod.NumberOfCompilerGeneratedTypeParameters; ide.TypeArguments.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType));
ide.TypeArguments.AddRange(method.TypeArguments.Skip(skipCount).Select(expressionBuilder.ConvertType));
} }
ide.AddAnnotation(localFunction); ide.AddAnnotation(localFunction);
target = ide.WithoutILInstruction() target = ide.WithoutILInstruction()
@ -812,7 +811,7 @@ namespace ICSharpCode.Decompiler.CSharp
// that are no longer required once we add the type arguments. // 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, // We lend overload resolution a hand by detecting such cases beforehand and requiring type arguments,
// if necessary. // if necessary.
if (!CanInferTypeArgumentsFromParameters(method, argumentList.Arguments.SelectArray(a => a.ResolveResult), expressionBuilder.typeInference)) { if (!CanInferTypeArgumentsFromArguments(method, argumentList, expressionBuilder.typeInference)) {
requireTypeArguments = true; requireTypeArguments = true;
typeArguments = method.TypeArguments.ToArray(); typeArguments = method.TypeArguments.ToArray();
appliedRequireTypeArgumentsShortcut = true; appliedRequireTypeArgumentsShortcut = true;
@ -896,14 +895,14 @@ namespace ICSharpCode.Decompiler.CSharp
return method.IsExtensionMethod && arguments.Count > 0 && arguments[0].Expression is NullReferenceExpression; return method.IsExtensionMethod && arguments.Count > 0 && arguments[0].Expression is NullReferenceExpression;
} }
public static bool CanInferTypeArgumentsFromParameters(IMethod method, IReadOnlyList<ResolveResult> arguments, static bool CanInferTypeArgumentsFromArguments(IMethod method, ArgumentList argumentList, TypeInference typeInference)
TypeInference typeInference)
{ {
if (method.TypeParameters.Count == 0) if (method.TypeParameters.Count == 0)
return true; return true;
// always use unspecialized member, otherwise type inference fails // always use unspecialized member, otherwise type inference fails
method = (IMethod)method.MemberDefinition; 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); out bool success);
return success; return success;
} }
@ -1327,7 +1326,6 @@ namespace ICSharpCode.Decompiler.CSharp
target = default; target = default;
targetType = default; targetType = default;
methodName = localFunction.Name; 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) { } else if (method.IsExtensionMethod && invokeMethod != null && method.Parameters.Count - 1 == invokeMethod.Parameters.Count) {
step = 5; step = 5;
targetType = method.Parameters[0].Type; targetType = method.Parameters[0].Type;
@ -1406,11 +1404,7 @@ namespace ICSharpCode.Decompiler.CSharp
} else { } else {
var ide = new IdentifierExpression(methodName); var ide = new IdentifierExpression(methodName);
if ((step & 2) != 0) { if ((step & 2) != 0) {
int skipCount = 0; ide.TypeArguments.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType));
if (localFunction != null && method.TypeArguments.Count > 0) {
skipCount = localFunction.ReducedMethod.NumberOfCompilerGeneratedTypeParameters;
}
ide.TypeArguments.AddRange(method.TypeArguments.Skip(skipCount).Select(expressionBuilder.ConvertType));
} }
targetExpression = ide.WithRR(result); targetExpression = ide.WithRR(result);
} }
@ -1469,7 +1463,7 @@ namespace ICSharpCode.Decompiler.CSharp
method.DeclaringType, method.DeclaringType,
new IParameterizedMember[] { method } 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
this.astBuilder = new TypeSystemAstBuilder(resolver); this.astBuilder = new TypeSystemAstBuilder(resolver);
this.astBuilder.AlwaysUseShortTypeNames = true; this.astBuilder.AlwaysUseShortTypeNames = true;
this.astBuilder.AddResolveResultAnnotations = true; this.astBuilder.AddResolveResultAnnotations = true;
this.astBuilder.ShowAttributes = true;
this.astBuilder.UseNullableSpecifierForValueTypes = settings.LiftNullables; this.astBuilder.UseNullableSpecifierForValueTypes = settings.LiftNullables;
this.typeInference = new TypeInference(compilation) { Algorithm = TypeInferenceAlgorithm.Improved }; this.typeInference = new TypeInference(compilation) { Algorithm = TypeInferenceAlgorithm.Improved };
} }
@ -639,6 +640,7 @@ namespace ICSharpCode.Decompiler.CSharp
if (lhs.Expression is DirectionExpression dirExpr && lhs.ResolveResult is ByReferenceResolveResult lhsRefRR) { if (lhs.Expression is DirectionExpression dirExpr && lhs.ResolveResult is ByReferenceResolveResult lhsRefRR) {
// ref (re-)assignment, emit "ref (a = ref b)". // ref (re-)assignment, emit "ref (a = ref b)".
lhs = lhs.UnwrapChild(dirExpr.Expression); lhs = lhs.UnwrapChild(dirExpr.Expression);
translatedValue = translatedValue.ConvertTo(lhsRefRR.Type, this, allowImplicitConversion: true);
var assign = new AssignmentExpression(lhs.Expression, translatedValue.Expression) var assign = new AssignmentExpression(lhs.Expression, translatedValue.Expression)
.WithRR(new OperatorResolveResult(lhs.Type, ExpressionType.Assign, lhsRefRR, translatedValue.ResolveResult)); .WithRR(new OperatorResolveResult(lhs.Type, ExpressionType.Assign, lhsRefRR, translatedValue.ResolveResult));
return new DirectionExpression(FieldDirection.Ref, assign) return new DirectionExpression(FieldDirection.Ref, assign)
@ -1075,7 +1077,11 @@ namespace ICSharpCode.Decompiler.CSharp
return null; return null;
if (inst.Operator == BinaryNumericOperator.Sub && inst.LeftInputType == StackType.Ref && inst.RightInputType == StackType.Ref) { if (inst.Operator == BinaryNumericOperator.Sub && inst.LeftInputType == StackType.Ref && inst.RightInputType == StackType.Ref) {
// ref - ref => i // 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() if (inst.LeftInputType == StackType.Ref && inst.RightInputType.IsIntegerType()
&& left.Type is ByReferenceType brt) { && left.Type is ByReferenceType brt) {
@ -1788,6 +1794,15 @@ namespace ICSharpCode.Decompiler.CSharp
// Case 4 (left-over extension from implicit conversion) can also be handled by our caller. // Case 4 (left-over extension from implicit conversion) can also be handled by our caller.
return arg.WithILInstruction(inst); 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: { default: {
// We need to convert to inst.TargetType, or to an equivalent type. // We need to convert to inst.TargetType, or to an equivalent type.
IType targetType; IType targetType;
@ -1983,7 +1998,7 @@ namespace ICSharpCode.Decompiler.CSharp
return SpecialType.UnknownType; 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); var variables = function.Variables.Where(v => v.Kind == VariableKind.Parameter).ToDictionary(v => v.Index);
int i = 0; int i = 0;
@ -1992,9 +2007,6 @@ namespace ICSharpCode.Decompiler.CSharp
if (string.IsNullOrEmpty(pd.Name) && !pd.Type.IsArgList()) { if (string.IsNullOrEmpty(pd.Name) && !pd.Type.IsArgList()) {
// needs to be consistent with logic in ILReader.CreateILVarable(ParameterDefinition) // needs to be consistent with logic in ILReader.CreateILVarable(ParameterDefinition)
pd.Name = "P_" + i; 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()) if (settings.AnonymousTypes && parameter.Type.ContainsAnonymousType())
pd.Type = null; pd.Type = null;
@ -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) 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 // 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;
using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.Output;
using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
namespace ICSharpCode.Decompiler.CSharp.OutputVisitor namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
{ {
@ -222,7 +223,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
{ {
TypeSystemAstBuilder astBuilder = CreateAstBuilder(); TypeSystemAstBuilder astBuilder = CreateAstBuilder();
EntityDeclaration node = astBuilder.ConvertEntity(member); 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); ConvertType(member.DeclaringType, writer, formattingPolicy);
writer.WriteToken(Roles.Dot, "."); writer.WriteToken(Roles.Dot, ".");
} }

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

@ -1770,6 +1770,33 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
EndNode(caseLabel); 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) public virtual void VisitThrowStatement(ThrowStatement throwStatement)
{ {
StartNode(throwStatement); StartNode(throwStatement);
@ -1887,19 +1914,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
public virtual void VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement) public virtual void VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement)
{ {
StartNode(localFunctionDeclarationStatement); StartNode(localFunctionDeclarationStatement);
localFunctionDeclarationStatement.Declaration.AcceptVisitor(this);
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);
EndNode(localFunctionDeclarationStatement); EndNode(localFunctionDeclarationStatement);
} }
@ -1950,7 +1965,11 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
WriteKeyword("get", PropertyDeclaration.GetKeywordRole); WriteKeyword("get", PropertyDeclaration.GetKeywordRole);
style = policy.PropertyGetBraceStyle; style = policy.PropertyGetBraceStyle;
} else if (accessor.Role == PropertyDeclaration.SetterRole) { } 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; style = policy.PropertySetBraceStyle;
} else if (accessor.Role == CustomEventDeclaration.AddAccessorRole) { } else if (accessor.Role == CustomEventDeclaration.AddAccessorRole) {
WriteKeyword("add", CustomEventDeclaration.AddKeywordRole); WriteKeyword("add", CustomEventDeclaration.AddKeywordRole);
@ -2596,7 +2615,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
} else if (childNode is Repeat) { } else if (childNode is Repeat) {
VisitRepeat((Repeat)childNode); VisitRepeat((Repeat)childNode);
} else { } else {
TextWriterTokenWriter.PrintPrimitiveValue(childNode); writer.WritePrimitiveValue(childNode);
} }
} }
#endregion #endregion

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

@ -52,6 +52,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
Shift, // << >> Shift, // << >>
Additive, // binary + - Additive, // binary + -
Multiplicative, // * / % Multiplicative, // * / %
Switch, // C# 8 switch expression
Range, // .. Range, // ..
Unary, Unary,
QueryOrLambda, QueryOrLambda,
@ -145,6 +146,8 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
return PrecedenceLevel.Conditional; return PrecedenceLevel.Conditional;
if (expr is AssignmentExpression || expr is LambdaExpression) if (expr is AssignmentExpression || expr is LambdaExpression)
return PrecedenceLevel.Assignment; return PrecedenceLevel.Assignment;
if (expr is SwitchExpression)
return PrecedenceLevel.Switch;
// anything else: primary expression // anything else: primary expression
return PrecedenceLevel.Primary; return PrecedenceLevel.Primary;
} }
@ -414,5 +417,11 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
} }
base.VisitNamedExpression (namedExpression); 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;
using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Solution; using ICSharpCode.Decompiler.Solution;
using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.DebugInfo;
using System.Collections.Concurrent;
namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
{ {
@ -205,7 +206,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
var progress = ProgressIndicator; var progress = ProgressIndicator;
DecompilerTypeSystem ts = new DecompilerTypeSystem(module, AssemblyResolver, Settings); DecompilerTypeSystem ts = new DecompilerTypeSystem(module, AssemblyResolver, Settings);
Parallel.ForEach( Parallel.ForEach(
files, Partitioner.Create(files, loadBalance: true),
new ParallelOptions { new ParallelOptions {
MaxDegreeOfParallelism = this.MaxDegreeOfParallelism, MaxDegreeOfParallelism = this.MaxDegreeOfParallelism,
CancellationToken = cancellationToken CancellationToken = cancellationToken

17
ICSharpCode.Decompiler/CSharp/RequiredNamespaceCollector.cs

@ -93,17 +93,18 @@ namespace ICSharpCode.Decompiler.CSharp
CollectNamespacesForTypeReference(field.ReturnType); CollectNamespacesForTypeReference(field.ReturnType);
break; break;
case IMethod method: 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 reader = module.PEFile.Reader;
var parts = mappingInfo.GetMethodParts((MethodDefinitionHandle)method.MetadataToken).ToList(); var parts = mappingInfo.GetMethodParts((MethodDefinitionHandle)method.MetadataToken).ToList();
foreach (var part in parts) { 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); HandleOverrides(part.GetMethodImplementations(module.metadata), module);
var methodDef = module.metadata.GetMethodDefinition(part); var methodDef = module.metadata.GetMethodDefinition(part);
if (method.HasBody) { if (method.HasBody) {

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

@ -423,8 +423,10 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
} }
} else { } else {
TypeInference ti = new TypeInference(compilation, conversions); TypeInference ti = new TypeInference(compilation, conversions);
IType[] parameterTypes = candidate.ArgumentToParameterMap
.SelectReadOnlyArray(parameterIndex => parameterIndex >= 0 ? candidate.ParameterTypes[parameterIndex] : SpecialType.UnknownType);
bool success; 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) if (!success)
candidate.AddError(OverloadResolutionErrors.TypeInferenceFailed); candidate.AddError(OverloadResolutionErrors.TypeInferenceFailed);
} }

43
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -120,7 +120,7 @@ namespace ICSharpCode.Decompiler.CSharp
return new IfElseStatement(condition, trueStatement, falseStatement).WithILInstruction(inst); 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; object value;
// unpack nullable type, if necessary: // unpack nullable type, if necessary:
@ -165,20 +165,14 @@ namespace ICSharpCode.Decompiler.CSharp
caseLabelMapping = new Dictionary<Block, ConstantResolveResult>(); caseLabelMapping = new Dictionary<Block, ConstantResolveResult>();
TranslatedExpression value; TranslatedExpression value;
var strToInt = inst.Value as StringToInt; if (inst.Value is StringToInt strToInt) {
if (strToInt != null) {
value = exprBuilder.Translate(strToInt.Argument); value = exprBuilder.Translate(strToInt.Argument);
} else { } else {
strToInt = null;
value = exprBuilder.Translate(inst.Value); value = exprBuilder.Translate(inst.Value);
} }
// Pick the section with the most labels as default section. IL.SwitchSection defaultSection = inst.GetDefaultSection();
IL.SwitchSection defaultSection = inst.Sections.First();
foreach (var section in inst.Sections) {
if (section.Labels.Count() > defaultSection.Labels.Count()) {
defaultSection = section;
}
}
var stmt = new SwitchStatement() { Expression = value }; var stmt = new SwitchStatement() { Expression = value };
Dictionary<IL.SwitchSection, Syntax.SwitchSection> translationDictionary = new Dictionary<IL.SwitchSection, Syntax.SwitchSection>(); Dictionary<IL.SwitchSection, Syntax.SwitchSection> translationDictionary = new Dictionary<IL.SwitchSection, Syntax.SwitchSection>();
@ -1002,34 +996,19 @@ namespace ICSharpCode.Decompiler.CSharp
LocalFunctionDeclarationStatement TranslateFunction(ILFunction function) LocalFunctionDeclarationStatement TranslateFunction(ILFunction function)
{ {
var stmt = new LocalFunctionDeclarationStatement();
var nestedBuilder = new StatementBuilder(typeSystem, exprBuilder.decompilationContext, function, settings, cancellationToken); var nestedBuilder = new StatementBuilder(typeSystem, exprBuilder.decompilationContext, function, settings, cancellationToken);
stmt.Name = function.Name; var astBuilder = exprBuilder.astBuilder;
stmt.Parameters.AddRange(exprBuilder.MakeParameters(function.Parameters, function)); var method = (MethodDeclaration)astBuilder.ConvertEntity(function.ReducedMethod);
stmt.ReturnType = exprBuilder.ConvertType(function.Method.ReturnType); method.Body = nestedBuilder.ConvertAsBlock(function.Body);
stmt.Body = nestedBuilder.ConvertAsBlock(function.Body);
Comment prev = null; Comment prev = null;
foreach (string warning in function.Warnings) { 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) { CSharpDecompiler.CleanUpMethodDeclaration(method, method.Body, function);
var astBuilder = exprBuilder.astBuilder; CSharpDecompiler.RemoveAttribute(method, KnownAttribute.CompilerGenerated);
if (astBuilder.ShowTypeParameters) { var stmt = new LocalFunctionDeclarationStatement(method);
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;
}
stmt.AddAnnotation(new MemberResolveResult(null, function.ReducedMethod)); stmt.AddAnnotation(new MemberResolveResult(null, function.ReducedMethod));
stmt.WithILInstruction(function); stmt.WithILInstruction(function);
return stmt; return stmt;

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

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

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

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

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

@ -16,69 +16,22 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System.Collections.Generic;
using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching;
namespace ICSharpCode.Decompiler.CSharp.Syntax namespace ICSharpCode.Decompiler.CSharp.Syntax
{ {
public class LocalFunctionDeclarationStatement : Statement public class LocalFunctionDeclarationStatement : Statement
{ {
public AstNodeCollection<TypeParameterDeclaration> TypeParameters { public static readonly Role<MethodDeclaration> MethodDeclarationRole = new Role<MethodDeclaration>("Method");
get { return GetChildrenByRole(Roles.TypeParameter); }
}
public CSharpTokenNode LParToken {
get { return GetChildByRole(Roles.LPar); }
}
public AstNodeCollection<ParameterDeclaration> Parameters {
get { return GetChildrenByRole(Roles.Parameter); }
}
public CSharpTokenNode RParToken { public MethodDeclaration Declaration {
get { return GetChildByRole(Roles.RPar); } get { return GetChildByRole(MethodDeclarationRole); }
set { SetChildByRole(MethodDeclarationRole, value); }
} }
public AstNodeCollection<Constraint> Constraints { public LocalFunctionDeclarationStatement(MethodDeclaration methodDeclaration)
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)
{ {
return (Modifiers & mod) == mod; AddChild(methodDeclaration, MethodDeclarationRole);
}
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); }
} }
public override void AcceptVisitor(IAstVisitor visitor) public override void AcceptVisitor(IAstVisitor visitor)
@ -98,13 +51,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
protected internal override bool DoMatch(AstNode other, Match match) protected internal override bool DoMatch(AstNode other, Match match)
{ {
LocalFunctionDeclarationStatement o = other as LocalFunctionDeclarationStatement; return other is LocalFunctionDeclarationStatement o && Declaration.DoMatch(o.Declaration, match);
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);
} }
} }
} }

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

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

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

@ -32,6 +32,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{ {
public static readonly TokenRole GetKeywordRole = new TokenRole ("get"); public static readonly TokenRole GetKeywordRole = new TokenRole ("get");
public static readonly TokenRole SetKeywordRole = new TokenRole ("set"); 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> GetterRole = new Role<Accessor>("Getter", Accessor.Null);
public static readonly Role<Accessor> SetterRole = new Role<Accessor>("Setter", 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); 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;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Reflection;
using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.CSharp.Resolver;
using ICSharpCode.Decompiler.CSharp.TypeSystem; using ICSharpCode.Decompiler.CSharp.TypeSystem;
using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.Semantics;
@ -205,6 +206,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
/// The default value is <see langword="false" />. /// The default value is <see langword="false" />.
/// </summary> /// </summary>
public bool PrintIntegralValuesAsHex { get; set; } 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 #endregion
#region Convert Type #region Convert Type
@ -1363,7 +1370,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return ConvertDestructor((IMethod)entity); return ConvertDestructor((IMethod)entity);
case SymbolKind.Accessor: case SymbolKind.Accessor:
IMethod accessor = (IMethod)entity; 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: default:
throw new ArgumentException("Invalid value for SymbolKind: " + entity.SymbolKind); throw new ArgumentException("Invalid value for SymbolKind: " + entity.SymbolKind);
} }
@ -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) if (accessor == null)
return Accessor.Null; return Accessor.Null;
Accessor decl = new Accessor(); Accessor decl = new Accessor();
if (this.ShowAccessibility && accessor.Accessibility != ownerAccessibility)
decl.Modifiers = ModifierFromAccessibility(accessor.Accessibility);
if (accessor.HasReadonlyModifier())
decl.Modifiers |= Modifiers.Readonly;
if (ShowAttributes) { if (ShowAttributes) {
decl.Attributes.AddRange(ConvertAttributes(accessor.GetAttributes())); decl.Attributes.AddRange(ConvertAttributes(accessor.GetAttributes()));
decl.Attributes.AddRange(ConvertAttributes(accessor.GetReturnTypeAttributes(), "return")); decl.Attributes.AddRange(ConvertAttributes(accessor.GetReturnTypeAttributes(), "return"));
@ -1570,10 +1574,35 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
decl.Attributes.AddRange(ConvertAttributes(accessor.Parameters.Last().GetAttributes(), "param")); 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) { if (AddResolveResultAnnotations) {
decl.AddAnnotation(new MemberResolveResult(null, accessor)); 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; return decl;
} }
@ -1592,8 +1621,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
ct.HasReadOnlySpecifier = true; ct.HasReadOnlySpecifier = true;
} }
decl.Name = property.Name; decl.Name = property.Name;
decl.Getter = ConvertAccessor(property.Getter, property.Accessibility, false); decl.Getter = ConvertAccessor(property.Getter, MethodSemanticsAttributes.Getter, property.Accessibility, false);
decl.Setter = ConvertAccessor(property.Setter, property.Accessibility, true); decl.Setter = ConvertAccessor(property.Setter, MethodSemanticsAttributes.Setter, property.Accessibility, true);
decl.PrivateImplementationType = GetExplicitInterfaceType (property); decl.PrivateImplementationType = GetExplicitInterfaceType (property);
MergeReadOnlyModifiers(decl, decl.Getter, decl.Setter); MergeReadOnlyModifiers(decl, decl.Getter, decl.Setter);
return decl; return decl;
@ -1624,8 +1653,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
foreach (IParameter p in indexer.Parameters) { foreach (IParameter p in indexer.Parameters) {
decl.Parameters.Add(ConvertParameter(p)); decl.Parameters.Add(ConvertParameter(p));
} }
decl.Getter = ConvertAccessor(indexer.Getter, indexer.Accessibility, false); decl.Getter = ConvertAccessor(indexer.Getter, MethodSemanticsAttributes.Getter, indexer.Accessibility, false);
decl.Setter = ConvertAccessor(indexer.Setter, indexer.Accessibility, true); decl.Setter = ConvertAccessor(indexer.Setter, MethodSemanticsAttributes.Setter, indexer.Accessibility, true);
decl.PrivateImplementationType = GetExplicitInterfaceType (indexer); decl.PrivateImplementationType = GetExplicitInterfaceType (indexer);
MergeReadOnlyModifiers(decl, decl.Getter, decl.Setter); MergeReadOnlyModifiers(decl, decl.Getter, decl.Setter);
return decl; return decl;
@ -1644,8 +1673,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
} }
decl.ReturnType = ConvertType(ev.ReturnType); decl.ReturnType = ConvertType(ev.ReturnType);
decl.Name = ev.Name; decl.Name = ev.Name;
decl.AddAccessor = ConvertAccessor(ev.AddAccessor, ev.Accessibility, true); decl.AddAccessor = ConvertAccessor(ev.AddAccessor, MethodSemanticsAttributes.Adder, ev.Accessibility, true);
decl.RemoveAccessor = ConvertAccessor(ev.RemoveAccessor, ev.Accessibility, true); decl.RemoveAccessor = ConvertAccessor(ev.RemoveAccessor, MethodSemanticsAttributes.Remover, ev.Accessibility, true);
decl.PrivateImplementationType = GetExplicitInterfaceType (ev); decl.PrivateImplementationType = GetExplicitInterfaceType (ev);
MergeReadOnlyModifiers(decl, decl.AddAccessor, decl.RemoveAccessor); MergeReadOnlyModifiers(decl, decl.AddAccessor, decl.RemoveAccessor);
return decl; return decl;
@ -1799,6 +1828,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return !member.IsStatic; return !member.IsStatic;
case SymbolKind.Destructor: case SymbolKind.Destructor:
return false; return false;
case SymbolKind.Method:
return !((IMethod)member).IsLocalFunction;
default: default:
return true; return true;
} }
@ -1811,7 +1842,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
m |= ModifierFromAccessibility (member.Accessibility); m |= ModifierFromAccessibility (member.Accessibility);
} }
if (this.ShowModifiers) { if (this.ShowModifiers) {
if (member.IsStatic) { if (member is LocalFunctionMethod localFunction) {
if (localFunction.IsStaticLocalFunction) {
m |= Modifiers.Static;
}
} else if (member.IsStatic) {
m |= Modifiers.Static; m |= Modifiers.Static;
} else { } else {
var declaringType = member.DeclaringType; var declaringType = member.DeclaringType;

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

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

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

@ -308,7 +308,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
internal static bool VariableNeedsDeclaration(VariableKind kind) internal static bool VariableNeedsDeclaration(VariableKind kind)
{ {
switch (kind) { switch (kind) {
case VariableKind.PinnedLocal: case VariableKind.PinnedRegionLocal:
case VariableKind.Parameter: case VariableKind.Parameter:
case VariableKind.ExceptionLocal: case VariableKind.ExceptionLocal:
case VariableKind.ExceptionStackSlot: case VariableKind.ExceptionStackSlot:
@ -468,6 +468,9 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
if (v.ILVariable.IsRefReadOnly && type is ComposedType composedType && composedType.HasRefSpecifier) { if (v.ILVariable.IsRefReadOnly && type is ComposedType composedType && composedType.HasRefSpecifier) {
composedType.HasReadOnlySpecifier = true; 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 vds = new VariableDeclarationStatement(type, v.Name, assignment.Right.Detach());
var init = vds.Variables.Single(); var init = vds.Variables.Single();
init.AddAnnotation(assignment.Left.GetResolveResult()); init.AddAnnotation(assignment.Left.GetResolveResult());

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

@ -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 { static readonly Accessor automaticEventPatternV4MCS = new Accessor {
Attributes = { new Repeat(new AnyNode()) }, Attributes = { new Repeat(new AnyNode()) },
Body = new BlockStatement { Body = new BlockStatement {
@ -837,10 +868,23 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
bool CheckAutomaticEventV4(CustomEventDeclaration ev) bool CheckAutomaticEventV4(CustomEventDeclaration ev)
{ {
Match addMatch = automaticEventPatternV4.Match(ev.AddAccessor); Match addMatch = automaticEventPatternV4.Match(ev.AddAccessor);
if (!CheckAutomaticEventMatch(addMatch, ev, true)) if (!CheckAutomaticEventMatch(addMatch, ev, isAddAccessor: true))
return false; return false;
Match removeMatch = automaticEventPatternV4.Match(ev.RemoveAccessor); 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 false;
return true; return true;
} }
@ -848,10 +892,10 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
bool CheckAutomaticEventV2(CustomEventDeclaration ev) bool CheckAutomaticEventV2(CustomEventDeclaration ev)
{ {
Match addMatch = automaticEventPatternV2.Match(ev.AddAccessor); Match addMatch = automaticEventPatternV2.Match(ev.AddAccessor);
if (!CheckAutomaticEventMatch(addMatch, ev, true)) if (!CheckAutomaticEventMatch(addMatch, ev, isAddAccessor: true))
return false; return false;
Match removeMatch = automaticEventPatternV2.Match(ev.RemoveAccessor); Match removeMatch = automaticEventPatternV2.Match(ev.RemoveAccessor);
if (!CheckAutomaticEventMatch(removeMatch, ev, false)) if (!CheckAutomaticEventMatch(removeMatch, ev, isAddAccessor: false))
return false; return false;
return true; return true;
} }
@ -872,7 +916,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
if (!ev.PrivateImplementationType.IsNull) if (!ev.PrivateImplementationType.IsNull)
return null; return null;
if (!ev.Modifiers.HasFlag(Modifiers.Abstract)) { if (!ev.Modifiers.HasFlag(Modifiers.Abstract)) {
if (!CheckAutomaticEventV4(ev) && !CheckAutomaticEventV2(ev) && !CheckAutomaticEventV4MCS(ev)) if (!CheckAutomaticEventV4AggressivelyInlined(ev) && !CheckAutomaticEventV4(ev) && !CheckAutomaticEventV2(ev) && !CheckAutomaticEventV4MCS(ev))
return null; return null;
} }
RemoveCompilerGeneratedAttribute(ev.AddAccessor.Attributes, attributeTypesToRemoveFromAutoEvents); RemoveCompilerGeneratedAttribute(ev.AddAccessor.Attributes, attributeTypesToRemoveFromAutoEvents);

59
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -114,17 +114,19 @@ namespace ICSharpCode.Decompiler
asyncEnumerator = false; asyncEnumerator = false;
staticLocalFunctions = false; staticLocalFunctions = false;
ranges = false; ranges = false;
switchExpressions = false;
} }
if (languageVersion < CSharp.LanguageVersion.Preview) { if (languageVersion < CSharp.LanguageVersion.Preview) {
nativeIntegers = false; nativeIntegers = false;
initAccessors = false;
} }
} }
public CSharp.LanguageVersion GetMinimumRequiredVersion() public CSharp.LanguageVersion GetMinimumRequiredVersion()
{ {
if (nativeIntegers) if (nativeIntegers || initAccessors)
return CSharp.LanguageVersion.Preview; return CSharp.LanguageVersion.Preview;
if (nullableReferenceTypes || readOnlyMethods || asyncEnumerator || asyncUsingAndForEachStatement || staticLocalFunctions || ranges) if (nullableReferenceTypes || readOnlyMethods || asyncEnumerator || asyncUsingAndForEachStatement || staticLocalFunctions || ranges || switchExpressions)
return CSharp.LanguageVersion.CSharp8_0; return CSharp.LanguageVersion.CSharp8_0;
if (introduceUnmanagedConstraint || tupleComparisons || stackAllocInitializers || patternBasedFixedStatement) if (introduceUnmanagedConstraint || tupleComparisons || stackAllocInitializers || patternBasedFixedStatement)
return CSharp.LanguageVersion.CSharp7_3; return CSharp.LanguageVersion.CSharp7_3;
@ -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; bool anonymousMethods = true;
/// <summary> /// <summary>
@ -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; CSharpFormattingOptions csharpFormattingOptions;
[Browsable(false)] [Browsable(false)]

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

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

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

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

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

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

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

@ -200,11 +200,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// * use only a single exit point if at all possible /// * use only a single exit point if at all possible
/// * minimize the amount of code in the in-loop partition /// * minimize the amount of code in the in-loop partition
/// (thus: maximize the amount of code in the out-of-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: /// Observations:
/// * If a node is in-loop, so are all its ancestors in the dominator tree (up to the loop entry point) /// * 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
} }
ControlFlowNode exitPoint = null; ControlFlowNode exitPoint = null;
int exitPointILOffset = -1; int exitPointILOffset = -1;
ConsiderReturnAsExitPoint((Block)loopHead.UserData, ref exitPoint, ref exitPointILOffset);
foreach (var node in loopHead.DominatorTreeChildren) { foreach (var node in loopHead.DominatorTreeChildren) {
PickExitPoint(node, ref exitPoint, ref exitPointILOffset); PickExitPoint(node, ref exitPoint, ref exitPointILOffset);
} }
@ -457,12 +453,31 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
exitPoint = node; exitPoint = node;
exitPointILOffset = block.StartILOffset; exitPointILOffset = block.StartILOffset;
return; // don't visit children, they are likely to have even later IL offsets and we'd end up 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) { foreach (var child in node.DominatorTreeChildren) {
PickExitPoint(child, ref exitPoint, ref exitPointILOffset); 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> /// <summary>
/// Constructs a new control flow graph. /// Constructs a new control flow graph.

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

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

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

@ -28,7 +28,7 @@ using System.Reflection.Metadata;
namespace ICSharpCode.Decompiler.IL.ControlFlow 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: // For a description on the code generated by the C# compiler for yield return:
// http://csharpindepth.com/Articles/Chapter6/IteratorBlockImplementation.aspx // http://csharpindepth.com/Articles/Chapter6/IteratorBlockImplementation.aspx

8
ICSharpCode.Decompiler/IL/ILReader.cs

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

13
ICSharpCode.Decompiler/IL/ILVariable.cs

@ -31,10 +31,14 @@ namespace ICSharpCode.Decompiler.IL
/// </summary> /// </summary>
Local, Local,
/// <summary> /// <summary>
/// A pinned local variable /// A pinned local variable (not associated with a pinned region)
/// </summary> /// </summary>
PinnedLocal, PinnedLocal,
/// <summary> /// <summary>
/// A pinned local variable (associated with a pinned region)
/// </summary>
PinnedRegionLocal,
/// <summary>
/// A local variable used as using-resource variable. /// A local variable used as using-resource variable.
/// </summary> /// </summary>
UsingLocal, UsingLocal,
@ -92,6 +96,7 @@ namespace ICSharpCode.Decompiler.IL
case VariableKind.ForeachLocal: case VariableKind.ForeachLocal:
case VariableKind.UsingLocal: case VariableKind.UsingLocal:
case VariableKind.PinnedLocal: case VariableKind.PinnedLocal:
case VariableKind.PinnedRegionLocal:
case VariableKind.DisplayClassLocal: case VariableKind.DisplayClassLocal:
return true; return true;
default: default:
@ -161,6 +166,7 @@ namespace ICSharpCode.Decompiler.IL
case VariableKind.Local: case VariableKind.Local:
case VariableKind.ForeachLocal: case VariableKind.ForeachLocal:
case VariableKind.PinnedLocal: case VariableKind.PinnedLocal:
case VariableKind.PinnedRegionLocal:
case VariableKind.UsingLocal: case VariableKind.UsingLocal:
case VariableKind.ExceptionLocal: case VariableKind.ExceptionLocal:
case VariableKind.DisplayClassLocal: case VariableKind.DisplayClassLocal:
@ -401,6 +407,9 @@ namespace ICSharpCode.Decompiler.IL
case VariableKind.PinnedLocal: case VariableKind.PinnedLocal:
output.Write("pinned local "); output.Write("pinned local ");
break; break;
case VariableKind.PinnedRegionLocal:
output.Write("PinnedRegion local ");
break;
case VariableKind.Parameter: case VariableKind.Parameter:
output.Write("param "); output.Write("param ");
break; break;
@ -438,7 +447,7 @@ namespace ICSharpCode.Decompiler.IL
output.Write(" : "); output.Write(" : ");
Type.WriteTo(output); Type.WriteTo(output);
output.Write('('); 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("Index={0}, ", Index);
} }
output.Write("LoadCount={0}, AddressCount={1}, StoreCount={2})", LoadCount, AddressCount, StoreCount); 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
base.CheckInvariant(phase); base.CheckInvariant(phase);
Debug.Assert(phase <= ILPhase.InILReader || this.IsDescendantOf(variable.Function)); Debug.Assert(phase <= ILPhase.InILReader || this.IsDescendantOf(variable.Function));
Debug.Assert(phase <= ILPhase.InILReader || variable.Function.Variables[variable.IndexInFunction] == variable); 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
/// <summary>Switch statement</summary> /// <summary>Switch statement</summary>
public sealed partial class SwitchInstruction : ILInstruction public sealed partial class SwitchInstruction : ILInstruction
{ {
public override StackType ResultType { get { return StackType.Void; } }
public override void AcceptVisitor(ILVisitor visitor) public override void AcceptVisitor(ILVisitor visitor)
{ {
visitor.VisitSwitchInstruction(this); visitor.VisitSwitchInstruction(this);

5
ICSharpCode.Decompiler/IL/Instructions.tt

@ -67,7 +67,8 @@
CustomChildren(new []{ CustomChildren(new []{
new ChildInfo("init") { CanInlineInto = true }, new ChildInfo("init") { CanInlineInto = true },
new ChildInfo("body") 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.", 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, CustomClassName("BinaryNumericInstruction"), Binary, CustomWriteTo, CustomConstructor, CustomComputeFlags,
MatchCondition("CheckForOverflow == o.CheckForOverflow && Sign == o.Sign && Operator == o.Operator && IsLifted == o.IsLifted")), MatchCondition("CheckForOverflow == o.CheckForOverflow && Sign == o.Sign && Operator == o.Operator && IsLifted == o.IsLifted")),
@ -116,7 +117,7 @@
new ChildInfo("fallbackInst"), new ChildInfo("fallbackInst"),
}), CustomConstructor, CustomComputeFlags, CustomWriteTo), }), CustomConstructor, CustomComputeFlags, CustomWriteTo),
new OpCode("switch", "Switch statement", 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)")), 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", new OpCode("switch.section", "Switch section within a switch statement",
CustomClassName("SwitchSection"), CustomChildren(new [] { new ChildInfo("body") }), CustomClassName("SwitchSection"), CustomChildren(new [] { new ChildInfo("body") }),

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

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

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

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

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

@ -130,7 +130,16 @@ namespace ICSharpCode.Decompiler.IL
clone.Sections.AddRange(this.Sections.Select(h => (SwitchSection)h.Clone())); clone.Sections.AddRange(this.Sections.Select(h => (SwitchSection)h.Clone()));
return 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) internal override void CheckInvariant(ILPhase phase)
{ {
base.CheckInvariant(phase); base.CheckInvariant(phase);
@ -143,12 +152,25 @@ namespace ICSharpCode.Decompiler.IL
} }
Debug.Assert(!section.Labels.IsEmpty || section.HasNullLabel); Debug.Assert(!section.Labels.IsEmpty || section.HasNullLabel);
Debug.Assert(!section.Labels.Overlaps(sets)); Debug.Assert(!section.Labels.Overlaps(sets));
Debug.Assert(section.Body.ResultType == this.ResultType);
sets = sets.UnionWith(section.Labels); sets = sets.UnionWith(section.Labels);
} }
Debug.Assert(sets.SetEquals(LongSet.Universe), "switch does not handle all possible cases"); Debug.Assert(sets.SetEquals(LongSet.Universe), "switch does not handle all possible cases");
Debug.Assert(!expectNullSection, "Lifted switch is missing 'case null'"); 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)); 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 partial class SwitchSection

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

@ -20,6 +20,8 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Reflection.Metadata;
using Humanizer; using Humanizer;
using ICSharpCode.Decompiler.CSharp.OutputVisitor; using ICSharpCode.Decompiler.CSharp.OutputVisitor;
using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem;
@ -51,6 +53,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
ILTransformContext context; ILTransformContext context;
string[] currentFieldNames; string[] currentFieldNames;
Dictionary<string, int> reservedVariableNames; Dictionary<string, int> reservedVariableNames;
Dictionary<MethodDefinitionHandle, string> localFunctionMapping;
HashSet<ILVariable> loopCounters; HashSet<ILVariable> loopCounters;
const char maxLoopVariableName = 'n'; const char maxLoopVariableName = 'n';
@ -59,6 +62,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
this.context = context; this.context = context;
currentFieldNames = function.Method.DeclaringTypeDefinition.Fields.Select(f => f.Name).ToArray(); currentFieldNames = function.Method.DeclaringTypeDefinition.Fields.Select(f => f.Name).ToArray();
reservedVariableNames = new Dictionary<string, int>(); reservedVariableNames = new Dictionary<string, int>();
localFunctionMapping = new Dictionary<MethodDefinitionHandle, string>();
loopCounters = CollectLoopCounters(function); loopCounters = CollectLoopCounters(function);
foreach (var f in function.Descendants.OfType<ILFunction>()) { foreach (var f in function.Descendants.OfType<ILFunction>()) {
if (f.Method != null) { if (f.Method != null) {
@ -180,6 +184,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (!LocalFunctionDecompiler.ParseLocalFunctionName(localFunction.Name, out _, out var newName) || !IsValidName(newName)) if (!LocalFunctionDecompiler.ParseLocalFunctionName(localFunction.Name, out _, out var newName) || !IsValidName(newName))
newName = null; newName = null;
localFunction.Name = newName; localFunction.Name = newName;
localFunction.ReducedMethod.Name = newName;
} }
// Now generate names: // Now generate names:
var mapping = new Dictionary<ILVariable, string>(ILVariableEqualityComparer.Instance); var mapping = new Dictionary<ILVariable, string>(ILVariableEqualityComparer.Instance);
@ -199,6 +204,25 @@ namespace ICSharpCode.Decompiler.IL.Transforms
newName = GetAlternativeName("f"); newName = GetAlternativeName("f");
} }
localFunction.Name = newName; 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
}; };
block.Instructions.Insert(i++, new StLoc(uninlinedArgs[j], arg)); block.Instructions.Insert(i++, new StLoc(uninlinedArgs[j], arg));
} }
CollectionExtensions.AddRange(v.Function.Variables, uninlinedArgs); v.Function.Variables.AddRange(uninlinedArgs);
// perform copy propagation: // perform copy propagation:
foreach (var expr in v.LoadInstructions.ToArray()) { foreach (var expr in v.LoadInstructions.ToArray()) {
var clone = copiedExpr.Clone(); var clone = copiedExpr.Clone();

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

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

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

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

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

@ -65,8 +65,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{ {
if (container.Kind == ContainerKind.Switch) { if (container.Kind == ContainerKind.Switch) {
// Special case for switch: Only visit the switch condition block. // 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); switchInst.Value.AcceptVisitor(this);
HandleSwitchExpression(container, switchInst);
} }
// No need to call base.VisitBlockContainer, see comment in VisitBlock. // No need to call base.VisitBlockContainer, see comment in VisitBlock.
} }
@ -121,12 +123,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
inst.Kind = ComparisonKind.Equality; inst.Kind = ComparisonKind.Equality;
} }
} }
var rightWithoutConv = inst.Right.UnwrapConv(ConversionKind.SignExtend).UnwrapConv(ConversionKind.ZeroExtend); var rightWithoutConv = inst.Right.UnwrapConv(ConversionKind.SignExtend).UnwrapConv(ConversionKind.ZeroExtend);
if (rightWithoutConv.MatchLdcI4(0) if (rightWithoutConv.MatchLdcI4(0)
&& inst.Sign == Sign.Unsigned && inst.Sign == Sign.Unsigned
&& (inst.Kind == ComparisonKind.GreaterThan || inst.Kind == ComparisonKind.LessThanOrEqual)) && (inst.Kind == ComparisonKind.GreaterThan || inst.Kind == ComparisonKind.LessThanOrEqual)) {
{
if (inst.Kind == ComparisonKind.GreaterThan) { if (inst.Kind == ComparisonKind.GreaterThan) {
context.Step("comp.unsigned(left > ldc.i4 0) => comp(left != ldc.i4 0)", inst); context.Step("comp.unsigned(left > ldc.i4 0) => comp(left != ldc.i4 0)", inst);
inst.Kind = ComparisonKind.Inequality; inst.Kind = ComparisonKind.Inequality;
@ -169,21 +170,19 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} }
} }
} }
protected internal override void VisitConv(Conv inst) protected internal override void VisitConv(Conv inst)
{ {
inst.Argument.AcceptVisitor(this); inst.Argument.AcceptVisitor(this);
if (inst.Argument.MatchLdLen(StackType.I, out ILInstruction array) && inst.TargetType.IsIntegerType() 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); context.Step("conv.i4(ldlen array) => ldlen.i4(array)", inst);
inst.AddILRange(inst.Argument); inst.AddILRange(inst.Argument);
inst.ReplaceWith(new LdLen(inst.TargetType.GetStackType(), array).WithILRange(inst)); inst.ReplaceWith(new LdLen(inst.TargetType.GetStackType(), array).WithILRange(inst));
return; return;
} }
if (inst.TargetType.IsFloatType() && inst.Argument is Conv conv if (inst.TargetType.IsFloatType() && inst.Argument is Conv conv
&& conv.Kind == ConversionKind.IntToFloat && conv.TargetType == PrimitiveType.R) && 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, // 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. // 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: // To avoid emitting '(float)(double)val', we combine these two conversions:
@ -270,7 +269,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
arg.AcceptVisitor(this); arg.AcceptVisitor(this);
} }
} }
protected internal override void VisitCall(Call inst) protected internal override void VisitCall(Call inst)
{ {
var expr = EarlyExpressionTransforms.HandleCall(inst, context); var expr = EarlyExpressionTransforms.HandleCall(inst, context);
@ -430,9 +429,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} else if (args.Count == 5) { } else if (args.Count == 5) {
int lo, mid, hi, isNegative, scale; int lo, mid, hi, isNegative, scale;
if (args[0].MatchLdcI4(out lo) && args[1].MatchLdcI4(out mid) && if (args[0].MatchLdcI4(out lo) && args[1].MatchLdcI4(out mid) &&
args[2].MatchLdcI4(out hi) && args[3].MatchLdcI4(out isNegative) && args[2].MatchLdcI4(out hi) && args[3].MatchLdcI4(out isNegative) &&
args[4].MatchLdcI4(out scale)) args[4].MatchLdcI4(out scale)) {
{
result = new LdcDecimal(new decimal(lo, mid, hi, isNegative != 0, (byte)scale)); result = new LdcDecimal(new decimal(lo, mid, hi, isNegative != 0, (byte)scale));
return true; return true;
} }
@ -501,8 +499,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// Be careful: when both LHS and RHS are the constant 1, we must not // 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. // swap the arguments as it would lead to an infinite transform loop.
if (inst.TrueInst.MatchLdcI4(0) && !inst.FalseInst.MatchLdcI4(0) 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); context.Step("canonicalize logic and/or", inst);
var t = inst.TrueInst; var t = inst.TrueInst;
inst.TrueInst = inst.FalseInst; inst.TrueInst = inst.FalseInst;
@ -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> /// <summary>
/// op is either add or remove/subtract: /// op is either add or remove/subtract:
/// if (dynamic.isevent (target)) { /// if (dynamic.isevent (target)) {
@ -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)); 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) protected internal override void VisitBinaryNumericInstruction(BinaryNumericInstruction inst)
{ {
base.VisitBinaryNumericInstruction(inst); base.VisitBinaryNumericInstruction(inst);
@ -670,8 +751,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
case BinaryNumericOperator.ShiftLeft: case BinaryNumericOperator.ShiftLeft:
case BinaryNumericOperator.ShiftRight: case BinaryNumericOperator.ShiftRight:
if (inst.Right.MatchBinaryNumericInstruction(BinaryNumericOperator.BitAnd, out var lhs, out var rhs) 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 // a << (b & 31) => a << b
context.Step("Combine bit.and into shift", inst); context.Step("Combine bit.and into shift", inst);
inst.Right = lhs; inst.Right = lhs;
@ -679,8 +759,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
break; break;
case BinaryNumericOperator.BitAnd: case BinaryNumericOperator.BitAnd:
if (inst.Left.InferType(context.TypeSystem).IsKnownType(KnownTypeCode.Boolean) 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)) { if (new NullableLiftingTransform(context).Run(inst)) {
// e.g. "(a.GetValueOrDefault() == b.GetValueOrDefault()) & (a.HasValue & b.HasValue)" // e.g. "(a.GetValueOrDefault() == b.GetValueOrDefault()) & (a.HasValue & b.HasValue)"
} }
@ -688,7 +767,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
break; break;
} }
} }
protected internal override void VisitTryCatchHandler(TryCatchHandler inst) protected internal override void VisitTryCatchHandler(TryCatchHandler inst)
{ {
base.VisitTryCatchHandler(inst); base.VisitTryCatchHandler(inst);

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

@ -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#, /// 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". /// thus allowing the emulation via "expr is T ? (T)expr : null".
/// </summary> /// </summary>
class FixLoneIsInst : IILTransform public class FixLoneIsInst : IILTransform
{ {
void IILTransform.Run(ILFunction function, ILTransformContext context) void IILTransform.Run(ILFunction function, ILTransformContext context)
{ {

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

@ -53,15 +53,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms
public void Run(Block block, int pos, StatementTransformContext context) 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; InliningOptions options = InliningOptions.None;
if (IsCatchWhenBlock(block)) if (context.Settings.AggressiveInlining || IsCatchWhenBlock(block)) {
options |= InliningOptions.Aggressive; options |= InliningOptions.Aggressive;
else { } else {
var function = block.Ancestors.OfType<ILFunction>().FirstOrDefault(); var function = block.Ancestors.OfType<ILFunction>().FirstOrDefault();
var inst = block.Instructions[pos]; var inst = block.Instructions[pos];
if (IsInConstructorInitializer(function, inst)) if (IsInConstructorInitializer(function, inst))
@ -77,7 +77,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
for (int i = instructions.Count - 1; i >= 0; i--) { for (int i = instructions.Count - 1; i >= 0; i--) {
if (instructions[i] is StLoc inst) { if (instructions[i] is StLoc inst) {
InliningOptions options = InliningOptions.None; InliningOptions options = InliningOptions.None;
if (IsCatchWhenBlock(block) || IsInConstructorInitializer(function, inst)) if (context.Settings.AggressiveInlining || IsCatchWhenBlock(block) || IsInConstructorInitializer(function, inst))
options = InliningOptions.Aggressive; options = InliningOptions.Aggressive;
if (InlineOneIfPossible(block, i, options, context)) { if (InlineOneIfPossible(block, i, options, context)) {
modified = true; modified = true;
@ -250,21 +250,26 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// Thus, we have to ensure we're operating on an r-value. // 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 // 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). // 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; 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
static bool IsUsedAsThisPointerInCall(LdLoca ldloca, out IMethod method) static bool IsUsedAsThisPointerInCall(LdLoca ldloca, out IMethod method)
{ {
method = null; method = null;
if (ldloca.ChildIndex != 0)
return false;
if (ldloca.Variable.Type.IsReferenceType ?? false) if (ldloca.Variable.Type.IsReferenceType ?? false)
return false; return false;
ILInstruction inst = ldloca; ILInstruction inst = ldloca;
while (inst.Parent is LdFlda ldflda) { while (inst.Parent is LdFlda ldflda) {
inst = ldflda; inst = ldflda;
} }
if (inst.ChildIndex != 0)
return false;
switch (inst.Parent.OpCode) { switch (inst.Parent.OpCode) {
case OpCode.Call: case OpCode.Call:
case OpCode.CallVirt: case OpCode.CallVirt:
@ -321,7 +326,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false; 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 internal enum ExpressionClassification
{ {
RValue, RValue,
@ -418,6 +434,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
case OpCode.NumericCompoundAssign: case OpCode.NumericCompoundAssign:
case OpCode.UserDefinedCompoundAssign: case OpCode.UserDefinedCompoundAssign:
case OpCode.Await: case OpCode.Await:
case OpCode.SwitchInstruction:
return true; return true;
case OpCode.LdLoc: case OpCode.LdLoc:
if (v.StateMachineField == null && ((LdLoc)inlinedExpression).Variable.StateMachineField != null) { if (v.StateMachineField == null && ((LdLoc)inlinedExpression).Variable.StateMachineField != null) {
@ -479,32 +496,21 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true; return true;
} }
break; 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: // decide based on the top-level target instruction into which we are inlining:
switch (next.OpCode) { switch (next.OpCode) {
case OpCode.Leave:
case OpCode.YieldReturn:
return parent == next;
case OpCode.IfInstruction: case OpCode.IfInstruction:
while (parent.MatchLogicNot(out _)) { while (parent.MatchLogicNot(out _)) {
parent = parent.Parent; parent = parent.Parent;
} }
return parent == next; 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: default:
return false; return false;
} }

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

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

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

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

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

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

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

@ -36,7 +36,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// <summary> /// <summary>
/// Decompiler step for C# 7.0 local functions /// Decompiler step for C# 7.0 local functions
/// </summary> /// </summary>
class LocalFunctionDecompiler : IILTransform public class LocalFunctionDecompiler : IILTransform
{ {
ILTransformContext context; ILTransformContext context;
ITypeResolveContext resolveContext; ITypeResolveContext resolveContext;
@ -484,7 +484,27 @@ namespace ICSharpCode.Decompiler.IL.Transforms
break; break;
parametersToRemove++; 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) static void TransformToLocalFunctionReference(ILFunction function, CallInstruction useSite)

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

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

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

@ -10,9 +10,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
using FindResult = ILInlining.FindResult; using FindResult = ILInlining.FindResult;
using FindResultType = ILInlining.FindResultType; 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); Debug.Assert(child.Parent == call);
if (call.IsInstanceCall && child.ChildIndex == 0) if (call.IsInstanceCall && child.ChildIndex == 0)
@ -106,7 +106,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{ {
if (!context.Settings.NamedArguments) if (!context.Settings.NamedArguments)
return; return;
var options = ILInlining.OptionsForBlock(block, pos); var options = ILInlining.OptionsForBlock(block, pos, context);
options |= InliningOptions.IntroduceNamedArguments; options |= InliningOptions.IntroduceNamedArguments;
ILInlining.InlineOneIfPossible(block, pos, options, context: context); ILInlining.InlineOneIfPossible(block, pos, options, context: context);
} }

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

@ -31,7 +31,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// ///
/// The ?? operator for nullable value types is handled by NullableLiftingTransform. /// The ?? operator for nullable value types is handled by NullableLiftingTransform.
/// </summary> /// </summary>
class NullCoalescingTransform : IStatementTransform public class NullCoalescingTransform : IStatementTransform
{ {
public void Run(Block block, int pos, StatementTransformContext context) 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
} }
} }
class NullPropagationStatementTransform : IStatementTransform public class NullPropagationStatementTransform : IStatementTransform
{ {
public void Run(Block block, int pos, StatementTransformContext context) 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
#endregion #endregion
} }
class NullableLiftingStatementTransform : IStatementTransform public class NullableLiftingStatementTransform : IStatementTransform
{ {
public void Run(Block block, int pos, StatementTransformContext context) 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
/// <summary> /// <summary>
/// Detects switch-on-nullable patterns employed by the C# compiler and transforms them to an ILAst-switch-instruction. /// Detects switch-on-nullable patterns employed by the C# compiler and transforms them to an ILAst-switch-instruction.
/// </summary> /// </summary>
class SwitchOnNullableTransform : IILTransform public class SwitchOnNullableTransform : IILTransform
{ {
public void Run(ILFunction function, ILTransformContext context) public void Run(ILFunction function, ILTransformContext context)
{ {

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

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

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

@ -378,9 +378,24 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false; return false;
if (!targetType.GetAllBaseTypes().Any(i => i.IsKnownType(KnownTypeCode.IEnumerable) || i.IsKnownType(KnownTypeCode.IEnumerableOfT))) if (!targetType.GetAllBaseTypes().Any(i => i.IsKnownType(KnownTypeCode.IEnumerable) || i.IsKnownType(KnownTypeCode.IEnumerableOfT)))
return false; return false;
return CSharp.CallBuilder.CanInferTypeArgumentsFromParameters( return CanInferTypeArgumentsFromParameters(method);
method, method.Parameters.SelectReadOnlyArray(p => new ResolveResult(p.Type)),
new TypeInference(resolveContext.Compilation)); 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) static IType GetReturnTypeFromInstruction(ILInstruction instruction)

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

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

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

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

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

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

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

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

55
ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs

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

107
ICSharpCode.Decompiler/Metadata/DotNetCorePathFinderExtensions.cs

@ -20,20 +20,26 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
using System.Reflection.Metadata; using System.Reflection.Metadata;
using System.Reflection.PortableExecutable; using System.Reflection.PortableExecutable;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.Metadata namespace ICSharpCode.Decompiler.Metadata
{ {
public static class DotNetCorePathFinderExtensions public static class DotNetCorePathFinderExtensions
{ {
static readonly string RefPathPattern = static readonly string PathPattern =
@"(Reference Assemblies[/\\]Microsoft[/\\]Framework[/\\](?<type>.NETFramework)[/\\]v(?<version>[^/\\]+)[/\\])" + @"(Reference Assemblies[/\\]Microsoft[/\\]Framework[/\\](?<type>.NETFramework)[/\\]v(?<version>[^/\\]+)[/\\])" +
@"|((?<type>Microsoft\.NET)[/\\]assembly[/\\]GAC_(MSIL|32|64)[/\\])" + @"|((?<type>Microsoft\.NET)[/\\]assembly[/\\]GAC_(MSIL|32|64)[/\\])" +
@"|((?<type>Microsoft\.NET)[/\\]Framework(64)?[/\\](?<version>[^/\\]+)[/\\])" + @"|((?<type>Microsoft\.NET)[/\\]Framework(64)?[/\\](?<version>[^/\\]+)[/\\])" +
@"|(NuGetFallbackFolder[/\\](?<type>[^/\\]+)\\(?<version>[^/\\]+)([/\\].*)?[/\\]ref[/\\])" + @"|(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) public static string DetectTargetFrameworkId(this PEFile assembly)
{ {
@ -49,44 +55,52 @@ namespace ICSharpCode.Decompiler.Metadata
var reader = assembly.GetMetadataReader(); var reader = assembly.GetMetadataReader();
foreach (var h in reader.GetCustomAttributes(Handle.AssemblyDefinition)) { foreach (var h in reader.GetCustomAttributes(Handle.AssemblyDefinition)) {
var attribute = reader.GetCustomAttribute(h); try {
if (attribute.GetAttributeType(reader).GetFullTypeName(reader).ToString() != TargetFrameworkAttributeName) var attribute = reader.GetCustomAttribute(h);
continue; if (attribute.GetAttributeType(reader).GetFullTypeName(reader).ToString() != TargetFrameworkAttributeName)
var blobReader = reader.GetBlobReader(attribute.Value); continue;
if (blobReader.ReadUInt16() == 0x0001) { var blobReader = reader.GetBlobReader(attribute.Value);
return blobReader.ReadSerializedString(); if (blobReader.ReadUInt16() == 0x0001) {
return blobReader.ReadSerializedString();
}
} catch (BadImageFormatException) {
// ignore malformed attributes
} }
} }
foreach (var h in reader.AssemblyReferences) { foreach (var h in reader.AssemblyReferences) {
var r = reader.GetAssemblyReference(h); try {
if (r.PublicKeyOrToken.IsNil) var r = reader.GetAssemblyReference(h);
continue; if (r.PublicKeyOrToken.IsNil)
string version; continue;
switch (reader.GetString(r.Name)) { string version;
case "netstandard": switch (reader.GetString(r.Name)) {
version = r.Version.ToString(3); case "netstandard":
return $".NETStandard,Version=v{version}"; version = r.Version.ToString(3);
case "System.Runtime": return $".NETStandard,Version=v{version}";
// System.Runtime.dll uses the following scheme: case "System.Runtime":
// 4.2.0 => .NET Core 2.0 // System.Runtime.dll uses the following scheme:
// 4.2.1 => .NET Core 2.1 / 3.0 // 4.2.0 => .NET Core 2.0
// 4.2.2 => .NET Core 3.1 // 4.2.1 => .NET Core 2.1 / 3.0
if (r.Version >= new Version(4, 2, 0)) { // 4.2.2 => .NET Core 3.1
version = "2.0"; if (r.Version >= new Version(4, 2, 0)) {
if (r.Version >= new Version(4, 2, 1)) { version = "2.0";
version = "3.0"; if (r.Version >= new Version(4, 2, 1)) {
} version = "3.0";
if (r.Version >= new Version(4, 2, 2)) { }
version = "3.1"; if (r.Version >= new Version(4, 2, 2)) {
version = "3.1";
}
return $".NETCoreApp,Version=v{version}";
} else {
continue;
} }
return $".NETCoreApp,Version=v{version}"; case "mscorlib":
} else { version = r.Version.ToString(2);
continue; return $".NETFramework,Version=v{version}";
} }
case "mscorlib": } catch (BadImageFormatException) {
version = r.Version.ToString(2); // ignore malformed references
return $".NETFramework,Version=v{version}";
} }
} }
@ -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 * - .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 * - .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); RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.ExplicitCapture);
if (pathMatch.Success) { if (pathMatch.Success) {
var type = pathMatch.Groups["type"].Value; var type = pathMatch.Groups["type"].Value;
@ -121,6 +135,25 @@ namespace ICSharpCode.Decompiler.Metadata
return string.Empty; 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 public class ReferenceLoadInfo

4
ICSharpCode.Decompiler/Metadata/MetadataExtensions.cs

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

4
ICSharpCode.Decompiler/Metadata/PEFile.cs

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

33
ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs

@ -92,16 +92,19 @@ namespace ICSharpCode.Decompiler.Metadata
public UniversalAssemblyResolver(string mainAssemblyFileName, bool throwOnError, string targetFramework, public UniversalAssemblyResolver(string mainAssemblyFileName, bool throwOnError, string targetFramework,
PEStreamOptions streamOptions = PEStreamOptions.Default, MetadataReaderOptions metadataOptions = MetadataReaderOptions.Default) PEStreamOptions streamOptions = PEStreamOptions.Default, MetadataReaderOptions metadataOptions = MetadataReaderOptions.Default)
{ {
this.mainAssemblyFileName = mainAssemblyFileName;
this.throwOnError = throwOnError;
this.streamOptions = streamOptions; this.streamOptions = streamOptions;
this.metadataOptions = metadataOptions; this.metadataOptions = metadataOptions;
this.targetFramework = targetFramework ?? string.Empty; this.targetFramework = targetFramework ?? string.Empty;
(targetFrameworkIdentifier, targetFrameworkVersion) = ParseTargetFramework(this.targetFramework); (targetFrameworkIdentifier, targetFrameworkVersion) = ParseTargetFramework(this.targetFramework);
this.mainAssemblyFileName = mainAssemblyFileName;
this.baseDirectory = Path.GetDirectoryName(mainAssemblyFileName); if (mainAssemblyFileName != null) {
this.throwOnError = throwOnError; string baseDirectory = Path.GetDirectoryName(mainAssemblyFileName);
if (string.IsNullOrWhiteSpace(this.baseDirectory)) if (string.IsNullOrWhiteSpace(this.baseDirectory))
this.baseDirectory = Environment.CurrentDirectory; this.baseDirectory = Environment.CurrentDirectory;
AddSearchDirectory(baseDirectory); AddSearchDirectory(baseDirectory);
}
} }
internal static (TargetFrameworkIdentifier, Version) ParseTargetFramework(string targetFramework) internal static (TargetFrameworkIdentifier, Version) ParseTargetFramework(string targetFramework)
@ -191,7 +194,10 @@ namespace ICSharpCode.Decompiler.Metadata
if (IsZeroOrAllOnes(targetFrameworkVersion)) if (IsZeroOrAllOnes(targetFrameworkVersion))
goto default; goto default;
if (dotNetCorePathFinder == null) { 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) { foreach (var directory in directories) {
dotNetCorePathFinder.AddSearchDirectory(directory); dotNetCorePathFinder.AddSearchDirectory(directory);
} }
@ -327,9 +333,13 @@ namespace ICSharpCode.Decompiler.Metadata
if (assembly != null) if (assembly != null)
return assembly; return assembly;
assembly = SearchDirectory(name, framework_dirs); // 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.
if (assembly != null) // 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
return assembly; if (name.Version <= new Version(4, 0, 0, 0)) {
assembly = SearchDirectory(name, framework_dirs);
if (assembly != null)
return assembly;
}
if (throwOnError) if (throwOnError)
throw new AssemblyResolutionException(name); throw new AssemblyResolutionException(name);
@ -388,6 +398,9 @@ namespace ICSharpCode.Decompiler.Metadata
return typeof(object).Module.FullyQualifiedName; return typeof(object).Module.FullyQualifiedName;
} }
if (reference.PublicKeyToken == null)
return null;
string path; string path;
if (decompilerRuntime == DecompilerRuntime.Mono) { if (decompilerRuntime == DecompilerRuntime.Mono) {
path = GetMonoMscorlibBasePath(version); path = GetMonoMscorlibBasePath(version);

63
ICSharpCode.Decompiler/Output/TextTokenWriter.cs

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

18
ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs

@ -224,24 +224,26 @@ namespace ICSharpCode.Decompiler.TypeSystem
var mainModuleWithOptions = mainModule.WithOptions(typeSystemOptions); var mainModuleWithOptions = mainModule.WithOptions(typeSystemOptions);
var referencedAssembliesWithOptions = referencedAssemblies.Select(file => file.WithOptions(typeSystemOptions)); var referencedAssembliesWithOptions = referencedAssemblies.Select(file => file.WithOptions(typeSystemOptions));
// Primitive types are necessary to avoid assertions in ILReader. // Primitive types are necessary to avoid assertions in ILReader.
// Fallback to MinimalCorlib to provide the primitive types. // Other known types are necessary in order for transforms to work (e.g. Task<T> for async transform).
if (!HasType(KnownTypeCode.Void) || !HasType(KnownTypeCode.Int32)) { // Figure out which known types are missing from our type system so far:
Init(mainModule.WithOptions(typeSystemOptions), referencedAssembliesWithOptions.Concat(new[] { MinimalCorlib.Instance })); var missingKnownTypes = KnownTypeReference.AllKnownTypes.Where(IsMissing).ToList();
if (missingKnownTypes.Count > 0) {
Init(mainModule.WithOptions(typeSystemOptions), referencedAssembliesWithOptions.Concat(new[] { MinimalCorlib.CreateWithTypes(missingKnownTypes) }));
} else { } else {
Init(mainModuleWithOptions, referencedAssembliesWithOptions); Init(mainModuleWithOptions, referencedAssembliesWithOptions);
} }
this.MainModule = (MetadataModule)base.MainModule; 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) if (!mainModule.GetTypeDefinition(name).IsNil)
return true; return false;
foreach (var file in referencedAssemblies) { foreach (var file in referencedAssemblies) {
if (!file.GetTypeDefinition(name).IsNil) 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
/// </summary> /// </summary>
bool ReturnTypeIsRefReadOnly { get; } 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> /// <summary>
/// Gets whether the method accepts the 'this' reference as ref readonly. /// 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' /// 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
IEnumerable<IAttribute> IMethod.GetReturnTypeAttributes() => EmptyList<IAttribute>.Instance; IEnumerable<IAttribute> IMethod.GetReturnTypeAttributes() => EmptyList<IAttribute>.Instance;
bool IMethod.ReturnTypeIsRefReadOnly => false; bool IMethod.ReturnTypeIsRefReadOnly => false;
bool IMethod.ThisIsRefReadOnly => false; bool IMethod.ThisIsRefReadOnly => false;
bool IMethod.IsInitOnly => false;
public IReadOnlyList<ITypeParameter> TypeParameters { get; set; } = EmptyList<ITypeParameter>.Instance; 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
/// </summary> /// </summary>
public FullTypeName FullTypeName { get { return fullTypeName; } } 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) { foreach (var asm in compilation.Modules) {
IType type = asm.GetTypeDefinition(fullTypeName); IType type = asm.GetTypeDefinition(fullTypeName);
if (type != null) if (type != null)
@ -107,7 +106,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
type = context.CurrentModule.GetTypeDefinition(fullTypeName); type = context.CurrentModule.GetTypeDefinition(fullTypeName);
} }
if (type == null) { if (type == null) {
type = ResolveInAllAssemblies(context); type = ResolveInAllAssemblies(context.Compilation, in fullTypeName);
} }
} else { } else {
// Assembly specified: only look in the specified assembly. // Assembly specified: only look in the specified assembly.
@ -118,7 +117,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
if (asm != null) { if (asm != null) {
type = asm.GetTypeDefinition(fullTypeName); type = asm.GetTypeDefinition(fullTypeName);
} else { } else {
type = ResolveInAllAssemblies(context); type = ResolveInAllAssemblies(context.Compilation, in fullTypeName);
} }
} }
return type ?? new UnknownType(fullTypeName, isReferenceType); return type ?? new UnknownType(fullTypeName, isReferenceType);

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

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

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

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

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

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

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

@ -31,22 +31,25 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
/// </summary> /// </summary>
public sealed class MinimalCorlib : IModule 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; } public ICompilation Compilation { get; }
CorlibTypeDefinition[] typeDefinitions; CorlibTypeDefinition[] typeDefinitions;
readonly CorlibNamespace rootNamespace; readonly CorlibNamespace rootNamespace;
private MinimalCorlib(ICompilation compilation) private MinimalCorlib(ICompilation compilation, IEnumerable<KnownTypeReference> types)
{ {
this.Compilation = compilation; 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); 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; bool IModule.IsMainModule => Compilation.MainModule == this;
@ -65,7 +68,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public ITypeDefinition GetTypeDefinition(TopLevelTypeName topLevelTypeName) public ITypeDefinition GetTypeDefinition(TopLevelTypeName topLevelTypeName)
{ {
foreach (var typeDef in typeDefinitions) { foreach (var typeDef in typeDefinitions) {
if (typeDef != null && typeDef.FullTypeName == topLevelTypeName) if (typeDef.FullTypeName == topLevelTypeName)
return typeDef; return typeDef;
} }
return null; return null;
@ -81,9 +84,16 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
sealed class CorlibModuleReference : IModuleReference sealed class CorlibModuleReference : IModuleReference
{ {
readonly IEnumerable<KnownTypeReference> types;
public CorlibModuleReference(IEnumerable<KnownTypeReference> types)
{
this.types = types;
}
IModule IModuleReference.Resolve(ITypeResolveContext context) 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
get { get {
var baseType = KnownTypeReference.Get(typeCode).baseType; var baseType = KnownTypeReference.Get(typeCode).baseType;
if (baseType != KnownTypeCode.None) if (baseType != KnownTypeCode.None)
return new[] { corlib.typeDefinitions[(int)baseType] }; return new[] { corlib.Compilation.FindType(baseType) };
else else
return EmptyList<IType>.Instance; return EmptyList<IType>.Instance;
} }

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

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

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

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

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

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

12
ICSharpCode.Decompiler/TypeSystem/KnownTypeReference.cs

@ -17,6 +17,7 @@
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Collections.Generic;
namespace ICSharpCode.Decompiler.TypeSystem namespace ICSharpCode.Decompiler.TypeSystem
{ {
@ -235,6 +236,17 @@ namespace ICSharpCode.Decompiler.TypeSystem
return knownTypeReferences[(int)typeCode]; 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 KnownTypeCode knownTypeCode;
readonly string namespaceName; readonly string namespaceName;
readonly string name; readonly string name;

60
ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs

@ -48,6 +48,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
readonly MetadataMethod[] methodDefs; readonly MetadataMethod[] methodDefs;
readonly MetadataProperty[] propertyDefs; readonly MetadataProperty[] propertyDefs;
readonly MetadataEvent[] eventDefs; readonly MetadataEvent[] eventDefs;
readonly IModule[] referencedAssemblies;
internal MetadataModule(ICompilation compilation, Metadata.PEFile peFile, TypeSystemOptions options) internal MetadataModule(ICompilation compilation, Metadata.PEFile peFile, TypeSystemOptions options)
{ {
@ -79,6 +80,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
this.methodDefs = new MetadataMethod[metadata.MethodDefinitions.Count + 1]; this.methodDefs = new MetadataMethod[metadata.MethodDefinitions.Count + 1];
this.propertyDefs = new MetadataProperty[metadata.PropertyDefinitions.Count + 1]; this.propertyDefs = new MetadataProperty[metadata.PropertyDefinitions.Count + 1];
this.eventDefs = new MetadataEvent[metadata.EventDefinitions.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
} }
#endregion #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 #region Resolve Type
public IType ResolveType(EntityHandle typeRefDefSpec, GenericContext context, CustomAttributeHandleCollection? typeAttributes = null, Nullability nullableContext = Nullability.Oblivious) 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
public IType GetTypeFromReference(SRM.MetadataReader reader, SRM.TypeReferenceHandle handle, byte rawTypeKind) public IType GetTypeFromReference(SRM.MetadataReader reader, SRM.TypeReferenceHandle handle, byte rawTypeKind)
{ {
bool? isReferenceType = IsReferenceType(reader, handle, rawTypeKind); IModule resolvedModule = module.GetDeclaringModule(handle);
var gctr = new GetClassTypeReference(handle.GetFullTypeName(reader), handle.GetDeclaringModule(reader), isReferenceType); var fullTypeName = handle.GetFullTypeName(reader);
return gctr.Resolve(module != null ? new SimpleTypeResolveContext(module) : new SimpleTypeResolveContext(compilation)); 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) public IType GetTypeFromSerializedName(string name)

59
ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs

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

1
ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs

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

8
ICSharpCode.Decompiler/Util/CollectionExtensions.cs

@ -12,10 +12,12 @@ namespace ICSharpCode.Decompiler.Util
value = pair.Value; value = pair.Value;
} }
#if !NETCORE
public static IEnumerable<(A, B)> Zip<A, B>(this IEnumerable<A> input1, IEnumerable<B> input2) public static IEnumerable<(A, B)> Zip<A, B>(this IEnumerable<A> input1, IEnumerable<B> input2)
{ {
return input1.Zip(input2, (a, b) => (a, b)); return input1.Zip(input2, (a, b) => (a, b));
} }
#endif
public static IEnumerable<(A, B)> ZipLongest<A, B>(this IEnumerable<A> input1, IEnumerable<B> input2) public static IEnumerable<(A, B)> ZipLongest<A, B>(this IEnumerable<A> input1, IEnumerable<B> input2)
{ {
@ -51,10 +53,12 @@ namespace ICSharpCode.Decompiler.Util
} }
} }
#if !NETCORE
public static HashSet<T> ToHashSet<T>(this IEnumerable<T> input) public static HashSet<T> ToHashSet<T>(this IEnumerable<T> input)
{ {
return new HashSet<T>(input); return new HashSet<T>(input);
} }
#endif
public static IEnumerable<T> SkipLast<T>(this IReadOnlyCollection<T> input, int count) public static IEnumerable<T> SkipLast<T>(this IReadOnlyCollection<T> input, int count)
{ {
@ -323,7 +327,7 @@ namespace ICSharpCode.Decompiler.Util
return first; 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 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 T[] array, Predicate<T> match) => Array.Exists(array, match);
public static bool Any<T>(this List<T> list, Predicate<T> match) => list.Exists(match); public static bool Any<T>(this List<T> list, Predicate<T> match) => list.Exists(match);
@ -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 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]; public static T Last<T>(this IList<T> list) => list[list.Count - 1];
#endregion #endregion
} }
} }

23
ILSpy.AddIn/AssemblyFileFinder.cs

@ -1,32 +1,29 @@
using System; using System;
using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Util; using ICSharpCode.Decompiler.Util;
using Mono.Cecil;
namespace ICSharpCode.ILSpy.AddIn namespace ICSharpCode.ILSpy.AddIn
{ {
public class AssemblyFileFinder 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, string tfi = DetectTargetFrameworkId(assemblyDefinition, assemblyFile);
DetectTargetFrameworkId(assemblyDefinition, assemblyFile)); UniversalAssemblyResolver assemblyResolver;
if (IsReferenceAssembly(assemblyDefinition, assemblyFile)) { 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[/\\]"; 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")) if (assemblyDef.CustomAttributes.Any(ca => ca.AttributeType.FullName == "System.Runtime.CompilerServices.ReferenceAssemblyAttribute"))
return true; return true;
@ -40,7 +37,7 @@ namespace ICSharpCode.ILSpy.AddIn
@"(Reference Assemblies[/\\]Microsoft[/\\]Framework[/\\](?<1>.NETFramework)[/\\]v(?<2>[^/\\]+)[/\\])" + @"(Reference Assemblies[/\\]Microsoft[/\\]Framework[/\\](?<1>.NETFramework)[/\\]v(?<2>[^/\\]+)[/\\])" +
@"|((NuGetFallbackFolder|packs)[/\\](?<1>[^/\\]+)\\(?<2>[^/\\]+)([/\\].*)?[/\\]ref[/\\])"; @"|((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) if (assembly == null)
throw new ArgumentNullException(nameof(assembly)); throw new ArgumentNullException(nameof(assembly));

14
ILSpy.AddIn/Commands/OpenCodeItemCommand.cs

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

5
ILSpy.AddIn/Commands/OpenILSpyCommand.cs

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

84
ILSpy.AddIn/ILSpy.AddIn.csproj

@ -12,7 +12,6 @@
<Company>IC#Code</Company> <Company>IC#Code</Company>
<Description>ILSpy</Description> <Description>ILSpy</Description>
<NeutralLanguage>en-US</NeutralLanguage>
<Version>1.7.1.0</Version> <Version>1.7.1.0</Version>
<FileVersion>1.7.1.0</FileVersion> <FileVersion>1.7.1.0</FileVersion>
@ -51,10 +50,15 @@
</ItemGroup> </ItemGroup>
<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>
<ItemGroup> <ItemGroup>
<Compile Include="..\ILSpy\NativeMethods.cs" Link="NativeMethods.cs" />
<Compile Include="Commands\AssemblyReferenceForILSpy.cs" /> <Compile Include="Commands\AssemblyReferenceForILSpy.cs" />
<Compile Include="Commands\NuGetReferenceForILSpy.cs" /> <Compile Include="Commands\NuGetReferenceForILSpy.cs" />
<Compile Include="Commands\ProjectItemForILSpy.cs" /> <Compile Include="Commands\ProjectItemForILSpy.cs" />
@ -85,10 +89,20 @@
<LastGenOutput>Resources.Designer.cs</LastGenOutput> <LastGenOutput>Resources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType> <SubType>Designer</SubType>
</EmbeddedResource> </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"> <EmbeddedResource Include="VSPackage.resx">
<MergeWithCTO>true</MergeWithCTO> <MergeWithCTO>true</MergeWithCTO>
<ManifestResourceName>VSPackage</ManifestResourceName> <ManifestResourceName>VSPackage</ManifestResourceName>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="VSPackage.zh-Hans.resx">
<MergeWithCTO>true</MergeWithCTO>
<LogicalName>VSPackage.zh-Hans.resources</LogicalName>
<DependentUpon>VSPackage.resx</DependentUpon>
</EmbeddedResource>
</ItemGroup> </ItemGroup>
<!-- <!--
@ -101,33 +115,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<AdditionalDependencies Include="$(ILSpyBuildPath)System.Collections.Immutable.dll" /> <Content Include="$(OutputPath)Mono.Cecil.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)">
<IncludeInVSIX>true</IncludeInVSIX> <IncludeInVSIX>true</IncludeInVSIX>
<VSIXSubPath>\</VSIXSubPath> <VSIXSubPath>\</VSIXSubPath>
</Content> </Content>
@ -135,22 +123,46 @@
<!-- END WORKAROUND --> <!-- END WORKAROUND -->
<ItemGroup> <ItemGroup>
<None Include="ILSpyAddIn.vsct" /> <Content Include="$(ILSpyBuildPath)*.dll;$(ILSpyBuildPath)ILSpy.exe;$(ILSpyBuildPath)ILSpy.exe.config">
<None Include="source.extension.vsixmanifest.template" /> <IncludeInVSIX>true</IncludeInVSIX>
<None Include="source.extension.vsixmanifest"> <VSIXSubPath>\ILSpy</VSIXSubPath>
<SubType>Designer</SubType> </Content>
</None>
</ItemGroup> </ItemGroup>
<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>
<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> <ResourceName>Menus.ctmenu</ResourceName>
<SubType>Designer</SubType> <SubType>Designer</SubType>
<DependentUpon>ILSpyAddIn.vsct</DependentUpon>
</VSCTCompile> </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>
<ItemGroup> <ItemGroup>

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

Loading…
Cancel
Save