Browse Source

Merge branch 'master' of https://github.com/icsharpcode/ILSpy into fancy-tooltips

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

2
DecompilerNuGetDemos.workbook

@ -6,7 +6,7 @@ platforms: @@ -6,7 +6,7 @@ platforms:
- DotNetCore
packages:
- id: ICSharpCode.Decompiler
version: 5.0.0.4793-preview2
version: 5.0.0.5066-preview4
---
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-preview2</Version>
<Version>5.0.0-preview4</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.4793-preview2" />
<PackageReference Include="ICSharpCode.Decompiler" Version="5.0.0.5066-preview4" />
<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.4793-preview2" />
<PackageReference Include="ICSharpCode.Decompiler" Version="5.0.0.5066-preview4" />
</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",
};

2
ICSharpCode.Decompiler.Tests/Helpers/Tester.VB.cs

@ -56,7 +56,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers @@ -56,7 +56,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
var provider = new VBCodeProvider(new Dictionary<string, string> { { "CompilerVersion", "v4.0" } });
CompilerParameters options = new CompilerParameters();
options.GenerateExecutable = !flags.HasFlag(CompilerOptions.Library);
options.CompilerOptions = "/o" + (flags.HasFlag(CompilerOptions.Optimize) ? "+" : "-");
options.CompilerOptions = "/optimize" + (flags.HasFlag(CompilerOptions.Optimize) ? "+" : "-");
options.CompilerOptions += (flags.HasFlag(CompilerOptions.UseDebug) ? " /debug" : "");
options.CompilerOptions += (flags.HasFlag(CompilerOptions.Force32Bit) ? " /platform:anycpu32bitpreferred" : "");
options.CompilerOptions += " /optioninfer+ /optionexplicit+";

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

@ -534,5 +534,27 @@ namespace ICSharpCode.Decompiler.Tests.Helpers @@ -534,5 +534,27 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
// If the last try still fails, don't catch the exception
action();
}
public static void SignAssembly(string assemblyPath, string keyFilePath)
{
string snPath = SdkUtility.GetSdkPath("sn.exe");
ProcessStartInfo info = new ProcessStartInfo(snPath);
info.Arguments = $"-R \"{assemblyPath}\" \"{keyFilePath}\"";
info.RedirectStandardError = true;
info.RedirectStandardOutput = true;
info.UseShellExecute = false;
Process process = Process.Start(info);
var outputTask = process.StandardOutput.ReadToEndAsync();
var errorTask = process.StandardError.ReadToEndAsync();
Task.WaitAll(outputTask, errorTask);
process.WaitForExit();
Console.WriteLine("output: " + outputTask.Result);
Console.WriteLine("errors: " + errorTask.Result);
}
}
}

30
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>
@ -42,13 +42,13 @@ @@ -42,13 +42,13 @@
<ItemGroup>
<PackageReference Include="DiffLib" Version="2017.7.26.1241" />
<PackageReference Include="Microsoft.Build.Locator" Version="1.2.2" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.0.0-beta4-final" />
<PackageReference Include="Microsoft.CodeAnalysis.VisualBasic" Version="3.0.0-beta4-final" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.3.0-beta1-final" />
<PackageReference Include="Microsoft.CodeAnalysis.VisualBasic" Version="3.3.0-beta1-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>
@ -61,11 +61,17 @@ @@ -61,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" />
@ -74,6 +80,15 @@ @@ -74,6 +80,15 @@
<ItemGroup>
<Compile Include="DisassemblerPrettyTestRunner.cs" />
<Compile Include="TestCases\ILPretty\ConstantBlobs.cs" />
<Compile Include="TestCases\Pretty\OutVariables.cs" />
<None Include="TestCases\VBPretty\VBCompoundAssign.cs" />
<Compile Include="TestCases\Pretty\ThrowExpressions.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" />
@ -83,12 +98,12 @@ @@ -83,12 +98,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" />
@ -230,6 +245,7 @@ @@ -230,6 +245,7 @@
<None Include="TestCases\ILPretty\Issue379.il" />
<None Include="TestCases\ILPretty\Issue982.il" />
<None Include="TestCases\Pretty\Readme.txt" />
<None Include="TestCases\VBPretty\VBCompoundAssign.vb" />
<None Include="TestCases\VBPretty\Async.vb" />
</ItemGroup>

20
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()
{
@ -91,13 +97,19 @@ namespace ICSharpCode.Decompiler.Tests @@ -91,13 +97,19 @@ namespace ICSharpCode.Decompiler.Tests
[Test]
public void FSharpUsing_Debug()
{
Run(settings: new DecompilerSettings { RemoveDeadCode = true });
Run(settings: new DecompilerSettings { RemoveDeadStores = true });
}
[Test]
public void FSharpUsing_Release()
{
Run(settings: new DecompilerSettings { RemoveDeadCode = true });
Run(settings: new DecompilerSettings { RemoveDeadStores = true });
}
[Test]
public void DirectCallToExplicitInterfaceImpl()
{
Run();
}
[Test]
@ -170,14 +182,14 @@ namespace ICSharpCode.Decompiler.Tests @@ -170,14 +182,14 @@ namespace ICSharpCode.Decompiler.Tests
public void FSharpLoops_Debug()
{
CopyFSharpCoreDll();
Run(settings: new DecompilerSettings { RemoveDeadCode = true });
Run(settings: new DecompilerSettings { RemoveDeadStores = true });
}
[Test]
public void FSharpLoops_Release()
{
CopyFSharpCoreDll();
Run(settings: new DecompilerSettings { RemoveDeadCode = true });
Run(settings: new DecompilerSettings { RemoveDeadStores = true });
}
void Run([CallerMemberName] string testName = null, DecompilerSettings settings = null)

2
ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs

@ -18,7 +18,7 @@ using NUnit.Framework; @@ -18,7 +18,7 @@ using NUnit.Framework;
namespace ICSharpCode.Decompiler.Tests
{
[TestFixture]
[TestFixture, Parallelizable(ParallelScope.All)]
public class PdbGenerationTestRunner
{
static readonly string TestCasePath = Tester.TestCasePath + "/PdbGen";

34
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -114,7 +114,7 @@ namespace ICSharpCode.Decompiler.Tests @@ -114,7 +114,7 @@ namespace ICSharpCode.Decompiler.Tests
RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings {
NullPropagation = false,
// legacy csc generates a dead store in debug builds
RemoveDeadCode = (cscOptions == CompilerOptions.None)
RemoveDeadStores = (cscOptions == CompilerOptions.None)
});
}
@ -123,7 +123,7 @@ namespace ICSharpCode.Decompiler.Tests @@ -123,7 +123,7 @@ namespace ICSharpCode.Decompiler.Tests
{
RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings {
// legacy csc generates a dead store in debug builds
RemoveDeadCode = (cscOptions == CompilerOptions.None)
RemoveDeadStores = (cscOptions == CompilerOptions.None)
});
}
@ -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);
}
@ -180,10 +180,16 @@ namespace ICSharpCode.Decompiler.Tests @@ -180,10 +180,16 @@ namespace ICSharpCode.Decompiler.Tests
{
RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings {
// legacy csc generates a dead store in debug builds
RemoveDeadCode = (cscOptions == CompilerOptions.None)
RemoveDeadStores = (cscOptions == CompilerOptions.None)
});
}
[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)
{
@ -317,6 +329,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -317,6 +329,12 @@ namespace ICSharpCode.Decompiler.Tests
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void ThrowExpressions([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void WellKnownConstants([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions)
{
@ -420,7 +438,13 @@ namespace ICSharpCode.Decompiler.Tests @@ -420,7 +438,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);
}

13
ICSharpCode.Decompiler.Tests/RoundtripAssembly.cs

@ -69,11 +69,7 @@ namespace ICSharpCode.Decompiler.Tests @@ -69,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.");
}
}
[Test]
@ -106,9 +102,9 @@ namespace ICSharpCode.Decompiler.Tests @@ -106,9 +102,9 @@ namespace ICSharpCode.Decompiler.Tests
RunWithOutput("Random Tests\\TestCases", "TestCase-1.exe");
}
void RunWithTest(string dir, string fileToRoundtrip, string fileToTest)
void RunWithTest(string dir, string fileToRoundtrip, string fileToTest, string keyFile = null)
{
RunInternal(dir, fileToRoundtrip, outputDir => RunTest(outputDir, fileToTest));
RunInternal(dir, fileToRoundtrip, outputDir => RunTest(outputDir, fileToTest), keyFile);
}
void RunWithOutput(string dir, string fileToRoundtrip)
@ -118,7 +114,7 @@ namespace ICSharpCode.Decompiler.Tests @@ -118,7 +114,7 @@ namespace ICSharpCode.Decompiler.Tests
outputDir => Tester.RunAndCompareOutput(fileToRoundtrip, Path.Combine(inputDir, fileToRoundtrip), Path.Combine(outputDir, fileToRoundtrip)));
}
void RunInternal(string dir, string fileToRoundtrip, Action<string> testAction)
void RunInternal(string dir, string fileToRoundtrip, Action<string> testAction, string snkFilePath = null)
{
if (!Directory.Exists(TestDir)) {
Assert.Ignore($"Assembly-roundtrip test ignored: test directory '{TestDir}' needs to be checked out separately." + Environment.NewLine +
@ -156,6 +152,9 @@ namespace ICSharpCode.Decompiler.Tests @@ -156,6 +152,9 @@ namespace ICSharpCode.Decompiler.Tests
decompiler.Settings = new DecompilerSettings(LanguageVersion.CSharp7_3);
// use a fixed GUID so that we can diff the output between different ILSpy runs without spurious changes
decompiler.ProjectGuid = Guid.Parse("{127C83E4-4587-4CF9-ADCA-799875F3DFE6}");
if (snkFilePath != null) {
decompiler.StrongNameKeyFile = Path.Combine(inputDir, snkFilePath);
}
decompiler.DecompileProject(module, decompiledDir);
Console.WriteLine($"Decompiled {fileToRoundtrip} in {w.Elapsed.TotalSeconds:f2}");
projectFile = Path.Combine(decompiledDir, module.Name + ".csproj");

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

53
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;
@ -25,6 +26,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -25,6 +26,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
public class Async
{
private int memberField;
public async void SimpleVoidMethod()
{
Console.WriteLine("Before");
@ -71,6 +74,16 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -71,6 +74,16 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
Console.WriteLine("No Await");
}
public async Task CapturingThis()
{
await Task.Delay(memberField);
}
public async Task CapturingThisWithoutAwait()
{
Console.WriteLine(memberField);
}
public async Task<bool> SimpleBoolTaskMethod()
{
Console.WriteLine("Before");
@ -126,6 +139,46 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -126,6 +139,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);
}
}
}

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

@ -146,6 +146,64 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -146,6 +146,64 @@ 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
}
public void Test2()
{
Noop("M3.new", new BaseClass().M3);
Noop("M3.new", new SubClass().M3);
}
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 +217,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -159,6 +217,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 +243,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -174,6 +243,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);

