Browse Source

Merge branch 'master' of github.com:icsharpcode/ILSpy into fsharp_using

# Conflicts:
#	ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
#	ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs
#	ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
pull/900/head
Siegfried Pammer 8 years ago
parent
commit
6ca952f1c9
  1. 3
      .gitignore
  2. 11
      BuildTools/appveyor-install.ps1
  3. 1
      BuildTools/update-assemblyinfo.ps1
  4. 21
      ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs
  5. 2
      ICSharpCode.Decompiler.Tests/DataFlowTest.cs
  6. 205
      ICSharpCode.Decompiler.Tests/DelegateConstruction.cs
  7. 17
      ICSharpCode.Decompiler.Tests/Helpers/SdkUtility.cs
  8. 26
      ICSharpCode.Decompiler.Tests/Helpers/Tester.cs
  9. 6
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  10. 2
      ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs
  11. 35
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  12. 89
      ICSharpCode.Decompiler.Tests/Switch.cs
  13. 6
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/Async.cs
  14. 47
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/Jmp.il
  15. 390
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/LINQRaytracer.cs
  16. 76
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/Switch.cs
  17. 2
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/.gitignore
  18. 5
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/CheckedUnchecked.cs
  19. 199
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs
  20. 1400
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.il
  21. 1150
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.opt.il
  22. 1144
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.opt.roslyn.il
  23. 1362
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.roslyn.il
  24. 17
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExceptionHandling.cs
  25. 14
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExceptionHandling.il
  26. 14
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExceptionHandling.opt.il
  27. 259
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExceptionHandling.opt.roslyn.il
  28. 255
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExceptionHandling.roslyn.il
  29. 118
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/FixProxyCalls.cs
  30. 1579
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/FixProxyCalls.il
  31. 1323
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/FixProxyCalls.opt.il
  32. 1294
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/FixProxyCalls.opt.roslyn.il
  33. 1424
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/FixProxyCalls.roslyn.il
  34. 484
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.cs
  35. 1522
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.il
  36. 1281
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.opt.il
  37. 1314
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.opt.roslyn.il
  38. 1533
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.roslyn.il
  39. 17
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.cs
  40. 70
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.il
  41. 57
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.opt.il
  42. 64
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.opt.roslyn.il
  43. 74
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.roslyn.il
  44. 2
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/PropertiesAndEvents.cs
  45. 2
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/QueryExpressions.cs
  46. 450
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs
  47. 1624
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il
  48. 1385
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il
  49. 1502
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il
  50. 1853
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il
  51. 15
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.cs
  52. 64
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.il
  53. 46
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.opt.il
  54. 42
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.opt.roslyn.il
  55. 60
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.roslyn.il
  56. 2
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Using.cs
  57. 14
      ICSharpCode.Decompiler/CSharp/Annotations.cs
  58. 31
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  59. 114
      ICSharpCode.Decompiler/CSharp/CallBuilder.cs
  60. 162
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  61. 14
      ICSharpCode.Decompiler/CSharp/NRefactoryExtensions.cs
  62. 14
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
  63. 12
      ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertMissingTokensDecorator.cs
  64. 6
      ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterOutputFormatter.cs
  65. 77
      ICSharpCode.Decompiler/CSharp/Resolver/LambdaResolveResult.cs
  66. 329
      ICSharpCode.Decompiler/CSharp/SequencePointBuilder.cs
  67. 164
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  68. 6
      ICSharpCode.Decompiler/CSharp/Syntax/Expressions/PrimitiveExpression.cs
  69. 26
      ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs
  70. 6
      ICSharpCode.Decompiler/CSharp/Transforms/FlattenSwitchBlocks.cs
  71. 15
      ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs
  72. 172
      ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs
  73. 4
      ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs
  74. 27
      ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs
  75. 4
      ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs
  76. 36
      ICSharpCode.Decompiler/DecompilerSettings.cs
  77. 20
      ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs
  78. 80
      ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs
  79. 19
      ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs
  80. 2
      ICSharpCode.Decompiler/DotNetCore/DotNetCorePathFinder.cs
  81. 1
      ICSharpCode.Decompiler/DotNetCore/DotNetCorePathFinderExtensions.cs
  82. 63
      ICSharpCode.Decompiler/FlowAnalysis/ControlFlowNode.cs
  83. 8
      ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs
  84. 28
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  85. 15
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.nuspec.template
  86. 19
      ICSharpCode.Decompiler/IL/BlockBuilder.cs
  87. 17
      ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs
  88. 51
      ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs
  89. 50
      ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs
  90. 151
      ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs
  91. 4
      ICSharpCode.Decompiler/IL/ControlFlow/StateRangeAnalysis.cs
  92. 61
      ICSharpCode.Decompiler/IL/ControlFlow/SwitchAnalysis.cs
  93. 45
      ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs
  94. 2
      ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs
  95. 28
      ICSharpCode.Decompiler/IL/DetectedLoop.cs
  96. 81
      ICSharpCode.Decompiler/IL/ILAstWritingOptions.cs
  97. 74
      ICSharpCode.Decompiler/IL/ILReader.cs
  98. 11
      ICSharpCode.Decompiler/IL/InstructionOutputExtensions.cs
  99. 524
      ICSharpCode.Decompiler/IL/Instructions.cs
  100. 25
      ICSharpCode.Decompiler/IL/Instructions.tt
  101. Some files were not shown because too many files have changed in this diff Show More

3
.gitignore vendored

