Browse Source

Merge branch 'master' into debugInfo

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

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" />
@ -92,6 +92,7 @@
<Compile Include="ProjectDecompiler\TargetFrameworkTests.cs" /> <Compile Include="ProjectDecompiler\TargetFrameworkTests.cs" />
<Compile Include="TestAssemblyResolver.cs" /> <Compile Include="TestAssemblyResolver.cs" />
<Compile Include="TestCases\Correctness\StringConcat.cs" /> <Compile Include="TestCases\Correctness\StringConcat.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]

13
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]

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

@ -228,7 +228,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)]

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

@ -405,6 +405,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;
} }
@ -1345,30 +1346,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) {

12
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()
@ -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
); );
} }

71
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 };
} }
@ -1075,7 +1076,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 +1793,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 +1997,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 +2006,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 +2933,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, ".");
} }

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

@ -1759,6 +1759,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);
@ -1876,19 +1903,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);
} }
@ -1939,7 +1954,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);

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

22
ICSharpCode.Decompiler/CSharp/RequiredNamespaceCollector.cs

@ -17,6 +17,7 @@ namespace ICSharpCode.Decompiler.CSharp
static readonly Decompiler.TypeSystem.GenericContext genericContext = default; static readonly Decompiler.TypeSystem.GenericContext genericContext = default;
readonly HashSet<string> namespaces; readonly HashSet<string> namespaces;
readonly HashSet<IType> visitedTypes = new HashSet<IType>();
public RequiredNamespaceCollector(HashSet<string> namespaces) public RequiredNamespaceCollector(HashSet<string> namespaces)
{ {
@ -92,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) {
@ -141,13 +143,15 @@ namespace ICSharpCode.Decompiler.CSharp
void CollectNamespacesForTypeReference(IType type) void CollectNamespacesForTypeReference(IType type)
{ {
if (!visitedTypes.Add(type))
return;
switch (type) { switch (type) {
case ParameterizedType parameterizedType: case ParameterizedType parameterizedType:
namespaces.Add(parameterizedType.Namespace); namespaces.Add(parameterizedType.Namespace);
CollectNamespacesForTypeReference(parameterizedType.GenericType); CollectNamespacesForTypeReference(parameterizedType.GenericType);
foreach (var arg in parameterizedType.TypeArguments) foreach (var arg in parameterizedType.TypeArguments)
CollectNamespacesForTypeReference(arg); CollectNamespacesForTypeReference(arg);
break; return; // no need to collect base types again
case TypeWithElementType typeWithElementType: case TypeWithElementType typeWithElementType:
CollectNamespacesForTypeReference(typeWithElementType.ElementType); CollectNamespacesForTypeReference(typeWithElementType.ElementType);
break; break;

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;

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

@ -346,6 +346,16 @@ 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);
@ -988,6 +998,16 @@ 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);
@ -1630,6 +1650,16 @@ 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

@ -103,6 +103,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);
@ -243,6 +245,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);
@ -383,6 +387,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;
} }
} }
} }

59
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -113,17 +113,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;
@ -163,6 +165,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>
@ -1387,6 +1423,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

@ -70,6 +70,7 @@
<Compile Include="CSharp\SequencePointBuilder.cs" /> <Compile Include="CSharp\SequencePointBuilder.cs" />
<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\SwitchExpression.cs" />
<Compile Include="CSharp\Syntax\FunctionPointerType.cs" /> <Compile Include="CSharp\Syntax\FunctionPointerType.cs" />
<Compile Include="IL\Transforms\FixLoneIsInst.cs" /> <Compile Include="IL\Transforms\FixLoneIsInst.cs" />
<Compile Include="IL\Transforms\IndexRangeTransform.cs" /> <Compile Include="IL\Transforms\IndexRangeTransform.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();
} }

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,13 +453,32 @@ 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.
/// Each node cfg[i] has a corresponding node rev[i]. /// Each node cfg[i] has a corresponding node rev[i].

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>()) {

2
ICSharpCode.Decompiler/IL/Instructions.cs

@ -1497,7 +1497,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);

2
ICSharpCode.Decompiler/IL/Instructions.tt