38
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();
@ -382,6 +410,16 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -382,6 +410,16 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
}
private static bool ConstantTarget(dynamic a)
{
return true.Equals(a);
}
private static IntPtr NewIntPtr(dynamic a)
{
return new IntPtr(a);
}
private static dynamic GetDynamic(int i)
{
return null;

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

@ -281,5 +281,75 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -281,5 +281,75 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
}
}
public void ReassignExceptionVar()
{
try {
Console.WriteLine("ReassignExceptionVar");
} catch (Exception innerException) {
if (innerException.InnerException != null) {
innerException = innerException.InnerException;
}
Console.WriteLine(innerException);
}
}
public int UseExceptionVarOutsideCatch()
{
Exception ex2;
try {
return 1;
} catch (Exception ex) {
ex2 = ex;
}
Console.WriteLine(ex2 != null);
return 2;
}
public void GenericException<TException>(int input) where TException : Exception
{
try {
Console.WriteLine(input);
} catch (TException val) {
Console.WriteLine(val.Message);
throw;
}
}
public void GenericException2<T>() where T : Exception
{
try {
Console.WriteLine("CatchT");
#if ROSLYN
} catch (T val) {
Console.WriteLine("{0} {1}", val, val.ToString());
}
#else
} catch (T arg) {
Console.WriteLine("{0} {1}", arg, arg.ToString());
}
#endif
}
#if CS60
public void GenericExceptionWithCondition<TException>(int input) where TException : Exception
{
try {
Console.WriteLine(input);
} catch (TException val) when (val.Message.Contains("Test")) {
Console.WriteLine(val.Message);
throw;
}
}
public void GenericException2WithCondition<TException>(int input) where TException : Exception
{
try {
Console.WriteLine(input);
} catch (TException val) when (val.Message.Contains("Test")) {
Console.WriteLine("{0} {1}", val, val.ToString());
}
}
#endif
}
}

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

@ -585,7 +585,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -585,7 +585,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
2004,
2008,
2012
}).Any<int>));
}).Any));
}
public void MethodGroupConstant()
@ -625,11 +625,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -625,11 +625,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
//no params
ToCode(X(), () => call(() => 42));
//one param
ToCode(X(), () => from x in new int[2] {
ToCode(X(), () => new int[2] {
37,
42
}
select x * 2);
}.Select((int x) => x * 2));
//two params
ToCode(X(), () => new int[2] {
37,
@ -731,13 +730,12 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -731,13 +730,12 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public void QuotedWithAnonymous()
{
ToCode(X(), () => (from o in new[] {
ToCode(X(), () => new[] {
new {
X = "a",
Y = "b"
}
}
select o.X + o.Y).Single());
}.Select(o => o.X + o.Y).Single());
}
public void StaticCall()

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

@ -1468,9 +1468,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.InitializerTests @@ -1468,9 +1468,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.InitializerTests
DateTimeFormat = new DateTimeFormatInfo {
ShortDatePattern = "ddmmyy"
},
NumberFormat = (from format in source
where format.CurrencySymbol == "$"
select format).First()
NumberFormat = source.Where((NumberFormatInfo format) => format.CurrencySymbol == "$").First()
}
});
}
@ -1678,13 +1676,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.InitializerTests @@ -1678,13 +1676,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 +1711,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.InitializerTests @@ -1717,13 +1711,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);
}
}
}

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

@ -0,0 +1,325 @@ @@ -0,0 +1,325 @@
// 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 Lazy<object> nonCapturinglocalFunctionInLambda = new Lazy<object>(delegate {
return CreateValue();
object CreateValue()
{
return null;
}
});
private Lazy<object> capturinglocalFunctionInLambda = new Lazy<object>(delegate {
int x = 42;
return Do();
object Do()
{
return CreateValue();
int CreateValue()
{
return x;
}
}
});
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);
// }
// }
//}
}
}

14
ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullPropagation.cs

@ -25,6 +25,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -25,6 +25,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
private class MyClass
{
public int IntVal;
public readonly int ReadonlyIntVal;
public MyStruct StructField;
public readonly MyStruct ReadonlyStructField;
public string Text;
public MyClass Field;
public MyClass Property {
@ -45,6 +48,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -45,6 +48,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
private struct MyStruct
{
public int IntVal;
public readonly int ReadonlyIntVal;
public MyClass Field;
public MyStruct? Property1 => null;
public MyStruct Property2 => default(MyStruct);
@ -179,6 +183,16 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -179,6 +183,16 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
Use(GetMyClass()?.Text ?? "Hello");
}
public void CallOnValueTypeField()
{
Use(GetMyClass()?.IntVal.ToString());
Use(GetMyStruct()?.IntVal.ToString());
Use(GetMyClass()?.ReadonlyIntVal.ToString());
Use(GetMyStruct()?.ReadonlyIntVal.ToString());
GetMyClass()?.StructField.Done();
GetMyClass()?.ReadonlyStructField.Done();
}
public void InvokeDelegate(EventHandler eh)
{
eh?.Invoke(null, EventArgs.Empty);

69
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,67 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -28,5 +33,67 @@ 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 void Constraints<UC, C, CN, NN, S, SN, D, DN, NND>() where C : class where CN : class? where NN : notnull where S : struct where D : IDisposable where DN : IDisposable? where NND : notnull, IDisposable
{
}
}
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;
}
public class T04_Dictionary<TKey, TValue> where TKey : notnull
{
private struct Entry
{
public TKey key;
public TValue value;
}
private int[]? _buckets;
private Entry[]? _entries;
private IEqualityComparer<TKey>? _comparer;
}
}

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

35
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 static void OutVarInShortCircuit(Dictionary<int, string> d)
{
if (d.Count > 2 && d.TryGetValue(42, out string value)) {
Console.WriteLine(value);
}
}
public RenameCallbackArguments(AstNode nodeToReplace, AstNode newNode)
public static Action CapturedOutVarInShortCircuit(Dictionary<int, string> d)
{
if (nodeToReplace == null)
throw new ArgumentNullException("nodeToReplace");
if (newNode == null)
throw new ArgumentNullException("newNode");
this.NodeToReplace = nodeToReplace;
this.NewNode = newNode;
// 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
}
}
}

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

@ -2,10 +2,52 @@ @@ -2,10 +2,52 @@
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 struct Issue1630
{
private object data;
private int next;
public static void Test()
{
Issue1630[] array = new Issue1630[1];
int num = 0;
while (num >= 0) {
ref Issue1630 reference = ref array[num];
Console.WriteLine(reference.data);
num = reference.next;
}
}
}
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 +77,28 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -35,6 +77,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 +144,97 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -80,5 +144,97 @@ 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);
if (s.GetHashCode() == 0) {
@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));
}
}
}

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

@ -1145,5 +1145,52 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -1145,5 +1145,52 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
Console.WriteLine();
}
public static string Issue1621(int x)
{
if (x == 5) {
return "5";
}
switch (x) {
case 1:
return "1";
case 2:
case 6:
case 7:
return "2-6-7";
case 3:
return "3";
case 4:
return "4";
case 5:
return "unreachable";
default:
throw new Exception();
}
}
public static int Issue1602(string x)
{
switch (x) {
case null:
return 0;
case "":
return -1;
case "A":
return 65;
case "B":
return 66;
case "C":
return 67;
case "D":
return 68;
case "E":
return 69;
case "F":
return 70;
default:
throw new ArgumentOutOfRangeException();
}
}
}
}

247
ICSharpCode.Decompiler.Tests/TestCases/Pretty/ThrowExpressions.cs