@ -10,4 +10,5 @@ _ReSharper*/ @@ -10,4 +10,5 @@ _ReSharper*/
*.ReSharper
*.patch
.vs/
/ILSpy.AddIn/Packages/*
/ILSpy.AddIn/Packages/*
/ICSharpCode.Decompiler.Tests/TestCases/Correctness/*.exe

11
BuildTools/appveyor-install.ps1

@ -23,9 +23,16 @@ if ($env:APPVEYOR_REPO_BRANCH -ne 'master') { @@ -23,9 +23,16 @@ if ($env:APPVEYOR_REPO_BRANCH -ne 'master') {
} else {
$branch = "";
}
if ($env:APPVEYOR_PULL_REQUEST_NUMBER) {
$suffix = "-pr$env:APPVEYOR_PULL_REQUEST_NUMBER";
} else {
$suffix = "";
}
$revision = [Int32]::Parse((git rev-list --count "$baseCommit..HEAD")) + $baseCommitRev;
$newVersion="$major.$minor.$build.$revision";
$env:appveyor_build_version="$newVersion$branch$versionName";
appveyor UpdateBuild -Version "$newVersion$branch$versionName";
$env:APPVEYOR_BUILD_VERSION="$newVersion$branch$versionName$suffix";
$env:ILSPY_VERSION_NUMBER="$newVersion$branch$versionName$suffix";
appveyor UpdateBuild -Version "$newVersion$branch$versionName$suffix";
Write-Host "new version: $newVersion$branch$versionName$suffix";

1
BuildTools/update-assemblyinfo.ps1

@ -42,6 +42,7 @@ function gitBranch() { @@ -42,6 +42,7 @@ function gitBranch() {
$templateFiles = (
@{Input=$globalAssemblyInfoTemplateFile; Output="ILSpy/Properties/AssemblyInfo.cs"},
@{Input="ICSharpCode.Decompiler/Properties/AssemblyInfo.template.cs"; Output="ICSharpCode.Decompiler/Properties/AssemblyInfo.cs"},
@{Input="ICSharpCode.Decompiler/ICSharpCode.Decompiler.nuspec.template"; Output="ICSharpCode.Decompiler/ICSharpCode.Decompiler.nuspec"},
@{Input="ILSpy/Properties/app.config.template"; Output = "ILSpy/app.config"}
);
[string]$mutexId = "ILSpyUpdateAssemblyInfo" + $PSScriptRoot.GetHashCode();

21
ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs

@ -162,6 +162,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -162,6 +162,12 @@ namespace ICSharpCode.Decompiler.Tests
RunIL("BitNot.il", CompilerOptions.UseDebug | CompilerOptions.Force32Bit, AssemblerOptions.Force32Bit);
}
[Test]
public void Jmp()
{
RunIL("Jmp.il");
}
[Test]
public void UnsafeCode([ValueSource("defaultOptions")] CompilerOptions options)
{
@ -192,8 +198,14 @@ namespace ICSharpCode.Decompiler.Tests @@ -192,8 +198,14 @@ namespace ICSharpCode.Decompiler.Tests
RunCS(options: options);
}
[Test, Ignore("Run() method cannot be fully decompiled.")]
public void Async([ValueSource("defaultOptions")] CompilerOptions options)
[Test]
public void Async([Values(CompilerOptions.None, CompilerOptions.Optimize)] CompilerOptions options)
{
RunCS(options: options);
}
[Test]
public void LINQRaytracer([ValueSource("defaultOptions")] CompilerOptions options)
{
RunCS(options: options);
}
@ -201,17 +213,18 @@ namespace ICSharpCode.Decompiler.Tests @@ -201,17 +213,18 @@ namespace ICSharpCode.Decompiler.Tests
void RunCS([CallerMemberName] string testName = null, CompilerOptions options = CompilerOptions.UseDebug)
{
string testFileName = testName + ".cs";
string testOutputFileName = testName + Tester.GetSuffix(options) + ".exe";
CompilerResults outputFile = null, decompiledOutputFile = null;
try {
outputFile = Tester.CompileCSharp(Path.Combine(TestCasePath, testFileName), options);
outputFile = Tester.CompileCSharp(Path.Combine(TestCasePath, testFileName), options,
outputFileName: Path.Combine(TestCasePath, testOutputFileName));
string decompiledCodeFile = Tester.DecompileCSharp(outputFile.PathToAssembly);
decompiledOutputFile = Tester.CompileCSharp(decompiledCodeFile, options);
Tester.RunAndCompareOutput(testFileName, outputFile.PathToAssembly, decompiledOutputFile.PathToAssembly, decompiledCodeFile);
File.Delete(decompiledCodeFile);
File.Delete(outputFile.PathToAssembly);
File.Delete(decompiledOutputFile.PathToAssembly);
} finally {
if (outputFile != null)

2
ICSharpCode.Decompiler.Tests/DataFlowTest.cs

@ -55,7 +55,7 @@ namespace ICSharpCode.Decompiler.Tests @@ -55,7 +55,7 @@ namespace ICSharpCode.Decompiler.Tests
public void TryFinallyWithAssignmentInFinally()
{
ILVariable v = new ILVariable(VariableKind.Local, SpecialType.UnknownType, 0);
ILFunction f = new ILFunction(null, new TryFinally(
ILFunction f = new ILFunction(null, null, new TryFinally(
new Nop(),
new StLoc(v, new LdcI4(0))
));

205
ICSharpCode.Decompiler.Tests/DelegateConstruction.cs

@ -1,205 +0,0 @@ @@ -1,205 +0,0 @@
// 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;
using System.Collections.Generic;
using System.Linq;
public static class DelegateConstruction
{
class InstanceTests
{
public Action CaptureOfThis()
{
return delegate {
CaptureOfThis();
};
}
public Action CaptureOfThisAndParameter(int a)
{
return delegate {
CaptureOfThisAndParameter(a);
};
}
public Action CaptureOfThisAndParameterInForEach(int a)
{
foreach (int item in Enumerable.Empty<int>()) {
if (item > 0) {
return delegate {
CaptureOfThisAndParameter(item + a);
};
}
}
return null;
}
public Action CaptureOfThisAndParameterInForEachWithItemCopy(int a)
{
foreach (int item in Enumerable.Empty<int>()) {
int copyOfItem = item;
if (item > 0) {
return delegate {
CaptureOfThisAndParameter(item + a + copyOfItem);
};
}
}
return null;
}
public void LambdaInForLoop()
{
for (int i = 0; i < 100000; i++) {
Bar(() => Foo());
}
}
public int Foo()
{
return 0;
}
public void Bar(Func<int> f)
{
}
}
public static void Test(this string a)
{
}
public static Action<string> ExtensionMethodUnbound()
{
return new Action<string>(DelegateConstruction.Test);
}
public static Action ExtensionMethodBound()
{
return new Action("abc".Test);
}
public static Action ExtensionMethodBoundOnNull()
{
return new Action(((string)null).Test);
}
public static object StaticMethod()
{
return new Func<Action>(DelegateConstruction.ExtensionMethodBound);
}
public static object InstanceMethod()
{
return new Func<string>("hello".ToUpper);
}
public static object InstanceMethodOnNull()
{
return new Func<string>(((string)null).ToUpper);
}
public static List<Action<int>> AnonymousMethodStoreWithinLoop()
{
List<Action<int>> list = new List<Action<int>>();
for (int i = 0; i < 10; i++)
{
int counter;
list.Add(delegate(int x)
{
counter = x;
}
);
}
return list;
}
public static List<Action<int>> AnonymousMethodStoreOutsideLoop()
{
List<Action<int>> list = new List<Action<int>>();
int counter;
for (int i = 0; i < 10; i++)
{
list.Add(delegate(int x)
{
counter = x;
}
);
}
return list;
}
public static Action StaticAnonymousMethodNoClosure()
{
return delegate
{
Console.WriteLine();
};
}
public static void NameConflict()
{
// i is captured variable,
// j is parameter in anonymous method
// k is local in anonymous method,
// l is local in main method
// Ensure that the decompiler doesn't introduce name conflicts
List<Action<int>> list = new List<Action<int>>();
for (int l = 0; l < 10; l++) {
int i;
for (i = 0; i < 10; i++) {
list.Add(
delegate (int j) {
for (int k = 0; k < i; k += j) {
Console.WriteLine();
}
});
}
}
}
public static void NameConflict2(int j)
{
List<Action<int>> list = new List<Action<int>>();
for (int k = 0; k < 10; k++) {
list.Add(
delegate(int i) {
Console.WriteLine(i);
});
}
}
public static Action<int> NameConflict3(int i)
{
return delegate(int j) {
for (int k = 0; k < j; k++) {
Console.WriteLine(k);
}
};
}
public static Func<int, Func<int, int>> CurriedAddition(int a)
{
return b => c => a + b + c;
}
public static Func<int, Func<int, Func<int, int>>> CurriedAddition2(int a)
{
return b => c => d => a + b + c + d;
}
}

17
ICSharpCode.Decompiler.Tests/Helpers/SdkUtility.cs

@ -160,6 +160,19 @@ namespace ICSharpCode.Decompiler.Tests.Helpers @@ -160,6 +160,19 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
return windowsSdk80InstallRoot;
}
}
static string WindowsSdk461InstallRoot = null;
/// <summary>
/// Location of the .NET 4.6.1 SDK install root.
/// </summary>
public static string WindowsSdk461NetFxTools {
get {
if (WindowsSdk461InstallRoot == null) {
WindowsSdk461InstallRoot = GetPathFromRegistryX86(@"SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\NETFXSDK\4.6.1\WinSDK-NetFx40Tools", "InstallationFolder") ?? string.Empty;
}
return WindowsSdk461InstallRoot;
}
}
#endregion
/// <summary>
@ -170,6 +183,10 @@ namespace ICSharpCode.Decompiler.Tests.Helpers @@ -170,6 +183,10 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
/// <returns>The path of the executable, or null if the exe is not found.</returns>
public static string GetSdkPath(string exeName) {
string execPath;
if (!string.IsNullOrEmpty(WindowsSdk461NetFxTools)) {
execPath = Path.Combine(WindowsSdk461NetFxTools, exeName);
if (File.Exists(execPath)) { return execPath; }
}
if (!string.IsNullOrEmpty(WindowsSdk80NetFxTools)) {
execPath = Path.Combine(WindowsSdk80NetFxTools, exeName);
if (File.Exists(execPath)) { return execPath; }

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

@ -65,7 +65,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers @@ -65,7 +65,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
public static string AssembleIL(string sourceFileName, AssemblerOptions options = AssemblerOptions.UseDebug)
{
string ilasmPath = Path.Combine(Environment.GetEnvironmentVariable("windir"), @"Microsoft.NET\Framework\v4.0.30319\ilasm.exe");
string outputFile = Path.GetFileNameWithoutExtension(sourceFileName);
string outputFile = Path.Combine(Path.GetDirectoryName(sourceFileName), Path.GetFileNameWithoutExtension(sourceFileName));
string otherOptions = " ";
if (options.HasFlag(AssemblerOptions.Library)) {
outputFile += ".dll";
@ -111,7 +111,8 @@ namespace ICSharpCode.Decompiler.Tests.Helpers @@ -111,7 +111,8 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
using (var writer = new StreamWriter(outputFile)) {
module.Name = Path.GetFileNameWithoutExtension(outputFile);
var output = new PlainTextOutput(writer);
ReflectionDisassembler rd = new ReflectionDisassembler(output, false, CancellationToken.None);
ReflectionDisassembler rd = new ReflectionDisassembler(output, CancellationToken.None);
rd.DetectControlStructure = false;
rd.WriteAssemblyReferences(module);
if (module.Assembly != null)
rd.WriteAssemblyHeader(module.Assembly);
@ -156,7 +157,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers @@ -156,7 +157,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
};
});
public static CompilerResults CompileCSharp(string sourceFileName, CompilerOptions flags = CompilerOptions.UseDebug)
public static CompilerResults CompileCSharp(string sourceFileName, CompilerOptions flags = CompilerOptions.UseDebug, string outputFileName = null)
{
List<string> sourceFileNames = new List<string> { sourceFileName };
foreach (Match match in Regex.Matches(File.ReadAllText(sourceFileName), @"#include ""([\w\d./]+)""")) {
@ -181,7 +182,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers @@ -181,7 +182,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
deterministic: true
));
CompilerResults results = new CompilerResults(new TempFileCollection());
results.PathToAssembly = Path.GetTempFileName();
results.PathToAssembly = outputFileName ?? Path.GetTempFileName();
var emitResult = compilation.Emit(results.PathToAssembly);
if (!emitResult.Success) {
StringBuilder b = new StringBuilder("Compiler error:");
@ -202,6 +203,9 @@ namespace ICSharpCode.Decompiler.Tests.Helpers @@ -202,6 +203,9 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
if (preprocessorSymbols.Count > 0) {
options.CompilerOptions += " /d:" + string.Join(";", preprocessorSymbols);
}
if (outputFileName != null) {
options.OutputAssembly = outputFileName;
}
options.ReferencedAssemblies.Add("System.Core.dll");
CompilerResults results = provider.CompileAssemblyFromFile(options, sourceFileNames.ToArray());
@ -216,6 +220,20 @@ namespace ICSharpCode.Decompiler.Tests.Helpers @@ -216,6 +220,20 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
}
}
internal static string GetSuffix(CompilerOptions cscOptions)
{
string suffix = "";
if ((cscOptions & CompilerOptions.Optimize) != 0)
suffix += ".opt";
if ((cscOptions & CompilerOptions.Force32Bit) != 0)
suffix += ".32";
if ((cscOptions & CompilerOptions.UseDebug) != 0)
suffix += ".dbg";
if ((cscOptions & CompilerOptions.UseRoslyn) != 0)
suffix += ".roslyn";
return suffix;
}
public static int Run(string assemblyFileName, out string output, out string error)
{
ProcessStartInfo info = new ProcessStartInfo(assemblyFileName);

6
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -44,6 +44,7 @@ @@ -44,6 +44,7 @@
</ItemGroup>
<ItemGroup>
<None Include="TestCases\Correctness\Jmp.il" />
<None Include="TestCases\ILPretty\FSharpUsing.fs" />
<None Include="TestCases\ILPretty\FSharpUsing_Debug.il" />
<None Include="TestCases\ILPretty\FSharpUsing_Release.il" />
@ -53,6 +54,8 @@ @@ -53,6 +54,8 @@
<ItemGroup>
<Compile Include="DataFlowTest.cs" />
<Compile Include="TestCases\Correctness\LINQRaytracer.cs" />
<Compile Include="TestCases\Pretty\DelegateConstruction.cs" />
<None Include="TestCases\ILPretty\FSharpUsing_Debug.cs" />
<None Include="TestCases\ILPretty\FSharpUsing_Release.cs" />
<Compile Include="Helpers\CodeAssert.cs" />
@ -67,6 +70,8 @@ @@ -67,6 +70,8 @@
<Compile Include="TestCases\Correctness\TrickyTypes.cs" />
<Compile Include="TestCases\Correctness\Using.cs" />
<Compile Include="TestCases\ILPretty\Issue379.cs" />
<Compile Include="TestCases\Pretty\FixProxyCalls.cs" />
<Compile Include="TestCases\Pretty\InitializerTests.cs" />
<None Include="TestCases\ILPretty\Issue646.cs" />
<Compile Include="TestCases\Pretty\Async.cs" />
<Compile Include="TestCases\Pretty\CheckedUnchecked.cs" />
@ -106,6 +111,7 @@ @@ -106,6 +111,7 @@
<Compile Include="TestCases\Pretty\PropertiesAndEvents.cs" />
<Compile Include="TestCases\Pretty\QueryExpressions.cs" />
<Compile Include="TestCases\Pretty\ShortCircuit.cs" />
<Compile Include="TestCases\Pretty\Switch.cs" />
<Compile Include="TestCases\Pretty\TypeAnalysisTests.cs" />
<Compile Include="TestCases\Pretty\Using.cs" />
<Compile Include="TestTraceListener.cs" />

2
ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs

@ -41,7 +41,7 @@ namespace ICSharpCode.Decompiler.Tests @@ -41,7 +41,7 @@ namespace ICSharpCode.Decompiler.Tests
{
Run();
}
[Test]
public void FSharpUsing_Debug()
{

35
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -97,6 +97,18 @@ namespace ICSharpCode.Decompiler.Tests @@ -97,6 +97,18 @@ namespace ICSharpCode.Decompiler.Tests
Run(cscOptions: cscOptions);
}
[Test]
public void Switch([ValueSource("defaultOptions")] CompilerOptions cscOptions)
{
Run(cscOptions: cscOptions);
}
[Test]
public void DelegateConstruction([ValueSource("defaultOptions")] CompilerOptions cscOptions)
{
Run(cscOptions: cscOptions);
}
[Test]
public void AnonymousTypes([Values(CompilerOptions.None, CompilerOptions.Optimize)] CompilerOptions cscOptions)
{
@ -176,18 +188,21 @@ namespace ICSharpCode.Decompiler.Tests @@ -176,18 +188,21 @@ namespace ICSharpCode.Decompiler.Tests
Run(cscOptions: cscOptions, asmOptions: AssemblerOptions.UseOwnDisassembler);
}
[Test]
public void InitializerTests([ValueSource("defaultOptions")] CompilerOptions cscOptions)
{
Run(cscOptions: cscOptions);
}
[Test]
public void FixProxyCalls([Values(CompilerOptions.None, CompilerOptions.Optimize, CompilerOptions.UseRoslyn)] CompilerOptions cscOptions)
{
Run(cscOptions: cscOptions);
}
void Run([CallerMemberName] string testName = null, AssemblerOptions asmOptions = AssemblerOptions.None, CompilerOptions cscOptions = CompilerOptions.None)
{
var ilFile = Path.Combine(TestCasePath, testName);
if ((cscOptions & CompilerOptions.Optimize) != 0)
ilFile += ".opt";
if ((cscOptions & CompilerOptions.Force32Bit) != 0)
ilFile += ".32";
if ((cscOptions & CompilerOptions.UseDebug) != 0)
ilFile += ".dbg";
if ((cscOptions & CompilerOptions.UseRoslyn) != 0)
ilFile += ".roslyn";
ilFile += ".il";
var ilFile = Path.Combine(TestCasePath, testName) + Tester.GetSuffix(cscOptions) + ".il";
var csFile = Path.Combine(TestCasePath, testName + ".cs");
if (!File.Exists(ilFile)) {

89
ICSharpCode.Decompiler.Tests/Switch.cs

@ -1,89 +0,0 @@ @@ -1,89 +0,0 @@
// 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;
public static class Switch
{
public static string ShortSwitchOverString(string text)
{
switch (text) {
case "First case":
return "Text";
default:
return "Default";
}
}
public static string SwitchOverString1(string text)
{
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";
}
}
public static string SwitchOverString2()
{
switch (Environment.UserName)
{
case "First case":
return "Text1";
case "Second case":
return "Text2";
case "Third case":
return "Text3";
case "Fourth case":
return "Text4";
case "Fifth case":
return "Text5";
case "Sixth case":
return "Text6";
default:
return "Default";
}
}
public static string SwitchOverBool(bool b)
{
switch (b) {
case true:
return bool.TrueString;
case false:
return bool.FalseString;
default:
return null;
}
}
}

6
ICSharpCode.Decompiler.Tests/TestCases/Correctness/Async.cs

@ -30,15 +30,15 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -30,15 +30,15 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
{
public static void Main()
{
new Async().Run();
new Async().Run().Wait();
}
public async void Run()
public async Task Run()
{
await SimpleBoolTaskMethod();
StreamCopyTo(new MemoryStream(new byte[1024]), 16);
StreamCopyToWithConfigureAwait(new MemoryStream(new byte[1024]), 16);
await AwaitInForEach(Enumerable.Range(0, 100).Select(i => Task<int>.FromResult(i)));
await AwaitInForEach(Enumerable.Range(0, 100).Select(i => Task.FromResult(i)));
await TaskMethodWithoutAwaitButWithExceptionHandling();
#if !LEGACY_CSC
await AwaitCatch(Task.FromResult(1));

47
ICSharpCode.Decompiler.Tests/TestCases/Correctness/Jmp.il

@ -0,0 +1,47 @@ @@ -0,0 +1,47 @@
.assembly extern mscorlib
{
.publickeytoken = ( b7 7a 5c 56 19 34 e0 89 )
.ver 4:0:0:0
}
.assembly 'Jmp'
{
.ver 0:0:0:0
}
.module Jmp.exe
.corflags 0x00000001 // ILOnly
.class private auto ansi abstract sealed beforefieldinit Program
extends [mscorlib]System.Object
{
.method public hidebysig static void Main (string[] args) cil managed
{
.maxstack 8
.entrypoint
ldstr "Method1(100) = {0}"
ldc.i4 100
call int32 Program::Method1(int32)
box int32
call void [mscorlib]System.Console::WriteLine(string, object)
ret
} // end of method Main
.method public static int32 Method1(int32 val)
{
ldarg.0
ldc.i4.1
add
starg.s 0
jmp int32 Program::Method2(int32)
}
.method public static int32 Method2(int32 val)
{
ldarg.0
ldc.i4.5
mul
ret
}
}

390
ICSharpCode.Decompiler.Tests/TestCases/Correctness/LINQRaytracer.cs

@ -0,0 +1,390 @@ @@ -0,0 +1,390 @@
// This test case is taken from https://blogs.msdn.microsoft.com/lukeh/2007/10/01/taking-linq-to-objects-to-extremes-a-fully-linqified-raytracer/
using System.Linq;
using System;
using System.Collections.Generic;
namespace RayTracer
{
public class RayTracer
{
static void Main()
{
const int width = 50;
const int height = 50;
RayTracer rayTracer = new RayTracer(width, height, (int x, int y, Color color) => {
Console.Write("{0},{1}:{2};", x, y, color);
});
rayTracer.Render(rayTracer.DefaultScene);
}
private int screenWidth;
private int screenHeight;
private const int MaxDepth = 5;
public Action<int, int, Color> setPixel;
public RayTracer(int screenWidth, int screenHeight, Action<int, int, Color> setPixel)
{
this.screenWidth = screenWidth;
this.screenHeight = screenHeight;
this.setPixel = setPixel;
}
private class Wrap<T>
{
public readonly Func<Wrap<T>, T> It;
public Wrap(Func<Wrap<T>, T> it) { It = it; }
}
public static Func<T, U> Y<T, U>(Func<Func<T, U>, Func<T, U>> f)
{
Func<Wrap<Func<T, U>>, Func<T, U>> g = wx => f(wx.It(wx));
return g(new Wrap<Func<T, U>>(wx => f(y => wx.It(wx)(y))));
}
class TraceRayArgs
{
public readonly Ray Ray;
public readonly Scene Scene;
public readonly int Depth;
public TraceRayArgs(Ray ray, Scene scene, int depth) { Ray = ray; Scene = scene; Depth = depth; }
}
internal void Render(Scene scene)
{
var pixelsQuery =
from y in Enumerable.Range(0, screenHeight)
let recenterY = -(y - (screenHeight / 2.0)) / (2.0 * screenHeight)
select from x in Enumerable.Range(0, screenWidth)
let recenterX = (x - (screenWidth / 2.0)) / (2.0 * screenWidth)
let point =
Vector.Norm(Vector.Plus(scene.Camera.Forward,
Vector.Plus(Vector.Times(recenterX, scene.Camera.Right),
Vector.Times(recenterY, scene.Camera.Up))))
let ray = new Ray() { Start = scene.Camera.Pos, Dir = point }
let computeTraceRay = (Func<Func<TraceRayArgs, Color>, Func<TraceRayArgs, Color>>)
(f => traceRayArgs =>
(from isect in
from thing in traceRayArgs.Scene.Things
select thing.Intersect(traceRayArgs.Ray)
where isect != null
orderby isect.Dist
let d = isect.Ray.Dir
let pos = Vector.Plus(Vector.Times(isect.Dist, isect.Ray.Dir), isect.Ray.Start)
let normal = isect.Thing.Normal(pos)
let reflectDir = Vector.Minus(d, Vector.Times(2 * Vector.Dot(normal, d), normal))
let naturalColors =
from light in traceRayArgs.Scene.Lights
let ldis = Vector.Minus(light.Pos, pos)
let livec = Vector.Norm(ldis)
let testRay = new Ray() { Start = pos, Dir = livec }
let testIsects = from inter in
from thing in traceRayArgs.Scene.Things
select thing.Intersect(testRay)
where inter != null
orderby inter.Dist
select inter
let testIsect = testIsects.FirstOrDefault()
let neatIsect = testIsect == null ? 0 : testIsect.Dist
let isInShadow = !((neatIsect > Vector.Mag(ldis)) || (neatIsect == 0))
where !isInShadow
let illum = Vector.Dot(livec, normal)
let lcolor = illum > 0 ? Color.Times(illum, light.Color) : Color.Make(0, 0, 0)
let specular = Vector.Dot(livec, Vector.Norm(reflectDir))
let scolor = specular > 0
? Color.Times(Math.Pow(specular, isect.Thing.Surface.Roughness),
light.Color)
: Color.Make(0, 0, 0)
select Color.Plus(Color.Times(isect.Thing.Surface.Diffuse(pos), lcolor),
Color.Times(isect.Thing.Surface.Specular(pos), scolor))
let reflectPos = Vector.Plus(pos, Vector.Times(.001, reflectDir))
let reflectColor = traceRayArgs.Depth >= MaxDepth
? Color.Make(.5, .5, .5)
: Color.Times(isect.Thing.Surface.Reflect(reflectPos),
f(new TraceRayArgs(new Ray() {
Start = reflectPos,
Dir = reflectDir
},
traceRayArgs.Scene,
traceRayArgs.Depth + 1)))
select naturalColors.Aggregate(reflectColor,
(color, natColor) => Color.Plus(color, natColor))
).DefaultIfEmpty(Color.Background).First())
let traceRay = Y(computeTraceRay)
select new { X = x, Y = y, Color = traceRay(new TraceRayArgs(ray, scene, 0)) };
foreach (var row in pixelsQuery)
foreach (var pixel in row)
setPixel(pixel.X, pixel.Y, pixel.Color);
}
internal readonly Scene DefaultScene =
new Scene() {
Things = new SceneObject[] {
new Plane() {
Norm = Vector.Make(0,1,0),
Offset = 0,
Surface = Surfaces.CheckerBoard
},
new Sphere() {
Center = Vector.Make(0,1,0),
Radius = 1,
Surface = Surfaces.Shiny
},
new Sphere() {
Center = Vector.Make(-1,.5,1.5),
Radius = .5,
Surface = Surfaces.Shiny
}},
Lights = new Light[] {
new Light() {
Pos = Vector.Make(-2,2.5,0),
Color = Color.Make(.49,.07,.07)
},
new Light() {
Pos = Vector.Make(1.5,2.5,1.5),
Color = Color.Make(.07,.07,.49)
},
new Light() {
Pos = Vector.Make(1.5,2.5,-1.5),
Color = Color.Make(.07,.49,.071)
},
new Light() {
Pos = Vector.Make(0,3.5,0),
Color = Color.Make(.21,.21,.35)
}},
Camera = Camera.Create(Vector.Make(3, 2, 4), Vector.Make(-1, .5, 0))
};
}
static class Surfaces
{
// Only works with X-Z plane.
public static readonly Surface CheckerBoard =
new Surface() {
Diffuse = pos => ((Math.Floor(pos.Z) + Math.Floor(pos.X)) % 2 != 0)
? Color.Make(1, 1, 1)
: Color.Make(0, 0, 0),
Specular = pos => Color.Make(1, 1, 1),
Reflect = pos => ((Math.Floor(pos.Z) + Math.Floor(pos.X)) % 2 != 0)
? .1
: .7,
Roughness = 150
};
public static readonly Surface Shiny =
new Surface() {
Diffuse = pos => Color.Make(1, 1, 1),
Specular = pos => Color.Make(.5, .5, .5),
Reflect = pos => .6,
Roughness = 50
};
}
class Vector
{
public readonly double X;
public readonly double Y;
public readonly double Z;
public Vector(double x, double y, double z) { X = x; Y = y; Z = z; }
public static Vector Make(double x, double y, double z) { return new Vector(x, y, z); }
public static Vector Times(double n, Vector v)
{
return new Vector(v.X * n, v.Y * n, v.Z * n);
}
public static Vector Minus(Vector v1, Vector v2)
{
return new Vector(v1.X - v2.X, v1.Y - v2.Y, v1.Z - v2.Z);
}
public static Vector Plus(Vector v1, Vector v2)
{
return new Vector(v1.X + v2.X, v1.Y + v2.Y, v1.Z + v2.Z);
}
public static double Dot(Vector v1, Vector v2)
{
return (v1.X * v2.X) + (v1.Y * v2.Y) + (v1.Z * v2.Z);
}
public static double Mag(Vector v) { return Math.Sqrt(Dot(v, v)); }
public static Vector Norm(Vector v)
{
double mag = Mag(v);
double div = mag == 0 ? double.PositiveInfinity : 1 / mag;
return Times(div, v);
}
public static Vector Cross(Vector v1, Vector v2)
{
return new Vector(((v1.Y * v2.Z) - (v1.Z * v2.Y)),
((v1.Z * v2.X) - (v1.X * v2.Z)),
((v1.X * v2.Y) - (v1.Y * v2.X)));
}
public static bool Equals(Vector v1, Vector v2)
{
return (v1.X == v2.X) && (v1.Y == v2.Y) && (v1.Z == v2.Z);
}
}
public class Color
{
public double R;
public double G;
public double B;
public Color(double r, double g, double b) { R = r; G = g; B = b; }
public static Color Make(double r, double g, double b) { return new Color(r, g, b); }
public static Color Times(double n, Color v)
{
return new Color(n * v.R, n * v.G, n * v.B);
}
public static Color Times(Color v1, Color v2)
{
return new Color(v1.R * v2.R, v1.G * v2.G, v1.B * v2.B);
}
public static Color Plus(Color v1, Color v2)
{
return new Color(v1.R + v2.R, v1.G + v2.G, v1.B + v2.B);
}
public static Color Minus(Color v1, Color v2)
{
return new Color(v1.R - v2.R, v1.G - v2.G, v1.B - v2.B);
}
public static readonly Color Background = Make(0, 0, 0);
public static readonly Color DefaultColor = Make(0, 0, 0);
private double Legalize(double d)
{
return d > 1 ? 1 : d;
}
public override string ToString()
{
return string.Format("[{0},{1},{2}]", R, G, B);
}
}
class Ray
{
public Vector Start;
public Vector Dir;
}
class ISect
{
public SceneObject Thing;
public Ray Ray;
public double Dist;
}
class Surface
{
public Func<Vector, Color> Diffuse;
public Func<Vector, Color> Specular;
public Func<Vector, double> Reflect;
public double Roughness;
}
class Camera
{
public Vector Pos;
public Vector Forward;
public Vector Up;
public Vector Right;
public static Camera Create(Vector pos, Vector lookAt)
{
Vector forward = Vector.Norm(Vector.Minus(lookAt, pos));
Vector down = new Vector(0, -1, 0);
Vector right = Vector.Times(1.5, Vector.Norm(Vector.Cross(forward, down)));
Vector up = Vector.Times(1.5, Vector.Norm(Vector.Cross(forward, right)));
return new Camera() { Pos = pos, Forward = forward, Up = up, Right = right };
}
}
class Light
{
public Vector Pos;
public Color Color;
}
abstract class SceneObject
{
public Surface Surface;
public abstract ISect Intersect(Ray ray);
public abstract Vector Normal(Vector pos);
}
class Sphere : SceneObject
{
public Vector Center;
public double Radius;
public override ISect Intersect(Ray ray)
{
Vector eo = Vector.Minus(Center, ray.Start);
double v = Vector.Dot(eo, ray.Dir);
double dist;
if (v < 0) {
dist = 0;
} else {
double disc = Math.Pow(Radius, 2) - (Vector.Dot(eo, eo) - Math.Pow(v, 2));
dist = disc < 0 ? 0 : v - Math.Sqrt(disc);
}
if (dist == 0) return null;
return new ISect() {
Thing = this,
Ray = ray,
Dist = dist
};
}
public override Vector Normal(Vector pos)
{
return Vector.Norm(Vector.Minus(pos, Center));
}
}
class Plane : SceneObject
{
public Vector Norm;
public double Offset;
public override ISect Intersect(Ray ray)
{
double denom = Vector.Dot(Norm, ray.Dir);
if (denom > 0) return null;
return new ISect() {
Thing = this,
Ray = ray,
Dist = (Vector.Dot(Norm, ray.Start) + Offset) / (-denom)
};
}
public override Vector Normal(Vector pos)
{
return Norm;
}
}
class Scene
{
public SceneObject[] Things;
public Light[] Lights;
public Camera Camera;
public IEnumerable<ISect> Intersect(Ray r)
{
return from thing in Things
select thing.Intersect(r);
}
}
}

76
ICSharpCode.Decompiler.Tests/TestCases/Correctness/Switch.cs

@ -26,10 +26,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -26,10 +26,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
{
TestCase(SparseIntegerSwitch, -100, 1, 2, 3, 4);
TestCase(ShortSwitchOverString, "First case", "Else");
TestCase(ShortSwitchOverString2, "First case", "Second case", "Third case", "Else");
TestCase(ShortSwitchOverStringNoExplicitDefault, "First case", "Second case", "Third case", "Else");
TestCase(SwitchOverString1, "First case", "Second case", "2nd case", "Third case", "Fourth case", "Fifth case", "Sixth case", null, "default", "else");
Console.WriteLine(SwitchOverString2());
Console.WriteLine(SwitchOverBool(true));
Console.WriteLine(SwitchOverBool(false));
SwitchInLoop(0);
SwitchWithGoto(1);
SwitchWithGoto(2);
SwitchWithGoto(3);
SwitchWithGoto(4);
}
static void TestCase<T>(Func<T, string> target, params T[] args)
@ -41,6 +48,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -41,6 +48,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
public static string SparseIntegerSwitch(int i)
{
Console.WriteLine("SparseIntegerSwitch: " + i);
switch (i) {
case -10000000: return "-10 mln";
case -100: return "-hundred";
@ -59,6 +67,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -59,6 +67,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
public static string ShortSwitchOverString(string text)
{
Console.WriteLine("ShortSwitchOverString: " + text);
switch (text) {
case "First case":
return "Text";
@ -67,8 +76,38 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -67,8 +76,38 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
}
}
public static string ShortSwitchOverString2(string text)
{
Console.WriteLine("ShortSwitchOverString2: " + text);
switch (text) {
case "First case":
return "Text1";
case "Second case":
return "Text2";
case "Third case":
return "Text3";
default:
return "Default";
}
}
public static string ShortSwitchOverStringNoExplicitDefault(string text)
{
Console.WriteLine("ShortSwitchOverStringNoExplicitDefault: " + text);
switch (text) {
case "First case":
return "Text1";
case "Second case":
return "Text2";
case "Third case":
return "Text3";
}
return "Default";
}
public static string SwitchOverString1(string text)
{
Console.WriteLine("SwitchOverString1: " + text);
switch (text) {
case "First case":
return "Text1";
@ -92,6 +131,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -92,6 +131,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
public static string SwitchOverString2()
{
Console.WriteLine("SwitchOverString2:");
switch (Environment.UserName) {
case "First case":
return "Text1";
@ -105,6 +145,16 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -105,6 +145,16 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
return "Text5";
case "Sixth case":
return "Text6";
case "Seventh case":
return "Text7";
case "Eighth case":
return "Text8";
case "Ninth case":
return "Text9";
case "Tenth case":
return "Text10";
case "Eleventh case":
return "Text11";
default:
return "Default";
}
@ -112,6 +162,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -112,6 +162,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
public static string SwitchOverBool(bool b)
{
Console.WriteLine("SwitchOverBool: " + b);
switch (b) {
case true:
return bool.TrueString;
@ -124,6 +175,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -124,6 +175,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
public static void SwitchInLoop(int i)
{
Console.WriteLine("SwitchInLoop: " + i);
while (true) {
switch (i) {
case 1:
@ -141,10 +193,32 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -141,10 +193,32 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
default:
Console.WriteLine("default");
Console.WriteLine("more code");
throw new ArgumentException();
return;
}
i++;
}
}
public static void SwitchWithGoto(int i)
{
Console.WriteLine("SwitchWithGoto: " + i);
switch (i) {
case 1:
Console.WriteLine("one");
goto default;
case 2:
Console.WriteLine("two");
goto case 3;
case 3:
Console.WriteLine("three");
break;
case 4:
Console.WriteLine("four");
return;
default:
Console.WriteLine("default");
break;
}
}
}
}

2
ICSharpCode.Decompiler.Tests/TestCases/Pretty/.gitignore vendored

@ -1 +1,3 @@ @@ -1 +1,3 @@
/*.res
/*.dll
/*.exe

5
ICSharpCode.Decompiler.Tests/TestCases/Pretty/CheckedUnchecked.cs

@ -83,16 +83,15 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -83,16 +83,15 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
});
}
public void ArrayInitializerChecked()
{
this.TestHelp(new int[2] {
1,
2
}, (Func<int[], int[]>)((int[] n) => checked(new int[2] {
}, (int[] n) => checked(new int[2] {
n[0] + 1,
n[1] + 1
})));
}));
}
public T TestHelp<T>(T t, Func<T, T> f)

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

@ -0,0 +1,199 @@ @@ -0,0 +1,199 @@
// 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;
using System.Collections.Generic;
using System.Linq;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
public static class DelegateConstruction
{
private class InstanceTests
{
public Action CaptureOfThis()
{
return delegate {
this.CaptureOfThis();
};
}
public Action CaptureOfThisAndParameter(int a)
{
return delegate {
this.CaptureOfThisAndParameter(a);
};
}
public Action CaptureOfThisAndParameterInForEach(int a)
{
foreach (int item in Enumerable.Empty<int>()) {
if (item > 0) {
return delegate {
this.CaptureOfThisAndParameter(item + a);
};
}
}
return null;
}
public Action CaptureOfThisAndParameterInForEachWithItemCopy(int a)
{
foreach (int item in Enumerable.Empty<int>()) {
int copyOfItem = item;
if (item > 0) {
return delegate {
this.CaptureOfThisAndParameter(item + a + copyOfItem);
};
}
}
return null;
}
public void LambdaInForLoop()
{
for (int i = 0; i < 100000; i++) {
this.Bar(() => this.Foo());
}
}
public int Foo()
{
return 0;
}
public void Bar(Func<int> f)
{
}
}
public static void Test(this string a)
{
}
public static Action<string> ExtensionMethodUnbound()
{
return DelegateConstruction.Test;
}
public static Action ExtensionMethodBound()
{
return "abc".Test;
}
public static Action ExtensionMethodBoundOnNull()
{
return ((string)null).Test;
}
public static object StaticMethod()
{
return new Func<Action>(DelegateConstruction.ExtensionMethodBound);
}
public static object InstanceMethod()
{
return new Func<string>("hello".ToUpper);
}
public static object InstanceMethodOnNull()
{
return new Func<string>(((string)null).ToUpper);
}
public static List<Action<int>> AnonymousMethodStoreWithinLoop()
{
List<Action<int>> list = new List<Action<int>>();
for (int i = 0; i < 10; i++) {
int counter;
list.Add(delegate(int x) {
counter = x;
});
}
return list;
}
public static List<Action<int>> AnonymousMethodStoreOutsideLoop()
{
List<Action<int>> list = new List<Action<int>>();
int counter;
for (int i = 0; i < 10; i++) {
list.Add(delegate(int x) {
counter = x;
});
}
return list;
}
public static Action StaticAnonymousMethodNoClosure()
{
return delegate {
Console.WriteLine();
};
}
public static void NameConflict()
{
// i is captured variable,
// j is parameter in anonymous method
// l is local in anonymous method,
// k is local in main method
// Ensure that the decompiler doesn't introduce name conflicts
List<Action<int>> list = new List<Action<int>>();
for (int k = 0; k < 10; k++) {
int i;
for (i = 0; i < 10; i++) {
list.Add(delegate(int j) {
for (int l = 0; l < i; l += j) {
Console.WriteLine();
}
});
}
}
}
public static void NameConflict2(int j)
{
List<Action<int>> list = new List<Action<int>>();
for (int k = 0; k < 10; k++) {
list.Add(delegate(int i) {
Console.WriteLine(i);
});
}
}
public static Action<int> NameConflict3(int i)
{
return delegate(int j) {
for (int k = 0; k < j; k++) {
Console.WriteLine(k);
}
};
}
public static Func<int, Func<int, int>> CurriedAddition(int a)
{
return (int b) => (int c) => a + b + c;
}
public static Func<int, Func<int, Func<int, int>>> CurriedAddition2(int a)
{
return (int b) => (int c) => (int d) => a + b + c + d;
}
}
}

1400
ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.il

File diff suppressed because it is too large Load Diff

1150
ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.opt.il

File diff suppressed because it is too large Load Diff

1144
ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.opt.roslyn.il

File diff suppressed because it is too large Load Diff

1362
ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.roslyn.il

File diff suppressed because it is too large Load Diff

17
ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExceptionHandling.cs

@ -97,6 +97,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -97,6 +97,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
return false;
}
public void CatchWhenWithConditionWithoutExceptionVar()
{
int num = 0;
try {
throw new Exception();
} catch (Exception) when (num == 0) {
Console.WriteLine("jo");
}
}
#endif
public bool SimpleTryFinally()
@ -144,7 +155,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -144,7 +155,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
Console.WriteLine("Try");
} catch (InvalidOperationException ex) {
Console.WriteLine(ex.Message);
} catch (Exception ex2) {
} catch (SystemException ex2) {
Console.WriteLine(ex2.Message);
} catch {
Console.WriteLine("other");
@ -176,5 +187,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -176,5 +187,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
}
}
}
}
}

14
ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExceptionHandling.il

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.17929
// Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0
// Copyright (c) Microsoft Corporation. All rights reserved.
@ -10,7 +10,7 @@ @@ -10,7 +10,7 @@
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
.assembly lxxrs5dq
.assembly vicbdq3v
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
.custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx
@ -20,15 +20,15 @@ @@ -20,15 +20,15 @@
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module lxxrs5dq.dll
// MVID: {82D6FFAF-FF9D-4E8C-AC93-080CC89EB80E}
.module vicbdq3v.dll
// MVID: {5E2AA050-D333-4323-8C5A-29B86C8D98BB}
.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x10000000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// Image base: 0x02E30000
// Image base: 0x01160000
// =============== CLASS MEMBERS DECLARATION ===================
@ -323,7 +323,7 @@ @@ -323,7 +323,7 @@
// Code size 68 (0x44)
.maxstack 1
.locals init (class [mscorlib]System.InvalidOperationException V_0,
class [mscorlib]System.Exception V_1)
class [mscorlib]System.SystemException V_1)
IL_0000: nop
.try
{
@ -347,7 +347,7 @@ @@ -347,7 +347,7 @@
IL_001f: leave.s IL_0042
} // end handler
catch [mscorlib]System.Exception
catch [mscorlib]System.SystemException
{
IL_0021: stloc.1
IL_0022: nop

14
ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExceptionHandling.opt.il

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.17929
// Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0
// Copyright (c) Microsoft Corporation. All rights reserved.
@ -10,7 +10,7 @@ @@ -10,7 +10,7 @@
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
.assembly mydv32cl
.assembly uidw4n1e
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
.custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx
@ -20,15 +20,15 @@ @@ -20,15 +20,15 @@
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module mydv32cl.dll
// MVID: {6B95C83E-B82F-493E-BD92-AF0A5E82F1B4}
.module uidw4n1e.dll
// MVID: {A6A100AA-2C72-4B40-9C9F-50DA438D4727}
.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x10000000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// Image base: 0x014A0000
// Image base: 0x00380000
// =============== CLASS MEMBERS DECLARATION ===================
@ -258,7 +258,7 @@ @@ -258,7 +258,7 @@
// Code size 54 (0x36)
.maxstack 1
.locals init (class [mscorlib]System.InvalidOperationException V_0,
class [mscorlib]System.Exception V_1)
class [mscorlib]System.SystemException V_1)
.try
{
IL_0000: ldstr "Try"
@ -275,7 +275,7 @@ @@ -275,7 +275,7 @@
IL_0018: leave.s IL_0035
} // end handler
catch [mscorlib]System.Exception
catch [mscorlib]System.SystemException
{
IL_001a: stloc.1
IL_001b: ldloc.1

259
ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExceptionHandling.opt.roslyn.il

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.17929
// Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0
// Copyright (c) Microsoft Corporation. All rights reserved.
@ -25,14 +25,14 @@ @@ -25,14 +25,14 @@
.ver 0:0:0:0
}
.module ExceptionHandling.dll
// MVID: {07FDA375-CD7B-478B-922A-E82E4CA881CF}
// MVID: {A4875794-8C32-4802-A38C-B190A38BD06B}
.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x10000000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// Image base: 0x02CE0000
// Image base: 0x029B0000
// =============== CLASS MEMBERS DECLARATION ===================
@ -53,135 +53,136 @@ @@ -53,135 +53,136 @@
instance void MoveNext() cil managed
{
.override [mscorlib]System.Runtime.CompilerServices.IAsyncStateMachine::MoveNext
// Code size 242 (0xf2)
// Code size 240 (0xf0)
.maxstack 3
.locals init (int32 V_0,
bool V_1,
valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool> V_2,
class [mscorlib]System.Exception V_3,
class [mscorlib]System.Exception V_4)
class ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling V_1,
bool V_2,
valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool> V_3,
class [mscorlib]System.Exception V_4,
class [mscorlib]System.Exception V_5)
IL_0000: ldarg.0
IL_0001: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>1__state'
IL_0006: stloc.0
IL_0007: ldarg.0
IL_0008: ldfld class ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>4__this'
IL_000d: stloc.1
.try
{
IL_0007: ldloc.0
IL_0008: pop
IL_0009: nop
IL_000e: ldloc.0
IL_000f: pop
IL_0010: nop
.try
{
IL_000a: ldloc.0
IL_000b: brfalse.s IL_0054
IL_000d: ldstr "Try"
IL_0012: call void [mscorlib]System.Console::WriteLine(string)
IL_0017: ldarg.0
IL_0018: ldfld class ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>4__this'
IL_001d: callvirt instance class [mscorlib]System.Threading.Tasks.Task`1<bool> ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling::T()
IL_0022: callvirt instance valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<!0> class [mscorlib]System.Threading.Tasks.Task`1<bool>::GetAwaiter()
IL_0027: stloc.2
IL_0028: ldloca.s V_2
IL_002a: call instance bool valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>::get_IsCompleted()
IL_002f: brtrue.s IL_0070
IL_0031: ldarg.0
IL_0032: ldc.i4.0
IL_0033: dup
IL_0034: stloc.0
IL_0035: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>1__state'
IL_003a: ldarg.0
IL_003b: ldloc.2
IL_003c: stfld valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool> ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>u__1'
IL_0041: ldarg.0
IL_0042: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<bool> ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>t__builder'
IL_0047: ldloca.s V_2
IL_0049: ldarg.0
IL_004a: call instance void valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<bool>::AwaitUnsafeOnCompleted<valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>,valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'>(!!0&,
IL_0011: ldloc.0
IL_0012: brfalse.s IL_0056
IL_0014: ldstr "Try"
IL_0019: call void [mscorlib]System.Console::WriteLine(string)
IL_001e: ldloc.1
IL_001f: callvirt instance class [mscorlib]System.Threading.Tasks.Task`1<bool> ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling::T()
IL_0024: callvirt instance valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<!0> class [mscorlib]System.Threading.Tasks.Task`1<bool>::GetAwaiter()
IL_0029: stloc.3
IL_002a: ldloca.s V_3
IL_002c: call instance bool valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>::get_IsCompleted()
IL_0031: brtrue.s IL_0072
IL_0033: ldarg.0
IL_0034: ldc.i4.0
IL_0035: dup
IL_0036: stloc.0
IL_0037: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>1__state'
IL_003c: ldarg.0
IL_003d: ldloc.3
IL_003e: stfld valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool> ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>u__1'
IL_0043: ldarg.0
IL_0044: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<bool> ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>t__builder'
IL_0049: ldloca.s V_3
IL_004b: ldarg.0
IL_004c: call instance void valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<bool>::AwaitUnsafeOnCompleted<valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>,valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'>(!!0&,
!!1&)
IL_004f: leave IL_00f1
IL_0054: ldarg.0
IL_0055: ldfld valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool> ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>u__1'
IL_005a: stloc.2
IL_005b: ldarg.0
IL_005c: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool> ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>u__1'
IL_0061: initobj valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>
IL_0067: ldarg.0
IL_0068: ldc.i4.m1
IL_0069: dup
IL_006a: stloc.0
IL_006b: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>1__state'
IL_0070: ldloca.s V_2
IL_0072: call instance !0 valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>::GetResult()
IL_0077: ldloca.s V_2
IL_0079: initobj valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>
IL_007f: stloc.1
IL_0080: leave.s IL_00dd
IL_0051: leave IL_00ef
IL_0056: ldarg.0
IL_0057: ldfld valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool> ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>u__1'
IL_005c: stloc.3
IL_005d: ldarg.0
IL_005e: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool> ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>u__1'
IL_0063: initobj valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>
IL_0069: ldarg.0
IL_006a: ldc.i4.m1
IL_006b: dup
IL_006c: stloc.0
IL_006d: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>1__state'
IL_0072: ldloca.s V_3
IL_0074: call instance !0 valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>::GetResult()
IL_0079: stloc.2
IL_007a: leave.s IL_00db
} // end .try
filter
{
IL_0082: isinst [mscorlib]System.Exception
IL_0087: dup
IL_0088: brtrue.s IL_008e
IL_008a: pop
IL_008b: ldc.i4.0
IL_008c: br.s IL_00a6
IL_008e: stloc.3
IL_008f: ldloc.3
IL_0090: isinst [mscorlib]System.ArgumentException
IL_0095: brtrue.s IL_00a2
IL_0097: ldloc.3
IL_0098: isinst [mscorlib]System.IO.IOException
IL_009d: ldnull
IL_009e: cgt.un
IL_00a0: br.s IL_00a3
IL_00a2: ldc.i4.1
IL_00a3: ldc.i4.0
IL_00a4: cgt.un
IL_00a6: endfilter
IL_007c: isinst [mscorlib]System.Exception
IL_0081: dup
IL_0082: brtrue.s IL_0088
IL_0084: pop
IL_0085: ldc.i4.0
IL_0086: br.s IL_00a3
IL_0088: stloc.s V_4
IL_008a: ldloc.s V_4
IL_008c: isinst [mscorlib]System.ArgumentException
IL_0091: brtrue.s IL_009f
IL_0093: ldloc.s V_4
IL_0095: isinst [mscorlib]System.IO.IOException
IL_009a: ldnull
IL_009b: cgt.un
IL_009d: br.s IL_00a0
IL_009f: ldc.i4.1
IL_00a0: ldc.i4.0
IL_00a1: cgt.un
IL_00a3: endfilter
} // end filter
{ // handler
IL_00a8: pop
IL_00a9: ldstr "CatchException ex: "
IL_00ae: ldloc.3
IL_00af: callvirt instance string [mscorlib]System.Object::ToString()
IL_00b4: call string [mscorlib]System.String::Concat(string,
IL_00a5: pop
IL_00a6: ldstr "CatchException ex: "
IL_00ab: ldloc.s V_4
IL_00ad: callvirt instance string [mscorlib]System.Object::ToString()
IL_00b2: call string [mscorlib]System.String::Concat(string,
string)
IL_00b9: call void [mscorlib]System.Console::WriteLine(string)
IL_00be: leave.s IL_00c0
IL_00b7: call void [mscorlib]System.Console::WriteLine(string)
IL_00bc: leave.s IL_00be
} // end handler
IL_00c0: ldc.i4.0
IL_00c1: stloc.1
IL_00c2: leave.s IL_00dd
IL_00be: ldc.i4.0
IL_00bf: stloc.2
IL_00c0: leave.s IL_00db
} // end .try
catch [mscorlib]System.Exception
{
IL_00c4: stloc.s V_4
IL_00c6: ldarg.0
IL_00c7: ldc.i4.s -2
IL_00c9: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>1__state'
IL_00ce: ldarg.0
IL_00cf: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<bool> ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>t__builder'
IL_00d4: ldloc.s V_4
IL_00d6: call instance void valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<bool>::SetException(class [mscorlib]System.Exception)
IL_00db: leave.s IL_00f1
IL_00c2: stloc.s V_5
IL_00c4: ldarg.0
IL_00c5: ldc.i4.s -2
IL_00c7: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>1__state'
IL_00cc: ldarg.0
IL_00cd: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<bool> ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>t__builder'
IL_00d2: ldloc.s V_5
IL_00d4: call instance void valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<bool>::SetException(class [mscorlib]System.Exception)
IL_00d9: leave.s IL_00ef
} // end handler
IL_00dd: ldarg.0
IL_00de: ldc.i4.s -2
IL_00e0: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>1__state'
IL_00e5: ldarg.0
IL_00e6: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<bool> ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>t__builder'
IL_00eb: ldloc.1
IL_00ec: call instance void valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<bool>::SetResult(!0)
IL_00f1: ret
IL_00db: ldarg.0
IL_00dc: ldc.i4.s -2
IL_00de: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>1__state'
IL_00e3: ldarg.0
IL_00e4: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<bool> ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>t__builder'
IL_00e9: ldloc.2
IL_00ea: call instance void valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<bool>::SetResult(!0)
IL_00ef: ret
} // end of method '<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::MoveNext
.method private hidebysig newslot virtual final
@ -478,6 +479,48 @@ @@ -478,6 +479,48 @@
IL_0038: ret
} // end of method ExceptionHandling::SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr
.method public hidebysig instance void
CatchWhenWithConditionWithoutExceptionVar() cil managed
{
// Code size 44 (0x2c)
.maxstack 2
.locals init (int32 V_0)
IL_0000: ldc.i4.0
IL_0001: stloc.0
.try
{
IL_0002: newobj instance void [mscorlib]System.Exception::.ctor()
IL_0007: throw
} // end .try
filter
{
IL_0008: isinst [mscorlib]System.Exception
IL_000d: dup
IL_000e: brtrue.s IL_0014
IL_0010: pop
IL_0011: ldc.i4.0
IL_0012: br.s IL_001c
IL_0014: pop
IL_0015: ldloc.0
IL_0016: ldc.i4.0
IL_0017: ceq
IL_0019: ldc.i4.0
IL_001a: cgt.un
IL_001c: endfilter
} // end filter
{ // handler
IL_001e: pop
IL_001f: ldstr "jo"
IL_0024: call void [mscorlib]System.Console::WriteLine(string)
IL_0029: leave.s IL_002b
} // end handler
IL_002b: ret
} // end of method ExceptionHandling::CatchWhenWithConditionWithoutExceptionVar
.method public hidebysig instance bool
SimpleTryFinally() cil managed
{
@ -586,7 +629,7 @@ @@ -586,7 +629,7 @@
IL_0016: leave.s IL_0031
} // end handler
catch [mscorlib]System.Exception
catch [mscorlib]System.SystemException
{
IL_0018: callvirt instance string [mscorlib]System.Exception::get_Message()
IL_001d: call void [mscorlib]System.Console::WriteLine(string)

255
ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExceptionHandling.roslyn.il

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.17929
// Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0
// Copyright (c) Microsoft Corporation. All rights reserved.
@ -25,14 +25,14 @@ @@ -25,14 +25,14 @@
.ver 0:0:0:0
}
.module ExceptionHandling.dll
// MVID: {07B52948-317B-4C30-A2A5-A7482568AD67}
// MVID: {9413A96F-08C3-4F13-A800-C6BE70786A44}
.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x10000000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// Image base: 0x00ED0000
// Image base: 0x00370000
// =============== CLASS MEMBERS DECLARATION ===================
@ -66,15 +66,14 @@ @@ -66,15 +66,14 @@
instance void MoveNext() cil managed
{
.override [mscorlib]System.Runtime.CompilerServices.IAsyncStateMachine::MoveNext
// Code size 306 (0x132)
// Code size 295 (0x127)
.maxstack 3
.locals init (int32 V_0,
bool V_1,
valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool> V_2,
bool V_3,
class ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8' V_4,
class [mscorlib]System.Exception V_5,
bool V_6)
class ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8' V_3,
class [mscorlib]System.Exception V_4,
bool V_5)
IL_0000: ldarg.0
IL_0001: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>1__state'
IL_0006: stloc.0
@ -96,7 +95,7 @@ @@ -96,7 +95,7 @@
IL_0013: br.s IL_0017
IL_0015: br.s IL_0065
IL_0015: br.s IL_0064
IL_0017: nop
IL_0018: ldstr "Try"
@ -109,7 +108,7 @@ @@ -109,7 +108,7 @@
IL_0033: stloc.2
IL_0034: ldloca.s V_2
IL_0036: call instance bool valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>::get_IsCompleted()
IL_003b: brtrue.s IL_0081
IL_003b: brtrue.s IL_0080
IL_003d: ldarg.0
IL_003e: ldc.i4.0
@ -120,117 +119,113 @@ @@ -120,117 +119,113 @@
IL_0047: ldloc.2
IL_0048: stfld valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool> ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>u__1'
IL_004d: ldarg.0
IL_004e: stloc.s V_4
IL_0050: ldarg.0
IL_0051: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<bool> ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>t__builder'
IL_0056: ldloca.s V_2
IL_0058: ldloca.s V_4
IL_005a: call instance void valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<bool>::AwaitUnsafeOnCompleted<valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>,class ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'>(!!0&,
IL_004e: stloc.3
IL_004f: ldarg.0
IL_0050: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<bool> ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>t__builder'
IL_0055: ldloca.s V_2
IL_0057: ldloca.s V_3
IL_0059: call instance void valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<bool>::AwaitUnsafeOnCompleted<valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>,class ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'>(!!0&,
!!1&)
IL_005f: nop
IL_0060: leave IL_0131
IL_0065: ldarg.0
IL_0066: ldfld valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool> ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>u__1'
IL_006b: stloc.2
IL_006c: ldarg.0
IL_006d: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool> ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>u__1'
IL_0072: initobj valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>
IL_0078: ldarg.0
IL_0079: ldc.i4.m1
IL_007a: dup
IL_007b: stloc.0
IL_007c: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>1__state'
IL_005e: nop
IL_005f: leave IL_0126
IL_0064: ldarg.0
IL_0065: ldfld valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool> ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>u__1'
IL_006a: stloc.2
IL_006b: ldarg.0
IL_006c: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool> ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>u__1'
IL_0071: initobj valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>
IL_0077: ldarg.0
IL_0078: ldc.i4.m1
IL_0079: dup
IL_007a: stloc.0
IL_007b: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>1__state'
IL_0080: ldarg.0
IL_0081: ldloca.s V_2
IL_0083: call instance !0 valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>::GetResult()
IL_0088: stloc.3
IL_0089: ldloca.s V_2
IL_008b: initobj valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>
IL_0091: ldarg.0
IL_0092: ldloc.3
IL_0093: stfld bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>s__1'
IL_0098: ldarg.0
IL_0099: ldfld bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>s__1'
IL_009e: stloc.1
IL_009f: leave.s IL_011c
IL_0088: stfld bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>s__1'
IL_008d: ldarg.0
IL_008e: ldfld bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>s__1'
IL_0093: stloc.1
IL_0094: leave.s IL_0111
} // end .try
filter
{
IL_00a1: isinst [mscorlib]System.Exception
IL_00a6: dup
IL_00a7: brtrue.s IL_00ad
IL_00a9: pop
IL_00aa: ldc.i4.0
IL_00ab: br.s IL_00dc
IL_00ad: stloc.s V_5
IL_00af: ldarg.0
IL_00b0: ldloc.s V_5
IL_00b2: stfld class [mscorlib]System.Exception ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<ex>5__2'
IL_00b7: ldarg.0
IL_00b8: ldfld class [mscorlib]System.Exception ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<ex>5__2'
IL_00bd: isinst [mscorlib]System.ArgumentException
IL_00c2: brtrue.s IL_00d4
IL_00c4: ldarg.0
IL_00c5: ldfld class [mscorlib]System.Exception ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<ex>5__2'
IL_00ca: isinst [mscorlib]System.IO.IOException
IL_00cf: ldnull
IL_00d0: cgt.un
IL_00d2: br.s IL_00d5
IL_00d4: ldc.i4.1
IL_00d5: stloc.s V_6
IL_00d7: ldloc.s V_6
IL_00d9: ldc.i4.0
IL_00da: cgt.un
IL_00dc: endfilter
IL_0096: isinst [mscorlib]System.Exception
IL_009b: dup
IL_009c: brtrue.s IL_00a2
IL_009e: pop
IL_009f: ldc.i4.0
IL_00a0: br.s IL_00d1
IL_00a2: stloc.s V_4
IL_00a4: ldarg.0
IL_00a5: ldloc.s V_4
IL_00a7: stfld class [mscorlib]System.Exception ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<ex>5__2'
IL_00ac: ldarg.0
IL_00ad: ldfld class [mscorlib]System.Exception ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<ex>5__2'
IL_00b2: isinst [mscorlib]System.ArgumentException
IL_00b7: brtrue.s IL_00c9
IL_00b9: ldarg.0
IL_00ba: ldfld class [mscorlib]System.Exception ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<ex>5__2'
IL_00bf: isinst [mscorlib]System.IO.IOException
IL_00c4: ldnull
IL_00c5: cgt.un
IL_00c7: br.s IL_00ca
IL_00c9: ldc.i4.1
IL_00ca: stloc.s V_5
IL_00cc: ldloc.s V_5
IL_00ce: ldc.i4.0
IL_00cf: cgt.un
IL_00d1: endfilter
} // end filter
{ // handler
IL_00de: pop
IL_00df: nop
IL_00e0: ldstr "CatchException ex: "
IL_00e5: ldarg.0
IL_00e6: ldfld class [mscorlib]System.Exception ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<ex>5__2'
IL_00eb: callvirt instance string [mscorlib]System.Object::ToString()
IL_00f0: call string [mscorlib]System.String::Concat(string,
IL_00d3: pop
IL_00d4: nop
IL_00d5: ldstr "CatchException ex: "
IL_00da: ldarg.0
IL_00db: ldfld class [mscorlib]System.Exception ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<ex>5__2'
IL_00e0: callvirt instance string [mscorlib]System.Object::ToString()
IL_00e5: call string [mscorlib]System.String::Concat(string,
string)
IL_00f5: call void [mscorlib]System.Console::WriteLine(string)
IL_00fa: nop
IL_00fb: nop
IL_00fc: leave.s IL_00fe
IL_00ea: call void [mscorlib]System.Console::WriteLine(string)
IL_00ef: nop
IL_00f0: nop
IL_00f1: leave.s IL_00f3
} // end handler
IL_00fe: ldc.i4.0
IL_00ff: stloc.1
IL_0100: leave.s IL_011c
IL_00f3: ldc.i4.0
IL_00f4: stloc.1
IL_00f5: leave.s IL_0111
} // end .try
catch [mscorlib]System.Exception
{
IL_0102: stloc.s V_5
IL_0104: ldarg.0
IL_0105: ldc.i4.s -2
IL_0107: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>1__state'
IL_010c: ldarg.0
IL_010d: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<bool> ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>t__builder'
IL_0112: ldloc.s V_5
IL_0114: call instance void valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<bool>::SetException(class [mscorlib]System.Exception)
IL_0119: nop
IL_011a: leave.s IL_0131
IL_00f7: stloc.s V_4
IL_00f9: ldarg.0
IL_00fa: ldc.i4.s -2
IL_00fc: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>1__state'
IL_0101: ldarg.0
IL_0102: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<bool> ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>t__builder'
IL_0107: ldloc.s V_4
IL_0109: call instance void valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<bool>::SetException(class [mscorlib]System.Exception)
IL_010e: nop
IL_010f: leave.s IL_0126
} // end handler
IL_011c: ldarg.0
IL_011d: ldc.i4.s -2
IL_011f: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>1__state'
IL_0124: ldarg.0
IL_0125: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<bool> ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>t__builder'
IL_012a: ldloc.1
IL_012b: call instance void valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<bool>::SetResult(!0)
IL_0130: nop
IL_0131: ret
IL_0111: ldarg.0
IL_0112: ldc.i4.s -2
IL_0114: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>1__state'
IL_0119: ldarg.0
IL_011a: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<bool> ICSharpCode.Decompiler.Tests.TestCases.Pretty.ExceptionHandling/'<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::'<>t__builder'
IL_011f: ldloc.1
IL_0120: call instance void valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<bool>::SetResult(!0)
IL_0125: nop
IL_0126: ret
} // end of method '<SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr>d__8'::MoveNext
.method private hidebysig newslot virtual final
@ -570,6 +565,56 @@ @@ -570,6 +565,56 @@
IL_003a: ret
} // end of method ExceptionHandling::SimpleAsyncTryCatchExceptionWithNameAndConditionWithOr
.method public hidebysig instance void
CatchWhenWithConditionWithoutExceptionVar() cil managed
{
// Code size 51 (0x33)
.maxstack 2
.locals init (int32 V_0,
bool V_1)
IL_0000: nop
IL_0001: ldc.i4.0
IL_0002: stloc.0
.try
{
IL_0003: nop
IL_0004: newobj instance void [mscorlib]System.Exception::.ctor()
IL_0009: throw
} // end .try
filter
{
IL_000a: isinst [mscorlib]System.Exception
IL_000f: dup
IL_0010: brtrue.s IL_0016
IL_0012: pop
IL_0013: ldc.i4.0
IL_0014: br.s IL_0020
IL_0016: pop
IL_0017: ldloc.0
IL_0018: ldc.i4.0
IL_0019: ceq
IL_001b: stloc.1
IL_001c: ldloc.1
IL_001d: ldc.i4.0
IL_001e: cgt.un
IL_0020: endfilter
} // end filter
{ // handler
IL_0022: pop
IL_0023: nop
IL_0024: ldstr "jo"
IL_0029: call void [mscorlib]System.Console::WriteLine(string)
IL_002e: nop
IL_002f: nop
IL_0030: leave.s IL_0032
} // end handler
IL_0032: ret
} // end of method ExceptionHandling::CatchWhenWithConditionWithoutExceptionVar
.method public hidebysig instance bool
SimpleTryFinally() cil managed
{
@ -700,7 +745,7 @@ @@ -700,7 +745,7 @@
// Code size 67 (0x43)
.maxstack 1
.locals init (class [mscorlib]System.InvalidOperationException V_0,
class [mscorlib]System.Exception V_1)
class [mscorlib]System.SystemException V_1)
IL_0000: nop
.try
{
@ -724,7 +769,7 @@ @@ -724,7 +769,7 @@
IL_001f: leave.s IL_0042
} // end handler
catch [mscorlib]System.Exception
catch [mscorlib]System.SystemException
{
IL_0021: stloc.1
IL_0022: nop

118
ICSharpCode.Decompiler.Tests/TestCases/Pretty/FixProxyCalls.cs

@ -0,0 +1,118 @@ @@ -0,0 +1,118 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty
{
internal class A
{
protected internal virtual Task<string> Test(string test)
{
return Task.Run(() => test.ToUpper());
}
}
internal class B : A
{
protected internal override async Task<string> Test(string test)
{
return await base.Test(test);
}
}
internal class C
{
protected internal virtual string Test(string test)
{
return string.Join(test, "fsdf");
}
}
internal class D : C
{
protected internal IEnumerable<string> Test2(string test)
{
yield return base.Test(test);
}
}
internal class E
{
protected internal virtual string Test(string test)
{
return string.Join(test, "fsdf");
}
}
internal class F : E
{
protected internal override string Test(string test)
{
Func<string, string> func = (string a) => base.Test(a);
test = string.Join(test, "aa");
return func(test);
}
}
internal class G
{
protected internal virtual void Test(string test)
{
string.Join(test, "fsdf");
}
}
internal class H : G
{
protected internal override void Test(string test)
{
Action<string> action = delegate(string a) {
base.Test(a);
};
if (test.Equals(1)) {
throw new Exception("roslyn optimize is inlining the assignment which lets the test fail");
}
action(test);
}
}
internal class I
{
protected internal virtual void Test(int a)
{
}
}
internal class J : I
{
protected internal override void Test(int a)
{
Action action = delegate {
base.Test(a);
};
if (a.Equals(1)) {
throw new Exception("roslyn optimize is inlining the assignment which lets the test fail");
}
action();
}
}
internal class K
{
protected internal virtual IEnumerable<int> Test(int p)
{
yield return p + 1;
yield return p + 2;
}
}
internal class L : K
{
protected internal override IEnumerable<int> Test(int p)
{
yield return base.Test(base.Test(0).GetEnumerator().Current).GetEnumerator().Current;
}
}
}

1579
ICSharpCode.Decompiler.Tests/TestCases/Pretty/FixProxyCalls.il

File diff suppressed because it is too large Load Diff

1323
ICSharpCode.Decompiler.Tests/TestCases/Pretty/FixProxyCalls.opt.il

File diff suppressed because it is too large Load Diff

1294
ICSharpCode.Decompiler.Tests/TestCases/Pretty/FixProxyCalls.opt.roslyn.il

File diff suppressed because it is too large Load Diff

1424
ICSharpCode.Decompiler.Tests/TestCases/Pretty/FixProxyCalls.roslyn.il

File diff suppressed because it is too large Load Diff

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

@ -0,0 +1,484 @@ @@ -0,0 +1,484 @@
// 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;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
public class InitializerTests
{
#region Types and helpers
public class C
{
public int Z;
public S Y;
public List<S> L;
}
public struct S
{
public int A;
public int B;
public S(int a)
{
this.A = a;
this.B = 0;
}
}
private enum MyEnum
{
a = 0,
b = 1
}
private enum MyEnum2
{
c = 0,
d = 1
}
private class Data
{
public List<MyEnum2> FieldList = new List<MyEnum2>();
public MyEnum a {
get;
set;
}
public MyEnum b {
get;
set;
}
public List<MyEnum2> PropertyList {
get;
set;
}
public Data MoreData {
get;
set;
}
public StructData NestedStruct {
get;
set;
}
public Data this[int i] {
get {
return null;
}
set {
}
}
public Data this[int i, string j] {
get {
return null;
}
set {
}
}
public event EventHandler TestEvent;
}
private struct StructData
{
public int Field;
public int Property {
get;
set;
}
public Data MoreData {
get;
set;
}
public StructData(int initialValue)
{
this = default(StructData);
this.Field = initialValue;
this.Property = initialValue;
}
}
// Helper methods used to ensure initializers used within expressions work correctly
private static void X(object a, object b)
{
}
private static object Y()
{
return null;
}
public static void TestCall(int a, Thread thread)
{
}
public static C TestCall(int a, C c)
{
return c;
}
#endregion
public C Test1()
{
C c = new C();
c.L = new List<S>();
c.L.Add(new S(1));
return c;
}
public C Test1Alternative()
{
return InitializerTests.TestCall(1, new C {
L = new List<S> {
new S(1)
}
});
}
public C Test2()
{
C c = new C();
c.Z = 1;
c.Z = 2;
return c;
}
public C Test3()
{
C c = new C();
c.Y = new S(1);
c.Y.A = 2;
return c;
}
public C Test3b()
{
return InitializerTests.TestCall(0, new C {
Z = 1,
Y = {
A = 2
}
});
}
public C Test4()
{
C c = new C();
c.Y.A = 1;
c.Z = 2;
c.Y.B = 3;
return c;
}
public static void CollectionInitializerList()
{
InitializerTests.X(InitializerTests.Y(), new List<int> {
1,
2,
3
});
}
public static object RecursiveCollectionInitializer()
{
List<object> list = new List<object>();
list.Add(list);
return list;
}
public static void CollectionInitializerDictionary()
{
InitializerTests.X(InitializerTests.Y(), new Dictionary<string, int> {
{
"First",
1
},
{
"Second",
2
},
{
"Third",
3
}
});
}
public static void CollectionInitializerDictionaryWithEnumTypes()
{
InitializerTests.X(InitializerTests.Y(), new Dictionary<MyEnum, MyEnum2> {
{
MyEnum.a,
MyEnum2.c
},
{
MyEnum.b,
MyEnum2.d
}
});
}
public static void NotACollectionInitializer()
{
List<int> list = new List<int>();
list.Add(1);
list.Add(2);
list.Add(3);
InitializerTests.X(InitializerTests.Y(), list);
}
public static void ObjectInitializer()
{
InitializerTests.X(InitializerTests.Y(), new Data {
a = MyEnum.a
});
}
public static void NotAnObjectInitializer()
{
Data data = new Data();
data.a = MyEnum.a;
InitializerTests.X(InitializerTests.Y(), data);
}
public static void NotAnObjectInitializerWithEvent()
{
Data data = new Data();
data.TestEvent += delegate(object sender, EventArgs e) {
Console.WriteLine();
};
InitializerTests.X(InitializerTests.Y(), data);
}
public static void ObjectInitializerAssignCollectionToField()
{
InitializerTests.X(InitializerTests.Y(), new Data {
a = MyEnum.a,
FieldList = new List<MyEnum2> {
MyEnum2.c,
MyEnum2.d
}
});
}
public static void ObjectInitializerAddToCollectionInField()
{
InitializerTests.X(InitializerTests.Y(), new Data {
a = MyEnum.a,
FieldList = {
MyEnum2.c,
MyEnum2.d
}
});
}
public static void ObjectInitializerAssignCollectionToProperty()
{
InitializerTests.X(InitializerTests.Y(), new Data {
a = MyEnum.a,
PropertyList = new List<MyEnum2> {
MyEnum2.c,
MyEnum2.d
}
});
}
public static void ObjectInitializerAddToCollectionInProperty()
{
InitializerTests.X(InitializerTests.Y(), new Data {
a = MyEnum.a,
PropertyList = {
MyEnum2.c,
MyEnum2.d
}
});
}
public static void ObjectInitializerWithInitializationOfNestedObjects()
{
InitializerTests.X(InitializerTests.Y(), new Data {
MoreData = {
a = MyEnum.a,
MoreData = {
a = MyEnum.b
}
}
});
}
private static int GetInt()
{
return 1;
}
private static string GetString()
{
return "Test";
}
#if !LEGACY_CSC
public static void SimpleDictInitializer()
{
InitializerTests.X(InitializerTests.Y(), new Data {
MoreData = {
a = MyEnum.a,
[2] = null
}
});
}
public static void MixedObjectAndDictInitializer()
{
InitializerTests.X(InitializerTests.Y(), new Data {
MoreData = {
a = MyEnum.a,
[InitializerTests.GetInt()] = {
a = MyEnum.b,
FieldList = {
MyEnum2.c
},
[InitializerTests.GetInt(), InitializerTests.GetString()] = new Data(),
[2] = null
}
}
});
}
#endif
public static void ObjectInitializerWithInitializationOfDeeplyNestedObjects()
{
InitializerTests.X(InitializerTests.Y(), new Data {
a = MyEnum.b,
MoreData = {
a = MyEnum.a,
MoreData = {
MoreData = {
MoreData = {
MoreData = {
MoreData = {
MoreData = {
a = MyEnum.b
}
}
}
}
}
}
}
});
}
public static void CollectionInitializerInsideObjectInitializers()
{
InitializerTests.X(InitializerTests.Y(), new Data {
MoreData = new Data {
a = MyEnum.a,
b = MyEnum.b,
PropertyList = {
MyEnum2.c
}
}
});
}
public static void NotAStructInitializer_DefaultConstructor()
{
StructData structData = default(StructData);
structData.Field = 1;
structData.Property = 2;
InitializerTests.X(InitializerTests.Y(), structData);
}
public static void StructInitializer_DefaultConstructor()
{
InitializerTests.X(InitializerTests.Y(), new StructData {
Field = 1,
Property = 2
});
}
public static void NotAStructInitializer_ExplicitConstructor()
{
StructData structData = new StructData(0);
structData.Field = 1;
structData.Property = 2;
InitializerTests.X(InitializerTests.Y(), structData);
}
public static void StructInitializer_ExplicitConstructor()
{
InitializerTests.X(InitializerTests.Y(), new StructData(0) {
Field = 1,
Property = 2
});
}
public static void StructInitializerWithInitializationOfNestedObjects()
{
InitializerTests.X(InitializerTests.Y(), new StructData {
MoreData = {
a = MyEnum.a,
FieldList = {
MyEnum2.c,
MyEnum2.d
}
}
});
}
public static void StructInitializerWithinObjectInitializer()
{
InitializerTests.X(InitializerTests.Y(), new Data {
NestedStruct = new StructData(2) {
Field = 1,
Property = 2
}
});
}
public static void Bug270_NestedInitialisers()
{
NumberFormatInfo[] source = null;
InitializerTests.TestCall(0, new Thread(InitializerTests.Bug270_NestedInitialisers) {
Priority = ThreadPriority.BelowNormal,
CurrentCulture = new CultureInfo(0) {
DateTimeFormat = new DateTimeFormatInfo {
ShortDatePattern = "ddmmyy"
},
NumberFormat = (from format in source
where format.CurrencySymbol == "$"
select format).First()
}
});
}
}
}

1522
ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.il

File diff suppressed because it is too large Load Diff

1281
ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.opt.il

File diff suppressed because it is too large Load Diff

1314
ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.opt.roslyn.il

File diff suppressed because it is too large Load Diff

1533
ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.roslyn.il

File diff suppressed because it is too large Load Diff

17
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.cs

@ -252,6 +252,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -252,6 +252,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
}
private IEnumerable<string> alternatives;
private static void Operation(ref int item)
{
}
@ -260,10 +262,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -260,10 +262,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
}
public void ForEach(IEnumerable<string> enumerable)
public void ForEachOnField()
{
foreach (string item in enumerable) {
item.ToLower();
foreach (string alternative in this.alternatives) {
alternative.ToLower();
}
}
public void ForEach(IEnumerable<string> alternatives)
{
foreach (string alternative in alternatives) {
alternative.ToLower();
}
}
@ -388,7 +397,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -388,7 +397,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
foreach (int item in items) {
int c = item;
Loops.Operation((Func<bool>)(() => c == 5));
Loops.Operation(() => c == 5);
}
}

70
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.il

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.17929
// Copyright (c) Microsoft Corporation. All rights reserved.
// Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0
// Copyright (c) Microsoft Corporation. Alle Rechte vorbehalten.
@ -10,7 +10,7 @@ @@ -10,7 +10,7 @@
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
.assembly y5enlto2
.assembly ic2bztjj
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
.custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx
@ -20,15 +20,15 @@ @@ -20,15 +20,15 @@
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module y5enlto2.dll
// MVID: {7F6CCF81-BE23-4326-8F55-7E3BF0A8B7A2}
.module ic2bztjj.dll
// MVID: {821665CB-F67F-4600-8C6C-27617D873D36}
.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x10000000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// Image base: 0x009B0000
// Image base: 0x01550000
// =============== CLASS MEMBERS DECLARATION ===================
@ -618,6 +618,7 @@ @@ -618,6 +618,7 @@
} // end of class '<>c__DisplayClass1'
.field private class [mscorlib]System.Collections.Generic.IEnumerable`1<string> alternatives
.method private hidebysig static void Operation(int32& item) cil managed
{
// Code size 2 (0x2)
@ -635,7 +636,60 @@ @@ -635,7 +636,60 @@
} // end of method Loops::Operation
.method public hidebysig instance void
ForEach(class [mscorlib]System.Collections.Generic.IEnumerable`1<string> enumerable) cil managed
ForEachOnField() cil managed
{
// Code size 62 (0x3e)
.maxstack 2
.locals init (string V_0,
class [mscorlib]System.Collections.Generic.IEnumerator`1<string> V_1,
bool V_2)
IL_0000: nop
IL_0001: nop
IL_0002: ldarg.0
IL_0003: ldfld class [mscorlib]System.Collections.Generic.IEnumerable`1<string> ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::alternatives
IL_0008: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> class [mscorlib]System.Collections.Generic.IEnumerable`1<string>::GetEnumerator()
IL_000d: stloc.1
.try
{
IL_000e: br.s IL_0020
IL_0010: ldloc.1
IL_0011: callvirt instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1<string>::get_Current()
IL_0016: stloc.0
IL_0017: nop
IL_0018: ldloc.0
IL_0019: callvirt instance string [mscorlib]System.String::ToLower()
IL_001e: pop
IL_001f: nop
IL_0020: ldloc.1
IL_0021: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
IL_0026: stloc.2
IL_0027: ldloc.2
IL_0028: brtrue.s IL_0010
IL_002a: leave.s IL_003c
} // end .try
finally
{
IL_002c: ldloc.1
IL_002d: ldnull
IL_002e: ceq
IL_0030: stloc.2
IL_0031: ldloc.2
IL_0032: brtrue.s IL_003b
IL_0034: ldloc.1
IL_0035: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_003a: nop
IL_003b: endfinally
} // end handler
IL_003c: nop
IL_003d: ret
} // end of method Loops::ForEachOnField
.method public hidebysig instance void
ForEach(class [mscorlib]System.Collections.Generic.IEnumerable`1<string> alternatives) cil managed
{
// Code size 57 (0x39)
.maxstack 2
@ -1750,4 +1804,4 @@ @@ -1750,4 +1804,4 @@
// =============================================================
// *********** DISASSEMBLY COMPLETE ***********************
// WARNING: Created Win32 resource file ../../../TestCases/Pretty\Loops.res
// Warnung: Win32-Ressourcendatei "../../../TestCases/Pretty\Loops.res" wurde erstellt.

57
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.opt.il

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.17929
// Copyright (c) Microsoft Corporation. All rights reserved.
// Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0
// Copyright (c) Microsoft Corporation. Alle Rechte vorbehalten.
@ -10,7 +10,7 @@ @@ -10,7 +10,7 @@
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
.assembly jiaqp12i
.assembly fabjeeha
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
.custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx
@ -20,15 +20,15 @@ @@ -20,15 +20,15 @@
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module jiaqp12i.dll
// MVID: {34208704-52B8-49EB-BC77-C3A95E1CBA19}
.module fabjeeha.dll
// MVID: {4FB3AE1F-ECE0-4E7D-84DC-E693282CA0AF}
.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x10000000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// Image base: 0x02520000
// Image base: 0x00D90000
// =============== CLASS MEMBERS DECLARATION ===================
@ -535,6 +535,7 @@ @@ -535,6 +535,7 @@
} // end of class '<>c__DisplayClass1'
.field private class [mscorlib]System.Collections.Generic.IEnumerable`1<string> alternatives
.method private hidebysig static void Operation(int32& item) cil managed
{
// Code size 1 (0x1)
@ -550,7 +551,47 @@ @@ -550,7 +551,47 @@
} // end of method Loops::Operation
.method public hidebysig instance void
ForEach(class [mscorlib]System.Collections.Generic.IEnumerable`1<string> enumerable) cil managed
ForEachOnField() cil managed
{
// Code size 49 (0x31)
.maxstack 1
.locals init (string V_0,
class [mscorlib]System.Collections.Generic.IEnumerator`1<string> V_1)
IL_0000: ldarg.0
IL_0001: ldfld class [mscorlib]System.Collections.Generic.IEnumerable`1<string> ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::alternatives
IL_0006: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> class [mscorlib]System.Collections.Generic.IEnumerable`1<string>::GetEnumerator()
IL_000b: stloc.1
.try
{
IL_000c: br.s IL_001c
IL_000e: ldloc.1
IL_000f: callvirt instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1<string>::get_Current()
IL_0014: stloc.0
IL_0015: ldloc.0
IL_0016: callvirt instance string [mscorlib]System.String::ToLower()
IL_001b: pop
IL_001c: ldloc.1
IL_001d: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
IL_0022: brtrue.s IL_000e
IL_0024: leave.s IL_0030
} // end .try
finally
{
IL_0026: ldloc.1
IL_0027: brfalse.s IL_002f
IL_0029: ldloc.1
IL_002a: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_002f: endfinally
} // end handler
IL_0030: ret
} // end of method Loops::ForEachOnField
.method public hidebysig instance void
ForEach(class [mscorlib]System.Collections.Generic.IEnumerable`1<string> alternatives) cil managed
{
// Code size 44 (0x2c)
.maxstack 1
@ -1361,4 +1402,4 @@ @@ -1361,4 +1402,4 @@
// =============================================================
// *********** DISASSEMBLY COMPLETE ***********************
// WARNING: Created Win32 resource file ../../../TestCases/Pretty\Loops.opt.res
// Warnung: Win32-Ressourcendatei "../../../TestCases/Pretty\Loops.opt.res" wurde erstellt.

64
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.opt.roslyn.il

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.17929
// Copyright (c) Microsoft Corporation. All rights reserved.
// Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0
// Copyright (c) Microsoft Corporation. Alle Rechte vorbehalten.
@ -25,14 +25,14 @@ @@ -25,14 +25,14 @@
.ver 0:0:0:0
}
.module Loops.dll
// MVID: {8D392B4A-5D21-407A-B579-FCDFA93069CE}
// MVID: {F03B196B-9B00-49BE-A335-2D460586E39A}
.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x10000000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// Image base: 0x00DA0000
// Image base: 0x02D60000
// =============== CLASS MEMBERS DECLARATION ===================
@ -510,7 +510,7 @@ @@ -510,7 +510,7 @@
} // end of property CustomStructEnumeratorWithIDisposable`1::Current
} // end of class CustomStructEnumeratorWithIDisposable`1
.class auto ansi sealed nested private beforefieldinit '<>c__DisplayClass23_0'
.class auto ansi sealed nested private beforefieldinit '<>c__DisplayClass25_0'
extends [mscorlib]System.Object
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
@ -523,7 +523,7 @@ @@ -523,7 +523,7 @@
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method '<>c__DisplayClass23_0'::.ctor
} // end of method '<>c__DisplayClass25_0'::.ctor
.method assembly hidebysig instance bool
'<ForeachWithCapturedVariable>b__0'() cil managed
@ -531,14 +531,15 @@ @@ -531,14 +531,15 @@
// Code size 10 (0xa)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass23_0'::c
IL_0001: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass25_0'::c
IL_0006: ldc.i4.5
IL_0007: ceq
IL_0009: ret
} // end of method '<>c__DisplayClass23_0'::'<ForeachWithCapturedVariable>b__0'
} // end of method '<>c__DisplayClass25_0'::'<ForeachWithCapturedVariable>b__0'
} // end of class '<>c__DisplayClass23_0'
} // end of class '<>c__DisplayClass25_0'
.field private class [mscorlib]System.Collections.Generic.IEnumerable`1<string> alternatives
.method private hidebysig static void Operation(int32& item) cil managed
{
// Code size 1 (0x1)
@ -554,7 +555,44 @@ @@ -554,7 +555,44 @@
} // end of method Loops::Operation
.method public hidebysig instance void
ForEach(class [mscorlib]System.Collections.Generic.IEnumerable`1<string> enumerable) cil managed
ForEachOnField() cil managed
{
// Code size 47 (0x2f)
.maxstack 1
.locals init (class [mscorlib]System.Collections.Generic.IEnumerator`1<string> V_0)
IL_0000: ldarg.0
IL_0001: ldfld class [mscorlib]System.Collections.Generic.IEnumerable`1<string> ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::alternatives
IL_0006: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> class [mscorlib]System.Collections.Generic.IEnumerable`1<string>::GetEnumerator()
IL_000b: stloc.0
.try
{
IL_000c: br.s IL_001a
IL_000e: ldloc.0
IL_000f: callvirt instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1<string>::get_Current()
IL_0014: callvirt instance string [mscorlib]System.String::ToLower()
IL_0019: pop
IL_001a: ldloc.0
IL_001b: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
IL_0020: brtrue.s IL_000e
IL_0022: leave.s IL_002e
} // end .try
finally
{
IL_0024: ldloc.0
IL_0025: brfalse.s IL_002d
IL_0027: ldloc.0
IL_0028: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_002d: endfinally
} // end handler
IL_002e: ret
} // end of method Loops::ForEachOnField
.method public hidebysig instance void
ForEach(class [mscorlib]System.Collections.Generic.IEnumerable`1<string> alternatives) cil managed
{
// Code size 42 (0x2a)
.maxstack 1
@ -1063,11 +1101,11 @@ @@ -1063,11 +1101,11 @@
IL_0009: ldloca.s V_0
IL_000b: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>::get_Current()
IL_0010: stloc.1
IL_0011: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass23_0'::.ctor()
IL_0011: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass25_0'::.ctor()
IL_0016: dup
IL_0017: ldloc.1
IL_0018: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass23_0'::c
IL_001d: ldftn instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass23_0'::'<ForeachWithCapturedVariable>b__0'()
IL_0018: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass25_0'::c
IL_001d: ldftn instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass25_0'::'<ForeachWithCapturedVariable>b__0'()
IL_0023: newobj instance void class [mscorlib]System.Func`1<bool>::.ctor(object,
native int)
IL_0028: call void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Operation(class [mscorlib]System.Func`1<bool>)

74
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.roslyn.il

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.17929
// Copyright (c) Microsoft Corporation. All rights reserved.
// Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0
// Copyright (c) Microsoft Corporation. Alle Rechte vorbehalten.
@ -25,14 +25,14 @@ @@ -25,14 +25,14 @@
.ver 0:0:0:0
}
.module Loops.dll
// MVID: {C6DBFA5B-CB31-4084-8E6F-7BA877923008}
// MVID: {F66BA703-8DC0-4A56-81BC-D288A1D83D30}
.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x10000000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// Image base: 0x00A80000
// Image base: 0x02D60000
// =============== CLASS MEMBERS DECLARATION ===================
@ -592,7 +592,7 @@ @@ -592,7 +592,7 @@
} // end of property CustomStructEnumeratorWithIDisposable`1::Current
} // end of class CustomStructEnumeratorWithIDisposable`1
.class auto ansi sealed nested private beforefieldinit '<>c__DisplayClass23_0'
.class auto ansi sealed nested private beforefieldinit '<>c__DisplayClass25_0'
extends [mscorlib]System.Object
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
@ -606,7 +606,7 @@ @@ -606,7 +606,7 @@
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: nop
IL_0007: ret
} // end of method '<>c__DisplayClass23_0'::.ctor
} // end of method '<>c__DisplayClass25_0'::.ctor
.method assembly hidebysig instance bool
'<ForeachWithCapturedVariable>b__0'() cil managed
@ -614,14 +614,15 @@ @@ -614,14 +614,15 @@
// Code size 10 (0xa)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass23_0'::c
IL_0001: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass25_0'::c
IL_0006: ldc.i4.5
IL_0007: ceq
IL_0009: ret
} // end of method '<>c__DisplayClass23_0'::'<ForeachWithCapturedVariable>b__0'
} // end of method '<>c__DisplayClass25_0'::'<ForeachWithCapturedVariable>b__0'
} // end of class '<>c__DisplayClass23_0'
} // end of class '<>c__DisplayClass25_0'
.field private class [mscorlib]System.Collections.Generic.IEnumerable`1<string> alternatives
.method private hidebysig static void Operation(int32& item) cil managed
{
// Code size 2 (0x2)
@ -639,7 +640,52 @@ @@ -639,7 +640,52 @@
} // end of method Loops::Operation
.method public hidebysig instance void
ForEach(class [mscorlib]System.Collections.Generic.IEnumerable`1<string> enumerable) cil managed
ForEachOnField() cil managed
{
// Code size 54 (0x36)
.maxstack 1
.locals init (class [mscorlib]System.Collections.Generic.IEnumerator`1<string> V_0,
string V_1)
IL_0000: nop
IL_0001: nop
IL_0002: ldarg.0
IL_0003: ldfld class [mscorlib]System.Collections.Generic.IEnumerable`1<string> ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::alternatives
IL_0008: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> class [mscorlib]System.Collections.Generic.IEnumerable`1<string>::GetEnumerator()
IL_000d: stloc.0
.try
{
IL_000e: br.s IL_0020
IL_0010: ldloc.0
IL_0011: callvirt instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1<string>::get_Current()
IL_0016: stloc.1
IL_0017: nop
IL_0018: ldloc.1
IL_0019: callvirt instance string [mscorlib]System.String::ToLower()
IL_001e: pop
IL_001f: nop
IL_0020: ldloc.0
IL_0021: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
IL_0026: brtrue.s IL_0010
IL_0028: leave.s IL_0035
} // end .try
finally
{
IL_002a: ldloc.0
IL_002b: brfalse.s IL_0034
IL_002d: ldloc.0
IL_002e: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0033: nop
IL_0034: endfinally
} // end handler
IL_0035: ret
} // end of method Loops::ForEachOnField
.method public hidebysig instance void
ForEach(class [mscorlib]System.Collections.Generic.IEnumerable`1<string> alternatives) cil managed
{
// Code size 49 (0x31)
.maxstack 1
@ -1259,7 +1305,7 @@ @@ -1259,7 +1305,7 @@
.maxstack 2
.locals init (valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32> V_0,
int32 V_1,
class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass23_0' V_2)
class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass25_0' V_2)
IL_0000: nop
IL_0001: nop
IL_0002: ldarg.0
@ -1272,14 +1318,14 @@ @@ -1272,14 +1318,14 @@
IL_000b: ldloca.s V_0
IL_000d: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>::get_Current()
IL_0012: stloc.1
IL_0013: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass23_0'::.ctor()
IL_0013: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass25_0'::.ctor()
IL_0018: stloc.2
IL_0019: nop
IL_001a: ldloc.2
IL_001b: ldloc.1
IL_001c: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass23_0'::c
IL_001c: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass25_0'::c
IL_0021: ldloc.2
IL_0022: ldftn instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass23_0'::'<ForeachWithCapturedVariable>b__0'()
IL_0022: ldftn instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass25_0'::'<ForeachWithCapturedVariable>b__0'()
IL_0028: newobj instance void class [mscorlib]System.Func`1<bool>::.ctor(object,
native int)
IL_002d: call void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Operation(class [mscorlib]System.Func`1<bool>)

2
ICSharpCode.Decompiler.Tests/TestCases/Pretty/PropertiesAndEvents.cs

@ -40,7 +40,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -40,7 +40,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public event EventHandler AutomaticEvent;
[field: NonSerialized]
public event EventHandler AutomaticEventWithInitializer = (EventHandler)delegate(object sender, EventArgs e) {
public event EventHandler AutomaticEventWithInitializer = delegate(object sender, EventArgs e) {
};
public event EventHandler CustomEvent {

2
ICSharpCode.Decompiler.Tests/TestCases/Pretty/QueryExpressions.cs

@ -111,7 +111,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -111,7 +111,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public object FromLetWhereSelect()
{
return from o in this.orders
let t = o.Details.Sum((Func<OrderDetail, decimal>)((OrderDetail d) => d.UnitPrice * d.Quantity))
let t = o.Details.Sum((OrderDetail d) => d.UnitPrice * d.Quantity)
where t >= 1000m
select new {
OrderID = o.OrderID,

450
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs

@ -0,0 +1,450 @@ @@ -0,0 +1,450 @@
// 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;
using System.Collections.Generic;
using System.Reflection;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
public static class Switch
{
public class SetProperty
{
public readonly PropertyInfo Property;
public int Set {
get;
set;
}
public SetProperty(PropertyInfo property)
{
this.Property = property;
}
}
public enum State
{
False = 0,
True = 1,
Null = 2
}
public static State SwitchOverNullableBool(bool? value)
{
switch (value) {
case false:
return State.False;
case true:
return State.True;
case null:
return State.Null;
default:
throw new InvalidOperationException();
}
}
public static bool? SwitchOverNullableEnum(State? state)
{
switch (state) {
case State.False:
return false;
case State.True:
return true;
case State.Null:
return null;
default:
throw new InvalidOperationException();
}
}
public static string SparseIntegerSwitch(int i)
{
Console.WriteLine("SparseIntegerSwitch: " + i);
switch (i) {
case -10000000:
return "-10 mln";
case -100:
return "-hundred";
case -1:
return "-1";
case 0:
return "0";
case 1:
return "1";
case 2:
return "2";
case 4:
return "4";
case 100:
return "hundred";
case 10000:
return "ten thousand";
case 10001:
return "ten thousand and one";
case 2147483647:
return "int.MaxValue";
default:
return "something else";
}
}
public static string SwitchOverNullableInt(int? i)
{
switch (i) {
case null:
return "null";
case 0:
return "zero";
case 5:
return "five";
case 10:
return "ten";
default:
return "large";
}
}
public static string SwitchOverNullableIntNullCaseCombined(int? i)
{
switch (i) {
case null:
case 0:
return "zero";
case 5:
return "five";
case 10:
return "ten";
default:
return "large";
}
}
public static string SwitchOverNullableIntShifted(int? i)
{
switch (i + 5) {
case null:
return "null";
case 0:
return "zero";
case 5:
return "five";
case 10:
return "ten";
default:
return "large";
}
}
public static string SwitchOverNullableIntShiftedNullCaseCombined(int? i)
{
switch (i + 5) {
case null:
case 0:
return "zero";
case 5:
return "five";
case 10:
return "ten";
default:
return "large";
}
}
public static string SwitchOverNullableIntNoNullCase(int? i)
{
switch (i) {
case 0:
return "zero";
case 5:
return "five";
case 10:
return "ten";
default:
return "other";
}
}
public static string SwitchOverNullableIntNoNullCaseShifted(int? i)
{
switch (i + 5) {
case 0:
return "zero";
case 5:
return "five";
case 10:
return "ten";
default:
return "other";
}
}
public static void SwitchOverInt(int i)
{
switch (i) {
case 0:
Console.WriteLine("zero");
break;
case 5:
Console.WriteLine("five");
break;
case 10:
Console.WriteLine("ten");
break;
case 15:
Console.WriteLine("fifteen");
break;
case 20:
Console.WriteLine("twenty");
break;
case 25:
Console.WriteLine("twenty-five");
break;
case 30:
Console.WriteLine("thirty");
break;
}
}
public static string ShortSwitchOverString(string text)
{
Console.WriteLine("ShortSwitchOverString: " + text);
switch (text) {
case "First case":
return "Text1";
case "Second case":
return "Text2";
case "Third case":
return "Text3";
default:
return "Default";
}
}
public static string ShortSwitchOverStringWithNullCase(string text)
{
Console.WriteLine("ShortSwitchOverStringWithNullCase: " + text);
switch (text) {
case "First case":
return "Text1";
case "Second case":
return "Text2";
case null:
return "null";
default:
return "Default";
}
}
public static string SwitchOverString1(string text)
{
Console.WriteLine("SwitchOverString1: " + text);
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";
}
}
public static string SwitchOverString2()
{
Console.WriteLine("SwitchOverString2:");
switch (Environment.UserName) {
case "First case":
return "Text1";
case "Second case":
return "Text2";
case "Third case":
return "Text3";
case "Fourth case":
return "Text4";
case "Fifth case":
return "Text5";
case "Sixth case":
return "Text6";
case "Seventh case":
return "Text7";
case "Eighth case":
return "Text8";
case "Ninth case":
return "Text9";
case "Tenth case":
return "Text10";
case "Eleventh case":
return "Text11";
default:
return "Default";
}
}
public static string SwitchOverBool(bool b)
{
Console.WriteLine("SwitchOverBool: " + b.ToString());
switch (b) {
case true:
return bool.TrueString;
case false:
return bool.FalseString;
default:
return null;
}
}
public static void SwitchInLoop(int i)
{
Console.WriteLine("SwitchInLoop: " + i);
while (true) {
switch (i) {
case 1:
Console.WriteLine("one");
break;
case 2:
Console.WriteLine("two");
break;
//case 3:
// Console.WriteLine("three");
// continue;
case 4:
Console.WriteLine("four");
return;
default:
Console.WriteLine("default");
Console.WriteLine("more code");
return;
}
i++;
}
}
public static void SwitchWithGoto(int i)
{
Console.WriteLine("SwitchWithGoto: " + i);
switch (i) {
case 1:
Console.WriteLine("one");
goto default;
case 2:
Console.WriteLine("two");
goto case 3;
case 3:
Console.WriteLine("three");
break;
case 4:
Console.WriteLine("four");
return;
default:
Console.WriteLine("default");
break;
}
Console.WriteLine("End of method");
}
private static SetProperty[] GetProperties()
{
return new SetProperty[0];
}
public static void SwitchOnStringInForLoop()
{
List<SetProperty> list = new List<SetProperty>();
List<SetProperty> list2 = new List<SetProperty>();
SetProperty[] properties = Switch.GetProperties();
for (int i = 0; i < properties.Length; i++) {
SetProperty setProperty = properties[i];
switch (setProperty.Property.Name) {
case "Name1":
setProperty.Set = 1;
list.Add(setProperty);
break;
case "Name2":
setProperty.Set = 2;
list.Add(setProperty);
break;
case "Name3":
setProperty.Set = 3;
list.Add(setProperty);
break;
case "Name4":
setProperty.Set = 4;
list.Add(setProperty);
break;
case "Name5":
case "Name6":
list.Add(setProperty);
break;
default:
list2.Add(setProperty);
break;
}
}
}
public static void SwitchWithComplexCondition(string[] args)
{
switch ((args.Length == 0) ? "dummy" : args[0]) {
case "a":
Console.WriteLine("a");
break;
case "b":
Console.WriteLine("b");
break;
case "c":
Console.WriteLine("c");
break;
case "d":
Console.WriteLine("d");
break;
}
Console.WriteLine("end");
}
public static void SwitchWithArray(string[] args)
{
switch (args[0]) {
case "a":
Console.WriteLine("a");
break;
case "b":
Console.WriteLine("b");
break;
case "c":
Console.WriteLine("c");
break;
case "d":
Console.WriteLine("d");
break;
}
Console.WriteLine("end");
}
}
}

1624
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il

File diff suppressed because it is too large Load Diff

1385
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il

File diff suppressed because it is too large Load Diff

1502
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il

File diff suppressed because it is too large Load Diff

1853
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il

File diff suppressed because it is too large Load Diff

15
ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.cs

@ -199,5 +199,20 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -199,5 +199,20 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
return Math.Abs(a - b);
}
public bool CompareDelegatesByValue(Action a, Action b)
{
return a == b;
}
public bool CompareDelegatesByReference(Action a, Action b)
{
return (object)a == b;
}
public bool CompareDelegateWithNull(Action a)
{
return a == null;
}
}
}

64
ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.il

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.18020
// Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0
// Copyright (c) Microsoft Corporation. All rights reserved.
@ -10,7 +10,7 @@ @@ -10,7 +10,7 @@
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
.assembly q5z5ui4l
.assembly ph2u0axx
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
.custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx
@ -20,15 +20,15 @@ @@ -20,15 +20,15 @@
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module q5z5ui4l.dll
// MVID: {E0E1769A-5636-428C-B7CE-29408F5D108F}
.module ph2u0axx.dll
// MVID: {8F821F70-8053-4F18-BDF7-88A43880B754}
.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x10000000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// Image base: 0x024D0000
// Image base: 0x00D70000
// =============== CLASS MEMBERS DECLARATION ===================
@ -681,6 +681,60 @@ @@ -681,6 +681,60 @@
IL_000d: ret
} // end of method TypeAnalysisTests::EnumDiff
.method public hidebysig instance bool
CompareDelegatesByValue(class [mscorlib]System.Action a,
class [mscorlib]System.Action b) cil managed
{
// Code size 13 (0xd)
.maxstack 2
.locals init (bool V_0)
IL_0000: nop
IL_0001: ldarg.1
IL_0002: ldarg.2
IL_0003: call bool [mscorlib]System.Delegate::op_Equality(class [mscorlib]System.Delegate,
class [mscorlib]System.Delegate)
IL_0008: stloc.0
IL_0009: br.s IL_000b
IL_000b: ldloc.0
IL_000c: ret
} // end of method TypeAnalysisTests::CompareDelegatesByValue
.method public hidebysig instance bool
CompareDelegatesByReference(class [mscorlib]System.Action a,
class [mscorlib]System.Action b) cil managed
{
// Code size 10 (0xa)
.maxstack 2
.locals init (bool V_0)
IL_0000: nop
IL_0001: ldarg.1
IL_0002: ldarg.2
IL_0003: ceq
IL_0005: stloc.0
IL_0006: br.s IL_0008
IL_0008: ldloc.0
IL_0009: ret
} // end of method TypeAnalysisTests::CompareDelegatesByReference
.method public hidebysig instance bool
CompareDelegateWithNull(class [mscorlib]System.Action a) cil managed
{
// Code size 10 (0xa)
.maxstack 2
.locals init (bool V_0)
IL_0000: nop
IL_0001: ldarg.1
IL_0002: ldnull
IL_0003: ceq
IL_0005: stloc.0
IL_0006: br.s IL_0008
IL_0008: ldloc.0
IL_0009: ret
} // end of method TypeAnalysisTests::CompareDelegateWithNull
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{

46
ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.opt.il

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.18020
// Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0
// Copyright (c) Microsoft Corporation. All rights reserved.
@ -10,7 +10,7 @@ @@ -10,7 +10,7 @@
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
.assembly '5lntuoy2'
.assembly frnxitb5
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
.custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx
@ -20,15 +20,15 @@ @@ -20,15 +20,15 @@
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module '5lntuoy2.dll'
// MVID: {85A70DA1-7D32-4ED1-89B1-4720D974D969}
.module frnxitb5.dll
// MVID: {DC6B1392-D29F-44A5-97D6-0C026DCD3A00}
.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x10000000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// Image base: 0x05730000
// Image base: 0x00D50000
// =============== CLASS MEMBERS DECLARATION ===================
@ -474,6 +474,42 @@ @@ -474,6 +474,42 @@
IL_0008: ret
} // end of method TypeAnalysisTests::EnumDiff
.method public hidebysig instance bool
CompareDelegatesByValue(class [mscorlib]System.Action a,
class [mscorlib]System.Action b) cil managed
{
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.1
IL_0001: ldarg.2
IL_0002: call bool [mscorlib]System.Delegate::op_Equality(class [mscorlib]System.Delegate,
class [mscorlib]System.Delegate)
IL_0007: ret
} // end of method TypeAnalysisTests::CompareDelegatesByValue
.method public hidebysig instance bool
CompareDelegatesByReference(class [mscorlib]System.Action a,
class [mscorlib]System.Action b) cil managed
{
// Code size 5 (0x5)
.maxstack 8
IL_0000: ldarg.1
IL_0001: ldarg.2
IL_0002: ceq
IL_0004: ret
} // end of method TypeAnalysisTests::CompareDelegatesByReference
.method public hidebysig instance bool
CompareDelegateWithNull(class [mscorlib]System.Action a) cil managed
{
// Code size 5 (0x5)
.maxstack 8
IL_0000: ldarg.1
IL_0001: ldnull
IL_0002: ceq
IL_0004: ret
} // end of method TypeAnalysisTests::CompareDelegateWithNull
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{

42
ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.opt.roslyn.il

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.18020
// Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0
// Copyright (c) Microsoft Corporation. All rights reserved.
@ -25,14 +25,14 @@ @@ -25,14 +25,14 @@
.ver 0:0:0:0
}
.module TypeAnalysisTests.dll
// MVID: {60E6985E-8E86-4693-9F02-AD3FF7341588}
// MVID: {B6659F2C-8CAA-447B-9D1D-D59D8C40C6D7}
.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x10000000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// Image base: 0x02440000
// Image base: 0x003E0000
// =============== CLASS MEMBERS DECLARATION ===================
@ -476,6 +476,42 @@ @@ -476,6 +476,42 @@
IL_0008: ret
} // end of method TypeAnalysisTests::EnumDiff
.method public hidebysig instance bool
CompareDelegatesByValue(class [mscorlib]System.Action a,
class [mscorlib]System.Action b) cil managed
{
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.1
IL_0001: ldarg.2
IL_0002: call bool [mscorlib]System.Delegate::op_Equality(class [mscorlib]System.Delegate,
class [mscorlib]System.Delegate)
IL_0007: ret
} // end of method TypeAnalysisTests::CompareDelegatesByValue
.method public hidebysig instance bool
CompareDelegatesByReference(class [mscorlib]System.Action a,
class [mscorlib]System.Action b) cil managed
{
// Code size 5 (0x5)
.maxstack 8
IL_0000: ldarg.1
IL_0001: ldarg.2
IL_0002: ceq
IL_0004: ret
} // end of method TypeAnalysisTests::CompareDelegatesByReference
.method public hidebysig instance bool
CompareDelegateWithNull(class [mscorlib]System.Action a) cil managed
{
// Code size 5 (0x5)
.maxstack 8
IL_0000: ldarg.1
IL_0001: ldnull
IL_0002: ceq
IL_0004: ret
} // end of method TypeAnalysisTests::CompareDelegateWithNull
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{

60
ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.roslyn.il

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.18020
// Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0
// Copyright (c) Microsoft Corporation. All rights reserved.
@ -25,14 +25,14 @@ @@ -25,14 +25,14 @@
.ver 0:0:0:0
}
.module TypeAnalysisTests.dll
// MVID: {389E1347-98C0-49C1-B484-EDF5FFA1CD16}
// MVID: {E25A3961-A07D-4220-AB6C-FE5B1624514C}
.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x10000000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// Image base: 0x027F0000
// Image base: 0x00D80000
// =============== CLASS MEMBERS DECLARATION ===================
@ -680,6 +680,60 @@ @@ -680,6 +680,60 @@
IL_000d: ret
} // end of method TypeAnalysisTests::EnumDiff
.method public hidebysig instance bool
CompareDelegatesByValue(class [mscorlib]System.Action a,
class [mscorlib]System.Action b) cil managed
{
// Code size 13 (0xd)
.maxstack 2
.locals init (bool V_0)
IL_0000: nop
IL_0001: ldarg.1
IL_0002: ldarg.2
IL_0003: call bool [mscorlib]System.Delegate::op_Equality(class [mscorlib]System.Delegate,
class [mscorlib]System.Delegate)
IL_0008: stloc.0
IL_0009: br.s IL_000b
IL_000b: ldloc.0
IL_000c: ret
} // end of method TypeAnalysisTests::CompareDelegatesByValue
.method public hidebysig instance bool
CompareDelegatesByReference(class [mscorlib]System.Action a,
class [mscorlib]System.Action b) cil managed
{
// Code size 10 (0xa)
.maxstack 2
.locals init (bool V_0)
IL_0000: nop
IL_0001: ldarg.1
IL_0002: ldarg.2
IL_0003: ceq
IL_0005: stloc.0
IL_0006: br.s IL_0008
IL_0008: ldloc.0
IL_0009: ret
} // end of method TypeAnalysisTests::CompareDelegatesByReference
.method public hidebysig instance bool
CompareDelegateWithNull(class [mscorlib]System.Action a) cil managed
{
// Code size 10 (0xa)
.maxstack 2
.locals init (bool V_0)
IL_0000: nop
IL_0001: ldarg.1
IL_0002: ldnull
IL_0003: ceq
IL_0005: stloc.0
IL_0006: br.s IL_0008
IL_0008: ldloc.0
IL_0009: ret
} // end of method TypeAnalysisTests::CompareDelegateWithNull
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{

2
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Using.cs

@ -58,7 +58,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -58,7 +58,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public void SimpleUsingExpressionStatementWithDeclaration()
{
using (MemoryStream memoryStream = new MemoryStream()) {
memoryStream.WriteByte((byte)42);
memoryStream.WriteByte(42);
Console.WriteLine("using-body: " + memoryStream.Position);
}
}

14
ICSharpCode.Decompiler/CSharp/Annotations.cs

@ -164,4 +164,18 @@ namespace ICSharpCode.Decompiler.CSharp @@ -164,4 +164,18 @@ namespace ICSharpCode.Decompiler.CSharp
this.Variable = v;
}
}
public class ForeachAnnotation
{
public readonly ILInstruction GetEnumeratorCall;
public readonly ILInstruction MoveNextCall;
public readonly ILInstruction GetCurrentCall;
public ForeachAnnotation(ILInstruction getEnumeratorCall, ILInstruction moveNextCall, ILInstruction getCurrentCall)
{
GetEnumeratorCall = getEnumeratorCall;
MoveNextCall = moveNextCall;
GetCurrentCall = getCurrentCall;
}
}
}

31
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -30,6 +30,7 @@ using ICSharpCode.Decompiler.IL; @@ -30,6 +30,7 @@ using ICSharpCode.Decompiler.IL;
using ICSharpCode.Decompiler.IL.ControlFlow;
using ICSharpCode.Decompiler.IL.Transforms;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.CSharp
{
@ -71,6 +72,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -71,6 +72,7 @@ namespace ICSharpCode.Decompiler.CSharp
new SplitVariables(),
new ILInlining(),
new DetectPinnedRegions(), // must run after inlining but before non-critical control flow transforms
new InlineReturnTransform(),
new YieldReturnDecompiler(), // must run after inlining but before loop detection
new AsyncAwaitDecompiler(), // must run after inlining but before loop detection
new DetectCatchWhenConditionBlocks(), // must run after inlining but before loop detection
@ -81,6 +83,9 @@ namespace ICSharpCode.Decompiler.CSharp @@ -81,6 +83,9 @@ namespace ICSharpCode.Decompiler.CSharp
new RemoveDeadVariableInit(),
new SplitVariables(), // split variables once again, because the stobj(ldloca V, ...) may open up new replacements
new SwitchDetection(),
new SwitchOnStringTransform(),
new SwitchOnNullableTransform(),
new SplitVariables(), // split variables once again, because SwitchOnNullableTransform eliminates ldloca
new BlockILTransform { // per-block transforms
PostOrderTransforms = {
// Even though it's a post-order block-transform as most other transforms,
@ -121,8 +126,9 @@ namespace ICSharpCode.Decompiler.CSharp @@ -121,8 +126,9 @@ namespace ICSharpCode.Decompiler.CSharp
)
}
},
new ProxyCallReplacer(),
new DelegateConstruction(),
new AssignVariableNames()
new AssignVariableNames(),
};
}
@ -139,7 +145,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -139,7 +145,7 @@ namespace ICSharpCode.Decompiler.CSharp
new IntroduceExtensionMethods(), // must run after IntroduceUsingDeclarations
new IntroduceQueryExpressions(), // must run after IntroduceExtensionMethods
new CombineQueryExpressions(),
//new FlattenSwitchBlocks(),
new FlattenSwitchBlocks(),
new FixNameCollisions(),
new AddXmlDocumentationTransform(),
};
@ -196,8 +202,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -196,8 +202,8 @@ namespace ICSharpCode.Decompiler.CSharp
if (settings.AsyncAwait && AsyncAwaitDecompiler.IsCompilerGeneratedStateMachine(type))
return true;
} else if (type.IsCompilerGenerated()) {
// if (type.Name.StartsWith("<PrivateImplementationDetails>", StringComparison.Ordinal))
// return true;
if (type.Name.StartsWith("<PrivateImplementationDetails>", StringComparison.Ordinal))
return true;
if (settings.AnonymousTypes && type.IsAnonymousType())
return true;
}
@ -210,8 +216,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -210,8 +216,8 @@ namespace ICSharpCode.Decompiler.CSharp
return true;
if (settings.AutomaticProperties && IsAutomaticPropertyBackingField(field))
return true;
// if (settings.SwitchStatementOnString && IsSwitchOnStringCache(field))
// return true;
if (settings.SwitchStatementOnString && IsSwitchOnStringCache(field))
return true;
}
// event-fields are not [CompilerGenerated]
if (settings.AutomaticEvents && field.DeclaringType.Events.Any(ev => ev.Name == field.Name))
@ -977,6 +983,19 @@ namespace ICSharpCode.Decompiler.CSharp @@ -977,6 +983,19 @@ namespace ICSharpCode.Decompiler.CSharp
}
#endregion
#region Sequence Points
/// <summary>
/// Creates sequence points for the given syntax tree.
///
/// This only works correctly when the nodes in the syntax tree have line/column information.
/// </summary>
public Dictionary<ILFunction, List<SequencePoint>> CreateSequencePoints(SyntaxTree syntaxTree)
{
SequencePointBuilder spb = new SequencePointBuilder();
syntaxTree.AcceptVisitor(spb);
return spb.GetSequencePoints();
}
#endregion
}
[Flags]

114
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

@ -22,16 +22,11 @@ using System.Diagnostics; @@ -22,16 +22,11 @@ using System.Diagnostics;
using System.Linq;
using ICSharpCode.Decompiler.CSharp.Resolver;
using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.CSharp.Transforms;
using ICSharpCode.Decompiler.CSharp.TypeSystem;
using ICSharpCode.Decompiler.IL;
using ICSharpCode.Decompiler.Semantics;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
using ICSharpCode.Decompiler.Util;
using ExpressionType = System.Linq.Expressions.ExpressionType;
using PrimitiveType = ICSharpCode.Decompiler.CSharp.Syntax.PrimitiveType;
using System.Threading;
namespace ICSharpCode.Decompiler.CSharp
{
@ -140,7 +135,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -140,7 +135,7 @@ namespace ICSharpCode.Decompiler.CSharp
atce.Initializers.Add(
new NamedExpression {
Name = expectedParameters[i].Name,
Expression = argumentExpressions[i]
Expression = arguments[i].ConvertTo(expectedParameters[i].Type, expressionBuilder)
});
}
}
@ -148,10 +143,16 @@ namespace ICSharpCode.Decompiler.CSharp @@ -148,10 +143,16 @@ namespace ICSharpCode.Decompiler.CSharp
.WithILInstruction(inst)
.WithRR(rr);
} else {
if (IsUnambiguousCall(inst, target, method, Array.Empty<IType>(), arguments) != OverloadResolutionErrors.None) {
for (int i = 0; i < arguments.Count; i++) {
if (!settings.AnonymousTypes || !expectedParameters[i].Type.ContainsAnonymousType())
if (settings.AnonymousTypes && expectedParameters[i].Type.ContainsAnonymousType()) {
if (arguments[i].Expression is LambdaExpression lambda) {
ModifyReturnTypeOfLambda(lambda);
}
} else {
arguments[i] = arguments[i].ConvertTo(expectedParameters[i].Type, expressionBuilder);
}
}
}
return new ObjectCreateExpression(expressionBuilder.ConvertType(inst.Method.DeclaringType), arguments.SelectArray(arg => arg.Expression))
@ -163,6 +164,9 @@ namespace ICSharpCode.Decompiler.CSharp @@ -163,6 +164,9 @@ namespace ICSharpCode.Decompiler.CSharp
return HandleAccessorCall(inst, target, method, arguments.ToList());
} else if (method.Name == "Invoke" && method.DeclaringType.Kind == TypeKind.Delegate) {
return new InvocationExpression(target, arguments.Select(arg => arg.Expression)).WithILInstruction(inst).WithRR(rr);
} else if (IsDelegateEqualityComparison(method, arguments)) {
return HandleDelegateEqualityComparison(method, arguments)
.WithILInstruction(inst).WithRR(rr);
} else {
bool requireTypeArguments = false;
bool targetCasted = false;
@ -182,8 +186,13 @@ namespace ICSharpCode.Decompiler.CSharp @@ -182,8 +186,13 @@ namespace ICSharpCode.Decompiler.CSharp
if (!argumentsCasted) {
argumentsCasted = true;
for (int i = 0; i < arguments.Count; i++) {
if (!settings.AnonymousTypes || !expectedParameters[i].Type.ContainsAnonymousType())
if (settings.AnonymousTypes && expectedParameters[i].Type.ContainsAnonymousType()) {
if (arguments[i].Expression is LambdaExpression lambda) {
ModifyReturnTypeOfLambda(lambda);
}
} else {
arguments[i] = arguments[i].ConvertTo(expectedParameters[i].Type, expressionBuilder);
}
}
} else if (!targetCasted) {
targetCasted = true;
@ -215,6 +224,51 @@ namespace ICSharpCode.Decompiler.CSharp @@ -215,6 +224,51 @@ namespace ICSharpCode.Decompiler.CSharp
}
}
private void ModifyReturnTypeOfLambda(LambdaExpression lambda)
{
var resolveResult = (DecompiledLambdaResolveResult)lambda.GetResolveResult();
if (lambda.Body is Expression exprBody)
lambda.Body = new TranslatedExpression(exprBody.Detach()).ConvertTo(resolveResult.ReturnType, expressionBuilder);
else
ModifyReturnStatementInsideLambda(resolveResult.ReturnType, lambda);
resolveResult.InferredReturnType = resolveResult.ReturnType;
}
private void ModifyReturnStatementInsideLambda(IType returnType, AstNode parent)
{
foreach (var child in parent.Children) {
if (child is LambdaExpression || child is AnonymousMethodExpression)
continue;
if (child is ReturnStatement ret) {
ret.Expression = new TranslatedExpression(ret.Expression.Detach()).ConvertTo(returnType, expressionBuilder);
continue;
}
ModifyReturnStatementInsideLambda(returnType, child);
}
}
private bool IsDelegateEqualityComparison(IMethod method, IList<TranslatedExpression> arguments)
{
// Comparison on a delegate type is a C# builtin operator
// that compiles down to a Delegate.op_Equality call.
// We handle this as a special case to avoid inserting a cast to System.Delegate.
return method.IsOperator
&& method.DeclaringType.IsKnownType(KnownTypeCode.Delegate)
&& (method.Name == "op_Equality" || method.Name == "op_Inequality")
&& arguments.Count == 2
&& arguments[0].Type.Kind == TypeKind.Delegate
&& arguments[1].Type.Equals(arguments[0].Type);
}
private Expression HandleDelegateEqualityComparison(IMethod method, IList<TranslatedExpression> arguments)
{
return new BinaryOperatorExpression(
arguments[0],
method.Name == "op_Equality" ? BinaryOperatorType.Equality : BinaryOperatorType.InEquality,
arguments[1]
);
}
OverloadResolutionErrors IsUnambiguousCall(ILInstruction inst, TranslatedExpression target, IMethod method, IType[] typeArguments, IList<TranslatedExpression> arguments)
{
var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentAssembly);
@ -321,39 +375,47 @@ namespace ICSharpCode.Decompiler.CSharp @@ -321,39 +375,47 @@ namespace ICSharpCode.Decompiler.CSharp
case OpCode.LdVirtFtn:
method = ((LdVirtFtn)func).Method;
break;
case OpCode.ILFunction:
method = ((ILFunction)func).Method;
return expressionBuilder.TranslateFunction(inst.Method.DeclaringType, (ILFunction)func)
.WithILInstruction(inst);
default:
method = (IMethod)typeSystem.Resolve(((ILFunction)func).Method);
break;
throw new ArgumentException($"Unknown instruction type: {func.OpCode}");
}
var invokeMethod = inst.Method.DeclaringType.GetDelegateInvokeMethod();
TranslatedExpression target;
IType targetType;
if (method.IsExtensionMethod && invokeMethod != null && method.Parameters.Count - 1 == invokeMethod.Parameters.Count) {
target = expressionBuilder.Translate(inst.Arguments[0]);
targetType = method.Parameters[0].Type;
} else {
target = expressionBuilder.TranslateTarget(method, inst.Arguments[0], func.OpCode == OpCode.LdFtn);
targetType = method.DeclaringType;
}
var target = expressionBuilder.TranslateTarget(method, inst.Arguments[0], func.OpCode == OpCode.LdFtn);
var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentAssembly);
var or = new OverloadResolution(resolver.Compilation, method.Parameters.SelectArray(p => new TypeResolveResult(p.Type)));
var result = lookup.Lookup(target.ResolveResult, method.Name, method.TypeArguments, true) as MethodGroupResolveResult;
var result = lookup.Lookup(target.ResolveResult, method.Name, method.TypeArguments, false);
if (result == null) {
target = target.ConvertTo(method.DeclaringType, expressionBuilder);
} else {
or.AddMethodLists(result.MethodsGroupedByDeclaringType.ToArray());
if (or.BestCandidateErrors != OverloadResolutionErrors.None || !IsAppropriateCallTarget(method, or.BestCandidate, func.OpCode == OpCode.LdVirtFtn))
target = target.ConvertTo(method.DeclaringType, expressionBuilder);
bool needsCast = true;
if (result is MethodGroupResolveResult mgrr) {
or.AddMethodLists(mgrr.MethodsGroupedByDeclaringType.ToArray());
needsCast = (or.BestCandidateErrors != OverloadResolutionErrors.None || !IsAppropriateCallTarget(method, or.BestCandidate, func.OpCode == OpCode.LdVirtFtn));
}
if (needsCast) {
target = target.ConvertTo(targetType, expressionBuilder);
result = lookup.Lookup(target.ResolveResult, method.Name, method.TypeArguments, false);
}
var mre = new MemberReferenceExpression(target, method.Name);
mre.TypeArguments.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType));
mre.WithRR(result);
var oce = new ObjectCreateExpression(expressionBuilder.ConvertType(inst.Method.DeclaringType), mre)
// .WithAnnotation(new DelegateConstruction.Annotation(func.OpCode == OpCode.LdVirtFtn, target, method.Name))
.WithILInstruction(inst)
.WithRR(new ConversionResolveResult(
inst.Method.DeclaringType,
new MemberResolveResult(target.ResolveResult, method),
// TODO handle extension methods capturing the first argument
Conversion.MethodGroupConversion(method, func.OpCode == OpCode.LdVirtFtn, false)));
if (func is ILFunction) {
return expressionBuilder.TranslateFunction(oce, target, (ILFunction)func);
} else {
return oce;
}
return oce;
}
}
}

162
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -69,7 +69,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -69,7 +69,7 @@ namespace ICSharpCode.Decompiler.CSharp
internal readonly ICompilation compilation;
internal readonly CSharpResolver resolver;
readonly TypeSystemAstBuilder astBuilder;
readonly DecompilerSettings settings;
internal readonly DecompilerSettings settings;
readonly CancellationToken cancellationToken;
public ExpressionBuilder(IDecompilerTypeSystem typeSystem, ITypeResolveContext decompilationContext, DecompilerSettings settings, CancellationToken cancellationToken)
@ -93,19 +93,21 @@ namespace ICSharpCode.Decompiler.CSharp @@ -93,19 +93,21 @@ namespace ICSharpCode.Decompiler.CSharp
return astType;
}
public ExpressionWithResolveResult ConvertConstantValue(ResolveResult rr)
public ExpressionWithResolveResult ConvertConstantValue(ResolveResult rr, bool allowImplicitConversion = false)
{
var expr = astBuilder.ConvertConstantValue(rr);
if (expr is NullReferenceExpression && rr.Type.Kind != TypeKind.Null) {
expr = new CastExpression(ConvertType(rr.Type), expr);
} else {
switch (rr.Type.GetDefinition()?.KnownTypeCode) {
case KnownTypeCode.SByte:
case KnownTypeCode.Byte:
case KnownTypeCode.Int16:
case KnownTypeCode.UInt16:
expr = new CastExpression(new PrimitiveType(KnownTypeReference.GetCSharpNameByTypeCode(rr.Type.GetDefinition().KnownTypeCode)), expr);
break;
if (!allowImplicitConversion) {
if (expr is NullReferenceExpression && rr.Type.Kind != TypeKind.Null) {
expr = new CastExpression(ConvertType(rr.Type), expr);
} else {
switch (rr.Type.GetDefinition()?.KnownTypeCode) {
case KnownTypeCode.SByte:
case KnownTypeCode.Byte:
case KnownTypeCode.Int16:
case KnownTypeCode.UInt16:
expr = new CastExpression(new PrimitiveType(KnownTypeReference.GetCSharpNameByTypeCode(rr.Type.GetDefinition().KnownTypeCode)), expr);
break;
}
}
}
var exprRR = expr.Annotation<ResolveResult>();
@ -310,12 +312,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -310,12 +312,12 @@ namespace ICSharpCode.Decompiler.CSharp
.WithILInstruction(inst)
.WithRR(new ConstantResolveResult(compilation.FindType(KnownTypeCode.String), inst.Value));
}
protected internal override TranslatedExpression VisitLdNull(LdNull inst, TranslationContext context)
{
return GetDefaultValueExpression(SpecialType.NullType).WithILInstruction(inst);
}
protected internal override TranslatedExpression VisitDefaultValue(DefaultValue inst, TranslationContext context)
{
return GetDefaultValueExpression(inst.Type).WithILInstruction(inst);
@ -323,8 +325,15 @@ namespace ICSharpCode.Decompiler.CSharp @@ -323,8 +325,15 @@ namespace ICSharpCode.Decompiler.CSharp
internal ExpressionWithResolveResult GetDefaultValueExpression(IType type)
{
var expr = type.IsReferenceType == true ? (Expression)new NullReferenceExpression() : new DefaultValueExpression(ConvertType(type));
var constantType = type.IsReferenceType == true ? SpecialType.NullType : type;
Expression expr;
IType constantType;
if (type.IsReferenceType == true || type.IsKnownType(KnownTypeCode.NullableOfT)) {
expr = new NullReferenceExpression();
constantType = SpecialType.NullType;
} else {
expr = new DefaultValueExpression(ConvertType(type));
constantType = type;
}
return expr.WithRR(new ConstantResolveResult(constantType, null));
}
@ -490,6 +499,19 @@ namespace ICSharpCode.Decompiler.CSharp @@ -490,6 +499,19 @@ namespace ICSharpCode.Decompiler.CSharp
left = AdjustConstantExpressionToType(left, right.Type);
right = AdjustConstantExpressionToType(right, left.Type);
if (left.Type.Kind == TypeKind.Delegate && right.Type.Kind == TypeKind.Null
|| left.Type.Kind == TypeKind.Null && right.Type.Kind == TypeKind.Delegate)
{
// When comparing a delegate with null, the C# compiler generates a reference comparison.
negateOutput = false;
return new BinaryOperatorExpression(left.Expression, inst.Kind.ToBinaryOperatorType(), right.Expression)
.WithILInstruction(inst)
.WithRR(new OperatorResolveResult(
compilation.FindType(KnownTypeCode.Boolean),
inst.Kind == ComparisonKind.Equality ? ExpressionType.Equal : ExpressionType.NotEqual,
left.ResolveResult, right.ResolveResult));
}
var rr = resolver.ResolveBinaryOperator(inst.Kind.ToBinaryOperatorType(), left.ResolveResult, right.ResolveResult)
as OperatorResolveResult;
if (rr == null || rr.IsError || rr.UserDefinedOperatorMethod != null
@ -672,6 +694,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -672,6 +694,12 @@ namespace ICSharpCode.Decompiler.CSharp
}
}
if ((op == BinaryOperatorType.BitwiseAnd || op == BinaryOperatorType.BitwiseOr || op == BinaryOperatorType.ExclusiveOr)
&& (left.Type.Kind == TypeKind.Enum || right.Type.Kind == TypeKind.Enum)) {
left = AdjustConstantExpressionToType(left, right.Type);
right = AdjustConstantExpressionToType(right, left.Type);
}
var rr = resolverWithOverflowCheck.ResolveBinaryOperator(op, left.ResolveResult, right.ResolveResult);
if (rr.IsError || NullableType.GetUnderlyingType(rr.Type).GetStackType() != inst.UnderlyingResultType
|| !IsCompatibleWithSign(left.Type, inst.Sign) || !IsCompatibleWithSign(right.Type, inst.Sign))
@ -1024,31 +1052,30 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1024,31 +1052,30 @@ namespace ICSharpCode.Decompiler.CSharp
return new CallBuilder(this, typeSystem, settings).Build(inst);
}
internal TranslatedExpression TranslateFunction(TranslatedExpression objectCreateExpression, TranslatedExpression target, ILFunction function)
internal ExpressionWithResolveResult TranslateFunction(IType delegateType, ILFunction function)
{
var method = typeSystem.Resolve(function.Method)?.MemberDefinition as IMethod;
var method = function.Method.MemberDefinition as IMethod;
Debug.Assert(method != null);
// Create AnonymousMethodExpression and prepare parameters
AnonymousMethodExpression ame = new AnonymousMethodExpression();
ame.IsAsync = function.IsAsync;
ame.Parameters.AddRange(MakeParameters(method, function));
ame.HasParameterList = true;
ame.HasParameterList = ame.Parameters.Count > 0;
StatementBuilder builder = new StatementBuilder(typeSystem.GetSpecializingTypeSystem(new SimpleTypeResolveContext(method)), this.decompilationContext, method, function, settings, cancellationToken);
var body = builder.ConvertAsBlock(function.Body);
bool isLambda = false;
bool isMultiLineLambda = false;
Comment prev = null;
foreach (string warning in function.Warnings) {
body.InsertChildAfter(prev, prev = new Comment(warning), Roles.Comment);
}
// if there is an anonymous type involved, we are forced to use a lambda expression.
bool isLambda = false;
if (ame.Parameters.Any(p => p.Type.IsNull)) {
// if there is an anonymous type involved, we are forced to use a lambda expression.
isLambda = true;
isMultiLineLambda = body.Statements.Count > 1;
} else if (ame.Parameters.All(p => p.ParameterModifier == ParameterModifier.None)) {
// otherwise use lambda only if an expression lambda is possible
isLambda = (body.Statements.Count == 1 && body.Statements.Single() is ReturnStatement);
}
// Remove the parameter list from an AnonymousMethodExpression if the original method had no names,
@ -1064,39 +1091,81 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1064,39 +1091,81 @@ namespace ICSharpCode.Decompiler.CSharp
ame.HasParameterList = false;
}
}
Expression replacement;
IType inferredReturnType;
if (isLambda) {
LambdaExpression lambda = new LambdaExpression();
lambda.IsAsync = ame.IsAsync;
lambda.CopyAnnotationsFrom(ame);
ame.Parameters.MoveTo(lambda.Parameters);
if (isMultiLineLambda) {
lambda.Body = body;
if (body.Statements.Count == 1 && body.Statements.Single() is ReturnStatement returnStmt) {
lambda.Body = returnStmt.Expression.Detach();
inferredReturnType = lambda.Body.GetResolveResult().Type;
} else {
Expression returnExpr = ((ReturnStatement)body.Statements.Single()).Expression;
returnExpr.Remove();
lambda.Body = returnExpr;
lambda.Body = body;
inferredReturnType = InferReturnType(body);
}
replacement = lambda;
} else {
ame.Body = body;
inferredReturnType = InferReturnType(body);
replacement = ame;
}
var expectedType = objectCreateExpression.ResolveResult.Type;
var expectedTypeDefinition = expectedType.GetDefinition();
if (expectedTypeDefinition != null && expectedTypeDefinition.Kind != TypeKind.Delegate) {
var simplifiedDelegateCreation = (ObjectCreateExpression)objectCreateExpression.Expression.Clone();
simplifiedDelegateCreation.Arguments.Clear();
simplifiedDelegateCreation.Arguments.Add(replacement);
replacement = simplifiedDelegateCreation;
} else if (!settings.AnonymousTypes || !expectedType.ContainsAnonymousType()) {
replacement = new CastExpression(ConvertType(expectedType), replacement);
if (ame.IsAsync) {
inferredReturnType = GetTaskType(inferredReturnType);
}
return replacement
.WithILInstruction(function)
.WithRR(objectCreateExpression.ResolveResult);
var rr = new DecompiledLambdaResolveResult(
function, delegateType, inferredReturnType,
hasParameterList: ame.HasParameterList,
isAnonymousMethod: !isLambda,
isImplicitlyTyped: ame.Parameters.Any(p => p.Type.IsNull));
TranslatedExpression translatedLambda = replacement.WithILInstruction(function).WithRR(rr);
return new CastExpression(ConvertType(delegateType), translatedLambda)
.WithRR(new ConversionResolveResult(delegateType, rr, LambdaConversion.Instance));
}
IType InferReturnType(BlockStatement body)
{
var returnExpressions = new List<ResolveResult>();
CollectReturnExpressions(body);
var ti = new TypeInference(compilation, resolver.conversions);
return ti.GetBestCommonType(returnExpressions, out _);
// Failure to infer a return type does not make the lambda invalid,
// so we can ignore the 'success' value
void CollectReturnExpressions(AstNode node)
{
if (node is ReturnStatement ret) {
if (!ret.Expression.IsNull) {
returnExpressions.Add(ret.Expression.GetResolveResult());
}
} else if (node is LambdaExpression || node is AnonymousMethodExpression) {
// do not recurse into nested lambdas
return;
}
foreach (var child in node.Children) {
CollectReturnExpressions(child);
}
}
}
IType GetTaskType(IType resultType)
{
if (resultType.Kind == TypeKind.Unknown)
return SpecialType.UnknownType;
if (resultType.Kind == TypeKind.Void)
return compilation.FindType(KnownTypeCode.Task);
ITypeDefinition def = compilation.FindType(KnownTypeCode.TaskOfT).GetDefinition();
if (def != null)
return new ParameterizedType(def, new[] { resultType });
else
return SpecialType.UnknownType;
}
IEnumerable<ParameterDeclaration> MakeParameters(IMethod method, ILFunction function)
{
var variables = function.Variables.Where(v => v.Kind == VariableKind.Parameter).ToDictionary(v => v.Index);
@ -1384,11 +1453,10 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1384,11 +1453,10 @@ namespace ICSharpCode.Decompiler.CSharp
if (currentPath == null) {
currentPath = info.Path;
} else {
int firstDifferenceIndex = Math.Min(currentPath.Count, info.Path.Count);
int index = 0;
while (index < firstDifferenceIndex && info.Path[index] == currentPath[index])
index++;
firstDifferenceIndex = index;
int minLen = Math.Min(currentPath.Count, info.Path.Count);
int firstDifferenceIndex = 0;
while (firstDifferenceIndex < minLen && info.Path[firstDifferenceIndex] == currentPath[firstDifferenceIndex])
firstDifferenceIndex++;
while (elementsStack.Count - 1 > firstDifferenceIndex) {
var methodElement = currentPath[elementsStack.Count - 1];
var pathElement = currentPath[elementsStack.Count - 2];

14
ICSharpCode.Decompiler/CSharp/NRefactoryExtensions.cs

@ -17,8 +17,10 @@ @@ -17,8 +17,10 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.Linq;
using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching;
using ICSharpCode.Decompiler.IL;
namespace ICSharpCode.Decompiler.CSharp
{
@ -31,7 +33,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -31,7 +33,7 @@ namespace ICSharpCode.Decompiler.CSharp
node.AddAnnotation(annotation);
return node;
}
public static T CopyAnnotationsFrom<T>(this T node, AstNode other) where T : AstNode
{
foreach (object annotation in other.Annotations) {
@ -39,7 +41,15 @@ namespace ICSharpCode.Decompiler.CSharp @@ -39,7 +41,15 @@ namespace ICSharpCode.Decompiler.CSharp
}
return node;
}
public static T CopyInstructionsFrom<T>(this T node, AstNode other) where T : AstNode
{
foreach (object annotation in other.Annotations.OfType<ILInstruction>()) {
node.AddAnnotation(annotation);
}
return node;
}
public static T Detach<T>(this T node) where T : AstNode
{
node.Remove();

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

@ -90,6 +90,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -90,6 +90,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
Space(policy.SpaceBeforeBracketComma);
// TODO: Comma policy has changed.
writer.WriteToken(Roles.Comma, ",");
isAfterSpace = false;
Space(!noSpaceAfterComma && policy.SpaceAfterBracketComma);
// TODO: Comma policy has changed.
}
@ -194,6 +195,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -194,6 +195,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
#region Write tokens
protected bool isAtStartOfLine = true;
protected bool isAfterSpace;
/// <summary>
/// Writes a keyword, and all specials up to
@ -207,18 +209,21 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -207,18 +209,21 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
{
writer.WriteKeyword(tokenRole, token);
isAtStartOfLine = false;
isAfterSpace = false;
}
protected virtual void WriteIdentifier(Identifier identifier)
{
writer.WriteIdentifier(identifier);
isAtStartOfLine = false;
isAfterSpace = false;
}
protected virtual void WriteIdentifier(string identifier)
{
AstType.Create(identifier).AcceptVisitor(this);
isAtStartOfLine = false;
isAfterSpace = false;
}
protected virtual void WriteToken(TokenRole tokenRole)
@ -230,6 +235,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -230,6 +235,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
{
writer.WriteToken(tokenRole, token);
isAtStartOfLine = false;
isAfterSpace = false;
}
protected virtual void LPar()
@ -260,8 +266,9 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -260,8 +266,9 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
/// </summary>
protected virtual void Space(bool addSpace = true)
{
if (addSpace) {
if (addSpace && !isAfterSpace) {
writer.Space();
isAfterSpace = true;
}
}
@ -269,6 +276,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -269,6 +276,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
{
writer.NewLine();
isAtStartOfLine = true;
isAfterSpace = false;
}
protected virtual void OpenBrace(BraceStyle style)
@ -278,7 +286,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -278,7 +286,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
case BraceStyle.EndOfLine:
case BraceStyle.BannerStyle:
if (!isAtStartOfLine)
writer.Space();
Space();
writer.WriteToken(Roles.LBrace, "{");
break;
case BraceStyle.EndOfLineWithoutSpace:
@ -882,6 +890,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -882,6 +890,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
{
StartNode(nullReferenceExpression);
writer.WritePrimitiveValue(null);
isAfterSpace = false;
EndNode(nullReferenceExpression);
}
@ -937,6 +946,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -937,6 +946,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
{
StartNode(primitiveExpression);
writer.WritePrimitiveValue(primitiveExpression.Value, primitiveExpression.UnsafeLiteralValue);
isAfterSpace = false;
EndNode(primitiveExpression);
}
#endregion

12
ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertMissingTokensDecorator.cs

@ -88,7 +88,10 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -88,7 +88,10 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
if (node != null)
node.Location = start;
}
if (t != null) currentList.Add(t);
if (t != null) {
currentList.Add(t);
t.Role = role;
}
base.WriteKeyword(role, keyword);
}
@ -103,13 +106,14 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -103,13 +106,14 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
public override void WritePrimitiveValue(object value, string literalValue = null)
{
Expression node = nodes.Peek().LastOrDefault() as Expression;
var startLocation = locationProvider.Location;
base.WritePrimitiveValue(value, literalValue);
if (node is PrimitiveExpression) {
((PrimitiveExpression)node).SetStartLocation(locationProvider.Location);
((PrimitiveExpression)node).SetLocation(startLocation, locationProvider.Location);
}
if (node is NullReferenceExpression) {
((NullReferenceExpression)node).SetStartLocation(locationProvider.Location);
((NullReferenceExpression)node).SetStartLocation(startLocation);
}
base.WritePrimitiveValue(value, literalValue);
}
public override void WritePrimitiveType(string type)

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

@ -128,7 +128,8 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -128,7 +128,8 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
case CommentType.SingleLine:
textWriter.Write("//");
textWriter.WriteLine(content);
column += 2 + content.Length;
column = 1;
line++;
needsIndent = true;
isAtStartOfLine = true;
break;
@ -144,7 +145,8 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -144,7 +145,8 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
case CommentType.Documentation:
textWriter.Write("///");
textWriter.WriteLine(content);
column += 3 + content.Length;
column = 1;
line++;
needsIndent = true;
isAtStartOfLine = true;
break;

77
ICSharpCode.Decompiler/CSharp/Resolver/LambdaResolveResult.cs

@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using ICSharpCode.Decompiler.Semantics;
using ICSharpCode.Decompiler.TypeSystem;
@ -97,4 +98,80 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -97,4 +98,80 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
return new [] { this.Body };
}
}
sealed class DecompiledLambdaResolveResult : LambdaResolveResult
{
readonly IL.ILFunction function;
public readonly IType DelegateType;
/// <summary>
/// The inferred return type.
/// Can differ from <c>ReturnType</c> if a return statement
/// performs an implicit conversion.
/// </summary>
public IType InferredReturnType;
public DecompiledLambdaResolveResult(IL.ILFunction function,
IType delegateType,
IType inferredReturnType,
bool hasParameterList,
bool isAnonymousMethod,
bool isImplicitlyTyped)
{
this.function = function ?? throw new ArgumentNullException(nameof(function));
this.DelegateType = delegateType ?? throw new ArgumentNullException(nameof(delegateType));
this.InferredReturnType = inferredReturnType ?? throw new ArgumentNullException(nameof(inferredReturnType));
this.HasParameterList = hasParameterList;
this.IsAnonymousMethod = isAnonymousMethod;
this.IsImplicitlyTyped = isImplicitlyTyped;
this.Body = new ResolveResult(SpecialType.UnknownType);
}
public override bool HasParameterList { get; }
public override bool IsAnonymousMethod { get; }
public override bool IsImplicitlyTyped { get; }
public override bool IsAsync => function.IsAsync;
public override IList<IParameter> Parameters => function.Method.Parameters;
public override IType ReturnType => function.Method.ReturnType;
public override ResolveResult Body { get; }
public override IType GetInferredReturnType(IType[] parameterTypes)
{
// We don't know how to compute which type would be inferred if
// given other parameter types.
// Let's hope this is good enough:
return InferredReturnType;
}
public override Conversion IsValid(IType[] parameterTypes, IType returnType, CSharpConversions conversions)
{
if (this.Parameters.Count != parameterTypes.Length)
return Conversion.None;
for (int i = 0; i < parameterTypes.Length; ++i) {
if (!parameterTypes[i].Equals(this.Parameters[i].Type)) {
if (IsImplicitlyTyped) {
// it's possible that different parameter types also lead to a valid conversion
return LambdaConversion.Instance;
} else {
return Conversion.None;
}
}
}
if (returnType.Equals(this.ReturnType)) {
return LambdaConversion.Instance;
} else {
return Conversion.None;
}
}
}
class LambdaConversion : Conversion
{
public static readonly LambdaConversion Instance = new LambdaConversion();
public override bool IsAnonymousFunctionConversion => true;
public override bool IsImplicit => true;
}
}

329
ICSharpCode.Decompiler/CSharp/SequencePointBuilder.cs

@ -0,0 +1,329 @@ @@ -0,0 +1,329 @@
// Copyright (c) 2017 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.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.IL;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.CSharp
{
/// <summary>
/// Given a SyntaxTree that was output from the decompiler, constructs the list of sequence points.
/// </summary>
class SequencePointBuilder : DepthFirstAstVisitor
{
struct StatePerSequencePoint
{
/// <summary>
/// Main AST node associated with this sequence point.
/// </summary>
internal readonly AstNode PrimaryNode;
/// <summary>
/// List of IL intervals that are associated with this sequence point.
/// </summary>
internal readonly List<Interval> Intervals;
/// <summary>
/// The function containing this sequence point.
/// </summary>
internal ILFunction Function;
public StatePerSequencePoint(AstNode primaryNode)
{
this.PrimaryNode = primaryNode;
this.Intervals = new List<Interval>();
this.Function = null;
}
}
readonly List<(ILFunction, SequencePoint)> sequencePoints = new List<(ILFunction, SequencePoint)>();
readonly HashSet<ILInstruction> mappedInstructions = new HashSet<ILInstruction>();
// Stack holding information for outer statements.
readonly Stack<StatePerSequencePoint> outerStates = new Stack<StatePerSequencePoint>();
// Collects information for the current sequence point.
StatePerSequencePoint current;
void VisitAsSequencePoint(AstNode node)
{
if (node.IsNull) return;
StartSequencePoint(node);
node.AcceptVisitor(this);
EndSequencePoint(node.StartLocation, node.EndLocation);
}
protected override void VisitChildren(AstNode node)
{
base.VisitChildren(node);
AddToSequencePoint(node);
}
public override void VisitBlockStatement(BlockStatement blockStatement)
{
foreach (var stmt in blockStatement.Statements) {
VisitAsSequencePoint(stmt);
}
}
public override void VisitForStatement(ForStatement forStatement)
{
// Every element of a for-statement is it's own sequence point.
foreach (var init in forStatement.Initializers) {
VisitAsSequencePoint(init);
}
VisitAsSequencePoint(forStatement.Condition);
foreach (var inc in forStatement.Iterators) {
VisitAsSequencePoint(inc);
}
VisitAsSequencePoint(forStatement.EmbeddedStatement);
}
public override void VisitSwitchStatement(SwitchStatement switchStatement)
{
StartSequencePoint(switchStatement);
switchStatement.Expression.AcceptVisitor(this);
foreach (var section in switchStatement.SwitchSections) {
// note: sections will not contribute to the current sequence point
section.AcceptVisitor(this);
}
// add switch statement itself to sequence point
// (call only after the sections are visited)
AddToSequencePoint(switchStatement);
EndSequencePoint(switchStatement.StartLocation, switchStatement.RParToken.EndLocation);
}
public override void VisitSwitchSection(Syntax.SwitchSection switchSection)
{
// every statement in the switch section is its own sequence point
foreach (var stmt in switchSection.Statements) {
VisitAsSequencePoint(stmt);
}
}
public override void VisitLambdaExpression(LambdaExpression lambdaExpression)
{
AddToSequencePoint(lambdaExpression);
VisitAsSequencePoint(lambdaExpression.Body);
}
public override void VisitUsingStatement(UsingStatement usingStatement)
{
StartSequencePoint(usingStatement);
usingStatement.ResourceAcquisition.AcceptVisitor(this);
VisitAsSequencePoint(usingStatement.EmbeddedStatement);
AddToSequencePoint(usingStatement);
EndSequencePoint(usingStatement.StartLocation, usingStatement.RParToken.EndLocation);
}
public override void VisitForeachStatement(ForeachStatement foreachStatement)
{
var foreachInfo = foreachStatement.Annotation<ForeachAnnotation>();
if (foreachInfo == null) {
base.VisitForeachStatement(foreachStatement);
return;
}
// TODO : Add a sequence point on foreach token (mapped to nop before using instruction).
StartSequencePoint(foreachStatement);
foreachStatement.InExpression.AcceptVisitor(this);
AddToSequencePoint(foreachInfo.GetEnumeratorCall);
EndSequencePoint(foreachStatement.InExpression.StartLocation, foreachStatement.InExpression.EndLocation);
StartSequencePoint(foreachStatement);
AddToSequencePoint(foreachInfo.MoveNextCall);
EndSequencePoint(foreachStatement.InToken.StartLocation, foreachStatement.InToken.EndLocation);
StartSequencePoint(foreachStatement);
AddToSequencePoint(foreachInfo.GetCurrentCall);
EndSequencePoint(foreachStatement.VariableType.StartLocation, foreachStatement.VariableNameToken.EndLocation);
VisitAsSequencePoint(foreachStatement.EmbeddedStatement);
}
public override void VisitLockStatement(LockStatement lockStatement)
{
StartSequencePoint(lockStatement);
lockStatement.Expression.AcceptVisitor(this);
VisitAsSequencePoint(lockStatement.EmbeddedStatement);
AddToSequencePoint(lockStatement);
EndSequencePoint(lockStatement.StartLocation, lockStatement.RParToken.EndLocation);
}
public override void VisitIfElseStatement(IfElseStatement ifElseStatement)
{
StartSequencePoint(ifElseStatement);
ifElseStatement.Condition.AcceptVisitor(this);
VisitAsSequencePoint(ifElseStatement.TrueStatement);
VisitAsSequencePoint(ifElseStatement.FalseStatement);
AddToSequencePoint(ifElseStatement);
EndSequencePoint(ifElseStatement.StartLocation, ifElseStatement.RParToken.EndLocation);
}
public override void VisitWhileStatement(WhileStatement whileStatement)
{
StartSequencePoint(whileStatement);
whileStatement.Condition.AcceptVisitor(this);
VisitAsSequencePoint(whileStatement.EmbeddedStatement);
AddToSequencePoint(whileStatement);
EndSequencePoint(whileStatement.StartLocation, whileStatement.RParToken.EndLocation);
}
public override void VisitDoWhileStatement(DoWhileStatement doWhileStatement)
{
StartSequencePoint(doWhileStatement);
VisitAsSequencePoint(doWhileStatement.EmbeddedStatement);
doWhileStatement.Condition.AcceptVisitor(this);
AddToSequencePoint(doWhileStatement);
EndSequencePoint(doWhileStatement.WhileToken.StartLocation, doWhileStatement.RParToken.EndLocation);
}
public override void VisitFixedStatement(FixedStatement fixedStatement)
{
foreach (var v in fixedStatement.Variables) {
VisitAsSequencePoint(v);
}
VisitAsSequencePoint(fixedStatement.EmbeddedStatement);
}
/// <summary>
/// Start a new C# statement = new sequence point.
/// </summary>
void StartSequencePoint(AstNode primaryNode)
{
outerStates.Push(current);
current = new StatePerSequencePoint(primaryNode);
}
void EndSequencePoint(TextLocation startLocation, TextLocation endLocation)
{
Debug.Assert(!startLocation.IsEmpty, "missing startLocation");
Debug.Assert(!endLocation.IsEmpty, "missing endLocation");
if (current.Intervals.Count > 0 && current.Function != null) {
// use LongSet to deduplicate and merge the intervals
var longSet = new LongSet(current.Intervals.Select(i => new LongInterval(i.Start, i.End)));
Debug.Assert(!longSet.IsEmpty);
sequencePoints.Add((current.Function, new SequencePoint {
Offset = (int)longSet.Intervals[0].Start,
EndOffset = (int)longSet.Intervals[0].End,
StartLine = startLocation.Line,
StartColumn = startLocation.Column,
EndLine = endLocation.Line,
EndColumn = endLocation.Column
}));
}
current = outerStates.Pop();
}
/// <summary>
/// Add the ILAst instruction associated with the AstNode to the sequence point.
/// Also add all its ILAst sub-instructions (unless they were already added to another sequence point).
/// </summary>
void AddToSequencePoint(AstNode node)
{
foreach (var inst in node.Annotations.OfType<ILInstruction>()) {
AddToSequencePoint(inst);
}
}
void AddToSequencePoint(ILInstruction inst)
{
if (!mappedInstructions.Add(inst)) {
// inst was already used by a nested sequence point within this sequence point
return;
}
// Add the IL range associated with this instruction to the current sequence point.
if (HasUsableILRange(inst) && current.Intervals != null) {
current.Intervals.Add(inst.ILRange);
var function = inst.Parent.Ancestors.OfType<ILFunction>().FirstOrDefault();
Debug.Assert(current.Function == null || current.Function == function);
current.Function = function;
}
// Do not add instructions of lambdas/delegates.
if (inst is ILFunction)
return;
// Also add the child IL instructions, unless they were already processed by
// another C# expression.
foreach (var child in inst.Children) {
AddToSequencePoint(child);
}
}
internal static bool HasUsableILRange(ILInstruction inst)
{
if (inst.ILRange.IsEmpty)
return false;
return !(inst is BlockContainer || inst is Block);
}
/// <summary>
/// Called after the visitor is done to return the results.
/// </summary>
internal Dictionary<ILFunction, List<SequencePoint>> GetSequencePoints()
{
var dict = new Dictionary<ILFunction, List<SequencePoint>>();
foreach (var (function, sequencePoint) in this.sequencePoints) {
if (!dict.TryGetValue(function, out var list)) {
dict.Add(function, list = new List<SequencePoint>());
}
list.Add(sequencePoint);
}
foreach (var (function, list) in dict.ToList()) {
// For each function, sort sequence points and fix overlaps+gaps
var newList = new List<SequencePoint>();
int pos = 0;
foreach (var sequencePoint in list.OrderBy(sp => sp.Offset).ThenBy(sp => sp.EndOffset)) {
if (sequencePoint.Offset < pos) {
// overlapping sequence point?
// delete previous sequence points that are after sequencePoint.Offset
while (newList.Count > 0 && newList.Last().EndOffset > pos) {
var last = newList.Last();
if (last.Offset >= sequencePoint.Offset) {
newList.RemoveAt(newList.Count - 1);
} else {
last.EndOffset = sequencePoint.Offset;
newList[newList.Count - 1] = last;
}
}
} else if (sequencePoint.Offset > pos) {
// insert hidden sequence point in the gap.
var hidden = new SequencePoint();
hidden.Offset = pos;
hidden.EndOffset = sequencePoint.Offset;
hidden.SetHidden();
newList.Add(hidden);
}
newList.Add(sequencePoint);
pos = sequencePoint.EndOffset;
}
if (pos < function.CecilMethod.Body.CodeSize) {
var hidden = new SequencePoint();
hidden.Offset = pos;
hidden.EndOffset = function.CecilMethod.Body.CodeSize;
hidden.SetHidden();
newList.Add(hidden);
}
dict[function] = newList;
}
return dict;
}
}
}

164
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -83,42 +83,134 @@ namespace ICSharpCode.Decompiler.CSharp @@ -83,42 +83,134 @@ namespace ICSharpCode.Decompiler.CSharp
return new IfElseStatement(condition, trueStatement, falseStatement);
}
CaseLabel CreateTypedCaseLabel(long i, IType type)
ConstantResolveResult CreateTypedCaseLabel(long i, IType type, string[] map = null)
{
object value;
// unpack nullable type, if necessary:
// we need to do this in all cases, because there are nullable bools and enum types as well.
type = NullableType.GetUnderlyingType(type);
if (type.IsKnownType(KnownTypeCode.Boolean)) {
value = i != 0;
} else if (type.IsKnownType(KnownTypeCode.String) && map != null) {
value = map[i];
} else if (type.Kind == TypeKind.Enum) {
var enumType = type.GetDefinition().EnumUnderlyingType;
value = CSharpPrimitiveCast.Cast(ReflectionHelper.GetTypeCode(enumType), i, false);
} else {
value = CSharpPrimitiveCast.Cast(ReflectionHelper.GetTypeCode(type), i, false);
}
return new CaseLabel(exprBuilder.ConvertConstantValue(new ConstantResolveResult(type, value)));
return new ConstantResolveResult(type, value);
}
protected internal override Statement VisitSwitchInstruction(SwitchInstruction inst)
{
return TranslateSwitch(null, inst);
}
SwitchStatement TranslateSwitch(BlockContainer switchContainer, SwitchInstruction inst)
{
Debug.Assert(switchContainer.EntryPoint.IncomingEdgeCount == 1);
var oldBreakTarget = breakTarget;
breakTarget = null; // 'break' within a switch would only leave the switch
var value = exprBuilder.Translate(inst.Value);
var stmt = new SwitchStatement() { Expression = value };
breakTarget = switchContainer; // 'break' within a switch would only leave the switch
var oldCaseLabelMapping = caseLabelMapping;
caseLabelMapping = new Dictionary<Block, ConstantResolveResult>();
TranslatedExpression value;
var strToInt = inst.Value as StringToInt;
if (strToInt != null) {
value = exprBuilder.Translate(strToInt.Argument);
} else {
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) {
var astSection = new Syntax.SwitchSection();
astSection.CaseLabels.AddRange(section.Labels.Values.Select(i => CreateTypedCaseLabel(i, value.Type)));
ConvertSwitchSectionBody(astSection, section.Body);
stmt.SwitchSections.Add(astSection);
if (section.Labels.Count() > defaultSection.Labels.Count()) {
defaultSection = section;
}
}
if (inst.DefaultBody.OpCode != OpCode.Nop) {
var stmt = new SwitchStatement() { Expression = value };
Dictionary<IL.SwitchSection, Syntax.SwitchSection> translationDictionary = new Dictionary<IL.SwitchSection, Syntax.SwitchSection>();
// initialize C# switch sections.
foreach (var section in inst.Sections) {
// This is used in the block-label mapping.
ConstantResolveResult firstValueResolveResult;
var astSection = new Syntax.SwitchSection();
astSection.CaseLabels.Add(new CaseLabel());
ConvertSwitchSectionBody(astSection, inst.DefaultBody);
// Create case labels:
if (section == defaultSection) {
astSection.CaseLabels.Add(new CaseLabel());
firstValueResolveResult = null;
} else {
var values = section.Labels.Values.Select(i => CreateTypedCaseLabel(i, value.Type, strToInt?.Map)).ToArray();
if (section.HasNullLabel) {
astSection.CaseLabels.Add(new CaseLabel(new NullReferenceExpression()));
firstValueResolveResult = new ConstantResolveResult(SpecialType.NullType, null);
} else {
Debug.Assert(values.Length > 0);
firstValueResolveResult = values[0];
}
astSection.CaseLabels.AddRange(values.Select(label => new CaseLabel(exprBuilder.ConvertConstantValue(label, allowImplicitConversion: true))));
}
switch (section.Body) {
case Branch br:
// we can only inline the block, if all branches are in the switchContainer.
if (br.TargetBlock.Parent == switchContainer && switchContainer.Descendants.OfType<Branch>().Where(b => b.TargetBlock == br.TargetBlock).All(b => BlockContainer.FindClosestSwitchContainer(b) == switchContainer))
caseLabelMapping.Add(br.TargetBlock, firstValueResolveResult);
break;
default:
break;
}
translationDictionary.Add(section, astSection);
stmt.SwitchSections.Add(astSection);
}
foreach (var section in inst.Sections) {
var astSection = translationDictionary[section];
switch (section.Body) {
case Branch br:
// we can only inline the block, if all branches are in the switchContainer.
if (br.TargetBlock.Parent == switchContainer && switchContainer.Descendants.OfType<Branch>().Where(b => b.TargetBlock == br.TargetBlock).All(b => BlockContainer.FindClosestSwitchContainer(b) == switchContainer))
ConvertSwitchSectionBody(astSection, br.TargetBlock);
else
ConvertSwitchSectionBody(astSection, section.Body);
break;
case Leave leave:
if (astSection.CaseLabels.Count == 1 && astSection.CaseLabels.First().Expression.IsNull && leave.TargetContainer == switchContainer) {
stmt.SwitchSections.Remove(astSection);
break;
}
goto default;
default:
ConvertSwitchSectionBody(astSection, section.Body);
break;
}
}
if (switchContainer != null) {
// Translate any remaining blocks:
var lastSectionStatements = stmt.SwitchSections.Last().Statements;
foreach (var block in switchContainer.Blocks.Skip(1)) {
if (caseLabelMapping.ContainsKey(block)) continue;
lastSectionStatements.Add(new LabelStatement { Label = block.Label });
foreach (var nestedInst in block.Instructions) {
var nestedStmt = Convert(nestedInst);
if (nestedStmt is BlockStatement b) {
foreach (var nested in b.Statements)
lastSectionStatements.Add(nested.Detach());
} else {
lastSectionStatements.Add(nestedStmt);
}
}
Debug.Assert(block.FinalInstruction.OpCode == OpCode.Nop);
}
if (endContainerLabels.TryGetValue(switchContainer, out string label)) {
lastSectionStatements.Add(new LabelStatement { Label = label });
lastSectionStatements.Add(new BreakStatement());
}
}
breakTarget = oldBreakTarget;
caseLabelMapping = oldCaseLabelMapping;
return stmt;
}
@ -141,6 +233,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -141,6 +233,8 @@ namespace ICSharpCode.Decompiler.CSharp
Block continueTarget;
/// <summary>Number of ContinueStatements that were created for the current continueTarget</summary>
int continueCount;
/// <summary>Maps blocks to cases.</summary>
Dictionary<Block, ConstantResolveResult> caseLabelMapping;
protected internal override Statement VisitBranch(Branch inst)
{
@ -148,6 +242,11 @@ namespace ICSharpCode.Decompiler.CSharp @@ -148,6 +242,11 @@ namespace ICSharpCode.Decompiler.CSharp
continueCount++;
return new ContinueStatement();
}
if (caseLabelMapping != null && caseLabelMapping.TryGetValue(inst.TargetBlock, out var label)) {
if (label == null)
return new GotoDefaultStatement();
return new GotoCaseStatement() { LabelExpression = exprBuilder.ConvertConstantValue(label, allowImplicitConversion: true) };
}
return new GotoStatement(inst.TargetLabel);
}
@ -407,6 +506,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -407,6 +506,7 @@ namespace ICSharpCode.Decompiler.CSharp
};
// Add the variable annotation for highlighting (TokenTextWriter expects it directly on the ForeachStatement).
foreachStmt.AddAnnotation(new ILVariableResolveResult(foreachVariable, foreachVariable.Type));
foreachStmt.AddAnnotation(new ForeachAnnotation(inst.ResourceExpression, loop.Conditions.Single(), singleGetter));
// If there was an optional return statement, return it as well.
if (optionalReturnAfterLoop != null) {
return new BlockStatement {
@ -608,8 +708,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -608,8 +708,12 @@ namespace ICSharpCode.Decompiler.CSharp
continueCount = oldContinueCount;
breakTarget = oldBreakTarget;
return loop;
} else if (container.EntryPoint.Instructions.Count == 1 && container.EntryPoint.Instructions[0] is SwitchInstruction switchInst) {
return TranslateSwitch(container, switchInst);
} else {
return ConvertBlockContainer(container, false);
var blockStmt = ConvertBlockContainer(container, false);
blockStmt.AddAnnotation(container);
return blockStmt;
}
}
@ -757,5 +861,33 @@ namespace ICSharpCode.Decompiler.CSharp @@ -757,5 +861,33 @@ namespace ICSharpCode.Decompiler.CSharp
return block.ChildIndex == container.Blocks.Count - 1
&& container == leave.TargetContainer;
}
protected internal override Statement VisitInitblk(Initblk inst)
{
var stmt = new ExpressionStatement(new InvocationExpression {
Target = new IdentifierExpression("memset"),
Arguments = {
exprBuilder.Translate(inst.Address),
exprBuilder.Translate(inst.Value),
exprBuilder.Translate(inst.Size)
}
});
stmt.AddChild(new Comment(" IL initblk instruction"), Roles.Comment);
return stmt;
}
protected internal override Statement VisitCpblk(Cpblk inst)
{
var stmt = new ExpressionStatement(new InvocationExpression {
Target = new IdentifierExpression("memcpy"),
Arguments = {
exprBuilder.Translate(inst.DestAddress),
exprBuilder.Translate(inst.SourceAddress),
exprBuilder.Translate(inst.Size)
}
});
stmt.AddChild(new Comment(" IL cpblk instruction"), Roles.Comment);
return stmt;
}
}
}

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

@ -43,11 +43,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -43,11 +43,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
}
}
internal void SetStartLocation(TextLocation value)
internal void SetLocation(TextLocation startLocation, TextLocation endLocation)
{
ThrowIfFrozen();
this.startLocation = value;
this.endLocation = null;
this.startLocation = startLocation;
this.endLocation = endLocation;
}
string literalValue;

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

@ -188,7 +188,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -188,7 +188,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
#endregion
#region FindInsertionPoints
List<(InsertionPoint InsertionPoint, BlockContainer Loop)> loopTracking = new List<(InsertionPoint, BlockContainer)>();
List<(InsertionPoint InsertionPoint, BlockContainer Scope)> scopeTracking = new List<(InsertionPoint, BlockContainer)>();
/// <summary>
/// Finds insertion points for all variables used within `node`
@ -202,25 +202,27 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -202,25 +202,27 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
/// </remarks>
void FindInsertionPoints(AstNode node, int nodeLevel)
{
BlockContainer loop = node.Annotation<BlockContainer>();
if (loop != null) {
loopTracking.Add((new InsertionPoint { level = nodeLevel, nextNode = node }, loop));
BlockContainer scope = node.Annotation<BlockContainer>();
if (scope != null && (scope.EntryPoint.IncomingEdgeCount > 1 || scope.Parent is ILFunction)) {
// track loops and function bodies as scopes, for comparison with CaptureScope.
scopeTracking.Add((new InsertionPoint { level = nodeLevel, nextNode = node }, scope));
} else {
scope = null; // don't remove a scope if we didn't add one
}
try {
for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) {
FindInsertionPoints(child, nodeLevel + 1);
}
var identExpr = node as IdentifierExpression;
if (identExpr != null) {
if (node is IdentifierExpression identExpr) {
var rr = identExpr.GetResolveResult() as ILVariableResolveResult;
if (rr != null && VariableNeedsDeclaration(rr.Variable.Kind)) {
var variable = rr.Variable;
InsertionPoint newPoint;
int startIndex = loopTracking.Count - 1;
if (variable.CaptureScope != null && startIndex > -1 && variable.CaptureScope != loopTracking[startIndex].Loop) {
while (startIndex > -1 && loopTracking[startIndex].Loop != variable.CaptureScope)
int startIndex = scopeTracking.Count - 1;
if (variable.CaptureScope != null && startIndex > 0 && variable.CaptureScope != scopeTracking[startIndex].Scope) {
while (startIndex > 0 && scopeTracking[startIndex].Scope != variable.CaptureScope)
startIndex--;
newPoint = loopTracking[startIndex + 1].InsertionPoint;
newPoint = scopeTracking[startIndex + 1].InsertionPoint;
} else {
newPoint = new InsertionPoint { level = nodeLevel, nextNode = identExpr };
}
@ -236,8 +238,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -236,8 +238,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
}
}
} finally {
if (loop != null)
loopTracking.RemoveAt(loopTracking.Count - 1);
if (scope != null)
scopeTracking.RemoveAt(scopeTracking.Count - 1);
}
}

6
ICSharpCode.Decompiler/CSharp/Transforms/FlattenSwitchBlocks.cs

@ -2,15 +2,15 @@ @@ -2,15 +2,15 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.Decompiler.CSharp.Syntax;
namespace ICSharpCode.Decompiler.CSharp.Transforms
{
class FlattenSwitchBlocks : IAstTransform
{
public void Run(AstNode compilationUnit)
public void Run(AstNode rootNode, TransformContext context)
{
foreach (var switchSection in compilationUnit.Descendants.OfType<SwitchSection>())
foreach (var switchSection in rootNode.Descendants.OfType<SwitchSection>())
{
if (switchSection.Statements.Count != 1)
continue;

15
ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs

@ -43,15 +43,24 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -43,15 +43,24 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
{
this.context = context;
this.usingScope = this.rootUsingScope = rootNode.Annotation<UsingScope>();
currentMember = context.DecompiledMember;
SetContext();
rootNode.AcceptVisitor(this);
}
void SetContext()
{
this.usingScope = rootUsingScope;
foreach (var name in currentMember.Namespace.Split('.'))
usingScope = new UsingScope(usingScope, name);
resolveContext = new CSharpTypeResolveContext(currentMember.ParentAssembly, usingScope.Resolve(context.TypeSystem.Compilation), currentMember.DeclaringTypeDefinition, currentMember);
string ns = currentMember?.Namespace ?? context.DecompiledTypeDefinition?.Namespace;
if (ns != null) {
foreach (var name in ns.Split('.'))
usingScope = new UsingScope(usingScope, name);
}
resolveContext = new CSharpTypeResolveContext(
context.DecompiledAssembly,
usingScope.Resolve(context.TypeSystem.Compilation),
currentMember?.DeclaringTypeDefinition ?? context.DecompiledTypeDefinition,
currentMember);
resolver = new CSharpResolver(resolveContext);
}

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

@ -96,11 +96,6 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -96,11 +96,6 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
public override AstNode VisitIfElseStatement(IfElseStatement ifElseStatement)
{
if (context.Settings.SwitchStatementOnString) {
AstNode result = TransformSwitchOnString(ifElseStatement);
if (result != null)
return result;
}
AstNode simplifiedIfElse = SimplifyCascadingIfElseStatements(ifElseStatement);
if (simplifiedIfElse != null)
return simplifiedIfElse;
@ -256,174 +251,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -256,174 +251,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
return null;
}
#endregion
#region switch on strings
static readonly IfElseStatement switchOnStringPattern = new IfElseStatement {
Condition = new BinaryOperatorExpression {
Left = new AnyNode("switchExpr"),
Operator = BinaryOperatorType.InEquality,
Right = new NullReferenceExpression()
},
TrueStatement = new BlockStatement {
new IfElseStatement {
Condition = new BinaryOperatorExpression {
Left = new AnyNode("cachedDict"),
Operator = BinaryOperatorType.Equality,
Right = new NullReferenceExpression()
},
TrueStatement = new AnyNode("dictCreation")
},
new IfElseStatement {
Condition = new InvocationExpression(new MemberReferenceExpression(new Backreference("cachedDict").ToExpression(), "TryGetValue"),
new NamedNode("switchVar", new IdentifierExpression(Pattern.AnyString)),
new DirectionExpression {
FieldDirection = FieldDirection.Out,
Expression = new IdentifierExpression(Pattern.AnyString).WithName("intVar")
}),
TrueStatement = new BlockStatement {
Statements = {
new NamedNode(
"switch", new SwitchStatement {
Expression = new IdentifierExpressionBackreference("intVar"),
SwitchSections = { new Repeat(new AnyNode()) }
})
}
}
},
new Repeat(new AnyNode("nonNullDefaultStmt")).ToStatement()
},
FalseStatement = new OptionalNode("nullStmt", new BlockStatement { Statements = { new Repeat(new AnyNode()) } })
};
public SwitchStatement TransformSwitchOnString(IfElseStatement node)
{
Match m = switchOnStringPattern.Match(node);
if (!m.Success)
return null;
// switchVar must be the same as switchExpr; or switchExpr must be an assignment and switchVar the left side of that assignment
if (!m.Get("switchVar").Single().IsMatch(m.Get("switchExpr").Single())) {
AssignmentExpression assign = m.Get("switchExpr").Single() as AssignmentExpression;
if (!(assign != null && m.Get("switchVar").Single().IsMatch(assign.Left)))
return null;
}
FieldReference cachedDictField = m.Get<AstNode>("cachedDict").Single().Annotation<FieldReference>();
if (cachedDictField == null)
return null;
List<Statement> dictCreation = m.Get<BlockStatement>("dictCreation").Single().Statements.ToList();
List<KeyValuePair<string, int>> dict = BuildDictionary(dictCreation);
SwitchStatement sw = m.Get<SwitchStatement>("switch").Single();
sw.Expression = m.Get<Expression>("switchExpr").Single().Detach();
foreach (SwitchSection section in sw.SwitchSections) {
List<CaseLabel> labels = section.CaseLabels.ToList();
section.CaseLabels.Clear();
foreach (CaseLabel label in labels) {
PrimitiveExpression expr = label.Expression as PrimitiveExpression;
if (expr == null || !(expr.Value is int))
continue;
int val = (int)expr.Value;
foreach (var pair in dict) {
if (pair.Value == val)
section.CaseLabels.Add(new CaseLabel { Expression = new PrimitiveExpression(pair.Key) });
}
}
}
if (m.Has("nullStmt")) {
SwitchSection section = new SwitchSection();
section.CaseLabels.Add(new CaseLabel { Expression = new NullReferenceExpression() });
BlockStatement block = m.Get<BlockStatement>("nullStmt").Single();
block.Statements.Add(new BreakStatement());
section.Statements.Add(block.Detach());
sw.SwitchSections.Add(section);
} else if (m.Has("nonNullDefaultStmt")) {
sw.SwitchSections.Add(
new SwitchSection {
CaseLabels = { new CaseLabel { Expression = new NullReferenceExpression() } },
Statements = { new BlockStatement { new BreakStatement() } }
});
}
if (m.Has("nonNullDefaultStmt")) {
SwitchSection section = new SwitchSection();
section.CaseLabels.Add(new CaseLabel());
BlockStatement block = new BlockStatement();
block.Statements.AddRange(m.Get<Statement>("nonNullDefaultStmt").Select(s => s.Detach()));
block.Add(new BreakStatement());
section.Statements.Add(block);
sw.SwitchSections.Add(section);
}
node.ReplaceWith(sw);
return sw;
}
List<KeyValuePair<string, int>> BuildDictionary(List<Statement> dictCreation)
{
if (context.Settings.ObjectOrCollectionInitializers && dictCreation.Count == 1)
return BuildDictionaryFromInitializer(dictCreation[0]);
return BuildDictionaryFromAddMethodCalls(dictCreation);
}
static readonly Statement assignInitializedDictionary = new ExpressionStatement {
Expression = new AssignmentExpression {
Left = new AnyNode().ToExpression(),
Right = new ObjectCreateExpression {
Type = new AnyNode(),
Arguments = { new Repeat(new AnyNode()) },
Initializer = new ArrayInitializerExpression {
Elements = { new Repeat(new AnyNode("dictJumpTable")) }
}
},
},
};
private List<KeyValuePair<string, int>> BuildDictionaryFromInitializer(Statement statement)
{
List<KeyValuePair<string, int>> dict = new List<KeyValuePair<string, int>>();
Match m = assignInitializedDictionary.Match(statement);
if (!m.Success)
return dict;
foreach (ArrayInitializerExpression initializer in m.Get<ArrayInitializerExpression>("dictJumpTable")) {
KeyValuePair<string, int> pair;
if (TryGetPairFrom(initializer.Elements, out pair))
dict.Add(pair);
}
return dict;
}
private static List<KeyValuePair<string, int>> BuildDictionaryFromAddMethodCalls(List<Statement> dictCreation)
{
List<KeyValuePair<string, int>> dict = new List<KeyValuePair<string, int>>();
for (int i = 0; i < dictCreation.Count; i++) {
ExpressionStatement es = dictCreation[i] as ExpressionStatement;
if (es == null)
continue;
InvocationExpression ie = es.Expression as InvocationExpression;
if (ie == null)
continue;
KeyValuePair<string, int> pair;
if (TryGetPairFrom(ie.Arguments, out pair))
dict.Add(pair);
}
return dict;
}
private static bool TryGetPairFrom(AstNodeCollection<Expression> expressions, out KeyValuePair<string, int> pair)
{
PrimitiveExpression arg1 = expressions.ElementAtOrDefault(0) as PrimitiveExpression;
PrimitiveExpression arg2 = expressions.ElementAtOrDefault(1) as PrimitiveExpression;
if (arg1 != null && arg2 != null && arg1.Value is string && arg2.Value is int) {
pair = new KeyValuePair<string, int>((string)arg1.Value, (int)arg2.Value);
return true;
}
pair = default(KeyValuePair<string, int>);
return false;
}
#endregion
#region Automatic Properties
static readonly PropertyDeclaration automaticPropertyPattern = new PropertyDeclaration {
Attributes = { new Repeat(new AnyNode()) },

4
ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs

@ -69,7 +69,9 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -69,7 +69,9 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
case "System.Type.GetTypeFromHandle":
if (arguments.Length == 1) {
if (typeHandleOnTypeOfPattern.IsMatch(arguments[0])) {
invocationExpression.ReplaceWith(((MemberReferenceExpression)arguments[0]).Target);
Expression target = ((MemberReferenceExpression)arguments[0]).Target;
target.CopyInstructionsFrom(invocationExpression);
invocationExpression.ReplaceWith(target);
return;
}
}

27
ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs

@ -172,10 +172,27 @@ namespace ICSharpCode.Decompiler.CSharp @@ -172,10 +172,27 @@ namespace ICSharpCode.Decompiler.CSharp
{
var type = this.Type;
if (type.Equals(targetType)) {
// Remove boxing conversion if possible
if (allowImplicitConversion && type.IsKnownType(KnownTypeCode.Object)) {
if (Expression is CastExpression cast && ResolveResult is ConversionResolveResult conversion && conversion.Conversion.IsBoxingConversion) {
return this.UnwrapChild(cast.Expression);
// Make explicit conversion implicit, if possible
if (allowImplicitConversion) {
switch (ResolveResult) {
case ConversionResolveResult conversion: {
if (Expression is CastExpression cast
&& (type.IsKnownType(KnownTypeCode.Object) && conversion.Conversion.IsBoxingConversion
|| type.Kind == TypeKind.Delegate && conversion.Conversion.IsAnonymousFunctionConversion
)) {
return this.UnwrapChild(cast.Expression);
} else if (Expression is ObjectCreateExpression oce && conversion.Conversion.IsMethodGroupConversion
&& oce.Arguments.Count == 1 && expressionBuilder.settings.UseImplicitMethodGroupConversion) {
return this.UnwrapChild(oce.Arguments.Single());
}
break;
}
case InvocationResolveResult invocation: {
if (Expression is ObjectCreateExpression oce && oce.Arguments.Count == 1 && invocation.Type.IsKnownType(KnownTypeCode.NullableOfT)) {
return this.UnwrapChild(oce.Arguments.Single());
}
break;
}
}
}
return this;
@ -296,7 +313,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -296,7 +313,7 @@ namespace ICSharpCode.Decompiler.CSharp
}
var rr = expressionBuilder.resolver.WithCheckForOverflow(checkForOverflow).ResolveCast(targetType, ResolveResult);
if (rr.IsCompileTimeConstant && !rr.IsError) {
return expressionBuilder.ConvertConstantValue(rr)
return expressionBuilder.ConvertConstantValue(rr, allowImplicitConversion)
.WithILInstruction(this.ILInstructions);
}
if (targetType.Kind == TypeKind.Pointer && (0.Equals(ResolveResult.ConstantValue) || 0u.Equals(ResolveResult.ConstantValue))) {

4
ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs

@ -283,8 +283,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -283,8 +283,8 @@ namespace ICSharpCode.Decompiler.CSharp
const string prop = "Properties";
if (directories.Add(prop))
Directory.CreateDirectory(Path.Combine(targetDirectory, prop));
string assemblyInfo = Path.Combine(targetDirectory, prop, "AssemblyInfo.cs");
using (StreamWriter w = new StreamWriter(assemblyInfo)) {
string assemblyInfo = Path.Combine(prop, "AssemblyInfo.cs");
using (StreamWriter w = new StreamWriter(Path.Combine(targetDirectory, assemblyInfo))) {
syntaxTree.AcceptVisitor(new CSharpOutputVisitor(w, settings.CSharpFormattingOptions));
}
return new Tuple<string, string>[] { Tuple.Create("Compile", assemblyInfo) };

36
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -228,7 +228,24 @@ namespace ICSharpCode.Decompiler @@ -228,7 +228,24 @@ namespace ICSharpCode.Decompiler
}
}
}
bool useImplicitMethodGroupConversion = true;
/// <summary>
/// Gets/Sets whether to use C# 2.0 method group conversions.
/// true: <c>EventHandler h = this.OnClick;</c>
/// false: <c>EventHandler h = new EventHandler(this.OnClick);</c>
/// </summary>
public bool UseImplicitMethodGroupConversion {
get { return useImplicitMethodGroupConversion; }
set {
if (useImplicitMethodGroupConversion != value) {
useImplicitMethodGroupConversion = value;
OnPropertyChanged();
}
}
}
bool fullyQualifyAmbiguousTypeNames = true;
public bool FullyQualifyAmbiguousTypeNames {
@ -343,8 +360,21 @@ namespace ICSharpCode.Decompiler @@ -343,8 +360,21 @@ namespace ICSharpCode.Decompiler
}
}
}
bool showDebugInfo;
public bool ShowDebugInfo {
get { return showDebugInfo; }
set {
if (showDebugInfo != value) {
showDebugInfo = value;
OnPropertyChanged();
}
}
}
#endregion
CSharpFormattingOptions csharpFormattingOptions;
public CSharpFormattingOptions CSharpFormattingOptions {
@ -365,7 +395,7 @@ namespace ICSharpCode.Decompiler @@ -365,7 +395,7 @@ namespace ICSharpCode.Decompiler
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)

20
ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs

@ -289,6 +289,19 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -289,6 +289,19 @@ namespace ICSharpCode.Decompiler.Disassembler
writer.Write(" modreq(");
((RequiredModifierType)type).ModifierType.WriteTo(writer, ILNameSyntax.TypeName);
writer.Write(") ");
} else if (type is FunctionPointerType fpt) {
writer.Write("method ");
fpt.ReturnType.WriteTo(writer, syntax);
writer.Write(" *(");
bool first = true;
foreach (var p in fpt.Parameters) {
if (first)
first = false;
else
writer.Write(", ");
p.ParameterType.WriteTo(writer, syntax);
}
writer.Write(')');
} else if (type is SentinelType) {
writer.Write("..., ");
((SentinelType)type).ElementType.WriteTo(writer, syntax);
@ -343,9 +356,10 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -343,9 +356,10 @@ namespace ICSharpCode.Decompiler.Disassembler
ParameterReference paramRef = operand as ParameterReference;
if (paramRef != null) {
if (string.IsNullOrEmpty(paramRef.Name))
writer.WriteReference(paramRef.Index.ToString(), paramRef);
else
if (string.IsNullOrEmpty(paramRef.Name)) {
var paramDef = paramRef.Resolve();
writer.WriteReference((paramDef == null ? paramRef.Index : paramDef.Sequence).ToString(), paramRef);
} else
writer.WriteReference(Escape(paramRef.Name), paramRef);
return;
}

80
ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs

@ -21,28 +21,38 @@ using System.Collections.Generic; @@ -21,28 +21,38 @@ using System.Collections.Generic;
using System.Threading;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Collections.Generic;
namespace ICSharpCode.Decompiler.Disassembler
{
/// <summary>
/// Disassembles a method body.
/// </summary>
public sealed class MethodBodyDisassembler
public class MethodBodyDisassembler
{
readonly ITextOutput output;
readonly bool detectControlStructure;
readonly CancellationToken cancellationToken;
public MethodBodyDisassembler(ITextOutput output, bool detectControlStructure, CancellationToken cancellationToken)
/// <summary>
/// Show .try/finally as blocks in IL code; indent loops.
/// </summary>
public bool DetectControlStructure { get; set; } = true;
/// <summary>
/// Show sequence points if debug information is loaded in Cecil.
/// </summary>
public bool ShowSequencePoints { get; set; }
Collection<SequencePoint> sequencePoints;
int nextSequencePointIndex;
public MethodBodyDisassembler(ITextOutput output, CancellationToken cancellationToken)
{
if (output == null)
throw new ArgumentNullException(nameof(output));
this.output = output;
this.detectControlStructure = detectControlStructure;
this.output = output ?? throw new ArgumentNullException(nameof(output));
this.cancellationToken = cancellationToken;
}
public void Disassemble(MethodBody body/*, MemberMapping methodMapping*/)
public virtual void Disassemble(MethodBody body)
{
// start writing IL code
MethodDefinition method = body.Method;
@ -55,30 +65,20 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -55,30 +65,20 @@ namespace ICSharpCode.Decompiler.Disassembler
DisassembleLocalsBlock(body);
output.WriteLine();
if (detectControlStructure && body.Instructions.Count > 0) {
sequencePoints = method.DebugInformation?.SequencePoints;
nextSequencePointIndex = 0;
if (DetectControlStructure && body.Instructions.Count > 0) {
Instruction inst = body.Instructions[0];
HashSet<int> branchTargets = GetBranchTargets(body.Instructions);
WriteStructureBody(new ILStructure(body), branchTargets, ref inst, method.Body.CodeSize);
} else {
foreach (var inst in method.Body.Instructions) {
//var startLocation = output.Location;
inst.WriteTo(output);
/*
if (debugSymbols != null) {
// add IL code mappings - used in debugger
debugSymbols.SequencePoints.Add(
new SequencePoint() {
StartLocation = output.Location,
EndLocation = output.Location,
ILRanges = new ILRange[] { new ILRange(inst.Offset, inst.Next == null ? method.Body.CodeSize : inst.Next.Offset) }
});
}*/
WriteInstruction(output, inst);
output.WriteLine();
}
WriteExceptionHandlers(body);
}
sequencePoints = null;
}
private void DisassembleLocalsBlock(MethodBody body)
@ -191,19 +191,7 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -191,19 +191,7 @@ namespace ICSharpCode.Decompiler.Disassembler
if (!isFirstInstructionInStructure && (prevInstructionWasBranch || branchTargets.Contains(offset))) {
output.WriteLine(); // put an empty line after branches, and in front of branch targets
}
//var startLocation = output.Location;
inst.WriteTo(output);
/*// add IL code mappings - used in debugger
if (debugSymbols != null) {
debugSymbols.SequencePoints.Add(
new SequencePoint() {
StartLocation = startLocation,
EndLocation = output.Location,
ILRanges = new ILRange[] { new ILRange(inst.Offset, inst.Next == null ? codeSize : inst.Next.Offset) }
});
}*/
WriteInstruction(output, inst);
output.WriteLine();
prevInstructionWasBranch = inst.OpCode.FlowControl == FlowControl.Branch
@ -237,5 +225,25 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -237,5 +225,25 @@ namespace ICSharpCode.Decompiler.Disassembler
throw new NotSupportedException();
}
}
protected virtual void WriteInstruction(ITextOutput output, Instruction instruction)
{
if (ShowSequencePoints && nextSequencePointIndex < sequencePoints?.Count) {
SequencePoint sp = sequencePoints[nextSequencePointIndex];
if (sp.Offset <= instruction.Offset) {
output.Write("// sequence point: ");
if (sp.Offset != instruction.Offset) {
output.Write("!! at " + DisassemblerHelpers.OffsetToString(sp.Offset) + " !!");
}
if (sp.IsHidden) {
output.WriteLine("hidden");
} else {
output.WriteLine($"(line {sp.StartLine}, col {sp.StartColumn}) to (line {sp.EndLine}, col {sp.EndColumn}) in {sp.Document?.Url}");
}
nextSequencePointIndex++;
}
}
instruction.WriteTo(output);
}
}
}

