Browse Source

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

pull/1600/head
Siegfried Pammer 6 years ago
parent
commit
79b56543e3
  1. 4
      .editorconfig
  2. 3
      .gitignore
  3. 40
      BuildTools/pipelines-install.ps1
  4. 36
      BuildTools/update-assemblyinfo.ps1
  5. 2
      DecompilerNuGetDemos.workbook
  6. 4
      ICSharpCode.Decompiler.Console/ICSharpCode.Decompiler.Console.csproj
  7. 2
      ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj
  8. 12
      ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs
  9. 1
      ICSharpCode.Decompiler.Tests/Helpers/RemoveCompilerAttribute.cs
  10. 28
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  11. 12
      ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs
  12. 22
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  13. 25
      ICSharpCode.Decompiler.Tests/RoundtripAssembly.cs
  14. 76
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/LocalFunctions.cs
  15. 79
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/RefLocalsAndReturns.cs
  16. 9
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/TrickyTypes.cs
  17. 14
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/DirectCallToExplicitInterfaceImpl.cs
  18. 36
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/DirectCallToExplicitInterfaceImpl.il
  19. 4
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1047.cs
  20. 39
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue684.cs
  21. 133
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue684.il
  22. 41
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs
  23. 73
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs
  24. 15
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ConstantsTests.cs
  25. 69
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs
  26. 28
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/DynamicTests.cs
  27. 20
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.cs
  28. 16
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.cs
  29. 11
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineAssignmentTest.cs
  30. 301
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs
  31. 52
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullableRefTypes.cs
  32. 10
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/OptionalArguments.cs
  33. 37
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/OutVariables.cs
  34. 11
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/QualifierTests.cs
  35. 135
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs
  36. 60
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.cs
  37. 134
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/UserDefinedConversions.cs
  38. 21
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/YieldReturn.cs
  39. 45
      ICSharpCode.Decompiler.Tests/TypeSystem/TypeSystemLoaderTests.cs
  40. 14
      ICSharpCode.Decompiler/CSharp/Annotations.cs
  41. 156
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  42. 227
      ICSharpCode.Decompiler/CSharp/CallBuilder.cs
  43. 211
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  44. 2
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs
  45. 30
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
  46. 2
      ICSharpCode.Decompiler/CSharp/OutputVisitor/FormattingOptionsFactory.cs
  47. 2
      ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs
  48. 2
      ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterTokenWriter.cs
  49. 18
      ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs
  50. 16
      ICSharpCode.Decompiler/CSharp/Resolver/CSharpInvocationResolveResult.cs
  51. 2
      ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs
  52. 4
      ICSharpCode.Decompiler/CSharp/Resolver/MethodGroupResolveResult.cs
  53. 5
      ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolution.cs
  54. 306
      ICSharpCode.Decompiler/CSharp/Resolver/ReducedExtensionMethod.cs
  55. 18
      ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs
  56. 56
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  57. 6
      ICSharpCode.Decompiler/CSharp/Syntax/ComposedType.cs
  58. 21
      ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs
  59. 6
      ICSharpCode.Decompiler/CSharp/Syntax/Expressions/AssignmentExpression.cs
  60. 2
      ICSharpCode.Decompiler/CSharp/Syntax/Expressions/UnaryOperatorExpression.cs
  61. 2
      ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/ExternAliasDeclaration.cs
  62. 3
      ICSharpCode.Decompiler/CSharp/Syntax/IAstVisitor.cs
  63. 110
      ICSharpCode.Decompiler/CSharp/Syntax/Statements/LocalFunctionDeclarationStatement.cs
  64. 4
      ICSharpCode.Decompiler/CSharp/Syntax/TextLocation.cs
  65. 2
      ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/MethodDeclaration.cs
  66. 31
      ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs
  67. 16
      ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
  68. 6
      ICSharpCode.Decompiler/CSharp/Transforms/AddCheckedBlocks.cs
  69. 2
      ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs
  70. 15
      ICSharpCode.Decompiler/CSharp/Transforms/FlattenSwitchBlocks.cs
  71. 13
      ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs
  72. 13
      ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs
  73. 21
      ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs
  74. 38
      ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs
  75. 33
      ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs
  76. 26
      ICSharpCode.Decompiler/DecompilerException.cs
  77. 33
      ICSharpCode.Decompiler/DecompilerSettings.cs
  78. 83
      ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs
  79. 125
      ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs
  80. 72
      ICSharpCode.Decompiler/FlowAnalysis/DefiniteAssignmentVisitor.cs
  81. 16
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  82. 1
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.nuspec.template
  83. 12
      ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs
  84. 4
      ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs
  85. 34
      ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs
  86. 15
      ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs
  87. 49
      ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs
  88. 2
      ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs
  89. 19
      ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs
  90. 9
      ICSharpCode.Decompiler/IL/ILInstructionExtensions.cs
  91. 33
      ICSharpCode.Decompiler/IL/ILReader.cs
  92. 18
      ICSharpCode.Decompiler/IL/ILVariable.cs
  93. 149
      ICSharpCode.Decompiler/IL/InstructionOutputExtensions.cs
  94. 61
      ICSharpCode.Decompiler/IL/Instructions.cs
  95. 26
      ICSharpCode.Decompiler/IL/Instructions.tt
  96. 53
      ICSharpCode.Decompiler/IL/Instructions/Block.cs
  97. 54
      ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs
  98. 22
      ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs
  99. 154
      ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs
  100. 165
      ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
  101. Some files were not shown because too many files have changed in this diff Show More

4
.editorconfig

@ -8,7 +8,9 @@ indent_size = 4 @@ -8,7 +8,9 @@ indent_size = 4
[*.il]
indent_style = space
indent_size = 2
[*.{yml,yaml}]
indent_style = space
indent_size = 2
[*.csproj]
indent_style = space
indent_size = 2

3
.gitignore vendored

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
bin/
obj/
AppPackages/
BundleArtifacts/
/ICSharpCode.Decompiler/Properties/AssemblyInfo.cs
/ILSpy/Properties/AssemblyInfo.cs
/ILSpy/app.config

40
BuildTools/pipelines-install.ps1

@ -0,0 +1,40 @@ @@ -0,0 +1,40 @@
$ErrorActionPreference = "Stop"
$baseCommit = "d779383cb85003d6dabeb976f0845631e07bf463";
$baseCommitRev = 1;
# make sure this list matches artifacts-only branches list in azure-pipelines.yml!
$masterBranches = @("master", "3.2.x");
$globalAssemblyInfoTemplateFile = "ILSpy/Properties/AssemblyInfo.template.cs";
$versionParts = @{};
Get-Content $globalAssemblyInfoTemplateFile | where { $_ -match 'string (\w+) = "?(\w+)"?;' } | foreach { $versionParts.Add($Matches[1], $Matches[2]) }
$major = $versionParts.Major;
$minor = $versionParts.Minor;
$build = $versionParts.Build;
$versionName = $versionParts.VersionName;
if ($versionName -ne "null") {
$versionName = "-$versionName";
} else {
$versionName = "";
}
if ($masterBranches -contains $env:BUILD_SOURCEBRANCHNAME) {
$branch = "";
} else {
$branch = "-$env:BUILD_SOURCEBRANCHNAME";
}
if ($env:SYSTEM_PULLREQUEST_PULLREQUESTNUMBER) {
$suffix = "-pr$env:SYSTEM_PULLREQUEST_PULLREQUESTNUMBER";
} else {
$suffix = "";
}
$revision = [Int32]::Parse((git rev-list --count "$baseCommit..HEAD")) + $baseCommitRev;
$newVersion="$major.$minor.$build.$revision";
$env:ILSPY_VERSION_NUMBER="$newVersion$branch$versionName$suffix";
Write-Host "##vso[build.updatebuildnumber]$newVersion$branch$versionName$suffix";
Write-Host "new version: $newVersion$branch$versionName$suffix";

36
BuildTools/update-assemblyinfo.ps1

@ -57,8 +57,10 @@ function gitBranch() { @@ -57,8 +57,10 @@ function gitBranch() {
return "no-branch";
}
if ($env:APPVEYOR_REPO_BRANCH -ne $null) {
if ($env:APPVEYOR_REPO_BRANCH -ne $null) {
return $env:APPVEYOR_REPO_BRANCH;
} elseif ($env:BUILD_SOURCEBRANCHNAME -ne $null) {
return $env:BUILD_SOURCEBRANCHNAME;
} else {
return ((git branch --no-color).Split([System.Environment]::NewLine) | where { $_ -match "^\* " } | select -First 1).Substring(2);
}
@ -71,6 +73,12 @@ $templateFiles = ( @@ -71,6 +73,12 @@ $templateFiles = (
@{Input="ILSpy/Properties/app.config.template"; Output = "ILSpy/app.config"},
@{Input="ILSpy.AddIn/source.extension.vsixmanifest.template"; Output = "ILSpy.AddIn/source.extension.vsixmanifest"}
);
$appxmanifestFiles = (
@{Input="ILSpy.Package/Package.appxmanifest"; Output="ILSpy.Package/Package.appxmanifest"},
@{Input="ILSpy.Package/Package-CI.appxmanifest"; Output="ILSpy.Package/Package-CI.appxmanifest"}
);
[string]$mutexId = "ILSpyUpdateAssemblyInfo" + (Get-Location).ToString().GetHashCode();
Write-Host $mutexId;
[bool]$createdNew = $false;
@ -142,6 +150,32 @@ try { @@ -142,6 +150,32 @@ try {
$out | Out-File -Encoding utf8 $file.Output;
}
}
# Only update these on the Build Agent when ReleaseChannel is set
if($Env:ReleaseChannel -ne '' -and $Env:ReleaseChannel -ne $null) {
foreach ($file in $appxmanifestFiles) {
[string]$in = (Get-Content $file.Input) -Join [System.Environment]::NewLine;
$out = $in.Replace('$INSERTVERSION$', $fullVersionNumber);
$out = $out.Replace('$INSERTMAJORVERSION$', $major);
$out = $out.Replace('$INSERTMINORVERSION$', $minor);
$out = $out.Replace('$INSERTREVISION$', $revision);
$out = $out.Replace('$INSERTCOMMITHASH$', $gitCommitHash);
$out = $out.Replace('$INSERTSHORTCOMMITHASH$', $gitCommitHash.Substring(0, 8));
$out = $out.Replace('$INSERTDATE$', [System.DateTime]::Now.ToString("MM/dd/yyyy"));
$out = $out.Replace('$INSERTYEAR$', [System.DateTime]::Now.Year.ToString());
$out = $out.Replace('$INSERTBRANCHNAME$', $branchName);
$out = $out.Replace('$INSERTBRANCHPOSTFIX$', $postfixBranchName);
$out = $out.Replace('$INSERTVERSIONNAME$', $versionName);
$out = $out.Replace('$INSERTVERSIONNAMEPOSTFIX$', $postfixVersionName);
$out = $out.Replace('$INSERTBUILDCONFIG$', $buildConfig);
if (((Get-Content $file.Input) -Join [System.Environment]::NewLine) -ne $out) {
$out | Out-File -Encoding utf8 $file.Output;
}
}
}
} finally {
$mutex.ReleaseMutex();
$mutex.Close();

2
DecompilerNuGetDemos.workbook

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

4
ICSharpCode.Decompiler.Console/ICSharpCode.Decompiler.Console.csproj

@ -7,7 +7,7 @@ @@ -7,7 +7,7 @@
<PackAsTool>true</PackAsTool>
<AssemblyName>ilspycmd</AssemblyName>
<ToolCommandName>ilspycmd</ToolCommandName>
<Version>5.0.0-preview1</Version>
<Version>5.0.0-preview3</Version>
<Description>Command-line decompiler using the ILSpy decompilation engine</Description>
<Copyright>Copyright 2011-2019 AlphaSierraPapa</Copyright>
<PackageProjectUrl>https://github.com/icsharpcode/ILSpy/</PackageProjectUrl>
@ -28,7 +28,7 @@ @@ -28,7 +28,7 @@
<ItemGroup>
<PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="2.3.2" />
<PackageReference Include="ICSharpCode.Decompiler" Version="5.0.0.4688-preview1" />
<PackageReference Include="ICSharpCode.Decompiler" Version="5.0.0.4861-preview3" />
<PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
<PackageReference Include="System.Runtime.Handles" Version="4.3.0" />

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

@ -8,7 +8,7 @@ @@ -8,7 +8,7 @@
<ItemGroup>
<PackageReference Include="PowerShellStandard.Library" Version="5.1.0" />
<PackageReference Include="ICSharpCode.Decompiler" Version="5.0.0.4688-preview1" />
<PackageReference Include="ICSharpCode.Decompiler" Version="5.0.0.4861-preview3" />
</ItemGroup>
</Project>

12
ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs

@ -196,12 +196,6 @@ namespace ICSharpCode.Decompiler.Tests @@ -196,12 +196,6 @@ namespace ICSharpCode.Decompiler.Tests
RunCS(options: options);
}
[Test]
public void RefLocalsAndReturns([ValueSource("roslynOnlyOptions")] CompilerOptions options)
{
RunCS(options: options);
}
[Test]
public void BitNot([Values(false, true)] bool force32Bit)
{
@ -295,12 +289,6 @@ namespace ICSharpCode.Decompiler.Tests @@ -295,12 +289,6 @@ namespace ICSharpCode.Decompiler.Tests
RunCS(options: options);
}
[Test]
public void LocalFunctions([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions options)
{
RunCS(options: options);
}
void RunCS([CallerMemberName] string testName = null, CompilerOptions options = CompilerOptions.UseDebug)
{
string testFileName = testName + ".cs";

1
ICSharpCode.Decompiler.Tests/Helpers/RemoveCompilerAttribute.cs

@ -40,6 +40,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers @@ -40,6 +40,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
"System.Runtime.CompilerServices.IsByRefLikeAttribute",
"System.Runtime.CompilerServices.IsUnmanagedAttribute",
"System.Runtime.CompilerServices.NullableAttribute",
"System.Runtime.CompilerServices.NullableContextAttribute",
"Microsoft.CodeAnalysis.EmbeddedAttribute",
};

28
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -7,7 +7,7 @@ @@ -7,7 +7,7 @@
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<NoWarn>1701;1702;1705,67,169,1058,728,1720,649,168,251</NoWarn>
<NoWarn>1701;1702;1705,67,169,1058,728,1720,649,168,251,660,661,675</NoWarn>
<GenerateAssemblyInfo>False</GenerateAssemblyInfo>
@ -41,13 +41,14 @@ @@ -41,13 +41,14 @@
<ItemGroup>
<PackageReference Include="DiffLib" Version="2017.7.26.1241" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.0.0-beta4-final" />
<PackageReference Include="Microsoft.CodeAnalysis.VisualBasic" Version="3.0.0-beta4-final" />
<PackageReference Include="Microsoft.Build.Locator" Version="1.2.2" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.2.0-beta4-final" />
<PackageReference Include="Microsoft.CodeAnalysis.VisualBasic" Version="3.2.0-beta4-final" />
<PackageReference Include="Microsoft.DiaSymReader.Converter.Xml" Version="1.1.0-beta1-63314-01" />
<PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
<PackageReference Include="System.Collections.Immutable" Version="1.5.0" />
<PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="System.Memory" Version="4.5.1" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="System.Memory" Version="4.5.3" />
</ItemGroup>
<ItemGroup>
@ -60,11 +61,17 @@ @@ -60,11 +61,17 @@
<None Include="TestCases\Correctness\StackTypes.il" />
<None Include="TestCases\Correctness\Uninit.vb" />
<None Include="TestCases\ILPretty\ConstantBlobs.il" />
<None Include="TestCases\ILPretty\CS1xSwitch_Debug.il" />
<None Include="TestCases\ILPretty\CS1xSwitch_Release.il" />
<None Include="TestCases\ILPretty\Issue1157.il" />
<None Include="TestCases\ILPretty\Issue684.cs" />
<None Include="TestCases\ILPretty\Issue684.il" />
<None Include="TestCases\ILPretty\FSharpLoops.fs" />
<None Include="TestCases\ILPretty\FSharpLoops_Debug.il" />
<None Include="TestCases\ILPretty\FSharpLoops_Release.il" />
<None Include="TestCases\ILPretty\FSharpUsing.fs" />
<None Include="TestCases\ILPretty\FSharpUsing_Debug.il" />
<None Include="TestCases\ILPretty\DirectCallToExplicitInterfaceImpl.il" />
<None Include="TestCases\ILPretty\FSharpUsing_Release.il" />
<None Include="TestCases\Correctness\BitNot.il" />
<None Include="TestCases\Correctness\Readme.txt" />
@ -73,6 +80,13 @@ @@ -73,6 +80,13 @@
<ItemGroup>
<Compile Include="DisassemblerPrettyTestRunner.cs" />
<Compile Include="TestCases\ILPretty\ConstantBlobs.cs" />
<Compile Include="TestCases\Pretty\OutVariables.cs" />
<None Include="TestCases\ILPretty\Issue1145.cs" />
<Compile Include="TestCases\ILPretty\Issue1157.cs" />
<None Include="TestCases\ILPretty\Unsafe.cs" />
<None Include="TestCases\ILPretty\CS1xSwitch_Debug.cs" />
<None Include="TestCases\ILPretty\CS1xSwitch_Release.cs" />
<None Include="TestCases\ILPretty\DirectCallToExplicitInterfaceImpl.cs" />
<Compile Include="TestCases\ILPretty\Issue1389.cs" />
<Compile Include="TestCases\ILPretty\Issue1454.cs" />
<Compile Include="TestCases\Pretty\Discards.cs" />
@ -82,12 +96,12 @@ @@ -82,12 +96,12 @@
<Compile Include="Semantics\ExplicitConversionTest.cs" />
<Compile Include="Semantics\OverloadResolutionTests.cs" />
<Compile Include="DataFlowTest.cs" />
<Compile Include="TestCases\Correctness\LocalFunctions.cs" />
<Compile Include="TestCases\Correctness\RefLocalsAndReturns.cs" />
<Compile Include="TestCases\Pretty\LocalFunctions.cs" />
<Compile Include="TestCases\ILPretty\Issue1256.cs" />
<Compile Include="TestCases\ILPretty\Issue1323.cs" />
<Compile Include="TestCases\Pretty\CustomAttributes2.cs" />
<Compile Include="TestCases\Pretty\EnumTests.cs" />
<Compile Include="TestCases\Pretty\UserDefinedConversions.cs" />
<None Include="TestCases\ILPretty\Unsafe.il" />
<None Include="TestCases\Pretty\NullableRefTypes.cs" />
<Compile Include="TestCases\Pretty\TypeMemberTests.cs" />

12
ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs

@ -58,6 +58,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -58,6 +58,12 @@ namespace ICSharpCode.Decompiler.Tests
Run();
}
[Test]
public void Issue684()
{
Run();
}
[Test]
public void Issue959()
{
@ -100,6 +106,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -100,6 +106,12 @@ namespace ICSharpCode.Decompiler.Tests
Run(settings: new DecompilerSettings { RemoveDeadCode = true });
}
[Test]
public void DirectCallToExplicitInterfaceImpl()
{
Run();
}
[Test]
public void CS1xSwitch_Debug()
{

22
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -134,7 +134,7 @@ namespace ICSharpCode.Decompiler.Tests @@ -134,7 +134,7 @@ namespace ICSharpCode.Decompiler.Tests
}
[Test]
public void DelegateConstruction([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions)
public void DelegateConstruction([ValueSource(nameof(defaultOptionsWithMcs))] CompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
@ -184,6 +184,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -184,6 +184,12 @@ namespace ICSharpCode.Decompiler.Tests
});
}
[Test]
public void LocalFunctions([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void PropertiesAndEvents([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions)
{
@ -233,6 +239,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -233,6 +239,12 @@ namespace ICSharpCode.Decompiler.Tests
RunForLibrary(cscOptions: cscOptions, asmOptions: AssemblerOptions.UseOwnDisassembler);
}
[Test]
public void OutVariables([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void InitializerTests([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions)
{
@ -420,7 +432,13 @@ namespace ICSharpCode.Decompiler.Tests @@ -420,7 +432,13 @@ namespace ICSharpCode.Decompiler.Tests
}
[Test]
public void YieldReturn([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions)
public void YieldReturn([ValueSource(nameof(defaultOptionsWithMcs))] CompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void UserDefinedConversions([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}

25
ICSharpCode.Decompiler.Tests/RoundtripAssembly.cs

@ -26,6 +26,7 @@ using System.Threading; @@ -26,6 +26,7 @@ using System.Threading;
using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Tests.Helpers;
using Microsoft.Build.Locator;
using Microsoft.Win32;
using NUnit.Framework;
@ -68,11 +69,7 @@ namespace ICSharpCode.Decompiler.Tests @@ -68,11 +69,7 @@ namespace ICSharpCode.Decompiler.Tests
[Test]
public void ICSharpCode_Decompiler()
{
try {
RunWithTest("ICSharpCode.Decompiler", "ICSharpCode.Decompiler.dll", "ICSharpCode.Decompiler.Tests.exe");
} catch (CompilationFailedException) {
Assert.Ignore("C# 7 local functions not yet supported.");
}
RunWithTest("ICSharpCode.Decompiler", "ICSharpCode.Decompiler.dll", "ICSharpCode.Decompiler.Tests.exe");
}
[Test]
@ -188,22 +185,16 @@ namespace ICSharpCode.Decompiler.Tests @@ -188,22 +185,16 @@ namespace ICSharpCode.Decompiler.Tests
File.Delete(file);
}
}
static string FindVS2017()
{
using (var key = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32)) {
using (var subkey = key.OpenSubKey(@"SOFTWARE\Microsoft\VisualStudio\SxS\VS7")) {
return subkey?.GetValue("15.0") as string;
}
}
}
static string FindMSBuild()
{
string vsPath = FindVS2017();
string vsPath = MSBuildLocator.QueryVisualStudioInstances(new VisualStudioInstanceQueryOptions { DiscoveryTypes = DiscoveryType.VisualStudioSetup })
.OrderByDescending(i => i.Version)
.FirstOrDefault()
?.MSBuildPath;
if (vsPath == null)
throw new InvalidOperationException("Could not find VS2017");
return Path.Combine(vsPath, @"MSBuild\15.0\bin\MSBuild.exe");
throw new InvalidOperationException("Could not find MSBuild");
return Path.Combine(vsPath, "msbuild.exe");
}
static void Compile(string projectFile, string outputDir)

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

@ -1,76 +0,0 @@ @@ -1,76 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LocalFunctions
{
class LocalFunctions
{
int field;
public static void Main(string[] args)
{
StaticContextNoCapture(10);
StaticContextSimpleCapture(10);
StaticContextCaptureInForLoop(10);
var inst = new LocalFunctions() { field = 10 };
inst.ContextNoCapture();
inst.ContextSimpleCapture();
inst.ContextCaptureInForLoop();
}
public static void StaticContextNoCapture(int length)
{
for (int i = 0; i < length; i++) {
LocalWrite("Hello " + i);
}
void LocalWrite(string s) => Console.WriteLine(s);
}
public static void StaticContextSimpleCapture(int length)
{
for (int i = 0; i < length; i++) {
LocalWrite();
}
void LocalWrite() => Console.WriteLine("Hello " + length);
}
public static void StaticContextCaptureInForLoop(int length)
{
for (int i = 0; i < length; i++) {
void LocalWrite() => Console.WriteLine("Hello " + i + "/" + length);
LocalWrite();
}
}
public void ContextNoCapture()
{
for (int i = 0; i < field; i++) {
LocalWrite("Hello " + i);
}
void LocalWrite(string s) => Console.WriteLine(s);
}
public void ContextSimpleCapture()
{
for (int i = 0; i < field; i++) {
LocalWrite();
}
void LocalWrite() => Console.WriteLine("Hello " + field);
}
public void ContextCaptureInForLoop()
{
for (int i = 0; i < field; i++) {
void LocalWrite() => Console.WriteLine("Hello " + i + "/" + field);
LocalWrite();
}
}
}
}

79
ICSharpCode.Decompiler.Tests/TestCases/Correctness/RefLocalsAndReturns.cs

@ -1,79 +0,0 @@ @@ -1,79 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
{
class RefLocalsAndReturns
{
static int[] numbers = { 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023 };
static string[] strings = { "Hello", "World" };
static string NullString = "";
static int DefaultInt = 0;
public delegate ref TReturn RefFunc<T1, TReturn>(T1 param1);
public static TReturn Invoker<T1, TReturn>(RefFunc<T1, TReturn> action, T1 value)
{
return action(value);
}
public static ref int FindNumber(int target)
{
for (int ctr = 0; ctr < numbers.Length; ctr++) {
if (numbers[ctr] >= target)
return ref numbers[ctr];
}
return ref numbers[0];
}
public static ref int LastNumber()
{
return ref numbers[numbers.Length - 1];
}
public static ref int ElementAtOrDefault(int index)
{
return ref index < 0 || index >= numbers.Length ? ref DefaultInt : ref numbers[index];
}
public static ref int LastOrDefault()
{
return ref numbers.Length > 0 ? ref numbers[numbers.Length - 1] : ref DefaultInt;
}
public static void DoubleNumber(ref int num)
{
Console.WriteLine("old: " + num);
num *= 2;
Console.WriteLine("new: " + num);
}
public static ref string GetOrSetString(int index)
{
if (index < 0 || index >= strings.Length)
return ref NullString;
return ref strings[index];
}
public static void Main(string[] args)
{
DoubleNumber(ref FindNumber(32));
Console.WriteLine(string.Join(", ", numbers));
DoubleNumber(ref LastNumber());
Console.WriteLine(string.Join(", ", numbers));
Console.WriteLine(GetOrSetString(0));
GetOrSetString(0) = "Goodbye";
Console.WriteLine(string.Join(" ", strings));
GetOrSetString(5) = "Here I mutated the null value!?";
Console.WriteLine(GetOrSetString(-5));
Console.WriteLine(Invoker(x => ref numbers[x], 0));
Console.WriteLine(LastOrDefault());
LastOrDefault() = 10000;
Console.WriteLine(ElementAtOrDefault(-5));
}
}
}

9
ICSharpCode.Decompiler.Tests/TestCases/Correctness/TrickyTypes.cs

@ -26,6 +26,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -26,6 +26,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
{
InterestingConstants();
TruncatedComp();
StringConcat();
}
static void Print<T>(T val)
@ -92,5 +93,13 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -92,5 +93,13 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
Print(val1 <= val2);
Print((int)val1 <= val2);
}
static void StringConcat()
{
// Some string.Concat()-cases that cannot be replaced using operator+
Print(string.Concat("String concat:"));
Print(string.Concat(1, 2));
Print(string.Concat(1, 2, "str"));
}
}
}

14
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/DirectCallToExplicitInterfaceImpl.cs

@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
using System;
public sealed class TestClass : IDisposable
{
void IDisposable.Dispose()
{
}
public void Test(TestClass other)
{
((IDisposable)this).Dispose();
((IDisposable)other).Dispose();
}
}

36
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/DirectCallToExplicitInterfaceImpl.il

@ -0,0 +1,36 @@ @@ -0,0 +1,36 @@
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
.assembly DirectCallToExplicitInterfaceImpl
{
.ver 1:0:0:0
}
.module DirectCallToExplicitInterfaceImpl.exe
.class public auto ansi sealed TestClass
extends [mscorlib]System.Object
implements [mscorlib]System.IDisposable
{
// Methods
.method private final hidebysig newslot virtual
instance void System.IDisposable.Dispose () cil managed
{
.override method instance void [mscorlib]System.IDisposable::Dispose()
ret
}
.method public hidebysig void Test (class TestClass other) cil managed
{
ldarg.0
call instance void TestClass::System.IDisposable.Dispose()
ldarg.1
call instance void TestClass::System.IDisposable.Dispose()
ret
}
} // end of class TestClass

4
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1047.cs

@ -6,12 +6,8 @@ @@ -6,12 +6,8 @@
private void ProblemMethod()
{
IL_0000:
while (!dummy) {
}
return;
IL_0014:
goto IL_0000;
}
}
}

39
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue684.cs

@ -0,0 +1,39 @@ @@ -0,0 +1,39 @@
using System;
public static class Issue684
{
static int Main(string[] A_0)
{
int[] array = new int[1000];
int num = int.Parse(Console.ReadLine());
// Point of this test was to ensure the stack slot here uses an appropriate type,
// (bool instead of int). Unfortunately our type fixup runs too late to affect variable names.
bool num2 = num >= 1000;
if (!num2) {
num2 = (num < 2);
}
if (num2) {
Console.WriteLine(-1);
} else {
int i = 2;
for (int num3 = 2; num3 <= num; num3 = i) {
Console.WriteLine(num3);
for (; i <= num; i += num3) {
int num4 = array[i] = 1;
}
i = num3;
while (true) {
bool num5 = i <= num;
if (num5) {
num5 = (array[i] != 0);
}
if (!num5) {
break;
}
i++;
}
}
}
return 0;
}
}

133
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue684.il

@ -0,0 +1,133 @@ @@ -0,0 +1,133 @@
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
.assembly Issue684
{
.ver 1:0:0:0
}
.module Issue684.exe
.class public auto ansi abstract sealed Issue684
extends [mscorlib]System.Object
{
// Methods
.method static privatescope
int32 Main$PST06000001 (
string[] ''
) cil managed
{
// Method begins at RVA 0x2050
// Code size 196 (0xc4)
.maxstack 11
.entrypoint
.locals init (
[0] int32,
[1] int32,
[2] int32,
[3] int32[],
[4] int32
)
IL_0000: ldc.i4 1000
IL_0005: newarr [mscorlib]System.Int32
IL_000a: stloc.3
IL_000b: call string [mscorlib]System.Console::ReadLine()
IL_0010: call int32 [mscorlib]System.Int32::Parse(string)
IL_0015: stloc.2
IL_0016: ldloc.2
IL_0017: ldc.i4 1000
IL_001c: clt
IL_001e: ldc.i4.0
IL_001f: ceq
IL_0021: dup
IL_0022: brtrue IL_0030
IL_0027: pop
IL_0028: ldloc.2
IL_0029: ldc.i4 2
IL_002e: clt
IL_0030: brfalse IL_0045
IL_0035: ldc.i4 1
IL_003a: neg
IL_003b: call void [mscorlib]System.Console::WriteLine(int32)
IL_0040: br IL_00c2
IL_0045: ldc.i4 2
IL_004a: stloc.0
IL_004b: ldc.i4 2
IL_0050: stloc.1
// loop start (head: IL_0051)
IL_0051: ldloc.1
IL_0052: ldloc.2
IL_0053: cgt
IL_0055: ldc.i4.0
IL_0056: ceq
IL_0058: brfalse IL_00c2
IL_005d: ldloc.1
IL_005e: call void [mscorlib]System.Console::WriteLine(int32)
// loop start (head: IL_0063)
IL_0063: ldloc.0
IL_0064: ldloc.2
IL_0065: cgt
IL_0067: ldc.i4.0
IL_0068: ceq
IL_006a: brfalse IL_0088
IL_006f: ldc.i4 1
IL_0074: stloc.s 4
IL_0076: ldloc.3
IL_0077: ldloc.0
IL_0078: ldloc.s 4
IL_007a: stelem.any [mscorlib]System.Int32
IL_007f: ldloc.0
IL_0080: ldloc.1
IL_0081: add
IL_0082: stloc.0
IL_0083: br IL_0063
// end loop
IL_0088: ldloc.1
IL_0089: stloc.0
// loop start (head: IL_008a)
IL_008a: ldloc.0
IL_008b: ldloc.2
IL_008c: cgt
IL_008e: ldc.i4.0
IL_008f: ceq
IL_0091: dup
IL_0092: brfalse IL_00a9
IL_0097: pop
IL_0098: ldloc.3
IL_0099: ldloc.0
IL_009a: ldelem.any [mscorlib]System.Int32
IL_009f: ldc.i4 0
IL_00a4: ceq
IL_00a6: ldc.i4.0
IL_00a7: ceq
IL_00a9: brfalse IL_00bb
IL_00ae: ldloc.0
IL_00af: ldc.i4 1
IL_00b4: add
IL_00b5: stloc.0
IL_00b6: br IL_008a
// end loop
IL_00bb: ldloc.0
IL_00bc: stloc.1
IL_00bd: br IL_0051
// end loop
IL_00c2: ldc.i4.0
IL_00c3: ret
} // end of method Program::Main
} // end of class Issue684

41
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs

@ -18,6 +18,7 @@ @@ -18,6 +18,7 @@
#pragma warning disable 1998
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
@ -126,6 +127,46 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -126,6 +127,46 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
}
#endif
public static async Task<int> GetIntegerSumAsync(IEnumerable<int> items)
{
await Task.Delay(100);
int num = 0;
foreach (int item in items) {
num += item;
}
return num;
}
public static Func<Task<int>> AsyncLambda()
{
return async () => await GetIntegerSumAsync(new int[3] {
1,
2,
3
});
}
public static Func<Task<int>> AsyncDelegate()
{
return async delegate {
await Task.Delay(10);
return 2;
};
}
#if CS70
public static async Task<int> AsyncLocalFunctions()
{
return await Nested(1) + await Nested(2);
async Task<int> Nested(int i)
{
await Task.Delay(i);
return i;
}
}
#endif
}
public struct HopToThreadPoolAwaitable : INotifyCompletion

73
ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs

@ -4577,12 +4577,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -4577,12 +4577,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
new CustomClass().StringProp += 1;
}
#if false
public uint PreIncrementIndexer(string name)
{
return ++M()[name];
}
#endif
public int PreIncrementByRef(ref int i)
{
return ++i;
@ -4593,6 +4592,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -4593,6 +4592,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
return ++(*GetPointer());
}
public unsafe int PreIncrementOfPointer(int* ptr)
{
return *(++ptr);
}
public int PreIncrement2DArray()
{
return ++Array()[1, 2];
@ -4627,12 +4631,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -4627,12 +4631,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
return array[Environment.TickCount] *= 10;
}
#if false
public uint CompoundAssignIndexer(string name)
{
return M()[name] -= 2;
return M()[name] -= 2u;
}
#endif
public uint CompoundAssignIndexerComplexIndex(string name)
{
return M()[ToString()] -= 2u;
}
public int CompoundAssignIncrement2DArray()
{
return Array()[1, 2] %= 10;
@ -4643,6 +4652,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -4643,6 +4652,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
return i <<= 2;
}
public unsafe int* CompoundAssignOfPointer(int* ptr)
{
return ptr += 10;
}
public unsafe double CompoundAssignByPointer(double* ptr)
{
return *ptr /= 1.5;
@ -4669,17 +4683,19 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -4669,17 +4683,19 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
return array[pos]--;
}
#if false
public uint PostIncrementIndexer(string name)
{
return M()[name]++;
}
#if false
public unsafe int PostIncrementOfPointer(int* ptr)
{
return *(ptr++);
}
#endif
public int PostDecrementInstanceField()
{
return M().Field--;
@ -4704,5 +4720,50 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -4704,5 +4720,50 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
return (*GetPointer())++;
}
public void Issue1552Pre(CustomStruct a, CustomStruct b)
{
CustomStruct customStruct = a + b;
Console.WriteLine(++customStruct);
}
public void Issue1552Stmt(CustomStruct a, CustomStruct b)
{
CustomStruct customStruct = a + b;
++customStruct;
}
public void Issue1552StmtUseLater(CustomStruct a, CustomStruct b)
{
CustomStruct lhs = a + b;
++lhs;
Console.WriteLine();
Console.WriteLine(lhs * b);
}
public void Issue1552Decimal(decimal a)
{
// Legacy csc compiles this using op_Increment,
// ensure we don't misdetect this as an invalid pre-increment "++(a * 10m)"
Console.WriteLine(a * 10m + 1m);
}
#if !(ROSLYN && OPT)
// Roslyn opt no longer has a detectable post-increment pattern
// due to optimizing out some of the stores.
// Our emitted code is valid but has some additional temporaries.
public void Issue1552Post(CustomStruct a, CustomStruct b)
{
CustomStruct customStruct = a + b;
Console.WriteLine(customStruct++);
}
public void Issue1552StmtTwice(CustomStruct a, CustomStruct b)
{
CustomStruct customStruct = a + b;
++customStruct;
++customStruct;
}
#endif
}
}

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