@ -0,0 +1,247 @@ @@ -0,0 +1,247 @@
using System;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
internal class ThrowExpressions
{
private class ArgumentCheckingCtor
{
private int initializedFromCtor = CountSheep() ?? throw new Exception("No sheep?!");
private object cacheObj = TryGetObj() ?? throw new Exception("What?");
private object simpleObj;
private int? nullableInt;
public ArgumentCheckingCtor(object simpleObj, int? nullableInt)
{
this.simpleObj = (simpleObj ?? throw new ArgumentNullException("simpleObj"));
this.nullableInt = (nullableInt ?? throw new ArgumentNullException("nullableInt"));
}
public ArgumentCheckingCtor(string input)
: this(input, GetIntOrNull(input ?? throw new ArgumentNullException("input")))
{
}
public ArgumentCheckingCtor(DataObject obj)
: this(obj ?? throw new Exception(), GetIntOrNull(obj.NullableDataField?.NullableDataField.ToString() ?? throw new ArgumentNullException("input")))
{
}
private static int? GetIntOrNull(string v)
{
if (int.TryParse(v, out int result)) {
return result;
}
return null;
}
private static int? CountSheep()
{
throw new NotImplementedException();
}
private static object TryGetObj()
{
return null;
}
public override int GetHashCode()
{
return initializedFromCtor;
}
public override bool Equals(object obj)
{
return true;
}
}
public class DataObject
{
public int IntField;
public int? NullableIntField;
public Data DataField;
public Data? NullableDataField;
public int IntProperty {
get;
set;
}
public int? NullableIntProperty {
get;
set;
}
public Data DataProperty {
get;
}
public Data? NullableDataProperty {
get;
}
}
public struct Data
{
public int IntField;
public int? NullableIntField;
public MoreData DataField;
public MoreData? NullableDataField;
public int IntProperty {
get;
set;
}
public int? NullableIntProperty {
get;
set;
}
public MoreData DataProperty {
get;
}
public MoreData? NullableDataProperty {
get;
}
}
public struct MoreData
{
public int IntField;
public int? NullableIntField;
public int IntProperty {
get;
set;
}
public int? NullableIntProperty {
get;
set;
}
}
public static int IntField;
public static int? NullableIntField;
public static object ObjectField;
public int InstIntField;
public int? InstNullableIntField;
public object InstObjectField;
public Data DataField;
public Data? NullableDataField;
public DataObject DataObjectField;
public static int IntProperty {
get;
}
public static int? NullableIntProperty {
get;
}
public static object ObjProperty {
get;
}
public int InstIntProperty {
get;
}
public int? InstNullableIntProperty {
get;
}
public object InstObjProperty {
get;
}
public Data DataProperty {
get;
}
public Data? NullableDataProperty {
get;
}
public DataObject DataObjectProperty {
get;
}
public static int ReturnIntField()
{
return NullableIntField ?? throw new Exception();
}
public static int ReturnIntProperty()
{
return NullableIntProperty ?? throw new Exception();
}
public static object ReturnObjField()
{
return ObjectField ?? throw new Exception();
}
public static object ReturnObjProperty()
{
return ObjProperty ?? throw new Exception();
}
public static int ReturnIntField(ThrowExpressions inst)
{
return inst.InstNullableIntField ?? throw new Exception();
}
public static int ReturnIntProperty(ThrowExpressions inst)
{
return inst.InstNullableIntProperty ?? throw new Exception();
}
public static object ReturnObjField(ThrowExpressions inst)
{
return inst.InstObjectField ?? throw new Exception();
}
public static object ReturnObjProperty(ThrowExpressions inst)
{
return inst.InstObjProperty ?? throw new Exception();
}
public static void UseComplexNullableStruct(ThrowExpressions inst)
{
Use(inst.InstNullableIntField ?? throw new Exception());
Use((inst.NullableDataField ?? throw new Exception()).IntField);
Use(inst.NullableDataField?.NullableIntField ?? throw new Exception());
Use((inst.NullableDataProperty ?? throw new Exception()).IntField);
Use(inst.NullableDataProperty?.NullableIntField ?? throw new Exception());
Use((inst.NullableDataField ?? throw new Exception()).DataField.IntField);
Use(inst.NullableDataField?.DataField.NullableIntField ?? throw new Exception());
Use((inst.NullableDataProperty ?? throw new Exception()).DataField.IntField);
Use(inst.NullableDataProperty?.DataField.NullableIntField ?? throw new Exception());
Use((inst.NullableDataField ?? throw new Exception()).DataProperty.IntField);
Use(inst.NullableDataField?.DataProperty.NullableIntField ?? throw new Exception());
Use((inst.NullableDataProperty ?? throw new Exception()).DataProperty.IntField);
Use(inst.NullableDataProperty?.DataProperty.NullableIntField ?? throw new Exception());
Use(inst.NullableDataField?.NullableDataField?.IntField ?? throw new Exception());
Use(inst.NullableDataField?.NullableDataField?.NullableIntField ?? throw new Exception());
Use(inst.NullableDataProperty?.NullableDataField?.IntField ?? throw new Exception());
Use(inst.NullableDataProperty?.NullableDataField?.NullableIntField ?? throw new Exception());
Use(inst.NullableDataField?.NullableDataProperty?.IntField ?? throw new Exception());
Use(inst.NullableDataField?.NullableDataProperty?.NullableIntField ?? throw new Exception());
Use(inst.NullableDataProperty?.NullableDataProperty?.IntField ?? throw new Exception());
Use(inst.NullableDataProperty?.NullableDataProperty?.NullableIntField ?? throw new Exception());
}
public static void UseComplexNullableObject(DataObject inst)
{
Use(inst?.NullableIntField ?? throw new Exception());
Use(inst?.NullableDataField?.IntField ?? throw new Exception());
Use(inst?.NullableDataField?.NullableIntField ?? throw new Exception());
Use(inst?.NullableDataProperty?.IntField ?? throw new Exception());
Use(inst?.NullableDataProperty?.NullableIntField ?? throw new Exception());
Use(inst?.NullableDataField?.DataField.IntField ?? throw new Exception());
Use(inst?.NullableDataField?.DataField.NullableIntField ?? throw new Exception());
Use(inst?.NullableDataProperty?.DataField.IntField ?? throw new Exception());
Use(inst?.NullableDataProperty?.DataField.NullableIntField ?? throw new Exception());
Use(inst?.NullableDataField?.DataProperty.IntField ?? throw new Exception());
Use(inst?.NullableDataField?.DataProperty.NullableIntField ?? throw new Exception());
Use(inst?.NullableDataProperty?.DataProperty.IntField ?? throw new Exception());
Use(inst?.NullableDataProperty?.DataProperty.NullableIntField ?? throw new Exception());
Use(inst?.NullableDataField?.NullableDataField?.IntField ?? throw new Exception());
Use(inst?.NullableDataField?.NullableDataField?.NullableIntField ?? throw new Exception());
Use(inst?.NullableDataProperty?.NullableDataField?.IntField ?? throw new Exception());
Use(inst?.NullableDataProperty?.NullableDataField?.NullableIntField ?? throw new Exception());
Use(inst?.NullableDataField?.NullableDataProperty?.IntField ?? throw new Exception());
Use(inst?.NullableDataField?.NullableDataProperty?.NullableIntField ?? throw new Exception());
Use(inst?.NullableDataProperty?.NullableDataProperty?.IntField ?? throw new Exception());
Use(inst?.NullableDataProperty?.NullableDataProperty?.NullableIntField ?? throw new Exception());
}
public static void Use<T>(T usage)
{
}
}
}

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

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

@ -66,6 +66,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -66,6 +66,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
S s = this;
s.SetField();
}
public void UseField(int val)
{
UseField(Get<S>().Field);
}
}
#if CS72
@ -263,5 +268,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -263,5 +268,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
r.Property = 2;
#endif
}
public static void CallOnFieldOfTemporary()
{
Get<S>().Field.ToString();
}
}
}

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

1
ICSharpCode.Decompiler.Tests/TestCases/VBPretty/.gitignore vendored

@ -0,0 +1 @@ @@ -0,0 +1 @@
*.dll

20
ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBCompoundAssign.cs

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
using Microsoft.VisualBasic;
using Microsoft.VisualBasic.CompilerServices;
[StandardModule]
internal sealed class VBCompoundAssign
{
public static double[] Sum3(int[] v)
{
double[] array = new double[4];
int num = Information.UBound(v);
checked {
for (int i = 0; i <= num; i += 3) {
array[0] += v[i];
array[1] += v[i + 1];
array[2] += v[i + 2];
}
return array;
}
}
}

14
ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBCompoundAssign.vb

@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
Imports System
Imports Microsoft.VisualBasic
Module VBCompoundAssign
Function Sum3(v As Int32()) As Double()
Dim arr(3) As Double
For i = 0 To UBound(v) Step 3
arr(0) += v(i)
arr(1) += v(i + 1)
arr(2) += v(i + 2)
Next
Return arr
End Function
End Module

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.Tests/VBPrettyTestRunner.cs

@ -61,17 +61,27 @@ namespace ICSharpCode.Decompiler.Tests @@ -61,17 +61,27 @@ namespace ICSharpCode.Decompiler.Tests
};
[Test, Ignore("Implement VB async/await")]
public void Async([ValueSource("defaultOptions")] CompilerOptions options)
public void Async([ValueSource(nameof(defaultOptions))] CompilerOptions options)
{
Run(options: options);
}
[Test] // TODO: legacy VB compound assign
public void VBCompoundAssign([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions options)
{
Run(options: options | CompilerOptions.Library);
}
void Run([CallerMemberName] string testName = null, CompilerOptions options = CompilerOptions.UseDebug, DecompilerSettings settings = null)
{
var vbFile = Path.Combine(TestCasePath, testName + ".vb");
var csFile = Path.Combine(TestCasePath, testName + ".cs");
var exeFile = Path.Combine(TestCasePath, testName) + Tester.GetSuffix(options) + ".exe";
if (options.HasFlag(CompilerOptions.Library)) {
exeFile = Path.ChangeExtension(exeFile, ".dll");
}
var executable = Tester.CompileVB(vbFile, options);
var executable = Tester.CompileVB(vbFile, options | CompilerOptions.ReferenceVisualBasic, exeFile);
var decompiled = Tester.DecompileCSharp(executable.PathToAssembly, settings);
CodeAssert.FilesAreEqual(csFile, decompiled);

33
ICSharpCode.Decompiler/CSharp/Annotations.cs

@ -19,6 +19,7 @@ @@ -19,6 +19,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.Decompiler.CSharp.Resolver;
using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.IL;
using ICSharpCode.Decompiler.Semantics;
@ -104,7 +105,23 @@ namespace ICSharpCode.Decompiler.CSharp @@ -104,7 +105,23 @@ namespace ICSharpCode.Decompiler.CSharp
public static ISymbol GetSymbol(this AstNode node)
{
var rr = node.Annotation<ResolveResult>();
return rr != null ? rr.GetSymbol() : null;
if (rr is MethodGroupResolveResult) {
// delegate construction?
var newObj = node.Annotation<NewObj>();
if (newObj != null) {
var funcptr = newObj.Arguments.ElementAtOrDefault(1);
if (funcptr is LdFtn ldftn) {
return ldftn.Method;
} else if (funcptr is LdVirtFtn ldVirtFtn) {
return ldVirtFtn.Method;
}
}
var ldVirtDelegate = node.Annotation<LdVirtDelegate>();
if (ldVirtDelegate != null) {
return ldVirtDelegate.Method;
}
}
return rr?.GetSymbol();
}
/// <summary>
@ -240,4 +257,18 @@ namespace ICSharpCode.Decompiler.CSharp @@ -240,4 +257,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;
}
}
}