19
ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs

@ -35,13 +35,28 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -35,13 +35,28 @@ namespace ICSharpCode.Decompiler.Disassembler
MethodBodyDisassembler methodBodyDisassembler;
MemberReference currentMember;
public ReflectionDisassembler(ITextOutput output, bool detectControlStructure, CancellationToken cancellationToken)
public bool DetectControlStructure {
get => methodBodyDisassembler.DetectControlStructure;
set => methodBodyDisassembler.DetectControlStructure = value;
}
public bool ShowSequencePoints {
get => methodBodyDisassembler.ShowSequencePoints;
set => methodBodyDisassembler.ShowSequencePoints = value;
}
public ReflectionDisassembler(ITextOutput output, CancellationToken cancellationToken)
: this(output, new MethodBodyDisassembler(output, cancellationToken), cancellationToken)
{
}
public ReflectionDisassembler(ITextOutput output, MethodBodyDisassembler methodBodyDisassembler, CancellationToken cancellationToken)
{
if (output == null)
throw new ArgumentNullException(nameof(output));
this.output = output;
this.cancellationToken = cancellationToken;
this.methodBodyDisassembler = new MethodBodyDisassembler(output, detectControlStructure, cancellationToken);
this.methodBodyDisassembler = methodBodyDisassembler;
}
#region Disassemble Method

