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

2
DecompilerNuGetDemos.workbook

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

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

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

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

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

12
ICSharpCode.Decompiler.Tests/DataFlowTest.cs

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

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

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

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

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

4
ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs

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

13
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

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

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

@ -228,7 +228,7 @@ namespace System.Runtime.CompilerServices @@ -228,7 +228,7 @@ namespace System.Runtime.CompilerServices
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IntPtr ByteOffset<T>(ref T origin, ref T target)
{
return Unsafe.ByteOffset(ref target, ref origin);
return Unsafe.ByteOffset(target: ref target, origin: ref origin);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]

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

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

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

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

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

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

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

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

42
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

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

12
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

@ -197,8 +197,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -197,8 +197,7 @@ namespace ICSharpCode.Decompiler.CSharp
} else if (localFunction != null) {
var ide = new IdentifierExpression(localFunction.Name);
if (method.TypeArguments.Count > 0) {
int skipCount = localFunction.ReducedMethod.NumberOfCompilerGeneratedTypeParameters;
ide.TypeArguments.AddRange(method.TypeArguments.Skip(skipCount).Select(expressionBuilder.ConvertType));
ide.TypeArguments.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType));
}
ide.AddAnnotation(localFunction);
target = ide.WithoutILInstruction()
@ -1327,7 +1326,6 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1327,7 +1326,6 @@ namespace ICSharpCode.Decompiler.CSharp
target = default;
targetType = default;
methodName = localFunction.Name;
// TODO : think about how to handle generic local functions
} else if (method.IsExtensionMethod && invokeMethod != null && method.Parameters.Count - 1 == invokeMethod.Parameters.Count) {
step = 5;
targetType = method.Parameters[0].Type;
@ -1406,11 +1404,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1406,11 +1404,7 @@ namespace ICSharpCode.Decompiler.CSharp
} else {
var ide = new IdentifierExpression(methodName);
if ((step & 2) != 0) {
int skipCount = 0;
if (localFunction != null && method.TypeArguments.Count > 0) {
skipCount = localFunction.ReducedMethod.NumberOfCompilerGeneratedTypeParameters;
}
ide.TypeArguments.AddRange(method.TypeArguments.Skip(skipCount).Select(expressionBuilder.ConvertType));
ide.TypeArguments.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType));
}
targetExpression = ide.WithRR(result);
}
@ -1469,7 +1463,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1469,7 +1463,7 @@ namespace ICSharpCode.Decompiler.CSharp
method.DeclaringType,
new IParameterizedMember[] { method }
)
}, method.TypeArguments.Skip(localFunction.ReducedMethod.NumberOfCompilerGeneratedTypeParameters).ToArray()
}, method.TypeArguments
);
}