140
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,8 +651,65 @@ namespace ICSharpCode.Decompiler.CSharp @@ -680,8 +651,65 @@ 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))
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);
if (closureTypeHandle != declaringType) {
// 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);
}
} else {
// Delegate body is declared in the same type
foreach (var m in closureType.GetMethods()) {
var methodDef = module.Metadata.GetMethodDefinition(m);
if (methodDef.Name == memberRef.Name)
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;
@ -692,6 +720,40 @@ namespace ICSharpCode.Decompiler.CSharp @@ -692,6 +720,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>

242
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;
@ -147,7 +142,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -147,7 +142,7 @@ namespace ICSharpCode.Decompiler.CSharp
public TranslatedExpression Build(CallInstruction inst)
{
if (inst is NewObj newobj && IL.Transforms.DelegateConstruction.IsDelegateConstruction(newobj, true)) {
if (inst is NewObj newobj && IL.Transforms.DelegateConstruction.IsDelegateConstruction(newobj)) {
return HandleDelegateConstruction(newobj);
}
if (settings.TupleTypes && TupleTransform.MatchTupleConstruction(inst as NewObj, out var tupleElements) && tupleElements.Length >= 2) {
@ -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(),
@ -211,6 +225,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -211,6 +225,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;
argumentList.AddNamesToPrimitiveValues = false;
@ -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.
@ -427,6 +447,9 @@ namespace ICSharpCode.Decompiler.CSharp @@ -427,6 +447,9 @@ namespace ICSharpCode.Decompiler.CSharp
var assignment = HandleAccessorCall(expectedTargetDetails, method, unused,
argumentList.Arguments.ToList(), argumentList.ArgumentNames);
if (((AssignmentExpression)assignment).Left is IndexerExpression indexer && !indexer.Target.IsNull)
indexer.Target.ReplaceWith(n => null);
if (value != null)
return assignment;
@ -565,9 +588,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -565,9 +588,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 +655,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -633,8 +655,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 +770,9 @@ namespace ICSharpCode.Decompiler.CSharp @@ -748,7 +770,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 +1003,14 @@ namespace ICSharpCode.Decompiler.CSharp @@ -979,11 +1003,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 +1134,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1107,8 +1134,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,28 +1222,70 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1196,28 +1222,70 @@ 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}");
}
var invokeMethod = inst.Method.DeclaringType.GetDelegateInvokeMethod();
return HandleDelegateConstruction(inst.Method.DeclaringType, method, expectedTargetDetails, thisArg, inst);
}
internal TranslatedExpression Build(LdVirtDelegate inst)
{
return HandleDelegateConstruction(inst.Type, inst.Method, new ExpectedTargetDetails { CallOpCode = OpCode.CallVirt }, inst.Argument, inst);
}
TranslatedExpression HandleDelegateConstruction(IType delegateType, IMethod method, ExpectedTargetDetails expectedTargetDetails, ILInstruction thisArg, ILInstruction inst)
{
var invokeMethod = delegateType.GetDelegateInvokeMethod();
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) {
@ -1230,63 +1298,105 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1230,63 +1298,105 @@ namespace ICSharpCode.Decompiler.CSharp
}
}
target = expressionBuilder.TranslateTarget(thisArg,
nonVirtualInvocation: func.OpCode == OpCode.LdFtn,
nonVirtualInvocation: expectedTargetDetails.CallOpCode == OpCode.Call,
memberStatic: method.IsStatic,
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));
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 {
requireTarget = true;
targetResolveResult = null;
}
IReadOnlyList<IType> typeArguments;
if ((step & 2) != 0) {
typeArguments = method.TypeArguments;
} else {
typeArguments = EmptyList<IType>.Instance;
}
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));
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);
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)
var oce = new ObjectCreateExpression(expressionBuilder.ConvertType(delegateType), targetExpression)
.WithILInstruction(inst)
.WithRR(new ConversionResolveResult(
inst.Method.DeclaringType,
new MemberResolveResult(target.ResolveResult, method),
Conversion.MethodGroupConversion(method, func.OpCode == OpCode.LdVirtFtn, false)));
delegateType,
result,
Conversion.MethodGroupConversion(method, expectedTargetDetails.CallOpCode == OpCode.CallVirt, 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);