2
ICSharpCode.Decompiler/DotNetCore/DotNetCorePathFinder.cs

@ -52,7 +52,7 @@ namespace ICSharpCode.Decompiler @@ -52,7 +52,7 @@ namespace ICSharpCode.Decompiler
var depsJsonFileName = Path.Combine(basePath, $"{assemblyName}.deps.json");
if (!File.Exists(depsJsonFileName)) {
loadInfo.AddMessage(assemblyName, MessageKind.Error, $"{assemblyName}.deps.json could not be found!");
loadInfo.AddMessage(assemblyName, MessageKind.Warning, $"{assemblyName}.deps.json could not be found!");
return;
}

1
ICSharpCode.Decompiler/DotNetCore/DotNetCorePathFinderExtensions.cs

@ -8,7 +8,6 @@ using Newtonsoft.Json.Linq; @@ -8,7 +8,6 @@ using Newtonsoft.Json.Linq;
namespace ICSharpCode.Decompiler
{
public static class DotNetCorePathFinderExtensions
{
public static string DetectTargetFrameworkId(this AssemblyDefinition assembly)

63
ICSharpCode.Decompiler/FlowAnalysis/ControlFlowNode.cs

@ -19,6 +19,7 @@ @@ -19,6 +19,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
@ -118,35 +119,37 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -118,35 +119,37 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
}
return false;
}
//public static GraphVizGraph ExportGraph(IReadOnlyList<ControlFlowNode> nodes, Func<ControlFlowNode, string> labelFunc = null)
//{
// if (labelFunc == null) {
// labelFunc = node => {
// var block = node.UserData as IL.Block;
// return block != null ? block.Label : node.UserData?.ToString();
// };
// }
// GraphVizGraph g = new GraphVizGraph();
// GraphVizNode[] n = new GraphVizNode[nodes.Count];
// for (int i = 0; i < n.Length; i++) {
// n[i] = new GraphVizNode(nodes[i].UserIndex);
// n[i].shape = "box";
// n[i].label = labelFunc(nodes[i]);
// g.AddNode(n[i]);
// }
// foreach (var source in nodes) {
// foreach (var target in source.Successors) {
// g.AddEdge(new GraphVizEdge(source.UserIndex, target.UserIndex));
// }
// if (source.ImmediateDominator != null) {
// g.AddEdge(
// new GraphVizEdge(source.ImmediateDominator.UserIndex, source.UserIndex) {
// color = "green"
// });
// }
// }
// return g;
//}
#if DEBUG
internal static GraphVizGraph ExportGraph(IReadOnlyList<ControlFlowNode> nodes, Func<ControlFlowNode, string> labelFunc = null)
{
if (labelFunc == null) {
labelFunc = node => {
var block = node.UserData as IL.Block;
return block != null ? block.Label : node.UserData?.ToString();
};
}
GraphVizGraph g = new GraphVizGraph();
GraphVizNode[] n = new GraphVizNode[nodes.Count];
for (int i = 0; i < n.Length; i++) {
n[i] = new GraphVizNode(nodes[i].UserIndex);
n[i].shape = "box";
n[i].label = labelFunc(nodes[i]);
g.AddNode(n[i]);
}
foreach (var source in nodes) {
foreach (var target in source.Successors) {
g.AddEdge(new GraphVizEdge(source.UserIndex, target.UserIndex));
}
if (source.ImmediateDominator != null) {
g.AddEdge(
new GraphVizEdge(source.ImmediateDominator.UserIndex, source.UserIndex) {
color = "green"
});
}
}
return g;
}
#endif
}
}