@ -116,7 +116,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)

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

@ -131,6 +131,15 @@ namespace ICSharpCode.Decompiler.IL
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;

157
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.
} }
@ -124,9 +126,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
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;
@ -174,16 +175,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{ {
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:
@ -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)"
} }

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)
{ {

36
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;
@ -418,6 +418,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 +480,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;

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);

25
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
{ {
@ -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,8 +182,8 @@ 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;
} }
@ -404,7 +409,7 @@ namespace ICSharpCode.Decompiler
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;

20
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

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;

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

@ -87,6 +87,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
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)

19
ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs

@ -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,7 +301,7 @@ 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);
} }
@ -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,8 +478,8 @@ 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
@ -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>

94
ILSpy.AddIn/ILSpyAddIn.en-US.vsct

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="utf-8"?>
<CommandTable xmlns="http://schemas.microsoft.com/VisualStudio/2005-10-18/CommandTable" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This is the file that defines the actual layout and type of the commands.
It is divided in different sections (e.g. command definition, command
placement, ...), with each defining a specific set of properties.
See the comment before each section for more details about how to
use it. -->
<!-- The VSCT compiler (the tool that translates this file into the binary
format that VisualStudio will consume) has the ability to run a preprocessor
on the vsct file; this preprocessor is (usually) the C++ preprocessor, so
it is possible to define includes and macros with the same syntax used
in C++ files. Using this ability of the compiler here, we include some files
defining some of the constants that we will use inside the file. -->
<!--This is the file that defines the IDs for all the commands exposed by VisualStudio. -->
<Extern href="stdidcmd.h"/>
<!--This header contains the command ids for the menus provided by the shell. -->
<Extern href="vsshlids.h"/>
<Include href="ILSpyAddIn.vsct" />
<!--The Commands section is where we the commands, menus and menu groups are defined.
This section uses a Guid to identify the package that provides the command defined inside it. -->
<Commands package="guidILSpyAddInPkg">
<!-- Inside this section we have different sub-sections: one for the menus, another
for the menu groups, one for the buttons (the actual commands), one for the combos
and the last one for the bitmaps used. Each element is identified by a command id that
is a unique pair of guid and numeric identifier; the guid part of the identifier is usually
called "command set" and is used to group different command inside a logically related
group; your package should define its own command set in order to avoid collisions
with command ids defined by other packages. -->
<!--Buttons section. -->
<!--This section defines the elements the user can interact with, like a menu command or a button
or combo box in a toolbar. -->
<Buttons>
<!--To define a menu group you have to specify its ID, the parent menu and its display priority.
The command is visible and enabled by default. If you need to change the visibility, status, etc, you can use
the CommandFlag node.
You can add more than one CommandFlag node e.g.:
<CommandFlag>DefaultInvisible</CommandFlag>
<CommandFlag>DynamicVisibility</CommandFlag>
If you do not want an image next to your command, remove the Icon node /> -->
<Button guid="guidILSpyAddInCmdSet" id="cmdidOpenReferenceInILSpy" priority="0x0600" type="Button">
<Parent guid="guidILSpyAddInCmdSet" id="OpenILSpyRefGroup" />
<Icon guid="guidImages" id="bmpLogo" />
<CommandFlag>DynamicVisibility</CommandFlag>
<CommandFlag>DefaultInvisible</CommandFlag>
<Strings>
<ButtonText>Open in ILSpy</ButtonText>
</Strings>
</Button>
<Button guid="guidILSpyAddInCmdSet" id="cmdidOpenProjectOutputInILSpy" priority="0x0600" type="Button">
<Parent guid="guidILSpyAddInCmdSet" id="OpenILSpyProjGroup" />
<Icon guid="guidImages" id="bmpLogo" />
<CommandFlag>DynamicVisibility</CommandFlag>
<CommandFlag>DefaultInvisible</CommandFlag>
<Strings>
<ButtonText>Open output in ILSpy</ButtonText>
</Strings>
</Button>
<Button guid="guidILSpyAddInCmdSet" id="cmdidOpenCodeItemInILSpy" priority="0x0600" type="Button">
<Parent guid="guidILSpyAddInCmdSet" id="OpenILSpyCodeItemGroup" />
<Icon guid="guidImages" id="bmpLogo" />
<CommandFlag>DynamicVisibility</CommandFlag>
<CommandFlag>DefaultInvisible</CommandFlag>
<Strings>
<ButtonText>Open code in ILSpy</ButtonText>
</Strings>
</Button>
<Button guid="guidILSpyAddInCmdSet" id="cmdidOpenILSpy" priority="0x0600" type="Button">
<Parent guid="guidILSpyAddInCmdSet" id="OpenILSpyGroup" />
<Icon guid="guidImages" id="bmpLogo" />
<Strings>
<ButtonText>ILSpy</ButtonText>
</Strings>
</Button>
</Buttons>
</Commands>
</CommandTable>