245
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;
@ -134,7 +134,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -134,7 +134,7 @@ namespace ICSharpCode.Decompiler.CSharp
};
var cexpr = inst.AcceptVisitor(this, context);
#if DEBUG
if (inst.ResultType != StackType.Void && cexpr.Type.Kind != TypeKind.Unknown && inst.ResultType != StackType.Unknown) {
if (inst.ResultType != StackType.Void && cexpr.Type.Kind != TypeKind.Unknown && inst.ResultType != StackType.Unknown && cexpr.Type.Kind != TypeKind.None) {
// Validate the Translate post-condition (documented at beginning of this file):
if (inst.ResultType.IsIntegerType()) {
Debug.Assert(cexpr.Type.GetStackType().IsIntegerType(), "IL instructions of integer type must convert into C# expressions of integer type");
@ -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.Equals(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);
}
}
@ -316,6 +348,11 @@ namespace ICSharpCode.Decompiler.CSharp @@ -316,6 +348,11 @@ namespace ICSharpCode.Decompiler.CSharp
return new CallBuilder(this, typeSystem, settings).Build(inst);
}
protected internal override TranslatedExpression VisitLdVirtDelegate(LdVirtDelegate inst, TranslationContext context)
{
return new CallBuilder(this, typeSystem, settings).Build(inst);
}
protected internal override TranslatedExpression VisitNewArr(NewArr inst, TranslationContext context)
{
var dimensions = inst.Indices.Count;
@ -511,7 +548,9 @@ namespace ICSharpCode.Decompiler.CSharp @@ -511,7 +548,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 +615,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -576,7 +615,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 +624,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -585,7 +624,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 +644,31 @@ namespace ICSharpCode.Decompiler.CSharp @@ -605,10 +644,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)
@ -889,6 +949,13 @@ namespace ICSharpCode.Decompiler.CSharp @@ -889,6 +949,13 @@ namespace ICSharpCode.Decompiler.CSharp
.WithILInstruction(inst);
}
protected internal override TranslatedExpression VisitThrow(Throw inst, TranslationContext context)
{
return new ThrowExpression(Translate(inst.Argument))
.WithILInstruction(inst)
.WithRR(new ThrowResolveResult());
}
protected internal override TranslatedExpression VisitUserDefinedLogicOperator(UserDefinedLogicOperator inst, TranslationContext context)
{
var left = Translate(inst.Left, inst.Method.Parameters[0].Type).ConvertTo(inst.Method.Parameters[0].Type, this);
@ -1018,7 +1085,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1018,7 +1085,7 @@ namespace ICSharpCode.Decompiler.CSharp
// ref - ref => i
return CallUnsafeIntrinsic("ByteOffset", new[] { left.Expression, right.Expression }, compilation.FindType(KnownTypeCode.IntPtr), inst);
}
if (inst.LeftInputType == StackType.Ref && inst.RightInputType == StackType.I
if (inst.LeftInputType == StackType.Ref && inst.RightInputType.IsIntegerType()
&& left.Type is ByReferenceType brt) {
// ref [+-] int
string name = (inst.Operator == BinaryNumericOperator.Sub ? "Subtract" : "Add");
@ -1035,7 +1102,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1035,7 +1102,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 {
@ -1226,8 +1293,9 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1226,8 +1293,9 @@ namespace ICSharpCode.Decompiler.CSharp
var resultExpr = new BinaryOperatorExpression(left.Expression, op, right.Expression)
.WithILInstruction(inst)
.WithRR(rr);
if (BinaryOperatorMightCheckForOverflow(op))
if (BinaryOperatorMightCheckForOverflow(op) && !inst.UnderlyingResultType.IsFloatType()) {
resultExpr.Expression.AddAnnotation(inst.CheckForOverflow ? AddCheckedBlocks.CheckedAnnotation : AddCheckedBlocks.UncheckedAnnotation);
}
return resultExpr;
}
@ -1326,7 +1394,13 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1326,7 +1394,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);
@ -1342,7 +1416,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1342,7 +1416,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)
@ -1421,10 +1495,15 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1421,10 +1495,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;
@ -1478,15 +1557,21 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1478,15 +1557,21 @@ namespace ICSharpCode.Decompiler.CSharp
.WithILInstruction(inst)
.WithRR(new OperatorResolveResult(target.Type, AssignmentExpression.GetLinqNodeType(op, inst.CheckForOverflow), target.ResolveResult, value.ResolveResult));
}
if (AssignmentOperatorMightCheckForOverflow(op))
if (AssignmentOperatorMightCheckForOverflow(op) && !inst.UnderlyingResultType.IsFloatType()) {
resultExpr.Expression.AddAnnotation(inst.CheckForOverflow ? AddCheckedBlocks.CheckedAnnotation : AddCheckedBlocks.UncheckedAnnotation);
}
return resultExpr;
}
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
@ -1641,6 +1726,10 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1641,6 +1726,10 @@ namespace ICSharpCode.Decompiler.CSharp
} else if (inst.TargetType == IL.PrimitiveType.Ref) {
// converting to unknown ref-type
targetType = new ByReferenceType(compilation.FindType(KnownTypeCode.Byte));
} else if (inst.TargetType == IL.PrimitiveType.None) {
// convert to some object type
// (e.g. invalid I4->O conversion)
targetType = compilation.FindType(KnownTypeCode.Object);
} else {
targetType = GetType(inst.TargetType.ToKnownTypeCode());
}
@ -1691,7 +1780,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1691,7 +1780,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;
}
@ -1823,16 +1912,22 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1823,16 +1912,22 @@ 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++;
@ -1897,46 +1992,46 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1897,46 +1992,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));
}
}
@ -1975,7 +2070,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1975,7 +2070,7 @@ namespace ICSharpCode.Decompiler.CSharp
protected internal override TranslatedExpression VisitLdLen(LdLen inst, TranslationContext context)
{
TranslatedExpression arrayExpr = Translate(inst.Array);
TranslatedExpression arrayExpr = Translate(inst.Array, typeHint: compilation.FindType(KnownTypeCode.Array));
if (arrayExpr.Type.Kind != TypeKind.Array) {
arrayExpr = arrayExpr.ConvertTo(compilation.FindType(KnownTypeCode.Array), this);
}
@ -2015,14 +2110,16 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2015,14 +2110,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);
}
@ -2036,7 +2133,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2036,7 +2133,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));
}
}
@ -2044,7 +2141,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2044,7 +2141,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)
@ -2059,7 +2156,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2059,7 +2156,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)
@ -2099,8 +2196,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2099,8 +2196,7 @@ namespace ICSharpCode.Decompiler.CSharp
// try via its effective base class.
arg = arg.ConvertTo(((ITypeParameter)targetType).EffectiveBaseClass, this);
}
}
else {
} else {
// Before unboxing arg must be a object
arg = arg.ConvertTo(compilation.FindType(KnownTypeCode.Object), this);
}
@ -2117,7 +2213,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2117,7 +2213,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)
@ -2177,7 +2273,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2177,7 +2273,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)
@ -2190,8 +2286,6 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2190,8 +2286,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:
@ -2526,19 +2620,6 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2526,19 +2620,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.
@ -2587,7 +2668,9 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2587,7 +2668,9 @@ namespace ICSharpCode.Decompiler.CSharp
var rr = resolver.ResolveBinaryOperator(BinaryOperatorType.NullCoalescing, value.ResolveResult, fallback.ResolveResult);
if (rr.IsError) {
IType targetType;
if (!value.Type.Equals(SpecialType.NullType) && !fallback.Type.Equals(SpecialType.NullType) && !value.Type.Equals(fallback.Type)) {
if (fallback.Expression is ThrowExpression && fallback.Type.Equals(SpecialType.NoType)) {
targetType = NullableType.GetUnderlyingType(value.Type);
} else if (!value.Type.Equals(SpecialType.NullType) && !fallback.Type.Equals(SpecialType.NullType) && !value.Type.Equals(fallback.Type)) {
targetType = compilation.FindType(inst.UnderlyingResultType.ToKnownTypeCode());
} else {
targetType = value.Type.Equals(SpecialType.NullType) ? fallback.Type : value.Type;
@ -2676,7 +2759,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2676,7 +2759,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)
@ -2696,7 +2779,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2696,7 +2779,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)
@ -2808,7 +2891,6 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2808,7 +2891,6 @@ namespace ICSharpCode.Decompiler.CSharp
{
Debug.Assert(!argumentInfo.HasFlag(CSharpArgumentInfoFlags.NamedArgument));
Debug.Assert(!argumentInfo.HasFlag(CSharpArgumentInfoFlags.IsOut));
Debug.Assert(!argumentInfo.HasFlag(CSharpArgumentInfoFlags.Constant));
if (argumentInfo.HasFlag(CSharpArgumentInfoFlags.IsStaticType) && IL.Transforms.TransformExpressionTrees.MatchGetTypeFromHandle(inst, out var callTargetType)) {
return new TypeReferenceExpression(ConvertType(callTargetType))
@ -2851,7 +2933,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2851,7 +2933,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));
@ -2860,16 +2942,16 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2860,16 +2942,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);
}
@ -3035,7 +3117,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -3035,7 +3117,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);

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

@ -1313,10 +1313,15 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -1313,10 +1313,15 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
}
WriteCommaSeparatedList(attributeSection.Attributes);
WriteToken(Roles.RBracket);
if (attributeSection.Parent is ParameterDeclaration || attributeSection.Parent is TypeParameterDeclaration) {
switch (attributeSection.Parent) {
case ParameterDeclaration _:
case TypeParameterDeclaration _:
case ComposedType _:
Space();
} else {
break;
default:
NewLine();
break;
}
EndNode(attributeSection);
}
@ -1884,6 +1889,25 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -1884,6 +1889,25 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
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)
{
StartNode(whileStatement);
@ -2173,21 +2197,26 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -2173,21 +2197,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);
@ -2325,6 +2354,11 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -2325,6 +2354,11 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
public virtual void VisitComposedType(ComposedType composedType)
{
StartNode(composedType);
if (composedType.Attributes.Any()) {
foreach (var attr in composedType.Attributes) {
attr.AcceptVisitor(this);
}
}
if (composedType.HasRefSpecifier) {
WriteKeyword(ComposedType.RefRole);
}

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:

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

@ -115,6 +115,9 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -115,6 +115,9 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
if (c != Conversion.None)
return c;
}
if (resolveResult is ThrowResolveResult) {
return Conversion.ThrowExpressionConversion;
}
if (allowUserDefined && allowTuple) {
// if allowUserDefined and allowTuple are true, we might as well use the cache
c = ImplicitConversion(resolveResult.Type, toType);
@ -647,7 +650,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -647,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
@ -1018,7 +1021,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -1018,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 {
@ -1027,7 +1030,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -1027,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;
@ -1071,9 +1074,9 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -1071,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);
}
@ -1132,11 +1135,11 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -1132,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 {

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

@ -49,20 +49,6 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -49,20 +49,6 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
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;
}
}
public CSharpInvocationResolveResult(
ResolveResult targetResult, IParameterizedMember member,
IList<ResolveResult> arguments,

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;

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

@ -179,7 +179,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -179,7 +179,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
/// 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
}
}

16
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);
}
@ -571,6 +571,10 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -571,6 +571,10 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
{
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)

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

@ -35,11 +35,15 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -35,11 +35,15 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{
public class ComposedType : AstType
{
public static readonly Role<AttributeSection> AttributeRole = EntityDeclaration.AttributeRole;
public static readonly TokenRole RefRole = new TokenRole("ref");
public static readonly TokenRole ReadonlyRole = new TokenRole("readonly");
public static readonly TokenRole NullableRole = new TokenRole("?");
public static readonly TokenRole PointerRole = new TokenRole("*");
public static readonly Role<ArraySpecifier> ArraySpecifierRole = new Role<ArraySpecifier>("ArraySpecifier");
public AstNodeCollection<AttributeSection> Attributes {
get { return base.GetChildrenByRole(AttributeRole); }
}
/// <summary>
/// Gets/sets whether this type has a 'ref' specifier.
@ -83,6 +87,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -83,6 +87,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);

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

@ -391,6 +391,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -391,6 +391,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
VisitChildren (variableDeclarationStatement);
}
public virtual void VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement)
{
VisitChildren(localFunctionDeclarationStatement);
}
public virtual void VisitWhileStatement (WhileStatement whileStatement)
{
VisitChildren (whileStatement);
@ -1038,6 +1043,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1038,6 +1043,11 @@ 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);
@ -1685,6 +1695,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1685,6 +1695,11 @@ 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);

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

@ -251,12 +251,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -251,12 +251,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
/// <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,

4
ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/Constraint.cs

@ -36,9 +36,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -36,9 +36,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public class Constraint : AstNode
{
public override NodeType NodeType {
get {
return NodeType.Unknown;
}
get { return NodeType.Unknown; }
}
public CSharpTokenNode WhereKeyword {

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

7
ICSharpCode.Decompiler/CSharp/Syntax/SyntaxExtensions.cs

@ -75,5 +75,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -75,5 +75,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
node.Remove();
return node;
}
public static Expression UnwrapInDirectionExpression(this Expression expr)
{
if (!(expr is DirectionExpression dir && dir.FieldDirection == FieldDirection.In))
return expr;
return dir.Expression.Detach();
}
}
}

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>

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