8
ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs

@ -614,17 +614,17 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -614,17 +614,17 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
DebugStartPoint(inst);
inst.Value.AcceptVisitor(this);
State beforeSections = state.Clone();
inst.DefaultBody.AcceptVisitor(this);
inst.Sections[0].AcceptVisitor(this);
State afterSections = state.Clone();
foreach (var section in inst.Sections) {
for (int i = 1; i < inst.Sections.Count; ++i) {
state.ReplaceWith(beforeSections);
section.AcceptVisitor(this);
inst.Sections[i].AcceptVisitor(this);
afterSections.JoinWith(state);
}
state = afterSections;
DebugEndPoint(inst);
}
protected internal override void VisitYieldReturn(YieldReturn inst)
{
DebugStartPoint(inst);

28
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -24,21 +24,23 @@ @@ -24,21 +24,23 @@
<!-- HACK: Disable package generation on Unix due to tooling issues. -->
<PropertyGroup Condition=" '$(OS)' == 'Windows_NT' ">
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<IncludeSymbols>true</IncludeSymbols>
<IncludeSymbolsInPackage>true</IncludeSymbolsInPackage>
<NuspecFile>ICSharpCode.Decompiler.nuspec</NuspecFile>
<NuspecProperties>Configuration=$(Configuration)</NuspecProperties>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<DebugType>full</DebugType>
<DebugType>portable</DebugType>
<DebugSymbols>true</DebugSymbols>
<EmbedSources>true</EmbedSources>
<CheckForOverflowUnderflow>True</CheckForOverflowUnderflow>
<DefineConstants>$(DefineConstants);STEP</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>pdbonly</DebugType>
<DebugType>portable</DebugType>
<DebugSymbols>true</DebugSymbols>
<EmbedSources>true</EmbedSources>
</PropertyGroup>
<PropertyGroup>
@ -60,6 +62,7 @@ @@ -60,6 +62,7 @@
<ItemGroup>
<Compile Include="CSharp\Annotations.cs" />
<Compile Include="CSharp\CallBuilder.cs" />
<Compile Include="CSharp\SequencePointBuilder.cs" />
<Compile Include="CSharp\Syntax\AstNode.cs" />
<Compile Include="CSharp\Syntax\AstNodeCollection.cs" />
<Compile Include="CSharp\Syntax\AstType.cs" />
@ -224,6 +227,7 @@ @@ -224,6 +227,7 @@
<Compile Include="CSharp\Resolver\RenameCallbackArguments.cs" />
<Compile Include="CSharp\Resolver\TypeInference.cs" />
<Compile Include="CSharp\Transforms\CombineQueryExpressions.cs" />
<Compile Include="CSharp\Transforms\FlattenSwitchBlocks.cs" />
<Compile Include="CSharp\Transforms\IntroduceExtensionMethods.cs" />
<Compile Include="CSharp\Transforms\IntroduceQueryExpressions.cs" />
<Compile Include="CSharp\Transforms\PrettifyAssignments.cs" />
@ -272,7 +276,17 @@ @@ -272,7 +276,17 @@
<Compile Include="DotNetCore\DotNetCorePathFinderExtensions.cs" />
<Compile Include="DotNetCore\UnresolvedAssemblyNameReference.cs" />
<Compile Include="IL\Instructions\ILInstructionExtensions.cs" />
<Compile Include="IL\ILAstWritingOptions.cs" />
<Compile Include="IL\SequencePoint.cs" />
<Compile Include="IL\Instructions\CallIndirect.cs" />
<Compile Include="IL\Instructions\DefaultValue.cs" />
<Compile Include="IL\Transforms\EarlyExpressionTransforms.cs" />
<Compile Include="IL\Transforms\ProxyCallReplacer.cs" />
<Compile Include="IL\Instructions\StringToInt.cs" />
<Compile Include="IL\Instructions\UsingInstruction.cs" />
<Compile Include="IL\Transforms\InlineReturnTransform.cs" />
<Compile Include="IL\Transforms\SwitchOnNullableTransform.cs" />
<Compile Include="IL\Transforms\SwitchOnStringTransform.cs" />
<Compile Include="IL\Transforms\UsingTransform.cs" />
<Compile Include="IL\ControlFlow\AsyncAwaitDecompiler.cs" />
<Compile Include="IL\ControlFlow\ControlFlowGraph.cs" />
@ -294,6 +308,7 @@ @@ -294,6 +308,7 @@
<Compile Include="IL\Transforms\StatementTransform.cs" />
<Compile Include="IL\Transforms\TransformCollectionAndObjectInitializers.cs" />
<Compile Include="Output\TextTokenWriter.cs" />
<Compile Include="Util\GraphVizGraph.cs" />
<Compile Include="Util\KeyComparer.cs" />
<Compile Include="Util\LongDict.cs" />
<Compile Include="Util\UnicodeNewline.cs" />
@ -575,4 +590,11 @@ @@ -575,4 +590,11 @@
</PropertyGroup>
<Exec WorkingDirectory="$(SolutionDir)" Command="$(UpdateAssemblyInfo)" Timeout="60000" />
</Target>
<!-- Workaround for EmbedSources support, see https://github.com/dotnet/roslyn/issues/19127 -->
<Target Name="PopulateEmbeddedFiles" AfterTargets="BeforeCompile" BeforeTargets="CoreCompile">
<ItemGroup>
<EmbeddedFiles Include="@(Compile)" />
</ItemGroup>
</Target>
</Project>

15
ICSharpCode.Decompiler/ICSharpCode.Decompiler.nuspec → ICSharpCode.Decompiler/ICSharpCode.Decompiler.nuspec.template

@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>ICSharpCode.Decompiler</id>
<version>3.0.0-alpha3</version>
<version>$INSERTVERSION$$INSERTVERSIONNAMEPOSTFIX$</version>
<title>ILSpy Decompiler Engine</title>
<authors>Daniel Grunwald, David Srbecky, Ed Harvey, Siegfried Pammer</authors>
<owners>Daniel Grunwald, SharpDevelop</owners>
@ -12,12 +12,13 @@ @@ -12,12 +12,13 @@
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>ICSharpCode.Decompiler is the decompiler engine used in ILSpy.</description>
<!--<releaseNotes></releaseNotes>-->
<copyright>Copyright 2011-2017 AlphaSierraPapa</copyright>
<copyright>Copyright 2011-$INSERTYEAR$ AlphaSierraPapa</copyright>
<tags>C# Decompiler ILSpy</tags>
<dependencies>
<dependency id="System.Collections.Immutable" version="1.3.0" />
<dependency id="System.ValueTuple" version="4.3.0" />
<dependency id="Newtonsoft.Json" version="10.0.3" />
<dependency id="Humanizer.Core" version="2.2.0" />
<dependency id="Newtonsoft.Json" version="10.0.3" />
<dependency id="System.Collections.Immutable" version="1.4.0" />
<dependency id="System.ValueTuple" version="4.4.0" />
</dependencies>
</metadata>
<files>
@ -30,9 +31,5 @@ @@ -30,9 +31,5 @@
<file src="bin\$Configuration$\netstandard2.0\ICSharpCode.Decompiler.pdb" target="lib\netstandard2.0" />
<file src="bin\$Configuration$\netstandard2.0\Mono.Cecil.dll" target="lib\netstandard2.0" />
<file src="bin\$Configuration$\netstandard2.0\Mono.Cecil.pdb" target="lib\netstandard2.0" />
<file src="**\*.cs" exclude="obj\**\*.cs" target="src\ICSharpCode.Decompiler" />
<file src="obj\$Configuration$\net461\**\*.cs" target="src\ICSharpCode.Decompiler\obj\$Configuration$\net461" />
<file src="obj\$Configuration$\netstandard2.0\**\*.cs" target="src\ICSharpCode.Decompiler\obj\$Configuration$\netstandard2.0" />
</files>
</package>

19
ICSharpCode.Decompiler/IL/BlockBuilder.cs

@ -163,8 +163,15 @@ namespace ICSharpCode.Decompiler.IL @@ -163,8 +163,15 @@ namespace ICSharpCode.Decompiler.IL
if (currentBlock == null)
return;
currentBlock.ILRange = new Interval(currentBlock.ILRange.Start, currentILOffset);
if (fallthrough)
currentBlock.Instructions.Add(new Branch(currentILOffset));
if (fallthrough) {
if (currentBlock.Instructions.LastOrDefault() is SwitchInstruction switchInst && switchInst.Sections.Last().Body.MatchNop()) {
// Instead of putting the default branch after the switch instruction
switchInst.Sections.Last().Body = new Branch(currentILOffset);
Debug.Assert(switchInst.HasFlag(InstructionFlags.EndPointUnreachable));
} else {
currentBlock.Instructions.Add(new Branch(currentILOffset));
}
}
currentBlock = null;
}
@ -175,6 +182,12 @@ namespace ICSharpCode.Decompiler.IL @@ -175,6 +182,12 @@ namespace ICSharpCode.Decompiler.IL
cancellationToken.ThrowIfCancellationRequested();
Debug.Assert(branch.TargetBlock == null);
branch.TargetBlock = FindBranchTarget(branch.TargetILOffset);
if (branch.TargetBlock == null) {
branch.ReplaceWith(new InvalidBranch("Could not find block for branch target "
+ Disassembler.DisassemblerHelpers.OffsetToString(branch.TargetILOffset)) {
ILRange = branch.ILRange
});
}
break;
case Leave leave:
// ret (in void method) = leave(mainContainer)
@ -210,7 +223,7 @@ namespace ICSharpCode.Decompiler.IL @@ -210,7 +223,7 @@ namespace ICSharpCode.Decompiler.IL
return block;
}
}
throw new InvalidOperationException("Could not find block for branch target");
return null;
}
}
}

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