56
ILSpy.AddIn/ILSpyAddIn.vsct

@ -20,9 +20,6 @@
<!--This header contains the command ids for the menus provided by the shell. --> <!--This header contains the command ids for the menus provided by the shell. -->
<Extern href="vsshlids.h"/> <Extern href="vsshlids.h"/>
<!--The Commands section is where we the commands, menus and menu groups are defined. <!--The Commands section is where we the commands, menus and menu groups are defined.
This section uses a Guid to identify the package that provides the command defined inside it. --> This section uses a Guid to identify the package that provides the command defined inside it. -->
<Commands package="guidILSpyAddInPkg"> <Commands package="guidILSpyAddInPkg">
@ -57,58 +54,6 @@
</Group> </Group>
</Groups> </Groups>
<!--Buttons section. -->
<!--This section defines the elements the user can interact with, like a menu command or a button
or combo box in a toolbar. -->
<Buttons>
<!--To define a menu group you have to specify its ID, the parent menu and its display priority.
The command is visible and enabled by default. If you need to change the visibility, status, etc, you can use
the CommandFlag node.
You can add more than one CommandFlag node e.g.:
<CommandFlag>DefaultInvisible</CommandFlag>
<CommandFlag>DynamicVisibility</CommandFlag>
If you do not want an image next to your command, remove the Icon node /> -->
<Button guid="guidILSpyAddInCmdSet" id="cmdidOpenReferenceInILSpy" priority="0x0600" type="Button">
<Parent guid="guidILSpyAddInCmdSet" id="OpenILSpyRefGroup" />
<Icon guid="guidImages" id="bmpLogo" />
<CommandFlag>DynamicVisibility</CommandFlag>
<CommandFlag>DefaultInvisible</CommandFlag>
<Strings>
<ButtonText>Open in ILSpy</ButtonText>
</Strings>
</Button>
<Button guid="guidILSpyAddInCmdSet" id="cmdidOpenProjectOutputInILSpy" priority="0x0600" type="Button">
<Parent guid="guidILSpyAddInCmdSet" id="OpenILSpyProjGroup" />
<Icon guid="guidImages" id="bmpLogo" />
<CommandFlag>DynamicVisibility</CommandFlag>
<CommandFlag>DefaultInvisible</CommandFlag>
<Strings>
<ButtonText>Open output in ILSpy</ButtonText>
</Strings>
</Button>
<Button guid="guidILSpyAddInCmdSet" id="cmdidOpenCodeItemInILSpy" priority="0x0600" type="Button">
<Parent guid="guidILSpyAddInCmdSet" id="OpenILSpyCodeItemGroup" />
<Icon guid="guidImages" id="bmpLogo" />
<CommandFlag>DynamicVisibility</CommandFlag>
<CommandFlag>DefaultInvisible</CommandFlag>
<Strings>
<ButtonText>Open code in ILSpy</ButtonText>
</Strings>
</Button>
<Button guid="guidILSpyAddInCmdSet" id="cmdidOpenILSpy" priority="0x0600" type="Button">
<Parent guid="guidILSpyAddInCmdSet" id="OpenILSpyGroup" />
<Icon guid="guidImages" id="bmpLogo" />
<Strings>
<ButtonText>ILSpy</ButtonText>
</Strings>
</Button>
</Buttons>
<!--The bitmaps section is used to define the bitmaps that are used for the commands.--> <!--The bitmaps section is used to define the bitmaps that are used for the commands.-->
<Bitmaps> <Bitmaps>
<!-- The bitmap id is defined in a way that is a little bit different from the others: <!-- The bitmap id is defined in a way that is a little bit different from the others:
@ -119,7 +64,6 @@
<Bitmap guid="guidImages" href="Resources\Images.png" usedList="bmpLogo, bmpPic1, bmpPic2, bmpPicX, bmpPicArrows"/> <Bitmap guid="guidImages" href="Resources\Images.png" usedList="bmpLogo, bmpPic1, bmpPic2, bmpPicX, bmpPicArrows"/>
</Bitmaps> </Bitmaps>
</Commands> </Commands>
<Symbols> <Symbols>
<!-- This is the package guid. --> <!-- This is the package guid. -->
<GuidSymbol name="guidILSpyAddInPkg" value="{a9120dbe-164a-4891-842f-fb7829273838}" /> <GuidSymbol name="guidILSpyAddInPkg" value="{a9120dbe-164a-4891-842f-fb7829273838}" />