@ -35,5 +35,20 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -35,5 +35,20 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
private void Test(bool expr)
{
}
private void Test(decimal expr)
{
}
public void Decimal()
{
// Roslyn and legacy csc both normalize the decimal constant references,
// but to a different representation (ctor call vs. field use)
Test(0m);
Test(1m);
Test(-1m);
Test(decimal.MinValue);
Test(decimal.MaxValue);
}
}
}

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

@ -146,6 +146,59 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -146,6 +146,59 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
}
public interface IM3
{
void M3();
}
public class BaseClass : IM3
{
protected virtual void M1()
{
}
protected virtual void M2()
{
}
public virtual void M3()
{
}
}
public class SubClass : BaseClass
{
protected override void M2()
{
}
public new void M3()
{
}
public void Test()
{
Noop("M1.base", base.M1);
Noop("M1", M1);
Noop("M2.base", base.M2);
Noop("M2", M2);
Noop("M3.base", base.M3);
Noop("M3.base_virt", ((BaseClass)this).M3);
Noop("M3.base_interface", ((IM3)this).M3);
#if CS70
Noop("M3", this.M3);
Noop("M3", M3);
void M3()
{
}
#else
Noop("M3", M3);
#endif
}
private void Noop(string name, Action _)
{
}
}
public static Func<string, string, bool> test0 = (string a, string b) => string.IsNullOrEmpty(a) || string.IsNullOrEmpty(b);
public static Func<string, string, bool> test1 = (string a, string b) => string.IsNullOrEmpty(a) || !string.IsNullOrEmpty(b);
public static Func<string, string, bool> test2 = (string a, string b) => !string.IsNullOrEmpty(a) || string.IsNullOrEmpty(b);
@ -159,6 +212,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -159,6 +212,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
}
public static Predicate<T> And<T>(this Predicate<T> filter1, Predicate<T> filter2)
{
if (filter1 == null) {
return filter2;
}
if (filter2 == null) {
return filter1;
}
return (T m) => filter1(m) && filter2(m);
}
public static Action<string> ExtensionMethodUnbound()
{
return Test;
@ -174,6 +238,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -174,6 +238,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
return ((string)null).Test;
}
public static Predicate<int> NoExtensionMethodOnLambda()
{
return And((int x) => x >= 0, (int x) => x <= 100);
}
public static object StaticMethod()
{
return new Func<Action>(ExtensionMethodBound);

28
ICSharpCode.Decompiler.Tests/TestCases/Pretty/DynamicTests.cs

@ -64,6 +64,34 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -64,6 +64,34 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
}
private static void CallWithOut(out dynamic d)
{
d = null;
}
#if CS70
private static void CallWithIn(in dynamic d)
{
}
#endif
private static void CallWithRef(ref dynamic d)
{
}
private static void RefCallSiteTests()
{
#if CS70
CallWithOut(out dynamic d);
CallWithIn(in d);
#else
dynamic d;
CallWithOut(out d);
#endif
CallWithRef(ref d);
d.SomeCall();
}
private static void InvokeConstructor()
{
DynamicTests dynamicTests = new DynamicTests();

20
ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.cs

@ -22,6 +22,7 @@ using System.Linq; @@ -22,6 +22,7 @@ using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
@ -584,7 +585,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -584,7 +585,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
2004,
2008,
2012
}).Any<int>));
}).Any));
}
public void MethodGroupConstant()
@ -1026,6 +1027,23 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -1026,6 +1027,23 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
Field = 3
});
}
public async Task Issue1524(string str)
{
await Task.Delay(100);
if (string.IsNullOrEmpty(str)) {
#if ROSLYN
if (int.TryParse(str, out int id)) {
#else
int id;
if (int.TryParse(str, out id)) {
#endif
(from a in new List<int>().AsQueryable()
where a == id
select a).FirstOrDefault();
}
}
}
}
internal static class Extensions

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

@ -1678,13 +1678,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.InitializerTests @@ -1678,13 +1678,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.InitializerTests
});
}
private void NestedListWithIndexInitializer(MyEnum myEnum)
private List<List<int>> NestedListWithIndexInitializer(MyEnum myEnum)
{
#if !OPT
List<List<int>> list = new List<List<int>> {
#else
List<List<int>> obj = new List<List<int>> {
#endif
return new List<List<int>> {
[0] = {
1,
2,
@ -1717,13 +1713,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.InitializerTests @@ -1717,13 +1713,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.InitializerTests
});
}
public static void Issue1390(IEnumerable<string> tokens, bool alwaysAllowAdministrators, char wireDelimiter)
public static List<KeyValuePair<string, string>> Issue1390(IEnumerable<string> tokens, bool alwaysAllowAdministrators, char wireDelimiter)
{
#if OPT
List<KeyValuePair<string, string>> obj = new List<KeyValuePair<string, string>> {
#else
List<KeyValuePair<string, string>> list = new List<KeyValuePair<string, string>> {
#endif
return new List<KeyValuePair<string, string>> {
{
"tokens",
string.Join(wireDelimiter.ToString(), tokens),

11
ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineAssignmentTest.cs

@ -32,11 +32,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -32,11 +32,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
get;
set;
}
public static int StaticProperty {
get;
set;
}
public bool BoolProperty {
get;
set;
}
public void SimpleInlineWithLocals()
{
int index;
@ -136,5 +142,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -136,5 +142,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
return InstanceProperty = GetIndex();
}
public bool BoolPropertyTest(object x)
{
return BoolProperty = (x != null);
}
}
}

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

@ -0,0 +1,301 @@ @@ -0,0 +1,301 @@
// 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 LocalFunctions
{
internal class LocalFunctions
{
private int field;
private static void Test(int x)
{
}
private static int GetInt(string a)
{
return a.Length;
}
private static string GetString(int a)
{
return a.ToString();
}
public static void StaticContextNoCapture(int length)
{
for (int i = 0; i < length; i++) {
LocalWrite("Hello " + i);
}
void LocalWrite(string s)
{
Console.WriteLine(s);
}
}
public static void StaticContextSimpleCapture(int length)
{
for (int i = 0; i < length; i++) {
LocalWrite();
}
void LocalWrite()
{
Console.WriteLine("Hello " + length);
}
}
public static void StaticContextCaptureForLoopVariable(int length)
{
int i;
for (i = 0; i < length; i++) {
LocalWrite();
}
void LocalWrite()
{
Console.WriteLine("Hello " + i + "/" + length);
}
}
public void ContextNoCapture()
{
for (int i = 0; i < field; i++) {
LocalWrite("Hello " + i);
}
void LocalWrite(string s)
{
Console.WriteLine(s);
}
}
public void ContextSimpleCapture()
{
for (int i = 0; i < field; i++) {
LocalWrite();
}
void LocalWrite()
{
Console.WriteLine("Hello " + field);
}
}
public void ContextCaptureForLoopVariable()
{
int i;
for (i = 0; i < field; i++) {
LocalWrite();
}
void LocalWrite()
{
Console.WriteLine("Hello " + i + "/" + field);
}
}
public void CapturedOutsideLoop()
{
int i = 0;
while (i < field) {
i = GetInt("asdf");
LocalWrite();
}
void LocalWrite()
{
Console.WriteLine("Hello " + i + "/" + field);
}
}
public void CapturedInForeachLoop(IEnumerable<string> args)
{
foreach (string arg2 in args) {
string arg = arg2;
LocalWrite();
void LocalWrite()
{
Console.WriteLine("Hello " + arg);
}
}
}
public void Overloading()
{
Test(5);
LocalFunctions.Test(2);
void Test(int x)
{
Console.WriteLine("x: {0}", x);
}
}
private void Name()
{
}
private void LocalFunctionHidingMethod()
{
Action action = this.Name;
Name();
action();
void Name()
{
}
}
public void NamedArgument()
{
Use(Get(1), Get(2), Get(3));
Use(Get(1), c: Get(2), b: Get(3));
int Get(int i)
{
return i;
}
void Use(int a, int b, int c)
{
Console.WriteLine(a + b + c);
}
}
public static Func<int> LambdaInLocalFunction()
{
int x = (int)Math.Pow(2.0, 10.0);
return Create();
Func<int> Create()
{
return () => x;
}
}
public static Func<int> MethodRef()
{
int x = (int)Math.Pow(2.0, 10.0);
Enumerable.Range(1, 100).Select(LocalFunction);
return null;
int LocalFunction(int y)
{
return x * y;
}
}
public static int Fib(int i)
{
return FibHelper(i);
int FibHelper(int n)
{
if (n <= 0) {
return 0;
}
return FibHelper(n - 1) + FibHelper(n - 2);
}
}
public int MutuallyRecursiveLocalFunctions()
{
return B(4) + C(3);
int A(int i)
{
if (i > 0) {
return A(i - 1) + 2 * B(i - 1) + 3 * C(i - 1);
}
return 1;
}
int B(int i)
{
if (i > 0) {
return 3 * A(i - 1) + B(i - 1);
}
return 1;
}
int C(int i)
{
if (i > 0) {
return 2 * A(i - 1) + C(i - 1);
}
return 1;
}
}
public static int NestedLocalFunctions(int i)
{
return A();
int A()
{
double x = Math.Pow(10.0, 2.0);
return B();
int B()
{
return i + (int)x;
}
}
}
public static int LocalFunctionInLambda(IEnumerable<int> xs)
{
return xs.First(delegate(int x) {
return Do();
bool Do()
{
return x == 3;
}
});
}
public static IEnumerable<int> YieldReturn(int n)
{
return GetNumbers();
IEnumerable<int> GetNumbers()
{
for (int i = 0; i < n; i++) {
yield return i;
}
}
}
//public static void LocalFunctionInUsing()
//{
// using (MemoryStream memoryStream = new MemoryStream()) {
// Do();
// void Do()
// {
// memoryStream.WriteByte(42);
// }
// }
//}
}
}

52
ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullableRefTypes.cs

@ -1,15 +1,20 @@ @@ -1,15 +1,20 @@
#nullable enable
using System;
using System.Collections.Generic;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
public class NullableRefTypes
public class T01_NullableRefTypes
{
private string field_string;
private string? field_nullable_string;
private dynamic? field_nullable_dynamic;
private Dictionary<string?, string> field_generic;
private Dictionary<int, string?[]> field_generic2;
private Dictionary<int?, string?[]> field_generic3;
private KeyValuePair<string?, string> field_generic_value_type;
private KeyValuePair<string?, string>? field_generic_nullable_value_type;
private (string, string?, string) field_tuple;
private string[]?[] field_array;
private Dictionary<(string, string?), (int, string[]?, string?[])> field_complex;
@ -28,5 +33,50 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -28,5 +33,50 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
return field_nullable_string?.Length + arr?.Length;
}
public void GenericNullable<T1, T2>((T1?, T1, T2, T2?, T1, T1?) x) where T1 : class where T2 : struct
{
}
public T ByRef<T>(ref T t)
{
return t;
}
public void CallByRef(ref string a, ref string? b)
{
ByRef(ref a).ToString();
ByRef(ref b)!.ToString();
}
}
public class T02_EverythingIsNullableInHere
{
private string? field1;
private object? field2;
// value types are irrelevant for the nullability attributes:
private int field3;
private int? field4;
public string? Property {
get;
set;
}
public event EventHandler? Event;
}
public class T03_EverythingIsNotNullableInHere
{
private string field1;
private object field2;
// value types are irrelevant for the nullability attributes:
private int field3;
private int? field4;
public string Property {
get;
set;
}
public event EventHandler Event;
}
}

10
ICSharpCode.Decompiler.Tests/TestCases/Pretty/OptionalArguments.cs

@ -97,6 +97,16 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -97,6 +97,16 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
};
}
private static string GetStr(int unused)
{
return " ";
}
public static string Issue1567(string str1, string str2)
{
return string.Concat(str1.Replace('"', '\''), str2: str2.Replace('"', '\''), str1: GetStr(42));
}
private void CallerMemberName([CallerMemberName] string memberName = null)
{

37
ICSharpCode.Decompiler/CSharp/Resolver/RenameCallbackArguments.cs → ICSharpCode.Decompiler.Tests/TestCases/Pretty/OutVariables.cs

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
// Copyright (c) 2010-2014 AlphaSierraPapa for the SharpDevelop Team
// 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
@ -17,26 +17,29 @@ @@ -17,26 +17,29 @@
// DEALINGS IN THE SOFTWARE.
using System;
using ICSharpCode.Decompiler.CSharp.Syntax;
using System.Collections.Generic;
namespace ICSharpCode.Decompiler.CSharp.Resolver
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
/// <summary>
/// Arguments for the callback of <see cref="FindReferences.RenameReferencesInFile"/>.
/// </summary>
public class RenameCallbackArguments
public class PatternMatching
{
public AstNode NodeToReplace { get; private set; }
public AstNode NewNode { get; private set; }
public RenameCallbackArguments(AstNode nodeToReplace, AstNode newNode)
public static void OutVarInShortCircuit(Dictionary<int, string> d)
{
if (nodeToReplace == null)
throw new ArgumentNullException("nodeToReplace");
if (newNode == null)
throw new ArgumentNullException("newNode");
this.NodeToReplace = nodeToReplace;
this.NewNode = newNode;
if (d.Count > 2 && d.TryGetValue(42, out string value)) {
Console.WriteLine(value);
}
}
public static Action CapturedOutVarInShortCircuit(Dictionary<int, string> d)
{
// Note: needs reasoning about "definitely assigned if true"
// to ensure that the value is initialized when the delegate is declared.
if (d.Count > 2 && d.TryGetValue(42, out string value)) {
return delegate {
Console.WriteLine(value);
};
}
return null;
}
}
}

11
ICSharpCode.Decompiler.Tests/TestCases/Pretty/QualifierTests.cs

@ -221,12 +221,21 @@ namespace ICSharpCode.Decompiler.Tests.Pretty @@ -221,12 +221,21 @@ namespace ICSharpCode.Decompiler.Tests.Pretty
{
}
#if CS72
public static void Do(this ref DateTime test)
{
}
#endif
public static void Do2(this int test)
public static void Do2(this int test, DateTime date)
{
test.Do();
((IEnumerable<int>)null).Any();
((object)null).Do();
#if CS72
date.Do();
#endif
}
}
}

135
ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs

@ -2,10 +2,33 @@ @@ -2,10 +2,33 @@
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
internal static class Ext
{
public static void ExtOnRef(this ref RefLocalsAndReturns.NormalStruct s)
{
}
public static void ExtOnIn(this in RefLocalsAndReturns.NormalStruct s)
{
}
public static void ExtOnRef(this ref RefLocalsAndReturns.ReadOnlyStruct s)
{
}
public static void ExtOnIn(this in RefLocalsAndReturns.ReadOnlyStruct s)
{
}
public static void ExtOnRef(this ref RefLocalsAndReturns.ReadOnlyRefStruct s)
{
}
public static void ExtOnIn(this in RefLocalsAndReturns.ReadOnlyRefStruct s)
{
}
}
internal class RefLocalsAndReturns
{
public delegate ref T RefFunc<T>();
public delegate ref readonly T ReadOnlyRefFunc<T>();
public delegate ref TReturn RefFunc<T1, TReturn>(T1 param1);
public ref struct RefStruct
{
@ -35,6 +58,28 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -35,6 +58,28 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
}
private static int[] numbers = new int[10] {
1,
3,
7,
15,
31,
63,
127,
255,
511,
1023
};
private static string[] strings = new string[2] {
"Hello",
"World"
};
private static string NullString = "";
private static int DefaultInt = 0;
public static ref T GetRef<T>()
{
throw new NotImplementedException();
@ -80,5 +125,95 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -80,5 +125,95 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
ReadOnlyStruct readOnlyStruct = rs;
readOnlyStruct.Method();
}
public static TReturn Invoker<T1, TReturn>(RefFunc<T1, TReturn> action, T1 value)
{
return action(value);
}
public static ref int FindNumber(int target)
{
for (int i = 0; i < numbers.Length; i++) {
if (numbers[i] >= target) {
return ref numbers[i];
}
}
return ref numbers[0];
}
public static ref int LastNumber()
{
return ref numbers[numbers.Length - 1];
}
public static ref int ElementAtOrDefault(int index)
{
if (index >= 0 && index < numbers.Length) {
return ref numbers[index];
}
return ref DefaultInt;
}
public static ref int LastOrDefault()
{
if (numbers.Length != 0) {
return ref numbers[numbers.Length - 1];
}
return ref DefaultInt;
}
public static void DoubleNumber(ref int num)
{
Console.WriteLine("old: " + num);
num *= 2;
Console.WriteLine("new: " + num);
}
public static ref string GetOrSetString(int index)
{
if (index < 0 || index >= strings.Length) {
return ref NullString;
}
return ref strings[index];
}
public void CallSiteTests(NormalStruct s, ReadOnlyStruct r, ReadOnlyRefStruct rr)
{
s.ExtOnIn();
s.ExtOnRef();
r.ExtOnIn();
r.ExtOnRef();
rr.ExtOnIn();
rr.ExtOnRef();
CallOnInParam(in s, in r);
}
public void RefReassignment(ref NormalStruct s)
{
ref NormalStruct @ref = ref GetRef<NormalStruct>();
RefReassignment(ref @ref);
@ref = ref GetRef<NormalStruct>();
RefReassignment(ref @ref.GetHashCode() == 4 ? ref @ref : ref s);
}
public static void Main(string[] args)
{
DoubleNumber(ref args.Length == 1 ? ref numbers[0] : ref DefaultInt);
DoubleNumber(ref FindNumber(32));
Console.WriteLine(string.Join(", ", numbers));
DoubleNumber(ref LastNumber());
Console.WriteLine(string.Join(", ", numbers));
Console.WriteLine(GetOrSetString(0));
GetOrSetString(0) = "Goodbye";
Console.WriteLine(string.Join(" ", strings));
GetOrSetString(5) = "Here I mutated the null value!?";
Console.WriteLine(GetOrSetString(-5));
Console.WriteLine(Invoker((int x) => ref numbers[x], 0));
Console.WriteLine(LastOrDefault());
LastOrDefault() = 10000;
Console.WriteLine(ElementAtOrDefault(-5));
}
}
}

60
ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.cs

@ -38,6 +38,15 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -38,6 +38,15 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
}
public struct GenericStruct<T>
{
public T Field;
public T Property {
get;
set;
}
}
public ValueTuple VT0;
public ValueTuple<int> VT1;
public ValueTuple<int, int, int, int, int, int, int, ValueTuple> VT7EmptyRest;
@ -88,6 +97,36 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -88,6 +97,36 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public object NotTargetTyping => ((string)null, (object)1, (Action)delegate {
});
public void UnnamedTupleOut(out (int, string, Action, dynamic) tuple)
{
tuple = (42, "Hello", Console.WriteLine, null);
}
public void UnnamedTupleIn(in (int, string, Action, dynamic) tuple)
{
}
public void UnnamedTupleRef(ref (int, string, Action, dynamic) tuple)
{
}
public void NamedTupleOut(out (int A, string B, Action C, dynamic D) tuple)
{
tuple = (42, "Hello", Console.WriteLine, null);
}
public void NamedTupleIn(in (int A, string B, Action C, dynamic D) tuple)
{
}
public void NamedTupleRef(ref (int A, string B, Action C, dynamic D) tuple)
{
}
public void UseDict()
{
if (TupleDict.Count > 10) {
@ -140,5 +179,26 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -140,5 +179,26 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
(2, "b")
});
}
public void DynamicTuple((dynamic A, dynamic B) a)
{
a.A.DynamicCall();
a.B.Dynamic = 42;
}
public void GenericStructWithElementNames(GenericStruct<(int A, int B)> s)
{
Console.WriteLine(s.Field.A + s.Property.B);
}
public void RefCallSites(out (int, string, Action, dynamic) tuple)
{
UnnamedTupleOut(out tuple);
UnnamedTupleIn(in tuple);
UnnamedTupleRef(ref tuple);
NamedTupleOut(out tuple);
NamedTupleIn(in tuple);
NamedTupleRef(ref tuple);
}
}
}

134
ICSharpCode.Decompiler.Tests/TestCases/Pretty/UserDefinedConversions.cs

@ -0,0 +1,134 @@ @@ -0,0 +1,134 @@
// Copyright (c) 2019 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
internal class T01Issue1574
{
private struct A
{
private bool val;
public static implicit operator bool(A a)
{
return a.val;
}
}
private struct C
{
private int val;
public static implicit operator C(bool b)
{
return default(C);
}
}
private C ChainedConversion()
{
return (bool)default(A);
}
public void Call_Overloaded()
{
Overloaded((bool)default(A));
}
private void Overloaded(A a)
{
}
private void Overloaded(bool a)
{
}
}
internal class T02BothDirectAndChainedConversionPossible
{
private struct A
{
private bool val;
public static implicit operator bool(A a)
{
return a.val;
}
}
private struct C
{
private int val;
public static implicit operator C(bool b)
{
return default(C);
}
public static implicit operator C(A a)
{
return default(C);
}
public static bool operator ==(C a, C b)
{
return true;
}
public static bool operator !=(C a, C b)
{
return false;
}
}
private C DirectConvert(A a)
{
return a;
}
private C IndirectConvert(A a)
{
return (bool)a;
}
private C? LiftedDirectConvert(A? a)
{
return a;
}
private C? LiftedIndirectConvert(A? a)
{
return (bool?)a;
}
private bool Compare(A a, C c)
{
return a == c;
}
private void LiftedCompare(A? a, C? c)
{
UseBool(a == c);
UseBool(a == default(C));
UseBool(c == default(A));
}
private void UseBool(bool b)
{
}
}
}

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

@ -335,5 +335,26 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -335,5 +335,26 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
yield return val;
}
}
public static IEnumerable<int> MultipleYieldBreakInTryFinally(int i)
{
try {
if (i == 2) {
yield break;
}
while (i < 40) {
if (i % 2 == 0) {
yield break;
}
i++;
yield return i;
}
} finally {
Console.WriteLine("finally");
}
Console.WriteLine("normal exit");
}
}
}

45
ICSharpCode.Decompiler.Tests/TypeSystem/TypeSystemLoaderTests.cs