71
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -92,6 +92,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -92,6 +92,7 @@ namespace ICSharpCode.Decompiler.CSharp
this.astBuilder = new TypeSystemAstBuilder(resolver);
this.astBuilder.AlwaysUseShortTypeNames = true;
this.astBuilder.AddResolveResultAnnotations = true;
this.astBuilder.ShowAttributes = true;
this.astBuilder.UseNullableSpecifierForValueTypes = settings.LiftNullables;
this.typeInference = new TypeInference(compilation) { Algorithm = TypeInferenceAlgorithm.Improved };
}
@ -1075,7 +1076,11 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1075,7 +1076,11 @@ namespace ICSharpCode.Decompiler.CSharp
return null;
if (inst.Operator == BinaryNumericOperator.Sub && inst.LeftInputType == StackType.Ref && inst.RightInputType == StackType.Ref) {
// ref - ref => i
return CallUnsafeIntrinsic("ByteOffset", new[] { left.Expression, right.Expression }, compilation.FindType(KnownTypeCode.IntPtr), inst);
return CallUnsafeIntrinsic("ByteOffset", new[] {
// ByteOffset() expects the parameters the wrong way around, so order using named arguments
new NamedArgumentExpression("target", left.Expression),
new NamedArgumentExpression("origin", right.Expression)
}, compilation.FindType(KnownTypeCode.IntPtr), inst);
}
if (inst.LeftInputType == StackType.Ref && inst.RightInputType.IsIntegerType()
&& left.Type is ByReferenceType brt) {
@ -1788,6 +1793,15 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1788,6 +1793,15 @@ namespace ICSharpCode.Decompiler.CSharp
// Case 4 (left-over extension from implicit conversion) can also be handled by our caller.
return arg.WithILInstruction(inst);
}
case ConversionKind.Invalid:
if (inst.InputType == StackType.Unknown && inst.TargetType == IL.PrimitiveType.None && arg.Type.Kind == TypeKind.Unknown) {
// Unknown -> O conversion.
// Our post-condition allows us to also use expressions with unknown type where O is expected,
// so avoid introducing an `(object)` cast because we're likely to cast back to the same unknown type,
// just in a signature context where we know that it's a class type.
return arg.WithILInstruction(inst);
}
goto default;
default: {
// We need to convert to inst.TargetType, or to an equivalent type.
IType targetType;
@ -1983,7 +1997,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1983,7 +1997,7 @@ namespace ICSharpCode.Decompiler.CSharp
return SpecialType.UnknownType;
}
internal IEnumerable<ParameterDeclaration> MakeParameters(IReadOnlyList<IParameter> parameters, ILFunction function)
IEnumerable<ParameterDeclaration> MakeParameters(IReadOnlyList<IParameter> parameters, ILFunction function)
{
var variables = function.Variables.Where(v => v.Kind == VariableKind.Parameter).ToDictionary(v => v.Index);
int i = 0;
@ -1992,9 +2006,6 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1992,9 +2006,6 @@ namespace ICSharpCode.Decompiler.CSharp
if (string.IsNullOrEmpty(pd.Name) && !pd.Type.IsArgList()) {
// needs to be consistent with logic in ILReader.CreateILVarable(ParameterDefinition)
pd.Name = "P_" + i;
// if this is a local function, we have to skip the parameters for closure references
if (settings.LocalFunctions && function.Kind == ILFunctionKind.LocalFunction && IL.Transforms.LocalFunctionDecompiler.IsClosureParameter(parameter, decompilationContext))
break;
}
if (settings.AnonymousTypes && parameter.Type.ContainsAnonymousType())
pd.Type = null;
@ -2922,6 +2933,56 @@ namespace ICSharpCode.Decompiler.CSharp @@ -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)
{
// HACK: this is only correct if the argument is an R-value; otherwise we're missing the copy to the temporary

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

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

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

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

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

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

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

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

22
ICSharpCode.Decompiler/CSharp/RequiredNamespaceCollector.cs

@ -17,6 +17,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -17,6 +17,7 @@ namespace ICSharpCode.Decompiler.CSharp
static readonly Decompiler.TypeSystem.GenericContext genericContext = default;
readonly HashSet<string> namespaces;
readonly HashSet<IType> visitedTypes = new HashSet<IType>();
public RequiredNamespaceCollector(HashSet<string> namespaces)
{
@ -92,17 +93,18 @@ namespace ICSharpCode.Decompiler.CSharp @@ -92,17 +93,18 @@ namespace ICSharpCode.Decompiler.CSharp
CollectNamespacesForTypeReference(field.ReturnType);
break;
case IMethod method:
HandleAttributes(method.GetAttributes());
HandleAttributes(method.GetReturnTypeAttributes());
CollectNamespacesForTypeReference(method.ReturnType);
foreach (var param in method.Parameters) {
HandleAttributes(param.GetAttributes());
CollectNamespacesForTypeReference(param.Type);
}
HandleTypeParameters(method.TypeParameters);
var reader = module.PEFile.Reader;
var parts = mappingInfo.GetMethodParts((MethodDefinitionHandle)method.MetadataToken).ToList();
foreach (var part in parts) {
var partMethod = module.ResolveMethod(part, genericContext);
HandleAttributes(partMethod.GetAttributes());
HandleAttributes(partMethod.GetReturnTypeAttributes());
CollectNamespacesForTypeReference(partMethod.ReturnType);
foreach (var param in partMethod.Parameters) {
HandleAttributes(param.GetAttributes());
CollectNamespacesForTypeReference(param.Type);
}
HandleTypeParameters(partMethod.TypeParameters);
HandleOverrides(part.GetMethodImplementations(module.metadata), module);
var methodDef = module.metadata.GetMethodDefinition(part);
if (method.HasBody) {
@ -141,13 +143,15 @@ namespace ICSharpCode.Decompiler.CSharp @@ -141,13 +143,15 @@ namespace ICSharpCode.Decompiler.CSharp
void CollectNamespacesForTypeReference(IType type)
{
if (!visitedTypes.Add(type))
return;
switch (type) {
case ParameterizedType parameterizedType:
namespaces.Add(parameterizedType.Namespace);
CollectNamespacesForTypeReference(parameterizedType.GenericType);
foreach (var arg in parameterizedType.TypeArguments)
CollectNamespacesForTypeReference(arg);
break;
return; // no need to collect base types again
case TypeWithElementType typeWithElementType:
CollectNamespacesForTypeReference(typeWithElementType.ElementType);
break;

43
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

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

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

@ -346,6 +346,16 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -346,6 +346,16 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
VisitChildren (caseLabel);
}
public virtual void VisitSwitchExpression(SwitchExpression switchExpression)
{
VisitChildren(switchExpression);
}
public virtual void VisitSwitchExpressionSection(SwitchExpressionSection switchExpressionSection)
{
VisitChildren(switchExpressionSection);
}
public virtual void VisitThrowStatement (ThrowStatement throwStatement)
{
VisitChildren (throwStatement);
@ -988,6 +998,16 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -988,6 +998,16 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return VisitChildren (caseLabel);
}
public virtual T VisitSwitchExpression(SwitchExpression switchExpression)
{
return VisitChildren(switchExpression);
}
public virtual T VisitSwitchExpressionSection(SwitchExpressionSection switchExpressionSection)
{
return VisitChildren(switchExpressionSection);
}
public virtual T VisitThrowStatement (ThrowStatement throwStatement)
{
return VisitChildren (throwStatement);
@ -1630,6 +1650,16 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1630,6 +1650,16 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return VisitChildren (caseLabel, data);
}
public virtual S VisitSwitchExpression(SwitchExpression switchExpression, T data)
{
return VisitChildren(switchExpression, data);
}
public virtual S VisitSwitchExpressionSection(SwitchExpressionSection switchExpressionSection, T data)
{
return VisitChildren(switchExpressionSection, data);
}
public virtual S VisitThrowStatement (ThrowStatement throwStatement, T data)
{
return VisitChildren (throwStatement, data);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

59
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -113,17 +113,19 @@ namespace ICSharpCode.Decompiler @@ -113,17 +113,19 @@ namespace ICSharpCode.Decompiler
asyncEnumerator = false;
staticLocalFunctions = false;
ranges = false;
switchExpressions = false;
}
if (languageVersion < CSharp.LanguageVersion.Preview) {
nativeIntegers = false;
initAccessors = false;
}
}
public CSharp.LanguageVersion GetMinimumRequiredVersion()
{
if (nativeIntegers)
if (nativeIntegers || initAccessors)
return CSharp.LanguageVersion.Preview;
if (nullableReferenceTypes || readOnlyMethods || asyncEnumerator || asyncUsingAndForEachStatement || staticLocalFunctions || ranges)
if (nullableReferenceTypes || readOnlyMethods || asyncEnumerator || asyncUsingAndForEachStatement || staticLocalFunctions || ranges || switchExpressions)
return CSharp.LanguageVersion.CSharp8_0;
if (introduceUnmanagedConstraint || tupleComparisons || stackAllocInitializers || patternBasedFixedStatement)
return CSharp.LanguageVersion.CSharp7_3;
@ -163,6 +165,40 @@ namespace ICSharpCode.Decompiler @@ -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;
/// <summary>
@ -1387,6 +1423,25 @@ namespace ICSharpCode.Decompiler @@ -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;
[Browsable(false)]

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -70,6 +70,7 @@ @@ -70,6 +70,7 @@
<Compile Include="CSharp\SequencePointBuilder.cs" />
<Compile Include="CSharp\ProjectDecompiler\TargetFramework.cs" />
<Compile Include="CSharp\ProjectDecompiler\TargetServices.cs" />
<Compile Include="CSharp\Syntax\Expressions\SwitchExpression.cs" />
<Compile Include="CSharp\Syntax\FunctionPointerType.cs" />
<Compile Include="IL\Transforms\FixLoneIsInst.cs" />
<Compile Include="IL\Transforms\IndexRangeTransform.cs" />

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

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

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

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

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

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

8
ICSharpCode.Decompiler/IL/ILReader.cs

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

2
ICSharpCode.Decompiler/IL/Instructions.cs

@ -1497,7 +1497,7 @@ namespace ICSharpCode.Decompiler.IL @@ -1497,7 +1497,7 @@ namespace ICSharpCode.Decompiler.IL
/// <summary>Switch statement</summary>
public sealed partial class SwitchInstruction : ILInstruction
{
public override StackType ResultType { get { return StackType.Void; } }
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitSwitchInstruction(this);

2
ICSharpCode.Decompiler/IL/Instructions.tt

@ -116,7 +116,7 @@ @@ -116,7 +116,7 @@
new ChildInfo("fallbackInst"),
}), CustomConstructor, CustomComputeFlags, CustomWriteTo),
new OpCode("switch", "Switch statement",
CustomClassName("SwitchInstruction"), CustomConstructor, CustomComputeFlags, CustomWriteTo, ResultType("Void"),
CustomClassName("SwitchInstruction"), CustomConstructor, CustomComputeFlags, CustomWriteTo,
MatchCondition("IsLifted == o.IsLifted && Value.PerformMatch(o.Value, ref match) && Patterns.ListMatch.DoMatch(this.Sections, o.Sections, ref match)")),
new OpCode("switch.section", "Switch section within a switch statement",
CustomClassName("SwitchSection"), CustomChildren(new [] { new ChildInfo("body") }),

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -53,15 +53,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -53,15 +53,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms
public void Run(Block block, int pos, StatementTransformContext context)
{
InlineOneIfPossible(block, pos, OptionsForBlock(block, pos), context: context);
InlineOneIfPossible(block, pos, OptionsForBlock(block, pos, context), context: context);
}
internal static InliningOptions OptionsForBlock(Block block, int pos)
internal static InliningOptions OptionsForBlock(Block block, int pos, ILTransformContext context)
{
InliningOptions options = InliningOptions.None;
if (IsCatchWhenBlock(block))
if (context.Settings.AggressiveInlining || IsCatchWhenBlock(block)) {
options |= InliningOptions.Aggressive;
else {
} else {
var function = block.Ancestors.OfType<ILFunction>().FirstOrDefault();
var inst = block.Instructions[pos];
if (IsInConstructorInitializer(function, inst))
@ -77,7 +77,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -77,7 +77,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
for (int i = instructions.Count - 1; i >= 0; i--) {
if (instructions[i] is StLoc inst) {
InliningOptions options = InliningOptions.None;
if (IsCatchWhenBlock(block) || IsInConstructorInitializer(function, inst))
if (context.Settings.AggressiveInlining || IsCatchWhenBlock(block) || IsInConstructorInitializer(function, inst))
options = InliningOptions.Aggressive;
if (InlineOneIfPossible(block, i, options, context)) {
modified = true;
@ -418,6 +418,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -418,6 +418,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
case OpCode.NumericCompoundAssign:
case OpCode.UserDefinedCompoundAssign:
case OpCode.Await:
case OpCode.SwitchInstruction:
return true;
case OpCode.LdLoc:
if (v.StateMachineField == null && ((LdLoc)inlinedExpression).Variable.StateMachineField != null) {
@ -479,32 +480,21 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -479,32 +480,21 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true;
}
break;
case OpCode.Leave:
case OpCode.YieldReturn:
return true;
case OpCode.SwitchInstruction:
//case OpCode.BinaryNumericInstruction when parent.SlotInfo == SwitchInstruction.ValueSlot:
case OpCode.StringToInt when parent.SlotInfo == SwitchInstruction.ValueSlot:
return true;
}
// decide based on the top-level target instruction into which we are inlining:
switch (next.OpCode) {
case OpCode.Leave:
case OpCode.YieldReturn:
return parent == next;
case OpCode.IfInstruction:
while (parent.MatchLogicNot(out _)) {
parent = parent.Parent;
}
return parent == next;
case OpCode.BlockContainer:
if (((BlockContainer)next).EntryPoint.Instructions[0] is SwitchInstruction switchInst) {
next = switchInst;
goto case OpCode.SwitchInstruction;
} else {
return false;
}
case OpCode.SwitchInstruction:
if (parent == next)
return true;
if (parent.MatchBinaryNumericInstruction(BinaryNumericOperator.Sub) && parent.Parent == next)
return true;
if (parent is StringToInt stringToInt && stringToInt.Parent == next)
return true;
return false;
default:
return false;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

55
ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs

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

107
ICSharpCode.Decompiler/Metadata/DotNetCorePathFinderExtensions.cs

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

4
ICSharpCode.Decompiler/Metadata/MetadataExtensions.cs

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

4
ICSharpCode.Decompiler/Metadata/PEFile.cs

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

33
ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs

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

25
ICSharpCode.Decompiler/Output/TextTokenWriter.cs

@ -19,6 +19,7 @@ @@ -19,6 +19,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.CSharp.OutputVisitor;
using ICSharpCode.Decompiler.CSharp.Resolver;
@ -26,6 +27,7 @@ using ICSharpCode.Decompiler.CSharp.Syntax; @@ -26,6 +27,7 @@ using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.IL;
using ICSharpCode.Decompiler.Semantics;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
namespace ICSharpCode.Decompiler
{
@ -110,6 +112,7 @@ namespace ICSharpCode.Decompiler @@ -110,6 +112,7 @@ namespace ICSharpCode.Decompiler
if (symbol != null && node.Role == Roles.Type && node.Parent is ObjectCreateExpression) {
symbol = node.Parent.GetSymbol();
}
if (node is IdentifierExpression && node.Role == Roles.TargetExpression && node.Parent is InvocationExpression && symbol is IMember member) {
var declaringType = member.DeclaringType;
if (declaringType != null && declaringType.Kind == TypeKind.Delegate)
@ -123,10 +126,8 @@ namespace ICSharpCode.Decompiler @@ -123,10 +126,8 @@ namespace ICSharpCode.Decompiler
if (symbol == null)
return null;
//if (settings.AutomaticEvents && member is FieldDefinition) {
// var field = (FieldDefinition)member;
// return field.DeclaringType.Events.FirstOrDefault(ev => ev.Name == field.Name) ?? member;
//}
if (symbol is LocalFunctionMethod)
return null;
return symbol;
}
@ -142,14 +143,18 @@ namespace ICSharpCode.Decompiler @@ -142,14 +143,18 @@ namespace ICSharpCode.Decompiler
if (letClauseVariable != null)
return letClauseVariable;
var gotoStatement = node as GotoStatement;
if (gotoStatement != null)
{
if (node is GotoStatement gotoStatement) {
var method = nodeStack.Select(nd => nd.GetSymbol() as IMethod).FirstOrDefault(mr => mr != null);
if (method != null)
return method + gotoStatement.Label;
}
if (node.Role == Roles.TargetExpression && node.Parent is InvocationExpression) {
var symbol = node.Parent.GetSymbol();
if (symbol is LocalFunctionMethod)
return symbol;
}
return null;
}
@ -177,8 +182,8 @@ namespace ICSharpCode.Decompiler @@ -177,8 +182,8 @@ namespace ICSharpCode.Decompiler
return method + label.Label;
}
if (node is LocalFunctionDeclarationStatement) {
var localFunction = node.GetResolveResult() as MemberResolveResult;
if (node is MethodDeclaration && node.Parent is LocalFunctionDeclarationStatement) {
var localFunction = node.Parent.GetResolveResult() as MemberResolveResult;
if (localFunction != null)
return localFunction.Member;
}
@ -404,7 +409,7 @@ namespace ICSharpCode.Decompiler @@ -404,7 +409,7 @@ namespace ICSharpCode.Decompiler
public static bool IsDefinition(ref AstNode node)
{
if (node is EntityDeclaration)
if (node is EntityDeclaration && !(node.Parent is LocalFunctionDeclarationStatement))
return true;
if (node is VariableInitializer && node.Parent is FieldDeclaration) {
node = node.Parent;

18
ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs

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

6
ICSharpCode.Decompiler/TypeSystem/IMethod.cs

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

12
ICSharpCode.Decompiler/TypeSystem/KnownTypeReference.cs

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

60
ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs

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

12
ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs

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

19
ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs

@ -292,7 +292,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -292,7 +292,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// Gets all type definitions in the compilation.
/// This may include types from referenced assemblies that are not accessible in the main assembly.
/// </summary>
public static IEnumerable<ITypeDefinition> GetAllTypeDefinitions (this ICompilation compilation)
public static IEnumerable<ITypeDefinition> GetAllTypeDefinitions(this ICompilation compilation)
{
return compilation.Modules.SelectMany(a => a.TypeDefinitions);
}
@ -301,7 +301,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -301,7 +301,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// Gets all top level type definitions in the compilation.
/// This may include types from referenced assemblies that are not accessible in the main assembly.
/// </summary>
public static IEnumerable<ITypeDefinition> GetTopLevelTypeDefinitions (this ICompilation compilation)
public static IEnumerable<ITypeDefinition> GetTopLevelTypeDefinitions(this ICompilation compilation)
{
return compilation.Modules.SelectMany(a => a.TopLevelTypeDefinitions);
}
@ -390,7 +390,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -390,7 +390,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// (if the given <paramref name="entity"/> in an <c>override</c>)
/// should be returned.
/// </param>
public static bool HasAttribute(this IEntity entity, KnownAttribute attributeType, bool inherit=false)
public static bool HasAttribute(this IEntity entity, KnownAttribute attributeType, bool inherit = false)
{
return GetAttribute(entity, attributeType, inherit) != null;
}
@ -410,7 +410,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -410,7 +410,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// If inherit is true, an from the entity itself will be returned if possible;
/// and the base entity will only be searched if none exists.
/// </returns>
public static IAttribute GetAttribute(this IEntity entity, KnownAttribute attributeType, bool inherit=false)
public static IAttribute GetAttribute(this IEntity entity, KnownAttribute attributeType, bool inherit = false)
{
return GetAttributes(entity, inherit).FirstOrDefault(a => a.AttributeType.IsKnownType(attributeType));
}
@ -478,8 +478,8 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -478,8 +478,8 @@ namespace ICSharpCode.Decompiler.TypeSystem
public static ITypeDefinition GetTypeDefinition(this IModule module, string namespaceName, string name, int typeParameterCount = 0)
{
if (module == null)
throw new ArgumentNullException ("assembly");
return module.GetTypeDefinition (new TopLevelTypeName (namespaceName, name, typeParameterCount));
throw new ArgumentNullException("assembly");
return module.GetTypeDefinition(new TopLevelTypeName(namespaceName, name, typeParameterCount));
}
#endregion
@ -560,7 +560,12 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -560,7 +560,12 @@ namespace ICSharpCode.Decompiler.TypeSystem
public static IModule FindModuleByReference(this ICompilation compilation, IAssemblyReference assemblyName)
{
foreach (var module in compilation.Modules) {
if (module.FullAssemblyName == assemblyName.FullName) {
if (string.Equals(module.FullAssemblyName, assemblyName.FullName, StringComparison.OrdinalIgnoreCase)) {
return module;
}
}
foreach (var module in compilation.Modules) {
if (string.Equals(module.Name, assemblyName.Name, StringComparison.OrdinalIgnoreCase)) {
return module;
}
}

1
ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs

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

8
ICSharpCode.Decompiler/Util/CollectionExtensions.cs

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

23
ILSpy.AddIn/AssemblyFileFinder.cs

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

14
ILSpy.AddIn/Commands/OpenCodeItemCommand.cs

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

5
ILSpy.AddIn/Commands/OpenILSpyCommand.cs

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

84
ILSpy.AddIn/ILSpy.AddIn.csproj

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

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

@ -0,0 +1,94 @@ @@ -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 @@ @@ -20,9 +20,6 @@
<!--This header contains the command ids for the menus provided by the shell. -->
<Extern href="vsshlids.h"/>
<!--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">
@ -57,58 +54,6 @@ @@ -57,58 +54,6 @@
</Group>
</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.-->
<Bitmaps>
<!-- The bitmap id is defined in a way that is a little bit different from the others:
@ -119,7 +64,6 @@ @@ -119,7 +64,6 @@
<Bitmap guid="guidImages" href="Resources\Images.png" usedList="bmpLogo, bmpPic1, bmpPic2, bmpPicX, bmpPicArrows"/>
</Bitmaps>
</Commands>
<Symbols>
<!-- This is the package guid. -->
<GuidSymbol name="guidILSpyAddInPkg" value="{a9120dbe-164a-4891-842f-fb7829273838}" />

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

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

34
ILSpy.AddIn/ILSpyInstance.cs

@ -12,11 +12,11 @@ namespace ICSharpCode.ILSpy.AddIn @@ -12,11 +12,11 @@ namespace ICSharpCode.ILSpy.AddIn
{
public ILSpyParameters(IEnumerable<string> assemblyFileNames, params string[] arguments)
{
this.AssemblyFileNames = assemblyFileNames;
this.AssemblyFileNames = assemblyFileNames.ToArray();
this.Arguments = arguments;
}
public IEnumerable<string> AssemblyFileNames { get; private set; }
public string[] AssemblyFileNames { get; private set; }
public string[] Arguments { get; private set; }
}
@ -32,16 +32,16 @@ namespace ICSharpCode.ILSpy.AddIn @@ -32,16 +32,16 @@ namespace ICSharpCode.ILSpy.AddIn
static string GetILSpyPath()
{
var basePath = Path.GetDirectoryName(typeof(ILSpyAddInPackage).Assembly.Location);
return Path.Combine(basePath, "ILSpy.exe");
return Path.Combine(basePath, "ILSpy", "ILSpy.exe");
}
public void Start()
{
var commandLineArguments = parameters?.AssemblyFileNames?.Concat(parameters.Arguments);
string ilSpyExe = GetILSpyPath();
var process = new Process() {
StartInfo = new ProcessStartInfo() {
FileName = GetILSpyPath(),
FileName = ilSpyExe,
UseShellExecute = false,
Arguments = "/navigateTo:none"
}
@ -50,13 +50,14 @@ namespace ICSharpCode.ILSpy.AddIn @@ -50,13 +50,14 @@ namespace ICSharpCode.ILSpy.AddIn
if ((commandLineArguments != null) && commandLineArguments.Any()) {
// 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>")]
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
Task.Run(async () => {
bool success = false;
@ -66,14 +67,17 @@ namespace ICSharpCode.ILSpy.AddIn @@ -66,14 +67,17 @@ namespace ICSharpCode.ILSpy.AddIn
(hWnd, lParam) => {
string windowTitle = NativeMethods.GetWindowText(hWnd, 100);
if (windowTitle.StartsWith("ILSpy", StringComparison.Ordinal)) {
Debug.WriteLine("Found {0:x4}: {1}", hWnd, windowTitle);
IntPtr result = Send(hWnd, message);
Debug.WriteLine("WM_COPYDATA result: {0:x8}", result);
if (result == (IntPtr)1) {
if (activate)
NativeMethods.SetForegroundWindow(hWnd);
success = true;
return false; // stop enumeration
string processName = NativeMethods.GetProcessNameFromWindow(hWnd);
Debug.WriteLine("Found {0:x4}: '{1}' in '{2}'", hWnd, windowTitle, processName);
if (string.Equals(processName, expectedProcessName, StringComparison.OrdinalIgnoreCase)) {
IntPtr result = Send(hWnd, message);
Debug.WriteLine("WM_COPYDATA result: {0:x8}", result);
if (result == (IntPtr)1) {
if (activate)
NativeMethods.SetForegroundWindow(hWnd);
success = true;
return false; // stop enumeration
}
}
}
return true; // continue enumeration

3
ILSpy.AddIn/Properties/AssemblyInfo.cs

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

95
ILSpy.AddIn/Utils.cs

@ -30,101 +30,6 @@ namespace ICSharpCode.ILSpy.AddIn @@ -30,101 +30,6 @@ namespace ICSharpCode.ILSpy.AddIn
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)
{
if (hex == null)

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

@ -0,0 +1,126 @@ @@ -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