92
ILSpy.AddIn/ILSpyAddIn.zh-Hans.vsct

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="utf-8"?>
<CommandTable xmlns="http://schemas.microsoft.com/VisualStudio/2005-10-18/CommandTable" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This is the file that defines the actual layout and type of the commands.
It is divided in different sections (e.g. command definition, command
placement, ...), with each defining a specific set of properties.
See the comment before each section for more details about how to
use it. -->
<!-- The VSCT compiler (the tool that translates this file into the binary
format that VisualStudio will consume) has the ability to run a preprocessor
on the vsct file; this preprocessor is (usually) the C++ preprocessor, so
it is possible to define includes and macros with the same syntax used
in C++ files. Using this ability of the compiler here, we include some files
defining some of the constants that we will use inside the file. -->
<!--This is the file that defines the IDs for all the commands exposed by VisualStudio. -->
<Extern href="stdidcmd.h"/>
<!--This header contains the command ids for the menus provided by the shell. -->
<Extern href="vsshlids.h"/>
<Include href="ILSpyAddIn.vsct" />
<!--The Commands section is where we the commands, menus and menu groups are defined.
This section uses a Guid to identify the package that provides the command defined inside it. -->
<Commands package="guidILSpyAddInPkg">
<!-- Inside this section we have different sub-sections: one for the menus, another
for the menu groups, one for the buttons (the actual commands), one for the combos
and the last one for the bitmaps used. Each element is identified by a command id that
is a unique pair of guid and numeric identifier; the guid part of the identifier is usually
called "command set" and is used to group different command inside a logically related
group; your package should define its own command set in order to avoid collisions
with command ids defined by other packages. -->
<!--Buttons section. -->
<!--This section defines the elements the user can interact with, like a menu command or a button
or combo box in a toolbar. -->
<Buttons>
<!--To define a menu group you have to specify its ID, the parent menu and its display priority.
The command is visible and enabled by default. If you need to change the visibility, status, etc, you can use
the CommandFlag node.
You can add more than one CommandFlag node e.g.:
<CommandFlag>DefaultInvisible</CommandFlag>
<CommandFlag>DynamicVisibility</CommandFlag>
If you do not want an image next to your command, remove the Icon node /> -->
<Button guid="guidILSpyAddInCmdSet" id="cmdidOpenReferenceInILSpy" priority="0x0600" type="Button">
<Parent guid="guidILSpyAddInCmdSet" id="OpenILSpyRefGroup" />
<Icon guid="guidImages" id="bmpLogo" />
<CommandFlag>DynamicVisibility</CommandFlag>
<CommandFlag>DefaultInvisible</CommandFlag>
<Strings>
<ButtonText>用 ILSpy 打开</ButtonText>
</Strings>
</Button>
<Button guid="guidILSpyAddInCmdSet" id="cmdidOpenProjectOutputInILSpy" priority="0x0600" type="Button">
<Parent guid="guidILSpyAddInCmdSet" id="OpenILSpyProjGroup" />
<Icon guid="guidImages" id="bmpLogo" />
<CommandFlag>DynamicVisibility</CommandFlag>
<CommandFlag>DefaultInvisible</CommandFlag>
<Strings>
<ButtonText>用 ILSpy 打开输出</ButtonText>
</Strings>
</Button>
<Button guid="guidILSpyAddInCmdSet" id="cmdidOpenCodeItemInILSpy" priority="0x0600" type="Button">
<Parent guid="guidILSpyAddInCmdSet" id="OpenILSpyCodeItemGroup" />
<Icon guid="guidImages" id="bmpLogo" />
<CommandFlag>DynamicVisibility</CommandFlag>
<CommandFlag>DefaultInvisible</CommandFlag>
<Strings>
<ButtonText>用 ILSpy 打开代码</ButtonText>
</Strings>
</Button>
<Button guid="guidILSpyAddInCmdSet" id="cmdidOpenILSpy" priority="0x0600" type="Button">
<Parent guid="guidILSpyAddInCmdSet" id="OpenILSpyGroup" />
<Icon guid="guidImages" id="bmpLogo" />
<Strings>
<ButtonText>ILSpy</ButtonText>
</Strings>
</Button>
</Buttons>
</Commands>
</CommandTable>