@ -707,8 +707,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem @@ -707,8 +707,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem
public void InOutParametersOnRefMethod()
{
IParameter p = GetTypeDefinition(typeof(NonCustomAttributes)).Methods.Single(m => m.Name == "DllMethod").Parameters.Single();
Assert.IsTrue(p.IsRef);
Assert.IsFalse(p.IsOut);
Assert.AreEqual(ReferenceKind.Ref, p.ReferenceKind);
var attr = p.GetAttributes().ToList();
Assert.AreEqual(2, attr.Count);
Assert.AreEqual("System.Runtime.InteropServices.InAttribute", attr[0].AttributeType.FullName);
@ -728,9 +727,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem @@ -728,9 +727,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem
{
IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithOutParameter").Parameters.Single();
Assert.IsFalse(p.IsOptional);
Assert.IsFalse(p.IsRef);
Assert.IsTrue(p.IsOut);
Assert.IsFalse(p.IsIn);
Assert.AreEqual(ReferenceKind.Out, p.ReferenceKind);
Assert.AreEqual(0, p.GetAttributes().Count());
Assert.IsTrue(p.Type.Kind == TypeKind.ByReference);
}
@ -740,9 +737,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem @@ -740,9 +737,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem
{
IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithRefParameter").Parameters.Single();
Assert.IsFalse(p.IsOptional);
Assert.IsTrue(p.IsRef);
Assert.IsFalse(p.IsOut);
Assert.IsFalse(p.IsIn);
Assert.AreEqual(ReferenceKind.Ref, p.ReferenceKind);
Assert.AreEqual(0, p.GetAttributes().Count());
Assert.IsTrue(p.Type.Kind == TypeKind.ByReference);
}
@ -752,9 +747,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem @@ -752,9 +747,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem
{
IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithInParameter").Parameters.Single();
Assert.IsFalse(p.IsOptional);
Assert.IsFalse(p.IsRef);
Assert.IsFalse(p.IsOut);
Assert.IsTrue(p.IsIn);
Assert.AreEqual(ReferenceKind.In, p.ReferenceKind);
Assert.AreEqual(0, p.GetAttributes().Count());
Assert.IsTrue(p.Type.Kind == TypeKind.ByReference);
}
@ -764,8 +757,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem @@ -764,8 +757,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem
{
IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithParamsArray").Parameters.Single();
Assert.IsFalse(p.IsOptional);
Assert.IsFalse(p.IsRef);
Assert.IsFalse(p.IsOut);
Assert.AreEqual(ReferenceKind.None, p.ReferenceKind);
Assert.IsTrue(p.IsParams);
Assert.AreEqual(0, p.GetAttributes().Count());
Assert.IsTrue(p.Type.Kind == TypeKind.Array);
@ -776,8 +768,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem @@ -776,8 +768,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem
{
IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithOptionalParameter").Parameters.Single();
Assert.IsTrue(p.IsOptional);
Assert.IsFalse(p.IsRef);
Assert.IsFalse(p.IsOut);
Assert.AreEqual(ReferenceKind.None, p.ReferenceKind);
Assert.IsFalse(p.IsParams);
Assert.IsTrue(p.HasConstantValueInSignature);
Assert.AreEqual(0, p.GetAttributes().Count());
@ -789,8 +780,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem @@ -789,8 +780,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem
{
IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithExplicitOptionalParameter").Parameters.Single();
Assert.IsTrue(p.IsOptional);
Assert.IsFalse(p.IsRef);
Assert.IsFalse(p.IsOut);
Assert.AreEqual(ReferenceKind.None, p.ReferenceKind);
Assert.IsFalse(p.IsParams);
Assert.IsFalse(p.HasConstantValueInSignature);
// explicit optional parameter appears in type system if it's read from C#, but not when read from IL
@ -802,8 +792,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem @@ -802,8 +792,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem
{
IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithEnumOptionalParameter").Parameters.Single();
Assert.IsTrue(p.IsOptional);
Assert.IsFalse(p.IsRef);
Assert.IsFalse(p.IsOut);
Assert.AreEqual(ReferenceKind.None, p.ReferenceKind);
Assert.IsFalse(p.IsParams);
Assert.IsTrue(p.HasConstantValueInSignature);
Assert.AreEqual(0, p.GetAttributes().Count());
@ -815,8 +804,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem @@ -815,8 +804,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem
{
IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithOptionalNullableParameter").Parameters.Single();
Assert.IsTrue(p.IsOptional);
Assert.IsFalse(p.IsRef);
Assert.IsFalse(p.IsOut);
Assert.AreEqual(ReferenceKind.None, p.ReferenceKind);
Assert.IsFalse(p.IsParams);
Assert.IsTrue(p.HasConstantValueInSignature);
Assert.AreEqual(0, p.GetAttributes().Count());
@ -828,8 +816,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem @@ -828,8 +816,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem
{
IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithOptionalLongParameter").Parameters.Single();
Assert.IsTrue(p.IsOptional);
Assert.IsFalse(p.IsRef);
Assert.IsFalse(p.IsOut);
Assert.AreEqual(ReferenceKind.None, p.ReferenceKind);
Assert.IsFalse(p.IsParams);
Assert.IsTrue(p.HasConstantValueInSignature);
Assert.AreEqual(1L, p.GetConstantValue());
@ -841,8 +828,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem @@ -841,8 +828,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem
{
IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithOptionalNullableLongParameter").Parameters.Single();
Assert.IsTrue(p.IsOptional);
Assert.IsFalse(p.IsRef);
Assert.IsFalse(p.IsOut);
Assert.AreEqual(ReferenceKind.None, p.ReferenceKind);
Assert.IsFalse(p.IsParams);
Assert.IsTrue(p.HasConstantValueInSignature);
Assert.AreEqual(1L, p.GetConstantValue());
@ -854,8 +840,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem @@ -854,8 +840,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem
{
IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithOptionalDecimalParameter").Parameters.Single();
Assert.IsTrue(p.IsOptional);
Assert.IsFalse(p.IsRef);
Assert.IsFalse(p.IsOut);
Assert.AreEqual(ReferenceKind.None, p.ReferenceKind);
Assert.IsFalse(p.IsParams);
Assert.IsTrue(p.HasConstantValueInSignature);
Assert.AreEqual(1M, p.GetConstantValue());
@ -867,8 +852,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem @@ -867,8 +852,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem
{
IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "VarArgsMethod").Parameters.Single();
Assert.IsFalse(p.IsOptional);
Assert.IsFalse(p.IsRef);
Assert.IsFalse(p.IsOut);
Assert.AreEqual(ReferenceKind.None, p.ReferenceKind);
Assert.IsFalse(p.IsParams);
Assert.AreEqual(TypeKind.ArgList, p.Type.Kind);
Assert.AreEqual("", p.Name);
@ -879,8 +863,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem @@ -879,8 +863,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem
{
IParameter p = GetTypeDefinition(typeof(VarArgsCtor)).Methods.Single(m => m.IsConstructor).Parameters.Single();
Assert.IsFalse(p.IsOptional);
Assert.IsFalse(p.IsRef);
Assert.IsFalse(p.IsOut);
Assert.AreEqual(ReferenceKind.None, p.ReferenceKind);
Assert.IsFalse(p.IsParams);
Assert.AreEqual(TypeKind.ArgList, p.Type.Kind);
Assert.AreEqual("", p.Name);

14
ICSharpCode.Decompiler/CSharp/Annotations.cs

@ -240,4 +240,18 @@ namespace ICSharpCode.Decompiler.CSharp @@ -240,4 +240,18 @@ namespace ICSharpCode.Decompiler.CSharp
this.Leave = leave;
}
}
/// <summary>
/// Annotates an expression when an implicit user-defined conversion was omitted.
/// </summary>
public class ImplicitConversionAnnotation
{
public readonly ConversionResolveResult ConversionResolveResult;
public IType TargetType => ConversionResolveResult.Type;
public ImplicitConversionAnnotation(ConversionResolveResult conversionResolveResult)
{
this.ConversionResolveResult = conversionResolveResult;
}
}
}

156
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -131,7 +131,6 @@ namespace ICSharpCode.Decompiler.CSharp @@ -131,7 +131,6 @@ namespace ICSharpCode.Decompiler.CSharp
// copy-propated (turned into two separate assignments of the constant).
// After is necessary because the assigned value might involve null coalescing/etc.
new StatementTransform(new ILInlining(), new TransformAssignment()),
new CopyPropagation(),
new StatementTransform(
// per-block transforms that depend on each other, and thus need to
// run interleaved (statement by statement).
@ -155,7 +154,11 @@ namespace ICSharpCode.Decompiler.CSharp @@ -155,7 +154,11 @@ namespace ICSharpCode.Decompiler.CSharp
}
},
new ProxyCallReplacer(),
new FixRemainingIncrements(),
new CopyPropagation(),
new DelegateConstruction(),
new LocalFunctionDecompiler(),
new TransformDisplayClassUsage(),
new HighLevelLoopTransform(),
new ReduceNestingTransform(),
new IntroduceDynamicTypeOnLocals(),
@ -395,7 +398,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -395,7 +398,7 @@ namespace ICSharpCode.Decompiler.CSharp
return new DecompilerTypeSystem(file, resolver);
}
TypeSystemAstBuilder CreateAstBuilder(ITypeResolveContext decompilationContext)
static TypeSystemAstBuilder CreateAstBuilder(ITypeResolveContext decompilationContext)
{
var typeSystemAstBuilder = new TypeSystemAstBuilder();
typeSystemAstBuilder.ShowAttributes = true;
@ -620,39 +623,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -620,39 +623,7 @@ namespace ICSharpCode.Decompiler.CSharp
var memberRef = module.Metadata.GetMemberReference((MemberReferenceHandle)token);
if (memberRef.GetKind() != MemberReferenceKind.Field)
continue;
switch (memberRef.Parent.Kind) {
case HandleKind.TypeReference:
// This should never happen in normal code, because we are looking at nested types
// If it's not a nested type, it can't be a reference to the state machine anyway, and
// those should be either TypeDef or TypeSpec.
continue;
case HandleKind.TypeDefinition:
fsmTypeDef = (TypeDefinitionHandle)memberRef.Parent;
break;
case HandleKind.TypeSpecification:
var ts = module.Metadata.GetTypeSpecification((TypeSpecificationHandle)memberRef.Parent);
if (ts.Signature.IsNil)
continue;
// Do a quick scan using BlobReader
var signature = module.Metadata.GetBlobReader(ts.Signature);
// When dealing with FSM implementations, we can safely assume that if it's a type spec,
// it must be a generic type instance.
if (signature.ReadByte() != (byte)SignatureTypeCode.GenericTypeInstance)
continue;
// Skip over the rawTypeKind: value type or class
var rawTypeKind = signature.ReadCompressedInteger();
if (rawTypeKind < 17 || rawTypeKind > 18)
continue;
// Only read the generic type, ignore the type arguments
var genericType = signature.ReadTypeHandle();
// Again, we assume this is a type def, because we are only looking at nested types
if (genericType.Kind != HandleKind.TypeDefinition)
continue;
fsmTypeDef = (TypeDefinitionHandle)genericType;
break;
default:
continue;
}
fsmTypeDef = ExtractDeclaringType(memberRef);
break;
default:
continue;
@ -662,10 +633,10 @@ namespace ICSharpCode.Decompiler.CSharp @@ -662,10 +633,10 @@ namespace ICSharpCode.Decompiler.CSharp
// Must be a nested type of the containing type.
if (fsmType.GetDeclaringType() != declaringType)
break;
if (!processedNestedTypes.Add(fsmTypeDef))
break;
if (YieldReturnDecompiler.IsCompilerGeneratorEnumerator(fsmTypeDef, module.Metadata)
|| AsyncAwaitDecompiler.IsCompilerGeneratedStateMachine(fsmTypeDef, module.Metadata)) {
if (!processedNestedTypes.Add(fsmTypeDef))
break;
foreach (var h in fsmType.GetMethods()) {
if (module.MethodSemanticsLookup.GetSemantics(h).Item2 != 0)
continue;
@ -680,9 +651,57 @@ namespace ICSharpCode.Decompiler.CSharp @@ -680,9 +651,57 @@ namespace ICSharpCode.Decompiler.CSharp
case ILOpCode.Ldftn:
// deal with ldftn instructions, i.e., lambdas
token = MetadataTokenHelpers.EntityHandleOrNil(blob.ReadInt32());
if (!token.IsNil && token.Kind == HandleKind.MethodDefinition) {
if (((MethodDefinitionHandle)token).IsCompilerGeneratedOrIsInCompilerGeneratedClass(module.Metadata))
connectedMethods.Enqueue((MethodDefinitionHandle)token);
if (token.IsNil)
continue;
TypeDefinitionHandle closureTypeHandle;
switch (token.Kind) {
case HandleKind.MethodDefinition:
if (((MethodDefinitionHandle)token).IsCompilerGeneratedOrIsInCompilerGeneratedClass(module.Metadata)) {
connectedMethods.Enqueue((MethodDefinitionHandle)token);
}
continue;
case HandleKind.MemberReference:
var memberRef = module.Metadata.GetMemberReference((MemberReferenceHandle)token);
if (memberRef.GetKind() != MemberReferenceKind.Method)
continue;
closureTypeHandle = ExtractDeclaringType(memberRef);
if (!closureTypeHandle.IsNil) {
var closureType = module.Metadata.GetTypeDefinition(closureTypeHandle);
// Must be a nested type of the containing type.
if (closureType.GetDeclaringType() != declaringType)
break;
if (!processedNestedTypes.Add(closureTypeHandle))
break;
foreach (var m in closureType.GetMethods()) {
connectedMethods.Enqueue(m);
}
break;
}
break;
default:
continue;
}
break;
case ILOpCode.Call:
case ILOpCode.Callvirt:
// deal with call/callvirt instructions, i.e., local function invocations
token = MetadataTokenHelpers.EntityHandleOrNil(blob.ReadInt32());
if (token.IsNil)
continue;
switch (token.Kind) {
case HandleKind.MethodDefinition:
break;
case HandleKind.MethodSpecification:
var methodSpec = module.Metadata.GetMethodSpecification((MethodSpecificationHandle)token);
if (methodSpec.Method.IsNil || methodSpec.Method.Kind != HandleKind.MethodDefinition)
continue;
token = methodSpec.Method;
break;
default:
continue;
}
if (LocalFunctionDecompiler.IsLocalFunctionMethod(module, (MethodDefinitionHandle)token)) {
connectedMethods.Enqueue((MethodDefinitionHandle)token);
}
break;
default:
@ -692,6 +711,40 @@ namespace ICSharpCode.Decompiler.CSharp @@ -692,6 +711,40 @@ namespace ICSharpCode.Decompiler.CSharp
}
info.AddMapping(parent, part);
TypeDefinitionHandle ExtractDeclaringType(MemberReference memberRef)
{
switch (memberRef.Parent.Kind) {
case HandleKind.TypeReference:
// This should never happen in normal code, because we are looking at nested types
// If it's not a nested type, it can't be a reference to the state machine or lambda anyway, and
// those should be either TypeDef or TypeSpec.
return default;
case HandleKind.TypeDefinition:
return (TypeDefinitionHandle)memberRef.Parent;
case HandleKind.TypeSpecification:
var ts = module.Metadata.GetTypeSpecification((TypeSpecificationHandle)memberRef.Parent);
if (ts.Signature.IsNil)
return default;
// Do a quick scan using BlobReader
var signature = module.Metadata.GetBlobReader(ts.Signature);
// When dealing with FSM implementations, we can safely assume that if it's a type spec,
// it must be a generic type instance.
if (signature.ReadByte() != (byte)SignatureTypeCode.GenericTypeInstance)
return default;
// Skip over the rawTypeKind: value type or class
var rawTypeKind = signature.ReadCompressedInteger();
if (rawTypeKind < 17 || rawTypeKind > 18)
return default;
// Only read the generic type, ignore the type arguments
var genericType = signature.ReadTypeHandle();
// Again, we assume this is a type def, because we are only looking at nested types
if (genericType.Kind != HandleKind.TypeDefinition)
return default;
return (TypeDefinitionHandle)genericType;
}
return default;
}
}
/// <summary>
@ -1373,7 +1426,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1373,7 +1426,7 @@ namespace ICSharpCode.Decompiler.CSharp
}
var fieldDefinition = metadata.GetFieldDefinition((FieldDefinitionHandle)field.MetadataToken);
if (fieldDefinition.HasFlag(System.Reflection.FieldAttributes.HasFieldRVA)) {
// Field data as specified in II.16.3.2 of ECMA-335 6th edition:
// Field data as specified in II.16.3.1 of ECMA-335 6th edition:
// .data I_X = int32(123)
// .field public static int32 _x at I_X
string message;
@ -1406,27 +1459,6 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1406,27 +1459,6 @@ namespace ICSharpCode.Decompiler.CSharp
return false;
}
internal static bool IsFixedField(MetadataReader metadata, FieldDefinitionHandle handle, out IType type, out int elementCount)
{
type = null;
elementCount = 0;
var field = metadata.GetFieldDefinition(handle);
foreach (var h in field.GetCustomAttributes()) {
var customAttribute = metadata.GetCustomAttribute(h);
if (customAttribute.IsKnownAttribute(metadata, KnownAttribute.FixedBuffer)) {
var value = customAttribute.DecodeValue(MetadataExtensions.minimalCorlibTypeProvider);
if (value.FixedArguments.Length == 2) {
if (value.FixedArguments[0].Value is IType trr && value.FixedArguments[1].Value is int length) {
type = trr;
elementCount = length;
return true;
}
}
}
}
return false;
}
EntityDeclaration DoDecompile(IProperty property, DecompileRun decompileRun, ITypeResolveContext decompilationContext)
{
Debug.Assert(decompilationContext.CurrentMember == property);

227
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

@ -54,11 +54,16 @@ namespace ICSharpCode.Decompiler.CSharp @@ -54,11 +54,16 @@ namespace ICSharpCode.Decompiler.CSharp
public bool IsExpandedForm;
public int Length => Arguments.Length;
private int GetActualArgumentCount()
{
if (FirstOptionalArgumentIndex < 0)
return Arguments.Length;
return FirstOptionalArgumentIndex;
}
public IEnumerable<ResolveResult> GetArgumentResolveResults(int skipCount = 0)
{
return FirstOptionalArgumentIndex < 0
? Arguments.Skip(skipCount).Select(a => a.ResolveResult)
: Arguments.Skip(skipCount).Take(FirstOptionalArgumentIndex).Select(a => a.ResolveResult);
return Arguments.Skip(skipCount).Take(GetActualArgumentCount()).Select(a => a.ResolveResult);
}
public IEnumerable<Expression> GetArgumentExpressions(int skipCount = 0)
@ -77,22 +82,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -77,22 +82,12 @@ namespace ICSharpCode.Decompiler.CSharp
}
}
}
int argumentCount = GetActualArgumentCount();
if (ArgumentNames == null) {
if (FirstOptionalArgumentIndex < 0)
return Arguments.Skip(skipCount).Select(arg => arg.Expression);
return Arguments.Skip(skipCount).Take(FirstOptionalArgumentIndex).Select(arg => arg.Expression);
return Arguments.Skip(skipCount).Take(argumentCount).Select(arg => arg.Expression);
} else {
Debug.Assert(skipCount == 0);
if (FirstOptionalArgumentIndex < 0) {
return Arguments.Zip(ArgumentNames,
(arg, name) => {
if (name == null)
return arg.Expression;
else
return new NamedArgumentExpression(name, arg);
});
}
return Arguments.Take(FirstOptionalArgumentIndex).Zip(ArgumentNames.Take(FirstOptionalArgumentIndex),
return Arguments.Take(argumentCount).Zip(ArgumentNames.Take(argumentCount),
(arg, name) => {
if (name == null)
return arg.Expression;
@ -177,13 +172,32 @@ namespace ICSharpCode.Decompiler.CSharp @@ -177,13 +172,32 @@ namespace ICSharpCode.Decompiler.CSharp
IReadOnlyList<int> argumentToParameterMap = null,
IType constrainedTo = null)
{
if (method.IsExplicitInterfaceImplementation && callOpCode == OpCode.Call) {
// Direct non-virtual call to explicit interface implementation.
// This can't really be represented in C#, but at least in the case where
// the class is sealed, we can equivalently call the interface member instead:
var interfaceMembers = method.ExplicitlyImplementedInterfaceMembers.ToList();
if (method.DeclaringTypeDefinition?.Kind == TypeKind.Class && method.DeclaringTypeDefinition.IsSealed && interfaceMembers.Count == 1) {
method = (IMethod)interfaceMembers.Single();
callOpCode = OpCode.CallVirt;
}
}
// Used for Call, CallVirt and NewObj
var expectedTargetDetails = new ExpectedTargetDetails {
CallOpCode = callOpCode
};
ILFunction localFunction = null;
if (method.IsLocalFunction) {
localFunction = expressionBuilder.ResolveLocalFunction(method);
Debug.Assert(localFunction != null);
}
TranslatedExpression target;
if (callOpCode == OpCode.NewObj) {
target = default(TranslatedExpression); // no target
} else if (method.IsLocalFunction && localFunction != null) {
target = new IdentifierExpression(localFunction.Name)
.WithoutILInstruction()
.WithRR(ToMethodGroup(method, localFunction));
} else {
target = expressionBuilder.TranslateTarget(
callArguments.FirstOrDefault(),
@ -210,6 +224,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -210,6 +224,12 @@ namespace ICSharpCode.Decompiler.CSharp
var argumentList = BuildArgumentList(expectedTargetDetails, target.ResolveResult, method,
firstParamIndex, callArguments, argumentToParameterMap);
if (method.IsLocalFunction) {
return new InvocationExpression(target, argumentList.GetArgumentExpressions())
.WithRR(new CSharpInvocationResolveResult(target.ResolveResult, method,
argumentList.GetArgumentResolveResults().ToList(), isExpandedForm: argumentList.IsExpandedForm));
}
if (method is VarArgInstanceMethod) {
argumentList.FirstOptionalArgumentIndex = -1;
@ -334,7 +354,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -334,7 +354,7 @@ namespace ICSharpCode.Decompiler.CSharp
// settings.AlwaysCastTargetsOfExplicitInterfaceImplementationCalls == true is used in Windows Forms' InitializeComponent methods.
if (method.IsExplicitInterfaceImplementation && (target.Expression is ThisReferenceExpression || settings.AlwaysCastTargetsOfExplicitInterfaceImplementationCalls)) {
var interfaceMember = method.ExplicitlyImplementedInterfaceMembers.First();
var castExpression = new CastExpression(expressionBuilder.ConvertType(interfaceMember.DeclaringType), target.Expression);
var castExpression = new CastExpression(expressionBuilder.ConvertType(interfaceMember.DeclaringType), target.Expression.Detach());
methodName = interfaceMember.Name;
targetExpr = new MemberReferenceExpression(castExpression, methodName);
typeArgumentList = ((MemberReferenceExpression)targetExpr).TypeArguments;
@ -386,7 +406,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -386,7 +406,7 @@ namespace ICSharpCode.Decompiler.CSharp
argumentList.ArgumentNames = null;
argumentList.AddNamesToPrimitiveValues = false;
var transform = GetRequiredTransformationsForCall(expectedTargetDetails, method, ref unused,
ref argumentList, CallTransformation.None, out IParameterizedMember foundMethod);
ref argumentList, CallTransformation.None, out _);
Debug.Assert(transform == CallTransformation.None || transform == CallTransformation.NoOptionalArgumentAllowed);
// Calls with only one argument do not need an array initializer expression to wrap them.
@ -565,9 +585,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -565,9 +585,8 @@ namespace ICSharpCode.Decompiler.CSharp
}
}
private ArgumentList BuildArgumentList(ExpectedTargetDetails expectedTargetDetails, ResolveResult target, IMethod method, int firstParamIndex,
IReadOnlyList<ILInstruction> callArguments, IReadOnlyList<int> argumentToParameterMap)
private ArgumentList BuildArgumentList(ExpectedTargetDetails expectedTargetDetails, ResolveResult target, IMethod method,
int firstParamIndex, IReadOnlyList<ILInstruction> callArguments, IReadOnlyList<int> argumentToParameterMap)
{
ArgumentList list = new ArgumentList();
@ -633,8 +652,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -633,8 +652,8 @@ namespace ICSharpCode.Decompiler.CSharp
arg = arg.ConvertTo(parameterType, expressionBuilder, allowImplicitConversion: arg.Type.Kind != TypeKind.Dynamic);
if (parameter.IsOut) {
arg = ExpressionBuilder.ChangeDirectionExpressionToOut(arg);
if (parameter.ReferenceKind != ReferenceKind.None) {
arg = ExpressionBuilder.ChangeDirectionExpressionTo(arg, parameter.ReferenceKind);
}
arguments.Add(arg);
@ -748,7 +767,9 @@ namespace ICSharpCode.Decompiler.CSharp @@ -748,7 +767,9 @@ namespace ICSharpCode.Decompiler.CSharp
if (expressionBuilder.HidesVariableWithName(method.Name)) {
requireTarget = true;
} else {
if (method.IsStatic)
if (method.IsLocalFunction)
requireTarget = false;
else if (method.IsStatic)
requireTarget = !expressionBuilder.IsCurrentOrContainingType(method.DeclaringTypeDefinition) || method.Name == ".cctor";
else if (method.Name == ".ctor")
requireTarget = true; // always use target for base/this-ctor-call, the constructor initializer pattern depends on this
@ -979,11 +1000,14 @@ namespace ICSharpCode.Decompiler.CSharp @@ -979,11 +1000,14 @@ namespace ICSharpCode.Decompiler.CSharp
} else if (method.IsOperator) {
IEnumerable<IParameterizedMember> operatorCandidates;
if (arguments.Count == 1) {
operatorCandidates = resolver.GetUserDefinedOperatorCandidates(arguments[0].Type, method.Name);
IType argType = NullableType.GetUnderlyingType(arguments[0].Type);
operatorCandidates = resolver.GetUserDefinedOperatorCandidates(argType, method.Name);
} else if (arguments.Count == 2) {
IType lhsType = NullableType.GetUnderlyingType(arguments[0].Type);
IType rhsType = NullableType.GetUnderlyingType(arguments[1].Type);
var hashSet = new HashSet<IParameterizedMember>();
hashSet.UnionWith(resolver.GetUserDefinedOperatorCandidates(arguments[0].Type, method.Name));
hashSet.UnionWith(resolver.GetUserDefinedOperatorCandidates(arguments[1].Type, method.Name));
hashSet.UnionWith(resolver.GetUserDefinedOperatorCandidates(lhsType, method.Name));
hashSet.UnionWith(resolver.GetUserDefinedOperatorCandidates(rhsType, method.Name));
operatorCandidates = hashSet;
} else {
operatorCandidates = EmptyList<IParameterizedMember>.Instance;
@ -1107,8 +1131,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1107,8 +1131,7 @@ namespace ICSharpCode.Decompiler.CSharp
}
var op = AssignmentOperatorType.Assign;
var parentEvent = method.AccessorOwner as IEvent;
if (parentEvent != null) {
if (method.AccessorOwner is IEvent parentEvent) {
if (method.Equals(parentEvent.AddAccessor)) {
op = AssignmentOperatorType.Add;
}
@ -1196,12 +1219,15 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1196,12 +1219,15 @@ namespace ICSharpCode.Decompiler.CSharp
ILInstruction thisArg = inst.Arguments[0];
ILInstruction func = inst.Arguments[1];
IMethod method;
ExpectedTargetDetails expectedTargetDetails = default;
switch (func.OpCode) {
case OpCode.LdFtn:
method = ((LdFtn)func).Method;
expectedTargetDetails.CallOpCode = OpCode.Call;
break;
case OpCode.LdVirtFtn:
method = ((LdVirtFtn)func).Method;
expectedTargetDetails.CallOpCode = OpCode.CallVirt;
break;
default:
throw new ArgumentException($"Unknown instruction type: {func.OpCode}");
@ -1210,14 +1236,43 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1210,14 +1236,43 @@ namespace ICSharpCode.Decompiler.CSharp
TranslatedExpression target;
IType targetType;
bool requireTarget;
if (method.IsExtensionMethod && invokeMethod != null && method.Parameters.Count - 1 == invokeMethod.Parameters.Count) {
ResolveResult result = null;
string methodName = method.Name;
// There are three possible adjustments, we can make, to solve conflicts:
// 1. add target (represented as bit 0)
// 2. add type arguments (represented as bit 1)
// 3. cast target (represented as bit 2)
int step;
if (method.IsLocalFunction) {
step = 0;
requireTarget = false;
var localFunction = expressionBuilder.ResolveLocalFunction(method);
result = ToMethodGroup(method, localFunction);
target = default;
targetType = default;
methodName = localFunction.Name;
// TODO : think about how to handle generic local functions
} else if (method.IsExtensionMethod && invokeMethod != null && method.Parameters.Count - 1 == invokeMethod.Parameters.Count) {
step = 5;
targetType = method.Parameters[0].Type;
if (targetType.Kind == TypeKind.ByReference && thisArg is Box thisArgBox) {
targetType = ((ByReferenceType)targetType).ElementType;
thisArg = thisArgBox.Argument;
}
target = expressionBuilder.Translate(thisArg, targetType);
// TODO : check if cast is necessary
target = target.ConvertTo(targetType, expressionBuilder);
requireTarget = true;
result = new MethodGroupResolveResult(
target.ResolveResult, method.Name,
new MethodListWithDeclaringType[] {
new MethodListWithDeclaringType(
null,
new[] { method }
)
},
method.TypeArguments
);
} else {
targetType = method.DeclaringType;
if (targetType.IsReferenceType == false && thisArg is Box thisArgBox) {
@ -1235,58 +1290,100 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1235,58 +1290,100 @@ namespace ICSharpCode.Decompiler.CSharp
memberDeclaringType: method.DeclaringType);
requireTarget = expressionBuilder.HidesVariableWithName(method.Name)
|| (method.IsStatic ? !expressionBuilder.IsCurrentOrContainingType(method.DeclaringTypeDefinition) : !(target.Expression is ThisReferenceExpression));
}
var expectedTargetDetails = new ExpectedTargetDetails {
CallOpCode = inst.OpCode
};
bool needsCast = false;
ResolveResult result = null;
var or = new OverloadResolution(resolver.Compilation, method.Parameters.SelectReadOnlyArray(p => new TypeResolveResult(p.Type)));
if (!requireTarget) {
result = resolver.ResolveSimpleName(method.Name, method.TypeArguments, isInvocationTarget: false);
if (result is MethodGroupResolveResult mgrr) {
or.AddMethodLists(mgrr.MethodsGroupedByDeclaringType.ToArray());
requireTarget = (or.BestCandidateErrors != OverloadResolutionErrors.None || !IsAppropriateCallTarget(expectedTargetDetails, method, or.BestCandidate));
} else {
requireTarget = true;
}
}
MemberLookup lookup = null;
if (requireTarget) {
lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentModule);
var rr = lookup.Lookup(target.ResolveResult, method.Name, method.TypeArguments, false) ;
needsCast = true;
result = rr;
if (rr is MethodGroupResolveResult mgrr) {
or.AddMethodLists(mgrr.MethodsGroupedByDeclaringType.ToArray());
needsCast = (or.BestCandidateErrors != OverloadResolutionErrors.None || !IsAppropriateCallTarget(expectedTargetDetails, method, or.BestCandidate));
var savedTarget = target;
for (step = requireTarget ? 1 : 0; step < 7; step++) {
ResolveResult targetResolveResult;
if (!method.IsLocalFunction && (step & 1) != 0) {
targetResolveResult = savedTarget.ResolveResult;
target = savedTarget;
} else {
targetResolveResult = null;
}
IReadOnlyList<IType> typeArguments;
if ((step & 2) != 0) {
typeArguments = method.TypeArguments;
} else {
typeArguments = EmptyList<IType>.Instance;
}
if (targetResolveResult != null && targetType != null && (step & 4) != 0) {
target = target.ConvertTo(targetType, expressionBuilder);
targetResolveResult = target.ResolveResult;
}
bool success = IsUnambiguousMethodReference(expectedTargetDetails, method, targetResolveResult, typeArguments, out var newResult);
if (newResult is MethodGroupResolveResult || result == null)
result = newResult;
if (success)
break;
}
}
if (needsCast) {
Debug.Assert(requireTarget);
target = target.ConvertTo(targetType, expressionBuilder);
result = lookup.Lookup(target.ResolveResult, method.Name, method.TypeArguments, false);
}
requireTarget = !method.IsLocalFunction && (step & 1) != 0;
Expression targetExpression;
Debug.Assert(result != null);
if (requireTarget) {
var mre = new MemberReferenceExpression(target, method.Name);
mre.TypeArguments.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType));
Debug.Assert(target.Expression != null);
var mre = new MemberReferenceExpression(target, methodName);
if ((step & 2) != 0)
mre.TypeArguments.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType));
mre.WithRR(result);
targetExpression = mre;
} else {
var ide = new IdentifierExpression(method.Name)
.WithRR(result);
var ide = new IdentifierExpression(methodName);
if ((step & 2) != 0)
ide.TypeArguments.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType));
ide.WithRR(result);
targetExpression = ide;
}
var oce = new ObjectCreateExpression(expressionBuilder.ConvertType(inst.Method.DeclaringType), targetExpression)
.WithILInstruction(inst)
.WithRR(new ConversionResolveResult(
inst.Method.DeclaringType,
new MemberResolveResult(target.ResolveResult, method),
result,
Conversion.MethodGroupConversion(method, func.OpCode == OpCode.LdVirtFtn, false)));
return oce;
}
bool IsUnambiguousMethodReference(ExpectedTargetDetails expectedTargetDetails, IMethod method, ResolveResult target, IReadOnlyList<IType> typeArguments, out ResolveResult result)
{
var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentModule);
var or = new OverloadResolution(resolver.Compilation,
arguments: method.Parameters.SelectReadOnlyArray(p => new TypeResolveResult(p.Type)), // there are no arguments, use parameter types
argumentNames: null, // argument names are not possible
typeArguments.ToArray(),
conversions: expressionBuilder.resolver.conversions
);
if (target == null) {
result = resolver.ResolveSimpleName(method.Name, typeArguments, isInvocationTarget: false);
if (!(result is MethodGroupResolveResult mgrr))
return false;
or.AddMethodLists(mgrr.MethodsGroupedByDeclaringType.ToArray());
} else {
result = lookup.Lookup(target, method.Name, typeArguments, isInvocation: false);
if (!(result is MethodGroupResolveResult mgrr))
return false;
or.AddMethodLists(mgrr.MethodsGroupedByDeclaringType.ToArray());
}
var foundMethod = or.GetBestCandidateWithSubstitutedTypeArguments();
if (!IsAppropriateCallTarget(expectedTargetDetails, method, foundMethod))
return false;
return result is MethodGroupResolveResult;
}
static MethodGroupResolveResult ToMethodGroup(IMethod method, ILFunction localFunction)
{
return new MethodGroupResolveResult(
null,
localFunction.Name,
new[] {
new MethodListWithDeclaringType(
method.DeclaringType,
new IParameterizedMember[] { method }
)
}, EmptyList<IType>.Instance
);
}
internal TranslatedExpression CallWithNamedArgs(Block block)
{
Debug.Assert(block.Kind == BlockKind.CallWithNamedArgs);

211
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -65,10 +65,10 @@ namespace ICSharpCode.Decompiler.CSharp @@ -65,10 +65,10 @@ namespace ICSharpCode.Decompiler.CSharp
/// * Otherwise, the C# type of the resulting expression shall match the IL stack type,
/// and the evaluated values shall be the same.
/// </remarks>
class ExpressionBuilder : ILVisitor<TranslationContext, TranslatedExpression>
sealed class ExpressionBuilder : ILVisitor<TranslationContext, TranslatedExpression>
{
readonly IDecompilerTypeSystem typeSystem;
readonly ITypeResolveContext decompilationContext;
internal readonly ITypeResolveContext decompilationContext;
internal readonly ILFunction currentFunction;
internal readonly ICompilation compilation;
internal readonly CSharpResolver resolver;
@ -181,7 +181,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -181,7 +181,7 @@ namespace ICSharpCode.Decompiler.CSharp
expr.WithRR(new ILVariableResolveResult(variable, elementType));
expr = new DirectionExpression(FieldDirection.Ref, expr);
return expr.WithRR(new ByReferenceResolveResult(elementType, isOut: false));
return expr.WithRR(new ByReferenceResolveResult(elementType, ReferenceKind.Ref));
} else {
return expr.WithRR(new ILVariableResolveResult(variable, variable.Type));
}
@ -189,7 +189,35 @@ namespace ICSharpCode.Decompiler.CSharp @@ -189,7 +189,35 @@ namespace ICSharpCode.Decompiler.CSharp
internal bool HidesVariableWithName(string name)
{
return currentFunction.Ancestors.OfType<ILFunction>().SelectMany(f => f.Variables).Any(v => v.Name == name);
return currentFunction.Ancestors.OfType<ILFunction>().Any(HidesVariableOrNestedFunction);
bool HidesVariableOrNestedFunction(ILFunction function)
{
foreach (var v in function.Variables) {
if (v.Name == name)
return true;
}
foreach (var f in function.LocalFunctions.OfType<ILFunction>()) {
if (f.Name == name)
return true;
}
return false;
}
}
internal ILFunction ResolveLocalFunction(IMethod method)
{
Debug.Assert(method.IsLocalFunction);
method = method.ReducedFrom;
foreach (var parent in currentFunction.Ancestors.OfType<ILFunction>()) {
var definition = parent.LocalFunctions.FirstOrDefault(f => f.Method == method);
if (definition != null) {
return definition;
}
}
return null;
}
bool RequiresQualifier(IMember member, TranslatedExpression target)
@ -220,19 +248,20 @@ namespace ICSharpCode.Decompiler.CSharp @@ -220,19 +248,20 @@ namespace ICSharpCode.Decompiler.CSharp
bool targetCasted = false;
var targetResolveResult = requireTarget ? target.ResolveResult : null;
bool IsUnambiguousAccess()
bool IsUnambiguousAccess(out MemberResolveResult result)
{
if (targetResolveResult == null) {
var result = resolver.ResolveSimpleName(field.Name, EmptyList<IType>.Instance, isInvocationTarget: false) as MemberResolveResult;
result = resolver.ResolveSimpleName(field.Name, EmptyList<IType>.Instance, isInvocationTarget: false) as MemberResolveResult;
return !(result == null || result.IsError || !result.Member.Equals(field, NormalizeTypeVisitor.TypeErasure));
} else {
var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentModule);
var result = lookup.Lookup(target.ResolveResult, field.Name, EmptyList<IType>.Instance, false) as MemberResolveResult;
result = lookup.Lookup(target.ResolveResult, field.Name, EmptyList<IType>.Instance, false) as MemberResolveResult;
return !(result == null || result.IsError || !result.Member.Equals(field, NormalizeTypeVisitor.TypeErasure));
}
}
while (!IsUnambiguousAccess()) {
MemberResolveResult mrr;
while (!IsUnambiguousAccess(out mrr)) {
if (!requireTarget) {
requireTarget = true;
targetResolveResult = target.ResolveResult;
@ -244,13 +273,16 @@ namespace ICSharpCode.Decompiler.CSharp @@ -244,13 +273,16 @@ namespace ICSharpCode.Decompiler.CSharp
break;
}
}
if (mrr == null) {
mrr = new MemberResolveResult(target.ResolveResult, field);
}
if (requireTarget) {
return new MemberReferenceExpression(target, field.Name)
.WithRR(new MemberResolveResult(target.ResolveResult, field));
.WithRR(mrr);
} else {
return new IdentifierExpression(field.Name)
.WithRR(new MemberResolveResult(target.ResolveResult, field));
.WithRR(mrr);
}
}
@ -511,7 +543,9 @@ namespace ICSharpCode.Decompiler.CSharp @@ -511,7 +543,9 @@ namespace ICSharpCode.Decompiler.CSharp
protected internal override TranslatedExpression VisitLdTypeToken(LdTypeToken inst, TranslationContext context)
{
return new MemberReferenceExpression(new TypeOfExpression(ConvertType(inst.Type)), "TypeHandle")
var typeofExpr = new TypeOfExpression(ConvertType(inst.Type))
.WithRR(new TypeOfResolveResult(compilation.FindType(KnownTypeCode.Type), inst.Type));
return new MemberReferenceExpression(typeofExpr, "TypeHandle")
.WithILInstruction(inst)
.WithRR(new TypeOfResolveResult(compilation.FindType(new TopLevelTypeName("System", "RuntimeTypeHandle")), inst.Type));
}
@ -576,7 +610,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -576,7 +610,7 @@ namespace ICSharpCode.Decompiler.CSharp
// because the DirectionExpression might get removed by dereferencing instructions such as LdObj
return new DirectionExpression(FieldDirection.Ref, expr.Expression)
.WithoutILInstruction()
.WithRR(new ByReferenceResolveResult(expr.ResolveResult, isOut: false));
.WithRR(new ByReferenceResolveResult(expr.ResolveResult, ReferenceKind.Ref));
}
protected internal override TranslatedExpression VisitStLoc(StLoc inst, TranslationContext context)
@ -585,7 +619,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -585,7 +619,7 @@ namespace ICSharpCode.Decompiler.CSharp
if (inst.Variable.Kind == VariableKind.StackSlot && !loadedVariablesSet.Contains(inst.Variable)) {
// Stack slots in the ILAst have inaccurate types (e.g. System.Object for StackType.O)
// so we should replace them with more accurate types where possible:
if ((inst.Variable.IsSingleDefinition || IsOtherValueType(translatedValue.Type) || inst.Variable.StackType == StackType.Ref)
if (CanUseTypeForStackSlot(inst.Variable, translatedValue.Type)
&& inst.Variable.StackType == translatedValue.Type.GetStackType()
&& translatedValue.Type.Kind != TypeKind.Null) {
inst.Variable.Type = translatedValue.Type;
@ -605,10 +639,31 @@ namespace ICSharpCode.Decompiler.CSharp @@ -605,10 +639,31 @@ namespace ICSharpCode.Decompiler.CSharp
return Assignment(lhs, translatedValue).WithILInstruction(inst);
}
bool CanUseTypeForStackSlot(ILVariable v, IType type)
{
return v.IsSingleDefinition
|| IsOtherValueType(type)
|| v.StackType == StackType.Ref
|| AllStoresUseConsistentType(v.StoreInstructions, type);
}
bool IsOtherValueType(IType type)
{
return type.IsReferenceType == false && type.GetStackType() == StackType.O;
}
bool AllStoresUseConsistentType(IReadOnlyList<IStoreInstruction> storeInstructions, IType expectedType)
{
expectedType = expectedType.AcceptVisitor(NormalizeTypeVisitor.TypeErasure);
foreach (var store in storeInstructions) {
if (!(store is StLoc stloc))
return false;
IType type = stloc.Value.InferType(compilation).AcceptVisitor(NormalizeTypeVisitor.TypeErasure);
if (!type.Equals(expectedType))
return false;
}
return true;
}
}
protected internal override TranslatedExpression VisitComp(Comp inst, TranslationContext context)
@ -1042,7 +1097,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1042,7 +1097,7 @@ namespace ICSharpCode.Decompiler.CSharp
.WithILInstruction(inst)
.WithRR(new ResolveResult(elementType));
return new DirectionExpression(FieldDirection.Ref, expr)
.WithoutILInstruction().WithRR(new ByReferenceResolveResult(expr.Type, isOut: false));
.WithoutILInstruction().WithRR(new ByReferenceResolveResult(expr.Type, ReferenceKind.Ref));
}
return CallUnsafeIntrinsic(name, new[] { left.Expression, Translate(offsetInst).Expression }, brt, inst);
} else {
@ -1333,7 +1388,13 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1333,7 +1388,13 @@ namespace ICSharpCode.Decompiler.CSharp
protected internal override TranslatedExpression VisitUserDefinedCompoundAssign(UserDefinedCompoundAssign inst, TranslationContext context)
{
var target = Translate(inst.Target);
IType loadType = inst.Method.Parameters[0].Type;
ExpressionWithResolveResult target;
if (inst.TargetKind == CompoundTargetKind.Address) {
target = LdObj(inst.Target, loadType);
} else {
target = Translate(inst.Target, loadType);
}
if (UserDefinedCompoundAssign.IsStringConcat(inst.Method)) {
Debug.Assert(inst.Method.Parameters.Count == 2);
var value = Translate(inst.Value).ConvertTo(inst.Method.Parameters[1].Type, this, allowImplicitConversion: true);
@ -1349,7 +1410,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1349,7 +1410,7 @@ namespace ICSharpCode.Decompiler.CSharp
.WithILInstruction(inst)
.WithRR(new OperatorResolveResult(inst.Method.ReturnType, AssignmentExpression.GetLinqNodeType(op.Value, false), inst.Method, inst.IsLifted, new[] { target.ResolveResult, value.ResolveResult }));
} else {
UnaryOperatorType? op = GetUnaryOperatorTypeFromMetadataName(inst.Method.Name, inst.CompoundAssignmentType == CompoundAssignmentType.EvaluatesToOldValue);
UnaryOperatorType? op = GetUnaryOperatorTypeFromMetadataName(inst.Method.Name, inst.EvalMode == CompoundEvalMode.EvaluatesToOldValue);
Debug.Assert(op != null);
return new UnaryOperatorExpression(op.Value, target)
@ -1428,10 +1489,15 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1428,10 +1489,15 @@ namespace ICSharpCode.Decompiler.CSharp
TranslatedExpression HandleCompoundAssignment(NumericCompoundAssign inst, AssignmentOperatorType op)
{
var target = Translate(inst.Target);
ExpressionWithResolveResult target;
if (inst.TargetKind == CompoundTargetKind.Address) {
target = LdObj(inst.Target, inst.Type);
} else {
target = Translate(inst.Target, inst.Type);
}
TranslatedExpression resultExpr;
if (inst.CompoundAssignmentType == CompoundAssignmentType.EvaluatesToOldValue) {
if (inst.EvalMode == CompoundEvalMode.EvaluatesToOldValue) {
Debug.Assert(op == AssignmentOperatorType.Add || op == AssignmentOperatorType.Subtract);
Debug.Assert(inst.Value.MatchLdcI(1));
UnaryOperatorType unary;
@ -1492,8 +1558,13 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1492,8 +1558,13 @@ namespace ICSharpCode.Decompiler.CSharp
TranslatedExpression HandleCompoundShift(NumericCompoundAssign inst, AssignmentOperatorType op)
{
Debug.Assert(inst.CompoundAssignmentType == CompoundAssignmentType.EvaluatesToNewValue);
var target = Translate(inst.Target);
Debug.Assert(inst.EvalMode == CompoundEvalMode.EvaluatesToNewValue);
ExpressionWithResolveResult target;
if (inst.TargetKind == CompoundTargetKind.Address) {
target = LdObj(inst.Target, inst.Type);
} else {
target = Translate(inst.Target, inst.Type);
}
var value = Translate(inst.Value);
// Shift operators in C# always expect type 'int' on the right-hand-side
@ -1698,7 +1769,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1698,7 +1769,7 @@ namespace ICSharpCode.Decompiler.CSharp
if (type.Kind == TypeKind.ByReference) {
return new DirectionExpression(FieldDirection.Ref, expr.Expression)
.WithoutILInstruction()
.WithRR(new ByReferenceResolveResult(expr.ResolveResult, isOut: false));
.WithRR(new ByReferenceResolveResult(expr.ResolveResult, ReferenceKind.Ref));
}
return expr;
}
@ -1830,22 +1901,28 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1830,22 +1901,28 @@ namespace ICSharpCode.Decompiler.CSharp
return SpecialType.UnknownType;
}
IEnumerable<ParameterDeclaration> MakeParameters(IReadOnlyList<IParameter> parameters, ILFunction function)
internal IEnumerable<ParameterDeclaration> MakeParameters(IReadOnlyList<IParameter> parameters, ILFunction function)
{
var variables = function.Variables.Where(v => v.Kind == VariableKind.Parameter).ToDictionary(v => v.Index);
int i = 0;
foreach (var parameter in parameters) {
var pd = astBuilder.ConvertParameter(parameter);
if (string.IsNullOrEmpty(pd.Name) && !pd.Type.IsArgList()) {
// needs to be consistent with logic in ILReader.CreateILVarable(ParameterDefinition)
pd.Name = "P_" + i;
// if this is a local function, we have to skip the parameters for closure references
if (settings.LocalFunctions && function.Kind == ILFunctionKind.LocalFunction && IL.Transforms.LocalFunctionDecompiler.IsClosureParameter(parameter, decompilationContext))
break;
}
if (settings.AnonymousTypes && parameter.Type.ContainsAnonymousType())
pd.Type = null;
ILVariable v;
if (variables.TryGetValue(i, out v))
if (variables.TryGetValue(i, out var v))
pd.AddAnnotation(new ILVariableResolveResult(v, parameters[i].Type));
yield return pd;
i++;
}
}
internal TranslatedExpression TranslateTarget(ILInstruction target, bool nonVirtualInvocation,
bool memberStatic, IType memberDeclaringType)
{
@ -1904,46 +1981,46 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1904,46 +1981,46 @@ namespace ICSharpCode.Decompiler.CSharp
protected internal override TranslatedExpression VisitLdObj(LdObj inst, TranslationContext context)
{
var target = Translate(inst.Target);
if (TypeUtils.IsCompatiblePointerTypeForMemoryAccess(target.Type, inst.Type)) {
TranslatedExpression result;
var result = LdObj(inst.Target, inst.Type);
//if (target.Type.IsSmallIntegerType() && loadType.IsSmallIntegerType() && target.Type.GetSign() != loadType.GetSign())
// return result.ConvertTo(loadType, this);
return result.WithILInstruction(inst);
}
ExpressionWithResolveResult LdObj(ILInstruction address, IType loadType)
{
var target = Translate(address);
if (TypeUtils.IsCompatiblePointerTypeForMemoryAccess(target.Type, loadType)) {
ExpressionWithResolveResult result;
if (target.Expression is DirectionExpression dirExpr) {
// we can dereference the managed reference by stripping away the 'ref'
result = target.UnwrapChild(dirExpr.Expression);
result.Expression.AddAnnotation(inst); // add LdObj in addition to the existing ILInstruction annotation
} else if (target.Type is PointerType pointerType) {
if (target.Expression is UnaryOperatorExpression uoe && uoe.Operator == UnaryOperatorType.AddressOf) {
// We can dereference the pointer by stripping away the '&'
result = target.UnwrapChild(uoe.Expression);
result.Expression.AddAnnotation(inst); // add LdObj in addition to the existing ILInstruction annotation
} else {
// Dereference the existing pointer
result = new UnaryOperatorExpression(UnaryOperatorType.Dereference, target.Expression)
.WithILInstruction(inst)
.WithRR(new ResolveResult(pointerType.ElementType));
}
} else {
// reference type behind non-DirectionExpression?
// this case should be impossible, but we can use a pointer cast
// just to make sure
target = target.ConvertTo(new PointerType(inst.Type), this);
target = target.ConvertTo(new PointerType(loadType), this);
return new UnaryOperatorExpression(UnaryOperatorType.Dereference, target.Expression)
.WithILInstruction(inst)
.WithRR(new ResolveResult(inst.Type));
.WithRR(new ResolveResult(loadType));
}
// we don't convert result to inst.Type, because the LdObj type
// might be inaccurate (it's often System.Object for all reference types),
// and our parent node should already insert casts where necessary
if (target.Type.IsSmallIntegerType() && inst.Type.IsSmallIntegerType() && target.Type.GetSign() != inst.Type.GetSign())
return result.ConvertTo(inst.Type, this);
return result;
} else {
// We need to cast the pointer type:
target = target.ConvertTo(new PointerType(inst.Type), this);
target = target.ConvertTo(new PointerType(loadType), this);
return new UnaryOperatorExpression(UnaryOperatorType.Dereference, target.Expression)
.WithILInstruction(inst)
.WithRR(new ResolveResult(inst.Type));
.WithRR(new ResolveResult(loadType));
}
}
@ -2022,14 +2099,16 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2022,14 +2099,16 @@ namespace ICSharpCode.Decompiler.CSharp
nonVirtualInvocation: true,
memberStatic: false,
memberDeclaringType: underlyingTupleType);
if (translatedTarget.Type is TupleType tupleType && tupleType.UnderlyingType.Equals(underlyingTupleType) && position <= tupleType.ElementNames.Length) {
if (translatedTarget.Type is TupleType tupleType && NormalizeTypeVisitor.TypeErasure.EquivalentTypes(tupleType, underlyingTupleType) && position <= tupleType.ElementNames.Length) {
string elementName = tupleType.ElementNames[position - 1];
if (elementName == null) {
elementName = "Item" + position;
}
// tupleType.ElementTypes are more accurate w.r.t. nullability/dynamic than inst.Field.Type
var rr = new MemberResolveResult(translatedTarget.ResolveResult, inst.Field,
returnTypeOverride: tupleType.ElementTypes[position - 1]);
expr = new MemberReferenceExpression(translatedTarget, elementName)
.WithRR(new MemberResolveResult(translatedTarget.ResolveResult, inst.Field))
.WithILInstruction(inst);
.WithRR(rr).WithILInstruction(inst);
} else {
expr = ConvertField(inst.Field, inst.Target).WithILInstruction(inst);
}
@ -2043,7 +2122,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2043,7 +2122,7 @@ namespace ICSharpCode.Decompiler.CSharp
} else {
// ldflda producing managed pointer
return new DirectionExpression(FieldDirection.Ref, expr)
.WithoutILInstruction().WithRR(new ByReferenceResolveResult(expr.ResolveResult, isOut: false));
.WithoutILInstruction().WithRR(new ByReferenceResolveResult(expr.ResolveResult, ReferenceKind.Ref));
}
}
@ -2051,7 +2130,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2051,7 +2130,7 @@ namespace ICSharpCode.Decompiler.CSharp
{
var expr = ConvertField(inst.Field).WithILInstruction(inst);
return new DirectionExpression(FieldDirection.Ref, expr)
.WithoutILInstruction().WithRR(new ByReferenceResolveResult(expr.Type, isOut: false));
.WithoutILInstruction().WithRR(new ByReferenceResolveResult(expr.Type, ReferenceKind.Ref));
}
protected internal override TranslatedExpression VisitLdElema(LdElema inst, TranslationContext context)
@ -2066,7 +2145,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2066,7 +2145,7 @@ namespace ICSharpCode.Decompiler.CSharp
arrayExpr, inst.Indices.Select(i => TranslateArrayIndex(i).Expression)
).WithILInstruction(inst).WithRR(new ResolveResult(arrayType.ElementType));
return new DirectionExpression(FieldDirection.Ref, expr)
.WithoutILInstruction().WithRR(new ByReferenceResolveResult(expr.Type, isOut: false));
.WithoutILInstruction().WithRR(new ByReferenceResolveResult(expr.Type, ReferenceKind.Ref));
}
TranslatedExpression TranslateArrayIndex(ILInstruction i)
@ -2124,7 +2203,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2124,7 +2203,7 @@ namespace ICSharpCode.Decompiler.CSharp
.WithRR(new ConversionResolveResult(inst.Type, arg.ResolveResult, Conversion.UnboxingConversion));
return new DirectionExpression(FieldDirection.Ref, castExpression)
.WithILInstruction(inst)
.WithRR(new ByReferenceResolveResult(castExpression.ResolveResult, isOut: false));
.WithRR(new ByReferenceResolveResult(castExpression.ResolveResult, ReferenceKind.Ref));
}
protected internal override TranslatedExpression VisitBox(Box inst, TranslationContext context)
@ -2184,7 +2263,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2184,7 +2263,7 @@ namespace ICSharpCode.Decompiler.CSharp
Arguments = { Translate(inst.Argument).Expression, new TypeReferenceExpression(ConvertType(inst.Type)) }
}.WithRR(new ResolveResult(inst.Type));
return new DirectionExpression(FieldDirection.Ref, expr.WithILInstruction(inst)).WithoutILInstruction()
.WithRR(new ByReferenceResolveResult(inst.Type, false));
.WithRR(new ByReferenceResolveResult(inst.Type, ReferenceKind.Ref));
}
protected internal override TranslatedExpression VisitBlock(Block block, TranslationContext context)
@ -2197,8 +2276,6 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2197,8 +2276,6 @@ namespace ICSharpCode.Decompiler.CSharp
case BlockKind.CollectionInitializer:
case BlockKind.ObjectInitializer:
return TranslateObjectAndCollectionInitializer(block);
case BlockKind.PostfixOperator:
return TranslatePostfixOperator(block);
case BlockKind.CallInlineAssign:
return TranslateSetterCallAssignment(block);
case BlockKind.CallWithNamedArgs:
@ -2533,19 +2610,6 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2533,19 +2610,6 @@ namespace ICSharpCode.Decompiler.CSharp
.WithRR(new ResolveResult(stloc.Variable.Type));
}
TranslatedExpression TranslatePostfixOperator(Block block)
{
var targetInst = (block.Instructions.ElementAtOrDefault(0) as StLoc)?.Value;
var inst = (block.Instructions.ElementAtOrDefault(1) as StLoc)?.Value as BinaryNumericInstruction;
if (targetInst == null || inst == null || (inst.Operator != BinaryNumericOperator.Add && inst.Operator != BinaryNumericOperator.Sub))
throw new ArgumentException("given Block is invalid!");
var op = inst.Operator == BinaryNumericOperator.Add ? UnaryOperatorType.PostIncrement : UnaryOperatorType.PostDecrement;
var target = Translate(targetInst);
return new UnaryOperatorExpression(op, target)
.WithILInstruction(block)
.WithRR(resolver.WithCheckForOverflow(inst.CheckForOverflow).ResolveUnaryOperator(op, target.ResolveResult));
}
/// <summary>
/// If expr is a constant integer expression, and its value fits into type,
/// convert the expression into the target type.
@ -2685,7 +2749,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2685,7 +2749,7 @@ namespace ICSharpCode.Decompiler.CSharp
new ConditionalExpression(condition.Expression, trueBranch.Expression, falseBranch.Expression)
.WithILInstruction(inst)
.WithRR(conditionalResolveResult)
).WithoutILInstruction().WithRR(new ByReferenceResolveResult(conditionalResolveResult, isOut: false));
).WithoutILInstruction().WithRR(new ByReferenceResolveResult(conditionalResolveResult, ReferenceKind.Ref));
} else {
return new ConditionalExpression(condition.Expression, trueBranch.Expression, falseBranch.Expression)
.WithILInstruction(inst)
@ -2705,7 +2769,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2705,7 +2769,7 @@ namespace ICSharpCode.Decompiler.CSharp
var value = Translate(inst.Value, targetTypeHint);
return new DirectionExpression(FieldDirection.Ref, value)
.WithILInstruction(inst)
.WithRR(new ByReferenceResolveResult(value.ResolveResult, false));
.WithRR(new ByReferenceResolveResult(value.ResolveResult, ReferenceKind.Ref));
}
protected internal override TranslatedExpression VisitAwait(Await inst, TranslationContext context)
@ -2860,7 +2924,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2860,7 +2924,7 @@ namespace ICSharpCode.Decompiler.CSharp
translatedExpression = translatedExpression.ConvertTo(typeHint, this);
}
if (info.HasFlag(CSharpArgumentInfoFlags.IsOut)) {
translatedExpression = ChangeDirectionExpressionToOut(translatedExpression);
translatedExpression = ChangeDirectionExpressionTo(translatedExpression, ReferenceKind.Out);
}
if (info.HasFlag(CSharpArgumentInfoFlags.NamedArgument) && !string.IsNullOrWhiteSpace(info.Name)) {
translatedExpression = new TranslatedExpression(new NamedArgumentExpression(info.Name, translatedExpression.Expression));
@ -2869,16 +2933,16 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2869,16 +2933,16 @@ namespace ICSharpCode.Decompiler.CSharp
return translatedExpression;
}
internal static TranslatedExpression ChangeDirectionExpressionToOut(TranslatedExpression input)
internal static TranslatedExpression ChangeDirectionExpressionTo(TranslatedExpression input, ReferenceKind kind)
{
if (!(input.Expression is DirectionExpression dirExpr && input.ResolveResult is ByReferenceResolveResult brrr))
return input;
dirExpr.FieldDirection = FieldDirection.Out;
dirExpr.FieldDirection = (FieldDirection)kind;
dirExpr.RemoveAnnotations<ByReferenceResolveResult>();
if (brrr.ElementResult == null)
brrr = new ByReferenceResolveResult(brrr.ElementType, isOut: true);
brrr = new ByReferenceResolveResult(brrr.ElementType, kind);
else
brrr = new ByReferenceResolveResult(brrr.ElementResult, isOut: true);
brrr = new ByReferenceResolveResult(brrr.ElementResult, kind);
dirExpr.AddAnnotation(brrr);
return new TranslatedExpression(dirExpr);
}
@ -3044,7 +3108,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -3044,7 +3108,12 @@ namespace ICSharpCode.Decompiler.CSharp
protected internal override TranslatedExpression VisitDynamicCompoundAssign(DynamicCompoundAssign inst, TranslationContext context)
{
var target = TranslateDynamicArgument(inst.Target, inst.TargetArgumentInfo);
ExpressionWithResolveResult target;
if (inst.TargetKind == CompoundTargetKind.Address) {
target = LdObj(inst.Target, SpecialType.Dynamic);
} else {
target = TranslateDynamicArgument(inst.Target, inst.TargetArgumentInfo);
}
var value = TranslateDynamicArgument(inst.Value, inst.ValueArgumentInfo);
var ae = new AssignmentExpression(target, AssignmentExpression.GetAssignmentOperatorTypeFromExpressionType(inst.Operation).Value, value);

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

@ -137,7 +137,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -137,7 +137,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
writer.Space();
writer.WriteToken(Roles.Colon, ":");
writer.Space();
if (symbol is IField f && CSharpDecompiler.IsFixedField(f.ParentModule.PEFile.Metadata, (System.Reflection.Metadata.FieldDefinitionHandle)f.MetadataToken, out var type, out int elementCount)) {
if (symbol is IField f && CSharpDecompiler.IsFixedField(f, out var type, out int elementCount)) {
rt = astBuilder.ConvertType(type);
new IndexerExpression(new TypeReferenceExpression(rt), astBuilder.ConvertConstantValue(f.Compilation.FindType(KnownTypeCode.Int32), elementCount))
.AcceptVisitor(new CSharpOutputVisitor(writer, formattingPolicy));

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

@ -1883,6 +1883,25 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -1883,6 +1883,25 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
Semicolon();
EndNode(variableDeclarationStatement);
}
public virtual void VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement)
{
StartNode(localFunctionDeclarationStatement);
WriteModifiers(localFunctionDeclarationStatement.ModifierTokens);
localFunctionDeclarationStatement.ReturnType.AcceptVisitor(this);
Space();
WriteIdentifier(localFunctionDeclarationStatement.NameToken);
WriteTypeParameters(localFunctionDeclarationStatement.TypeParameters);
Space(policy.SpaceBeforeMethodDeclarationParentheses);
WriteCommaSeparatedListInParenthesis(localFunctionDeclarationStatement.Parameters, policy.SpaceWithinMethodDeclarationParentheses);
foreach (Constraint constraint in localFunctionDeclarationStatement.Constraints) {
constraint.AcceptVisitor(this);
}
WriteMethodBody(localFunctionDeclarationStatement.Body, policy.MethodBraceStyle);
EndNode(localFunctionDeclarationStatement);
}
public virtual void VisitWhileStatement(WhileStatement whileStatement)
{
@ -2173,21 +2192,26 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -2173,21 +2192,26 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
{
StartNode(parameterDeclaration);
WriteAttributes(parameterDeclaration.Attributes);
if (parameterDeclaration.HasThisModifier) {
WriteKeyword(ParameterDeclaration.ThisModifierRole);
Space();
}
switch (parameterDeclaration.ParameterModifier) {
case ParameterModifier.Ref:
WriteKeyword(ParameterDeclaration.RefModifierRole);
Space();
break;
case ParameterModifier.Out:
WriteKeyword(ParameterDeclaration.OutModifierRole);
Space();
break;
case ParameterModifier.Params:
WriteKeyword(ParameterDeclaration.ParamsModifierRole);
break;
case ParameterModifier.This:
WriteKeyword(ParameterDeclaration.ThisModifierRole);
Space();
break;
case ParameterModifier.In:
WriteKeyword(ParameterDeclaration.InModifierRole);
Space();
break;
}
parameterDeclaration.Type.AcceptVisitor(this);

2
ICSharpCode.Decompiler/CSharp/OutputVisitor/FormattingOptionsFactory.cs

@ -209,7 +209,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -209,7 +209,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
}
/// <summary>
/// The K&R style, so named because it was used in Kernighan and Ritchie's book The C Programming Language,
/// The K&amp;R style, so named because it was used in Kernighan and Ritchie's book The C Programming Language,
/// is commonly used in C. It is less common for C++, C#, and others.
/// </summary>
public static CSharpFormattingOptions CreateKRStyle()

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

@ -362,7 +362,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -362,7 +362,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
{
// assignment is right-associative
ParenthesizeIfRequired(assignmentExpression.Left, Assignment + 1);
if (InsertParenthesesForReadability) {
if (InsertParenthesesForReadability && !(assignmentExpression.Right is DirectionExpression)) {
ParenthesizeIfRequired(assignmentExpression.Right, RelationalAndTypeTesting + 1);
} else {
ParenthesizeIfRequired(assignmentExpression.Right, Assignment);

2
ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterTokenWriter.cs

@ -382,6 +382,8 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -382,6 +382,8 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
// ASCII characters we allow directly in the output even though we don't use
// other Unicode characters of the same category.
return null;
case '\ufffd':
return "\\u" + ((int)ch).ToString("x4");
default:
switch (char.GetUnicodeCategory(ch)) {
case UnicodeCategory.ModifierLetter:

18
ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs

@ -650,7 +650,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -650,7 +650,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
{
TypeKind kind = type.Kind;
return kind == TypeKind.Class && type.GetDefinition().IsSealed
|| kind == TypeKind.Delegate || kind == TypeKind.Anonymous;
|| kind == TypeKind.Delegate;
}
#endregion
@ -1021,7 +1021,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -1021,7 +1021,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
if (f.IsImplicitlyTyped) {
// If F has an implicitly typed parameter list, D has no ref or out parameters.
foreach (IParameter p in d.Parameters) {
if (p.IsOut || p.IsRef)
if (p.ReferenceKind != ReferenceKind.None)
return Conversion.None;
}
} else {
@ -1030,7 +1030,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -1030,7 +1030,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
for (int i = 0; i < f.Parameters.Count; i++) {
IParameter pD = d.Parameters[i];
IParameter pF = f.Parameters[i];
if (pD.IsRef != pF.IsRef || pD.IsOut != pF.IsOut)
if (pD.ReferenceKind != pF.ReferenceKind)
return Conversion.None;
if (!IdentityConversion(dParamTypes[i], pF.Type))
return Conversion.None;
@ -1074,9 +1074,9 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -1074,9 +1074,9 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
for (int i = 0; i < args.Length; i++) {
IParameter param = invoke.Parameters[i];
IType parameterType = param.Type;
if ((param.IsRef || param.IsOut) && parameterType.Kind == TypeKind.ByReference) {
if (param.ReferenceKind != ReferenceKind.None && parameterType.Kind == TypeKind.ByReference) {
parameterType = ((ByReferenceType)parameterType).ElementType;
args[i] = new ByReferenceResolveResult(parameterType, param.IsOut);
args[i] = new ByReferenceResolveResult(parameterType, param.ReferenceKind);
} else {
args[i] = new ResolveResult(parameterType);
}
@ -1135,11 +1135,11 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -1135,11 +1135,11 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
for (int i = 0; i < invoke.Parameters.Count; i++) {
var pm = m.Parameters[firstParameterInM + i];
var pd = invoke.Parameters[i];
// ret/out must match
if (pm.IsRef != pd.IsRef || pm.IsOut != pd.IsOut)
// ret/out/in must match
if (pm.ReferenceKind != pd.ReferenceKind)
return false;
if (pm.IsRef || pm.IsOut) {
// ref/out parameters must have same types
if (pm.ReferenceKind != ReferenceKind.None) {
// ref/out/in parameters must have same types
if (!pm.Type.Equals(pd.Type))
return false;
} else {

16
ICSharpCode.Decompiler/CSharp/Resolver/CSharpInvocationResolveResult.cs

@ -46,22 +46,8 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -46,22 +46,8 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
/// Gets whether a params-Array is being used in its expanded form.
/// </summary>
public readonly bool IsExpandedForm;
readonly IReadOnlyList<int> argumentToParameterMap;
/// <summary>
/// If IsExtensionMethodInvocation is true this property holds the reduced method.
/// </summary>
IMethod reducedMethod;
public IMethod ReducedMethod {
get {
if (!IsExtensionMethodInvocation)
return null;
if (reducedMethod == null && Member is IMethod)
reducedMethod = new ReducedExtensionMethod ((IMethod)Member);
return reducedMethod;
}
}
readonly IReadOnlyList<int> argumentToParameterMap;
public CSharpInvocationResolveResult(
ResolveResult targetResult, IParameterizedMember member,

2
ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs

@ -2084,7 +2084,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -2084,7 +2084,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
// create the parameter:
ByReferenceResolveResult brrr = arguments[i] as ByReferenceResolveResult;
if (brrr != null) {
list.Add(new DefaultParameter(arguments[i].Type, argumentNames[i], isRef: brrr.IsRef, isOut: brrr.IsOut));
list.Add(new DefaultParameter(arguments[i].Type, argumentNames[i], referenceKind: brrr.ReferenceKind));
} else {
// argument might be a lambda or delegate type, so we have to try to guess the delegate type
IType type = arguments[i].Type;

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

@ -174,12 +174,12 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -174,12 +174,12 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
}
return extensionMethods ?? Enumerable.Empty<IEnumerable<IMethod>>();
}
/// <summary>
/// Gets the eligible extension methods.
/// </summary>
/// <param name="substituteInferredTypes">
/// Specifies whether to produce a <see cref="SpecializedMethod"/>
/// Specifies whether to produce a <c>SpecializedMethod</c>
/// when type arguments could be inferred from <see cref="TargetType"/>.
/// This setting is only used for inferred types and has no effect if the type parameters are
/// specified explicitly.

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

@ -589,10 +589,10 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -589,10 +589,10 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
ByReferenceResolveResult brrr = arguments[i] as ByReferenceResolveResult;
if (brrr != null) {
if ((brrr.IsOut && !candidate.Parameters[parameterIndex].IsOut) || (brrr.IsRef && !candidate.Parameters[parameterIndex].IsRef))
if (brrr.ReferenceKind != candidate.Parameters[parameterIndex].ReferenceKind)
candidate.AddError(OverloadResolutionErrors.ParameterPassingModeMismatch);
} else {
if (candidate.Parameters[parameterIndex].IsOut || candidate.Parameters[parameterIndex].IsRef)
if (candidate.Parameters[parameterIndex].ReferenceKind != ReferenceKind.None)
candidate.AddError(OverloadResolutionErrors.ParameterPassingModeMismatch);
}
IType parameterType = candidate.ParameterTypes[parameterIndex];
@ -940,6 +940,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -940,6 +940,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
/// <param name="initializerStatements">
/// Statements for Objects/Collections initializer.
/// <see cref="InvocationResolveResult.InitializerStatements"/>
/// </param>
/// <param name="returnTypeOverride">
/// If not null, use this instead of the ReturnType of the member as the type of the created resolve result.
/// </param>

306
ICSharpCode.Decompiler/CSharp/Resolver/ReducedExtensionMethod.cs

@ -1,306 +0,0 @@ @@ -1,306 +0,0 @@
//
// ReducedExtensionMethod.cs
//
// Author:
// Mike Krüger <mkrueger@xamarin.com>
//
// Copyright (c) 2013 Xamarin Inc. (http://xamarin.com)
//
// 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;
using System.Reflection;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.CSharp.Resolver
{
/// <summary>
/// An invocated extension method hides the extension parameter in its parameter list.
/// It's used to hide the internals of extension method invocation in certain situation to simulate the
/// syntactic way of writing extension methods on semantic level.
/// </summary>
public class ReducedExtensionMethod : IMethod
{
readonly IMethod baseMethod;
public ReducedExtensionMethod(IMethod baseMethod)
{
this.baseMethod = baseMethod;
}
public bool Equals(IMember obj, TypeVisitor typeNormalization)
{
var other = obj as ReducedExtensionMethod;
if (other == null)
return false;
return baseMethod.Equals(other.baseMethod, typeNormalization);
}
public override bool Equals(object obj)
{
var other = obj as ReducedExtensionMethod;
if (other == null)
return false;
return baseMethod.Equals(other.baseMethod);
}
public override int GetHashCode()
{
unchecked {
return baseMethod.GetHashCode() + 1;
}
}
public override string ToString()
{
return string.Format("[ReducedExtensionMethod: ReducedFrom={0}]", ReducedFrom);
}
#region IMember implementation
public IMember MemberDefinition {
get {
return baseMethod.MemberDefinition;
}
}
public IType ReturnType {
get {
return baseMethod.ReturnType;
}
}
public IEnumerable<IMember> ExplicitlyImplementedInterfaceMembers {
get {
return baseMethod.ExplicitlyImplementedInterfaceMembers;
}
}
public bool IsExplicitInterfaceImplementation {
get {
return baseMethod.IsExplicitInterfaceImplementation;
}
}
public bool IsVirtual {
get {
return baseMethod.IsVirtual;
}
}
public bool IsOverride {
get {
return baseMethod.IsOverride;
}
}
public bool IsOverridable {
get {
return baseMethod.IsOverridable;
}
}
public TypeParameterSubstitution Substitution {
get {
return baseMethod.Substitution;
}
}
public IMethod Specialize(TypeParameterSubstitution substitution)
{
return new ReducedExtensionMethod((IMethod)baseMethod.Specialize(substitution));
}
IMember IMember.Specialize(TypeParameterSubstitution substitution)
{
return Specialize(substitution);
}
#endregion
#region IMethod implementation
public IReadOnlyList<ITypeParameter> TypeParameters {
get {
return baseMethod.TypeParameters;
}
}
public bool IsExtensionMethod {
get {
return true;
}
}
public bool IsConstructor {
get {
return baseMethod.IsConstructor;
}
}
public bool IsDestructor {
get {
return baseMethod.IsDestructor;
}
}
public bool IsOperator {
get {
return baseMethod.IsOperator;
}
}
public bool HasBody {
get {
return baseMethod.HasBody;
}
}
public bool IsAccessor => baseMethod.IsAccessor;
public IMember AccessorOwner => baseMethod.AccessorOwner;
public MethodSemanticsAttributes AccessorKind => baseMethod.AccessorKind;
public IMethod ReducedFrom {
get {
return baseMethod;
}
}
public IReadOnlyList<IType> TypeArguments {
get {
return baseMethod.TypeArguments;
}
}
#endregion
#region IParameterizedMember implementation
List<IParameter> parameters;
public IReadOnlyList<IParameter> Parameters {
get {
if (parameters == null)
parameters = new List<IParameter> (baseMethod.Parameters.Skip (1));
return parameters;
}
}
#endregion
#region IEntity implementation
public System.Reflection.Metadata.EntityHandle MetadataToken => baseMethod.MetadataToken;
public SymbolKind SymbolKind {
get {
return baseMethod.SymbolKind;
}
}
public ITypeDefinition DeclaringTypeDefinition {
get {
return baseMethod.DeclaringTypeDefinition;
}
}
public IType DeclaringType {
get {
return baseMethod.DeclaringType;
}
}
public IModule ParentModule {
get {
return baseMethod.ParentModule;
}
}
IEnumerable<IAttribute> IEntity.GetAttributes() => baseMethod.GetAttributes();
IEnumerable<IAttribute> IMethod.GetReturnTypeAttributes() => baseMethod.GetReturnTypeAttributes();
bool IMethod.ReturnTypeIsRefReadOnly => baseMethod.ReturnTypeIsRefReadOnly;
public bool IsStatic {
get {
return false;
}
}
public bool IsAbstract {
get {
return baseMethod.IsAbstract;
}
}
public bool IsSealed {
get {
return baseMethod.IsSealed;
}
}
#endregion
#region IHasAccessibility implementation
public Accessibility Accessibility {
get {
return baseMethod.Accessibility;
}
}
#endregion
#region INamedElement implementation
public string FullName {
get {
return baseMethod.FullName;
}
}
public string Name {
get {
return baseMethod.Name;
}
}
public string ReflectionName {
get {
return baseMethod.ReflectionName;
}
}
public string Namespace {
get {
return baseMethod.Namespace;
}
}
#endregion
#region ICompilationProvider implementation
public ICompilation Compilation {
get {
return baseMethod.Compilation;
}
}
#endregion
}
}

18
ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs

@ -513,9 +513,9 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -513,9 +513,9 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
for (int i = 0; i < args.Length; i++) {
IParameter param = m.Parameters[i];
IType parameterType = param.Type.AcceptVisitor(substitution);
if ((param.IsRef || param.IsOut) && parameterType.Kind == TypeKind.ByReference) {
if ((param.ReferenceKind != ReferenceKind.None) && parameterType.Kind == TypeKind.ByReference) {
parameterType = ((ByReferenceType)parameterType).ElementType;
args[i] = new ByReferenceResolveResult(parameterType, param.IsOut);
args[i] = new ByReferenceResolveResult(parameterType, param.ReferenceKind);
} else {
args[i] = new ResolveResult(parameterType);
}
@ -570,7 +570,11 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -570,7 +570,11 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
void MakeExactInference(IType U, IType V)
{
Log.WriteLine("MakeExactInference from " + U + " to " + V);
if (V is NullabilityAnnotatedTypeParameter nullableTP) {
V = nullableTP.OriginalTypeParameter;
}
// If V is one of the unfixed Xi then U is added to the set of bounds for Xi.
TP tp = GetTPForType(V);
if (tp != null && tp.IsFixed == false) {
@ -640,7 +644,13 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -640,7 +644,13 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
MakeLowerBoundInference(NullableType.GetUnderlyingType(U), NullableType.GetUnderlyingType(V));
return;
}
// Handle by reference types:
ByReferenceType brU = U as ByReferenceType;
ByReferenceType brV = V as ByReferenceType;
if (brU != null && brV != null) {
MakeExactInference(brU.ElementType, brV.ElementType);
return;
}
// Handle array types:
ArrayType arrU = U as ArrayType;
ArrayType arrV = V as ArrayType;

56
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -28,10 +28,11 @@ using System; @@ -28,10 +28,11 @@ using System;
using System.Threading;
using ICSharpCode.Decompiler.IL.Transforms;
using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching;
using ICSharpCode.Decompiler.CSharp.Resolver;
namespace ICSharpCode.Decompiler.CSharp
{
class StatementBuilder : ILVisitor<Statement>
sealed class StatementBuilder : ILVisitor<Statement>
{
internal readonly ExpressionBuilder exprBuilder;
readonly ILFunction currentFunction;
@ -411,6 +412,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -411,6 +412,7 @@ namespace ICSharpCode.Decompiler.CSharp
AstNode usingInit = resource;
var var = inst.Variable;
if (!inst.ResourceExpression.MatchLdNull() && !NullableType.GetUnderlyingType(var.Type).GetAllBaseTypes().Any(b => b.IsKnownType(KnownTypeCode.IDisposable))) {
Debug.Assert(var.Kind == VariableKind.UsingLocal);
var.Kind = VariableKind.Local;
var disposeType = exprBuilder.compilation.FindType(KnownTypeCode.IDisposable);
var disposeVariable = currentFunction.RegisterVariable(
@ -707,7 +709,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -707,7 +709,7 @@ namespace ICSharpCode.Decompiler.CSharp
while (inst.Parent is UnboxAny || inst.Parent is CastClass)
inst = inst.Parent;
// One variable was found.
if (inst.Parent is StLoc stloc) {
if (inst.Parent is StLoc stloc && (stloc.Variable.Kind == VariableKind.Local || stloc.Variable.Kind == VariableKind.StackSlot)) {
// Must be a plain assignment expression and variable must only be used in 'body' + only assigned once.
if (stloc.Parent == loopBody && VariableIsOnlyUsedInBlock(stloc, usingContainer)) {
foreachVariable = stloc.Variable;
@ -725,18 +727,32 @@ namespace ICSharpCode.Decompiler.CSharp @@ -725,18 +727,32 @@ namespace ICSharpCode.Decompiler.CSharp
/// <summary>
/// Determines whether storeInst.Variable is only assigned once and used only inside <paramref name="usingContainer"/>.
/// Loads by reference (ldloca) are only allowed in the context of this pointer in call instructions.
/// Loads by reference (ldloca) are only allowed in the context of this pointer in call instructions,
/// or as target of ldobj.
/// (This only applies to value types.)
/// </summary>
bool VariableIsOnlyUsedInBlock(StLoc storeInst, BlockContainer usingContainer)
{
if (storeInst.Variable.LoadInstructions.Any(ld => !ld.IsDescendantOf(usingContainer)))
return false;
if (storeInst.Variable.AddressInstructions.Any(la => !la.IsDescendantOf(usingContainer) || !ILInlining.IsUsedAsThisPointerInCall(la) || IsTargetOfSetterCall(la, la.Variable.Type)))
if (storeInst.Variable.AddressInstructions.Any(inst => !AddressUseAllowed(inst)))
return false;
if (storeInst.Variable.StoreInstructions.OfType<ILInstruction>().Any(st => st != storeInst))
return false;
return true;
bool AddressUseAllowed(LdLoca la)
{
if (!la.IsDescendantOf(usingContainer))
return false;
if (ILInlining.IsUsedAsThisPointerInCall(la) && !IsTargetOfSetterCall(la, la.Variable.Type))
return true;
var current = la.Parent;
while (current is LdFlda next) {
current = next.Parent;
}
return current is LdObj;
}
}
/// <summary>
@ -849,6 +865,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -849,6 +865,7 @@ namespace ICSharpCode.Decompiler.CSharp
if (blockStatement.LastOrDefault() is ContinueStatement continueStmt)
continueStmt.Remove();
DeclareLocalFunctions(currentFunction, container, blockStatement);
return new WhileStatement(new PrimitiveExpression(true), blockStatement);
case ContainerKind.While:
continueTarget = container.EntryPoint;
@ -870,6 +887,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -870,6 +887,7 @@ namespace ICSharpCode.Decompiler.CSharp
if (blockStatement.LastOrDefault() is ContinueStatement continueStmt2)
continueStmt2.Remove();
DeclareLocalFunctions(currentFunction, container, blockStatement);
return new WhileStatement(exprBuilder.TranslateCondition(condition), blockStatement);
case ContainerKind.DoWhile:
continueTarget = container.Blocks.Last();
@ -888,6 +906,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -888,6 +906,7 @@ namespace ICSharpCode.Decompiler.CSharp
// to continue statements, we have to introduce an extra label.
blockStatement.Add(new LabelStatement { Label = continueTarget.Label });
}
DeclareLocalFunctions(currentFunction, container, blockStatement);
if (blockStatement.Statements.Count == 0) {
return new WhileStatement {
Condition = exprBuilder.TranslateCondition(condition),
@ -919,6 +938,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -919,6 +938,7 @@ namespace ICSharpCode.Decompiler.CSharp
}
if (continueTarget.IncomingEdgeCount > continueCount)
blockStatement.Add(new LabelStatement { Label = continueTarget.Label });
DeclareLocalFunctions(currentFunction, container, blockStatement);
return forStmt;
default:
throw new ArgumentOutOfRangeException();
@ -927,7 +947,33 @@ namespace ICSharpCode.Decompiler.CSharp @@ -927,7 +947,33 @@ namespace ICSharpCode.Decompiler.CSharp
BlockStatement ConvertBlockContainer(BlockContainer container, bool isLoop)
{
return ConvertBlockContainer(new BlockStatement(), container, container.Blocks, isLoop);
var blockStatement = ConvertBlockContainer(new BlockStatement(), container, container.Blocks, isLoop);
DeclareLocalFunctions(currentFunction, container, blockStatement);
return blockStatement;
}
void DeclareLocalFunctions(ILFunction currentFunction, BlockContainer container, BlockStatement blockStatement)
{
foreach (var localFunction in currentFunction.LocalFunctions.OrderBy(f => f.Name)) {
if (localFunction.DeclarationScope != container)
continue;
blockStatement.Add(TranslateFunction(localFunction));
}
LocalFunctionDeclarationStatement TranslateFunction(ILFunction function)
{
var stmt = new LocalFunctionDeclarationStatement();
var nestedBuilder = new StatementBuilder(typeSystem, exprBuilder.decompilationContext, function, settings, cancellationToken);
stmt.Name = function.Name;
stmt.Parameters.AddRange(exprBuilder.MakeParameters(function.Parameters, function));
stmt.ReturnType = exprBuilder.ConvertType(function.Method.ReturnType);
stmt.Body = nestedBuilder.ConvertAsBlock(function.Body);
if (function.IsAsync) {
stmt.Modifiers |= Modifiers.Async;
}
stmt.AddAnnotation(new MemberResolveResult(null, function.ReducedMethod));
return stmt;
}
}
BlockStatement ConvertBlockContainer(BlockStatement blockStatement, BlockContainer container, IEnumerable<Block> blocks, bool isLoop)

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

@ -83,6 +83,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -83,6 +83,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
}
}
public bool HasOnlyNullableSpecifier {
get {
return HasNullableSpecifier && !HasRefSpecifier && !HasReadOnlySpecifier && PointerRank == 0 && ArraySpecifiers.Count == 0;
}
}
public CSharpTokenNode NullableSpecifierToken {
get {
return GetChildByRole(NullableRole);

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

@ -390,7 +390,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -390,7 +390,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{
VisitChildren (variableDeclarationStatement);
}
public virtual void VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement)
{
VisitChildren(localFunctionDeclarationStatement);
}
public virtual void VisitWhileStatement (WhileStatement whileStatement)
{
VisitChildren (whileStatement);
@ -1037,7 +1042,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1037,7 +1042,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{
return VisitChildren (variableDeclarationStatement);
}
public virtual T VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement)
{
return VisitChildren(localFunctionDeclarationStatement);
}
public virtual T VisitWhileStatement (WhileStatement whileStatement)
{
return VisitChildren (whileStatement);
@ -1684,7 +1694,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1684,7 +1694,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{
return VisitChildren (variableDeclarationStatement, data);
}
public virtual S VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement, T data)
{
return VisitChildren(localFunctionDeclarationStatement, data);
}
public virtual S VisitWhileStatement (WhileStatement whileStatement, T data)
{
return VisitChildren (whileStatement, data);

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

@ -250,13 +250,13 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -250,13 +250,13 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
Divide,
/// <summary>left %= right</summary>
Modulus,
/// <summary>left <<= right</summary>
/// <summary>left &lt;&lt;= right</summary>
ShiftLeft,
/// <summary>left >>= right</summary>
ShiftRight,
/// <summary>left &= right</summary>
/// <summary>left &amp;= right</summary>
BitwiseAnd,
/// <summary>left |= right</summary>
BitwiseOr,

2
ICSharpCode.Decompiler/CSharp/Syntax/Expressions/UnaryOperatorExpression.cs

@ -182,7 +182,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -182,7 +182,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
PostDecrement,
/// <summary>Dereferencing (*a)</summary>
Dereference,
/// <summary>Get address (&a)</summary>
/// <summary>Get address (&amp;a)</summary>
AddressOf,
/// <summary>C# 5.0 await</summary>
Await,

2
ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/ExternAliasDeclaration.cs

@ -28,7 +28,7 @@ @@ -28,7 +28,7 @@
namespace ICSharpCode.Decompiler.CSharp.Syntax
{
/// <summary>
/// extern alias <Identifier>;
/// extern alias IDENTIFIER;
/// </summary>
public class ExternAliasDeclaration : AstNode
{

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

@ -110,6 +110,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -110,6 +110,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
void VisitUnsafeStatement(UnsafeStatement unsafeStatement);
void VisitUsingStatement(UsingStatement usingStatement);
void VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement);
void VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement);
void VisitWhileStatement(WhileStatement whileStatement);
void VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement);
void VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement);
@ -251,6 +252,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -251,6 +252,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
S VisitUnsafeStatement(UnsafeStatement unsafeStatement);
S VisitUsingStatement(UsingStatement usingStatement);
S VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement);
S VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement);
S VisitWhileStatement(WhileStatement whileStatement);
S VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement);
S VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement);
@ -392,6 +394,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -392,6 +394,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
S VisitUnsafeStatement(UnsafeStatement unsafeStatement, T data);
S VisitUsingStatement(UsingStatement usingStatement, T data);
S VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement, T data);
S VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement, T data);
S VisitWhileStatement(WhileStatement whileStatement, T data);
S VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement, T data);
S VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement, T data);

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

@ -0,0 +1,110 @@ @@ -0,0 +1,110 @@
// Copyright (c) 2019 Siegfried Pammer
//
// 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.Collections.Generic;
using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching;
namespace ICSharpCode.Decompiler.CSharp.Syntax
{
public class LocalFunctionDeclarationStatement : Statement
{
public AstNodeCollection<TypeParameterDeclaration> TypeParameters {
get { return GetChildrenByRole(Roles.TypeParameter); }
}
public CSharpTokenNode LParToken {
get { return GetChildByRole(Roles.LPar); }
}
public AstNodeCollection<ParameterDeclaration> Parameters {
get { return GetChildrenByRole(Roles.Parameter); }
}
public CSharpTokenNode RParToken {
get { return GetChildByRole(Roles.RPar); }
}
public AstNodeCollection<Constraint> Constraints {
get { return GetChildrenByRole(Roles.Constraint); }
}
public BlockStatement Body {
get { return GetChildByRole(Roles.Body); }
set { SetChildByRole(Roles.Body, value); }
}
public Modifiers Modifiers {
get { return EntityDeclaration.GetModifiers(this); }
set { EntityDeclaration.SetModifiers(this, value); }
}
public bool HasModifier(Modifiers mod)
{
return (Modifiers & mod) == mod;
}
public IEnumerable<CSharpModifierToken> ModifierTokens {
get { return GetChildrenByRole(EntityDeclaration.ModifierRole); }
}
public virtual string Name {
get {
return GetChildByRole(Roles.Identifier).Name;
}
set {
SetChildByRole(Roles.Identifier, Identifier.Create(value, TextLocation.Empty));
}
}
public virtual Identifier NameToken {
get { return GetChildByRole(Roles.Identifier); }
set { SetChildByRole(Roles.Identifier, value); }
}
public virtual AstType ReturnType {
get { return GetChildByRole(Roles.Type); }
set { SetChildByRole(Roles.Type, value); }
}
public override void AcceptVisitor(IAstVisitor visitor)
{
visitor.VisitLocalFunctionDeclarationStatement(this);
}
public override T AcceptVisitor<T>(IAstVisitor<T> visitor)
{
return visitor.VisitLocalFunctionDeclarationStatement(this);
}
public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data)
{
return visitor.VisitLocalFunctionDeclarationStatement(this, data);
}
protected internal override bool DoMatch(AstNode other, Match match)
{
LocalFunctionDeclarationStatement o = other as LocalFunctionDeclarationStatement;
return o != null && MatchString(this.Name, o.Name)
&& (this.Modifiers == Modifiers.Any || this.Modifiers == o.Modifiers)
&& this.ReturnType.DoMatch(o.ReturnType, match)
&& this.TypeParameters.DoMatch(o.TypeParameters, match)
&& this.Parameters.DoMatch(o.Parameters, match) && this.Constraints.DoMatch(o.Constraints, match)
&& this.Body.DoMatch(o.Body, match);
}
}
}

4
ICSharpCode.Decompiler/CSharp/Syntax/TextLocation.cs

@ -26,10 +26,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -26,10 +26,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
/// A line/column position.
/// Text editor lines/columns are counted started from one.
/// </summary>
/// <remarks>
/// The document provides the methods <see cref="IDocument.GetLocation"/> and
/// <see cref="IDocument.GetOffset(ICSharpCode.Decompiler.CSharp.Syntax.TextLocation)"/> to convert between offsets and TextLocations.
/// </remarks>
[Serializable]
[TypeConverter(typeof(TextLocationConverter))]
public struct TextLocation : IComparable<TextLocation>, IEquatable<TextLocation>

2
ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/MethodDeclaration.cs

@ -71,7 +71,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -71,7 +71,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public bool IsExtensionMethod {
get {
ParameterDeclaration pd = (ParameterDeclaration)GetChildByRole (Roles.Parameter);
return pd != null && pd.ParameterModifier == ParameterModifier.This;
return pd != null && pd.HasThisModifier;
}
}

31
ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs

@ -27,15 +27,15 @@ @@ -27,15 +27,15 @@
namespace ICSharpCode.Decompiler.CSharp.Syntax
{
public enum ParameterModifier {
public enum ParameterModifier
{
None,
Ref,
Out,
Params,
This,
In
}
public class ParameterDeclaration : AstNode
{
public static readonly Role<AttributeSection> AttributeRole = EntityDeclaration.AttributeRole;
@ -96,11 +96,30 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -96,11 +96,30 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return NodeType.Unknown;
}
}
public AstNodeCollection<AttributeSection> Attributes {
get { return GetChildrenByRole (AttributeRole); }
get { return GetChildrenByRole(AttributeRole); }
}
bool hasThisModifier;
public CSharpTokenNode ThisKeyword {
get {
if (hasThisModifier) {
return GetChildByRole(ThisModifierRole);
}
return CSharpTokenNode.Null;
}
}
public bool HasThisModifier {
get { return hasThisModifier; }
set {
ThrowIfFrozen();
hasThisModifier = value;
}
}
ParameterModifier parameterModifier;
public ParameterModifier ParameterModifier {

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

@ -407,7 +407,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -407,7 +407,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
bool TypeDefMatches(ITypeDefinition typeDef, IType type)
{
if (type.Name != typeDef.Name || type.Namespace != typeDef.Namespace || type.TypeParameterCount != typeDef.TypeParameterCount)
if (type == null || type.Name != typeDef.Name || type.Namespace != typeDef.Namespace || type.TypeParameterCount != typeDef.TypeParameterCount)
return false;
bool defIsNested = typeDef.DeclaringTypeDefinition != null;
bool typeIsNested = type.DeclaringType != null;
@ -739,7 +739,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -739,7 +739,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
if (type == null)
throw new ArgumentNullException("type");
if (constantValue == null) {
if (type.IsReferenceType == true) {
if (type.IsReferenceType == true || type.IsKnownType(KnownTypeCode.NullableOfT)) {
var expr = new NullReferenceExpression();
if (AddResolveResultAnnotations)
expr.AddAnnotation(new ConstantResolveResult(SpecialType.NullType, null));
@ -1086,8 +1086,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1086,8 +1086,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{
AstType mathAstType = ConvertType(mathType);
var fieldRef = new MemberReferenceExpression(new TypeReferenceExpression(mathAstType), memberName);
if (AddResolveResultAnnotations)
fieldRef.WithRR(new MemberResolveResult(mathAstType.GetResolveResult(), mathType.GetFields(f => f.Name == memberName).Single()));
if (AddResolveResultAnnotations) {
var field = mathType.GetFields(f => f.Name == memberName).FirstOrDefault();
if (field != null) {
fieldRef.WithRR(new MemberResolveResult(mathAstType.GetResolveResult(), field));
}
}
if (type.IsKnownType(KnownTypeCode.Double))
return fieldRef;
if (mathType.Name == "MathF")
@ -1629,8 +1633,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1629,8 +1633,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
foreach (IParameter p in method.Parameters) {
decl.Parameters.Add(ConvertParameter(p));
}
if (method.IsExtensionMethod && method.ReducedFrom == null && decl.Parameters.Any() && decl.Parameters.First().ParameterModifier == ParameterModifier.None)
decl.Parameters.First().ParameterModifier = ParameterModifier.This;
if (method.IsExtensionMethod && method.ReducedFrom == null && decl.Parameters.Any())
decl.Parameters.First().HasThisModifier = true;
if (this.ShowTypeParameters && this.ShowTypeParameterConstraints && !method.IsOverride && !method.IsExplicitInterfaceImplementation) {
foreach (ITypeParameter tp in method.TypeParameters) {

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

@ -296,9 +296,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -296,9 +296,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
costUncheckedContextCheckedBlockOpen += stmtResult.CostInCheckedContext;
nodesUncheckedContextCheckedBlockOpen += stmtResult.NodesToInsertInCheckedContext;
if (statement is LabelStatement) {
if (statement is LabelStatement || statement is LocalFunctionDeclarationStatement) {
// We can't move labels into blocks because that might cause goto-statements
// to be unable to just to the labels.
// to be unable to jump to the labels.
// Also, we can't move local functions into blocks, because that might cause
// them to become out of scope from the call-sites.
costCheckedContextUncheckedBlockOpen = Cost.Infinite;
costUncheckedContextCheckedBlockOpen = Cost.Infinite;
}

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

@ -260,7 +260,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -260,7 +260,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
} else {
newPoint = new InsertionPoint { level = nodeLevel, nextNode = identExpr };
if (variable.HasInitialValue) {
// Uninitialized variables are logically initialized at the beginning of the functin
// Uninitialized variables are logically initialized at the beginning of the function
// Because it's possible that the variable has a loop-carried dependency,
// declare it outside of any loops.
while (startIndex >= 0) {

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

@ -16,12 +16,25 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -16,12 +16,25 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
continue;
var blockStatement = switchSection.Statements.First() as BlockStatement;
if (blockStatement == null || blockStatement.Statements.Any(st => st is VariableDeclarationStatement))
if (blockStatement == null || blockStatement.Statements.Any(ContainsLocalDeclaration))
continue;
blockStatement.Remove();
blockStatement.Statements.MoveTo(switchSection.Statements);
}
bool ContainsLocalDeclaration(AstNode node)
{
if (node is VariableDeclarationStatement || node is LocalFunctionDeclarationStatement || node is OutVarDeclarationExpression)
return true;
if (node is BlockStatement)
return false;
foreach (var child in node.Children) {
if (ContainsLocalDeclaration(child))
return true;
}
return false;
}
}
}
}

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

@ -137,7 +137,13 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -137,7 +137,13 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
}
if (!CanTransformToExtensionMethodCall(resolver, method, typeArguments, target, args, argNames))
return;
if (firstArgument is NullReferenceExpression) {
if (firstArgument is DirectionExpression dirExpr) {
if (!context.Settings.RefExtensionMethods || dirExpr.FieldDirection == FieldDirection.Out)
return;
firstArgument = dirExpr.Expression;
target = firstArgument.GetResolveResult();
dirExpr.Detach();
} else if (firstArgument is NullReferenceExpression) {
Debug.Assert(context.RequiredNamespacesSuperset.Contains(method.Parameters[0].Type.Namespace));
firstArgument = firstArgument.ReplaceWith(expr => new CastExpression(context.TypeSystemAstBuilder.ConvertType(method.Parameters[0].Type), expr.Detach()));
}
@ -162,13 +168,16 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -162,13 +168,16 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
public static bool CanTransformToExtensionMethodCall(CSharpResolver resolver, IMethod method,
IReadOnlyList<IType> typeArguments, ResolveResult target, ResolveResult[] arguments, string[] argumentNames)
{
if (target is LambdaResolveResult)
return false;
var rr = resolver.ResolveMemberAccess(target, method.Name, typeArguments, NameLookupMode.InvocationTarget) as MethodGroupResolveResult;
if (rr == null)
return false;
var or = rr.PerformOverloadResolution(resolver.CurrentTypeResolveContext.Compilation, arguments, argumentNames, allowExtensionMethods: true);
if (or == null || or.IsAmbiguous)
return false;
return method.Equals(or.GetBestCandidateWithSubstitutedTypeArguments());
return method.Equals(or.GetBestCandidateWithSubstitutedTypeArguments())
&& CSharpResolver.IsEligibleExtensionMethod(target.Type, method, useTypeInference: false, out _);
}
public static bool CanTransformToExtensionMethodCall(IMethod method, CSharpTypeResolveContext resolveContext, bool ignoreTypeArguments = false, bool ignoreArgumentNames = true)

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

@ -467,6 +467,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -467,6 +467,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
statementsToDelete.Add(stmt.GetNextStatement());
var itemVariable = foreachVariable.GetILVariable();
if (itemVariable == null || !itemVariable.IsSingleDefinition
|| (itemVariable.Kind != IL.VariableKind.Local && itemVariable.Kind != IL.VariableKind.StackSlot)
|| !upperBounds.All(ub => ub.IsSingleDefinition && ub.LoadCount == 1)
|| !lowerBounds.All(lb => lb.StoreCount == 2 && lb.LoadCount == 3 && lb.AddressCount == 0))
return null;
@ -776,8 +777,16 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -776,8 +777,16 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
default:
return false;
}
if (!ev.ReturnType.IsMatch(m.Get("type").Single()))
return false; // variable types must match event type
if (!ev.ReturnType.IsMatch(m.Get("type").Single())) {
// Variable types must match event type,
// except that the event type may have an additional nullability annotation
if (ev.ReturnType is ComposedType ct && ct.HasOnlyNullableSpecifier) {
if (!ct.BaseType.IsMatch(m.Get("type").Single()))
return false;
} else {
return false;
}
}
var combineMethod = m.Get<AstNode>("delegateCombine").Single().Parent.GetSymbol() as IMethod;
if (combineMethod == null || combineMethod.Name != (isAddAccessor ? "Combine" : "Remove"))
return false;

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

@ -122,6 +122,22 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -122,6 +122,22 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
}
UnaryOperatorType? uop = GetUnaryOperatorTypeFromMetadataName(method.Name);
if (uop != null && arguments.Length == 1) {
if (uop == UnaryOperatorType.Increment || uop == UnaryOperatorType.Decrement) {
// `op_Increment(a)` is not equivalent to `++a`,
// because it doesn't assign the incremented value to a.
if (method.DeclaringType.IsKnownType(KnownTypeCode.Decimal)) {
// Legacy csc optimizes "d + 1m" to "op_Increment(d)",
// so reverse that optimization here:
invocationExpression.ReplaceWith(
new BinaryOperatorExpression(
arguments[0].Detach(),
(uop == UnaryOperatorType.Increment ? BinaryOperatorType.Add : BinaryOperatorType.Subtract),
new PrimitiveExpression(1m)
).CopyAnnotationsFrom(invocationExpression)
);
}
return;
}
arguments[0].Remove(); // detach argument
invocationExpression.ReplaceWith(
new UnaryOperatorExpression(uop.Value, arguments[0]).CopyAnnotationsFrom(invocationExpression)
@ -154,8 +170,9 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -154,8 +170,9 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
if (arguments.Length < 2)
return false;
return arguments[0].GetResolveResult().Type.IsKnownType(KnownTypeCode.String) ||
arguments[1].GetResolveResult().Type.IsKnownType(KnownTypeCode.String);
return !arguments.Any(arg => arg is NamedArgumentExpression) &&
(arguments[0].GetResolveResult().Type.IsKnownType(KnownTypeCode.String) ||
arguments[1].GetResolveResult().Type.IsKnownType(KnownTypeCode.String));
}
static BinaryOperatorType? GetBinaryOperatorTypeFromMetadataName(string name)

38
ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs

@ -191,7 +191,11 @@ namespace ICSharpCode.Decompiler.CSharp @@ -191,7 +191,11 @@ namespace ICSharpCode.Decompiler.CSharp
conversion.Input.Type,
type, targetType
)) {
return this.UnwrapChild(cast.Expression);
var result = this.UnwrapChild(cast.Expression);
if (conversion.Conversion.IsUserDefined) {
result.Expression.AddAnnotation(new ImplicitConversionAnnotation(conversion));
}
return result;
} else if (Expression is ObjectCreateExpression oce && conversion.Conversion.IsMethodGroupConversion
&& oce.Arguments.Count == 1 && expressionBuilder.settings.UseImplicitMethodGroupConversion) {
return this.UnwrapChild(oce.Arguments.Single());
@ -211,6 +215,18 @@ namespace ICSharpCode.Decompiler.CSharp @@ -211,6 +215,18 @@ namespace ICSharpCode.Decompiler.CSharp
if (targetType.Kind == TypeKind.Unknown || targetType.Kind == TypeKind.Void || targetType.Kind == TypeKind.None) {
return this; // don't attempt to insert cast to '?' or 'void' as these are not valid.
}
var convAnnotation = this.Expression.Annotation<ImplicitConversionAnnotation>();
if (convAnnotation != null) {
// If an implicit user-defined conversion was stripped from this expression;
// it needs to be re-introduced before we can apply other casts to this expression.
// This happens when the CallBuilder discovers that the conversion is necessary in
// order to choose the correct overload.
this.Expression.RemoveAnnotations<ImplicitConversionAnnotation>();
return new CastExpression(expressionBuilder.ConvertType(convAnnotation.TargetType), Expression)
.WithoutILInstruction()
.WithRR(convAnnotation.ConversionResolveResult)
.ConvertTo(targetType, expressionBuilder, checkForOverflow, allowImplicitConversion);
}
if (Expression is ThrowExpression && allowImplicitConversion) {
return this; // Throw expressions have no type and are implicitly convertible to any type
}
@ -234,8 +250,9 @@ namespace ICSharpCode.Decompiler.CSharp @@ -234,8 +250,9 @@ namespace ICSharpCode.Decompiler.CSharp
}
var compilation = expressionBuilder.compilation;
var conversions = Resolver.CSharpConversions.Get(compilation);
if (ResolveResult is ConversionResolveResult conv && Expression is CastExpression cast2 &&
CastCanBeMadeImplicit(conversions, conv.Conversion, conv.Input.Type, type, targetType))
if (ResolveResult is ConversionResolveResult conv && Expression is CastExpression cast2
&& !conv.Conversion.IsUserDefined
&& CastCanBeMadeImplicit(conversions, conv.Conversion, conv.Input.Type, type, targetType))
{
var unwrapped = this.UnwrapChild(cast2.Expression);
if (allowImplicitConversion)
@ -362,7 +379,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -362,7 +379,7 @@ namespace ICSharpCode.Decompiler.CSharp
var convertedTemp = this.UnwrapChild(thisDir.Expression).ConvertTo(elementType, expressionBuilder, checkForOverflow);
return new DirectionExpression(FieldDirection.Ref, convertedTemp)
.WithILInstruction(this.ILInstructions)
.WithRR(new ByReferenceResolveResult(convertedTemp.ResolveResult, false));
.WithRR(new ByReferenceResolveResult(convertedTemp.ResolveResult, ReferenceKind.Ref));
}
// Convert from integer/pointer to reference.
// First, convert to the corresponding pointer type:
@ -382,7 +399,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -382,7 +399,7 @@ namespace ICSharpCode.Decompiler.CSharp
// And then take a reference:
return new DirectionExpression(FieldDirection.Ref, expr)
.WithoutILInstruction()
.WithRR(new ByReferenceResolveResult(elementRR, false));
.WithRR(new ByReferenceResolveResult(elementRR, ReferenceKind.Ref));
}
var rr = expressionBuilder.resolver.WithCheckForOverflow(checkForOverflow).ResolveCast(targetType, ResolveResult);
if (rr.IsCompileTimeConstant && !rr.IsError) {
@ -399,8 +416,15 @@ namespace ICSharpCode.Decompiler.CSharp @@ -399,8 +416,15 @@ namespace ICSharpCode.Decompiler.CSharp
.WithILInstruction(this.ILInstructions)
.WithRR(new ConstantResolveResult(targetType, null));
}
if (allowImplicitConversion && conversions.ImplicitConversion(ResolveResult, targetType).IsValid) {
return this;
if (allowImplicitConversion) {
if (conversions.ImplicitConversion(ResolveResult, targetType).IsValid) {
return this;
}
} else {
if (targetType.Kind != TypeKind.Dynamic && type.Kind != TypeKind.Dynamic && NormalizeTypeVisitor.TypeErasure.EquivalentTypes(type, targetType)) {
// avoid an explicit cast when types differ only in nullability of reference types
return this;
}
}
var castExpr = new CastExpression(expressionBuilder.ConvertType(targetType), Expression);
bool needsCheckAnnotation = targetUType.GetStackType().IsIntegerType();

33
ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs

@ -35,6 +35,7 @@ using System.Reflection.Metadata; @@ -35,6 +35,7 @@ using System.Reflection.Metadata;
using static ICSharpCode.Decompiler.Metadata.DotNetCorePathFinderExtensions;
using static ICSharpCode.Decompiler.Metadata.MetadataExtensions;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Solution;
namespace ICSharpCode.Decompiler.CSharp
{
@ -101,7 +102,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -101,7 +102,7 @@ namespace ICSharpCode.Decompiler.CSharp
}
}
public void DecompileProject(PEFile moduleDefinition, string targetDirectory, TextWriter projectFileWriter, CancellationToken cancellationToken = default(CancellationToken))
public ProjectId DecompileProject(PEFile moduleDefinition, string targetDirectory, TextWriter projectFileWriter, CancellationToken cancellationToken = default(CancellationToken))
{
if (string.IsNullOrEmpty(targetDirectory)) {
throw new InvalidOperationException("Must set TargetDirectory");
@ -110,7 +111,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -110,7 +111,7 @@ namespace ICSharpCode.Decompiler.CSharp
directories.Clear();
var files = WriteCodeFilesInProject(moduleDefinition, cancellationToken).ToList();
files.AddRange(WriteResourceFilesInProject(moduleDefinition));
WriteProjectFile(projectFileWriter, files, moduleDefinition);
return WriteProjectFile(projectFileWriter, files, moduleDefinition);
}
enum LanguageTargets
@ -120,11 +121,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -120,11 +121,12 @@ namespace ICSharpCode.Decompiler.CSharp
}
#region WriteProjectFile
void WriteProjectFile(TextWriter writer, IEnumerable<Tuple<string, string>> files, Metadata.PEFile module)
ProjectId WriteProjectFile(TextWriter writer, IEnumerable<Tuple<string, string>> files, Metadata.PEFile module)
{
const string ns = "http://schemas.microsoft.com/developer/msbuild/2003";
string platformName = GetPlatformName(module);
Guid guid = this.ProjectGuid ?? Guid.NewGuid();
using (XmlTextWriter w = new XmlTextWriter(writer)) {
w.Formatting = Formatting.Indented;
w.WriteStartDocument();
@ -167,6 +169,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -167,6 +169,7 @@ namespace ICSharpCode.Decompiler.CSharp
bool useTargetFrameworkAttribute = false;
LanguageTargets languageTargets = LanguageTargets.None;
string targetFramework = module.Reader.DetectTargetFrameworkId();
int frameworkVersionNumber = 0;
if (!string.IsNullOrEmpty(targetFramework)) {
string[] frameworkParts = targetFramework.Split(',');
string frameworkIdentifier = frameworkParts.FirstOrDefault(a => !a.StartsWith("Version=", StringComparison.OrdinalIgnoreCase) && !a.StartsWith("Profile=", StringComparison.OrdinalIgnoreCase));
@ -182,6 +185,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -182,6 +185,8 @@ namespace ICSharpCode.Decompiler.CSharp
if (frameworkVersion != null) {
w.WriteElementString("TargetFrameworkVersion", frameworkVersion.Substring("Version=".Length));
useTargetFrameworkAttribute = true;
frameworkVersionNumber = int.Parse(frameworkVersion.Substring("Version=v".Length).Replace(".", ""));
if (frameworkVersionNumber < 100) frameworkVersionNumber *= 10;
}
string frameworkProfile = frameworkParts.FirstOrDefault(a => a.StartsWith("Profile=", StringComparison.OrdinalIgnoreCase));
if (frameworkProfile != null)
@ -190,16 +195,20 @@ namespace ICSharpCode.Decompiler.CSharp @@ -190,16 +195,20 @@ namespace ICSharpCode.Decompiler.CSharp
if (!useTargetFrameworkAttribute) {
switch (module.GetRuntime()) {
case Metadata.TargetRuntime.Net_1_0:
frameworkVersionNumber = 100;
w.WriteElementString("TargetFrameworkVersion", "v1.0");
break;
case Metadata.TargetRuntime.Net_1_1:
frameworkVersionNumber = 110;
w.WriteElementString("TargetFrameworkVersion", "v1.1");
break;
case Metadata.TargetRuntime.Net_2_0:
frameworkVersionNumber = 200;
w.WriteElementString("TargetFrameworkVersion", "v2.0");
// TODO: Detect when .NET 3.0/3.5 is required
break;
default:
frameworkVersionNumber = 400;
w.WriteElementString("TargetFrameworkVersion", "v4.0");
break;
}
@ -212,8 +221,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -212,8 +221,8 @@ namespace ICSharpCode.Decompiler.CSharp
w.WriteStartElement("PropertyGroup"); // platform-specific
w.WriteAttributeString("Condition", " '$(Platform)' == '" + platformName + "' ");
w.WriteElementString("PlatformTarget", platformName);
if ((module.Reader.PEHeaders.CorHeader.Flags & CorFlags.Prefers32Bit) != 0) {
w.WriteElementString("Prefer32Bit", "True");
if (frameworkVersionNumber > 400 && platformName == "AnyCPU" && (module.Reader.PEHeaders.CorHeader.Flags & CorFlags.Prefers32Bit) == 0) {
w.WriteElementString("Prefer32Bit", "false");
}
w.WriteEndElement(); // </PropertyGroup> (platform-specific)
@ -274,6 +283,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -274,6 +283,8 @@ namespace ICSharpCode.Decompiler.CSharp
w.WriteEndDocument();
}
return new ProjectId(platformName, guid);
}
protected virtual bool IsGacAssembly(Metadata.IAssemblyReference r, Metadata.PEFile asm)
@ -344,10 +355,14 @@ namespace ICSharpCode.Decompiler.CSharp @@ -344,10 +355,14 @@ namespace ICSharpCode.Decompiler.CSharp
},
delegate (IGrouping<string, TypeDefinitionHandle> file) {
using (StreamWriter w = new StreamWriter(Path.Combine(targetDirectory, file.Key))) {
CSharpDecompiler decompiler = CreateDecompiler(ts);
decompiler.CancellationToken = cancellationToken;
var syntaxTree = decompiler.DecompileTypes(file.ToArray());
syntaxTree.AcceptVisitor(new CSharpOutputVisitor(w, settings.CSharpFormattingOptions));
try {
CSharpDecompiler decompiler = CreateDecompiler(ts);
decompiler.CancellationToken = cancellationToken;
var syntaxTree = decompiler.DecompileTypes(file.ToArray());
syntaxTree.AcceptVisitor(new CSharpOutputVisitor(w, settings.CSharpFormattingOptions));
} catch (Exception innerException) when (!(innerException is OperationCanceledException || innerException is DecompilerException)) {
throw new DecompilerException(module, $"Error decompiling for '{file.Key}'", innerException);
}
}
});
return files.Select(f => Tuple.Create("Compile", f.Key)).Concat(WriteAssemblyInfo(ts, cancellationToken));

26
ICSharpCode.Decompiler/DecompilerException.cs

@ -21,10 +21,12 @@ using System.Diagnostics; @@ -21,10 +21,12 @@ using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Security;
using System.Text;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler
@ -34,20 +36,35 @@ namespace ICSharpCode.Decompiler @@ -34,20 +36,35 @@ namespace ICSharpCode.Decompiler
/// </summary>
public class DecompilerException : Exception, ISerializable
{
public string AssemblyName => Module.AssemblyName;
public string AssemblyName => File.Name;
public string FileName => Module.PEFile.FileName;
public string FileName => File.FileName;
public IEntity DecompiledEntity { get; }
public IModule Module { get; }
public PEFile File { get; }
public DecompilerException(MetadataModule module, IEntity decompiledEntity, Exception innerException, string message = null)
: base((message ?? "Error decompiling " + decompiledEntity?.FullName) + Environment.NewLine, innerException)
: base(message ?? GetDefaultMessage(decompiledEntity), innerException)
{
this.File = module.PEFile;
this.Module = module;
this.DecompiledEntity = decompiledEntity;
}
public DecompilerException(PEFile file, string message, Exception innerException)
: base(message, innerException)
{
this.File = file;
}
static string GetDefaultMessage(IEntity entity)
{
if (entity == null)
return "Error decompiling";
return $"Error decompiling @{MetadataTokens.GetToken(entity.MetadataToken):X8} {entity.FullName}";
}
// This constructor is needed for serialization.
protected DecompilerException(SerializationInfo info, StreamingContext context) : base(info, context)
{
@ -71,7 +88,8 @@ namespace ICSharpCode.Decompiler @@ -71,7 +88,8 @@ namespace ICSharpCode.Decompiler
+ stacktrace;
exceptionType = GetTypeName(exception);
}
return this.Message
return this.Message + Environment.NewLine
+ $"in assembly \"{this.FileName}\"" + Environment.NewLine
+ " ---> " + exceptionType + ": " + exception.Message + Environment.NewLine
+ stacktrace;
}

33
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -98,6 +98,7 @@ namespace ICSharpCode.Decompiler @@ -98,6 +98,7 @@ namespace ICSharpCode.Decompiler
introduceReadonlyAndInModifiers = false;
introduceRefModifiersOnStructs = false;
nonTrailingNamedArguments = false;
refExtensionMethods = false;
}
if (languageVersion < CSharp.LanguageVersion.CSharp7_3) {
introduceUnmanagedConstraint = false;
@ -115,7 +116,7 @@ namespace ICSharpCode.Decompiler @@ -115,7 +116,7 @@ namespace ICSharpCode.Decompiler
return CSharp.LanguageVersion.CSharp8_0;
if (introduceUnmanagedConstraint || tupleComparisons || stackAllocInitializers)
return CSharp.LanguageVersion.CSharp7_3;
if (introduceRefModifiersOnStructs || introduceReadonlyAndInModifiers || nonTrailingNamedArguments)
if (introduceRefModifiersOnStructs || introduceReadonlyAndInModifiers || nonTrailingNamedArguments || refExtensionMethods)
return CSharp.LanguageVersion.CSharp7_2;
// C# 7.1 missing
if (outVariables || throwExpressions || tupleTypes || tupleConversions || discards || localFunctions)
@ -627,6 +628,23 @@ namespace ICSharpCode.Decompiler @@ -627,6 +628,23 @@ namespace ICSharpCode.Decompiler
}
}
bool refExtensionMethods = true;
/// <summary>
/// Gets/Sets whether to use C# 7.2 'ref' extension methods.
/// </summary>
[Category("C# 7.2 / VS 2017.4")]
[Description("DecompilerSettings.AllowExtensionMethodSyntaxOnRef")]
public bool RefExtensionMethods {
get { return refExtensionMethods; }
set {
if (refExtensionMethods != value) {
refExtensionMethods = value;
OnPropertyChanged();
}
}
}
bool stringInterpolation = true;
/// <summary>
@ -960,22 +978,19 @@ namespace ICSharpCode.Decompiler @@ -960,22 +978,19 @@ namespace ICSharpCode.Decompiler
}
}
bool localFunctions = false;
bool localFunctions = true;
/// <summary>
/// Gets/Sets whether C# 7.0 local functions should be used.
/// Note: this language feature is currently not implemented and this setting is always false.
/// Gets/Sets whether C# 7.0 local functions should be transformed.
/// </summary>
[Category("C# 7.0 / VS 2017")]
[Description("DecompilerSettings.IntroduceLocalFunctionsNOTIMPLEMENTED")]
[Browsable(false)]
[Description("DecompilerSettings.IntroduceLocalFunctions")]
public bool LocalFunctions {
get { return localFunctions; }
set {
if (localFunctions != value) {
throw new NotImplementedException("C# 7.0 local functions are not implemented!");
//localFunctions = value;
//OnPropertyChanged();
localFunctions = value;
OnPropertyChanged();
}
}
}

83
ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs

@ -225,19 +225,25 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -225,19 +225,25 @@ namespace ICSharpCode.Decompiler.Disassembler
output.WriteLine();
output.Indent();
var declaringType = methodDefinition.GetDeclaringType();
var signatureProvider = new DisassemblerSignatureProvider(module, output);
var signature = methodDefinition.DecodeSignature(signatureProvider, genericContext);
if (signature.Header.HasExplicitThis) {
output.Write("instance explicit ");
} else if (signature.Header.IsInstance) {
output.Write("instance ");
}
MethodSignature<Action<ILNameSyntax>>? signature;
try {
var signatureProvider = new DisassemblerSignatureProvider(module, output);
signature = methodDefinition.DecodeSignature(signatureProvider, genericContext);
if (signature.Value.Header.HasExplicitThis) {
output.Write("instance explicit ");
} else if (signature.Value.Header.IsInstance) {
output.Write("instance ");
}
//call convention
WriteEnum(signature.Header.CallingConvention, callingConvention);
//call convention
WriteEnum(signature.Value.Header.CallingConvention, callingConvention);
//return type
signature.ReturnType(ILNameSyntax.Signature);
//return type
signature.Value.ReturnType(ILNameSyntax.Signature);
} catch (BadImageFormatException) {
signature = null;
output.Write("<bad signature>");
}
output.Write(' ');
var parameters = methodDefinition.GetParameters();
@ -261,10 +267,10 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -261,10 +267,10 @@ namespace ICSharpCode.Decompiler.Disassembler
//( params )
output.Write(" (");
if (signature.ParameterTypes.Length > 0) {
if (signature?.ParameterTypes.Length > 0) {
output.WriteLine();
output.Indent();
WriteParameters(metadata, parameters, signature);
WriteParameters(metadata, parameters, signature.Value);
output.Unindent();
}
output.Write(") ");
@ -1105,8 +1111,11 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -1105,8 +1111,11 @@ namespace ICSharpCode.Decompiler.Disassembler
output.Write(' ');
var fieldName = metadata.GetString(fieldDefinition.Name);
output.Write(DisassemblerHelpers.Escape(fieldName));
char sectionPrefix = 'D';
if (fieldDefinition.HasFlag(FieldAttributes.HasFieldRVA)) {
output.Write(" at I_{0:x8}", fieldDefinition.GetRelativeVirtualAddress());
int rva = fieldDefinition.GetRelativeVirtualAddress();
sectionPrefix = GetRVASectionPrefix(module.Reader.PEHeaders, rva);
output.Write(" at {1}_{0:X8}", rva, sectionPrefix);
}
var defaultValue = fieldDefinition.GetDefaultValue();
@ -1121,6 +1130,52 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -1121,6 +1130,52 @@ namespace ICSharpCode.Decompiler.Disassembler
WriteAttributes(module, fieldDefinition.GetCustomAttributes());
output.MarkFoldEnd();
}
if (fieldDefinition.HasFlag(FieldAttributes.HasFieldRVA)) {
// Field data as specified in II.16.3.1 of ECMA-335 6th edition
int rva = fieldDefinition.GetRelativeVirtualAddress();
int sectionIndex = module.Reader.PEHeaders.GetContainingSectionIndex(rva);
if (sectionIndex < 0) {
output.WriteLine($"// RVA {rva:X8} invalid (not in any section)");
} else {
BlobReader initVal;
try {
initVal = fieldDefinition.GetInitialValue(module.Reader, null);
} catch (BadImageFormatException ex) {
initVal = default;
output.WriteLine("// .data {2}_{0:X8} = {1}", fieldDefinition.GetRelativeVirtualAddress(), ex.Message, sectionPrefix);
}
if (initVal.Length > 0) {
var sectionHeader = module.Reader.PEHeaders.SectionHeaders[sectionIndex];
output.Write(".data ");
if (sectionHeader.Name == ".text") {
output.Write("cil ");
} else if (sectionHeader.Name == ".tls") {
output.Write("tls ");
} else if (sectionHeader.Name != ".data") {
output.Write($"/* {sectionHeader.Name} */ ");
}
output.Write($"{sectionPrefix}_{rva:X8} = bytearray ");
WriteBlob(initVal);
output.WriteLine();
}
}
}
}
char GetRVASectionPrefix(System.Reflection.PortableExecutable.PEHeaders headers, int rva)
{
int sectionIndex = headers.GetContainingSectionIndex(rva);
if (sectionIndex < 0)
return 'D';
var sectionHeader = headers.SectionHeaders[sectionIndex];
switch (sectionHeader.Name) {
case ".tls":
return 'T';
case ".text":
return 'I';
default:
return 'D';
}
}
#endregion

125
ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs

@ -222,6 +222,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -222,6 +222,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
this.bottomState = initialState.Clone();
this.bottomState.ReplaceWithBottom();
Debug.Assert(bottomState.IsBottom);
this.stateOnNullableRewrap = bottomState.Clone();
this.currentStateOnException = state.Clone();
}
@ -254,7 +255,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -254,7 +255,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
#endif
[Conditional("DEBUG")]
void DebugStartPoint(ILInstruction inst)
protected void DebugStartPoint(ILInstruction inst)
{
#if DEBUG
DebugPoint(debugInputState, inst);
@ -262,7 +263,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -262,7 +263,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
}
[Conditional("DEBUG")]
void DebugEndPoint(ILInstruction inst)
protected void DebugEndPoint(ILInstruction inst)
{
#if DEBUG
DebugPoint(debugOutputState, inst);
@ -286,7 +287,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -286,7 +287,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
foreach (var child in inst.Children) {
child.AcceptVisitor(this);
Debug.Assert(state.IsBottom || !child.HasFlag(InstructionFlags.EndPointUnreachable),
"Unreachable code must be in the bottom state.");
"Unreachable code must be in the bottom state.");
}
DebugEndPoint(inst);
@ -602,16 +603,110 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -602,16 +603,110 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
protected internal override void VisitIfInstruction(IfInstruction inst)
{
DebugStartPoint(inst);
inst.Condition.AcceptVisitor(this);
State branchState = state.Clone();
var (beforeThen, beforeElse) = EvaluateCondition(inst.Condition);
state = beforeThen;
inst.TrueInst.AcceptVisitor(this);
State afterTrueState = state;
state = branchState;
state = beforeElse;
inst.FalseInst.AcceptVisitor(this);
state.JoinWith(afterTrueState);
DebugEndPoint(inst);
}
/// <summary>
/// Evaluates the condition of an if.
/// </summary>
/// <returns>
/// A pair of:
/// * The state after the condition evaluates to true
/// * The state after the condition evaluates to false
/// </returns>
/// <remarks>
/// <c>this.state</c> is invalid after this function was called, and must be overwritten
/// with one of the return values.
/// </remarks>
(State OnTrue, State OnFalse) EvaluateCondition(ILInstruction inst)
{
if (inst is IfInstruction ifInst) {
// 'if (a?b:c)' or similar.
// This also includes conditions that are logic.not, logic.and, logic.or.
DebugStartPoint(ifInst);
var (beforeThen, beforeElse) = EvaluateCondition(ifInst.Condition);
state = beforeThen;
var (afterThenTrue, afterThenFalse) = EvaluateCondition(ifInst.TrueInst);
state = beforeElse;
var (afterElseTrue, afterElseFalse) = EvaluateCondition(ifInst.FalseInst);
var onTrue = afterThenTrue;
onTrue.JoinWith(afterElseTrue);
var onFalse = afterThenFalse;
onFalse.JoinWith(afterElseFalse);
DebugEndPoint(ifInst);
return (onTrue, onFalse);
} else if (inst is LdcI4 constant) {
if (constant.Value == 0) {
return (bottomState.Clone(), state);
} else {
return (state, bottomState.Clone());
}
} else {
// other kind of condition
inst.AcceptVisitor(this);
return (state, state.Clone());
}
}
protected internal override void VisitNullCoalescingInstruction(NullCoalescingInstruction inst)
{
HandleBinaryWithOptionalEvaluation(inst, inst.ValueInst, inst.FallbackInst);
}
protected internal override void VisitDynamicLogicOperatorInstruction(DynamicLogicOperatorInstruction inst)
{
HandleBinaryWithOptionalEvaluation(inst, inst.Left, inst.Right);
}
protected internal override void VisitUserDefinedLogicOperator(UserDefinedLogicOperator inst)
{
HandleBinaryWithOptionalEvaluation(inst, inst.Left, inst.Right);
}
void HandleBinaryWithOptionalEvaluation(ILInstruction parent, ILInstruction left, ILInstruction right)
{
DebugStartPoint(parent);
left.AcceptVisitor(this);
State branchState = state.Clone();
right.AcceptVisitor(this);
state.JoinWith(branchState);
DebugEndPoint(parent);
}
State stateOnNullableRewrap;
protected internal override void VisitNullableRewrap(NullableRewrap inst)
{
DebugStartPoint(inst);
var oldState = stateOnNullableRewrap.Clone();
stateOnNullableRewrap.ReplaceWithBottom();
inst.Argument.AcceptVisitor(this);
// Join incoming control flow from the NullableUnwraps.
state.JoinWith(stateOnNullableRewrap);
stateOnNullableRewrap = oldState;
DebugEndPoint(inst);
}
protected internal override void VisitNullableUnwrap(NullableUnwrap inst)
{
DebugStartPoint(inst);
inst.Argument.AcceptVisitor(this);
// Conditional control flow edge to the surrounding NullableRewrap.
stateOnNullableRewrap.JoinWith(state);
DebugEndPoint(inst);
}
protected internal override void VisitSwitchInstruction(SwitchInstruction inst)
{
DebugStartPoint(inst);
@ -635,6 +730,22 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -635,6 +730,22 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
DebugEndPoint(inst);
}
protected internal override void VisitUsingInstruction(UsingInstruction inst)
{
DebugStartPoint(inst);
inst.ResourceExpression.AcceptVisitor(this);
inst.Body.AcceptVisitor(this);
DebugEndPoint(inst);
}
protected internal override void VisitLockInstruction(LockInstruction inst)
{
DebugStartPoint(inst);
inst.OnExpression.AcceptVisitor(this);
inst.Body.AcceptVisitor(this);
DebugEndPoint(inst);
}
protected internal override void VisitILFunction(ILFunction function)
{
throw new NotImplementedException();

72
ICSharpCode.Decompiler/FlowAnalysis/DefiniteAssignmentVisitor.cs

@ -20,6 +20,9 @@ using System.Diagnostics; @@ -20,6 +20,9 @@ using System.Diagnostics;
using ICSharpCode.Decompiler.IL;
using ICSharpCode.Decompiler.Util;
using System.Threading;
using System;
using System.Collections.Generic;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
@ -117,6 +120,9 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -117,6 +120,9 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
readonly CancellationToken cancellationToken;
readonly ILFunction scope;
readonly BitSet variablesWithUninitializedUsage;
readonly Dictionary<IMethod, State> stateOfLocalFunctionUse = new Dictionary<IMethod, State>();
readonly HashSet<IMethod> localFunctionsNeedingAnalysis = new HashSet<IMethod>();
public DefiniteAssignmentVisitor(ILFunction scope, CancellationToken cancellationToken)
{
@ -203,8 +209,46 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -203,8 +209,46 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
HandleCall(inst);
}
protected internal override void VisitILFunction(ILFunction inst)
{
DebugStartPoint(inst);
State stateBeforeFunction = state.Clone();
State stateOnExceptionBeforeFunction = currentStateOnException.Clone();
// Note: lambdas are handled at their point of declaration.
// We immediately visit their body, because captured variables need to be definitely initialized at this point.
// We ignore the state after the lambda body (by resetting to the state before), because we don't know
// when the lambda will be invoked.
// This also makes this logic unsuitable for reaching definitions, as we wouldn't see the effect of stores in lambdas.
// Only the simpler case of definite assignment can support lambdas.
inst.Body.AcceptVisitor(this);
// For local functions, the situation is similar to lambdas.
// However, we don't use the state of the declaration site when visiting local functions,
// but instead the state(s) of their point of use.
// Because we might discover additional points of use within the local functions,
// we use a fixed-point iteration.
bool changed;
do {
changed = false;
foreach (var nestedFunction in inst.LocalFunctions) {
if (!localFunctionsNeedingAnalysis.Contains(nestedFunction.ReducedMethod))
continue;
localFunctionsNeedingAnalysis.Remove(nestedFunction.ReducedMethod);
State stateOnEntry = stateOfLocalFunctionUse[nestedFunction.ReducedMethod];
this.state.ReplaceWith(stateOnEntry);
this.currentStateOnException.ReplaceWith(stateOnEntry);
nestedFunction.AcceptVisitor(this);
changed = true;
}
} while (changed);
currentStateOnException = stateOnExceptionBeforeFunction;
state = stateBeforeFunction;
DebugEndPoint(inst);
}
void HandleCall(CallInstruction call)
{
DebugStartPoint(call);
bool hasOutArgs = false;
foreach (var arg in call.Arguments) {
if (arg.MatchLdLoca(out var v) && call.GetParameter(arg.ChildIndex)?.IsOut == true) {
@ -223,6 +267,34 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -223,6 +267,34 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
}
}
}
HandleLocalFunctionUse(call.Method);
DebugEndPoint(call);
}
/// <summary>
/// For a use of a local function, remember the current state to use as stateOnEntry when
/// later processing the local function body.
/// </summary>
void HandleLocalFunctionUse(IMethod method)
{
if (method.IsLocalFunction) {
if (stateOfLocalFunctionUse.TryGetValue(method, out var stateOnEntry)) {
if (!state.LessThanOrEqual(stateOnEntry)) {
stateOnEntry.JoinWith(state);
localFunctionsNeedingAnalysis.Add(method);
}
} else {
stateOfLocalFunctionUse.Add(method, state.Clone());
localFunctionsNeedingAnalysis.Add(method);
}
}
}
protected internal override void VisitLdFtn(LdFtn inst)
{
DebugStartPoint(inst);
HandleLocalFunctionUse(inst.Method);
DebugEndPoint(inst);
}
}
}

16
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -16,7 +16,7 @@ @@ -16,7 +16,7 @@
<EnableDefaultItems>false</EnableDefaultItems>
<LangVersion>7.2</LangVersion>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<SignAssembly>True</SignAssembly>
<AssemblyOriginatorKeyFile>ICSharpCode.Decompiler.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
@ -48,6 +48,10 @@ @@ -48,6 +48,10 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<NoWarn>1701;1702;1591;1573</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Humanizer.Core" Version="2.2.0" />
<PackageReference Include="System.Collections.Immutable" Version="1.5.0" />
@ -61,6 +65,9 @@ @@ -61,6 +65,9 @@
<Compile Include="CSharp\CSharpLanguageVersion.cs" />
<Compile Include="CSharp\RequiredNamespaceCollector.cs" />
<Compile Include="CSharp\SequencePointBuilder.cs" />
<Compile Include="Solution\ProjectId.cs" />
<Compile Include="Solution\ProjectItem.cs" />
<Compile Include="Solution\SolutionCreator.cs" />
<Compile Include="CSharp\Syntax\AstNode.cs" />
<Compile Include="CSharp\Syntax\AstNodeCollection.cs" />
<Compile Include="CSharp\Syntax\AstType.cs" />
@ -161,6 +168,7 @@ @@ -161,6 +168,7 @@
<Compile Include="CSharp\Syntax\Statements\GotoStatement.cs" />
<Compile Include="CSharp\Syntax\Statements\IfElseStatement.cs" />
<Compile Include="CSharp\Syntax\Statements\LabelStatement.cs" />
<Compile Include="CSharp\Syntax\Statements\LocalFunctionDeclarationStatement.cs" />
<Compile Include="CSharp\Syntax\Statements\LockStatement.cs" />
<Compile Include="CSharp\Syntax\Statements\ReturnStatement.cs" />
<Compile Include="CSharp\Syntax\Statements\Statement.cs" />
@ -222,8 +230,9 @@ @@ -222,8 +230,9 @@
<Compile Include="CSharp\Resolver\NameLookupMode.cs" />
<Compile Include="CSharp\Resolver\OverloadResolution.cs" />
<Compile Include="CSharp\Resolver\OverloadResolutionErrors.cs" />
<Compile Include="CSharp\Resolver\ReducedExtensionMethod.cs" />
<Compile Include="CSharp\Resolver\RenameCallbackArguments.cs" />
<Compile Include="IL\Transforms\FixRemainingIncrements.cs" />
<Compile Include="IL\Transforms\ILExtraction.cs" />
<Compile Include="TypeSystem\Implementation\LocalFunctionMethod.cs" />
<Compile Include="CSharp\Resolver\TypeInference.cs" />
<Compile Include="CSharp\Transforms\CombineQueryExpressions.cs" />
<Compile Include="CSharp\Transforms\FlattenSwitchBlocks.cs" />
@ -272,6 +281,7 @@ @@ -272,6 +281,7 @@
<Compile Include="IL\Transforms\DynamicIsEventAssignmentTransform.cs" />
<Compile Include="IL\Transforms\ReduceNestingTransform.cs" />
<Compile Include="IL\Transforms\LocalFunctionDecompiler.cs" />
<Compile Include="IL\Transforms\TransformDisplayClassUsage.cs" />
<Compile Include="IL\Transforms\UserDefinedLogicTransform.cs" />
<Compile Include="Metadata\AssemblyReferences.cs" />
<Compile Include="Metadata\CodeMappingInfo.cs" />

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.nuspec.template

@ -24,5 +24,6 @@ @@ -24,5 +24,6 @@
<files>
<file src="bin\$Configuration$\netstandard2.0\ICSharpCode.Decompiler.dll" target="lib\netstandard2.0" />
<file src="bin\$Configuration$\netstandard2.0\ICSharpCode.Decompiler.pdb" target="lib\netstandard2.0" />
<file src="bin\$Configuration$\netstandard2.0\ICSharpCode.Decompiler.xml" target="lib\netstandard2.0" />
</files>
</package>

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

@ -128,7 +128,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -128,7 +128,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
CleanDoFinallyBodies(function);
context.Step("Translate fields to local accesses", function);
MarkGeneratedVariables(function);
YieldReturnDecompiler.TranslateFieldsToLocalAccess(function, function, fieldToParameterMap);
TranslateCachedFieldsToLocals();
@ -902,17 +901,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -902,17 +901,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
#endregion
void MarkGeneratedVariables(ILFunction function)
{
// Variables after the awaiters are usually compiler-generated;
// so mark them as stack slots.
foreach (var v in function.Variables) {
if (v.Kind == VariableKind.Local && v.Index >= smallestAwaiterVarIndex) {
v.Kind = VariableKind.StackSlot;
}
}
}
/// <summary>
/// Eliminates usage of doFinallyBodies
/// </summary>

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

@ -354,7 +354,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -354,7 +354,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
//assert then block terminates
var trueExitInst = GetExit(ifInst.TrueInst);
var exitInst = GetExit(block);
context.Step("Negate if for desired branch "+trueExitInst, ifInst);
context.Step($"InvertIf at IL_{ifInst.StartILOffset:x4}", ifInst);
//if the then block terminates, else blocks are redundant, and should not exist
Debug.Assert(IsEmpty(ifInst.FalseInst));
@ -403,7 +403,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -403,7 +403,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// <summary>
/// if (cond) { if (nestedCond) { nestedThen... } }
/// ->
/// if (cond && nestedCond) { nestedThen... }
/// if (cond &amp;&amp; nestedCond) { nestedThen... }
/// </summary>
private void IntroduceShortCircuit(IfInstruction ifInst)
{

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

@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
// 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;
@ -45,6 +46,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -45,6 +46,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
context.CancellationToken.ThrowIfCancellationRequested();
RemoveNopInstructions(block);
RemoveDeadStackStores(block, aggressive: context.Settings.RemoveDeadCode);
InlineVariableInReturnBlock(block, context);
// 1st pass SimplifySwitchInstruction before SimplifyBranchChains()
@ -68,6 +70,35 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -68,6 +70,35 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
block.Instructions.RemoveAll(inst => inst.OpCode == OpCode.Nop);
}
private void RemoveDeadStackStores(Block block, bool aggressive)
{
// Previously copy propagation did this;
// ideally the ILReader would already do this,
// for now do this here (even though it's not control-flow related).
for (int i = block.Instructions.Count - 1; i >= 0; i--) {
if (block.Instructions[i] is StLoc stloc && stloc.Variable.IsSingleDefinition && stloc.Variable.LoadCount == 0 && stloc.Variable.Kind == VariableKind.StackSlot) {
if (aggressive ? SemanticHelper.IsPure(stloc.Value.Flags) : IsSimple(stloc.Value)) {
Debug.Assert(SemanticHelper.IsPure(stloc.Value.Flags));
block.Instructions.RemoveAt(i++);
} else {
stloc.Value.AddILRange(stloc);
stloc.ReplaceWith(stloc.Value);
}
}
}
bool IsSimple(ILInstruction inst)
{
switch (inst.OpCode) {
case OpCode.LdLoc:
case OpCode.LdStr: // C# 1.0 compiler sometimes emits redundant ldstr in switch-on-string pattern
return true;
default:
return false;
}
}
}
void InlineVariableInReturnBlock(Block block, ILTransformContext context)
{
// In debug mode, the C#-compiler generates 'return blocks' that
@ -158,9 +189,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -158,9 +189,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
// Remove return blocks that are no longer reachable:
container.Blocks.RemoveAll(b => b.IncomingEdgeCount == 0 && b.Instructions.Count == 0);
if (context.Settings.RemoveDeadCode) {
container.SortBlocks(deleteUnreachableBlocks: true);
}
}
}

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

@ -504,26 +504,23 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -504,26 +504,23 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// stloc nativeVar(conv o->i (ldloc pinnedVar))
// if (comp(ldloc nativeVar == conv i4->i <sign extend>(ldc.i4 0))) br targetBlock
// br adjustOffsetToStringData
if (!body.EntryPoint.Instructions[0].MatchStLoc(out ILVariable nativeVar, out ILInstruction initInst))
return;
ILVariable newVar;
if (body.EntryPoint.Instructions.Count != 3) {
if (!body.EntryPoint.Instructions[0].MatchStLoc(out ILVariable nativeVar, out ILInstruction initInst)) {
// potentially a special case with legacy csc and an unused pinned variable:
if (nativeVar.IsSingleDefinition && nativeVar.LoadCount == 0 && initInst.MatchLdLoc(pinnedRegion.Variable)
&& pinnedRegion.Variable.LoadCount == 1)
{
// initInst is dead store
body.EntryPoint.Instructions.RemoveAt(0);
if (pinnedRegion.Variable.AddressCount == 0 && pinnedRegion.Variable.LoadCount == 0) {
var charPtr = new PointerType(context.TypeSystem.FindType(KnownTypeCode.Char));
newVar = new ILVariable(VariableKind.PinnedLocal, charPtr, pinnedRegion.Variable.Index);
newVar.Name = pinnedRegion.Variable.Name;
newVar.HasGeneratedName = pinnedRegion.Variable.HasGeneratedName;
nativeVar.Function.Variables.Add(newVar);
pinnedRegion.Variable.Function.Variables.Add(newVar);
pinnedRegion.Variable = newVar;
pinnedRegion.Init = new ArrayToPointer(pinnedRegion.Init);
}
return;
}
if (body.EntryPoint.Instructions.Count != 3) {
return;
}
if (nativeVar.Type.GetStackType() != StackType.I)
return;

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

@ -107,7 +107,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -107,7 +107,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
IncludeNestedContainers(loop);
// Try to extend the loop to reduce the number of exit points:
ExtendLoop(h, loop, out var exitPoint);
IncludeUnreachablePredecessors(loop);
// 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)
@ -115,7 +114,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -115,7 +114,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
Debug.Assert(loop[0] == h);
foreach (var node in loop) {
node.Visited = false; // reset visited flag so that we can find outer loops
Debug.Assert(h.Dominates(node) || !node.IsReachable, "The loop body must be dominated by the loop head");
Debug.Assert(h.Dominates(node), "The loop body must be dominated by the loop head");
}
ConstructLoop(loop, exitPoint);
}
@ -150,7 +149,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -150,7 +149,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// (the entry-point itself doesn't have a CFG node, because it's newly created by this transform)
for (int i = 1; i < nestedContainer.Blocks.Count; i++) {
var node = context.ControlFlowGraph.GetNode(nestedContainer.Blocks[i]);
Debug.Assert(loop[0].Dominates(node) || !node.IsReachable);
Debug.Assert(loop[0].Dominates(node));
if (!node.Visited) {
node.Visited = true;
loop.Add(node);
@ -258,6 +257,22 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -258,6 +257,22 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
loop.Add(node);
}
}
// The loop/switch can only be entered through the entry point.
if (isSwitch) {
// In the case of a switch, false positives in the "continue;" detection logic
// can lead to falsely excludes some blocks from the body.
// Fix that by including all predecessors of included blocks.
Debug.Assert(loop[0] == loopHead);
for (int i = 1; i < loop.Count; i++) {
foreach (var p in loop[i].Predecessors) {
if (!p.Visited) {
p.Visited = true;
loop.Add(p);
}
}
}
}
Debug.Assert(loop.All(n => n == loopHead || n.Predecessors.All(p => p.Visited)));
} else {
// We are in case 2, but could not find a suitable exit point.
// Heuristically try to minimize the number of exit points
@ -458,7 +473,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -458,7 +473,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// or that leave the block Container.
/// </summary>
/// <param name="loopHead">Entry point of the loop.</param>
/// <param name="isSwitch">Whether to ignore branches that map to C# 'continue' statements.</param>
/// <param name="exitNodeArity">out: The number of different CFG nodes.
/// Possible values:
/// 0 = no CFG nodes used as exit nodes (although edges leaving the block container might still be exits);
@ -603,30 +617,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -603,30 +617,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
#endregion
/// <summary>
/// While our normal dominance logic ensures the loop has just a single reachable entry point,
/// it's possible that there are unreachable code blocks that have jumps into the loop.
/// We'll also include those into the loop.
///
/// Requires and maintains the invariant that a node is marked as visited iff it is contained in the loop.
/// </summary>
private void IncludeUnreachablePredecessors(List<ControlFlowNode> loop)
{
for (int i = 1; i < loop.Count; i++) {
Debug.Assert(loop[i].Visited);
foreach (var pred in loop[i].Predecessors) {
if (!pred.Visited) {
if (pred.IsReachable) {
Debug.Fail("All jumps into the loop body should go through the entry point");
} else {
pred.Visited = true;
loop.Add(pred);
}
}
}
}
}
/// <summary>
/// Move the blocks associated with the loop into a new block container.
/// </summary>
@ -709,7 +699,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -709,7 +699,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
exitPoint = null;
}
IncludeUnreachablePredecessors(nodesInSwitch);
context.Step("Create BlockContainer for switch", switchInst);
// Sort blocks in the loop in reverse post-order to make the output look a bit nicer.
@ -718,7 +707,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -718,7 +707,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
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");
Debug.Assert(h.Dominates(node), "The switch body must be dominated by the switch head");
}
BlockContainer switchContainer = new BlockContainer(ContainerKind.Switch);

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

@ -441,7 +441,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -441,7 +441,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// s c
///
/// where:
/// p|n: if (a && b) goto c; goto s;
/// p|n: if (a &amp;&amp; b) goto c; goto s;
///
/// Note that if n has only 1 successor, but is still a flow node, then a short circuit expression
/// has a target (c) with no corresponding block (leave)

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

@ -70,7 +70,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -70,7 +70,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// <remarks>Set in AnalyzeCurrentProperty()</remarks>
IField currentField;
/// <summary>The disposing field of the compiler-generated enumerator class./summary>
/// <summary>The disposing field of the compiler-generated enumerator class.</summary>
/// <remarks>Set in ConstructExceptionTable() for assembly compiled with Mono</remarks>
IField disposingField;
@ -142,7 +142,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -142,7 +142,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
function.Body = newBody;
// register any locals used in newBody
function.Variables.AddRange(newBody.Descendants.OfType<IInstructionWithVariableOperand>().Select(inst => inst.Variable).Distinct());
function.CheckInvariant(ILPhase.Normal);
PrintFinallyMethodStateRanges(newBody);
@ -164,6 +163,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -164,6 +163,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// Note: because this only deletes blocks outright, the 'stateChanges' entries remain valid
// (though some may point to now-deleted blocks)
newBody.SortBlocks(deleteUnreachableBlocks: true);
function.CheckInvariant(ILPhase.Normal);
if (!isCompiledWithMono) {
DecompileFinallyBlocks();
@ -338,7 +338,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -338,7 +338,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
public static bool IsCompilerGeneratorEnumerator(TypeDefinitionHandle type, MetadataReader metadata)
{
TypeDefinition td;
if (type.IsNil || !type.IsCompilerGenerated(metadata) || (td = metadata.GetTypeDefinition(type)).GetDeclaringType().IsNil)
if (type.IsNil || !type.IsCompilerGeneratedOrIsInCompilerGeneratedClass(metadata) || (td = metadata.GetTypeDefinition(type)).GetDeclaringType().IsNil)
return false;
foreach (var i in td.GetInterfaceImplementations()) {
var tr = metadata.GetInterfaceImplementation(i).Interface.GetFullTypeName(metadata);
@ -390,7 +390,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -390,7 +390,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
methodTypeParameters: null);
var body = context.TypeSystem.MainModule.PEFile.Reader.GetMethodBody(methodDef.RelativeVirtualAddress);
var il = context.CreateILReader()
.ReadIL(method, body, genericContext, context.CancellationToken);
.ReadIL(method, body, genericContext, ILFunctionKind.TopLevelFunction, context.CancellationToken);
il.RunTransforms(CSharpDecompiler.EarlyILTransforms(true),
new ILTransformContext(il, context.TypeSystem, context.DebugInfo, context.Settings) {
CancellationToken = context.CancellationToken,
@ -810,14 +810,15 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -810,14 +810,15 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
break;
case Leave leave:
if (leave.MatchReturn(out var value)) {
bool validYieldBreak = value.MatchLdcI4(0);
if (value.MatchLdLoc(out var v)
&& (v.Kind == VariableKind.Local || v.Kind == VariableKind.StackSlot)
&& v.StoreInstructions.Count == 1
&& v.StoreInstructions[0] is StLoc stloc) {
returnStores.Add(stloc);
value = stloc.Value;
&& v.StoreInstructions.All(store => store is StLoc stloc && stloc.Value.MatchLdcI4(0)))
{
validYieldBreak = true;
returnStores.AddRange(v.StoreInstructions.Cast<StLoc>());
}
if (value.MatchLdcI4(0)) {
if (validYieldBreak) {
// yield break
leave.ReplaceWith(new Leave(newBody).WithILRange(leave));
} else {

9
ICSharpCode.Decompiler/IL/ILInstructionExtensions.cs

@ -18,5 +18,14 @@ namespace ICSharpCode.Decompiler.IL @@ -18,5 +18,14 @@ namespace ICSharpCode.Decompiler.IL
target.AddILRange(range);
return target;
}
public static ILInstruction GetNextSibling(this ILInstruction instruction)
{
if (instruction?.Parent == null)
return null;
if (instruction.ChildIndex + 1 >= instruction.Parent.Children.Count)
return null;
return instruction.Parent.Children[instruction.ChildIndex + 1];
}
}
}

33
ICSharpCode.Decompiler/IL/ILReader.cs

@ -480,21 +480,31 @@ namespace ICSharpCode.Decompiler.IL @@ -480,21 +480,31 @@ namespace ICSharpCode.Decompiler.IL
/// <summary>
/// Decodes the specified method body and returns an ILFunction.
/// </summary>
public ILFunction ReadIL(MethodDefinitionHandle method, MethodBodyBlock body, GenericContext genericContext = default, CancellationToken cancellationToken = default)
public ILFunction ReadIL(MethodDefinitionHandle method, MethodBodyBlock body, GenericContext genericContext = default, ILFunctionKind kind = ILFunctionKind.TopLevelFunction, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
Init(method, body, genericContext);
ReadInstructions(cancellationToken);
var blockBuilder = new BlockBuilder(body, variableByExceptionHandler);
blockBuilder.CreateBlocks(mainContainer, instructionBuilder, isBranchTarget, cancellationToken);
var function = new ILFunction(this.method, body.GetCodeSize(), this.genericContext, mainContainer);
var function = new ILFunction(this.method, body.GetCodeSize(), this.genericContext, mainContainer, kind);
CollectionExtensions.AddRange(function.Variables, parameterVariables);
CollectionExtensions.AddRange(function.Variables, localVariables);
CollectionExtensions.AddRange(function.Variables, stackVariables);
CollectionExtensions.AddRange(function.Variables, variableByExceptionHandler.Values);
function.AddRef(); // mark the root node
var removedBlocks = new List<Block>();
foreach (var c in function.Descendants.OfType<BlockContainer>()) {
c.SortBlocks();
var newOrder = c.TopologicalSort(deleteUnreachableBlocks: true);
if (newOrder.Count < c.Blocks.Count) {
removedBlocks.AddRange(c.Blocks.Except(newOrder));
}
c.Blocks.ReplaceList(newOrder);
}
if (removedBlocks.Count > 0) {
removedBlocks.SortBy(b => b.StartILOffset);
function.Warnings.Add("Discarded unreachable code: "
+ string.Join(", ", removedBlocks.Select(b => $"IL_{b.StartILOffset:x4}")));
}
function.Warnings.AddRange(Warnings);
return function;
@ -1187,14 +1197,14 @@ namespace ICSharpCode.Decompiler.IL @@ -1187,14 +1197,14 @@ namespace ICSharpCode.Decompiler.IL
return Pop(StackType.O);
case false:
// field of value type: ldfld can handle temporaries
if (PeekStackType() == StackType.O)
if (PeekStackType() == StackType.O || PeekStackType() == StackType.Unknown)
return new AddressOf(Pop());
else
return PopPointer();
default:
// field in unresolved type
if (PeekStackType() == StackType.O)
return Pop(StackType.O);
if (PeekStackType() == StackType.O || PeekStackType() == StackType.Unknown)
return Pop();
else
return PopPointer();
}
@ -1401,12 +1411,17 @@ namespace ICSharpCode.Decompiler.IL @@ -1401,12 +1411,17 @@ namespace ICSharpCode.Decompiler.IL
var signatureHandle = (StandaloneSignatureHandle)ReadAndDecodeMetadataToken();
var signature = module.DecodeMethodSignature(signatureHandle, genericContext);
var functionPointer = Pop(StackType.I);
Debug.Assert(!signature.Header.IsInstance);
var arguments = new ILInstruction[signature.ParameterTypes.Length];
int firstArgument = signature.Header.IsInstance ? 1 : 0;
var arguments = new ILInstruction[firstArgument + signature.ParameterTypes.Length];
for (int i = signature.ParameterTypes.Length - 1; i >= 0; i--) {
arguments[i] = Pop(signature.ParameterTypes[i].GetStackType());
arguments[firstArgument + i] = Pop(signature.ParameterTypes[i].GetStackType());
}
if (firstArgument == 1) {
arguments[0] = Pop();
}
var call = new CallIndirect(
signature.Header.IsInstance,
signature.Header.HasExplicitThis,
signature.Header.CallingConvention,
signature.ReturnType,
signature.ParameterTypes,

18
ICSharpCode.Decompiler/IL/ILVariable.cs

@ -74,6 +74,11 @@ namespace ICSharpCode.Decompiler.IL @@ -74,6 +74,11 @@ namespace ICSharpCode.Decompiler.IL
static class VariableKindExtensions
{
public static bool IsThis(this ILVariable v)
{
return v.Kind == VariableKind.Parameter && v.Index < 0;
}
public static bool IsLocal(this VariableKind kind)
{
switch (kind) {
@ -102,8 +107,12 @@ namespace ICSharpCode.Decompiler.IL @@ -102,8 +107,12 @@ namespace ICSharpCode.Decompiler.IL
internal set {
if (kind == VariableKind.Parameter)
throw new InvalidOperationException("Kind=Parameter cannot be changed!");
if (Index != null && value.IsLocal())
Debug.Assert(kind.IsLocal());
if (Index != null && value.IsLocal() && !kind.IsLocal()) {
// For variables, Index has different meaning than for stack slots,
// so we need to reset it to null.
// StackSlot -> ForeachLocal can happen sometimes (e.g. PST.TransformForeachOnArray)
Index = null;
}
kind = value;
}
}
@ -133,7 +142,7 @@ namespace ICSharpCode.Decompiler.IL @@ -133,7 +142,7 @@ namespace ICSharpCode.Decompiler.IL
/// For ExceptionStackSlot, the index is the IL offset of the exception handler.
/// For other kinds, the index has no meaning, and is usually null.
/// </summary>
public readonly int? Index;
public int? Index { get; private set; }
[Conditional("DEBUG")]
internal void CheckInvariant()
@ -410,7 +419,8 @@ namespace ICSharpCode.Decompiler.IL @@ -410,7 +419,8 @@ namespace ICSharpCode.Decompiler.IL
output.Write(" init");
}
if (CaptureScope != null) {
output.Write(" captured in " + CaptureScope.EntryPoint.Label);
output.Write(" captured in ");
output.WriteLocalReference(CaptureScope.EntryPoint.Label, CaptureScope);
}
if (StateMachineField != null) {
output.Write(" from state-machine");

149
ICSharpCode.Decompiler/IL/InstructionOutputExtensions.cs

@ -133,14 +133,7 @@ namespace ICSharpCode.Decompiler.IL @@ -133,14 +133,7 @@ namespace ICSharpCode.Decompiler.IL
case HandleKind.MethodDefinition: {
var md = metadata.GetMethodDefinition((MethodDefinitionHandle)entity);
methodSignature = md.DecodeSignature(new DisassemblerSignatureProvider(module, output), new Metadata.GenericContext((MethodDefinitionHandle)entity, module));
if (methodSignature.Header.HasExplicitThis) {
output.Write("instance explicit ");
} else if (methodSignature.Header.IsInstance) {
output.Write("instance ");
}
if (methodSignature.Header.CallingConvention == SignatureCallingConvention.VarArgs) {
output.Write("vararg ");
}
WriteSignatureHeader(output, methodSignature);
methodSignature.ReturnType(ILNameSyntax.SignatureNoNamedTypeParameters);
output.Write(' ');
var declaringType = md.GetDeclaringType();
@ -189,13 +182,7 @@ namespace ICSharpCode.Decompiler.IL @@ -189,13 +182,7 @@ namespace ICSharpCode.Decompiler.IL
}
output.Write('>');
}
output.Write("(");
for (int i = 0; i < methodSignature.ParameterTypes.Length; ++i) {
if (i > 0)
output.Write(", ");
methodSignature.ParameterTypes[i](ILNameSyntax.SignatureNoNamedTypeParameters);
}
output.Write(")");
WriteParameterList(output, methodSignature);
break;
}
case HandleKind.MemberReference:
@ -204,28 +191,13 @@ namespace ICSharpCode.Decompiler.IL @@ -204,28 +191,13 @@ namespace ICSharpCode.Decompiler.IL
switch (mr.GetKind()) {
case MemberReferenceKind.Method:
methodSignature = mr.DecodeMethodSignature(new DisassemblerSignatureProvider(module, output), genericContext);
if (methodSignature.Header.HasExplicitThis) {
output.Write("instance explicit ");
} else if (methodSignature.Header.IsInstance) {
output.Write("instance ");
}
if (methodSignature.Header.CallingConvention == SignatureCallingConvention.VarArgs) {
output.Write("vararg ");
}
WriteSignatureHeader(output, methodSignature);
methodSignature.ReturnType(ILNameSyntax.SignatureNoNamedTypeParameters);
output.Write(' ');
WriteParent(output, module, metadata, mr.Parent, genericContext, syntax);
output.Write("::");
output.WriteReference(module, entity, DisassemblerHelpers.Escape(memberName));
output.Write("(");
for (int i = 0; i < methodSignature.ParameterTypes.Length; ++i) {
if (i > 0)
output.Write(", ");
if (i == methodSignature.RequiredParameterCount)
output.Write("..., ");
methodSignature.ParameterTypes[i](ILNameSyntax.SignatureNoNamedTypeParameters);
}
output.Write(")");
WriteParameterList(output, methodSignature);
break;
case MemberReferenceKind.Field:
var fieldSignature = mr.DecodeFieldSignature(new DisassemblerSignatureProvider(module, output), genericContext);
@ -245,14 +217,7 @@ namespace ICSharpCode.Decompiler.IL @@ -245,14 +217,7 @@ namespace ICSharpCode.Decompiler.IL
var methodDefinition = metadata.GetMethodDefinition((MethodDefinitionHandle)ms.Method);
var methodName = metadata.GetString(methodDefinition.Name);
methodSignature = methodDefinition.DecodeSignature(new DisassemblerSignatureProvider(module, output), genericContext);
if (methodSignature.Header.HasExplicitThis) {
output.Write("instance explicit ");
} else if (methodSignature.Header.IsInstance) {
output.Write("instance ");
}
if (methodSignature.Header.CallingConvention == SignatureCallingConvention.VarArgs) {
output.Write("vararg ");
}
WriteSignatureHeader(output, methodSignature);
methodSignature.ReturnType(ILNameSyntax.SignatureNoNamedTypeParameters);
output.Write(' ');
var declaringType = methodDefinition.GetDeclaringType();
@ -266,52 +231,21 @@ namespace ICSharpCode.Decompiler.IL @@ -266,52 +231,21 @@ namespace ICSharpCode.Decompiler.IL
} else {
output.Write(DisassemblerHelpers.Escape(methodName));
}
output.Write('<');
for (int i = 0; i < substitution.Length; i++) {
if (i > 0)
output.Write(", ");
substitution[i](syntax);
}
output.Write('>');
output.Write("(");
for (int i = 0; i < methodSignature.ParameterTypes.Length; ++i) {
if (i > 0)
output.Write(", ");
methodSignature.ParameterTypes[i](ILNameSyntax.SignatureNoNamedTypeParameters);
}
output.Write(")");
WriteTypeParameterList(output, syntax, substitution);
WriteParameterList(output, methodSignature);
break;
case HandleKind.MemberReference:
var memberReference = metadata.GetMemberReference((MemberReferenceHandle)ms.Method);
memberName = metadata.GetString(memberReference.Name);
methodSignature = memberReference.DecodeMethodSignature(new DisassemblerSignatureProvider(module, output), genericContext);
if (methodSignature.Header.HasExplicitThis) {
output.Write("instance explicit ");
} else if (methodSignature.Header.IsInstance) {
output.Write("instance ");
}
if (methodSignature.Header.CallingConvention == SignatureCallingConvention.VarArgs) {
output.Write("vararg ");
}
WriteSignatureHeader(output, methodSignature);
methodSignature.ReturnType(ILNameSyntax.SignatureNoNamedTypeParameters);
output.Write(' ');
WriteParent(output, module, metadata, memberReference.Parent, genericContext, syntax);
output.Write("::");
output.Write(DisassemblerHelpers.Escape(memberName));
output.Write('<');
for (int i = 0; i < substitution.Length; i++) {
if (i > 0)
output.Write(", ");
substitution[i](syntax);
}
output.Write('>');
output.Write("(");
for (int i = 0; i < methodSignature.ParameterTypes.Length; ++i) {
if (i > 0)
output.Write(", ");
methodSignature.ParameterTypes[i](ILNameSyntax.SignatureNoNamedTypeParameters);
}
output.Write(")");
WriteTypeParameterList(output, syntax, substitution);
WriteParameterList(output, methodSignature);
break;
}
break;
@ -319,15 +253,10 @@ namespace ICSharpCode.Decompiler.IL @@ -319,15 +253,10 @@ namespace ICSharpCode.Decompiler.IL
var standaloneSig = metadata.GetStandaloneSignature((StandaloneSignatureHandle)entity);
switch (standaloneSig.GetKind()) {
case StandaloneSignatureKind.Method:
var methodSig = standaloneSig.DecodeMethodSignature(new DisassemblerSignatureProvider(module, output), genericContext);
methodSig.ReturnType(ILNameSyntax.SignatureNoNamedTypeParameters);
output.Write('(');
for (int i = 0; i < methodSig.ParameterTypes.Length; i++) {
if (i > 0)
output.Write(", ");
methodSig.ParameterTypes[i](ILNameSyntax.SignatureNoNamedTypeParameters);
}
output.Write(')');
methodSignature = standaloneSig.DecodeMethodSignature(new DisassemblerSignatureProvider(module, output), genericContext);
WriteSignatureHeader(output, methodSignature);
methodSignature.ReturnType(ILNameSyntax.SignatureNoNamedTypeParameters);
WriteParameterList(output, methodSignature);
break;
case StandaloneSignatureKind.LocalVariables:
default:
@ -341,6 +270,56 @@ namespace ICSharpCode.Decompiler.IL @@ -341,6 +270,56 @@ namespace ICSharpCode.Decompiler.IL
}
}
static void WriteTypeParameterList(ITextOutput output, ILNameSyntax syntax, System.Collections.Immutable.ImmutableArray<Action<ILNameSyntax>> substitution)
{
output.Write('<');
for (int i = 0; i < substitution.Length; i++) {
if (i > 0)
output.Write(", ");
substitution[i](syntax);
}
output.Write('>');
}
static void WriteParameterList(ITextOutput output, MethodSignature<Action<ILNameSyntax>> methodSignature)
{
output.Write("(");
for (int i = 0; i < methodSignature.ParameterTypes.Length; ++i) {
if (i > 0)
output.Write(", ");
if (i == methodSignature.RequiredParameterCount)
output.Write("..., ");
methodSignature.ParameterTypes[i](ILNameSyntax.SignatureNoNamedTypeParameters);
}
output.Write(")");
}
static void WriteSignatureHeader(ITextOutput output, MethodSignature<Action<ILNameSyntax>> methodSignature)
{
if (methodSignature.Header.HasExplicitThis) {
output.Write("instance explicit ");
} else if (methodSignature.Header.IsInstance) {
output.Write("instance ");
}
switch (methodSignature.Header.CallingConvention) {
case SignatureCallingConvention.CDecl:
output.Write("unmanaged cdecl ");
break;
case SignatureCallingConvention.StdCall:
output.Write("unmanaged stdcall ");
break;
case SignatureCallingConvention.ThisCall:
output.Write("unmanaged thiscall ");
break;
case SignatureCallingConvention.FastCall:
output.Write("unmanaged fastcall ");
break;
case SignatureCallingConvention.VarArgs:
output.Write("vararg ");
break;
}
}
static void WriteParent(ITextOutput output, PEFile module, MetadataReader metadata, EntityHandle parentHandle, Metadata.GenericContext genericContext, ILNameSyntax syntax)
{
switch (parentHandle.Kind) {

61
ICSharpCode.Decompiler/IL/Instructions.cs

@ -115,7 +115,7 @@ namespace ICSharpCode.Decompiler.IL @@ -115,7 +115,7 @@ namespace ICSharpCode.Decompiler.IL
/// In case 3 (managed reference), the dereferenced value is the input being tested, and the nullable.unwrap instruction returns the managed reference unmodified (if the value is non-null).</summary>
NullableUnwrap,
/// <summary>Serves as jump target for the nullable.unwrap instruction.
/// If the input evaluates normally, evaluates to the input value (wrapped in Nullable<T> if the input is a non-nullable value type).If a nullable.unwrap instruction encounters a null input and jumps to the (endpoint of the) nullable.rewrap instruction,the nullable.rewrap instruction evaluates to null.</summary>
/// If the input evaluates normally, evaluates to the input value (wrapped in Nullable&lt;T&gt; if the input is a non-nullable value type).If a nullable.unwrap instruction encounters a null input and jumps to the (endpoint of the) nullable.rewrap instruction,the nullable.rewrap instruction evaluates to null.</summary>
NullableRewrap,
/// <summary>Loads a constant string.</summary>
LdStr,
@ -189,7 +189,7 @@ namespace ICSharpCode.Decompiler.IL @@ -189,7 +189,7 @@ namespace ICSharpCode.Decompiler.IL
StringToInt,
/// <summary>ILAst representation of Expression.Convert.</summary>
ExpressionTreeCast,
/// <summary>Use of user-defined && or || operator.</summary>
/// <summary>Use of user-defined &amp;&amp; or || operator.</summary>
UserDefinedLogicOperator,
/// <summary>ILAst representation of a short-circuiting binary operator inside a dynamic expression.</summary>
DynamicLogicOperatorInstruction,
@ -476,7 +476,7 @@ namespace ICSharpCode.Decompiler.IL @@ -476,7 +476,7 @@ namespace ICSharpCode.Decompiler.IL
{
switch (index) {
default:
this.Arguments[index - 0] = value;
this.Arguments[index - 0] = (ILInstruction)value;
break;
}
}
@ -491,7 +491,7 @@ namespace ICSharpCode.Decompiler.IL @@ -491,7 +491,7 @@ namespace ICSharpCode.Decompiler.IL
{
var clone = (CallInstruction)ShallowClone();
clone.Arguments = new InstructionCollection<ILInstruction>(clone, 0);
clone.Arguments.AddRange(this.Arguments.Select(arg => arg.Clone()));
clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone()));
return clone;
}
protected override InstructionFlags ComputeFlags()
@ -742,9 +742,11 @@ namespace ICSharpCode.Decompiler.IL @@ -742,9 +742,11 @@ namespace ICSharpCode.Decompiler.IL
SetChildInstruction(ref this.body, value, 0);
}
}
public static readonly SlotInfo LocalFunctionsSlot = new SlotInfo("LocalFunctions");
public InstructionCollection<ILFunction> LocalFunctions { get; private set; }
protected sealed override int GetChildCount()
{
return 1;
return 1 + LocalFunctions.Count;
}
protected sealed override ILInstruction GetChild(int index)
{
@ -752,7 +754,7 @@ namespace ICSharpCode.Decompiler.IL @@ -752,7 +754,7 @@ namespace ICSharpCode.Decompiler.IL
case 0:
return this.body;
default:
throw new IndexOutOfRangeException();
return this.LocalFunctions[index - 1];
}
}
protected sealed override void SetChild(int index, ILInstruction value)
@ -762,7 +764,8 @@ namespace ICSharpCode.Decompiler.IL @@ -762,7 +764,8 @@ namespace ICSharpCode.Decompiler.IL
this.Body = value;
break;
default:
throw new IndexOutOfRangeException();
this.LocalFunctions[index - 1] = (ILFunction)value;
break;
}
}
protected sealed override SlotInfo GetChildSlot(int index)
@ -771,13 +774,15 @@ namespace ICSharpCode.Decompiler.IL @@ -771,13 +774,15 @@ namespace ICSharpCode.Decompiler.IL
case 0:
return BodySlot;
default:
throw new IndexOutOfRangeException();
return LocalFunctionsSlot;
}
}
public sealed override ILInstruction Clone()
{
var clone = (ILFunction)ShallowClone();
clone.Body = this.body.Clone();
clone.LocalFunctions = new InstructionCollection<ILFunction>(clone, 1);
clone.LocalFunctions.AddRange(this.LocalFunctions.Select(arg => (ILFunction)arg.Clone()));
clone.CloneVariables();
return clone;
}
@ -797,7 +802,7 @@ namespace ICSharpCode.Decompiler.IL @@ -797,7 +802,7 @@ namespace ICSharpCode.Decompiler.IL
protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match)
{
var o = other as ILFunction;
return o != null && this.body.PerformMatch(o.body, ref match);
return o != null && this.body.PerformMatch(o.body, ref match) && Patterns.ListMatch.DoMatch(this.LocalFunctions, o.LocalFunctions, ref match);
}
}
}
@ -1058,7 +1063,7 @@ namespace ICSharpCode.Decompiler.IL @@ -1058,7 +1063,7 @@ namespace ICSharpCode.Decompiler.IL
protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match)
{
var o = other as NumericCompoundAssign;
return o != null && type.Equals(o.type) && CheckForOverflow == o.CheckForOverflow && Sign == o.Sign && Operator == o.Operator && Target.PerformMatch(o.Target, ref match) && Value.PerformMatch(o.Value, ref match);
return o != null && type.Equals(o.type) && CheckForOverflow == o.CheckForOverflow && Sign == o.Sign && Operator == o.Operator && this.EvalMode == o.EvalMode && this.TargetKind == o.TargetKind && Target.PerformMatch(o.Target, ref match) && Value.PerformMatch(o.Value, ref match);
}
}
}
@ -1092,7 +1097,7 @@ namespace ICSharpCode.Decompiler.IL @@ -1092,7 +1097,7 @@ namespace ICSharpCode.Decompiler.IL
protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match)
{
var o = other as UserDefinedCompoundAssign;
return o != null && this.Method.Equals(o.Method) && this.CompoundAssignmentType == o.CompoundAssignmentType && Target.PerformMatch(o.Target, ref match) && Value.PerformMatch(o.Value, ref match);
return o != null && this.Method.Equals(o.Method) && this.EvalMode == o.EvalMode && this.TargetKind == o.TargetKind && Target.PerformMatch(o.Target, ref match) && Value.PerformMatch(o.Value, ref match);
}
}
}
@ -1126,7 +1131,7 @@ namespace ICSharpCode.Decompiler.IL @@ -1126,7 +1131,7 @@ namespace ICSharpCode.Decompiler.IL
protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match)
{
var o = other as DynamicCompoundAssign;
return o != null && this.CompoundAssignmentType == o.CompoundAssignmentType && Target.PerformMatch(o.Target, ref match) && Value.PerformMatch(o.Value, ref match);
return o != null && this.EvalMode == o.EvalMode && this.TargetKind == o.TargetKind && Target.PerformMatch(o.Target, ref match) && Value.PerformMatch(o.Value, ref match);
}
}
}
@ -2709,7 +2714,7 @@ namespace ICSharpCode.Decompiler.IL @@ -2709,7 +2714,7 @@ namespace ICSharpCode.Decompiler.IL
namespace ICSharpCode.Decompiler.IL
{
/// <summary>Serves as jump target for the nullable.unwrap instruction.
/// If the input evaluates normally, evaluates to the input value (wrapped in Nullable<T> if the input is a non-nullable value type).If a nullable.unwrap instruction encounters a null input and jumps to the (endpoint of the) nullable.rewrap instruction,the nullable.rewrap instruction evaluates to null.</summary>
/// If the input evaluates normally, evaluates to the input value (wrapped in Nullable&lt;T&gt; if the input is a non-nullable value type).If a nullable.unwrap instruction encounters a null input and jumps to the (endpoint of the) nullable.rewrap instruction,the nullable.rewrap instruction evaluates to null.</summary>
public sealed partial class NullableRewrap : UnaryInstruction
{
public NullableRewrap(ILInstruction argument) : base(OpCode.NullableRewrap, argument)
@ -4254,7 +4259,7 @@ namespace ICSharpCode.Decompiler.IL @@ -4254,7 +4259,7 @@ namespace ICSharpCode.Decompiler.IL
{
switch (index) {
default:
this.Indices[index - 0] = value;
this.Indices[index - 0] = (ILInstruction)value;
break;
}
}
@ -4269,7 +4274,7 @@ namespace ICSharpCode.Decompiler.IL @@ -4269,7 +4274,7 @@ namespace ICSharpCode.Decompiler.IL
{
var clone = (NewArr)ShallowClone();
clone.Indices = new InstructionCollection<ILInstruction>(clone, 0);
clone.Indices.AddRange(this.Indices.Select(arg => arg.Clone()));
clone.Indices.AddRange(this.Indices.Select(arg => (ILInstruction)arg.Clone()));
return clone;
}
public override StackType ResultType { get { return StackType.O; } }
@ -4607,7 +4612,7 @@ namespace ICSharpCode.Decompiler.IL @@ -4607,7 +4612,7 @@ namespace ICSharpCode.Decompiler.IL
this.Array = value;
break;
default:
this.Indices[index - 1] = value;
this.Indices[index - 1] = (ILInstruction)value;
break;
}
}
@ -4625,7 +4630,7 @@ namespace ICSharpCode.Decompiler.IL @@ -4625,7 +4630,7 @@ namespace ICSharpCode.Decompiler.IL
var clone = (LdElema)ShallowClone();
clone.Array = this.array.Clone();
clone.Indices = new InstructionCollection<ILInstruction>(clone, 1);
clone.Indices.AddRange(this.Indices.Select(arg => arg.Clone()));
clone.Indices.AddRange(this.Indices.Select(arg => (ILInstruction)arg.Clone()));
return clone;
}
public bool DelayExceptions; // NullReferenceException/IndexOutOfBoundsException only occurs when the reference is dereferenced
@ -4905,7 +4910,7 @@ namespace ICSharpCode.Decompiler.IL @@ -4905,7 +4910,7 @@ namespace ICSharpCode.Decompiler.IL
}
namespace ICSharpCode.Decompiler.IL
{
/// <summary>Use of user-defined && or || operator.</summary>
/// <summary>Use of user-defined &amp;&amp; or || operator.</summary>
public sealed partial class UserDefinedLogicOperator : ILInstruction, IInstructionWithMethodOperand
{
public UserDefinedLogicOperator(IMethod method, ILInstruction left, ILInstruction right) : base(OpCode.UserDefinedLogicOperator)
@ -5578,7 +5583,7 @@ namespace ICSharpCode.Decompiler.IL @@ -5578,7 +5583,7 @@ namespace ICSharpCode.Decompiler.IL
{
switch (index) {
default:
this.Arguments[index - 0] = value;
this.Arguments[index - 0] = (ILInstruction)value;
break;
}
}
@ -5593,7 +5598,7 @@ namespace ICSharpCode.Decompiler.IL @@ -5593,7 +5598,7 @@ namespace ICSharpCode.Decompiler.IL
{
var clone = (DynamicGetIndexInstruction)ShallowClone();
clone.Arguments = new InstructionCollection<ILInstruction>(clone, 0);
clone.Arguments.AddRange(this.Arguments.Select(arg => arg.Clone()));
clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone()));
return clone;
}
protected override InstructionFlags ComputeFlags()
@ -5646,7 +5651,7 @@ namespace ICSharpCode.Decompiler.IL @@ -5646,7 +5651,7 @@ namespace ICSharpCode.Decompiler.IL
{
switch (index) {
default:
this.Arguments[index - 0] = value;
this.Arguments[index - 0] = (ILInstruction)value;
break;
}
}
@ -5661,7 +5666,7 @@ namespace ICSharpCode.Decompiler.IL @@ -5661,7 +5666,7 @@ namespace ICSharpCode.Decompiler.IL
{
var clone = (DynamicSetIndexInstruction)ShallowClone();
clone.Arguments = new InstructionCollection<ILInstruction>(clone, 0);
clone.Arguments.AddRange(this.Arguments.Select(arg => arg.Clone()));
clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone()));
return clone;
}
protected override InstructionFlags ComputeFlags()
@ -5714,7 +5719,7 @@ namespace ICSharpCode.Decompiler.IL @@ -5714,7 +5719,7 @@ namespace ICSharpCode.Decompiler.IL
{
switch (index) {
default:
this.Arguments[index - 0] = value;
this.Arguments[index - 0] = (ILInstruction)value;
break;
}
}
@ -5729,7 +5734,7 @@ namespace ICSharpCode.Decompiler.IL @@ -5729,7 +5734,7 @@ namespace ICSharpCode.Decompiler.IL
{
var clone = (DynamicInvokeMemberInstruction)ShallowClone();
clone.Arguments = new InstructionCollection<ILInstruction>(clone, 0);
clone.Arguments.AddRange(this.Arguments.Select(arg => arg.Clone()));
clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone()));
return clone;
}
protected override InstructionFlags ComputeFlags()
@ -5782,7 +5787,7 @@ namespace ICSharpCode.Decompiler.IL @@ -5782,7 +5787,7 @@ namespace ICSharpCode.Decompiler.IL
{
switch (index) {
default:
this.Arguments[index - 0] = value;
this.Arguments[index - 0] = (ILInstruction)value;
break;
}
}
@ -5797,7 +5802,7 @@ namespace ICSharpCode.Decompiler.IL @@ -5797,7 +5802,7 @@ namespace ICSharpCode.Decompiler.IL
{
var clone = (DynamicInvokeConstructorInstruction)ShallowClone();
clone.Arguments = new InstructionCollection<ILInstruction>(clone, 0);
clone.Arguments.AddRange(this.Arguments.Select(arg => arg.Clone()));
clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone()));
return clone;
}
protected override InstructionFlags ComputeFlags()
@ -5850,7 +5855,7 @@ namespace ICSharpCode.Decompiler.IL @@ -5850,7 +5855,7 @@ namespace ICSharpCode.Decompiler.IL
{
switch (index) {
default:
this.Arguments[index - 0] = value;
this.Arguments[index - 0] = (ILInstruction)value;
break;
}
}
@ -5865,7 +5870,7 @@ namespace ICSharpCode.Decompiler.IL @@ -5865,7 +5870,7 @@ namespace ICSharpCode.Decompiler.IL
{
var clone = (DynamicInvokeInstruction)ShallowClone();
clone.Arguments = new InstructionCollection<ILInstruction>(clone, 0);
clone.Arguments.AddRange(this.Arguments.Select(arg => arg.Clone()));
clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone()));
return clone;
}
protected override InstructionFlags ComputeFlags()