@ -25,7 +25,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -25,7 +25,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public abstract class EntityDeclaration : AstNode
{
public static readonly Role<AttributeSection> AttributeRole = new Role<AttributeSection>("Attribute");
public static readonly Role<AttributeSection> UnattachedAttributeRole = new Role<AttributeSection>("UnattachedAttribute");
public static readonly Role<CSharpModifierToken> ModifierRole = new Role<CSharpModifierToken>("Modifier");
public static readonly Role<AstType> PrivateImplementationTypeRole = new Role<AstType>("PrivateImplementationType", AstType.Null);

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

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

@ -27,12 +27,12 @@ @@ -27,12 +27,12 @@
namespace ICSharpCode.Decompiler.CSharp.Syntax
{
public enum ParameterModifier {
public enum ParameterModifier
{
None,
Ref,
Out,
Params,
This,
In
}
@ -101,6 +101,25 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -101,6 +101,25 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
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 {

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

@ -527,12 +527,18 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -527,12 +527,18 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{
Attribute attr = new Attribute();
attr.Type = ConvertAttributeType(attribute.AttributeType);
SimpleType st = attr.Type as SimpleType;
MemberType mt = attr.Type as MemberType;
if (st != null && st.Identifier.EndsWith("Attribute", StringComparison.Ordinal)) {
switch (attr.Type) {
case SimpleType st:
if (st.Identifier.EndsWith("Attribute", StringComparison.Ordinal))
st.Identifier = st.Identifier.Substring(0, st.Identifier.Length - 9);
} else if (mt != null && mt.MemberName.EndsWith("Attribute", StringComparison.Ordinal)) {
break;
case MemberType mt:
if (mt.MemberName.EndsWith("Attribute", StringComparison.Ordinal))
mt.MemberName = mt.MemberName.Substring(0, mt.MemberName.Length - 9);
break;
}
if (AddResolveResultAnnotations && attribute.Constructor != null) {
attr.AddAnnotation(new MemberResolveResult(null, attribute.Constructor));
}
var parameters = attribute.Constructor?.Parameters ?? EmptyList<IParameter>.Instance;
for (int i = 0; i < attribute.FixedArguments.Length; i++) {
@ -1633,8 +1639,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1633,8 +1639,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) {
@ -1779,7 +1785,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1779,7 +1785,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
Constraint ConvertTypeParameterConstraint(ITypeParameter tp)
{
if (!tp.HasDefaultConstructorConstraint && !tp.HasReferenceTypeConstraint && !tp.HasValueTypeConstraint && tp.DirectBaseTypes.All(IsObjectOrValueType)) {
if (!tp.HasDefaultConstructorConstraint && !tp.HasReferenceTypeConstraint && !tp.HasValueTypeConstraint && tp.NullabilityConstraint != Nullability.NotNullable && tp.DirectBaseTypes.All(IsObjectOrValueType)) {
return null;
}
Constraint c = new Constraint();
@ -1796,10 +1802,22 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1796,10 +1802,22 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
} else {
c.BaseTypes.Add(new PrimitiveType("struct"));
}
} else if (tp.NullabilityConstraint == Nullability.NotNullable) {
c.BaseTypes.Add(new PrimitiveType("notnull"));
}
foreach (TypeConstraint t in tp.TypeConstraints) {
if (!IsObjectOrValueType(t.Type) || t.Attributes.Count > 0) {
AstType astType = ConvertType(t.Type);
if (t.Attributes.Count > 0) {
var attrSection = new AttributeSection();
attrSection.Attributes.AddRange(t.Attributes.Select(ConvertAttribute));
astType = new ComposedType {
Attributes = { attrSection },
BaseType = astType
};
}
c.BaseTypes.Add(astType);
}
foreach (IType t in tp.DirectBaseTypes) {
if (!IsObjectOrValueType(t))
c.BaseTypes.Add(ConvertType(t));
}
if (tp.HasDefaultConstructorConstraint && !tp.HasValueTypeConstraint) {
c.BaseTypes.Add(new PrimitiveType("new"));

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

4
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) {
@ -291,7 +291,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -291,7 +291,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
}
}
bool VariableNeedsDeclaration(VariableKind kind)
internal static bool VariableNeedsDeclaration(VariableKind kind)
{
switch (kind) {
case VariableKind.PinnedLocal:

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)

14
ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs

@ -90,10 +90,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -90,10 +90,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
if (mre == null || IsNullConditional(mre.Target))
return null;
switch (mre.MemberName) {
case "Select":
{
case "Select": {
if (invocation.Arguments.Count != 1)
return null;
if (!IsComplexQuery(mre))
return null;
ParameterDeclaration parameter;
Expression body;
if (MatchSimpleLambda(invocation.Arguments.Single(), out parameter, out body)) {
@ -158,6 +159,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -158,6 +159,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
{
if (invocation.Arguments.Count != 1)
return null;
if (!IsComplexQuery(mre))
return null;
ParameterDeclaration parameter;
Expression body;
if (MatchSimpleLambda(invocation.Arguments.Single(), out parameter, out body)) {
@ -175,6 +178,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -175,6 +178,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
{
if (invocation.Arguments.Count != 1)
return null;
if (!IsComplexQuery(mre))
return null;
ParameterDeclaration parameter;
Expression orderExpression;
if (MatchSimpleLambda(invocation.Arguments.Single(), out parameter, out orderExpression)) {
@ -250,6 +255,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -250,6 +255,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
}
}
static bool IsComplexQuery(MemberReferenceExpression mre)
{
return ((mre.Target is InvocationExpression && mre.Parent is InvocationExpression) || mre.Parent?.Parent is QueryClause);
}
QueryFromClause MakeFromClause(ParameterDeclaration parameter, Expression body)
{
QueryFromClause fromClause = new QueryFromClause {

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

@ -153,7 +153,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -153,7 +153,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
Statements = {
new Repeat(new AnyNode("statement")),
new NamedNode(
"increment",
"iterator",
new ExpressionStatement(
new AssignmentExpression {
Left = new Backreference("ident"),
@ -180,6 +180,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -180,6 +180,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
if (variable != m3.Get<IdentifierExpression>("ident").Single().GetILVariable())
return null;
WhileStatement loop = (WhileStatement)next;
// Cannot convert to for loop, if any variable that is used in the "iterator" part of the pattern,
// will be declared in the body of the while-loop.
var iteratorStatement = m3.Get<Statement>("iterator").Single();
if (IteratorVariablesDeclaredInsideLoopBody(iteratorStatement))
return null;
// Cannot convert to for loop, because that would change the semantics of the program.
// continue in while jumps to the condition block.
// Whereas continue in for jumps to the increment block.
@ -193,7 +198,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -193,7 +198,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
forStatement.CopyAnnotationsFrom(loop);
forStatement.Initializers.Add(node);
forStatement.Condition = loop.Condition.Detach();
forStatement.Iterators.Add(m3.Get<Statement>("increment").Single().Detach());
forStatement.Iterators.Add(iteratorStatement.Detach());
forStatement.EmbeddedStatement = newBody;
loop.ReplaceWith(forStatement);
return forStatement;
@ -216,6 +221,18 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -216,6 +221,18 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
return true;
return false;
}
bool IteratorVariablesDeclaredInsideLoopBody(Statement iteratorStatement)
{
foreach (var id in iteratorStatement.DescendantsAndSelf.OfType<IdentifierExpression>()) {
var v = id.GetILVariable();
if (v == null || !DeclareVariables.VariableNeedsDeclaration(v.Kind))
continue;
if (declareVariables.GetDeclarationPoint(v).Parent == iteratorStatement.Parent)
return true;
}
return false;
}
#endregion
#region foreach
@ -467,6 +484,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -467,6 +484,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 +794,16 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -776,8 +794,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;
@ -976,24 +1002,26 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -976,24 +1002,26 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
/// <summary>
/// Use associativity of logic operators to avoid parentheses.
/// </summary>
public override AstNode VisitBinaryOperatorExpression(BinaryOperatorExpression boe1)
public override AstNode VisitBinaryOperatorExpression(BinaryOperatorExpression expr)
{
switch (boe1.Operator) {
switch (expr.Operator) {
case BinaryOperatorType.ConditionalAnd:
case BinaryOperatorType.ConditionalOr:
// a && (b && c) ==> (a && b) && c
var boe2 = boe1.Right as BinaryOperatorExpression;
if (boe2 != null && boe2.Operator == boe1.Operator) {
// make boe2 the parent and boe1 the child
var b = boe2.Left.Detach();
boe1.ReplaceWith(boe2.Detach());
boe2.Left = boe1;
boe1.Right = b;
return base.VisitBinaryOperatorExpression(boe2);
var bAndC = expr.Right as BinaryOperatorExpression;
if (bAndC != null && bAndC.Operator == expr.Operator) {
// make bAndC the parent and expr the child
var b = bAndC.Left.Detach();
var c = bAndC.Right.Detach();
expr.ReplaceWith(bAndC.Detach());
bAndC.Left = expr;
bAndC.Right = c;
expr.Right = b;
return base.VisitBinaryOperatorExpression(bAndC);
}
break;
}
return base.VisitBinaryOperatorExpression(boe1);
return base.VisitBinaryOperatorExpression(expr);
}
#endregion
}

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

@ -60,7 +60,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -60,7 +60,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
invocationExpression.Arguments.Clear(); // detach arguments from invocationExpression
Expression expr = arguments[0];
for (int i = 1; i < arguments.Length; i++) {
expr = new BinaryOperatorExpression(expr, BinaryOperatorType.Add, arguments[i]);
expr = new BinaryOperatorExpression(expr, BinaryOperatorType.Add, arguments[i].UnwrapInDirectionExpression());
}
expr.CopyAnnotationsFrom(invocationExpression);
invocationExpression.ReplaceWith(expr);
@ -116,28 +116,48 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -116,28 +116,48 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
if (bop != null && arguments.Length == 2) {
invocationExpression.Arguments.Clear(); // detach arguments from invocationExpression
invocationExpression.ReplaceWith(
new BinaryOperatorExpression(arguments[0], bop.Value, arguments[1]).CopyAnnotationsFrom(invocationExpression)
new BinaryOperatorExpression(
arguments[0].UnwrapInDirectionExpression(),
bop.Value,
arguments[1].UnwrapInDirectionExpression()
).CopyAnnotationsFrom(invocationExpression)
);
return;
}
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].UnwrapInDirectionExpression().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)
new UnaryOperatorExpression(uop.Value, arguments[0].UnwrapInDirectionExpression()).CopyAnnotationsFrom(invocationExpression)
);
return;
}
if (method.Name == "op_Explicit" && arguments.Length == 1) {
arguments[0].Remove(); // detach argument
invocationExpression.ReplaceWith(
new CastExpression(context.TypeSystemAstBuilder.ConvertType(method.ReturnType), arguments[0])
new CastExpression(context.TypeSystemAstBuilder.ConvertType(method.ReturnType), arguments[0].UnwrapInDirectionExpression())
.CopyAnnotationsFrom(invocationExpression)
);
return;
}
if (method.Name == "op_True" && arguments.Length == 1 && invocationExpression.Role == Roles.Condition) {
invocationExpression.ReplaceWith(arguments[0]);
invocationExpression.ReplaceWith(arguments[0].UnwrapInDirectionExpression());
return;
}
@ -154,8 +174,9 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -154,8 +174,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)

50
ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs

@ -176,6 +176,17 @@ namespace ICSharpCode.Decompiler.CSharp @@ -176,6 +176,17 @@ namespace ICSharpCode.Decompiler.CSharp
///
/// From the caller's perspective, IntPtr/UIntPtr behave like normal C# integers except that they have native int size.
/// All the special cases necessary to make IntPtr/UIntPtr behave sanely are handled internally in ConvertTo().
///
/// Post-condition:
/// The "expected evaluation result" is the value computed by <c>this.Expression</c>,
/// converted to targetType via an IL conv instruction.
///
/// ConvertTo(targetType, allowImplicitConversion=false).Type must be equal to targetType (modulo identity conversions).
/// The value computed by the converted expression must match the "expected evaluation result".
///
/// ConvertTo(targetType, allowImplicitConversion=true) must produce an expression that,
/// when evaluated in a context where it will be implicitly converted to targetType,
/// evaluates to the "expected evaluation result".
/// </remarks>
public TranslatedExpression ConvertTo(IType targetType, ExpressionBuilder expressionBuilder, bool checkForOverflow = false, bool allowImplicitConversion = false)
{
@ -191,7 +202,11 @@ namespace ICSharpCode.Decompiler.CSharp @@ -191,7 +202,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 +226,21 @@ namespace ICSharpCode.Decompiler.CSharp @@ -211,6 +226,21 @@ 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
}
if (Expression is TupleExpression tupleExpr && targetType is TupleType targetTupleType
&& tupleExpr.Elements.Count == targetTupleType.ElementTypes.Length)
{
@ -231,8 +261,9 @@ namespace ICSharpCode.Decompiler.CSharp @@ -231,8 +261,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)
@ -359,7 +390,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -359,7 +390,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:
@ -379,7 +410,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -379,7 +410,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) {
@ -396,9 +427,16 @@ namespace ICSharpCode.Decompiler.CSharp @@ -396,9 +427,16 @@ namespace ICSharpCode.Decompiler.CSharp
.WithILInstruction(this.ILInstructions)
.WithRR(new ConstantResolveResult(targetType, null));
}
if (allowImplicitConversion && conversions.ImplicitConversion(ResolveResult, targetType).IsValid) {
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();
if (needsCheckAnnotation) {

24
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
{
@ -78,6 +79,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -78,6 +79,12 @@ namespace ICSharpCode.Decompiler.CSharp
/// </summary>
public Guid? ProjectGuid { get; set; }
/// <summary>
/// Path to the snk file to use for signing.
/// <c>null</c> to not sign.
/// </summary>
public string StrongNameKeyFile { get; set; }
public int MaxDegreeOfParallelism { get; set; } = Environment.ProcessorCount;
#endregion
@ -101,7 +108,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -101,7 +108,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 +117,10 @@ namespace ICSharpCode.Decompiler.CSharp @@ -110,7 +117,10 @@ namespace ICSharpCode.Decompiler.CSharp
directories.Clear();
var files = WriteCodeFilesInProject(moduleDefinition, cancellationToken).ToList();
files.AddRange(WriteResourceFilesInProject(moduleDefinition));
WriteProjectFile(projectFileWriter, files, moduleDefinition);
if (StrongNameKeyFile != null) {
File.Copy(StrongNameKeyFile, Path.Combine(targetDirectory, Path.GetFileName(StrongNameKeyFile)));
}
return WriteProjectFile(projectFileWriter, files, moduleDefinition);
}
enum LanguageTargets
@ -120,11 +130,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -120,11 +130,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();
@ -214,6 +225,11 @@ namespace ICSharpCode.Decompiler.CSharp @@ -214,6 +225,11 @@ namespace ICSharpCode.Decompiler.CSharp
w.WriteElementString("WarningLevel", "4");
w.WriteElementString("AllowUnsafeBlocks", "True");
if (StrongNameKeyFile != null) {
w.WriteElementString("SignAssembly", "True");
w.WriteElementString("AssemblyOriginatorKeyFile", Path.GetFileName(StrongNameKeyFile));
}
w.WriteEndElement(); // </PropertyGroup>
w.WriteStartElement("PropertyGroup"); // platform-specific
@ -281,6 +297,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -281,6 +297,8 @@ namespace ICSharpCode.Decompiler.CSharp
w.WriteEndDocument();
}
return new ProjectId(platformName, guid);
}
protected virtual bool IsGacAssembly(Metadata.IAssemblyReference r, Metadata.PEFile asm)

10
ICSharpCode.Decompiler/DecompilerException.cs

@ -21,6 +21,7 @@ using System.Diagnostics; @@ -21,6 +21,7 @@ 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;
@ -44,7 +45,7 @@ namespace ICSharpCode.Decompiler @@ -44,7 +45,7 @@ namespace ICSharpCode.Decompiler
public PEFile File { get; }
public DecompilerException(MetadataModule module, IEntity decompiledEntity, Exception innerException, string message = null)
: base(message ?? "Error decompiling " + decompiledEntity?.FullName, innerException)
: base(message ?? GetDefaultMessage(decompiledEntity), innerException)
{
this.File = module.PEFile;
this.Module = module;
@ -57,6 +58,13 @@ namespace ICSharpCode.Decompiler @@ -57,6 +58,13 @@ namespace ICSharpCode.Decompiler
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)
{

67
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -88,6 +88,7 @@ namespace ICSharpCode.Decompiler @@ -88,6 +88,7 @@ namespace ICSharpCode.Decompiler
}
if (languageVersion < CSharp.LanguageVersion.CSharp7) {
outVariables = false;
throwExpressions = false;
tupleTypes = false;
tupleConversions = false;
discards = false;
@ -97,6 +98,7 @@ namespace ICSharpCode.Decompiler @@ -97,6 +98,7 @@ namespace ICSharpCode.Decompiler
introduceReadonlyAndInModifiers = false;
introduceRefModifiersOnStructs = false;
nonTrailingNamedArguments = false;
refExtensionMethods = false;
}
if (languageVersion < CSharp.LanguageVersion.CSharp7_3) {
introduceUnmanagedConstraint = false;
@ -114,10 +116,10 @@ namespace ICSharpCode.Decompiler @@ -114,10 +116,10 @@ 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 || tupleTypes || tupleConversions || discards || localFunctions)
if (outVariables || throwExpressions || tupleTypes || tupleConversions || discards || localFunctions)
return CSharp.LanguageVersion.CSharp7;
if (awaitInCatchFinally || useExpressionBodyForCalculatedGetterOnlyProperties || nullPropagation
|| stringInterpolation || dictionaryInitializers || extensionMethodsInCollectionInitializers)
@ -626,6 +628,23 @@ namespace ICSharpCode.Decompiler @@ -626,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>
@ -856,6 +875,23 @@ namespace ICSharpCode.Decompiler @@ -856,6 +875,23 @@ namespace ICSharpCode.Decompiler
}
}
bool throwExpressions = true;
/// <summary>
/// Gets/Sets whether throw expressions should be used.
/// </summary>
[Category("C# 7.0 / VS 2017")]
[Description("DecompilerSettings.UseThrowExpressions")]
public bool ThrowExpressions {
get { return throwExpressions; }
set {
if (throwExpressions != value) {
throwExpressions = value;
OnPropertyChanged();
}
}
}
bool tupleConversions = true;
/// <summary>
@ -942,22 +978,19 @@ namespace ICSharpCode.Decompiler @@ -942,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();
}
}
}
@ -1062,6 +1095,20 @@ namespace ICSharpCode.Decompiler @@ -1062,6 +1095,20 @@ namespace ICSharpCode.Decompiler
}
}
}
bool removeDeadStores = false;
[Category("DecompilerSettings.FSpecificOptions")]
[Description("DecompilerSettings.RemoveDeadStores")]
public bool RemoveDeadStores {
get { return removeDeadStores; }
set {
if (removeDeadStores != value) {
removeDeadStores = value;
OnPropertyChanged();
}
}
}
#endregion
#region Assembly Load and Resolve options

26
ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs

@ -310,7 +310,7 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -310,7 +310,7 @@ namespace ICSharpCode.Decompiler.Disassembler
}
foreach (var p in methodDefinition.GetGenericParameters()) {
WriteGenericParameterAttributes(module, p);
WriteGenericParameterAttributes(module, genericContext, p);
}
foreach (var p in methodDefinition.GetParameters()) {
WriteParameterAttributes(module, p);
@ -1005,15 +1005,28 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -1005,15 +1005,28 @@ namespace ICSharpCode.Decompiler.Disassembler
output.WriteLine();
}
void WriteGenericParameterAttributes(PEFile module, GenericParameterHandle handle)
void WriteGenericParameterAttributes(PEFile module, GenericContext context, GenericParameterHandle handle)
{
var metadata = module.Metadata;
var p = metadata.GetGenericParameter(handle);
if (p.GetCustomAttributes().Count == 0)
return;
if (p.GetCustomAttributes().Count > 0) {
output.Write(".param type {0}", metadata.GetString(p.Name));
output.WriteLine();
output.Indent();
WriteAttributes(module, p.GetCustomAttributes());
output.Unindent();
}
foreach (var constraintHandle in p.GetConstraints()) {
var constraint = metadata.GetGenericParameterConstraint(constraintHandle);
if (constraint.GetCustomAttributes().Count > 0) {
output.Write(".param constraint {0}, ", metadata.GetString(p.Name));
constraint.Type.WriteTo(module, output, context, ILNameSyntax.TypeName);
output.WriteLine();
output.Indent();
WriteAttributes(module, constraint.GetCustomAttributes());
output.Unindent();
}
}
}
void WriteParameterAttributes(PEFile module, ParameterHandle handle)
@ -1028,7 +1041,9 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -1028,7 +1041,9 @@ namespace ICSharpCode.Decompiler.Disassembler
WriteConstant(metadata, metadata.GetConstant(p.GetDefaultValue()));
}
output.WriteLine();
output.Indent();
WriteAttributes(module, p.GetCustomAttributes());
output.Unindent();
}
void WriteConstant(MetadataReader metadata, Constant constant)
@ -1378,6 +1393,9 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -1378,6 +1393,9 @@ namespace ICSharpCode.Decompiler.Disassembler
isInType = true;
WriteAttributes(module, typeDefinition.GetCustomAttributes());
WriteSecurityDeclarations(module, typeDefinition.GetDeclarativeSecurityAttributes());
foreach (var tp in typeDefinition.GetGenericParameters()) {
WriteGenericParameterAttributes(module, genericContext, tp);
}
var layout = typeDefinition.GetLayout();
if (!layout.IsDefault) {
output.WriteLine(".pack {0}", layout.PackingSize);

121
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);
@ -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
{
@ -118,6 +121,9 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -118,6 +121,9 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
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)
{
cancellationToken.ThrowIfCancellationRequested();
@ -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);
}
}
}