1
ILSpy.AddIn/ILSpyAddInPackage.cs

@ -37,6 +37,7 @@ namespace ICSharpCode.ILSpy.AddIn
[ProvideMenuResource("Menus.ctmenu", 1)] [ProvideMenuResource("Menus.ctmenu", 1)]
[Guid(GuidList.guidILSpyAddInPkgString)] [Guid(GuidList.guidILSpyAddInPkgString)]
[ProvideAutoLoad(VSConstants.UICONTEXT.SolutionExistsAndFullyLoaded_string, PackageAutoLoadFlags.BackgroundLoad)] [ProvideAutoLoad(VSConstants.UICONTEXT.SolutionExistsAndFullyLoaded_string, PackageAutoLoadFlags.BackgroundLoad)]
[ProvideBindingPath]
public sealed class ILSpyAddInPackage : AsyncPackage public sealed class ILSpyAddInPackage : AsyncPackage
{ {
/// <summary> /// <summary>

34
ILSpy.AddIn/ILSpyInstance.cs

@ -12,11 +12,11 @@ namespace ICSharpCode.ILSpy.AddIn
{ {
public ILSpyParameters(IEnumerable<string> assemblyFileNames, params string[] arguments) public ILSpyParameters(IEnumerable<string> assemblyFileNames, params string[] arguments)
{ {
this.AssemblyFileNames = assemblyFileNames; this.AssemblyFileNames = assemblyFileNames.ToArray();
this.Arguments = arguments; this.Arguments = arguments;
} }
public IEnumerable<string> AssemblyFileNames { get; private set; } public string[] AssemblyFileNames { get; private set; }
public string[] Arguments { get; private set; } public string[] Arguments { get; private set; }
} }
@ -32,16 +32,16 @@ namespace ICSharpCode.ILSpy.AddIn
static string GetILSpyPath() static 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");
} }
public void Start() public void Start()
{ {
var commandLineArguments = parameters?.AssemblyFileNames?.Concat(parameters.Arguments); var commandLineArguments = parameters?.AssemblyFileNames?.Concat(parameters.Arguments);
string ilSpyExe = GetILSpyPath();
var process = new Process() { var process = new Process() {
StartInfo = new ProcessStartInfo() { StartInfo = new ProcessStartInfo() {
FileName = GetILSpyPath(), FileName = ilSpyExe,
UseShellExecute = false, UseShellExecute = false,
Arguments = "/navigateTo:none" Arguments = "/navigateTo:none"
} }
@ -50,13 +50,14 @@ namespace ICSharpCode.ILSpy.AddIn
if ((commandLineArguments != null) && commandLineArguments.Any()) { if ((commandLineArguments != null) && commandLineArguments.Any()) {
// Only need a message to started process if there are any parameters to pass // Only need a message to started process if there are any parameters to pass
SendMessage(process, "ILSpy:\r\n" + string.Join(Environment.NewLine, commandLineArguments), true); SendMessage(ilSpyExe, "ILSpy:\r\n" + string.Join(Environment.NewLine, commandLineArguments), true);
} }
} }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD110:Observe result of async calls", Justification = "<Pending>")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD110:Observe result of async calls", Justification = "<Pending>")]
void SendMessage(Process ilspyProcess, string message, bool activate) void SendMessage(string ilSpyExe, string message, bool activate)
{ {
string expectedProcessName = Path.GetFileNameWithoutExtension(ilSpyExe);
// We wait asynchronously until target window can be found and try to find it multiple times // We wait asynchronously until target window can be found and try to find it multiple times
Task.Run(async () => { Task.Run(async () => {
bool success = false; bool success = false;
@ -66,14 +67,17 @@ namespace ICSharpCode.ILSpy.AddIn
(hWnd, lParam) => { (hWnd, lParam) => {
string windowTitle = NativeMethods.GetWindowText(hWnd, 100); string windowTitle = NativeMethods.GetWindowText(hWnd, 100);
if (windowTitle.StartsWith("ILSpy", StringComparison.Ordinal)) { if (windowTitle.StartsWith("ILSpy", StringComparison.Ordinal)) {
Debug.WriteLine("Found {0:x4}: {1}", hWnd, windowTitle); string processName = NativeMethods.GetProcessNameFromWindow(hWnd);
IntPtr result = Send(hWnd, message); Debug.WriteLine("Found {0:x4}: '{1}' in '{2}'", hWnd, windowTitle, processName);
Debug.WriteLine("WM_COPYDATA result: {0:x8}", result); if (string.Equals(processName, expectedProcessName, StringComparison.OrdinalIgnoreCase)) {
if (result == (IntPtr)1) { IntPtr result = Send(hWnd, message);
if (activate) Debug.WriteLine("WM_COPYDATA result: {0:x8}", result);
NativeMethods.SetForegroundWindow(hWnd); if (result == (IntPtr)1) {
success = true; if (activate)
return false; // stop enumeration NativeMethods.SetForegroundWindow(hWnd);
success = true;
return false; // stop enumeration
}
} }
} }
return true; // continue enumeration return true; // continue enumeration

3
ILSpy.AddIn/Properties/AssemblyInfo.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Reflection; using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@ -11,6 +12,6 @@ using System.Runtime.InteropServices;
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
[assembly: ComVisible(false)] [assembly: ComVisible(false)]
[assembly: CLSCompliant(false)] [assembly: CLSCompliant(false)]
[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
[assembly: InternalsVisibleTo("ILSpy.AddIn_IntegrationTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100653c4a319be4f524972c3c5bba5fd243330f8e900287d9022d7821a63fd0086fd3801e3683dbe9897f2ecc44727023e9b40adcf180730af70c81c54476b3e5ba8b0f07f5132b2c3cc54347a2c1a9d64ebaaaf3cbffc1a18c427981e2a51d53d5ab02536b7550e732f795121c38a0abfdb38596353525d034baf9e6f1fd8ac4ac")] [assembly: InternalsVisibleTo("ILSpy.AddIn_IntegrationTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100653c4a319be4f524972c3c5bba5fd243330f8e900287d9022d7821a63fd0086fd3801e3683dbe9897f2ecc44727023e9b40adcf180730af70c81c54476b3e5ba8b0f07f5132b2c3cc54347a2c1a9d64ebaaaf3cbffc1a18c427981e2a51d53d5ab02536b7550e732f795121c38a0abfdb38596353525d034baf9e6f1fd8ac4ac")]
[assembly: InternalsVisibleTo("ILSpy.AddIn_UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100653c4a319be4f524972c3c5bba5fd243330f8e900287d9022d7821a63fd0086fd3801e3683dbe9897f2ecc44727023e9b40adcf180730af70c81c54476b3e5ba8b0f07f5132b2c3cc54347a2c1a9d64ebaaaf3cbffc1a18c427981e2a51d53d5ab02536b7550e732f795121c38a0abfdb38596353525d034baf9e6f1fd8ac4ac")] [assembly: InternalsVisibleTo("ILSpy.AddIn_UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100653c4a319be4f524972c3c5bba5fd243330f8e900287d9022d7821a63fd0086fd3801e3683dbe9897f2ecc44727023e9b40adcf180730af70c81c54476b3e5ba8b0f07f5132b2c3cc54347a2c1a9d64ebaaaf3cbffc1a18c427981e2a51d53d5ab02536b7550e732f795121c38a0abfdb38596353525d034baf9e6f1fd8ac4ac")]

95
ILSpy.AddIn/Utils.cs

@ -30,101 +30,6 @@ namespace ICSharpCode.ILSpy.AddIn
static class Utils static class Utils
{ {
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass")]
[DllImport("shell32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern unsafe char** CommandLineToArgvW([MarshalAs(UnmanagedType.LPWStr)] string lpCmdLine, out int pNumArgs);
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass")]
[DllImport("kernel32.dll")]
static extern IntPtr LocalFree(IntPtr hMem);
#region CommandLine <-> Argument Array
/// <summary>
/// Decodes a command line into an array of arguments according to the CommandLineToArgvW rules.
/// </summary>
/// <remarks>
/// Command line parsing rules:
/// - 2n backslashes followed by a quotation mark produce n backslashes, and the quotation mark is considered to be the end of the argument.
/// - (2n) + 1 backslashes followed by a quotation mark again produce n backslashes followed by a quotation mark.
/// - n backslashes not followed by a quotation mark simply produce n backslashes.
/// </remarks>
public static unsafe string[] CommandLineToArgumentArray(string commandLine)
{
if (string.IsNullOrEmpty(commandLine))
return new string[0];
int numberOfArgs;
char** arr = CommandLineToArgvW(commandLine, out numberOfArgs);
if (arr == null)
throw new Win32Exception();
try {
string[] result = new string[numberOfArgs];
for (int i = 0; i < numberOfArgs; i++) {
result[i] = new string(arr[i]);
}
return result;
} finally {
// Free memory obtained by CommandLineToArgW.
LocalFree(new IntPtr(arr));
}
}
static readonly char[] charsNeedingQuoting = { ' ', '\t', '\n', '\v', '"' };
/// <summary>
/// Escapes a set of arguments according to the CommandLineToArgvW rules.
/// </summary>
/// <remarks>
/// Command line parsing rules:
/// - 2n backslashes followed by a quotation mark produce n backslashes, and the quotation mark is considered to be the end of the argument.
/// - (2n) + 1 backslashes followed by a quotation mark again produce n backslashes followed by a quotation mark.
/// - n backslashes not followed by a quotation mark simply produce n backslashes.
/// </remarks>
public static string ArgumentArrayToCommandLine(params string[] arguments)
{
if (arguments == null)
return null;
StringBuilder b = new StringBuilder();
for (int i = 0; i < arguments.Length; i++) {
if (i > 0)
b.Append(' ');
AppendArgument(b, arguments[i]);
}
return b.ToString();
}
static void AppendArgument(StringBuilder b, string arg)
{
if (arg == null) {
return;
}
if (arg.Length > 0 && arg.IndexOfAny(charsNeedingQuoting) < 0) {
b.Append(arg);
} else {
b.Append('"');
for (int j = 0; ; j++) {
int backslashCount = 0;
while (j < arg.Length && arg[j] == '\\') {
backslashCount++;
j++;
}
if (j == arg.Length) {
b.Append('\\', backslashCount * 2);
break;
} else if (arg[j] == '"') {
b.Append('\\', backslashCount * 2 + 1);
b.Append('"');
} else {
b.Append('\\', backslashCount);
b.Append(arg[j]);
}
}
b.Append('"');
}
}
#endregion
public static byte[] HexStringToBytes(string hex) public static byte[] HexStringToBytes(string hex)
{ {
if (hex == null) if (hex == null)

126
ILSpy.AddIn/VSPackage.en-US.resx

@ -0,0 +1,126 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="110" xml:space="preserve">
<value>ILSpy.AddIn</value>
</data>
<data name="112" xml:space="preserve">
<value>Integration of the ILSpy Decompiler into Visual Studio.</value>
</data>
</root>

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

Loading…
Cancel
Save