26
ICSharpCode.Decompiler/IL/Instructions.tt

@ -49,7 +49,8 @@ @@ -49,7 +49,8 @@
VoidResult, NoArguments, CustomWriteTo),
new OpCode("ILFunction", "A container of IL blocks.",
CustomChildren(new [] {
new ChildInfo("body")
new ChildInfo("body"),
new ChildInfo("localFunctions") { IsCollection = true, Type = "ILFunction" }
}), CustomConstructor, CustomWriteTo, CustomComputeFlags, CustomVariableName("function"), ResultType("O")
),
new OpCode("BlockContainer", "A container of IL blocks.",
@ -74,19 +75,23 @@ @@ -74,19 +75,23 @@
CustomClassName("NumericCompoundAssign"), BaseClass("CompoundAssignmentInstruction"), CustomConstructor, CustomComputeFlags,
MayThrow, HasTypeOperand, ResultType("type.GetStackType()"), CustomWriteTo,
MatchCondition("CheckForOverflow == o.CheckForOverflow && Sign == o.Sign && Operator == o.Operator"),
MatchCondition("this.EvalMode == o.EvalMode"),
MatchCondition("this.TargetKind == o.TargetKind"),
MatchCondition("Target.PerformMatch(o.Target, ref match)"),
MatchCondition("Value.PerformMatch(o.Value, ref match)")),
new OpCode("user.compound", "Common instruction for user-defined compound assignments.",
CustomClassName("UserDefinedCompoundAssign"), BaseClass("CompoundAssignmentInstruction"), CustomConstructor,
MayThrow, SideEffect, CustomWriteTo,
MatchCondition("this.Method.Equals(o.Method)"),
MatchCondition("this.CompoundAssignmentType == o.CompoundAssignmentType"),
MatchCondition("this.EvalMode == o.EvalMode"),
MatchCondition("this.TargetKind == o.TargetKind"),
MatchCondition("Target.PerformMatch(o.Target, ref match)"),
MatchCondition("Value.PerformMatch(o.Value, ref match)")),
new OpCode("dynamic.compound", "Common instruction for dynamic compound assignments.",
CustomClassName("DynamicCompoundAssign"), BaseClass("CompoundAssignmentInstruction"),
MayThrow, SideEffect, CustomWriteTo, CustomConstructor, ResultType("O"),
MatchCondition("this.CompoundAssignmentType == o.CompoundAssignmentType"),
MatchCondition("this.EvalMode == o.EvalMode"),
MatchCondition("this.TargetKind == o.TargetKind"),
MatchCondition("Target.PerformMatch(o.Target, ref match)"),
MatchCondition("Value.PerformMatch(o.Value, ref match)")),
new OpCode("bit.not", "Bitwise NOT", Unary, CustomConstructor, MatchCondition("IsLifted == o.IsLifted && UnderlyingResultType == o.UnderlyingResultType")),
@ -189,7 +194,7 @@ @@ -189,7 +194,7 @@
+ "returns the managed reference unmodified (if the value is non-null).",
Unary, CustomConstructor, CustomWriteTo, HasFlag("InstructionFlags.MayUnwrapNull")),
new OpCode("nullable.rewrap", "Serves as jump target for the nullable.unwrap instruction." + Environment.NewLine
+ "If the input evaluates normally, evaluates to the input value (wrapped in Nullable<T> if the input is a non-nullable value type)."
+ "If the input evaluates normally, evaluates to the input value (wrapped in Nullable&lt;T&gt; if the input is a non-nullable value type)."
+ "If a nullable.unwrap instruction encounters a null input and jumps to the (endpoint of the) nullable.rewrap instruction,"
+ "the nullable.rewrap instruction evaluates to null.",
Unary, CustomComputeFlags),
@ -281,7 +286,7 @@ @@ -281,7 +286,7 @@
CustomClassName("ExpressionTreeCast"), Unary, HasTypeOperand, MayThrow, CustomConstructor, CustomWriteTo, ResultType("type.GetStackType()"),
MatchCondition("this.IsChecked == o.IsChecked")),
new OpCode("user.logic.operator", "Use of user-defined && or || operator.",
new OpCode("user.logic.operator", "Use of user-defined &amp;&amp; or || operator.",
CustomClassName("UserDefinedLogicOperator"),
HasMethodOperand, ResultType("O"),
CustomChildren(new []{
@ -769,6 +774,7 @@ namespace ICSharpCode.Decompiler.IL @@ -769,6 +774,7 @@ namespace ICSharpCode.Decompiler.IL
public readonly string SlotName;
public bool IsCollection;
public string Type = "ILInstruction";
public bool CanInlineInto;
public string[] ExpectedTypes;
@ -814,7 +820,7 @@ namespace ICSharpCode.Decompiler.IL @@ -814,7 +820,7 @@ namespace ICSharpCode.Decompiler.IL
childCount = children.Length - 1;
opCode.Flags.Add(argProp + ".Aggregate(InstructionFlags.None, (f, arg) => f | arg.Flags)");
opCode.ConstructorParameters.Add("params ILInstruction[] " + arg);
opCode.ConstructorBody.Add("this." + argProp + " = new InstructionCollection<ILInstruction>(this, " + i + ");");
opCode.ConstructorBody.Add("this." + argProp + " = new InstructionCollection<" + children[i].Type + ">(this, " + i + ");");
opCode.ConstructorBody.Add("this." + argProp + ".AddRange(" + arg + ");");
opCode.PerformMatchConditions.Add("Patterns.ListMatch.DoMatch(this." + argProp + ", o." + argProp + ", ref match)");
if (i == 0)
@ -827,7 +833,7 @@ namespace ICSharpCode.Decompiler.IL @@ -827,7 +833,7 @@ namespace ICSharpCode.Decompiler.IL
opCode.WriteArguments.Add("\t" + arg + ".WriteTo(output, options);");
opCode.WriteArguments.Add("}");
opCode.Members.Add("public static readonly SlotInfo " + children[i].SlotName + " = " + children[i].GetSlotInit() + ";");
opCode.Members.Add("public InstructionCollection<ILInstruction> " + argProp + " { get; private set; }");
opCode.Members.Add("public InstructionCollection<" + children[i].Type + "> " + argProp + " { get; private set; }");
} else {
opCode.Flags.Add(arg + ".Flags");
opCode.ConstructorParameters.Add("ILInstruction " + arg);
@ -906,7 +912,7 @@ namespace ICSharpCode.Decompiler.IL @@ -906,7 +912,7 @@ namespace ICSharpCode.Decompiler.IL
if (collection == null)
b.AppendLine("\t\t\tthrow new IndexOutOfRangeException();");
else {
b.AppendLine("\t\t\tthis." + collection.PropertyName + "[index - " + childCount + "] = value;");
b.AppendLine("\t\t\tthis." + collection.PropertyName + "[index - " + childCount + "] = (" + collection.Type + ")value;");
b.AppendLine("\t\t\tbreak;");
}
b.AppendLine("\t}");
@ -936,8 +942,8 @@ namespace ICSharpCode.Decompiler.IL @@ -936,8 +942,8 @@ namespace ICSharpCode.Decompiler.IL
b.AppendLine("\tvar clone = (" + opCode.Name + ")ShallowClone();");
for (int i = 0; i < children.Length; i++) {
if (children[i].IsCollection) {
b.AppendLine("\tclone." + children[i].PropertyName + " = new InstructionCollection<ILInstruction>(clone, " + i + ");");
b.AppendLine("\tclone." + children[i].PropertyName + ".AddRange(this." + children[i].PropertyName + ".Select(arg => arg.Clone()));");
b.AppendLine("\tclone." + children[i].PropertyName + " = new InstructionCollection<" + children[i].Type + ">(clone, " + i + ");");
b.AppendLine("\tclone." + children[i].PropertyName + ".AddRange(this." + children[i].PropertyName + ".Select(arg => (" + children[i].Type + ")arg.Clone()));");
} else {
b.AppendLine("\tclone." + children[i].PropertyName + " = this." + children[i].Name + ".Clone();");
}

53
ICSharpCode.Decompiler/IL/Instructions/Block.cs

@ -43,11 +43,11 @@ namespace ICSharpCode.Decompiler.IL @@ -43,11 +43,11 @@ namespace ICSharpCode.Decompiler.IL
{
public static readonly SlotInfo InstructionSlot = new SlotInfo("Instruction", isCollection: true);
public static readonly SlotInfo FinalInstructionSlot = new SlotInfo("FinalInstruction");
public readonly BlockKind Kind;
public readonly InstructionCollection<ILInstruction> Instructions;
ILInstruction finalInstruction;
/// <summary>
/// For blocks in a block container, this field holds
/// the number of incoming control flow edges to this block.
@ -77,21 +77,21 @@ namespace ICSharpCode.Decompiler.IL @@ -77,21 +77,21 @@ namespace ICSharpCode.Decompiler.IL
SetChildInstruction(ref finalInstruction, value, Instructions.Count);
}
}
protected internal override void InstructionCollectionUpdateComplete()
{
base.InstructionCollectionUpdateComplete();
if (finalInstruction.Parent == this)
finalInstruction.ChildIndex = Instructions.Count;
}
public Block(BlockKind kind = BlockKind.ControlFlow) : base(OpCode.Block)
{
this.Kind = kind;
this.Instructions = new InstructionCollection<ILInstruction>(this, 0);
this.FinalInstruction = new Nop();
}
public override ILInstruction Clone()
{
Block clone = new Block(Kind);
@ -100,7 +100,7 @@ namespace ICSharpCode.Decompiler.IL @@ -100,7 +100,7 @@ namespace ICSharpCode.Decompiler.IL
clone.FinalInstruction = this.FinalInstruction.Clone();
return clone;
}
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
@ -133,18 +133,17 @@ namespace ICSharpCode.Decompiler.IL @@ -133,18 +133,17 @@ namespace ICSharpCode.Decompiler.IL
break;
}
}
public override StackType ResultType {
get {
return finalInstruction.ResultType;
}
}
/// <summary>
/// Gets the name of this block.
/// </summary>
public string Label
{
public string Label {
get { return Disassembler.DisassemblerHelpers.OffsetToString(this.StartILOffset); }
}
@ -179,19 +178,19 @@ namespace ICSharpCode.Decompiler.IL @@ -179,19 +178,19 @@ namespace ICSharpCode.Decompiler.IL
output.Write("}");
output.MarkFoldEnd();
}
protected override int GetChildCount()
{
return Instructions.Count + 1;
}
protected override ILInstruction GetChild(int index)
{
if (index == Instructions.Count)
return finalInstruction;
return Instructions[index];
}
protected override void SetChild(int index, ILInstruction value)
{
if (index == Instructions.Count)
@ -199,7 +198,7 @@ namespace ICSharpCode.Decompiler.IL @@ -199,7 +198,7 @@ namespace ICSharpCode.Decompiler.IL
else
Instructions[index] = value;
}
protected override SlotInfo GetChildSlot(int index)
{
if (index == Instructions.Count)
@ -207,7 +206,7 @@ namespace ICSharpCode.Decompiler.IL @@ -207,7 +206,7 @@ namespace ICSharpCode.Decompiler.IL
else
return InstructionSlot;
}
protected override InstructionFlags ComputeFlags()
{
var flags = InstructionFlags.None;
@ -217,7 +216,7 @@ namespace ICSharpCode.Decompiler.IL @@ -217,7 +216,7 @@ namespace ICSharpCode.Decompiler.IL
flags |= FinalInstruction.Flags;
return flags;
}
public override InstructionFlags DirectFlags {
get {
return InstructionFlags.None;
@ -280,6 +279,21 @@ namespace ICSharpCode.Decompiler.IL @@ -280,6 +279,21 @@ namespace ICSharpCode.Decompiler.IL
return inst;
}
/// <summary>
/// Gets the closest parent Block.
/// Returns null, if the instruction is not a descendant of a Block.
/// </summary>
public static Block FindClosestBlock(ILInstruction inst)
{
var curr = inst;
while (curr != null) {
if (curr is Block)
return (Block)curr;
curr = curr.Parent;
}
return null;
}
public bool MatchInlineAssignBlock(out CallInstruction call, out ILInstruction value)
{
call = null;
@ -315,13 +329,6 @@ namespace ICSharpCode.Decompiler.IL @@ -315,13 +329,6 @@ namespace ICSharpCode.Decompiler.IL
ObjectInitializer,
StackAllocInitializer,
/// <summary>
/// Block is used for postfix operator on local variable.
/// </summary>
/// <remarks>
/// Postfix operators on non-locals use CompoundAssignmentInstruction with CompoundAssignmentType.EvaluatesToOldValue.
/// </remarks>
PostfixOperator,
/// <summary>
/// Block is used for using the result of a property setter inline.
/// Example: <code>Use(this.Property = value);</code>
/// This is only for inline assignments to property or indexers; other inline assignments work

54
ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs

@ -184,6 +184,7 @@ namespace ICSharpCode.Decompiler.IL @@ -184,6 +184,7 @@ namespace ICSharpCode.Decompiler.IL
Debug.Assert(EntryPoint == null || Parent is ILFunction || !HasILRange);
Debug.Assert(Blocks.All(b => b.HasFlag(InstructionFlags.EndPointUnreachable)));
Debug.Assert(Blocks.All(b => b.Kind == BlockKind.ControlFlow)); // this also implies that the blocks don't use FinalInstruction
Debug.Assert(TopologicalSort(deleteUnreachableBlocks: true).Count == Blocks.Count, "Container should not have any unreachable blocks");
Block bodyStartBlock;
switch (Kind) {
case ContainerKind.Normal:
@ -237,45 +238,56 @@ namespace ICSharpCode.Decompiler.IL @@ -237,45 +238,56 @@ namespace ICSharpCode.Decompiler.IL
return InstructionFlags.ControlFlow;
}
}
/// <summary>
/// Sort the blocks in reverse post-order over the control flow graph between the blocks.
/// Topologically sort the blocks.
/// The new order is returned without modifying the BlockContainer.
/// </summary>
public void SortBlocks(bool deleteUnreachableBlocks = false)
/// <param name="deleteUnreachableBlocks">If true, unreachable blocks are not included in the new order.</param>
public List<Block> TopologicalSort(bool deleteUnreachableBlocks = false)
{
if (Blocks.Count < 2)
return;
// Visit blocks in post-order
BitSet visited = new BitSet(Blocks.Count);
List<Block> postOrder = new List<Block>();
Action<Block> visit = null;
visit = delegate(Block block) {
Visit(EntryPoint);
postOrder.Reverse();
if (!deleteUnreachableBlocks) {
for (int i = 0; i < Blocks.Count; i++) {
if (!visited[i])
postOrder.Add(Blocks[i]);
}
}
return postOrder;
void Visit(Block block)
{
Debug.Assert(block.Parent == this);
if (!visited[block.ChildIndex]) {
visited[block.ChildIndex] = true;
foreach (var branch in block.Descendants.OfType<Branch>()) {
if (branch.TargetBlock.Parent == this) {
visit(branch.TargetBlock);
Visit(branch.TargetBlock);
}
}
postOrder.Add(block);
}
};
visit(EntryPoint);
postOrder.Reverse();
if (!deleteUnreachableBlocks) {
for (int i = 0; i < Blocks.Count; i++) {
if (!visited[i])
postOrder.Add(Blocks[i]);
}
}
Debug.Assert(postOrder[0] == Blocks[0]);
Blocks.ReplaceList(postOrder);
}
/// <summary>
/// Topologically sort the blocks.
/// </summary>
/// <param name="deleteUnreachableBlocks">If true, delete unreachable blocks.</param>
public void SortBlocks(bool deleteUnreachableBlocks = false)
{
if (Blocks.Count < 2)
return;
var newOrder = TopologicalSort(deleteUnreachableBlocks);
Debug.Assert(newOrder[0] == Blocks[0]);
Blocks.ReplaceList(newOrder);
}
public static BlockContainer FindClosestContainer(ILInstruction inst)

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

@ -32,7 +32,8 @@ namespace ICSharpCode.Decompiler.IL @@ -32,7 +32,8 @@ namespace ICSharpCode.Decompiler.IL
public readonly InstructionCollection<ILInstruction> Arguments;
ILInstruction functionPointer;
public bool IsInstance { get; }
public bool HasExplicitThis { get; }
public System.Reflection.Metadata.SignatureCallingConvention CallingConvention { get; }
public IType ReturnType { get; }
public ImmutableArray<IType> ParameterTypes { get; }
@ -61,9 +62,11 @@ namespace ICSharpCode.Decompiler.IL @@ -61,9 +62,11 @@ namespace ICSharpCode.Decompiler.IL
functionPointer.ChildIndex = Arguments.Count;
}
public CallIndirect(System.Reflection.Metadata.SignatureCallingConvention callingConvention, IType returnType, ImmutableArray<IType> parameterTypes,
public CallIndirect(bool isInstance, bool hasExplicitThis, System.Reflection.Metadata.SignatureCallingConvention callingConvention, IType returnType, ImmutableArray<IType> parameterTypes,
IEnumerable<ILInstruction> arguments, ILInstruction functionPointer) : base(OpCode.CallIndirect)
{
this.IsInstance = isInstance;
this.HasExplicitThis = hasExplicitThis;
this.CallingConvention = callingConvention;
this.ReturnType = returnType ?? throw new ArgumentNullException("returnType");
this.ParameterTypes = parameterTypes.ToImmutableArray();
@ -74,7 +77,7 @@ namespace ICSharpCode.Decompiler.IL @@ -74,7 +77,7 @@ namespace ICSharpCode.Decompiler.IL
public override ILInstruction Clone()
{
return new CallIndirect(CallingConvention, ReturnType, ParameterTypes,
return new CallIndirect(IsInstance, HasExplicitThis, CallingConvention, ReturnType, ParameterTypes,
this.Arguments.Select(inst => inst.Clone()), functionPointer.Clone()
).WithILRange(this);
}
@ -84,7 +87,7 @@ namespace ICSharpCode.Decompiler.IL @@ -84,7 +87,7 @@ namespace ICSharpCode.Decompiler.IL
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
Debug.Assert(Arguments.Count == ParameterTypes.Length);
Debug.Assert(Arguments.Count == ParameterTypes.Length + (IsInstance ? 1 : 0));
}
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
@ -94,7 +97,12 @@ namespace ICSharpCode.Decompiler.IL @@ -94,7 +97,12 @@ namespace ICSharpCode.Decompiler.IL
ReturnType.WriteTo(output);
output.Write('(');
bool first = true;
foreach (var (inst, type) in Arguments.Zip(ParameterTypes, (a,b) => (a,b))) {
int firstArgument = IsInstance ? 1 : 0;
if (firstArgument == 1) {
Arguments[0].WriteTo(output, options);
first = false;
}
foreach (var (inst, type) in Arguments.Skip(firstArgument).Zip(ParameterTypes, (a,b) => (a,b))) {
if (first)
first = false;
else
@ -155,6 +163,10 @@ namespace ICSharpCode.Decompiler.IL @@ -155,6 +163,10 @@ namespace ICSharpCode.Decompiler.IL
bool EqualSignature(CallIndirect other)
{
if (IsInstance != other.IsInstance)
return false;
if (HasExplicitThis != other.HasExplicitThis)
return false;
if (CallingConvention != other.CallingConvention)
return false;
if (ParameterTypes.Length != other.ParameterTypes.Length)

154
ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs

@ -23,36 +23,101 @@ using ICSharpCode.Decompiler.TypeSystem; @@ -23,36 +23,101 @@ using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL
{
public enum CompoundAssignmentType : byte
public enum CompoundEvalMode : byte
{
/// <summary>
/// The compound.assign instruction will evaluate to the old value.
/// This mode is used only for post-increment/decrement.
/// </summary>
EvaluatesToOldValue,
/// <summary>
/// The compound.assign instruction will evaluate to the new value.
/// This mode is used for compound assignments and pre-increment/decrement.
/// </summary>
EvaluatesToNewValue
}
public enum CompoundTargetKind : byte
{
/// <summary>
/// The target is an instruction computing an address,
/// and the compound.assign will implicitly load/store from/to that address.
/// </summary>
Address,
/// <summary>
/// The Target must be a call to a property getter,
/// and the compound.assign will implicitly call the corresponding property setter.
/// </summary>
Property,
/// <summary>
/// The target is a dynamic call.
/// </summary>
Dynamic
}
public abstract partial class CompoundAssignmentInstruction : ILInstruction
{
public readonly CompoundAssignmentType CompoundAssignmentType;
public readonly CompoundEvalMode EvalMode;
/// <summary>
/// If TargetIsProperty is true, the Target must be a call to a property getter,
/// and the compound.assign will implicitly call the corresponding property setter.
/// Otherwise, the Target can be any instruction that evaluates to an address,
/// and the compound.assign will implicit load and store from/to that address.
/// </summary>
public readonly CompoundTargetKind TargetKind;
public CompoundAssignmentInstruction(OpCode opCode, CompoundAssignmentType compoundAssignmentType, ILInstruction target, ILInstruction value)
public CompoundAssignmentInstruction(OpCode opCode, CompoundEvalMode evalMode, ILInstruction target, CompoundTargetKind targetKind, ILInstruction value)
: base(opCode)
{
this.CompoundAssignmentType = compoundAssignmentType;
this.EvalMode = evalMode;
this.Target = target;
this.TargetKind = targetKind;
this.Value = value;
CheckValidTarget();
}
internal static bool IsValidCompoundAssignmentTarget(ILInstruction inst)
internal override void CheckInvariant(ILPhase phase)
{
switch (inst.OpCode) {
// case OpCode.LdLoc: -- not valid -- does not mark the variable as written to
case OpCode.LdObj:
return true;
case OpCode.Call:
case OpCode.CallVirt:
var owner = ((CallInstruction)inst).Method.AccessorOwner as IProperty;
return owner != null && owner.CanSet;
default:
return false;
base.CheckInvariant(phase);
CheckValidTarget();
}
[Conditional("DEBUG")]
void CheckValidTarget()
{
switch (TargetKind) {
case CompoundTargetKind.Address:
Debug.Assert(target.ResultType == StackType.Ref || target.ResultType == StackType.I);
break;
case CompoundTargetKind.Property:
Debug.Assert(target.OpCode == OpCode.Call || target.OpCode == OpCode.CallVirt);
var owner = ((CallInstruction)target).Method.AccessorOwner as IProperty;
Debug.Assert(owner != null && owner.CanSet);
break;
case CompoundTargetKind.Dynamic:
Debug.Assert(target.OpCode == OpCode.DynamicGetMemberInstruction || target.OpCode == OpCode.DynamicGetIndexInstruction);
break;
}
}
protected void WriteSuffix(ITextOutput output)
{
switch (TargetKind) {
case CompoundTargetKind.Address:
output.Write(".address");
break;
case CompoundTargetKind.Property:
output.Write(".property");
break;
}
switch (EvalMode) {
case CompoundEvalMode.EvaluatesToNewValue:
output.Write(".new");
break;
case CompoundEvalMode.EvaluatesToOldValue:
output.Write(".old");
break;
}
}
}
@ -82,8 +147,9 @@ namespace ICSharpCode.Decompiler.IL @@ -82,8 +147,9 @@ namespace ICSharpCode.Decompiler.IL
public bool IsLifted { get; }
public NumericCompoundAssign(BinaryNumericInstruction binary, ILInstruction target, ILInstruction value, IType type, CompoundAssignmentType compoundAssignmentType)
: base(OpCode.NumericCompoundAssign, compoundAssignmentType, target, value)
public NumericCompoundAssign(BinaryNumericInstruction binary, ILInstruction target,
CompoundTargetKind targetKind, ILInstruction value, IType type, CompoundEvalMode evalMode)
: base(OpCode.NumericCompoundAssign, evalMode, target, targetKind, value)
{
Debug.Assert(IsBinaryCompatibleWithType(binary, type));
this.CheckForOverflow = binary.CheckForOverflow;
@ -95,8 +161,8 @@ namespace ICSharpCode.Decompiler.IL @@ -95,8 +161,8 @@ namespace ICSharpCode.Decompiler.IL
this.IsLifted = binary.IsLifted;
this.type = type;
this.AddILRange(binary);
Debug.Assert(compoundAssignmentType == CompoundAssignmentType.EvaluatesToNewValue || (Operator == BinaryNumericOperator.Add || Operator == BinaryNumericOperator.Sub));
Debug.Assert(IsValidCompoundAssignmentTarget(Target));
Debug.Assert(evalMode == CompoundEvalMode.EvaluatesToNewValue || (Operator == BinaryNumericOperator.Add || Operator == BinaryNumericOperator.Sub));
Debug.Assert(this.ResultType == (IsLifted ? StackType.O : UnderlyingResultType));
}
/// <summary>
@ -175,16 +241,20 @@ namespace ICSharpCode.Decompiler.IL @@ -175,16 +241,20 @@ namespace ICSharpCode.Decompiler.IL
WriteILRange(output, options);
output.Write(OpCode);
output.Write("." + BinaryNumericInstruction.GetOperatorName(Operator));
if (CompoundAssignmentType == CompoundAssignmentType.EvaluatesToNewValue)
output.Write(".new");
else
output.Write(".old");
if (CheckForOverflow)
if (CheckForOverflow) {
output.Write(".ovf");
if (Sign == Sign.Unsigned)
}
if (Sign == Sign.Unsigned) {
output.Write(".unsigned");
else if (Sign == Sign.Signed)
} else if (Sign == Sign.Signed) {
output.Write(".signed");
}
output.Write('.');
output.Write(UnderlyingResultType.ToString().ToLowerInvariant());
if (IsLifted) {
output.Write(".lifted");
}
base.WriteSuffix(output);
output.Write('(');
Target.WriteTo(output, options);
output.Write(", ");
@ -198,13 +268,13 @@ namespace ICSharpCode.Decompiler.IL @@ -198,13 +268,13 @@ namespace ICSharpCode.Decompiler.IL
public readonly IMethod Method;
public bool IsLifted => false; // TODO: implement lifted user-defined compound assignments
public UserDefinedCompoundAssign(IMethod method, CompoundAssignmentType compoundAssignmentType, ILInstruction target, ILInstruction value)
: base(OpCode.UserDefinedCompoundAssign, compoundAssignmentType, target, value)
public UserDefinedCompoundAssign(IMethod method, CompoundEvalMode evalMode,
ILInstruction target, CompoundTargetKind targetKind, ILInstruction value)
: base(OpCode.UserDefinedCompoundAssign, evalMode, target, targetKind, value)
{
this.Method = method;
Debug.Assert(Method.IsOperator || IsStringConcat(method));
Debug.Assert(compoundAssignmentType == CompoundAssignmentType.EvaluatesToNewValue || (Method.Name == "op_Increment" || Method.Name == "op_Decrement"));
Debug.Assert(IsValidCompoundAssignmentTarget(Target));
Debug.Assert(evalMode == CompoundEvalMode.EvaluatesToNewValue || (Method.Name == "op_Increment" || Method.Name == "op_Decrement"));
}
public static bool IsStringConcat(IMethod method)
@ -218,11 +288,7 @@ namespace ICSharpCode.Decompiler.IL @@ -218,11 +288,7 @@ namespace ICSharpCode.Decompiler.IL
{
WriteILRange(output, options);
output.Write(OpCode);
if (CompoundAssignmentType == CompoundAssignmentType.EvaluatesToNewValue)
output.Write(".new");
else
output.Write(".old");
base.WriteSuffix(output);
output.Write(' ');
Method.WriteTo(output);
output.Write('(');
@ -240,8 +306,11 @@ namespace ICSharpCode.Decompiler.IL @@ -240,8 +306,11 @@ namespace ICSharpCode.Decompiler.IL
public CSharpArgumentInfo ValueArgumentInfo { get; }
public CSharpBinderFlags BinderFlags { get; }
public DynamicCompoundAssign(ExpressionType op, CSharpBinderFlags binderFlags, ILInstruction target, CSharpArgumentInfo targetArgumentInfo, ILInstruction value, CSharpArgumentInfo valueArgumentInfo)
: base(OpCode.DynamicCompoundAssign, CompoundAssignmentTypeFromOperation(op), target, value)
public DynamicCompoundAssign(ExpressionType op, CSharpBinderFlags binderFlags,
ILInstruction target, CSharpArgumentInfo targetArgumentInfo,
ILInstruction value, CSharpArgumentInfo valueArgumentInfo,
CompoundTargetKind targetKind = CompoundTargetKind.Dynamic)
: base(OpCode.DynamicCompoundAssign, CompoundEvalModeFromOperation(op), target, targetKind, value)
{
if (!IsExpressionTypeSupported(op))
throw new ArgumentOutOfRangeException("op");
@ -257,10 +326,7 @@ namespace ICSharpCode.Decompiler.IL @@ -257,10 +326,7 @@ namespace ICSharpCode.Decompiler.IL
output.Write(OpCode);
output.Write("." + Operation.ToString().ToLower());
DynamicInstruction.WriteBinderFlags(BinderFlags, output, options);
if (CompoundAssignmentType == CompoundAssignmentType.EvaluatesToNewValue)
output.Write(".new");
else
output.Write(".old");
base.WriteSuffix(output);
output.Write(' ');
DynamicInstruction.WriteArgumentList(output, options, (Target, TargetArgumentInfo), (Value, ValueArgumentInfo));
}
@ -286,14 +352,14 @@ namespace ICSharpCode.Decompiler.IL @@ -286,14 +352,14 @@ namespace ICSharpCode.Decompiler.IL
|| type == ExpressionType.SubtractAssignChecked;
}
static CompoundAssignmentType CompoundAssignmentTypeFromOperation(ExpressionType op)
static CompoundEvalMode CompoundEvalModeFromOperation(ExpressionType op)
{
switch (op) {
case ExpressionType.PostIncrementAssign:
case ExpressionType.PostDecrementAssign:
return CompoundAssignmentType.EvaluatesToOldValue;
return CompoundEvalMode.EvaluatesToOldValue;
default:
return CompoundAssignmentType.EvaluatesToNewValue;
return CompoundEvalMode.EvaluatesToNewValue;
}
}
}

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

@ -18,25 +18,60 @@ @@ -18,25 +18,60 @@
using System;
using System.Collections.Generic;
using ICSharpCode.Decompiler.IL.Transforms;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.Decompiler.IL.Transforms;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
using System.Diagnostics;
namespace ICSharpCode.Decompiler.IL
{
partial class ILFunction
{
/// <summary>
/// Gets the method definition from metadata.
/// May be null for functions that were not constructed from metadata,
/// e.g., expression trees.
/// </summary>
public readonly IMethod Method;
/// <summary>
/// Gets the generic context of this function.
/// </summary>
public readonly GenericContext GenericContext;
/// <summary>
/// Gets the name of this function, usually this returns the name from metadata.
/// <para>
/// For local functions:
/// This is the name that is used to declare and use the function.
/// It may not conflict with the names of local variables of ancestor functions
/// and may be overwritten by the AssignVariableNames step.
///
/// For top-level functions, delegates and expressions trees modifying this usually
/// has no effect, as the name should not be used in the final AST construction.
/// </para>
/// </summary>
public string Name;
/// <summary>
/// Size of the IL code in this function.
/// Note: after async/await transform, this is the code size of the MoveNext function.
/// </summary>
public int CodeSize;
/// <summary>
/// List of ILVariables used in this function.
/// </summary>
public readonly ILVariableCollection Variables;
/// <summary>
/// Gets the scope in which the local function is declared.
/// Returns null, if this is not a local function.
/// </summary>
public BlockContainer DeclarationScope { get; internal set; }
/// <summary>
/// List of warnings of ILReader.
/// </summary>
@ -51,6 +86,9 @@ namespace ICSharpCode.Decompiler.IL @@ -51,6 +86,9 @@ namespace ICSharpCode.Decompiler.IL
/// </summary>
public bool IsIterator;
/// <summary>
/// Gets whether the YieldReturnDecompiler determined that the Mono C# compiler was used to compile this function.
/// </summary>
public bool StateMachineCompiledWithMono;
/// <summary>
@ -70,6 +108,11 @@ namespace ICSharpCode.Decompiler.IL @@ -70,6 +108,11 @@ namespace ICSharpCode.Decompiler.IL
/// </summary>
public IMethod MoveNextMethod;
/// <summary>
/// If this function is a local function, this field stores the reduced version of the function.
/// </summary>
internal TypeSystem.Implementation.LocalFunctionMethod ReducedMethod;
internal DebugInfo.AsyncDebugInfo AsyncDebugInfo;
int ctorCallStart = int.MinValue;
@ -96,37 +139,99 @@ namespace ICSharpCode.Decompiler.IL @@ -96,37 +139,99 @@ namespace ICSharpCode.Decompiler.IL
/// <summary>
/// If this is an expression tree or delegate, returns the expression tree type Expression{T} or T.
/// T is the delegate type that matches the signature of this method.
/// Otherwise this must be null.
/// </summary>
public IType DelegateType;
public bool IsExpressionTree => DelegateType != null && DelegateType.FullName == "System.Linq.Expressions.Expression" && DelegateType.TypeParameterCount == 1;
ILFunctionKind kind;
/// <summary>
/// Gets which kind of function this is.
/// </summary>
public ILFunctionKind Kind {
get => kind;
internal set {
if (kind == ILFunctionKind.TopLevelFunction || kind == ILFunctionKind.LocalFunction)
throw new InvalidOperationException("ILFunction.Kind of a top-level or local function may not be changed.");
kind = value;
}
}
/// <summary>
/// Return type of this function.
/// Might be null, if this function was not created from metadata.
/// </summary>
public readonly IType ReturnType;
/// <summary>
/// List of parameters of this function.
/// Might be null, if this function was not created from metadata.
/// </summary>
public readonly IReadOnlyList<IParameter> Parameters;
public ILFunction(IMethod method, int codeSize, GenericContext genericContext, ILInstruction body) : base(OpCode.ILFunction)
/// <summary>
/// Constructs a new ILFunction from the given metadata and with the given ILAst body.
/// </summary>
/// <remarks>
/// Use <see cref="ILReader"/> to create ILAst.
/// <paramref name="method"/> may be null.
/// </remarks>
public ILFunction(IMethod method, int codeSize, GenericContext genericContext, ILInstruction body, ILFunctionKind kind = ILFunctionKind.TopLevelFunction) : base(OpCode.ILFunction)
{
this.Method = method;
this.Name = Method?.Name;
this.CodeSize = codeSize;
this.GenericContext = genericContext;
this.Body = body;
this.ReturnType = Method?.ReturnType;
this.Parameters = Method?.Parameters;
this.Variables = new ILVariableCollection(this);
this.LocalFunctions = new InstructionCollection<ILFunction>(this, 1);
this.kind = kind;
}
public ILFunction(IType returnType, IReadOnlyList<IParameter> parameters, GenericContext genericContext, ILInstruction body) : base(OpCode.ILFunction)
/// <summary>
/// This constructor is only to be used by the TransformExpressionTrees step.
/// </summary>
internal ILFunction(IType returnType, IReadOnlyList<IParameter> parameters, GenericContext genericContext, ILInstruction body) : base(OpCode.ILFunction)
{
this.GenericContext = genericContext;
this.Body = body;
this.ReturnType = returnType;
this.Parameters = parameters;
this.Variables = new ILVariableCollection(this);
this.LocalFunctions = new InstructionCollection<ILFunction>(this, 1);
this.kind = ILFunctionKind.ExpressionTree;
}
internal override void CheckInvariant(ILPhase phase)
{
switch (kind) {
case ILFunctionKind.TopLevelFunction:
Debug.Assert(Parent == null);
Debug.Assert(DelegateType == null);
Debug.Assert(DeclarationScope == null);
Debug.Assert(Method != null);
break;
case ILFunctionKind.Delegate:
Debug.Assert(Parent != null && !(Parent is Block));
Debug.Assert(DelegateType != null);
Debug.Assert(DeclarationScope == null);
Debug.Assert(!(DelegateType?.FullName == "System.Linq.Expressions.Expression" && DelegateType.TypeParameterCount == 1));
break;
case ILFunctionKind.ExpressionTree:
Debug.Assert(Parent != null && !(Parent is Block));
Debug.Assert(DelegateType != null);
Debug.Assert(DeclarationScope == null);
Debug.Assert(DelegateType?.FullName == "System.Linq.Expressions.Expression" && DelegateType.TypeParameterCount == 1);
break;
case ILFunctionKind.LocalFunction:
Debug.Assert(Parent is ILFunction && SlotInfo == ILFunction.LocalFunctionsSlot);
Debug.Assert(DeclarationScope != null);
Debug.Assert(DelegateType == null);
Debug.Assert(Method != null);
break;
}
for (int i = 0; i < Variables.Count; i++) {
Debug.Assert(Variables[i].Function == this);
Debug.Assert(Variables[i].IndexInFunction == i);
@ -148,8 +253,13 @@ namespace ICSharpCode.Decompiler.IL @@ -148,8 +253,13 @@ namespace ICSharpCode.Decompiler.IL
output.Write(' ');
Method.WriteTo(output);
}
if (IsExpressionTree) {
output.Write(".ET");
switch (kind) {
case ILFunctionKind.ExpressionTree:
output.Write(".ET");
break;
case ILFunctionKind.LocalFunction:
output.Write(".local");
break;
}
if (DelegateType != null) {
output.Write("[");
@ -165,6 +275,11 @@ namespace ICSharpCode.Decompiler.IL @@ -165,6 +275,11 @@ namespace ICSharpCode.Decompiler.IL
if (IsIterator) {
output.WriteLine(".iterator");
}
if (DeclarationScope != null) {
output.Write("declared as " + Name + " in ");
output.WriteLocalReference(DeclarationScope.EntryPoint.Label, DeclarationScope);
output.WriteLine();
}
output.MarkFoldStart(Variables.Count + " variable(s)", true);
foreach (var variable in Variables) {
@ -181,6 +296,11 @@ namespace ICSharpCode.Decompiler.IL @@ -181,6 +296,11 @@ namespace ICSharpCode.Decompiler.IL
body.WriteTo(output, options);
output.WriteLine();
foreach (var localFunction in LocalFunctions) {
output.WriteLine();
localFunction.WriteTo(output, options);
}
if (options.ShowILRanges) {
var unusedILRanges = FindUnusedILRanges();
if (!unusedILRanges.IsEmpty) {
@ -276,6 +396,8 @@ namespace ICSharpCode.Decompiler.IL @@ -276,6 +396,8 @@ namespace ICSharpCode.Decompiler.IL
/// </summary>
internal void RecombineVariables(ILVariable variable1, ILVariable variable2)
{
if (variable1 == variable2)
return;
Debug.Assert(ILVariableEqualityComparer.Instance.Equals(variable1, variable2));
foreach (var ldloc in variable2.LoadInstructions.ToArray()) {
ldloc.Variable = variable1;
@ -290,4 +412,33 @@ namespace ICSharpCode.Decompiler.IL @@ -290,4 +412,33 @@ namespace ICSharpCode.Decompiler.IL
Debug.Assert(ok);
}
}
public enum ILFunctionKind
{
/// <summary>
/// ILFunction is a "top-level" function, i.e., method, accessor, constructor, destructor or operator.
/// </summary>
TopLevelFunction,
/// <summary>
/// ILFunction is a delegate or lambda expression.
/// </summary>
/// <remarks>
/// This kind is introduced by the DelegateConstruction and TransformExpressionTrees steps in the decompiler pipeline.
/// </remarks>
Delegate,
/// <summary>
/// ILFunction is an expression tree lambda.
/// </summary>
/// <remarks>
/// This kind is introduced by the TransformExpressionTrees step in the decompiler pipeline.
/// </remarks>
ExpressionTree,
/// <summary>
/// ILFunction is a C# 7.0 local function.
/// </summary>
/// <remarks>
/// This kind is introduced by the LocalFunctionDecompiler step in the decompiler pipeline.
/// </remarks>
LocalFunction
}
}

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

Loading…
Cancel
Save