15
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -16,9 +16,10 @@ @@ -16,9 +16,10 @@
<EnableDefaultItems>false</EnableDefaultItems>
<LangVersion>7.2</LangVersion>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<SignAssembly>True</SignAssembly>
<AssemblyOriginatorKeyFile>ICSharpCode.Decompiler.snk</AssemblyOriginatorKeyFile>
<NoWarn>1701;1702;1591;1573</NoWarn>
</PropertyGroup>
<!-- HACK: Disable package generation on Unix due to tooling issues. -->
@ -61,6 +62,9 @@ @@ -61,6 +62,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 +165,7 @@ @@ -161,6 +165,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 +227,9 @@ @@ -222,8 +227,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 +278,7 @@ @@ -272,6 +278,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" />
@ -374,6 +381,7 @@ @@ -374,6 +381,7 @@
<Compile Include="TypeSystem\ModifiedType.cs" />
<Compile Include="TypeSystem\Implementation\PinnedType.cs" />
<Compile Include="Metadata\MetadataExtensions.cs" />
<Compile Include="Semantics\ThrowResolveResult.cs" />
<Compile Include="Semantics\TupleResolveResult.cs" />
<Compile Include="TypeSystem\NormalizeTypeVisitor.cs" />
<Compile Include="TypeSystem\Nullability.cs" />
@ -433,7 +441,6 @@ @@ -433,7 +441,6 @@
<Compile Include="IL\Transforms\IILTransform.cs" />
<Compile Include="IL\Transforms\ILInlining.cs" />
<Compile Include="IL\Transforms\ExpressionTransforms.cs" />
<Compile Include="IL\Transforms\LoopingTransform.cs" />
<Compile Include="IL\Transforms\DelegateConstruction.cs" />
<Compile Include="IL\Transforms\RemoveDeadVariableInit.cs" />
<Compile Include="IL\Transforms\SplitVariables.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>