@ -169,7 +169,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -169,7 +169,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
return false;
if (startCall.Method.Name != "Start")
return false;
taskType = context.TypeSystem.Resolve(function.Method.ReturnType);
taskType = function.Method.ReturnType;
builderType = startCall.Method.DeclaringTypeDefinition;
const string ns = "System.Runtime.CompilerServices";
if (taskType.IsKnownType(KnownTypeCode.Void)) {
@ -774,17 +774,26 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -774,17 +774,26 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
return false;
if (!target.MatchLdThis())
return false;
if (field.MemberDefinition != awaiterField)
if (!field.Equals(awaiterField))
return false;
pos++;
// stfld awaiterField(ldloc this, default.value)
if (block.Instructions[pos].MatchStFld(out target, out field, out value)
&& target.MatchLdThis()
&& field.MemberDefinition == awaiterField
&& field.Equals(awaiterField)
&& value.OpCode == OpCode.DefaultValue)
{
pos++;
} else {
// {stloc V_6(default.value System.Runtime.CompilerServices.TaskAwaiter)}
// {stobj System.Runtime.CompilerServices.TaskAwaiter`1[[System.Int32]](ldflda <>u__$awaiter4(ldloc this), ldloc V_6) at IL_0163}
if (block.Instructions[pos].MatchStLoc(out var variable, out value) && value.OpCode == OpCode.DefaultValue
&& block.Instructions[pos + 1].MatchStFld(out target, out field, out value)
&& field.Equals(awaiterField)
&& value.MatchLdLoc(variable)) {
pos += 2;
}
}
// stloc S_28(ldc.i4 -1)
@ -804,7 +813,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -804,7 +813,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
if (block.Instructions[pos].MatchStFld(out target, out field, out value)) {
if (!target.MatchLdThis())
return false;
if (field.MemberDefinition != stateField)
if (!field.MemberDefinition.Equals(stateField.MemberDefinition))
return false;
if (!(value.MatchLdcI4(initialState) || value.MatchLdLoc(m1Var)))
return false;

51
ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs

@ -63,18 +63,14 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -63,18 +63,14 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// Last instruction is one with unreachable endpoint
// (guaranteed by combination of BlockContainer and Block invariants)
Debug.Assert(block.Instructions.Last().HasFlag(InstructionFlags.EndPointUnreachable));
ILInstruction exitInst = block.Instructions.Last();
ILInstruction exitInst = block.Instructions.Last();
// Previous-to-last instruction might have conditional control flow,
// usually an IfInstruction with a branch:
IfInstruction ifInst = block.Instructions.SecondToLastOrDefault() as IfInstruction;
if (ifInst != null && ifInst.FalseInst.OpCode == OpCode.Nop) {
HandleIfInstruction(cfgNode, block, ifInst, ref exitInst);
} else {
SwitchInstruction switchInst = block.Instructions.SecondToLastOrDefault() as SwitchInstruction;
if (switchInst != null) {
HandleSwitchInstruction(cfgNode, block, switchInst, ref exitInst);
}
}
if (IsUsableBranchToChild(cfgNode, exitInst)) {
// "...; goto usableblock;"
@ -297,48 +293,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -297,48 +293,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
&& targetBlock.IncomingEdgeCount == 1 && targetBlock.FinalInstruction.OpCode == OpCode.Nop
&& cfgNode.Dominates(context.ControlFlowGraph.GetNode(targetBlock));
}
private void HandleSwitchInstruction(ControlFlowNode cfgNode, Block block, SwitchInstruction sw, ref ILInstruction exitInst)
{
Debug.Assert(sw.DefaultBody is Nop);
// First, move blocks into the switch section
foreach (var section in sw.Sections) {
if (IsUsableBranchToChild(cfgNode, section.Body)) {
// case ...: goto targetBlock;
var targetBlock = ((Branch)section.Body).TargetBlock;
targetBlock.Remove();
section.Body = targetBlock;
}
}
// Move the code following the switch into the default section
if (IsUsableBranchToChild(cfgNode, exitInst)) {
// switch(...){} goto targetBlock;
// ---> switch(..) { default: { targetBlock } }
var targetBlock = ((Branch)exitInst).TargetBlock;
targetBlock.Remove();
sw.DefaultBody = targetBlock;
if (IsBranchOrLeave(targetBlock.Instructions.Last())) {
exitInst = block.Instructions[block.Instructions.Count - 1] = targetBlock.Instructions.Last();
targetBlock.Instructions.RemoveAt(targetBlock.Instructions.Count - 1);
} else {
exitInst = null;
block.Instructions.RemoveAt(block.Instructions.Count - 1);
}
}
// Remove compatible exitInsts from switch sections:
foreach (var section in sw.Sections) {
Block sectionBlock = section.Body as Block;
if (sectionBlock != null && exitInst == null && IsBranchOrLeave(sectionBlock.Instructions.Last())) {
exitInst = sectionBlock.Instructions.Last();
sectionBlock.Instructions.RemoveAt(sectionBlock.Instructions.Count - 1);
block.Instructions.Add(exitInst);
} else if (sectionBlock != null && DetectExitPoints.CompatibleExitInstruction(exitInst, sectionBlock.Instructions.Last())) {
sectionBlock.Instructions.RemoveAt(sectionBlock.Instructions.Count - 1);
}
}
sw.Sections.ReplaceList(sw.Sections.OrderBy(s => s.Body.ILRange.Start));
}
private bool IsBranchOrLeave(ILInstruction inst)
{
switch (inst) {

50
ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs

@ -19,6 +19,7 @@ using System.Collections.Generic; @@ -19,6 +19,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.Decompiler.IL.Transforms;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL.ControlFlow
{
@ -43,18 +44,30 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -43,18 +44,30 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
foreach (var block in function.Descendants.OfType<Block>()) {
context.CancellationToken.ThrowIfCancellationRequested();
// Remove 'nop' instructions
block.Instructions.RemoveAll(inst => inst.OpCode == OpCode.Nop);
RemoveNopInstructions(block);
InlineVariableInReturnBlock(block, context);
// 1st pass SimplifySwitchInstruction before SimplifyBranchChains()
// starts duplicating return instructions.
SwitchDetection.SimplifySwitchInstruction(block);
}
SimplifyBranchChains(function, context);
CleanUpEmptyBlocks(function);
CleanUpEmptyBlocks(function, context);
}
private static void RemoveNopInstructions(Block block)
{
// Move ILRanges of special nop instructions to the previous non-nop instruction.
for (int i = block.Instructions.Count - 1; i > 0; i--) {
if (block.Instructions[i] is Nop nop && nop.Kind == NopKind.Pop) {
block.Instructions[i - 1].AddILRange(nop.ILRange);
}
}
// Remove 'nop' instructions
block.Instructions.RemoveAll(inst => inst.OpCode == OpCode.Nop);
}
void InlineVariableInReturnBlock(Block block, ILTransformContext context)
{
// In debug mode, the C#-compiler generates 'return blocks' that
@ -129,13 +142,13 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -129,13 +142,13 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
}
void CleanUpEmptyBlocks(ILFunction function)
void CleanUpEmptyBlocks(ILFunction function, ILTransformContext context)
{
foreach (var container in function.Descendants.OfType<BlockContainer>()) {
foreach (var block in container.Blocks) {
if (block.Instructions.Count == 0)
continue; // block is already marked for deletion
while (CombineBlockWithNextBlock(container, block)) {
while (CombineBlockWithNextBlock(container, block, context)) {
// repeat combining blocks until it is no longer possible
// (this loop terminates because a block is deleted in every iteration)
}
@ -153,7 +166,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -153,7 +166,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
return targetBlock.Instructions[0].MatchReturn(out var value) && value is LdLoc;
}
static bool CombineBlockWithNextBlock(BlockContainer container, Block block)
static bool CombineBlockWithNextBlock(BlockContainer container, Block block, ILTransformContext context)
{
Debug.Assert(container == block.Parent);
// Ensure the block will stay a basic block -- we don't want extended basic blocks prior to LoopDetection.
@ -165,12 +178,31 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -165,12 +178,31 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
return false;
if (br.TargetBlock == block)
return false; // don't inline block into itself
context.Step("CombineBlockWithNextBlock", br);
var targetBlock = br.TargetBlock;
if (targetBlock.ILRange.Start < block.ILRange.Start && IsDeadTrueStore(block)) {
// The C# compiler generates a dead store for the condition of while (true) loops.
block.Instructions.RemoveRange(block.Instructions.Count - 3, 2);
}
block.Instructions.Remove(br);
block.Instructions.AddRange(targetBlock.Instructions);
targetBlock.Instructions.Clear(); // mark targetBlock for deletion
return true;
}
/// <summary>
/// Returns true if the last two instructions before the branch are storing the value 'true' into an unused variable.
/// </summary>
private static bool IsDeadTrueStore(Block block)
{
if (block.Instructions.Count < 3) return false;
if (!(block.Instructions.SecondToLastOrDefault() is StLoc deadStore && block.Instructions[block.Instructions.Count - 3] is StLoc tempStore))
return false;
if (!(deadStore.Variable.LoadCount == 0 && deadStore.Variable.AddressCount == 0))
return false;
if (!(deadStore.Value.MatchLdLoc(tempStore.Variable) && tempStore.Variable.IsSingleDefinition && tempStore.Variable.LoadCount == 1))
return false;
return tempStore.Value.MatchLdcI4(1) && deadStore.Variable.Type.IsKnownType(KnownTypeCode.Boolean);
}
}
}

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

@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
@ -57,6 +58,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -57,6 +58,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// Because this is a post-order block transform, we can assume that
// any nested loops within this loop have already been constructed.
if (block.Instructions.Last() is SwitchInstruction switchInst) {
// Switch instructions support "break;" just like loops
DetectSwitchBody(block, switchInst);
}
ControlFlowNode h = context.ControlFlowNode; // CFG node for our potential loop head
Debug.Assert(h.UserData == block);
Debug.Assert(!TreeTraversal.PreOrder(h, n => n.DominatorTreeChildren).Any(n => n.Visited));
@ -101,7 +107,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -101,7 +107,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
ConstructLoop(loop, exitPoint);
}
}
/// <summary>
/// Recurse into the dominator tree and find back edges/natural loops.
/// </summary>
@ -202,9 +208,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -202,9 +208,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
///
/// Precondition: Requires that a node is marked as visited iff it is contained in the loop.
/// </remarks>
void ExtendLoop(ControlFlowNode loopHead, List<ControlFlowNode> loop, out ControlFlowNode exitPoint)
void ExtendLoop(ControlFlowNode loopHead, List<ControlFlowNode> loop, out ControlFlowNode exitPoint, bool isSwitch=false)
{
exitPoint = FindExitPoint(loopHead, loop);
exitPoint = FindExitPoint(loopHead, loop, isSwitch);
Debug.Assert(!loop.Contains(exitPoint), "Cannot pick an exit point that is part of the natural loop");
if (exitPoint != null) {
// Either we are in case 1 and just picked an exit that maximizes the amount of code
@ -227,9 +233,15 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -227,9 +233,15 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// Finds a suitable single exit point for the specified loop.
/// </summary>
/// <remarks>This method must not write to the Visited flags on the CFG.</remarks>
ControlFlowNode FindExitPoint(ControlFlowNode loopHead, IReadOnlyList<ControlFlowNode> naturalLoop)
ControlFlowNode FindExitPoint(ControlFlowNode loopHead, IReadOnlyList<ControlFlowNode> naturalLoop, bool treatBackEdgesAsExits)
{
if (!context.ControlFlowGraph.HasReachableExit(loopHead)) {
bool hasReachableExit = context.ControlFlowGraph.HasReachableExit(loopHead);
if (!hasReachableExit && treatBackEdgesAsExits) {
// If we're analyzing the switch, there's no reachable exit, but the loopHead (=switchHead) block
// is also a loop head, we consider the back-edge a reachable exit for the switch.
hasReachableExit = loopHead.Predecessors.Any(p => loopHead.Dominates(p));
}
if (!hasReachableExit) {
// Case 1:
// There are no nodes n so that loopHead dominates a predecessor of n but not n itself
// -> we could build a loop with zero exit points.
@ -244,7 +256,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -244,7 +256,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// We need to pick our exit point so that all paths from the loop head
// to the reachable exits run through that exit point.
var cfg = context.ControlFlowGraph.cfg;
var revCfg = PrepareReverseCFG(loopHead);
var revCfg = PrepareReverseCFG(loopHead, treatBackEdgesAsExits);
//ControlFlowNode.ExportGraph(cfg).Show("cfg");
//ControlFlowNode.ExportGraph(revCfg).Show("rev");
ControlFlowNode commonAncestor = revCfg[loopHead.UserIndex];
@ -255,22 +267,63 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -255,22 +267,63 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
commonAncestor = Dominance.FindCommonDominator(commonAncestor, revNode);
}
}
// All paths from within the loop to a reachable exit run through 'commonAncestor'.
// However, this doesn't mean that 'commonAncestor' is valid as an exit point.
// We walk up the post-dominator tree until we've got a valid exit point:
ControlFlowNode exitPoint;
while (commonAncestor.UserIndex >= 0) {
exitPoint = cfg[commonAncestor.UserIndex];
Debug.Assert(exitPoint.Visited == naturalLoop.Contains(exitPoint));
if (exitPoint.Visited) {
commonAncestor = commonAncestor.ImmediateDominator;
continue;
} else {
// It's possible that 'commonAncestor' is itself part of the natural loop.
// If so, it's not a valid exit point.
if (!exitPoint.Visited && ValidateExitPoint(loopHead, exitPoint)) {
// we found an exit point
return exitPoint;
}
commonAncestor = commonAncestor.ImmediateDominator;
}
// least common dominator is the artificial exit node
// least common post-dominator is the artificial exit node
return null;
}
}
/// <summary>
/// Validates an exit point.
///
/// An exit point is invalid iff there is a node reachable from the exit point that
/// is dominated by the loop head, but not by the exit point.
/// (i.e. this method returns false iff the exit point's dominance frontier contains
/// a node dominated by the loop head. but we implement this the slow way because
/// we don't have dominance frontiers precomputed)
/// </summary>
/// <remarks>
/// We need this because it's possible that there's a return block (thus reverse-unreachable node ignored by post-dominance)
/// that is reachable both directly from the loop, and from the exit point.
/// </remarks>
bool ValidateExitPoint(ControlFlowNode loopHead, ControlFlowNode exitPoint)
{
var cfg = context.ControlFlowGraph;
return IsValid(exitPoint);
bool IsValid(ControlFlowNode node)
{
if (!cfg.HasReachableExit(node)) {
// Optimization: if the dominance frontier is empty, we don't need
// to check every node.
return true;
}
foreach (var succ in node.Successors) {
if (loopHead != succ && loopHead.Dominates(succ) && !exitPoint.Dominates(succ))
return false;
}
foreach (var child in node.DominatorTreeChildren) {
if (!IsValid(child))
return false;
}
return true;
}
}
/// <summary>
/// Pick exit point by picking any node that has no reachable exits.
///
@ -308,7 +361,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -308,7 +361,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
}
ControlFlowNode[] PrepareReverseCFG(ControlFlowNode loopHead)
ControlFlowNode[] PrepareReverseCFG(ControlFlowNode loopHead, bool treatBackEdgesAsExits)
{
ControlFlowNode[] cfg = context.ControlFlowGraph.cfg;
ControlFlowNode[] rev = new ControlFlowNode[cfg.Length + 1];
@ -322,7 +375,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -322,7 +375,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
continue;
// Add reverse edges for all edges in cfg
foreach (var succ in cfg[i].Successors) {
if (loopHead.Dominates(succ)) {
if (loopHead.Dominates(succ) && (!treatBackEdgesAsExits || loopHead != succ)) {
rev[succ.UserIndex].AddEdgeTo(rev[i]);
} else {
exitNode.AddEdgeTo(rev[i]);
@ -412,13 +465,25 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -412,13 +465,25 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// Move contents of oldEntryPoint to newEntryPoint
// (we can't move the block itself because it might be the target of branch instructions outside the loop)
newEntryPoint.Instructions.ReplaceList(oldEntryPoint.Instructions);
newEntryPoint.FinalInstruction = oldEntryPoint.FinalInstruction;
newEntryPoint.ILRange = oldEntryPoint.ILRange;
oldEntryPoint.Instructions.ReplaceList(new[] { loopContainer });
if (exitTargetBlock != null)
oldEntryPoint.Instructions.Add(new Branch(exitTargetBlock));
oldEntryPoint.FinalInstruction = new Nop();
MoveBlocksIntoContainer(loop, loopContainer);
// Rewrite branches within the loop from oldEntryPoint to newEntryPoint:
foreach (var branch in loopContainer.Descendants.OfType<Branch>()) {
if (branch.TargetBlock == oldEntryPoint) {
branch.TargetBlock = newEntryPoint;
} else if (branch.TargetBlock == exitTargetBlock) {
branch.ReplaceWith(new Leave(loopContainer) { ILRange = branch.ILRange });
}
}
}
private void MoveBlocksIntoContainer(List<ControlFlowNode> loop, BlockContainer loopContainer)
{
// Move other blocks into the loop body: they're all dominated by the loop header,
// and thus cannot be the target of branch instructions outside the loop.
for (int i = 1; i < loop.Count; i++) {
@ -440,12 +505,56 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -440,12 +505,56 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
Block block = (Block)loop[i].UserData;
Debug.Assert(block.IsDescendantOf(loopContainer));
}
}
private void DetectSwitchBody(Block block, SwitchInstruction switchInst)
{
Debug.Assert(block.Instructions.Last() == switchInst);
ControlFlowNode h = context.ControlFlowNode; // CFG node for our switch head
Debug.Assert(h.UserData == block);
Debug.Assert(!TreeTraversal.PreOrder(h, n => n.DominatorTreeChildren).Any(n => n.Visited));
var nodesInSwitch = new List<ControlFlowNode>();
nodesInSwitch.Add(h);
h.Visited = true;
ExtendLoop(h, nodesInSwitch, out var exitPoint, isSwitch: true);
if (exitPoint != null && exitPoint.Predecessors.Count == 1 && !context.ControlFlowGraph.HasReachableExit(exitPoint)) {
// If the exit point is reachable from just one single "break;",
// it's better to move the code into the switch.
// (unlike loops which should not be nested unless necessary,
// nesting switches makes it clearer in which cases a piece of code is reachable)
nodesInSwitch.AddRange(TreeTraversal.PreOrder(exitPoint, p => p.DominatorTreeChildren));
exitPoint = null;
}
context.Step("Create BlockContainer for switch", switchInst);
// Sort blocks in the loop in reverse post-order to make the output look a bit nicer.
// (if the loop doesn't contain nested loops, this is a topological sort)
nodesInSwitch.Sort((a, b) => b.PostOrderNumber.CompareTo(a.PostOrderNumber));
Debug.Assert(nodesInSwitch[0] == h);
foreach (var node in nodesInSwitch) {
node.Visited = false; // reset visited flag so that we can find outer loops
Debug.Assert(h.Dominates(node) || !node.IsReachable, "The switch body must be dominated by the switch head");
}
BlockContainer switchContainer = new BlockContainer();
Block newEntryPoint = new Block();
newEntryPoint.ILRange = switchInst.ILRange;
switchContainer.Blocks.Add(newEntryPoint);
newEntryPoint.Instructions.Add(switchInst);
block.Instructions[block.Instructions.Count - 1] = switchContainer;
Block exitTargetBlock = (Block)exitPoint?.UserData;
if (exitTargetBlock != null) {
block.Instructions.Add(new Branch(exitTargetBlock));
}
MoveBlocksIntoContainer(nodesInSwitch, switchContainer);
// Rewrite branches within the loop from oldEntryPoint to newEntryPoint:
foreach (var branch in loopContainer.Descendants.OfType<Branch>()) {
if (branch.TargetBlock == oldEntryPoint) {
branch.TargetBlock = newEntryPoint;
} else if (branch.TargetBlock == exitTargetBlock) {
branch.ReplaceWith(new Leave(loopContainer) { ILRange = branch.ILRange });
foreach (var branch in switchContainer.Descendants.OfType<Branch>()) {
if (branch.TargetBlock == exitTargetBlock) {
branch.ReplaceWith(new Leave(switchContainer) { ILRange = branch.ILRange });
}
}
}

4
ICSharpCode.Decompiler/IL/ControlFlow/StateRangeAnalysis.cs

@ -140,7 +140,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -140,7 +140,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
SymbolicValue val = evalContext.Eval(switchInst.Value);
if (val.Type != SymbolicValueType.State)
goto default;
List<LongInterval> allSectionLabels = new List<LongInterval>();
List<LongInterval> exitIntervals = new List<LongInterval>();
foreach (var section in switchInst.Sections) {
// switch (state + Constant)
@ -148,12 +147,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -148,12 +147,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// iff (state + Constant == value)
// iff (state == value - Constant)
var effectiveLabels = section.Labels.AddOffset(unchecked(-val.Constant));
allSectionLabels.AddRange(effectiveLabels.Intervals);
var result = AssignStateRanges(section.Body, stateRange.IntersectWith(effectiveLabels));
exitIntervals.AddRange(result.Intervals);
}
var defaultSectionLabels = stateRange.ExceptWith(new LongSet(allSectionLabels));
exitIntervals.AddRange(AssignStateRanges(switchInst.DefaultBody, defaultSectionLabels).Intervals);
// exitIntervals = union of exits of all sections
return new LongSet(exitIntervals);
case IfInstruction ifInst:

61
ICSharpCode.Decompiler/IL/ControlFlow/SwitchAnalysis.cs

@ -42,8 +42,18 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -42,8 +42,18 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// </summary>
public readonly List<KeyValuePair<LongSet, ILInstruction>> Sections = new List<KeyValuePair<LongSet, ILInstruction>>();
/// <summary>
/// Used to de-duplicate sections with a branch instruction.
/// Invariant: (Sections[targetBlockToSectionIndex[branch.TargetBlock]].Instruction as Branch).TargetBlock == branch.TargetBlock
/// </summary>
readonly Dictionary<Block, int> targetBlockToSectionIndex = new Dictionary<Block, int>();
/// <summary>
/// Used to de-duplicate sections with a value-less leave instruction.
/// Invariant: (Sections[targetBlockToSectionIndex[leave.TargetContainer]].Instruction as Leave).TargetContainer == leave.TargetContainer
/// </summary>
readonly Dictionary<BlockContainer, int> targetContainerToSectionIndex = new Dictionary<BlockContainer, int>();
/// <summary>
/// Blocks that can be deleted if the tail of the initial block is replaced with a switch instruction.
/// </summary>
@ -61,6 +71,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -61,6 +71,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
switchVar = null;
rootBlock = block;
targetBlockToSectionIndex.Clear();
targetContainerToSectionIndex.Clear();
Sections.Clear();
InnerBlocks.Clear();
ContainsILSwitch = false;
@ -83,25 +94,26 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -83,25 +94,26 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// If false, analyze the whole block.</param>
bool AnalyzeBlock(Block block, LongSet inputValues, bool tailOnly = false)
{
if (block.Instructions.Count == 0) {
// might happen if the block was already marked for deletion in SwitchDetection
return false;
}
if (tailOnly) {
Debug.Assert(block == rootBlock);
if (block.Instructions.Count < 2)
return false;
} else {
Debug.Assert(switchVar != null); // switchVar should always be determined by the top-level call
if (block.IncomingEdgeCount != 1 || block == rootBlock)
return false; // for now, let's only consider if-structures that form a tree
if (block.Instructions.Count != 2)
return false;
if (block.Parent != rootBlock.Parent)
return false; // all blocks should belong to the same container
}
var inst = block.Instructions[block.Instructions.Count - 2];
ILInstruction condition, trueInst;
LongSet trueValues;
if (inst.MatchIfInstruction(out condition, out trueInst)
if (block.Instructions.Count >= 2
&& block.Instructions[block.Instructions.Count - 2].MatchIfInstruction(out var condition, out var trueInst)
&& AnalyzeCondition(condition, out trueValues)
) {
if (!(tailOnly || block.Instructions.Count == 2))
return false;
trueValues = trueValues.IntersectWith(inputValues);
Block trueBlock;
if (trueInst.MatchBranch(out trueBlock) && AnalyzeBlock(trueBlock, trueValues)) {
@ -111,9 +123,12 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -111,9 +123,12 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// Create switch section for trueInst.
AddSection(trueValues, trueInst);
}
} else if (inst.OpCode == OpCode.SwitchInstruction) {
if (AnalyzeSwitch((SwitchInstruction)inst, inputValues, out trueValues)) {
} else if (block.Instructions.Last() is SwitchInstruction switchInst) {
if (!(tailOnly || block.Instructions.Count == 1))
return false;
if (AnalyzeSwitch(switchInst, inputValues)) {
ContainsILSwitch = true; // OK
return true;
} else { // switch analysis failed (e.g. switchVar mismatch)
return false;
}
@ -134,10 +149,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -134,10 +149,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
return true;
}
private bool AnalyzeSwitch(SwitchInstruction inst, LongSet inputValues, out LongSet anyMatchValues)
private bool AnalyzeSwitch(SwitchInstruction inst, LongSet inputValues)
{
Debug.Assert(inst.DefaultBody is Nop);
anyMatchValues = LongSet.Empty;
Debug.Assert(!inst.IsLifted);
long offset;
if (MatchSwitchVar(inst.Value)) {
offset = 0;
@ -163,8 +177,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -163,8 +177,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
foreach (var section in inst.Sections) {
var matchValues = section.Labels.AddOffset(offset).IntersectWith(inputValues);
AddSection(matchValues, section.Body);
anyMatchValues = anyMatchValues.UnionWith(matchValues);
if (matchValues.Count() > 1 && section.Body.MatchBranch(out var targetBlock) && AnalyzeBlock(targetBlock, matchValues)) {
InnerBlocks.Add(targetBlock);
} else {
AddSection(matchValues, section.Body);
}
}
return true;
}
@ -180,10 +197,8 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -180,10 +197,8 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
if (values.IsEmpty) {
return;
}
Block targetBlock;
if (inst.MatchBranch(out targetBlock)) {
int index;
if (targetBlockToSectionIndex.TryGetValue(targetBlock, out index)) {
if (inst.MatchBranch(out Block targetBlock)) {
if (targetBlockToSectionIndex.TryGetValue(targetBlock, out int index)) {
Sections[index] = new KeyValuePair<LongSet, ILInstruction>(
Sections[index].Key.UnionWith(values),
inst
@ -192,6 +207,16 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -192,6 +207,16 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
targetBlockToSectionIndex.Add(targetBlock, Sections.Count);
Sections.Add(new KeyValuePair<LongSet, ILInstruction>(values, inst));
}
} else if (inst.MatchLeave(out BlockContainer targetContainer)) {
if (targetContainerToSectionIndex.TryGetValue(targetContainer, out int index)) {
Sections[index] = new KeyValuePair<LongSet, ILInstruction>(
Sections[index].Key.UnionWith(values),
inst
);
} else {
targetContainerToSectionIndex.Add(targetContainer, Sections.Count);
Sections.Add(new KeyValuePair<LongSet, ILInstruction>(values, inst));
}
} else {
Sections.Add(new KeyValuePair<LongSet, ILInstruction>(values, inst));
}

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

@ -60,16 +60,20 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -60,16 +60,20 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
var sw = new SwitchInstruction(new LdLoc(analysis.SwitchVariable));
foreach (var section in analysis.Sections) {
if (!section.Key.SetEquals(defaultSection.Key)) {
sw.Sections.Add(new SwitchSection
{
Labels = section.Key,
Body = section.Value
});
}
sw.Sections.Add(new SwitchSection {
Labels = section.Key,
Body = section.Value
});
}
if (block.Instructions.Last() is SwitchInstruction) {
// we'll replace the switch
} else {
Debug.Assert(block.Instructions.SecondToLastOrDefault() is IfInstruction);
// Remove branch/leave after if; it's getting moved into a section.
block.Instructions.RemoveAt(block.Instructions.Count - 1);
}
block.Instructions[block.Instructions.Count - 2] = sw;
block.Instructions[block.Instructions.Count - 1] = defaultSection.Value;
block.Instructions[block.Instructions.Count - 1] = sw;
// mark all inner blocks that were converted to the switch statement for deletion
foreach (var innerBlock in analysis.InnerBlocks) {
Debug.Assert(innerBlock.Parent == block.Parent);
@ -77,6 +81,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -77,6 +81,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
innerBlock.Instructions.Clear();
}
blockContainerNeedsCleanup = true;
SortSwitchSections(sw);
} else {
// 2nd pass of SimplifySwitchInstruction (after duplicating return blocks),
// (1st pass was in ControlFlowSimplification)
@ -87,8 +92,8 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -87,8 +92,8 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
internal static void SimplifySwitchInstruction(Block block)
{
// due to our of of basic blocks at this point,
// switch instructions can only appear as second-to-last insturction
var sw = block.Instructions.SecondToLastOrDefault() as SwitchInstruction;
// switch instructions can only appear as last insturction
var sw = block.Instructions.LastOrDefault() as SwitchInstruction;
if (sw == null)
return;
@ -96,19 +101,13 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -96,19 +101,13 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// Any switch instructions will only have branch instructions in the sections.
// Combine sections with identical branch target:
block.Instructions.Last().MatchBranch(out Block defaultTarget);
var dict = new Dictionary<Block, SwitchSection>(); // branch target -> switch section
sw.Sections.RemoveAll(
section => {
Block target;
if (section.Body.MatchBranch(out target)) {
SwitchSection primarySection;
if (target == defaultTarget) {
// This section is just an alternative for 'default'.
Debug.Assert(sw.DefaultBody is Nop);
return true; // remove this section
} else if (dict.TryGetValue(target, out primarySection)) {
if (section.Body.MatchBranch(out Block target)) {
if (dict.TryGetValue(target, out SwitchSection primarySection)) {
primarySection.Labels = primarySection.Labels.UnionWith(section.Labels);
primarySection.HasNullLabel |= section.HasNullLabel;
return true; // remove this section
} else {
dict.Add(target, section);
@ -117,6 +116,12 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -117,6 +116,12 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
return false;
});
AdjustLabels(sw);
SortSwitchSections(sw);
}
static void SortSwitchSections(SwitchInstruction sw)
{
sw.Sections.ReplaceList(sw.Sections.OrderBy(s => (s.Body as Branch)?.TargetILOffset).ThenBy(s => s.Labels.Values.FirstOrDefault()));
}
static void AdjustLabels(SwitchInstruction sw)

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

@ -105,7 +105,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -105,7 +105,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
if (!context.Settings.YieldReturn)
return; // abort if enumerator decompilation is disabled
this.context = context;
this.currentType = function.Method.DeclaringType;
this.currentType = function.CecilMethod.DeclaringType;
this.enumeratorType = null;
this.enumeratorCtor = null;
this.stateField = null;

28
ICSharpCode.Decompiler/IL/DetectedLoop.cs

@ -76,7 +76,7 @@ namespace ICSharpCode.Decompiler.IL @@ -76,7 +76,7 @@ namespace ICSharpCode.Decompiler.IL
conditions.Add(ifInst.Condition);
i--;
}
if (i == -1) {
if (i == -1 && conditions.Any()) {
return b;
}
}
@ -116,10 +116,10 @@ namespace ICSharpCode.Decompiler.IL @@ -116,10 +116,10 @@ namespace ICSharpCode.Decompiler.IL
AdditionalBlocks = Container.Blocks.Skip(1).Where(b => b != IncrementBlock).ToArray();
return this;
} else if (trueInst is Block block) {
var variable = GetVariableFromCondition(conditionInst);
var last = block.Instructions.LastOrDefault();
var secondToLast = block.Instructions.SecondToLastOrDefault();
if (variable != null && last != null && secondToLast != null && last.MatchBranch(Container.EntryPoint) && MatchIncrement(secondToLast, variable)) {
if (last != null && secondToLast != null && last.MatchBranch(Container.EntryPoint) &&
MatchIncrement(secondToLast, out var variable) && conditionInst.Children.Any(c => c.MatchLdLoc(variable))) {
Kind = LoopKind.For;
IncrementTarget = variable;
AdditionalBlocks = Container.Blocks.Skip(1).ToArray();
@ -144,22 +144,16 @@ namespace ICSharpCode.Decompiler.IL @@ -144,22 +144,16 @@ namespace ICSharpCode.Decompiler.IL
return this;
}
static ILVariable GetVariableFromCondition(ILInstruction conditionInst)
static bool MatchIncrement(ILInstruction inst, out ILVariable variable)
{
var ldLocs = conditionInst.Children.OfType<LdLoc>().ToArray();
if (ldLocs.Length == 1)
return ldLocs[0].Variable;
else
return null;
}
static bool MatchIncrement(ILInstruction inst, ILVariable variable)
{
if (!inst.MatchStLoc(variable, out var value))
if (!inst.MatchStLoc(out variable, out var value))
return false;
if (!value.MatchBinaryNumericInstruction(BinaryNumericOperator.Add, out var left, out var right))
return false;
return left.MatchLdLoc(variable) && right.MatchLdcI(out var val) && val == 1;
if (!value.MatchBinaryNumericInstruction(BinaryNumericOperator.Add, out var left, out var right)) {
if (value is CompoundAssignmentInstruction cai) {
left = cai.Target;
} else return false;
}
return left.MatchLdLoc(variable);
}
}
}

81
ICSharpCode.Decompiler/IL/ILAstWritingOptions.cs

@ -0,0 +1,81 @@ @@ -0,0 +1,81 @@
// Copyright (c) 2017 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.
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace ICSharpCode.Decompiler.IL
{
public class ILAstWritingOptions : INotifyPropertyChanged
{
private bool useLogicOperationSugar;
private bool useFieldSugar;
private bool showILRanges;
/// <summary>
/// Sugar for logic.not/and/or.
/// </summary>
public bool UseLogicOperationSugar {
get { return useLogicOperationSugar; }
set {
if (useLogicOperationSugar != value) {
useLogicOperationSugar = value;
OnPropertyChanged();
}
}
}
/// <summary>
/// Sugar for ldfld/stfld.
/// </summary>
public bool UseFieldSugar {
get { return useFieldSugar; }
set {
if (useFieldSugar != value) {
useFieldSugar = value;
OnPropertyChanged();
}
}
}
/// <summary>
/// Show IL ranges in ILAst output.
/// </summary>
public bool ShowILRanges {
get { return showILRanges; }
set {
if (showILRanges != value) {
showILRanges = value;
OnPropertyChanged();
}
}
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChanged?.Invoke(this, e);
}
public event PropertyChangedEventHandler PropertyChanged;
}
}

74
ICSharpCode.Decompiler/IL/ILReader.cs

@ -304,7 +304,8 @@ namespace ICSharpCode.Decompiler.IL @@ -304,7 +304,8 @@ namespace ICSharpCode.Decompiler.IL
inst.WriteTo(output, new ILAstWritingOptions());
output.WriteLine();
}
new Disassembler.MethodBodyDisassembler(output, false, cancellationToken).WriteExceptionHandlers(body);
new Disassembler.MethodBodyDisassembler(output, cancellationToken) { DetectControlStructure = false }
.WriteExceptionHandlers(body);
}
/// <summary>
@ -317,7 +318,8 @@ namespace ICSharpCode.Decompiler.IL @@ -317,7 +318,8 @@ namespace ICSharpCode.Decompiler.IL
ReadInstructions(cancellationToken);
var blockBuilder = new BlockBuilder(body, typeSystem, variableByExceptionHandler);
blockBuilder.CreateBlocks(mainContainer, instructionBuilder, isBranchTarget, cancellationToken);
var function = new ILFunction(body.Method, mainContainer);
var method = typeSystem.Resolve(body.Method);
var function = new ILFunction(method, body.Method, mainContainer);
CollectionExtensions.AddRange(function.Variables, parameterVariables);
CollectionExtensions.AddRange(function.Variables, localVariables);
CollectionExtensions.AddRange(function.Variables, stackVariables);
@ -443,7 +445,7 @@ namespace ICSharpCode.Decompiler.IL @@ -443,7 +445,7 @@ namespace ICSharpCode.Decompiler.IL
case Cil.Code.Callvirt:
return DecodeCall(OpCode.CallVirt);
case Cil.Code.Calli:
throw new NotImplementedException();
return DecodeCallIndirect();
case Cil.Code.Ceq:
return Push(Comparison(ComparisonKind.Equality));
case Cil.Code.Cgt:
@ -523,7 +525,7 @@ namespace ICSharpCode.Decompiler.IL @@ -523,7 +525,7 @@ namespace ICSharpCode.Decompiler.IL
case Cil.Code.Conv_Ovf_U_Un:
return Push(new Conv(Pop(), PrimitiveType.U, true, Sign.Unsigned));
case Cil.Code.Cpblk:
throw new NotImplementedException();
return new Cpblk(size: Pop(StackType.I4), sourceAddress: PopPointer(), destAddress: PopPointer());
case Cil.Code.Div:
return BinaryNumeric(BinaryNumericOperator.Div, false, Sign.Signed);
case Cil.Code.Div_Un:
@ -535,9 +537,9 @@ namespace ICSharpCode.Decompiler.IL @@ -535,9 +537,9 @@ namespace ICSharpCode.Decompiler.IL
case Cil.Code.Endfinally:
return new Leave(null);
case Cil.Code.Initblk:
throw new NotImplementedException();
return new Initblk(size: Pop(StackType.I4), value: Pop(StackType.I4), address: PopPointer());
case Cil.Code.Jmp:
throw new NotImplementedException();
return DecodeJmp();
case Cil.Code.Ldarg:
case Cil.Code.Ldarg_S:
return Push(Ldarg(((ParameterDefinition)cecilInst.Operand).Sequence));
@ -648,7 +650,7 @@ namespace ICSharpCode.Decompiler.IL @@ -648,7 +650,7 @@ namespace ICSharpCode.Decompiler.IL
return BinaryNumeric(BinaryNumericOperator.BitOr);
case Cil.Code.Pop:
Pop();
return new Nop();
return new Nop() { Kind = NopKind.Pop };
case Cil.Code.Rem:
return BinaryNumeric(BinaryNumericOperator.Rem, false, Sign.Signed);
case Cil.Code.Rem_Un:
@ -916,9 +918,16 @@ namespace ICSharpCode.Decompiler.IL @@ -916,9 +918,16 @@ namespace ICSharpCode.Decompiler.IL
ILInstruction inst = Pop();
if (expectedType != inst.ResultType) {
if (expectedType == StackType.I && inst.ResultType == StackType.I4) {
// IL allows implicit I4->I conversions
inst = new Conv(inst, PrimitiveType.I, false, Sign.None);
} else if (expectedType == StackType.I4 && inst.ResultType == StackType.I) {
// C++/CLI also sometimes implicitly converts in the other direction:
inst = new Conv(inst, PrimitiveType.I4, false, Sign.None);
} else if (expectedType == StackType.Ref && inst.ResultType == StackType.I) {
// implicitly start GC tracking
} else if (expectedType == StackType.I && inst.ResultType == StackType.Ref) {
// Implicitly stop GC tracking; this occurs when passing the result of 'ldloca' or 'ldsflda'
// to a method expecting a native pointer.
} else if (inst is InvalidExpression) {
((InvalidExpression)inst).ExpectedResultType = expectedType;
} else {
@ -1002,7 +1011,9 @@ namespace ICSharpCode.Decompiler.IL @@ -1002,7 +1011,9 @@ namespace ICSharpCode.Decompiler.IL
ILInstruction InitObj(ILInstruction target, IType type)
{
return new StObj(target, new DefaultValue(type), type);
var value = new DefaultValue(type);
value.ILStackWasEmpty = currentStack.IsEmpty;
return new StObj(target, value, type);
}
private ILInstruction DecodeConstrainedCall()
@ -1105,7 +1116,31 @@ namespace ICSharpCode.Decompiler.IL @@ -1105,7 +1116,31 @@ namespace ICSharpCode.Decompiler.IL
return call;
}
}
ILInstruction DecodeCallIndirect()
{
var functionPointer = Pop(StackType.I);
var signature = (CallSite)currentInstruction.Operand;
Debug.Assert(!signature.HasThis);
var parameterTypes = new IType[signature.Parameters.Count];
var arguments = new ILInstruction[parameterTypes.Length];
for (int i = signature.Parameters.Count - 1; i >= 0; i--) {
parameterTypes[i] = typeSystem.Resolve(signature.Parameters[i].ParameterType);
arguments[i] = Pop(parameterTypes[i].GetStackType());
}
var call = new CallIndirect(
signature.CallingConvention,
typeSystem.Resolve(signature.ReturnType),
parameterTypes.ToImmutableArray(),
arguments,
functionPointer
);
if (call.ResultType != StackType.Void)
return Push(call);
else
return call;
}
static int GetPopCount(OpCode callCode, MethodReference methodReference)
{
int popCount = methodReference.Parameters.Count;
@ -1232,7 +1267,10 @@ namespace ICSharpCode.Decompiler.IL @@ -1232,7 +1267,10 @@ namespace ICSharpCode.Decompiler.IL
section.Body = new Branch(target);
instr.Sections.Add(section);
}
var defaultSection = new SwitchSection();
defaultSection.Labels = new LongSet(new LongInterval(0, labels.Length)).Invert();
defaultSection.Body = new Nop();
instr.Sections.Add(defaultSection);
return instr;
}
@ -1251,6 +1289,22 @@ namespace ICSharpCode.Decompiler.IL @@ -1251,6 +1289,22 @@ namespace ICSharpCode.Decompiler.IL
return Push(new BinaryNumericInstruction(@operator, left, right, checkForOverflow, sign));
}
ILInstruction DecodeJmp()
{
IMethod method = ReadAndDecodeMethodReference();
// Translate jmp into tail call:
Call call = new Call(method);
call.IsTail = true;
call.ILStackWasEmpty = true;
if (!method.IsStatic) {
call.Arguments.Add(Ldarg(0));
}
foreach (var p in method.Parameters) {
call.Arguments.Add(Ldarg(call.Arguments.Count));
}
return new Leave(mainContainer, call);
}
ILInstruction LdToken(IMetadataTokenProvider token)
{
if (token is TypeReference)

11
ICSharpCode.Decompiler/IL/InstructionOutputExtensions.cs

@ -18,6 +18,7 @@ @@ -18,6 +18,7 @@
using ICSharpCode.Decompiler.Disassembler;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.IL
{
@ -50,5 +51,15 @@ namespace ICSharpCode.Decompiler.IL @@ -50,5 +51,15 @@ namespace ICSharpCode.Decompiler.IL
else
output.WriteReference(symbol.Name, symbol);
}
public static void WriteTo(this Interval interval, ITextOutput output, ILAstWritingOptions options)
{
if (!options.ShowILRanges)
return;
if (interval.IsEmpty)
output.Write("[empty] ");
else
output.Write($"[{interval.Start:x4}..{interval.InclusiveEnd:x4}] ");
}
}
}

524
ICSharpCode.Decompiler/IL/Instructions.cs

@ -83,6 +83,8 @@ namespace ICSharpCode.Decompiler.IL @@ -83,6 +83,8 @@ namespace ICSharpCode.Decompiler.IL
Call,
/// <summary>Virtual method call.</summary>
CallVirt,
/// <summary>Unsafe function pointer call.</summary>
CallIndirect,
/// <summary>Checks that the input float is not NaN or infinite.</summary>
Ckfinite,
/// <summary>Numeric cast.</summary>
@ -121,6 +123,10 @@ namespace ICSharpCode.Decompiler.IL @@ -121,6 +123,10 @@ namespace ICSharpCode.Decompiler.IL
LdMemberToken,
/// <summary>Allocates space in the stack frame</summary>
LocAlloc,
/// <summary>memcpy(destAddress, sourceAddress, size);</summary>
Cpblk,
/// <summary>memset(address, value, size)</summary>
Initblk,
/// <summary>Load address of instance field</summary>
LdFlda,
/// <summary>Load static field address</summary>
@ -158,6 +164,8 @@ namespace ICSharpCode.Decompiler.IL @@ -158,6 +164,8 @@ namespace ICSharpCode.Decompiler.IL
/// <summary>Converts an array pointer (O) to a reference to the first element, or to a null reference if the array is null or empty.
/// Also used to convert a string to a reference to the first character.</summary>
ArrayToPointer,
/// <summary>Maps a string value to an integer. This is used in switch(string).</summary>
StringToInt,
/// <summary>Push a typed reference of type class onto the stack.</summary>
MakeRefAny,
/// <summary>Push the type token stored in a typed reference.</summary>
@ -289,6 +297,7 @@ namespace ICSharpCode.Decompiler.IL @@ -289,6 +297,7 @@ namespace ICSharpCode.Decompiler.IL
}
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
output.Write(OpCode);
output.Write('(');
this.argument.WriteTo(output, options);
@ -381,6 +390,7 @@ namespace ICSharpCode.Decompiler.IL @@ -381,6 +390,7 @@ namespace ICSharpCode.Decompiler.IL
}
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
output.Write(OpCode);
output.Write('(');
this.left.WriteTo(output, options);
@ -808,6 +818,7 @@ namespace ICSharpCode.Decompiler.IL @@ -808,6 +818,7 @@ namespace ICSharpCode.Decompiler.IL
}
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
output.Write(OpCode);
output.Write(' ');
variable.WriteTo(output);
@ -1012,6 +1023,15 @@ namespace ICSharpCode.Decompiler.IL @@ -1012,6 +1023,15 @@ namespace ICSharpCode.Decompiler.IL
public sealed partial class Branch : SimpleInstruction
{
public override StackType ResultType { get { return StackType.Void; } }
protected override InstructionFlags ComputeFlags()
{
return InstructionFlags.EndPointUnreachable | InstructionFlags.MayBranch;
}
public override InstructionFlags DirectFlags {
get {
return InstructionFlags.EndPointUnreachable | InstructionFlags.MayBranch;
}
}
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitBranch(this);
@ -1317,7 +1337,7 @@ namespace ICSharpCode.Decompiler.IL @@ -1317,7 +1337,7 @@ namespace ICSharpCode.Decompiler.IL
protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match)
{
var o = other as SwitchInstruction;
return o != null && Value.PerformMatch(o.Value, ref match) && DefaultBody.PerformMatch(o.DefaultBody, ref match) && Patterns.ListMatch.DoMatch(this.Sections, o.Sections, ref match);
return o != null && IsLifted == o.IsLifted && Value.PerformMatch(o.Value, ref match) && Patterns.ListMatch.DoMatch(this.Sections, o.Sections, ref match);
}
}
}
@ -1389,7 +1409,7 @@ namespace ICSharpCode.Decompiler.IL @@ -1389,7 +1409,7 @@ namespace ICSharpCode.Decompiler.IL
protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match)
{
var o = other as SwitchSection;
return o != null && this.body.PerformMatch(o.body, ref match) && this.Labels.Intervals.SequenceEqual(o.Labels.Intervals);
return o != null && this.body.PerformMatch(o.body, ref match) && this.Labels.SetEquals(o.Labels) && this.HasNullLabel == o.HasNullLabel;
}
}
}
@ -1950,6 +1970,31 @@ namespace ICSharpCode.Decompiler.IL @@ -1950,6 +1970,31 @@ namespace ICSharpCode.Decompiler.IL
}
}
namespace ICSharpCode.Decompiler.IL
{
/// <summary>Unsafe function pointer call.</summary>
public sealed partial class CallIndirect : ILInstruction
{
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitCallIndirect(this);
}
public override T AcceptVisitor<T>(ILVisitor<T> visitor)
{
return visitor.VisitCallIndirect(this);
}
public override T AcceptVisitor<C, T>(ILVisitor<C, T> visitor, C context)
{
return visitor.VisitCallIndirect(this, context);
}
protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match)
{
var o = other as CallIndirect;
return o != null && EqualSignature(o) && Patterns.ListMatch.DoMatch(this.Arguments, o.Arguments, ref match) && this.FunctionPointer.PerformMatch(o.FunctionPointer, ref match);
}
}
}
namespace ICSharpCode.Decompiler.IL
{
/// <summary>Checks that the input float is not NaN or infinite.</summary>
public sealed partial class Ckfinite : UnaryInstruction
@ -2071,6 +2116,7 @@ namespace ICSharpCode.Decompiler.IL @@ -2071,6 +2116,7 @@ namespace ICSharpCode.Decompiler.IL
}
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
output.Write(OpCode);
output.Write(' ');
variable.WriteTo(output);
@ -2145,6 +2191,7 @@ namespace ICSharpCode.Decompiler.IL @@ -2145,6 +2191,7 @@ namespace ICSharpCode.Decompiler.IL
}
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
output.Write(OpCode);
output.Write(' ');
variable.WriteTo(output);
@ -2276,6 +2323,7 @@ namespace ICSharpCode.Decompiler.IL @@ -2276,6 +2323,7 @@ namespace ICSharpCode.Decompiler.IL
}
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
output.Write(OpCode);
output.Write(' ');
variable.WriteTo(output);
@ -2370,6 +2418,7 @@ namespace ICSharpCode.Decompiler.IL @@ -2370,6 +2418,7 @@ namespace ICSharpCode.Decompiler.IL
}
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
output.Write(OpCode);
output.Write('(');
this.value.WriteTo(output, options);
@ -2463,6 +2512,7 @@ namespace ICSharpCode.Decompiler.IL @@ -2463,6 +2512,7 @@ namespace ICSharpCode.Decompiler.IL
public override StackType ResultType { get { return StackType.O; } }
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
output.Write(OpCode);
output.Write(' ');
Disassembler.DisassemblerHelpers.WriteOperand(output, Value);
@ -2499,6 +2549,7 @@ namespace ICSharpCode.Decompiler.IL @@ -2499,6 +2549,7 @@ namespace ICSharpCode.Decompiler.IL
public override StackType ResultType { get { return StackType.I4; } }
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
output.Write(OpCode);
output.Write(' ');
Disassembler.DisassemblerHelpers.WriteOperand(output, Value);
@ -2535,6 +2586,7 @@ namespace ICSharpCode.Decompiler.IL @@ -2535,6 +2586,7 @@ namespace ICSharpCode.Decompiler.IL
public override StackType ResultType { get { return StackType.I8; } }
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
output.Write(OpCode);
output.Write(' ');
Disassembler.DisassemblerHelpers.WriteOperand(output, Value);
@ -2571,6 +2623,7 @@ namespace ICSharpCode.Decompiler.IL @@ -2571,6 +2623,7 @@ namespace ICSharpCode.Decompiler.IL
public override StackType ResultType { get { return StackType.F; } }
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
output.Write(OpCode);
output.Write(' ');
Disassembler.DisassemblerHelpers.WriteOperand(output, Value);
@ -2607,6 +2660,7 @@ namespace ICSharpCode.Decompiler.IL @@ -2607,6 +2660,7 @@ namespace ICSharpCode.Decompiler.IL
public override StackType ResultType { get { return StackType.O; } }
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
output.Write(OpCode);
output.Write(' ');
Disassembler.DisassemblerHelpers.WriteOperand(output, Value);
@ -2673,6 +2727,7 @@ namespace ICSharpCode.Decompiler.IL @@ -2673,6 +2727,7 @@ namespace ICSharpCode.Decompiler.IL
public override StackType ResultType { get { return StackType.I; } }
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
output.Write(OpCode);
output.Write(' ');
method.WriteTo(output);
@ -2720,6 +2775,7 @@ namespace ICSharpCode.Decompiler.IL @@ -2720,6 +2775,7 @@ namespace ICSharpCode.Decompiler.IL
}
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
output.Write(OpCode);
output.Write(' ');
method.WriteTo(output);
@ -2761,6 +2817,7 @@ namespace ICSharpCode.Decompiler.IL @@ -2761,6 +2817,7 @@ namespace ICSharpCode.Decompiler.IL
public override StackType ResultType { get { return StackType.O; } }
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
output.Write(OpCode);
output.Write(' ');
type.WriteTo(output);
@ -2799,6 +2856,7 @@ namespace ICSharpCode.Decompiler.IL @@ -2799,6 +2856,7 @@ namespace ICSharpCode.Decompiler.IL
public override StackType ResultType { get { return StackType.O; } }
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
output.Write(OpCode);
output.Write(' ');
member.WriteTo(output);
@ -2860,6 +2918,288 @@ namespace ICSharpCode.Decompiler.IL @@ -2860,6 +2918,288 @@ namespace ICSharpCode.Decompiler.IL
}
}
namespace ICSharpCode.Decompiler.IL
{
/// <summary>memcpy(destAddress, sourceAddress, size);</summary>
public sealed partial class Cpblk : ILInstruction, ISupportsVolatilePrefix, ISupportsUnalignedPrefix
{
public Cpblk(ILInstruction destAddress, ILInstruction sourceAddress, ILInstruction size) : base(OpCode.Cpblk)
{
this.DestAddress = destAddress;
this.SourceAddress = sourceAddress;
this.Size = size;
}
public static readonly SlotInfo DestAddressSlot = new SlotInfo("DestAddress", canInlineInto: true);
ILInstruction destAddress;
public ILInstruction DestAddress {
get { return this.destAddress; }
set {
ValidateChild(value);
SetChildInstruction(ref this.destAddress, value, 0);
}
}
public static readonly SlotInfo SourceAddressSlot = new SlotInfo("SourceAddress", canInlineInto: true);
ILInstruction sourceAddress;
public ILInstruction SourceAddress {
get { return this.sourceAddress; }
set {
ValidateChild(value);
SetChildInstruction(ref this.sourceAddress, value, 1);
}
}
public static readonly SlotInfo SizeSlot = new SlotInfo("Size", canInlineInto: true);
ILInstruction size;
public ILInstruction Size {
get { return this.size; }
set {
ValidateChild(value);
SetChildInstruction(ref this.size, value, 2);
}
}
protected sealed override int GetChildCount()
{
return 3;
}
protected sealed override ILInstruction GetChild(int index)
{
switch (index) {
case 0:
return this.destAddress;
case 1:
return this.sourceAddress;
case 2:
return this.size;
default:
throw new IndexOutOfRangeException();
}
}
protected sealed override void SetChild(int index, ILInstruction value)
{
switch (index) {
case 0:
this.DestAddress = value;
break;
case 1:
this.SourceAddress = value;
break;
case 2:
this.Size = value;
break;
default:
throw new IndexOutOfRangeException();
}
}
protected sealed override SlotInfo GetChildSlot(int index)
{
switch (index) {
case 0:
return DestAddressSlot;
case 1:
return SourceAddressSlot;
case 2:
return SizeSlot;
default:
throw new IndexOutOfRangeException();
}
}
public sealed override ILInstruction Clone()
{
var clone = (Cpblk)ShallowClone();
clone.DestAddress = this.destAddress.Clone();
clone.SourceAddress = this.sourceAddress.Clone();
clone.Size = this.size.Clone();
return clone;
}
/// <summary>Gets/Sets whether the memory access is volatile.</summary>
public bool IsVolatile { get; set; }
/// <summary>Returns the alignment specified by the 'unaligned' prefix; or 0 if there was no 'unaligned' prefix.</summary>
public byte UnalignedPrefix { get; set; }
public override StackType ResultType { get { return StackType.Void; } }
protected override InstructionFlags ComputeFlags()
{
return destAddress.Flags | sourceAddress.Flags | size.Flags | InstructionFlags.MayThrow | InstructionFlags.SideEffect;
}
public override InstructionFlags DirectFlags {
get {
return InstructionFlags.MayThrow | InstructionFlags.SideEffect;
}
}
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
if (IsVolatile)
output.Write("volatile.");
if (UnalignedPrefix > 0)
output.Write("unaligned(" + UnalignedPrefix + ").");
output.Write(OpCode);
output.Write('(');
this.destAddress.WriteTo(output, options);
output.Write(", ");
this.sourceAddress.WriteTo(output, options);
output.Write(", ");
this.size.WriteTo(output, options);
output.Write(')');
}
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitCpblk(this);
}
public override T AcceptVisitor<T>(ILVisitor<T> visitor)
{
return visitor.VisitCpblk(this);
}
public override T AcceptVisitor<C, T>(ILVisitor<C, T> visitor, C context)
{
return visitor.VisitCpblk(this, context);
}
protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match)
{
var o = other as Cpblk;
return o != null && this.destAddress.PerformMatch(o.destAddress, ref match) && this.sourceAddress.PerformMatch(o.sourceAddress, ref match) && this.size.PerformMatch(o.size, ref match) && IsVolatile == o.IsVolatile && UnalignedPrefix == o.UnalignedPrefix;
}
}
}
namespace ICSharpCode.Decompiler.IL
{
/// <summary>memset(address, value, size)</summary>
public sealed partial class Initblk : ILInstruction, ISupportsVolatilePrefix, ISupportsUnalignedPrefix
{
public Initblk(ILInstruction address, ILInstruction value, ILInstruction size) : base(OpCode.Initblk)
{
this.Address = address;
this.Value = value;
this.Size = size;
}
public static readonly SlotInfo AddressSlot = new SlotInfo("Address", canInlineInto: true);
ILInstruction address;
public ILInstruction Address {
get { return this.address; }
set {
ValidateChild(value);
SetChildInstruction(ref this.address, value, 0);
}
}
public static readonly SlotInfo ValueSlot = new SlotInfo("Value", canInlineInto: true);
ILInstruction value;
public ILInstruction Value {
get { return this.value; }
set {
ValidateChild(value);
SetChildInstruction(ref this.value, value, 1);
}
}
public static readonly SlotInfo SizeSlot = new SlotInfo("Size", canInlineInto: true);
ILInstruction size;
public ILInstruction Size {
get { return this.size; }
set {
ValidateChild(value);
SetChildInstruction(ref this.size, value, 2);
}
}
protected sealed override int GetChildCount()
{
return 3;
}
protected sealed override ILInstruction GetChild(int index)
{
switch (index) {
case 0:
return this.address;
case 1:
return this.value;
case 2:
return this.size;
default:
throw new IndexOutOfRangeException();
}
}
protected sealed override void SetChild(int index, ILInstruction value)
{
switch (index) {
case 0:
this.Address = value;
break;
case 1:
this.Value = value;
break;
case 2:
this.Size = value;
break;
default:
throw new IndexOutOfRangeException();
}
}
protected sealed override SlotInfo GetChildSlot(int index)
{
switch (index) {
case 0:
return AddressSlot;
case 1:
return ValueSlot;
case 2:
return SizeSlot;
default:
throw new IndexOutOfRangeException();
}
}
public sealed override ILInstruction Clone()
{
var clone = (Initblk)ShallowClone();
clone.Address = this.address.Clone();
clone.Value = this.value.Clone();
clone.Size = this.size.Clone();
return clone;
}
/// <summary>Gets/Sets whether the memory access is volatile.</summary>
public bool IsVolatile { get; set; }
/// <summary>Returns the alignment specified by the 'unaligned' prefix; or 0 if there was no 'unaligned' prefix.</summary>
public byte UnalignedPrefix { get; set; }
public override StackType ResultType { get { return StackType.Void; } }
protected override InstructionFlags ComputeFlags()
{
return address.Flags | value.Flags | size.Flags | InstructionFlags.MayThrow | InstructionFlags.SideEffect;
}
public override InstructionFlags DirectFlags {
get {
return InstructionFlags.MayThrow | InstructionFlags.SideEffect;
}
}
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
if (IsVolatile)
output.Write("volatile.");
if (UnalignedPrefix > 0)
output.Write("unaligned(" + UnalignedPrefix + ").");
output.Write(OpCode);
output.Write('(');
this.address.WriteTo(output, options);
output.Write(", ");
this.value.WriteTo(output, options);
output.Write(", ");
this.size.WriteTo(output, options);
output.Write(')');
}
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitInitblk(this);
}
public override T AcceptVisitor<T>(ILVisitor<T> visitor)
{
return visitor.VisitInitblk(this);
}
public override T AcceptVisitor<C, T>(ILVisitor<C, T> visitor, C context)
{
return visitor.VisitInitblk(this, context);
}
protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match)
{
var o = other as Initblk;
return o != null && this.address.PerformMatch(o.address, ref match) && this.value.PerformMatch(o.value, ref match) && this.size.PerformMatch(o.size, ref match) && IsVolatile == o.IsVolatile && UnalignedPrefix == o.UnalignedPrefix;
}
}
}
namespace ICSharpCode.Decompiler.IL
{
/// <summary>Load address of instance field</summary>
public sealed partial class LdFlda : ILInstruction, IInstructionWithFieldOperand
@ -2932,6 +3272,7 @@ namespace ICSharpCode.Decompiler.IL @@ -2932,6 +3272,7 @@ namespace ICSharpCode.Decompiler.IL
}
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
output.Write(OpCode);
output.Write(' ');
field.WriteTo(output);
@ -2973,6 +3314,7 @@ namespace ICSharpCode.Decompiler.IL @@ -2973,6 +3314,7 @@ namespace ICSharpCode.Decompiler.IL
public IField Field { get { return field; } }
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
output.Write(OpCode);
output.Write(' ');
field.WriteTo(output);
@ -3020,6 +3362,7 @@ namespace ICSharpCode.Decompiler.IL @@ -3020,6 +3362,7 @@ namespace ICSharpCode.Decompiler.IL
}
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
output.Write(OpCode);
output.Write(' ');
type.WriteTo(output);
@ -3061,6 +3404,7 @@ namespace ICSharpCode.Decompiler.IL @@ -3061,6 +3404,7 @@ namespace ICSharpCode.Decompiler.IL
public override StackType ResultType { get { return StackType.O; } }
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
output.Write(OpCode);
output.Write(' ');
type.WriteTo(output);
@ -3163,6 +3507,7 @@ namespace ICSharpCode.Decompiler.IL @@ -3163,6 +3507,7 @@ namespace ICSharpCode.Decompiler.IL
}
void OriginalWriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
if (IsVolatile)
output.Write("volatile.");
if (UnalignedPrefix > 0)
@ -3287,6 +3632,7 @@ namespace ICSharpCode.Decompiler.IL @@ -3287,6 +3632,7 @@ namespace ICSharpCode.Decompiler.IL
}
void OriginalWriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
if (IsVolatile)
output.Write("volatile.");
if (UnalignedPrefix > 0)
@ -3343,6 +3689,7 @@ namespace ICSharpCode.Decompiler.IL @@ -3343,6 +3689,7 @@ namespace ICSharpCode.Decompiler.IL
}
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
output.Write(OpCode);
output.Write(' ');
type.WriteTo(output);
@ -3393,6 +3740,7 @@ namespace ICSharpCode.Decompiler.IL @@ -3393,6 +3740,7 @@ namespace ICSharpCode.Decompiler.IL
}
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
output.Write(OpCode);
output.Write(' ');
type.WriteTo(output);
@ -3443,6 +3791,7 @@ namespace ICSharpCode.Decompiler.IL @@ -3443,6 +3791,7 @@ namespace ICSharpCode.Decompiler.IL
}
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
output.Write(OpCode);
output.Write(' ');
type.WriteTo(output);
@ -3553,6 +3902,7 @@ namespace ICSharpCode.Decompiler.IL @@ -3553,6 +3902,7 @@ namespace ICSharpCode.Decompiler.IL
}
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
output.Write(OpCode);
output.Write(' ');
type.WriteTo(output);
@ -3598,6 +3948,7 @@ namespace ICSharpCode.Decompiler.IL @@ -3598,6 +3948,7 @@ namespace ICSharpCode.Decompiler.IL
public override StackType ResultType { get { return type.GetStackType(); } }
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
output.Write(OpCode);
output.Write(' ');
type.WriteTo(output);
@ -3710,6 +4061,7 @@ namespace ICSharpCode.Decompiler.IL @@ -3710,6 +4061,7 @@ namespace ICSharpCode.Decompiler.IL
public override StackType ResultType { get { return StackType.I4; } }
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
output.Write(OpCode);
output.Write(' ');
type.WriteTo(output);
@ -3895,6 +4247,7 @@ namespace ICSharpCode.Decompiler.IL @@ -3895,6 +4247,7 @@ namespace ICSharpCode.Decompiler.IL
}
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
if (IsReadOnly)
output.Write("readonly.");
output.Write(OpCode);
@ -3996,6 +4349,7 @@ namespace ICSharpCode.Decompiler.IL @@ -3996,6 +4349,7 @@ namespace ICSharpCode.Decompiler.IL
}
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
output.Write(OpCode);
output.Write('(');
this.array.WriteTo(output, options);
@ -4021,6 +4375,87 @@ namespace ICSharpCode.Decompiler.IL @@ -4021,6 +4375,87 @@ namespace ICSharpCode.Decompiler.IL
}
}
namespace ICSharpCode.Decompiler.IL
{
/// <summary>Maps a string value to an integer. This is used in switch(string).</summary>
public sealed partial class StringToInt : ILInstruction
{
public static readonly SlotInfo ArgumentSlot = new SlotInfo("Argument", canInlineInto: true);
ILInstruction argument;
public ILInstruction Argument {
get { return this.argument; }
set {
ValidateChild(value);
SetChildInstruction(ref this.argument, value, 0);
}
}
protected sealed override int GetChildCount()
{
return 1;
}
protected sealed override ILInstruction GetChild(int index)
{
switch (index) {
case 0:
return this.argument;
default:
throw new IndexOutOfRangeException();
}
}
protected sealed override void SetChild(int index, ILInstruction value)
{
switch (index) {
case 0:
this.Argument = value;
break;
default:
throw new IndexOutOfRangeException();
}
}
protected sealed override SlotInfo GetChildSlot(int index)
{
switch (index) {
case 0:
return ArgumentSlot;
default:
throw new IndexOutOfRangeException();
}
}
public sealed override ILInstruction Clone()
{
var clone = (StringToInt)ShallowClone();
clone.Argument = this.argument.Clone();
return clone;
}
public override StackType ResultType { get { return StackType.I4; } }
protected override InstructionFlags ComputeFlags()
{
return argument.Flags;
}
public override InstructionFlags DirectFlags {
get {
return InstructionFlags.None;
}
}
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitStringToInt(this);
}
public override T AcceptVisitor<T>(ILVisitor<T> visitor)
{
return visitor.VisitStringToInt(this);
}
public override T AcceptVisitor<C, T>(ILVisitor<C, T> visitor, C context)
{
return visitor.VisitStringToInt(this, context);
}
protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match)
{
var o = other as StringToInt;
return o != null && this.argument.PerformMatch(o.argument, ref match);
}
}
}
namespace ICSharpCode.Decompiler.IL
{
/// <summary>Push a typed reference of type class onto the stack.</summary>
public sealed partial class MakeRefAny : UnaryInstruction
@ -4035,6 +4470,7 @@ namespace ICSharpCode.Decompiler.IL @@ -4035,6 +4470,7 @@ namespace ICSharpCode.Decompiler.IL
public override StackType ResultType { get { return StackType.O; } }
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
output.Write(OpCode);
output.Write(' ');
type.WriteTo(output);
@ -4113,6 +4549,7 @@ namespace ICSharpCode.Decompiler.IL @@ -4113,6 +4549,7 @@ namespace ICSharpCode.Decompiler.IL
}
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
output.Write(OpCode);
output.Write(' ');
type.WriteTo(output);
@ -4207,6 +4644,7 @@ namespace ICSharpCode.Decompiler.IL @@ -4207,6 +4644,7 @@ namespace ICSharpCode.Decompiler.IL
}
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
output.Write(OpCode);
output.Write('(');
this.value.WriteTo(output, options);
@ -4299,6 +4737,7 @@ namespace ICSharpCode.Decompiler.IL @@ -4299,6 +4737,7 @@ namespace ICSharpCode.Decompiler.IL
}
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
output.Write(OpCode);
output.Write('(');
this.value.WriteTo(output, options);
@ -4360,6 +4799,7 @@ namespace ICSharpCode.Decompiler.IL.Patterns @@ -4360,6 +4799,7 @@ namespace ICSharpCode.Decompiler.IL.Patterns
}
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
ILRange.WriteTo(output, options);
output.Write(OpCode);
output.Write('(');
output.Write(')');
@ -4485,6 +4925,10 @@ namespace ICSharpCode.Decompiler.IL @@ -4485,6 +4925,10 @@ namespace ICSharpCode.Decompiler.IL
{
Default(inst);
}
protected internal virtual void VisitCallIndirect(CallIndirect inst)
{
Default(inst);
}
protected internal virtual void VisitCkfinite(Ckfinite inst)
{
Default(inst);
@ -4561,6 +5005,14 @@ namespace ICSharpCode.Decompiler.IL @@ -4561,6 +5005,14 @@ namespace ICSharpCode.Decompiler.IL
{
Default(inst);
}
protected internal virtual void VisitCpblk(Cpblk inst)
{
Default(inst);
}
protected internal virtual void VisitInitblk(Initblk inst)
{
Default(inst);
}
protected internal virtual void VisitLdFlda(LdFlda inst)
{
Default(inst);
@ -4633,6 +5085,10 @@ namespace ICSharpCode.Decompiler.IL @@ -4633,6 +5085,10 @@ namespace ICSharpCode.Decompiler.IL
{
Default(inst);
}
protected internal virtual void VisitStringToInt(StringToInt inst)
{
Default(inst);
}
protected internal virtual void VisitMakeRefAny(MakeRefAny inst)
{
Default(inst);
@ -4771,6 +5227,10 @@ namespace ICSharpCode.Decompiler.IL @@ -4771,6 +5227,10 @@ namespace ICSharpCode.Decompiler.IL
{
return Default(inst);
}
protected internal virtual T VisitCallIndirect(CallIndirect inst)
{
return Default(inst);
}
protected internal virtual T VisitCkfinite(Ckfinite inst)
{
return Default(inst);
@ -4847,6 +5307,14 @@ namespace ICSharpCode.Decompiler.IL @@ -4847,6 +5307,14 @@ namespace ICSharpCode.Decompiler.IL
{
return Default(inst);
}
protected internal virtual T VisitCpblk(Cpblk inst)
{
return Default(inst);
}
protected internal virtual T VisitInitblk(Initblk inst)
{
return Default(inst);
}
protected internal virtual T VisitLdFlda(LdFlda inst)
{
return Default(inst);
@ -4919,6 +5387,10 @@ namespace ICSharpCode.Decompiler.IL @@ -4919,6 +5387,10 @@ namespace ICSharpCode.Decompiler.IL
{
return Default(inst);
}
protected internal virtual T VisitStringToInt(StringToInt inst)
{
return Default(inst);
}
protected internal virtual T VisitMakeRefAny(MakeRefAny inst)
{
return Default(inst);
@ -5057,6 +5529,10 @@ namespace ICSharpCode.Decompiler.IL @@ -5057,6 +5529,10 @@ namespace ICSharpCode.Decompiler.IL
{
return Default(inst, context);
}
protected internal virtual T VisitCallIndirect(CallIndirect inst, C context)
{
return Default(inst, context);
}
protected internal virtual T VisitCkfinite(Ckfinite inst, C context)
{
return Default(inst, context);
@ -5133,6 +5609,14 @@ namespace ICSharpCode.Decompiler.IL @@ -5133,6 +5609,14 @@ namespace ICSharpCode.Decompiler.IL
{
return Default(inst, context);
}
protected internal virtual T VisitCpblk(Cpblk inst, C context)
{
return Default(inst, context);
}
protected internal virtual T VisitInitblk(Initblk inst, C context)
{
return Default(inst, context);
}
protected internal virtual T VisitLdFlda(LdFlda inst, C context)
{
return Default(inst, context);
@ -5205,6 +5689,10 @@ namespace ICSharpCode.Decompiler.IL @@ -5205,6 +5689,10 @@ namespace ICSharpCode.Decompiler.IL
{
return Default(inst, context);
}
protected internal virtual T VisitStringToInt(StringToInt inst, C context)
{
return Default(inst, context);
}
protected internal virtual T VisitMakeRefAny(MakeRefAny inst, C context)
{
return Default(inst, context);
@ -5257,6 +5745,7 @@ namespace ICSharpCode.Decompiler.IL @@ -5257,6 +5745,7 @@ namespace ICSharpCode.Decompiler.IL
"comp",
"call",
"callvirt",
"calli",
"ckfinite",
"conv",
"ldloc",
@ -5276,6 +5765,8 @@ namespace ICSharpCode.Decompiler.IL @@ -5276,6 +5765,8 @@ namespace ICSharpCode.Decompiler.IL
"ldtypetoken",
"ldmembertoken",
"localloc",
"cpblk",
"initblk",
"ldflda",
"ldsflda",
"castclass",
@ -5294,6 +5785,7 @@ namespace ICSharpCode.Decompiler.IL @@ -5294,6 +5785,7 @@ namespace ICSharpCode.Decompiler.IL
"ldlen",
"ldelema",
"array.to.pointer",
"string.to.int",
"mkrefany",
"refanytype",
"refanyval",
@ -5585,6 +6077,34 @@ namespace ICSharpCode.Decompiler.IL @@ -5585,6 +6077,34 @@ namespace ICSharpCode.Decompiler.IL
argument = default(ILInstruction);
return false;
}
public bool MatchCpblk(out ILInstruction destAddress, out ILInstruction sourceAddress, out ILInstruction size)
{
var inst = this as Cpblk;
if (inst != null) {
destAddress = inst.DestAddress;
sourceAddress = inst.SourceAddress;
size = inst.Size;
return true;
}
destAddress = default(ILInstruction);
sourceAddress = default(ILInstruction);
size = default(ILInstruction);
return false;
}
public bool MatchInitblk(out ILInstruction address, out ILInstruction value, out ILInstruction size)
{
var inst = this as Initblk;
if (inst != null) {
address = inst.Address;
value = inst.Value;
size = inst.Size;
return true;
}
address = default(ILInstruction);
value = default(ILInstruction);
size = default(ILInstruction);
return false;
}
public bool MatchLdFlda(out ILInstruction target, out IField field)
{
var inst = this as LdFlda;

25
ICSharpCode.Decompiler/IL/Instructions.tt

@ -73,7 +73,7 @@ @@ -73,7 +73,7 @@
new OpCode("bit.not", "Bitwise NOT", Unary, CustomConstructor, MatchCondition("IsLifted == o.IsLifted && UnderlyingResultType == o.UnderlyingResultType")),
new OpCode("arglist", "Retrieves the RuntimeArgumentHandle.", NoArguments, ResultType("O")),
new OpCode("br", "Unconditional branch. <c>goto target;</c>",
CustomClassName("Branch"), NoArguments, CustomConstructor, UnconditionalBranch, MayBranch, CustomComputeFlags,
CustomClassName("Branch"), NoArguments, CustomConstructor, UnconditionalBranch, MayBranch,
MatchCondition("this.TargetBlock == o.TargetBlock")),
new OpCode("leave", "Unconditional branch to end of block container. Return is represented using IsLeavingFunction and an (optional) return value. The block container evaluates to the value produced by the argument of the leave instruction.",
CustomConstructor, CustomArguments("value"), UnconditionalBranch, MayBranch, CustomWriteTo, CustomComputeFlags,
@ -93,11 +93,11 @@ @@ -93,11 +93,11 @@
}), CustomConstructor, CustomComputeFlags, CustomWriteTo),
new OpCode("switch", "Switch statement",
CustomClassName("SwitchInstruction"), CustomConstructor, CustomComputeFlags, CustomWriteTo, ResultType("Void"),
MatchCondition("Value.PerformMatch(o.Value, ref match) && DefaultBody.PerformMatch(o.DefaultBody, ref match) && Patterns.ListMatch.DoMatch(this.Sections, o.Sections, ref match)")),
MatchCondition("IsLifted == o.IsLifted && Value.PerformMatch(o.Value, ref match) && Patterns.ListMatch.DoMatch(this.Sections, o.Sections, ref match)")),
new OpCode("switch.section", "Switch section within a switch statement",
CustomClassName("SwitchSection"), CustomChildren(new [] { new ChildInfo("body") }),
CustomConstructor, CustomComputeFlags, CustomWriteTo, ResultType("Void"),
MatchCondition("this.Labels.Intervals.SequenceEqual(o.Labels.Intervals)")),
MatchCondition("this.Labels.SetEquals(o.Labels) && this.HasNullLabel == o.HasNullLabel")),
new OpCode("try.catch", "Try-catch statement.",
BaseClass("TryInstruction"), CustomConstructor, CustomComputeFlags, CustomWriteTo,
MatchCondition("TryBlock.PerformMatch(o.TryBlock, ref match)"),
@ -135,6 +135,12 @@ @@ -135,6 +135,12 @@
new OpCode("call", "Non-virtual method call.", Call),
new OpCode("callvirt", "Virtual method call.",
CustomClassName("CallVirt"), Call),
new OpCode("calli", "Unsafe function pointer call.",
CustomClassName("CallIndirect"),
CustomConstructor, CustomWriteTo,
MatchCondition("EqualSignature(o)"),
MatchCondition("Patterns.ListMatch.DoMatch(this.Arguments, o.Arguments, ref match)"),
MatchCondition("this.FunctionPointer.PerformMatch(o.FunctionPointer, ref match)")),
new OpCode("ckfinite", "Checks that the input float is not NaN or infinite.",
Unary, MayThrow, VoidResult),
new OpCode("conv", "Numeric cast.",
@ -176,7 +182,15 @@ @@ -176,7 +182,15 @@
CustomClassName("LdMemberToken"), NoArguments, HasMemberOperand, ResultType("O")),
new OpCode("localloc", "Allocates space in the stack frame",
CustomClassName("LocAlloc"), Unary, ResultType("I"), MayThrow),
new OpCode("cpblk", "memcpy(destAddress, sourceAddress, size);",
CustomArguments("destAddress", "sourceAddress", "size"),
MayThrow, MemoryAccess,
SupportsVolatilePrefix, SupportsUnalignedPrefix, ResultType("Void")),
new OpCode("initblk", "memset(address, value, size)",
CustomArguments("address", "value", "size"),
MayThrow, MemoryAccess,
SupportsVolatilePrefix, SupportsUnalignedPrefix, ResultType("Void")),
new OpCode("ldflda", "Load address of instance field",
CustomClassName("LdFlda"), CustomArguments("target"), MayThrowIfNotDelayed, HasFieldOperand, ResultType("Ref")),
new OpCode("ldsflda", "Load static field address",
@ -220,6 +234,8 @@ @@ -220,6 +234,8 @@
new OpCode("array.to.pointer", "Converts an array pointer (O) to a reference to the first element, or to a null reference if the array is null or empty." + Environment.NewLine
+ "Also used to convert a string to a reference to the first character.",
CustomArguments("array"), ResultType("Ref")),
new OpCode("string.to.int", "Maps a string value to an integer. This is used in switch(string).",
CustomArguments("argument"), CustomConstructor, CustomWriteTo, ResultType("I4")),
new OpCode("mkrefany", "Push a typed reference of type class onto the stack.",
CustomClassName("MakeRefAny"), Unary, HasTypeOperand, ResultType("O")),
@ -483,6 +499,7 @@ namespace ICSharpCode.Decompiler.IL @@ -483,6 +499,7 @@ namespace ICSharpCode.Decompiler.IL
public IEnumerable<string> WriteToBody {
get {
yield return "ILRange.WriteTo(output, options);";
foreach (string line in WriteOpCodePrefix)
yield return line;
yield return "output.Write(OpCode);";

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

Loading…
Cancel
Save