48
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();
@ -355,28 +354,24 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -355,28 +354,24 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
throw new SymbolicAnalysisFailedException();
if (blockContainer.EntryPoint.IncomingEdgeCount != 1)
throw new SymbolicAnalysisFailedException();
cachedStateVar = null;
int pos = 0;
if (blockContainer.EntryPoint.Instructions[0].MatchStLoc(out cachedStateVar, out var cachedStateInit)) {
// stloc(cachedState, ldfld(valuetype StateMachineStruct::<>1__state, ldloc(this)))
if (!cachedStateInit.MatchLdFld(out var target, out var loadedField))
throw new SymbolicAnalysisFailedException();
if (!target.MatchLdThis())
throw new SymbolicAnalysisFailedException();
if (loadedField.MemberDefinition != stateField)
throw new SymbolicAnalysisFailedException();
++pos;
}
while (blockContainer.EntryPoint.Instructions[pos] is StLoc stloc) {
// stloc V_1(ldfld <>4__this(ldloc this))
if (!stloc.Variable.IsSingleDefinition)
throw new SymbolicAnalysisFailedException();
if (!stloc.Value.MatchLdFld(out var target, out var field))
throw new SymbolicAnalysisFailedException();
if (!target.MatchLdThis())
throw new SymbolicAnalysisFailedException();
if (!fieldToParameterMap.TryGetValue((IField)field.MemberDefinition, out var param))
if (field.MemberDefinition == stateField && cachedStateVar == null) {
// stloc(cachedState, ldfld(valuetype StateMachineStruct::<>1__state, ldloc(this)))
cachedStateVar = stloc.Variable;
} else if (fieldToParameterMap.TryGetValue((IField)field.MemberDefinition, out var param)) {
if (!stloc.Variable.IsSingleDefinition)
throw new SymbolicAnalysisFailedException();
cachedFieldToParameterMap[stloc.Variable] = param;
} else {
throw new SymbolicAnalysisFailedException();
}
pos++;
}
mainTryCatch = blockContainer.EntryPoint.Instructions[pos] as TryCatch;
@ -735,15 +730,21 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -735,15 +730,21 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// if (call get_IsCompleted(ldloca awaiterVar)) br completedBlock
if (!block.Instructions[block.Instructions.Count - 2].MatchIfInstruction(out var condition, out var trueInst))
return;
if (!MatchCall(condition, "get_IsCompleted", out var isCompletedArgs) || isCompletedArgs.Count != 1)
return;
if (!isCompletedArgs[0].MatchLdLocRef(awaiterVar))
return;
if (!trueInst.MatchBranch(out var completedBlock))
return;
// br awaitBlock
if (!block.Instructions.Last().MatchBranch(out var awaitBlock))
return;
// condition might be inverted, swap branches:
if (condition.MatchLogicNot(out var negatedCondition)) {
condition = negatedCondition;
ExtensionMethods.Swap(ref completedBlock, ref awaitBlock);
}
// continue matching call get_IsCompleted(ldloca awaiterVar)
if (!MatchCall(condition, "get_IsCompleted", out var isCompletedArgs) || isCompletedArgs.Count != 1)
return;
if (!isCompletedArgs[0].MatchLdLocRef(awaiterVar))
return;
// Check awaitBlock and resumeBlock:
if (!awaitBlocks.TryGetValue(awaitBlock, out var awaitBlockData))
return;
@ -902,17 +903,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -902,17 +903,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.RemoveDeadStores);
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);

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

@ -28,10 +28,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -28,10 +28,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// <summary>
/// The variable to be used as the argument of the switch instruction.
/// </summary>
public ILVariable SwitchVariable
{
get { return switchVar; }
}
public ILVariable SwitchVariable => switchVar;
/// <summary>
/// Whether at least one the analyzed blocks contained an IL switch constructors.
@ -62,6 +59,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -62,6 +59,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
public Block RootBlock { get; private set; }
/// <summary>
/// Gets/sets whether to allow unreachable cases in switch instructions.
/// </summary>
public bool AllowUnreachableCases { get; set; }
/// <summary>
/// Analyze the last two statements in the block and see if they can be turned into a
/// switch instruction.
@ -180,6 +182,8 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -180,6 +182,8 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
foreach (var section in inst.Sections) {
var matchValues = section.Labels.AddOffset(offset).IntersectWith(inputValues);
if (!AllowUnreachableCases && matchValues.IsEmpty)
return false;
if (matchValues.Count() > 1 && section.Body.MatchBranch(out var targetBlock) && AnalyzeBlock(targetBlock, matchValues)) {
InnerBlocks.Add(targetBlock);
} else {

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

@ -132,6 +132,8 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -132,6 +132,8 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
{
this.context = context;
analysis.AllowUnreachableCases = context.Settings.RemoveDeadCode;
foreach (var container in function.Descendants.OfType<BlockContainer>()) {
currentContainer = container;
controlFlowGraph = null;
@ -158,13 +160,13 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -158,13 +160,13 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
void ProcessBlock(Block block, ref bool blockContainerNeedsCleanup)
{
bool analysisSuccess = analysis.AnalyzeBlock(block);
KeyValuePair<LongSet, ILInstruction> defaultSection;
if (analysisSuccess && UseCSharpSwitch(out defaultSection)) {
if (analysisSuccess && UseCSharpSwitch(out _)) {
// complex multi-block switch that can be combined into a single SwitchInstruction
ILInstruction switchValue = new LdLoc(analysis.SwitchVariable);
if (switchValue.ResultType == StackType.Unknown) {
Debug.Assert(switchValue.ResultType.IsIntegerType() || switchValue.ResultType == StackType.Unknown);
if (!(switchValue.ResultType == StackType.I4 || switchValue.ResultType == StackType.I8)) {
// switchValue must have a result type of either I4 or I8
switchValue = new Conv(switchValue, PrimitiveType.I8, false, TypeSystem.Sign.Signed);
switchValue = new Conv(switchValue, PrimitiveType.I8, false, Sign.Signed);
}
var sw = new SwitchInstruction(switchValue);
foreach (var section in analysis.Sections) {
@ -441,7 +443,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -441,7 +443,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)

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

Loading…
Cancel
Save