Browse Source

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

pull/1596/head
Siegfried Pammer 6 years ago
parent
commit
3c185f2845
  1. 6
      ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs
  2. 1
      ICSharpCode.Decompiler.Tests/Helpers/RemoveCompilerAttribute.cs
  3. 10
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  4. 12
      ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs
  5. 6
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  6. 6
      ICSharpCode.Decompiler.Tests/RoundtripAssembly.cs
  7. 76
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/LocalFunctions.cs
  8. 14
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/DirectCallToExplicitInterfaceImpl.cs
  9. 36
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/DirectCallToExplicitInterfaceImpl.il
  10. 39
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue684.cs
  11. 133
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue684.il
  12. 41
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs
  13. 45
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs
  14. 15
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ConstantsTests.cs
  15. 53
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs
  16. 2
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.cs
  17. 288
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs
  18. 52
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullableRefTypes.cs
  19. 21
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/YieldReturn.cs
  20. 130
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  21. 212
      ICSharpCode.Decompiler/CSharp/CallBuilder.cs
  22. 67
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  23. 19
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
  24. 16
      ICSharpCode.Decompiler/CSharp/Resolver/CSharpInvocationResolveResult.cs
  25. 306
      ICSharpCode.Decompiler/CSharp/Resolver/ReducedExtensionMethod.cs
  26. 6
      ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs
  27. 56
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  28. 6
      ICSharpCode.Decompiler/CSharp/Syntax/ComposedType.cs
  29. 21
      ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs
  30. 3
      ICSharpCode.Decompiler/CSharp/Syntax/IAstVisitor.cs
  31. 110
      ICSharpCode.Decompiler/CSharp/Syntax/Statements/LocalFunctionDeclarationStatement.cs
  32. 2
      ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs
  33. 13
      ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs
  34. 16
      ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs
  35. 11
      ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs
  36. 10
      ICSharpCode.Decompiler/DecompilerException.cs
  37. 13
      ICSharpCode.Decompiler/DecompilerSettings.cs
  38. 5
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  39. 12
      ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs
  40. 13
      ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs
  41. 4
      ICSharpCode.Decompiler/IL/ILReader.cs
  42. 13
      ICSharpCode.Decompiler/IL/ILVariable.cs
  43. 47
      ICSharpCode.Decompiler/IL/Instructions.cs
  44. 14
      ICSharpCode.Decompiler/IL/Instructions.tt
  45. 163
      ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
  46. 36
      ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs
  47. 7
      ICSharpCode.Decompiler/IL/Instructions/NullableInstructions.cs
  48. 12
      ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs
  49. 17
      ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs
  50. 22
      ICSharpCode.Decompiler/IL/Transforms/EarlyExpressionTransforms.cs
  51. 35
      ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs
  52. 65
      ICSharpCode.Decompiler/IL/Transforms/FixRemainingIncrements.cs
  53. 103
      ICSharpCode.Decompiler/IL/Transforms/ILExtraction.cs
  54. 289
      ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs
  55. 2
      ICSharpCode.Decompiler/IL/Transforms/ProxyCallReplacer.cs
  56. 13
      ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs
  57. 20
      ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs
  58. 109
      ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs
  59. 9
      ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs
  60. 4
      ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs
  61. 11
      ICSharpCode.Decompiler/Output/TextTokenWriter.cs
  62. 25
      ICSharpCode.Decompiler/SRMExtensions.cs
  63. 55
      ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs
  64. 6
      ICSharpCode.Decompiler/TypeSystem/IMethod.cs
  65. 6
      ICSharpCode.Decompiler/TypeSystem/ITypeDefinition.cs
  66. 2
      ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs
  67. 1
      ICSharpCode.Decompiler/TypeSystem/Implementation/FakeMember.cs
  68. 2
      ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs
  69. 138
      ICSharpCode.Decompiler/TypeSystem/Implementation/LocalFunctionMethod.cs
  70. 6
      ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataEvent.cs
  71. 3
      ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataField.cs
  72. 21
      ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs
  73. 21
      ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataProperty.cs
  74. 7
      ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs
  75. 10
      ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeParameter.cs
  76. 1
      ICSharpCode.Decompiler/TypeSystem/Implementation/MinimalCorlib.cs
  77. 13
      ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs
  78. 12
      ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs
  79. 7
      ICSharpCode.Decompiler/TypeSystem/Implementation/TypeWithElementType.cs
  80. 10
      ICSharpCode.Decompiler/TypeSystem/Implementation/UnknownType.cs
  81. 14
      ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs
  82. 10
      ICSharpCode.Decompiler/TypeSystem/NormalizeTypeVisitor.cs
  83. 4
      ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs
  84. 2
      ILSpy.BamlDecompiler/Rewrite/ConnectionIdRewritePass.cs
  85. 2
      ILSpy/Languages/ILAstLanguage.cs
  86. 6
      ILSpy/Properties/Resources.Designer.cs
  87. 4
      ILSpy/Properties/Resources.resx
  88. 6
      ILSpy/Properties/Resources.zh-Hans.resx

6
ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs

@ -295,12 +295,6 @@ namespace ICSharpCode.Decompiler.Tests @@ -295,12 +295,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",
};

10
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -42,8 +42,8 @@ @@ -42,8 +42,8 @@
<ItemGroup>
<PackageReference Include="DiffLib" Version="2017.7.26.1241" />
<PackageReference Include="Microsoft.Build.Locator" Version="1.2.2" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.2.0-beta3-final" />
<PackageReference Include="Microsoft.CodeAnalysis.VisualBasic" Version="3.2.0-beta3-final" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.2.0-beta4-final" />
<PackageReference Include="Microsoft.CodeAnalysis.VisualBasic" Version="3.2.0-beta4-final" />
<PackageReference Include="Microsoft.DiaSymReader.Converter.Xml" Version="1.1.0-beta1-63314-01" />
<PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
<PackageReference Include="System.Collections.Immutable" Version="1.5.0" />
@ -61,11 +61,14 @@ @@ -61,11 +61,14 @@
<None Include="TestCases\Correctness\StackTypes.il" />
<None Include="TestCases\Correctness\Uninit.vb" />
<None Include="TestCases\ILPretty\ConstantBlobs.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 +77,7 @@ @@ -74,6 +77,7 @@
<ItemGroup>
<Compile Include="DisassemblerPrettyTestRunner.cs" />
<Compile Include="TestCases\ILPretty\ConstantBlobs.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,7 +87,7 @@ @@ -83,7 +87,7 @@
<Compile Include="Semantics\ExplicitConversionTest.cs" />
<Compile Include="Semantics\OverloadResolutionTests.cs" />
<Compile Include="DataFlowTest.cs" />
<Compile Include="TestCases\Correctness\LocalFunctions.cs" />
<Compile Include="TestCases\Pretty\LocalFunctions.cs" />
<Compile Include="TestCases\Correctness\RefLocalsAndReturns.cs" />
<Compile Include="TestCases\ILPretty\Issue1256.cs" />
<Compile Include="TestCases\ILPretty\Issue1323.cs" />

12
ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs

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

6
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -184,6 +184,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -184,6 +184,12 @@ namespace ICSharpCode.Decompiler.Tests
});
}
[Test]
public void LocalFunctions([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void PropertiesAndEvents([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions)
{

6
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.");
}
RunWithTest("ICSharpCode.Decompiler", "ICSharpCode.Decompiler.dll", "ICSharpCode.Decompiler.Tests.exe");
}
[Test]

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

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

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

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

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

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

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

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

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

@ -4720,5 +4720,50 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -4720,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);
}
}
}

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

@ -146,6 +146,59 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -146,6 +146,59 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
}
public interface IM3
{
void M3();
}
public class BaseClass : IM3
{
protected virtual void M1()
{
}
protected virtual void M2()
{
}
public virtual void M3()
{
}
}
public class SubClass : BaseClass
{
protected override void M2()
{
}
public new void M3()
{
}
public void Test()
{
Noop("M1.base", base.M1);
Noop("M1", M1);
Noop("M2.base", base.M2);
Noop("M2", M2);
Noop("M3.base", base.M3);
Noop("M3.base_virt", ((BaseClass)this).M3);
Noop("M3.base_interface", ((IM3)this).M3);
#if CS70
Noop("M3", this.M3);
Noop("M3", M3);
void M3()
{
}
#else
Noop("M3", M3);
#endif
}
private void Noop(string name, Action _)
{
}
}
public static Func<string, string, bool> test0 = (string a, string b) => string.IsNullOrEmpty(a) || string.IsNullOrEmpty(b);
public static Func<string, string, bool> test1 = (string a, string b) => string.IsNullOrEmpty(a) || !string.IsNullOrEmpty(b);
public static Func<string, string, bool> test2 = (string a, string b) => !string.IsNullOrEmpty(a) || string.IsNullOrEmpty(b);

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

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

@ -0,0 +1,288 @@ @@ -0,0 +1,288 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
namespace LocalFunctions
{
internal class LocalFunctions
{
private int field;
private static void Test(int x)
{
}
private static int GetInt(string a)
{
return a.Length;
}
private static string GetString(int a)
{
return a.ToString();
}
public static void StaticContextNoCapture(int length)
{
for (int i = 0; i < length; i++) {
LocalWrite("Hello " + i);
}
void LocalWrite(string s)
{
Console.WriteLine(s);
}
}
public static void StaticContextSimpleCapture(int length)
{
for (int i = 0; i < length; i++) {
LocalWrite();
}
void LocalWrite()
{
Console.WriteLine("Hello " + length);
}
}
public static void StaticContextCaptureForLoopVariable(int length)
{
int i;
for (i = 0; i < length; i++) {
LocalWrite();
}
void LocalWrite()
{
Console.WriteLine("Hello " + i + "/" + length);
}
}
public void ContextNoCapture()
{
for (int i = 0; i < field; i++) {
LocalWrite("Hello " + i);
}
void LocalWrite(string s)
{
Console.WriteLine(s);
}
}
public void ContextSimpleCapture()
{
for (int i = 0; i < field; i++) {
LocalWrite();
}
void LocalWrite()
{
Console.WriteLine("Hello " + field);
}
}
public void ContextCaptureForLoopVariable()
{
int i;
for (i = 0; i < field; i++) {
LocalWrite();
}
void LocalWrite()
{
Console.WriteLine("Hello " + i + "/" + field);
}
}
public void CapturedOutsideLoop()
{
int i = 0;
while (i < field) {
i = GetInt("asdf");
LocalWrite();
}
void LocalWrite()
{
Console.WriteLine("Hello " + i + "/" + field);
}
}
public void CapturedInForeachLoop(IEnumerable<string> args)
{
foreach (string arg2 in args) {
string arg = arg2;
LocalWrite();
void LocalWrite()
{
Console.WriteLine("Hello " + arg);
}
}
}
public void Overloading()
{
Test(5);
LocalFunctions.Test(2);
void Test(int x)
{
Console.WriteLine("x: {0}", x);
}
}
private void Name()
{
}
private void LocalFunctionHidingMethod()
{
Action action = this.Name;
Name();
action();
void Name()
{
}
}
public void NamedArgument()
{
Use(Get(1), Get(2), Get(3));
Use(Get(1), c: Get(2), b: Get(3));
int Get(int i)
{
return i;
}
void Use(int a, int b, int c)
{
Console.WriteLine(a + b + c);
}
}
public static Func<int> LambdaInLocalFunction()
{
int x = (int)Math.Pow(2.0, 10.0);
return Create();
Func<int> Create()
{
return () => x;
}
}
public static Func<int> MethodRef()
{
int x = (int)Math.Pow(2.0, 10.0);
Enumerable.Range(1, 100).Select(LocalFunction);
return null;
int LocalFunction(int y)
{
return x * y;
}
}
public static int Fib(int i)
{
return FibHelper(i);
int FibHelper(int n)
{
if (n <= 0) {
return 0;
}
return FibHelper(n - 1) + FibHelper(n - 2);
}
}
public int MutuallyRecursiveLocalFunctions()
{
return B(4) + C(3);
int A(int i)
{
if (i > 0) {
return A(i - 1) + 2 * B(i - 1) + 3 * C(i - 1);
}
return 1;
}
int B(int i)
{
if (i > 0) {
return 3 * A(i - 1) + B(i - 1);
}
return 1;
}
int C(int i)
{
if (i > 0) {
return 2 * A(i - 1) + C(i - 1);
}
return 1;
}
}
public static int NestedLocalFunctions(int i)
{
return A();
int A()
{
double x = Math.Pow(10.0, 2.0);
return B();
int B()
{
return i + (int)x;
}
}
}
public static int LocalFunctionInLambda(IEnumerable<int> xs)
{
return xs.First(delegate(int x) {
return Do();
bool Do()
{
return x == 3;
}
});
}
//public static void LocalFunctionInUsing()
//{
// using (MemoryStream memoryStream = new MemoryStream()) {
// Do();
// void Do()
// {
// memoryStream.WriteByte(42);
// }
// }
//}
}
}

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

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

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

130
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -155,7 +155,9 @@ namespace ICSharpCode.Decompiler.CSharp @@ -155,7 +155,9 @@ namespace ICSharpCode.Decompiler.CSharp
}
},
new ProxyCallReplacer(),
new FixRemainingIncrements(),
new DelegateConstruction(),
new LocalFunctionDecompiler(),
new TransformDisplayClassUsage(),
new HighLevelLoopTransform(),
new ReduceNestingTransform(),
@ -396,7 +398,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -396,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;
@ -621,39 +623,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -621,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;
@ -663,10 +633,10 @@ namespace ICSharpCode.Decompiler.CSharp @@ -663,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;
@ -681,9 +651,57 @@ namespace ICSharpCode.Decompiler.CSharp @@ -681,9 +651,57 @@ namespace ICSharpCode.Decompiler.CSharp
case ILOpCode.Ldftn:
// deal with ldftn instructions, i.e., lambdas
token = MetadataTokenHelpers.EntityHandleOrNil(blob.ReadInt32());
if (!token.IsNil && token.Kind == HandleKind.MethodDefinition) {
if (((MethodDefinitionHandle)token).IsCompilerGeneratedOrIsInCompilerGeneratedClass(module.Metadata))
connectedMethods.Enqueue((MethodDefinitionHandle)token);
if (token.IsNil)
continue;
TypeDefinitionHandle closureTypeHandle;
switch (token.Kind) {
case HandleKind.MethodDefinition:
if (((MethodDefinitionHandle)token).IsCompilerGeneratedOrIsInCompilerGeneratedClass(module.Metadata)) {
connectedMethods.Enqueue((MethodDefinitionHandle)token);
}
continue;
case HandleKind.MemberReference:
var memberRef = module.Metadata.GetMemberReference((MemberReferenceHandle)token);
if (memberRef.GetKind() != MemberReferenceKind.Method)
continue;
closureTypeHandle = ExtractDeclaringType(memberRef);
if (!closureTypeHandle.IsNil) {
var closureType = module.Metadata.GetTypeDefinition(closureTypeHandle);
// Must be a nested type of the containing type.
if (closureType.GetDeclaringType() != declaringType)
break;
if (!processedNestedTypes.Add(closureTypeHandle))
break;
foreach (var m in closureType.GetMethods()) {
connectedMethods.Enqueue(m);
}
break;
}
break;
default:
continue;
}
break;
case ILOpCode.Call:
case ILOpCode.Callvirt:
// deal with call/callvirt instructions, i.e., local function invocations
token = MetadataTokenHelpers.EntityHandleOrNil(blob.ReadInt32());
if (token.IsNil)
continue;
switch (token.Kind) {
case HandleKind.MethodDefinition:
break;
case HandleKind.MethodSpecification:
var methodSpec = module.Metadata.GetMethodSpecification((MethodSpecificationHandle)token);
if (methodSpec.Method.IsNil || methodSpec.Method.Kind != HandleKind.MethodDefinition)
continue;
token = methodSpec.Method;
break;
default:
continue;
}
if (LocalFunctionDecompiler.IsLocalFunctionMethod(module, (MethodDefinitionHandle)token)) {
connectedMethods.Enqueue((MethodDefinitionHandle)token);
}
break;
default:
@ -693,6 +711,40 @@ namespace ICSharpCode.Decompiler.CSharp @@ -693,6 +711,40 @@ namespace ICSharpCode.Decompiler.CSharp
}
info.AddMapping(parent, part);
TypeDefinitionHandle ExtractDeclaringType(MemberReference memberRef)
{
switch (memberRef.Parent.Kind) {
case HandleKind.TypeReference:
// This should never happen in normal code, because we are looking at nested types
// If it's not a nested type, it can't be a reference to the state machine or lambda anyway, and
// those should be either TypeDef or TypeSpec.
return default;
case HandleKind.TypeDefinition:
return (TypeDefinitionHandle)memberRef.Parent;
case HandleKind.TypeSpecification:
var ts = module.Metadata.GetTypeSpecification((TypeSpecificationHandle)memberRef.Parent);
if (ts.Signature.IsNil)
return default;
// Do a quick scan using BlobReader
var signature = module.Metadata.GetBlobReader(ts.Signature);
// When dealing with FSM implementations, we can safely assume that if it's a type spec,
// it must be a generic type instance.
if (signature.ReadByte() != (byte)SignatureTypeCode.GenericTypeInstance)
return default;
// Skip over the rawTypeKind: value type or class
var rawTypeKind = signature.ReadCompressedInteger();
if (rawTypeKind < 17 || rawTypeKind > 18)
return default;
// Only read the generic type, ignore the type arguments
var genericType = signature.ReadTypeHandle();
// Again, we assume this is a type def, because we are only looking at nested types
if (genericType.Kind != HandleKind.TypeDefinition)
return default;
return (TypeDefinitionHandle)genericType;
}
return default;
}
}
/// <summary>

212
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

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

67
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;
@ -189,7 +189,35 @@ namespace ICSharpCode.Decompiler.CSharp @@ -189,7 +189,35 @@ namespace ICSharpCode.Decompiler.CSharp
internal bool HidesVariableWithName(string name)
{
return currentFunction.Ancestors.OfType<ILFunction>().SelectMany(f => f.Variables).Any(v => v.Name == name);
return currentFunction.Ancestors.OfType<ILFunction>().Any(HidesVariableOrNestedFunction);
bool HidesVariableOrNestedFunction(ILFunction function)
{
foreach (var v in function.Variables) {
if (v.Name == name)
return true;
}
foreach (var f in function.LocalFunctions.OfType<ILFunction>()) {
if (f.Name == name)
return true;
}
return false;
}
}
internal ILFunction ResolveLocalFunction(IMethod method)
{
Debug.Assert(method.IsLocalFunction);
method = method.ReducedFrom;
foreach (var parent in currentFunction.Ancestors.OfType<ILFunction>()) {
var definition = parent.LocalFunctions.FirstOrDefault(f => f.Method == method);
if (definition != null) {
return definition;
}
}
return null;
}
bool RequiresQualifier(IMember member, TranslatedExpression target)
@ -585,7 +613,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -585,7 +613,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 +633,31 @@ namespace ICSharpCode.Decompiler.CSharp @@ -605,10 +633,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)
@ -1839,7 +1888,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1839,7 +1888,7 @@ 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;
@ -1848,17 +1897,19 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1848,17 +1897,19 @@ namespace ICSharpCode.Decompiler.CSharp
if (string.IsNullOrEmpty(pd.Name) && !pd.Type.IsArgList()) {
// needs to be consistent with logic in ILReader.CreateILVarable(ParameterDefinition)
pd.Name = "P_" + i;
// if this is a local function, we have to skip the parameters for closure references
if (settings.LocalFunctions && function.Kind == ILFunctionKind.LocalFunction && IL.Transforms.LocalFunctionDecompiler.IsClosureParameter(parameter, decompilationContext))
break;
}
if (settings.AnonymousTypes && parameter.Type.ContainsAnonymousType())
pd.Type = null;
ILVariable v;
if (variables.TryGetValue(i, out v))
if (variables.TryGetValue(i, out var v))
pd.AddAnnotation(new ILVariableResolveResult(v, parameters[i].Type));
yield return pd;
i++;
}
}
internal TranslatedExpression TranslateTarget(ILInstruction target, bool nonVirtualInvocation,
bool memberStatic, IType memberDeclaringType)
{

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

@ -1883,6 +1883,25 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -1883,6 +1883,25 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
Semicolon();
EndNode(variableDeclarationStatement);
}
public virtual void VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement)
{
StartNode(localFunctionDeclarationStatement);
WriteModifiers(localFunctionDeclarationStatement.ModifierTokens);
localFunctionDeclarationStatement.ReturnType.AcceptVisitor(this);
Space();
WriteIdentifier(localFunctionDeclarationStatement.NameToken);
WriteTypeParameters(localFunctionDeclarationStatement.TypeParameters);
Space(policy.SpaceBeforeMethodDeclarationParentheses);
WriteCommaSeparatedListInParenthesis(localFunctionDeclarationStatement.Parameters, policy.SpaceWithinMethodDeclarationParentheses);
foreach (Constraint constraint in localFunctionDeclarationStatement.Constraints) {
constraint.AcceptVisitor(this);
}
WriteMethodBody(localFunctionDeclarationStatement.Body, policy.MethodBraceStyle);
EndNode(localFunctionDeclarationStatement);
}
public virtual void VisitWhileStatement(WhileStatement whileStatement)
{

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

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

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

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

@ -570,7 +570,11 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -570,7 +570,11 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
void MakeExactInference(IType U, IType V)
{
Log.WriteLine("MakeExactInference from " + U + " to " + V);
if (V is NullabilityAnnotatedTypeParameter nullableTP) {
V = nullableTP.OriginalTypeParameter;
}
// If V is one of the unfixed Xi then U is added to the set of bounds for Xi.
TP tp = GetTPForType(V);
if (tp != null && tp.IsFixed == false) {

56
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

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

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

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

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

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

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

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

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

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

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

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

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

11
ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs

@ -413,8 +413,15 @@ namespace ICSharpCode.Decompiler.CSharp @@ -413,8 +413,15 @@ namespace ICSharpCode.Decompiler.CSharp
.WithILInstruction(this.ILInstructions)
.WithRR(new ConstantResolveResult(targetType, null));
}
if (allowImplicitConversion && conversions.ImplicitConversion(ResolveResult, targetType).IsValid) {
return this;
if (allowImplicitConversion) {
if (conversions.ImplicitConversion(ResolveResult, targetType).IsValid) {
return this;
}
} else {
if (NormalizeTypeVisitor.RemoveModifiersAndNullability.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();

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

13
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -960,22 +960,19 @@ namespace ICSharpCode.Decompiler @@ -960,22 +960,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();
}
}
}

5
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -165,6 +165,7 @@ @@ -165,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" />
@ -226,7 +227,9 @@ @@ -226,7 +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="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" />

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

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

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

@ -390,7 +390,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -390,7 +390,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
methodTypeParameters: null);
var body = context.TypeSystem.MainModule.PEFile.Reader.GetMethodBody(methodDef.RelativeVirtualAddress);
var il = context.CreateILReader()
.ReadIL(method, body, genericContext, context.CancellationToken);
.ReadIL(method, body, genericContext, ILFunctionKind.TopLevelFunction, context.CancellationToken);
il.RunTransforms(CSharpDecompiler.EarlyILTransforms(true),
new ILTransformContext(il, context.TypeSystem, context.DebugInfo, context.Settings) {
CancellationToken = context.CancellationToken,
@ -810,14 +810,15 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -810,14 +810,15 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
break;
case Leave leave:
if (leave.MatchReturn(out var value)) {
bool validYieldBreak = value.MatchLdcI4(0);
if (value.MatchLdLoc(out var v)
&& (v.Kind == VariableKind.Local || v.Kind == VariableKind.StackSlot)
&& v.StoreInstructions.Count == 1
&& v.StoreInstructions[0] is StLoc stloc) {
returnStores.Add(stloc);
value = stloc.Value;
&& v.StoreInstructions.All(store => store is StLoc stloc && stloc.Value.MatchLdcI4(0)))
{
validYieldBreak = true;
returnStores.AddRange(v.StoreInstructions.Cast<StLoc>());
}
if (value.MatchLdcI4(0)) {
if (validYieldBreak) {
// yield break
leave.ReplaceWith(new Leave(newBody).WithILRange(leave));
} else {

4
ICSharpCode.Decompiler/IL/ILReader.cs

@ -480,14 +480,14 @@ namespace ICSharpCode.Decompiler.IL @@ -480,14 +480,14 @@ namespace ICSharpCode.Decompiler.IL
/// <summary>
/// Decodes the specified method body and returns an ILFunction.
/// </summary>
public ILFunction ReadIL(MethodDefinitionHandle method, MethodBodyBlock body, GenericContext genericContext = default, CancellationToken cancellationToken = default)
public ILFunction ReadIL(MethodDefinitionHandle method, MethodBodyBlock body, GenericContext genericContext = default, ILFunctionKind kind = ILFunctionKind.TopLevelFunction, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
Init(method, body, genericContext);
ReadInstructions(cancellationToken);
var blockBuilder = new BlockBuilder(body, variableByExceptionHandler);
blockBuilder.CreateBlocks(mainContainer, instructionBuilder, isBranchTarget, cancellationToken);
var function = new ILFunction(this.method, body.GetCodeSize(), this.genericContext, mainContainer);
var function = new ILFunction(this.method, body.GetCodeSize(), this.genericContext, mainContainer, kind);
CollectionExtensions.AddRange(function.Variables, parameterVariables);
CollectionExtensions.AddRange(function.Variables, localVariables);
CollectionExtensions.AddRange(function.Variables, stackVariables);

13
ICSharpCode.Decompiler/IL/ILVariable.cs

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

47
ICSharpCode.Decompiler/IL/Instructions.cs

@ -476,7 +476,7 @@ namespace ICSharpCode.Decompiler.IL @@ -476,7 +476,7 @@ namespace ICSharpCode.Decompiler.IL
{
switch (index) {
default:
this.Arguments[index - 0] = value;
this.Arguments[index - 0] = (ILInstruction)value;
break;
}
}
@ -491,7 +491,7 @@ namespace ICSharpCode.Decompiler.IL @@ -491,7 +491,7 @@ namespace ICSharpCode.Decompiler.IL
{
var clone = (CallInstruction)ShallowClone();
clone.Arguments = new InstructionCollection<ILInstruction>(clone, 0);
clone.Arguments.AddRange(this.Arguments.Select(arg => arg.Clone()));
clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone()));
return clone;
}
protected override InstructionFlags ComputeFlags()
@ -742,9 +742,11 @@ namespace ICSharpCode.Decompiler.IL @@ -742,9 +742,11 @@ namespace ICSharpCode.Decompiler.IL
SetChildInstruction(ref this.body, value, 0);
}
}
public static readonly SlotInfo LocalFunctionsSlot = new SlotInfo("LocalFunctions");
public InstructionCollection<ILFunction> LocalFunctions { get; private set; }
protected sealed override int GetChildCount()
{
return 1;
return 1 + LocalFunctions.Count;
}
protected sealed override ILInstruction GetChild(int index)
{
@ -752,7 +754,7 @@ namespace ICSharpCode.Decompiler.IL @@ -752,7 +754,7 @@ namespace ICSharpCode.Decompiler.IL
case 0:
return this.body;
default:
throw new IndexOutOfRangeException();
return this.LocalFunctions[index - 1];
}
}
protected sealed override void SetChild(int index, ILInstruction value)
@ -762,7 +764,8 @@ namespace ICSharpCode.Decompiler.IL @@ -762,7 +764,8 @@ namespace ICSharpCode.Decompiler.IL
this.Body = value;
break;
default:
throw new IndexOutOfRangeException();
this.LocalFunctions[index - 1] = (ILFunction)value;
break;
}
}
protected sealed override SlotInfo GetChildSlot(int index)
@ -771,13 +774,15 @@ namespace ICSharpCode.Decompiler.IL @@ -771,13 +774,15 @@ namespace ICSharpCode.Decompiler.IL
case 0:
return BodySlot;
default:
throw new IndexOutOfRangeException();
return LocalFunctionsSlot;
}
}
public sealed override ILInstruction Clone()
{
var clone = (ILFunction)ShallowClone();
clone.Body = this.body.Clone();
clone.LocalFunctions = new InstructionCollection<ILFunction>(clone, 1);
clone.LocalFunctions.AddRange(this.LocalFunctions.Select(arg => (ILFunction)arg.Clone()));
clone.CloneVariables();
return clone;
}
@ -797,7 +802,7 @@ namespace ICSharpCode.Decompiler.IL @@ -797,7 +802,7 @@ namespace ICSharpCode.Decompiler.IL
protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match)
{
var o = other as ILFunction;
return o != null && this.body.PerformMatch(o.body, ref match);
return o != null && this.body.PerformMatch(o.body, ref match) && Patterns.ListMatch.DoMatch(this.LocalFunctions, o.LocalFunctions, ref match);
}
}
}
@ -4254,7 +4259,7 @@ namespace ICSharpCode.Decompiler.IL @@ -4254,7 +4259,7 @@ namespace ICSharpCode.Decompiler.IL
{
switch (index) {
default:
this.Indices[index - 0] = value;
this.Indices[index - 0] = (ILInstruction)value;
break;
}
}
@ -4269,7 +4274,7 @@ namespace ICSharpCode.Decompiler.IL @@ -4269,7 +4274,7 @@ namespace ICSharpCode.Decompiler.IL
{
var clone = (NewArr)ShallowClone();
clone.Indices = new InstructionCollection<ILInstruction>(clone, 0);
clone.Indices.AddRange(this.Indices.Select(arg => arg.Clone()));
clone.Indices.AddRange(this.Indices.Select(arg => (ILInstruction)arg.Clone()));
return clone;
}
public override StackType ResultType { get { return StackType.O; } }
@ -4607,7 +4612,7 @@ namespace ICSharpCode.Decompiler.IL @@ -4607,7 +4612,7 @@ namespace ICSharpCode.Decompiler.IL
this.Array = value;
break;
default:
this.Indices[index - 1] = value;
this.Indices[index - 1] = (ILInstruction)value;
break;
}
}
@ -4625,7 +4630,7 @@ namespace ICSharpCode.Decompiler.IL @@ -4625,7 +4630,7 @@ namespace ICSharpCode.Decompiler.IL
var clone = (LdElema)ShallowClone();
clone.Array = this.array.Clone();
clone.Indices = new InstructionCollection<ILInstruction>(clone, 1);
clone.Indices.AddRange(this.Indices.Select(arg => arg.Clone()));
clone.Indices.AddRange(this.Indices.Select(arg => (ILInstruction)arg.Clone()));
return clone;
}
public bool DelayExceptions; // NullReferenceException/IndexOutOfBoundsException only occurs when the reference is dereferenced
@ -5578,7 +5583,7 @@ namespace ICSharpCode.Decompiler.IL @@ -5578,7 +5583,7 @@ namespace ICSharpCode.Decompiler.IL
{
switch (index) {
default:
this.Arguments[index - 0] = value;
this.Arguments[index - 0] = (ILInstruction)value;
break;
}
}
@ -5593,7 +5598,7 @@ namespace ICSharpCode.Decompiler.IL @@ -5593,7 +5598,7 @@ namespace ICSharpCode.Decompiler.IL
{
var clone = (DynamicGetIndexInstruction)ShallowClone();
clone.Arguments = new InstructionCollection<ILInstruction>(clone, 0);
clone.Arguments.AddRange(this.Arguments.Select(arg => arg.Clone()));
clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone()));
return clone;
}
protected override InstructionFlags ComputeFlags()
@ -5646,7 +5651,7 @@ namespace ICSharpCode.Decompiler.IL @@ -5646,7 +5651,7 @@ namespace ICSharpCode.Decompiler.IL
{
switch (index) {
default:
this.Arguments[index - 0] = value;
this.Arguments[index - 0] = (ILInstruction)value;
break;
}
}
@ -5661,7 +5666,7 @@ namespace ICSharpCode.Decompiler.IL @@ -5661,7 +5666,7 @@ namespace ICSharpCode.Decompiler.IL
{
var clone = (DynamicSetIndexInstruction)ShallowClone();
clone.Arguments = new InstructionCollection<ILInstruction>(clone, 0);
clone.Arguments.AddRange(this.Arguments.Select(arg => arg.Clone()));
clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone()));
return clone;
}
protected override InstructionFlags ComputeFlags()
@ -5714,7 +5719,7 @@ namespace ICSharpCode.Decompiler.IL @@ -5714,7 +5719,7 @@ namespace ICSharpCode.Decompiler.IL
{
switch (index) {
default:
this.Arguments[index - 0] = value;
this.Arguments[index - 0] = (ILInstruction)value;
break;
}
}
@ -5729,7 +5734,7 @@ namespace ICSharpCode.Decompiler.IL @@ -5729,7 +5734,7 @@ namespace ICSharpCode.Decompiler.IL
{
var clone = (DynamicInvokeMemberInstruction)ShallowClone();
clone.Arguments = new InstructionCollection<ILInstruction>(clone, 0);
clone.Arguments.AddRange(this.Arguments.Select(arg => arg.Clone()));
clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone()));
return clone;
}
protected override InstructionFlags ComputeFlags()
@ -5782,7 +5787,7 @@ namespace ICSharpCode.Decompiler.IL @@ -5782,7 +5787,7 @@ namespace ICSharpCode.Decompiler.IL
{
switch (index) {
default:
this.Arguments[index - 0] = value;
this.Arguments[index - 0] = (ILInstruction)value;
break;
}
}
@ -5797,7 +5802,7 @@ namespace ICSharpCode.Decompiler.IL @@ -5797,7 +5802,7 @@ namespace ICSharpCode.Decompiler.IL
{
var clone = (DynamicInvokeConstructorInstruction)ShallowClone();
clone.Arguments = new InstructionCollection<ILInstruction>(clone, 0);
clone.Arguments.AddRange(this.Arguments.Select(arg => arg.Clone()));
clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone()));
return clone;
}
protected override InstructionFlags ComputeFlags()
@ -5850,7 +5855,7 @@ namespace ICSharpCode.Decompiler.IL @@ -5850,7 +5855,7 @@ namespace ICSharpCode.Decompiler.IL
{
switch (index) {
default:
this.Arguments[index - 0] = value;
this.Arguments[index - 0] = (ILInstruction)value;
break;
}
}
@ -5865,7 +5870,7 @@ namespace ICSharpCode.Decompiler.IL @@ -5865,7 +5870,7 @@ namespace ICSharpCode.Decompiler.IL
{
var clone = (DynamicInvokeInstruction)ShallowClone();
clone.Arguments = new InstructionCollection<ILInstruction>(clone, 0);
clone.Arguments.AddRange(this.Arguments.Select(arg => arg.Clone()));
clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone()));
return clone;
}
protected override InstructionFlags ComputeFlags()

14
ICSharpCode.Decompiler/IL/Instructions.tt

@ -49,7 +49,8 @@ @@ -49,7 +49,8 @@
VoidResult, NoArguments, CustomWriteTo),
new OpCode("ILFunction", "A container of IL blocks.",
CustomChildren(new [] {
new ChildInfo("body")
new ChildInfo("body"),
new ChildInfo("localFunctions") { IsCollection = true, Type = "ILFunction" }
}), CustomConstructor, CustomWriteTo, CustomComputeFlags, CustomVariableName("function"), ResultType("O")
),
new OpCode("BlockContainer", "A container of IL blocks.",
@ -773,6 +774,7 @@ namespace ICSharpCode.Decompiler.IL @@ -773,6 +774,7 @@ namespace ICSharpCode.Decompiler.IL
public readonly string SlotName;
public bool IsCollection;
public string Type = "ILInstruction";
public bool CanInlineInto;
public string[] ExpectedTypes;
@ -818,7 +820,7 @@ namespace ICSharpCode.Decompiler.IL @@ -818,7 +820,7 @@ namespace ICSharpCode.Decompiler.IL
childCount = children.Length - 1;
opCode.Flags.Add(argProp + ".Aggregate(InstructionFlags.None, (f, arg) => f | arg.Flags)");
opCode.ConstructorParameters.Add("params ILInstruction[] " + arg);
opCode.ConstructorBody.Add("this." + argProp + " = new InstructionCollection<ILInstruction>(this, " + i + ");");
opCode.ConstructorBody.Add("this." + argProp + " = new InstructionCollection<" + children[i].Type + ">(this, " + i + ");");
opCode.ConstructorBody.Add("this." + argProp + ".AddRange(" + arg + ");");
opCode.PerformMatchConditions.Add("Patterns.ListMatch.DoMatch(this." + argProp + ", o." + argProp + ", ref match)");
if (i == 0)
@ -831,7 +833,7 @@ namespace ICSharpCode.Decompiler.IL @@ -831,7 +833,7 @@ namespace ICSharpCode.Decompiler.IL
opCode.WriteArguments.Add("\t" + arg + ".WriteTo(output, options);");
opCode.WriteArguments.Add("}");
opCode.Members.Add("public static readonly SlotInfo " + children[i].SlotName + " = " + children[i].GetSlotInit() + ";");
opCode.Members.Add("public InstructionCollection<ILInstruction> " + argProp + " { get; private set; }");
opCode.Members.Add("public InstructionCollection<" + children[i].Type + "> " + argProp + " { get; private set; }");
} else {
opCode.Flags.Add(arg + ".Flags");
opCode.ConstructorParameters.Add("ILInstruction " + arg);
@ -910,7 +912,7 @@ namespace ICSharpCode.Decompiler.IL @@ -910,7 +912,7 @@ namespace ICSharpCode.Decompiler.IL
if (collection == null)
b.AppendLine("\t\t\tthrow new IndexOutOfRangeException();");
else {
b.AppendLine("\t\t\tthis." + collection.PropertyName + "[index - " + childCount + "] = value;");
b.AppendLine("\t\t\tthis." + collection.PropertyName + "[index - " + childCount + "] = (" + collection.Type + ")value;");
b.AppendLine("\t\t\tbreak;");
}
b.AppendLine("\t}");
@ -940,8 +942,8 @@ namespace ICSharpCode.Decompiler.IL @@ -940,8 +942,8 @@ namespace ICSharpCode.Decompiler.IL
b.AppendLine("\tvar clone = (" + opCode.Name + ")ShallowClone();");
for (int i = 0; i < children.Length; i++) {
if (children[i].IsCollection) {
b.AppendLine("\tclone." + children[i].PropertyName + " = new InstructionCollection<ILInstruction>(clone, " + i + ");");
b.AppendLine("\tclone." + children[i].PropertyName + ".AddRange(this." + children[i].PropertyName + ".Select(arg => arg.Clone()));");
b.AppendLine("\tclone." + children[i].PropertyName + " = new InstructionCollection<" + children[i].Type + ">(clone, " + i + ");");
b.AppendLine("\tclone." + children[i].PropertyName + ".AddRange(this." + children[i].PropertyName + ".Select(arg => (" + children[i].Type + ")arg.Clone()));");
} else {
b.AppendLine("\tclone." + children[i].PropertyName + " = this." + children[i].Name + ".Clone();");
}

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

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

36
ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs

@ -19,6 +19,7 @@ @@ -19,6 +19,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using ICSharpCode.Decompiler.IL.Patterns;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
@ -758,6 +759,41 @@ namespace ICSharpCode.Decompiler.IL @@ -758,6 +759,41 @@ namespace ICSharpCode.Decompiler.IL
}
return false;
}
/// <summary>
/// Extracts the this instruction.
/// The instruction is replaced with a load of a new temporary variable;
/// and the instruction is moved to a store to said variable at block-level.
/// Returns the new variable.
///
/// If extraction is not possible, the ILAst is left unmodified and the function returns null.
/// May return null if extraction is not possible.
/// </summary>
public ILVariable Extract()
{
return Transforms.ExtractionContext.Extract(this);
}
/// <summary>
/// Prepares "extracting" a descendant instruction out of this instruction.
/// This is the opposite of ILInlining. It may involve re-compiling high-level constructs into lower-level constructs.
/// </summary>
/// <returns>True if extraction is possible; false otherwise.</returns>
internal virtual bool PrepareExtract(int childIndex, Transforms.ExtractionContext ctx)
{
if (!GetChildSlot(childIndex).CanInlineInto) {
return false;
}
// Check whether re-ordering with predecessors is valid:
for (int i = childIndex - 1; i >= 0; --i) {
ILInstruction predecessor = GetChild(i);
if (!GetChildSlot(i).CanInlineInto) {
return false;
}
ctx.RegisterMoveIfNecessary(predecessor);
}
return true;
}
}
public interface IInstructionWithTypeOperand

7
ICSharpCode.Decompiler/IL/Instructions/NullableInstructions.cs

@ -18,6 +18,7 @@ @@ -18,6 +18,7 @@
using System.Diagnostics;
using System.Linq;
using ICSharpCode.Decompiler.IL.Transforms;
namespace ICSharpCode.Decompiler.IL
{
@ -125,5 +126,11 @@ namespace ICSharpCode.Decompiler.IL @@ -125,5 +126,11 @@ namespace ICSharpCode.Decompiler.IL
return StackType.O;
}
}
internal override bool PrepareExtract(int childIndex, ExtractionContext ctx)
{
return base.PrepareExtract(childIndex, ctx)
&& (ctx.FlagsBeingMoved & InstructionFlags.MayUnwrapNull) == 0;
}
}
}

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

@ -162,6 +162,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -162,6 +162,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
break;
}
}
foreach (var localFunction in function.LocalFunctions) {
if (!LocalFunctionDecompiler.ParseLocalFunctionName(localFunction.Name, out _, out var newName) || !IsValidName(newName))
newName = null;
localFunction.Name = newName;
}
// Now generate names:
var mapping = new Dictionary<ILVariable, string>(ILVariableEqualityComparer.Instance);
foreach (var inst in function.Descendants.OfType<IInstructionWithVariableOperand>()) {
@ -174,6 +179,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -174,6 +179,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms
v.Name = name;
}
}
foreach (var localFunction in function.LocalFunctions) {
var newName = localFunction.Name;
if (newName == null) {
newName = GetAlternativeName("f");
}
localFunction.Name = newName;
}
}
/// <remarks>

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

@ -24,7 +24,10 @@ using ICSharpCode.Decompiler.TypeSystem; @@ -24,7 +24,10 @@ using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL.Transforms
{
public class DelegateConstruction : IILTransform
/// <summary>
///
/// </summary>
class DelegateConstruction : IILTransform
{
ILTransformContext context;
ITypeResolveContext decompilationContext;
@ -121,11 +124,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -121,11 +124,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
var targetMethod = ((IInstructionWithMethodOperand)value.Arguments[1]).Method;
if (!IsAnonymousMethod(decompilationContext.CurrentTypeDefinition, targetMethod))
return null;
if (LocalFunctionDecompiler.IsLocalFunctionMethod(targetMethod.ParentModule.PEFile, (MethodDefinitionHandle)targetMethod.MetadataToken))
return null;
target = value.Arguments[0];
if (targetMethod.MetadataToken.IsNil)
return null;
if (LocalFunctionDecompiler.IsLocalFunctionMethod(targetMethod, context))
return null;
target = value.Arguments[0];
var methodDefinition = context.PEFile.Metadata.GetMethodDefinition((MethodDefinitionHandle)targetMethod.MetadataToken);
if (!methodDefinition.HasBody())
return null;
@ -134,12 +137,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -134,12 +137,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return null;
var ilReader = context.CreateILReader();
var body = context.PEFile.Reader.GetMethodBody(methodDefinition.RelativeVirtualAddress);
var function = ilReader.ReadIL((MethodDefinitionHandle)targetMethod.MetadataToken, body, genericContext.Value, context.CancellationToken);
var function = ilReader.ReadIL((MethodDefinitionHandle)targetMethod.MetadataToken, body, genericContext.Value, ILFunctionKind.Delegate, context.CancellationToken);
function.DelegateType = value.Method.DeclaringType;
function.CheckInvariant(ILPhase.Normal);
// Embed the lambda into the parent function's ILAst, so that "Show steps" can show
// how the lambda body is being transformed.
value.ReplaceWith(function);
function.CheckInvariant(ILPhase.Normal);
var contextPrefix = targetMethod.Name;
foreach (ILVariable v in function.Variables.Where(v => v.Kind != VariableKind.Parameter)) {
@ -169,7 +172,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -169,7 +172,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// Replaces loads of 'this' with the target expression.
/// Async delegates use: ldobj(ldloca this).
/// </summary>
class ReplaceDelegateTargetVisitor : ILVisitor
internal class ReplaceDelegateTargetVisitor : ILVisitor
{
readonly ILVariable thisVariable;
readonly ILInstruction target;

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

@ -66,6 +66,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -66,6 +66,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
protected internal override void VisitLdObj(LdObj inst)
{
base.VisitLdObj(inst);
AddressOfLdLocToLdLoca(inst, context);
LdObjToLdLoc(inst, context);
}
@ -83,6 +84,27 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -83,6 +84,27 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false;
}
internal static void AddressOfLdLocToLdLoca(LdObj inst, ILTransformContext context)
{
// ldobj(...(addressof(ldloc V))) where ... can be zero or more ldflda instructions
// =>
// ldobj(...(ldloca V))
var temp = inst.Target;
var range = temp.ILRanges;
while (temp.MatchLdFlda(out var ldfldaTarget, out _)) {
temp = ldfldaTarget;
range = range.Concat(temp.ILRanges);
}
if (temp.MatchAddressOf(out var addressOfTarget) && addressOfTarget.MatchLdLoc(out var v)) {
context.Step($"ldobj(...(addressof(ldloca {v.Name}))) => ldobj(...(ldloca {v.Name}))", inst);
var replacement = new LdLoca(v).WithILRange(addressOfTarget);
foreach (var r in range) {
replacement = replacement.WithILRange(r);
}
temp.ReplaceWith(replacement);
}
}
protected internal override void VisitCall(Call inst)
{
var expr = HandleCall(inst, context);

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

@ -390,10 +390,37 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -390,10 +390,37 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false;
}
bool TransformDecimalFieldToConstant(LdObj inst, out LdcDecimal result)
{
if (inst.MatchLdsFld(out var field) && field.DeclaringType.IsKnownType(KnownTypeCode.Decimal)) {
decimal? value = null;
if (field.Name == "One") {
value = decimal.One;
} else if (field.Name == "MinusOne") {
value = decimal.MinusOne;
} else if (field.Name == "Zero") {
value = decimal.Zero;
}
if (value != null) {
result = new LdcDecimal(value.Value).WithILRange(inst).WithILRange(inst.Target);
return true;
}
}
result = null;
return false;
}
protected internal override void VisitLdObj(LdObj inst)
{
base.VisitLdObj(inst);
EarlyExpressionTransforms.LdObjToLdLoc(inst, context);
EarlyExpressionTransforms.AddressOfLdLocToLdLoca(inst, context);
if (EarlyExpressionTransforms.LdObjToLdLoc(inst, context))
return;
if (TransformDecimalFieldToConstant(inst, out LdcDecimal decimalConstant)) {
context.Step("TransformDecimalFieldToConstant", inst);
inst.ReplaceWith(decimalConstant);
return;
}
}
protected internal override void VisitStObj(StObj inst)
@ -406,6 +433,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -406,6 +433,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
TransformAssignment.HandleCompoundAssign(inst, context);
}
protected internal override void VisitStLoc(StLoc inst)
{
base.VisitStLoc(inst);
TransformAssignment.HandleCompoundAssign(inst, context);
}
protected internal override void VisitIfInstruction(IfInstruction inst)
{
inst.TrueInst.AcceptVisitor(this);

65
ICSharpCode.Decompiler/IL/Transforms/FixRemainingIncrements.cs

@ -0,0 +1,65 @@ @@ -0,0 +1,65 @@
using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL.Transforms
{
public class FixRemainingIncrements : IILTransform
{
void IILTransform.Run(ILFunction function, ILTransformContext context)
{
var callsToFix = new List<Call>();
foreach (var call in function.Descendants.OfType<Call>()) {
if (!(call.Method.IsOperator && (call.Method.Name == "op_Increment" || call.Method.Name == "op_Decrement")))
continue;
if (call.Arguments.Count != 1)
continue;
if (call.Method.DeclaringType.IsKnownType(KnownTypeCode.Decimal)) {
// For decimal, legacy csc can optimize "d + 1m" to "op_Increment(d)".
// We can handle these calls in ReplaceMethodCallsWithOperators.
continue;
}
callsToFix.Add(call);
}
foreach (var call in callsToFix) {
// A user-defined increment/decrement that was not handled by TransformAssignment.
// This can happen because the variable-being-incremented was optimized out by Roslyn,
// e.g.
// public void Issue1552Pre(UserType a, UserType b)
// {
// UserType num = a + b;
// Console.WriteLine(++num);
// }
// can end up being compiled to:
// Console.WriteLine(UserType.op_Increment(a + b));
if (call.SlotInfo == StLoc.ValueSlot && call.Parent.SlotInfo == Block.InstructionSlot) {
var store = (StLoc)call.Parent;
var block = (Block)store.Parent;
context.Step($"Fix {call.Method.Name} call at 0x{call.StartILOffset:x4} using {store.Variable.Name}", call);
// stloc V(call op_Increment(...))
// ->
// stloc V(...)
// compound.assign op_Increment(V)
call.ReplaceWith(call.Arguments[0]);
block.Instructions.Insert(store.ChildIndex + 1,
new UserDefinedCompoundAssign(call.Method, CompoundEvalMode.EvaluatesToNewValue,
new LdLoca(store.Variable), CompoundTargetKind.Address, new LdcI4(1)).WithILRange(call));
} else {
context.Step($"Fix {call.Method.Name} call at 0x{call.StartILOffset:x4} using new local", call);
var newVariable = call.Arguments[0].Extract();
if (newVariable == null) {
Debug.Fail("Failed to extract argument of remaining increment/decrement");
continue;
}
newVariable.Type = call.GetParameter(0).Type;
Debug.Assert(call.Arguments[0].MatchLdLoc(newVariable));
call.ReplaceWith(new UserDefinedCompoundAssign(call.Method, CompoundEvalMode.EvaluatesToNewValue,
new LdLoca(newVariable), CompoundTargetKind.Address, new LdcI4(1)).WithILRange(call));
}
}
}
}
}

103
ICSharpCode.Decompiler/IL/Transforms/ILExtraction.cs

@ -0,0 +1,103 @@ @@ -0,0 +1,103 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
namespace ICSharpCode.Decompiler.IL.Transforms
{
/// <summary>
/// Context object for the ILInstruction.Extract() operation.
/// </summary>
class ExtractionContext
{
/// <summary>
/// Nearest function, used for registering the new locals that are created by extraction.
/// </summary>
readonly ILFunction Function;
/// <summary>
/// Combined flags of all instructions being moved.
/// </summary>
internal InstructionFlags FlagsBeingMoved;
/// <summary>
/// List of actions to be executed when performing the extraction.
///
/// Each function in this list has the side-effect of replacing the instruction-to-be-moved
/// with a load of a fresh temporary variable; and returns the the store to the temporary variable,
/// which will be inserted at block-level.
/// </summary>
readonly List<Func<ILInstruction>> MoveActions = new List<Func<ILInstruction>>();
ExtractionContext(ILFunction function)
{
Debug.Assert(function != null);
this.Function = function;
}
internal void RegisterMove(ILInstruction predecessor)
{
FlagsBeingMoved |= predecessor.Flags;
MoveActions.Add(delegate {
var v = Function.RegisterVariable(VariableKind.StackSlot, predecessor.ResultType);
predecessor.ReplaceWith(new LdLoc(v));
return new StLoc(v, predecessor);
});
}
internal void RegisterMoveIfNecessary(ILInstruction predecessor)
{
if (!CanReorderWithInstructionsBeingMoved(predecessor)) {
RegisterMove(predecessor);
}
}
/// <summary>
/// Currently, <c>predecessor</c> is evaluated before the instructions being moved.
/// If this function returns true, <c>predecessor</c> can stay as-is, despite the move changing the evaluation order.
/// If this function returns false, <c>predecessor</c> will need to also move, to ensure the evaluation order stays unchanged.
/// </summary>
public bool CanReorderWithInstructionsBeingMoved(ILInstruction predecessor)
{
// We could track the instructions being moved and be smarter about unnecessary moves,
// but given the limited scenarios where extraction is used so far,
// this seems unnecessary.
return predecessor.Flags == InstructionFlags.None;
}
/// <summary>
/// Extracts the specified instruction:
/// The instruction is replaced with a load of a new temporary variable;
/// and the instruction is moved to a store to said variable at block-level.
///
/// May return null if extraction is not possible.
/// </summary>
public static ILVariable Extract(ILInstruction instToExtract)
{
var function = instToExtract.Ancestors.OfType<ILFunction>().First();
ExtractionContext ctx = new ExtractionContext(function);
ctx.FlagsBeingMoved = instToExtract.Flags;
ILInstruction inst = instToExtract;
while (inst != null) {
if (inst.Parent is Block block && block.Kind == BlockKind.ControlFlow) {
// We've reached the target block, and extraction is possible all the way.
int insertIndex = inst.ChildIndex;
// Move instToExtract itself:
var v = function.RegisterVariable(VariableKind.StackSlot, instToExtract.ResultType);
instToExtract.ReplaceWith(new LdLoc(v));
block.Instructions.Insert(insertIndex, new StLoc(v, instToExtract));
// Apply the other move actions:
foreach (var moveAction in ctx.MoveActions) {
block.Instructions.Insert(insertIndex, moveAction());
}
return v;
}
if (!inst.Parent.PrepareExtract(inst.ChildIndex, ctx))
return null;
inst = inst.Parent;
}
return null;
}
}
}

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

@ -1,24 +1,292 @@ @@ -1,24 +1,292 @@
using System;
// 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;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
using System.Text;
using System.Text.RegularExpressions;
using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.IL.Transforms
{
/// <summary>
/// Decompiler step for C# 7.0 local functions
/// </summary>
class LocalFunctionDecompiler : IILTransform
{
ILTransformContext context;
ITypeResolveContext resolveContext;
struct LocalFunctionInfo
{
public List<CallInstruction> UseSites;
public ILFunction Definition;
}
/// <summary>
/// The transform works like this:
///
/// <para>
/// local functions can either be used in method calls, i.e., call and callvirt instructions,
/// or can be used as part of the "delegate construction" pattern, i.e., <c>newobj Delegate(&lt;target-expression&gt;, ldftn &lt;method&gt;)</c>.
/// </para>
/// As local functions can be declared practically anywhere, we have to take a look at all use-sites and infer the declaration location from that. Use-sites can be call, callvirt and ldftn instructions.
/// After all use-sites are collected we construct the ILAst of the local function and add it to the parent function.
/// Then all use-sites of the local-function are transformed to a call to the <c>LocalFunctionMethod</c> or a ldftn of the <c>LocalFunctionMethod</c>.
/// In a next step we handle all nested local functions.
/// After all local functions are transformed, we move all local functions that capture any variables to their respective declaration scope.
/// </summary>
public void Run(ILFunction function, ILTransformContext context)
{
throw new NotImplementedException();
if (!context.Settings.LocalFunctions)
return;
this.context = context;
this.resolveContext = new SimpleTypeResolveContext(function.Method);
var localFunctions = new Dictionary<MethodDefinitionHandle, LocalFunctionInfo>();
var cancellationToken = context.CancellationToken;
// Find all local functions declared inside this method, including nested local functions or local functions declared in lambdas.
FindUseSites(function, context, localFunctions);
foreach (var (method, info) in localFunctions) {
cancellationToken.ThrowIfCancellationRequested();
var firstUseSite = info.UseSites[0];
context.StepStartGroup($"Transform " + info.Definition.Name, info.Definition);
try {
var localFunction = info.Definition;
if (!localFunction.Method.IsStatic) {
var target = firstUseSite.Arguments[0];
context.Step($"Replace 'this' with {target}", localFunction);
var thisVar = localFunction.Variables.SingleOrDefault(VariableKindExtensions.IsThis);
localFunction.AcceptVisitor(new DelegateConstruction.ReplaceDelegateTargetVisitor(target, thisVar));
}
foreach (var useSite in info.UseSites) {
context.Step("Transform use site at " + useSite.StartILOffset, useSite);
if (useSite.OpCode == OpCode.NewObj) {
TransformToLocalFunctionReference(localFunction, useSite);
} else {
DetermineCaptureAndDeclarationScope(localFunction, useSite);
TransformToLocalFunctionInvocation(localFunction.ReducedMethod, useSite);
}
}
if (localFunction.DeclarationScope == null) {
localFunction.DeclarationScope = (BlockContainer)function.Body;
} else if (localFunction.DeclarationScope != function.Body && localFunction.DeclarationScope.Parent is ILFunction declaringFunction) {
function.LocalFunctions.Remove(localFunction);
declaringFunction.LocalFunctions.Add(localFunction);
}
} finally {
context.StepEndGroup();
}
}
}
void FindUseSites(ILFunction function, ILTransformContext context, Dictionary<MethodDefinitionHandle, LocalFunctionInfo> localFunctions)
{
foreach (var inst in function.Body.Descendants) {
context.CancellationToken.ThrowIfCancellationRequested();
if (inst is CallInstruction call && !call.Method.IsLocalFunction && IsLocalFunctionMethod(call.Method, context)) {
HandleUseSite(call.Method, call);
} else if (inst is LdFtn ldftn && !ldftn.Method.IsLocalFunction && ldftn.Parent is NewObj newObj && IsLocalFunctionMethod(ldftn.Method, context) && DelegateConstruction.IsDelegateConstruction(newObj)) {
HandleUseSite(ldftn.Method, newObj);
}
}
void HandleUseSite(IMethod targetMethod, CallInstruction inst)
{
if (!localFunctions.TryGetValue((MethodDefinitionHandle)targetMethod.MetadataToken, out var info)) {
context.StepStartGroup($"Read local function '{targetMethod.Name}'", inst);
info = new LocalFunctionInfo() {
UseSites = new List<CallInstruction>() { inst },
Definition = ReadLocalFunctionDefinition(context.Function, targetMethod)
};
localFunctions.Add((MethodDefinitionHandle)targetMethod.MetadataToken, info);
FindUseSites(info.Definition, context, localFunctions);
context.StepEndGroup();
} else {
info.UseSites.Add(inst);
}
}
}
public static bool IsLocalFunctionMethod(PEFile module, MethodDefinitionHandle methodHandle)
ILFunction ReadLocalFunctionDefinition(ILFunction rootFunction, IMethod targetMethod)
{
var methodDefinition = context.PEFile.Metadata.GetMethodDefinition((MethodDefinitionHandle)targetMethod.MetadataToken);
if (!methodDefinition.HasBody())
return null;
var genericContext = DelegateConstruction.GenericContextFromTypeArguments(targetMethod.Substitution);
if (genericContext == null)
return null;
var ilReader = context.CreateILReader();
var body = context.PEFile.Reader.GetMethodBody(methodDefinition.RelativeVirtualAddress);
var function = ilReader.ReadIL((MethodDefinitionHandle)targetMethod.MetadataToken, body, genericContext.Value, ILFunctionKind.LocalFunction, context.CancellationToken);
// Embed the local function into the parent function's ILAst, so that "Show steps" can show
// how the local function body is being transformed.
rootFunction.LocalFunctions.Add(function);
function.DeclarationScope = (BlockContainer)rootFunction.Body;
function.CheckInvariant(ILPhase.Normal);
var nestedContext = new ILTransformContext(context, function);
function.RunTransforms(CSharpDecompiler.GetILTransforms().TakeWhile(t => !(t is LocalFunctionDecompiler)), nestedContext);
function.DeclarationScope = null;
function.ReducedMethod = ReduceToLocalFunction(targetMethod);
return function;
}
static T FindCommonAncestorInstruction<T>(ILInstruction a, ILInstruction b)
where T : ILInstruction
{
var ancestorsOfB = new HashSet<T>(b.Ancestors.OfType<T>());
return a.Ancestors.OfType<T>().FirstOrDefault(ancestorsOfB.Contains);
}
internal static bool IsClosureParameter(IParameter parameter, ITypeResolveContext context)
{
if (!parameter.IsRef)
return false;
var type = ((ByReferenceType)parameter.Type).ElementType.GetDefinition();
return type != null
&& type.Kind == TypeKind.Struct
&& TransformDisplayClassUsage.IsPotentialClosure(context.CurrentTypeDefinition, type);
}
static IType UnwrapByRef(IType type)
{
if (type is ByReferenceType byRef) {
type = byRef.ElementType;
}
return type;
}
internal static ILInstruction GetStatement(ILInstruction inst)
{
while (inst.Parent != null) {
if (inst.Parent is Block b && b.Kind == BlockKind.ControlFlow)
return inst;
inst = inst.Parent;
}
return inst;
}
LocalFunctionMethod ReduceToLocalFunction(IMethod method)
{
int parametersToRemove = 0;
for (int i = method.Parameters.Count - 1; i >= 0; i--) {
if (!IsClosureParameter(method.Parameters[i], resolveContext))
break;
parametersToRemove++;
}
return new LocalFunctionMethod(method, parametersToRemove);
}
static void TransformToLocalFunctionReference(ILFunction function, CallInstruction useSite)
{
useSite.Arguments[0].ReplaceWith(new LdNull().WithILRange(useSite.Arguments[0]));
var fnptr = (IInstructionWithMethodOperand)useSite.Arguments[1];
var replacement = new LdFtn(function.ReducedMethod).WithILRange((ILInstruction)fnptr);
useSite.Arguments[1].ReplaceWith(replacement);
}
void TransformToLocalFunctionInvocation(LocalFunctionMethod reducedMethod, CallInstruction useSite)
{
bool wasInstanceCall = !useSite.Method.IsStatic;
var replacement = new Call(reducedMethod);
int firstArgumentIndex = wasInstanceCall ? 1 : 0;
int argumentCount = useSite.Arguments.Count;
int reducedArgumentCount = argumentCount - (reducedMethod.NumberOfCompilerGeneratedParameters + firstArgumentIndex);
replacement.Arguments.AddRange(useSite.Arguments.Skip(firstArgumentIndex).Take(reducedArgumentCount));
// copy flags
replacement.ConstrainedTo = useSite.ConstrainedTo;
replacement.ILStackWasEmpty = useSite.ILStackWasEmpty;
replacement.IsTail = useSite.IsTail;
// copy IL ranges
replacement.AddILRange(useSite);
if (wasInstanceCall) {
replacement.AddILRange(useSite.Arguments[0]);
}
for (int i = 0; i < reducedMethod.NumberOfCompilerGeneratedParameters; i++) {
replacement.AddILRange(useSite.Arguments[argumentCount - i - 1]);
}
useSite.ReplaceWith(replacement);
}
void DetermineCaptureAndDeclarationScope(ILFunction function, CallInstruction useSite)
{
int firstArgumentIndex = function.Method.IsStatic ? 0 : 1;
for (int i = useSite.Arguments.Count - 1; i >= 0; i--) {
var arg = useSite.Arguments[i];
ILVariable closureVar;
if (!(arg.MatchLdLoc(out closureVar) || arg.MatchLdLoca(out closureVar)))
break;
if (closureVar.Kind == VariableKind.NamedArgument)
break;
if (!TransformDisplayClassUsage.IsPotentialClosure(context, UnwrapByRef(closureVar.Type).GetDefinition()))
break;
if (i - firstArgumentIndex >= 0) {
Debug.Assert(i - firstArgumentIndex < function.Method.Parameters.Count && IsClosureParameter(function.Method.Parameters[i - firstArgumentIndex], resolveContext));
}
if (closureVar.AddressCount == 0 && closureVar.StoreInstructions.Count == 0)
continue;
// determine the capture scope of closureVar and the declaration scope of the function
var instructions = closureVar.StoreInstructions.OfType<ILInstruction>()
.Concat(closureVar.AddressInstructions).OrderBy(inst => inst.StartILOffset);
var additionalScope = BlockContainer.FindClosestContainer(instructions.First());
if (closureVar.CaptureScope == null)
closureVar.CaptureScope = additionalScope;
else
closureVar.CaptureScope = FindCommonAncestorInstruction<BlockContainer>(closureVar.CaptureScope, additionalScope);
if (function.DeclarationScope == null)
function.DeclarationScope = closureVar.CaptureScope;
else
function.DeclarationScope = FindCommonAncestorInstruction<BlockContainer>(function.DeclarationScope, closureVar.CaptureScope);
}
}
internal static bool IsLocalFunctionReference(NewObj inst, ILTransformContext context)
{
if (inst == null || inst.Arguments.Count != 2 || inst.Method.DeclaringType.Kind != TypeKind.Delegate)
return false;
var opCode = inst.Arguments[1].OpCode;
return opCode == OpCode.LdFtn
&& IsLocalFunctionMethod(((IInstructionWithMethodOperand)inst.Arguments[1]).Method, context);
}
public static bool IsLocalFunctionMethod(IMethod method, ILTransformContext context)
{
if (method.MetadataToken.IsNil)
return false;
return IsLocalFunctionMethod(method.ParentModule.PEFile, (MethodDefinitionHandle)method.MetadataToken, context);
}
public static bool IsLocalFunctionMethod(PEFile module, MethodDefinitionHandle methodHandle, ILTransformContext context = null)
{
if (context != null && context.PEFile != module)
return false;
var metadata = module.Metadata;
var method = metadata.GetMethodDefinition(methodHandle);
var declaringType = method.GetDeclaringType();
@ -32,12 +300,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -32,12 +300,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true;
}
public static bool IsLocalFunctionDisplayClass(PEFile module, TypeDefinitionHandle typeHandle)
public static bool IsLocalFunctionDisplayClass(PEFile module, TypeDefinitionHandle typeHandle, ILTransformContext context = null)
{
if (context != null && context.PEFile != module)
return false;
var metadata = module.Metadata;
var type = metadata.GetTypeDefinition(typeHandle);
if ((type.Attributes & TypeAttributes.NestedPrivate) == 0)
if ((type.Attributes & TypeAttributes.VisibilityMask) != TypeAttributes.NestedPrivate)
return false;
if (!type.HasGeneratedName(metadata))
return false;
@ -46,7 +317,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -46,7 +317,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
var declaringType = metadata.GetTypeDefinition(declaringTypeHandle);
foreach (var method in declaringType.GetMethods()) {
if (!IsLocalFunctionMethod(module, method))
if (!IsLocalFunctionMethod(module, method, context))
continue;
var md = metadata.GetMethodDefinition(method);
if (md.DecodeSignature(new FindTypeDecoder(typeHandle), default).ParameterTypes.Any())
@ -60,9 +331,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -60,9 +331,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// Newer Roslyn versions use the format "&lt;callerName&gt;g__functionName|x_y"
/// Older versions use "&lt;callerName&gt;g__functionNamex_y"
/// </summary>
static readonly Regex functionNameRegex = new Regex(@"^<(.*)>g__(.*)\|{0,1}\d+(_\d+)?$", RegexOptions.Compiled);
static readonly Regex functionNameRegex = new Regex(@"^<(.*)>g__([^\|]*)\|{0,1}\d+(_\d+)?$", RegexOptions.Compiled);
static bool ParseLocalFunctionName(string name, out string callerName, out string functionName)
internal static bool ParseLocalFunctionName(string name, out string callerName, out string functionName)
{
callerName = null;
functionName = null;
@ -76,7 +347,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -76,7 +347,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
struct FindTypeDecoder : ISignatureTypeProvider<bool, Unit>
{
TypeDefinitionHandle handle;
readonly TypeDefinitionHandle handle;
public FindTypeDecoder(TypeDefinitionHandle handle)
{

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

@ -36,7 +36,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -36,7 +36,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// partially copied from CSharpDecompiler
var ilReader = context.CreateILReader();
var body = context.PEFile.Reader.GetMethodBody(methodDef.RelativeVirtualAddress);
var proxyFunction = ilReader.ReadIL(handle, body, genericContext.Value, context.CancellationToken);
var proxyFunction = ilReader.ReadIL(handle, body, genericContext.Value, ILFunctionKind.TopLevelFunction, context.CancellationToken);
var transformContext = new ILTransformContext(context, proxyFunction);
proxyFunction.RunTransforms(CSharp.CSharpDecompiler.EarlyILTransforms(), transformContext);
if (!(proxyFunction.Body is BlockContainer blockContainer))

13
ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs

@ -28,6 +28,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -28,6 +28,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// <summary>
/// Constructs compound assignments and inline assignments.
/// </summary>
/// <remarks>
/// This is a statement transform;
/// but some portions are executed as an expression transform instead
/// (with HandleCompoundAssign() as entry point)
/// </remarks>
public class TransformAssignment : IStatementTransform
{
StatementTransformContext context;
@ -294,6 +299,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -294,6 +299,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
ILInstruction newInst;
if (UnwrapSmallIntegerConv(setterValue, out var smallIntConv) is BinaryNumericInstruction binary) {
if (compoundStore is StLoc) {
// transform local variables only for user-defined operators
return false;
}
if (!IsMatchingCompoundLoad(binary.Left, compoundStore, out var target, out var targetKind, out var finalizeMatch, forbiddenVariable: storeInSetter?.Variable))
return false;
if (!ValidateCompoundAssign(binary, smallIntConv, targetType))
@ -335,6 +344,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -335,6 +344,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
newInst = new DynamicCompoundAssign(dynamicBinaryOp.Operation, dynamicBinaryOp.BinderFlags, target, dynamicBinaryOp.LeftArgumentInfo, dynamicBinaryOp.Right, dynamicBinaryOp.RightArgumentInfo, targetKind);
} else if (setterValue is Call concatCall && UserDefinedCompoundAssign.IsStringConcat(concatCall.Method)) {
// setterValue is a string.Concat() invocation
if (compoundStore is StLoc) {
// transform local variables only for user-defined operators
return false;
}
if (concatCall.Arguments.Count != 2)
return false; // for now we only support binary compound assignments
if (!targetType.IsKnownType(KnownTypeCode.String))

20
ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs

@ -216,15 +216,25 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -216,15 +216,25 @@ namespace ICSharpCode.Decompiler.IL.Transforms
case AccessPathKind.Setter:
if (isCollection || !pathStack.Peek().Add(lastElement))
return false;
if (values.Count == 1) {
blockKind = BlockKind.ObjectInitializer;
return true;
}
return false;
if (values.Count != 1 || !IsValidObjectInitializerTarget(currentPath))
return false;
blockKind = BlockKind.ObjectInitializer;
return true;
default:
return false;
}
}
bool IsValidObjectInitializerTarget(List<AccessPathElement> path)
{
if (path.Count == 0)
return true;
var element = path.Last();
var previous = path.SkipLast(1).LastOrDefault();
if (!(element.Member is IProperty p))
return true;
return !p.IsIndexer || (previous.Member?.ReturnType.Equals(element.Member.DeclaringType) == true);
}
}
public enum AccessPathKind

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

@ -27,7 +27,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -27,7 +27,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// <summary>
/// Transforms closure fields to local variables.
///
/// This is a post-processing step of <see cref="DelegateConstruction"/> and <see cref="TransformExpressionTrees"/>.
/// This is a post-processing step of <see cref="DelegateConstruction"/>, <see cref="LocalFunctionDecompiler"/> and <see cref="TransformExpressionTrees"/>.
/// </summary>
class TransformDisplayClassUsage : ILVisitor, IILTransform
{
@ -39,6 +39,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -39,6 +39,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
public ITypeDefinition Definition;
public Dictionary<IField, DisplayClassVariable> Variables;
public BlockContainer CaptureScope;
public ILFunction DeclaringFunction;
}
struct DisplayClassVariable
@ -65,20 +66,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -65,20 +66,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
foreach (var v in f.Variables.ToArray()) {
if (HandleMonoStateMachine(function, v, decompilationContext, f))
continue;
if (!(v.IsSingleDefinition && v.StoreInstructions.SingleOrDefault() is StLoc inst))
continue;
if (IsClosureInit(inst, out ITypeDefinition closureType)) {
// TODO : figure out whether it is a mono compiled closure, without relying on the type name
bool isMono = f.StateMachineCompiledWithMono || closureType.Name.Contains("AnonStorey");
displayClasses.Add(inst.Variable, new DisplayClass {
IsMono = isMono,
Initializer = inst,
Variable = v,
Definition = closureType,
Variables = new Dictionary<IField, DisplayClassVariable>(),
CaptureScope = isMono && IsMonoNestedCaptureScope(closureType) ? null : inst.Variable.CaptureScope
});
instructionsToRemove.Add(inst);
if (IsClosure(v, out ITypeDefinition closureType, out var inst)) {
AddOrUpdateDisplayClass(f, v, closureType, inst, localFunctionClosureParameter: false);
}
if (context.Settings.LocalFunctions && f.Kind == ILFunctionKind.LocalFunction && v.Kind == VariableKind.Parameter && v.Index > -1 && f.Method.Parameters[v.Index.Value] is IParameter p && LocalFunctionDecompiler.IsClosureParameter(p, decompilationContext)) {
AddOrUpdateDisplayClass(f, v, ((ByReferenceType)p.Type).ElementType.GetDefinition(), f.Body, localFunctionClosureParameter: true);
}
}
foreach (var displayClass in displayClasses.Values.OrderByDescending(d => d.Initializer.StartILOffset).ToArray()) {
@ -102,6 +94,59 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -102,6 +94,59 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
}
private void AddOrUpdateDisplayClass(ILFunction f, ILVariable v, ITypeDefinition closureType, ILInstruction inst, bool localFunctionClosureParameter)
{
var displayClass = displayClasses.Values.FirstOrDefault(c => c.Definition == closureType);
// TODO : figure out whether it is a mono compiled closure, without relying on the type name
bool isMono = f.StateMachineCompiledWithMono || closureType.Name.Contains("AnonStorey");
if (displayClass == null) {
displayClasses.Add(v, new DisplayClass {
IsMono = isMono,
Initializer = inst,
Variable = v,
Definition = closureType,
Variables = new Dictionary<IField, DisplayClassVariable>(),
CaptureScope = (isMono && IsMonoNestedCaptureScope(closureType)) || localFunctionClosureParameter ? null : v.CaptureScope,
DeclaringFunction = localFunctionClosureParameter ? f.DeclarationScope.Ancestors.OfType<ILFunction>().First() : f
});
} else {
if (displayClass.CaptureScope == null && !localFunctionClosureParameter)
displayClass.CaptureScope = isMono && IsMonoNestedCaptureScope(closureType) ? null : v.CaptureScope;
displayClass.Variable = v;
displayClass.Initializer = inst;
displayClasses.Add(v, displayClass);
}
}
bool IsClosure(ILVariable variable, out ITypeDefinition closureType, out ILInstruction initializer)
{
closureType = null;
initializer = null;
if (variable.IsSingleDefinition && variable.StoreInstructions.SingleOrDefault() is StLoc inst) {
initializer = inst;
if (IsClosureInit(inst, out closureType)) {
instructionsToRemove.Add(inst);
return true;
}
}
closureType = variable.Type.GetDefinition();
if (context.Settings.LocalFunctions && closureType?.Kind == TypeKind.Struct && variable.HasInitialValue && IsPotentialClosure(this.context, closureType)) {
initializer = LocalFunctionDecompiler.GetStatement(variable.AddressInstructions.OrderBy(i => i.StartILOffset).First());
return true;
}
return false;
}
bool IsClosureInit(StLoc inst, out ITypeDefinition closureType)
{
if (inst.Value is NewObj newObj) {
closureType = newObj.Method.DeclaringTypeDefinition;
return closureType != null && IsPotentialClosure(this.context, newObj);
}
closureType = null;
return false;
}
bool IsOuterClosureReference(IField field)
{
return displayClasses.Values.Any(disp => disp.Definition == field.DeclaringTypeDefinition);
@ -181,6 +226,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -181,6 +226,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return IsPotentialClosure(decompilationContext.CurrentTypeDefinition, inst.Method.DeclaringTypeDefinition);
}
internal static bool IsPotentialClosure(ILTransformContext context, ITypeDefinition potentialDisplayClass)
{
var decompilationContext = new SimpleTypeResolveContext(context.Function.Ancestors.OfType<ILFunction>().Last().Method);
return IsPotentialClosure(decompilationContext.CurrentTypeDefinition, potentialDisplayClass);
}
internal static bool IsPotentialClosure(ITypeDefinition decompiledTypeDefinition, ITypeDefinition potentialDisplayClass)
{
if (potentialDisplayClass == null || !potentialDisplayClass.IsCompilerGeneratedOrIsInCompilerGeneratedClass())
@ -193,6 +244,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -193,6 +244,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true;
}
bool IsDisplayClassLoad(ILInstruction target, out ILVariable variable)
{
if (target.MatchLdLoc(out variable) || target.MatchLdLoca(out variable))
return true;
return false;
}
protected override void Default(ILInstruction inst)
{
foreach (var child in inst.Children) {
@ -209,18 +267,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -209,18 +267,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (inst.Value.MatchLdLoc(out var closureVariable) && displayClasses.TryGetValue(closureVariable, out var displayClass)) {
displayClasses[inst.Variable] = displayClass;
instructionsToRemove.Add(inst);
} else if (inst.Variable.Kind == VariableKind.Local && inst.Variable.IsSingleDefinition && inst.Variable.LoadCount == 0 && inst.Value is StLoc) {
inst.ReplaceWith(inst.Value);
}
}
bool IsClosureInit(StLoc inst, out ITypeDefinition closureType)
{
closureType = null;
if (!(inst.Value is NewObj newObj))
return false;
closureType = newObj.Method.DeclaringTypeDefinition;
return closureType != null && IsPotentialClosure(this.context, newObj);
}
protected internal override void VisitStObj(StObj inst)
{
base.VisitStObj(inst);
@ -231,7 +282,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -231,7 +282,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (!inst.Target.MatchLdFlda(out ILInstruction target, out IField field))
return;
// Get display class info
if (!(target is LdLoc displayClassLoad && displayClasses.TryGetValue(displayClassLoad.Variable, out var displayClass)))
if (!IsDisplayClassLoad(target, out var displayClassLoad) || !displayClasses.TryGetValue(displayClassLoad, out var displayClass))
return;
field = (IField)field.MemberDefinition;
if (displayClass.Variables.TryGetValue(field, out DisplayClassVariable info)) {
@ -247,10 +298,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -247,10 +298,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} else {
Debug.Assert(displayClass.Definition == field.DeclaringTypeDefinition);
// Introduce a fresh variable for the display class field.
v = currentFunction.RegisterVariable(VariableKind.Local, field.Type, field.Name);
if (displayClass.IsMono && displayClass.CaptureScope == null && !IsOuterClosureReference(field)) {
displayClass.CaptureScope = BlockContainer.FindClosestContainer(inst);
}
v = displayClass.DeclaringFunction.RegisterVariable(VariableKind.Local, field.Type, field.Name);
v.CaptureScope = displayClass.CaptureScope;
inst.ReplaceWith(new StLoc(v, inst.Value).WithILRange(inst));
value = new LdLoc(v);
@ -266,7 +317,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -266,7 +317,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (!inst.Target.MatchLdFlda(out var target, out IField field))
return;
// Get display class info
if (!(target is LdLoc displayClassLoad && displayClasses.TryGetValue(displayClassLoad.Variable, out var displayClass)))
if (!IsDisplayClassLoad(target, out var displayClassLoad) || !displayClasses.TryGetValue(displayClassLoad, out var displayClass))
return;
// Get display class variable info
if (!displayClass.Variables.TryGetValue((IField)field.MemberDefinition, out DisplayClassVariable info))
@ -291,13 +342,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -291,13 +342,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (inst.Parent is LdObj || inst.Parent is StObj)
return;
// Get display class info
if (!(inst.Target is LdLoc displayClassLoad && displayClasses.TryGetValue(displayClassLoad.Variable, out var displayClass)))
if (!IsDisplayClassLoad(inst.Target, out var displayClassLoad) || !displayClasses.TryGetValue(displayClassLoad, out var displayClass))
return;
var field = (IField)inst.Field.MemberDefinition;
if (!displayClass.Variables.TryGetValue(field, out DisplayClassVariable info)) {
// Introduce a fresh variable for the display class field.
Debug.Assert(displayClass.Definition == field.DeclaringTypeDefinition);
var v = currentFunction.RegisterVariable(VariableKind.Local, field.Type, field.Name);
var v = displayClass.DeclaringFunction.RegisterVariable(VariableKind.Local, field.Type, field.Name);
v.CaptureScope = displayClass.CaptureScope;
inst.ReplaceWith(new LdLoca(v).WithILRange(inst));
displayClass.Variables.Add(field, new DisplayClassVariable { Value = new LdLoc(v), Variable = v });

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

@ -156,6 +156,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -156,6 +156,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
var returnType = functionType.GetDelegateInvokeMethod()?.ReturnType;
var function = new ILFunction(returnType, parameterList, context.Function.GenericContext, container);
function.DelegateType = functionType;
function.Kind = IsExpressionTree(functionType) ? ILFunctionKind.ExpressionTree : ILFunctionKind.Delegate;
function.Variables.AddRange(parameterVariablesList);
function.AddILRange(instruction);
lambdaStack.Push(function);
@ -196,6 +197,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -196,6 +197,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
void SetExpressionTreeFlag(ILFunction lambda, CallInstruction call)
{
lambda.Kind = IsExpressionTree(call.Method.ReturnType) ? ILFunctionKind.ExpressionTree : ILFunctionKind.Delegate;
lambda.DelegateType = call.Method.ReturnType;
}
@ -340,8 +342,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -340,8 +342,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
return (null, SpecialType.UnknownType);
case ILFunction function:
if (function.IsExpressionTree) {
if (function.Kind == ILFunctionKind.ExpressionTree) {
function.DelegateType = UnwrapExpressionTree(function.DelegateType);
function.Kind = ILFunctionKind.Delegate;
}
return (function, function.DelegateType);
case LdLoc ldloc:
@ -371,6 +374,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -371,6 +374,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
}
bool IsExpressionTree(IType delegateType) => delegateType is ParameterizedType pt
&& pt.FullName == "System.Linq.Expressions.Expression"
&& pt.TypeArguments.Count == 1;
IType UnwrapExpressionTree(IType delegateType)
{
if (delegateType is ParameterizedType pt && pt.FullName == "System.Linq.Expressions.Expression" && pt.TypeArguments.Count == 1) {

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

@ -76,6 +76,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -76,6 +76,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false;
if (!(storeInst.Value.MatchLdNull() || CheckResourceType(storeInst.Variable.Type)))
return false;
if (storeInst.Variable.Kind != VariableKind.Local)
return false;
if (storeInst.Variable.LoadInstructions.Any(ld => !ld.IsDescendantOf(tryFinally)))
return false;
if (storeInst.Variable.AddressInstructions.Any(la => !la.IsDescendantOf(tryFinally) || (la.IsDescendantOf(tryFinally.TryBlock) && !ILInlining.IsUsedAsThisPointerInCall(la))))
@ -125,6 +127,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -125,6 +127,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false;
if (!(storeInst.Value.MatchLdNull() || CheckResourceType(storeInst.Variable.Type)))
return false;
if (storeInst.Variable.Kind != VariableKind.Local)
return false;
if (storeInst.Variable.LoadInstructions.Any(ld => !ld.IsDescendantOf(tryFinally)))
return false;
if (storeInst.Variable.AddressInstructions.Any(la => !la.IsDescendantOf(tryFinally) || (la.IsDescendantOf(tryFinally.TryBlock) && !ILInlining.IsUsedAsThisPointerInCall(la))))

11
ICSharpCode.Decompiler/Output/TextTokenWriter.cs

@ -21,8 +21,10 @@ using System.Collections.Generic; @@ -21,8 +21,10 @@ using System.Collections.Generic;
using System.Linq;
using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.CSharp.OutputVisitor;
using ICSharpCode.Decompiler.CSharp.Resolver;
using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.IL;
using ICSharpCode.Decompiler.Semantics;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler
@ -169,13 +171,18 @@ namespace ICSharpCode.Decompiler @@ -169,13 +171,18 @@ namespace ICSharpCode.Decompiler
return variable;
}
var label = node as LabelStatement;
if (label != null) {
if (node is LabelStatement label) {
var method = nodeStack.Select(nd => nd.GetSymbol() as IMethod).FirstOrDefault(mr => mr != null);
if (method != null)
return method + label.Label;
}
if (node is LocalFunctionDeclarationStatement) {
var localFunction = node.GetResolveResult() as MemberResolveResult;
if (localFunction != null)
return localFunction.Member;
}
return null;
}

25
ICSharpCode.Decompiler/SRMExtensions.cs

@ -1,13 +1,10 @@ @@ -1,13 +1,10 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
using SRM = System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
using ICSharpCode.Decompiler.Util;
using System.Reflection.Metadata.Ecma335;
@ -371,6 +368,28 @@ namespace ICSharpCode.Decompiler @@ -371,6 +368,28 @@ namespace ICSharpCode.Decompiler
{
return attr.GetAttributeType(metadata).IsKnownType(metadata, attrType);
}
public static Nullability? GetNullableContext(this CustomAttributeHandleCollection customAttributes, MetadataReader metadata)
{
foreach (var handle in customAttributes) {
var customAttribute = metadata.GetCustomAttribute(handle);
if (customAttribute.IsKnownAttribute(metadata, KnownAttribute.NullableContext)) {
// Decode
CustomAttributeValue<IType> value;
try {
value = customAttribute.DecodeValue(Metadata.MetadataExtensions.MinimalAttributeTypeProvider);
} catch (BadImageFormatException) {
continue;
} catch (Metadata.EnumUnderlyingTypeResolveException) {
continue;
}
if (value.FixedArguments.Length == 1 && value.FixedArguments[0].Value is byte b && b <= 2) {
return (Nullability)b;
}
}
}
return null;
}
#endregion
public static unsafe SRM.BlobReader GetInitialValue(this FieldDefinition field, PEReader pefile, ICompilation typeSystem)

55
ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs

@ -37,14 +37,19 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -37,14 +37,19 @@ namespace ICSharpCode.Decompiler.TypeSystem
SRM.CustomAttributeHandleCollection? attributes,
SRM.MetadataReader metadata,
TypeSystemOptions options,
Nullability nullableContext,
bool typeChildrenOnly = false)
{
bool hasDynamicAttribute = false;
bool[] dynamicAttributeData = null;
string[] tupleElementNames = null;
bool hasNullableAttribute = false;
Nullability nullability = Nullability.Oblivious;
Nullability nullability;
Nullability[] nullableAttributeData = null;
if ((options & TypeSystemOptions.NullabilityAnnotations) != 0) {
nullability = nullableContext;
} else {
nullability = Nullability.Oblivious;
}
const TypeSystemOptions relevantOptions = TypeSystemOptions.Dynamic | TypeSystemOptions.Tuple | TypeSystemOptions.NullabilityAnnotations;
if (attributes != null && (options & relevantOptions) != 0) {
foreach (var attrHandle in attributes.Value) {
@ -70,7 +75,6 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -70,7 +75,6 @@ namespace ICSharpCode.Decompiler.TypeSystem
}
}
} else if ((options & TypeSystemOptions.NullabilityAnnotations) != 0 && attrType.IsKnownType(metadata, KnownAttribute.Nullable)) {
hasNullableAttribute = true;
var ctor = attr.DecodeValue(Metadata.MetadataExtensions.minimalCorlibTypeProvider);
if (ctor.FixedArguments.Length == 1) {
var arg = ctor.FixedArguments[0];
@ -78,13 +82,15 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -78,13 +82,15 @@ namespace ICSharpCode.Decompiler.TypeSystem
&& values.All(v => v.Value is byte b && b <= 2)) {
nullableAttributeData = values.SelectArray(v => (Nullability)(byte)v.Value);
} else if (arg.Value is byte b && b <= 2) {
nullability = (Nullability)(byte)arg.Value;
nullability = (Nullability)b;
}
}
}
}
}
if (hasDynamicAttribute || hasNullableAttribute || (options & (TypeSystemOptions.Tuple | TypeSystemOptions.KeepModifiers)) != TypeSystemOptions.KeepModifiers) {
if (hasDynamicAttribute || nullability != Nullability.Oblivious || nullableAttributeData != null
|| (options & (TypeSystemOptions.Tuple | TypeSystemOptions.KeepModifiers)) != TypeSystemOptions.KeepModifiers)
{
var visitor = new ApplyAttributeTypeVisitor(
compilation, hasDynamicAttribute, dynamicAttributeData,
options, tupleElementNames,
@ -148,16 +154,21 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -148,16 +154,21 @@ namespace ICSharpCode.Decompiler.TypeSystem
Nullability GetNullability()
{
if (nullabilityTypeIndex < nullableAttributeData?.Length)
return nullableAttributeData[nullabilityTypeIndex];
return nullableAttributeData[nullabilityTypeIndex++];
else
return defaultNullability;
}
void ExpectDummyNullabilityForGenericValueType()
{
var n = GetNullability();
Debug.Assert(n == Nullability.Oblivious);
}
public override IType VisitArrayType(ArrayType type)
{
var nullability = GetNullability();
dynamicTypeIndex++;
nullabilityTypeIndex++;
return base.VisitArrayType(type).ChangeNullability(nullability);
}
@ -181,18 +192,18 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -181,18 +192,18 @@ namespace ICSharpCode.Decompiler.TypeSystem
elementNames = ImmutableArray.CreateRange(extractedValues);
}
tupleTypeIndex += tupleCardinality;
ExpectDummyNullabilityForGenericValueType();
var elementTypes = ImmutableArray.CreateBuilder<IType>(tupleCardinality);
do {
int normalArgCount = Math.Min(type.TypeArguments.Count, TupleType.RestPosition - 1);
for (int i = 0; i < normalArgCount; i++) {
dynamicTypeIndex++;
nullabilityTypeIndex++;
elementTypes.Add(type.TypeArguments[i].AcceptVisitor(this));
}
if (type.TypeArguments.Count == TupleType.RestPosition) {
type = type.TypeArguments.Last() as ParameterizedType;
ExpectDummyNullabilityForGenericValueType();
dynamicTypeIndex++;
nullabilityTypeIndex++;
if (type != null && TupleType.IsTupleCompatible(type, out int nestedCardinality)) {
tupleTypeIndex += nestedCardinality;
} else {
@ -218,11 +229,13 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -218,11 +229,13 @@ namespace ICSharpCode.Decompiler.TypeSystem
// Visit generic type and type arguments.
// Like base implementation, except that it increments dynamicTypeIndex.
var genericType = type.GenericType.AcceptVisitor(this);
if (genericType.IsReferenceType != true && !genericType.IsKnownType(KnownTypeCode.NullableOfT)) {
ExpectDummyNullabilityForGenericValueType();
}
bool changed = type.GenericType != genericType;
var arguments = new IType[type.TypeArguments.Count];
for (int i = 0; i < type.TypeArguments.Count; i++) {
dynamicTypeIndex++;
nullabilityTypeIndex++;
arguments[i] = type.TypeArguments[i].AcceptVisitor(this);
changed = changed || arguments[i] != type.TypeArguments[i];
}
@ -240,8 +253,28 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -240,8 +253,28 @@ namespace ICSharpCode.Decompiler.TypeSystem
else if (dynamicAttributeData[dynamicTypeIndex])
newType = SpecialType.Dynamic;
}
if (type.IsReferenceType == true) {
Nullability nullability = GetNullability();
return newType.ChangeNullability(nullability);
} else {
return newType;
}
}
public override IType VisitOtherType(IType type)
{
type = base.VisitOtherType(type);
if (type.Kind == TypeKind.Unknown && type.IsReferenceType == true) {
Nullability nullability = GetNullability();
type = type.ChangeNullability(nullability);
}
return type;
}
public override IType VisitTypeParameter(ITypeParameter type)
{
Nullability nullability = GetNullability();
return newType.ChangeNullability(nullability);
return type.ChangeNullability(nullability);
}
}
}

6
ICSharpCode.Decompiler/TypeSystem/IMethod.cs

@ -53,6 +53,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -53,6 +53,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
IReadOnlyList<IType> TypeArguments { get; }
bool IsExtensionMethod { get; }
bool IsLocalFunction { get; }
bool IsConstructor { get; }
bool IsDestructor { get; }
bool IsOperator { get; }
@ -81,8 +82,9 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -81,8 +82,9 @@ namespace ICSharpCode.Decompiler.TypeSystem
MethodSemanticsAttributes AccessorKind { get; }
/// <summary>
/// If this method is reduced from an extension method return the original method, <c>null</c> otherwise.
/// A reduced method doesn't contain the extension method parameter. That means that has one parameter less than it's definition.
/// If this method is reduced from an extension method or a local function returns the original method, <c>null</c> otherwise.
/// A reduced method doesn't contain the extension method parameter. That means that it has one parameter less than its definition.
/// A local function doesn't contain compiler-generated method parameters at the end.
/// </summary>
IMethod ReducedFrom { get; }

6
ICSharpCode.Decompiler/TypeSystem/ITypeDefinition.cs

@ -67,5 +67,11 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -67,5 +67,11 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// </summary>
/// <remarks>This property is used to speed up the search for extension methods.</remarks>
bool HasExtensionMethods { get; }
/// <summary>
/// The nullability specified in the [NullableContext] attribute on the type.
/// This serves as default nullability for members of the type that do not have a [Nullable] attribute.
/// </summary>
Nullability NullableContext { get; }
}
}

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

@ -208,6 +208,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -208,6 +208,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
return (options & TypeSystemOptions.UnmanagedConstraints) != 0 && target == SymbolKind.TypeParameter;
case "NullableAttribute":
return (options & TypeSystemOptions.NullabilityAnnotations) != 0;
case "NullableContextAttribute":
return (options & TypeSystemOptions.NullabilityAnnotations) != 0 && (target == SymbolKind.TypeDefinition || target == SymbolKind.Method);
default:
return false;
}

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

@ -139,6 +139,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -139,6 +139,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
IReadOnlyList<IType> IMethod.TypeArguments => TypeParameters;
bool IMethod.IsExtensionMethod => false;
bool IMethod.IsLocalFunction => false;
bool IMethod.IsConstructor => symbolKind == SymbolKind.Constructor;
bool IMethod.IsDestructor => symbolKind == SymbolKind.Destructor;
bool IMethod.IsOperator => symbolKind == SymbolKind.Operator;

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

@ -40,6 +40,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -40,6 +40,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
Dynamic,
TupleElementNames,
Nullable,
NullableContext,
Conditional,
Obsolete,
IsReadOnly,
@ -107,6 +108,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -107,6 +108,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
new TopLevelTypeName("System.Runtime.CompilerServices", nameof(DynamicAttribute)),
new TopLevelTypeName("System.Runtime.CompilerServices", nameof(TupleElementNamesAttribute)),
new TopLevelTypeName("System.Runtime.CompilerServices", "NullableAttribute"),
new TopLevelTypeName("System.Runtime.CompilerServices", "NullableContextAttribute"),
new TopLevelTypeName("System.Diagnostics", nameof(ConditionalAttribute)),
new TopLevelTypeName("System", nameof(ObsoleteAttribute)),
new TopLevelTypeName("System.Runtime.CompilerServices", "IsReadOnlyAttribute"),

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

@ -0,0 +1,138 @@ @@ -0,0 +1,138 @@
// 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;
using System.Collections.Generic;
using System.Reflection;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.TypeSystem.Implementation
{
/// <summary>
/// A local function has zero or more compiler-generated parameters added at the end.
/// </summary>
class LocalFunctionMethod : IMethod
{
readonly IMethod baseMethod;
public LocalFunctionMethod(IMethod baseMethod, int numberOfCompilerGeneratedParameters)
{
this.baseMethod = baseMethod;
this.NumberOfCompilerGeneratedParameters = numberOfCompilerGeneratedParameters;
}
public bool Equals(IMember obj, TypeVisitor typeNormalization)
{
if (!(obj is LocalFunctionMethod other))
return false;
return baseMethod.Equals(other.baseMethod, typeNormalization)
&& NumberOfCompilerGeneratedParameters == other.NumberOfCompilerGeneratedParameters;
}
public override bool Equals(object obj)
{
if (!(obj is LocalFunctionMethod other))
return false;
return baseMethod.Equals(other.baseMethod)
&& NumberOfCompilerGeneratedParameters == other.NumberOfCompilerGeneratedParameters;
}
public override int GetHashCode()
{
unchecked {
return baseMethod.GetHashCode() + NumberOfCompilerGeneratedParameters + 1;
}
}
public override string ToString()
{
return string.Format("[LocalFunctionMethod: ReducedFrom={0}, NumberOfGeneratedParameters={1}]", ReducedFrom, NumberOfCompilerGeneratedParameters);
}
internal int NumberOfCompilerGeneratedParameters { get; }
public IMember MemberDefinition => this;
public IType ReturnType => baseMethod.ReturnType;
IEnumerable<IMember> IMember.ExplicitlyImplementedInterfaceMembers => baseMethod.ExplicitlyImplementedInterfaceMembers;
bool IMember.IsExplicitInterfaceImplementation => baseMethod.IsExplicitInterfaceImplementation;
public bool IsVirtual => baseMethod.IsVirtual;
public bool IsOverride => baseMethod.IsOverride;
public bool IsOverridable => baseMethod.IsOverridable;
public TypeParameterSubstitution Substitution => baseMethod.Substitution;
public IMethod Specialize(TypeParameterSubstitution substitution)
{
return SpecializedMethod.Create(this, substitution);
}
IMember IMember.Specialize(TypeParameterSubstitution substitution)
{
return Specialize(substitution);
}
public IReadOnlyList<ITypeParameter> TypeParameters => baseMethod.TypeParameters;
public bool IsExtensionMethod => baseMethod.IsExtensionMethod;
public bool IsLocalFunction => true;
public bool IsConstructor => baseMethod.IsConstructor;
public bool IsDestructor => baseMethod.IsDestructor;
public bool IsOperator => baseMethod.IsOperator;
public bool HasBody => baseMethod.HasBody;
public bool IsAccessor => baseMethod.IsAccessor;
public IMember AccessorOwner => baseMethod.AccessorOwner;
public MethodSemanticsAttributes AccessorKind => baseMethod.AccessorKind;
public IMethod ReducedFrom => baseMethod;
public IReadOnlyList<IType> TypeArguments => baseMethod.TypeArguments;
List<IParameter> parameters;
public IReadOnlyList<IParameter> Parameters {
get {
if (parameters == null)
parameters = new List<IParameter>(baseMethod.Parameters.SkipLast(NumberOfCompilerGeneratedParameters));
return parameters;
}
}
public System.Reflection.Metadata.EntityHandle MetadataToken => baseMethod.MetadataToken;
public SymbolKind SymbolKind => baseMethod.SymbolKind;
public ITypeDefinition DeclaringTypeDefinition => baseMethod.DeclaringTypeDefinition;
public IType DeclaringType => baseMethod.DeclaringType;
public IModule ParentModule => baseMethod.ParentModule;
IEnumerable<IAttribute> IEntity.GetAttributes() => baseMethod.GetAttributes();
IEnumerable<IAttribute> IMethod.GetReturnTypeAttributes() => baseMethod.GetReturnTypeAttributes();
bool IMethod.ReturnTypeIsRefReadOnly => baseMethod.ReturnTypeIsRefReadOnly;
/// <summary>
/// We consider local functions as always static, because they do not have a "this parameter".
/// Even local functions in instance methods capture this.
/// </summary>
public bool IsStatic => true;
public bool IsAbstract => baseMethod.IsAbstract;
public bool IsSealed => baseMethod.IsSealed;
public Accessibility Accessibility => baseMethod.Accessibility;
public string FullName => baseMethod.FullName;
public string Name => baseMethod.Name;
public string ReflectionName => baseMethod.ReflectionName;
public string Namespace => baseMethod.Namespace;
public ICompilation Compilation => baseMethod.Compilation;
}
}

6
ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataEvent.cs

@ -79,8 +79,10 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -79,8 +79,10 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
return returnType;
var metadata = module.metadata;
var ev = metadata.GetEventDefinition(handle);
var context = new GenericContext(DeclaringTypeDefinition?.TypeParameters);
returnType = module.ResolveType(ev.Type, context, ev.GetCustomAttributes());
var declaringTypeDef = DeclaringTypeDefinition;
var context = new GenericContext(declaringTypeDef?.TypeParameters);
var nullableContext = declaringTypeDef?.NullableContext ?? Nullability.Oblivious;
returnType = module.ResolveType(ev.Type, context, ev.GetCustomAttributes(), nullableContext);
return LazyInit.GetOrSet(ref this.returnType, returnType);
}
}

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

@ -186,7 +186,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -186,7 +186,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
ty = mod.ElementType;
}
ty = ApplyAttributeTypeVisitor.ApplyAttributesToType(ty, Compilation,
fieldDef.GetCustomAttributes(), metadata, module.TypeSystemOptions);
fieldDef.GetCustomAttributes(), metadata, module.TypeSystemOptions,
DeclaringTypeDefinition?.NullableContext ?? Nullability.Oblivious);
} catch (BadImageFormatException) {
ty = SpecialType.UnknownType;
}

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

@ -41,6 +41,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -41,6 +41,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
readonly EntityHandle accessorOwner;
public MethodSemanticsAttributes AccessorKind { get; }
public bool IsExtensionMethod { get; }
bool IMethod.IsLocalFunction => false;
// lazy-loaded fields:
ITypeDefinition declaringType;
@ -148,6 +149,13 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -148,6 +149,13 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
}
}
internal Nullability NullableContext {
get {
var methodDef = module.metadata.GetMethodDefinition(handle);
return methodDef.GetCustomAttributes().GetNullableContext(module.metadata) ?? DeclaringTypeDefinition.NullableContext;
}
}
private void DecodeSignature()
{
var methodDef = module.metadata.GetMethodDefinition(handle);
@ -155,8 +163,9 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -155,8 +163,9 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
IType returnType;
IParameter[] parameters;
try {
var nullableContext = methodDef.GetCustomAttributes().GetNullableContext(module.metadata) ?? DeclaringTypeDefinition.NullableContext;
var signature = methodDef.DecodeSignature(module.TypeProvider, genericContext);
(returnType, parameters) = DecodeSignature(module, this, signature, methodDef.GetParameters());
(returnType, parameters) = DecodeSignature(module, this, signature, methodDef.GetParameters(), nullableContext);
} catch (BadImageFormatException) {
returnType = SpecialType.UnknownType;
parameters = Empty<IParameter>.Array;
@ -165,7 +174,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -165,7 +174,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
LazyInit.GetOrSet(ref this.parameters, parameters);
}
internal static (IType, IParameter[]) DecodeSignature(MetadataModule module, IParameterizedMember owner, MethodSignature<IType> signature, ParameterHandleCollection? parameterHandles)
internal static (IType, IParameter[]) DecodeSignature(MetadataModule module, IParameterizedMember owner, MethodSignature<IType> signature, ParameterHandleCollection? parameterHandles, Nullability nullableContext)
{
var metadata = module.metadata;
int i = 0;
@ -186,14 +195,14 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -186,14 +195,14 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
// Fill gaps in the sequence with non-metadata parameters:
while (i < par.SequenceNumber - 1) {
parameterType = ApplyAttributeTypeVisitor.ApplyAttributesToType(
signature.ParameterTypes[i], module.Compilation, null, metadata, module.TypeSystemOptions);
signature.ParameterTypes[i], module.Compilation, null, metadata, module.TypeSystemOptions, nullableContext);
parameters[i] = new DefaultParameter(parameterType, name: string.Empty, owner,
isRef: parameterType.Kind == TypeKind.ByReference);
i++;
}
parameterType = ApplyAttributeTypeVisitor.ApplyAttributesToType(
signature.ParameterTypes[i], module.Compilation,
par.GetCustomAttributes(), metadata, module.TypeSystemOptions);
par.GetCustomAttributes(), metadata, module.TypeSystemOptions, nullableContext);
parameters[i] = new MetadataParameter(module, owner, parameterType, parameterHandle);
i++;
}
@ -201,7 +210,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -201,7 +210,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
}
while (i < signature.RequiredParameterCount) {
parameterType = ApplyAttributeTypeVisitor.ApplyAttributesToType(
signature.ParameterTypes[i], module.Compilation, null, metadata, module.TypeSystemOptions);
signature.ParameterTypes[i], module.Compilation, null, metadata, module.TypeSystemOptions, nullableContext);
parameters[i] = new DefaultParameter(parameterType, name: string.Empty, owner,
isRef: parameterType.Kind == TypeKind.ByReference);
i++;
@ -212,7 +221,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -212,7 +221,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
}
Debug.Assert(i == parameters.Length);
var returnType = ApplyAttributeTypeVisitor.ApplyAttributesToType(signature.ReturnType,
module.Compilation, returnTypeAttributes, metadata, module.TypeSystemOptions);
module.Compilation, returnTypeAttributes, metadata, module.TypeSystemOptions, nullableContext);
return (returnType, parameters);
}
#endregion

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

@ -123,13 +123,22 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -123,13 +123,22 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
var signature = propertyDef.DecodeSignature(module.TypeProvider, genericContext);
var accessors = propertyDef.GetAccessors();
ParameterHandleCollection? parameterHandles;
if (!accessors.Getter.IsNil)
parameterHandles = module.metadata.GetMethodDefinition(accessors.Getter).GetParameters();
else if (!accessors.Setter.IsNil)
parameterHandles = module.metadata.GetMethodDefinition(accessors.Setter).GetParameters();
else
Nullability nullableContext;
if (!accessors.Getter.IsNil) {
var getter = module.metadata.GetMethodDefinition(accessors.Getter);
parameterHandles = getter.GetParameters();
nullableContext = getter.GetCustomAttributes().GetNullableContext(module.metadata)
?? DeclaringTypeDefinition?.NullableContext ?? Nullability.Oblivious;
} else if (!accessors.Setter.IsNil) {
var setter = module.metadata.GetMethodDefinition(accessors.Setter);
parameterHandles = setter.GetParameters();
nullableContext = setter.GetCustomAttributes().GetNullableContext(module.metadata)
?? DeclaringTypeDefinition?.NullableContext ?? Nullability.Oblivious;
} else {
parameterHandles = null;
(returnType, parameters) = MetadataMethod.DecodeSignature(module, this, signature, parameterHandles);
nullableContext = DeclaringTypeDefinition?.NullableContext ?? Nullability.Oblivious;
}
(returnType, parameters) = MetadataMethod.DecodeSignature(module, this, signature, parameterHandles, nullableContext);
} catch (BadImageFormatException) {
returnType = SpecialType.UnknownType;
parameters = Empty<IParameter>.Array;

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

@ -51,6 +51,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -51,6 +51,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public KnownTypeCode KnownTypeCode { get; }
public IType EnumUnderlyingType { get; }
public bool HasExtensionMethods { get; }
public Nullability NullableContext { get; }
// lazy-loaded:
IMember[] members;
@ -77,10 +78,14 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -77,10 +78,14 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
// Create type parameters:
this.TypeParameters = MetadataTypeParameter.Create(module, this.DeclaringTypeDefinition, this, td.GetGenericParameters());
this.NullableContext = td.GetCustomAttributes().GetNullableContext(metadata) ?? this.DeclaringTypeDefinition.NullableContext;
} else {
// Create type parameters:
this.TypeParameters = MetadataTypeParameter.Create(module, this, td.GetGenericParameters());
this.NullableContext = td.GetCustomAttributes().GetNullableContext(metadata) ?? module.NullableContext;
var topLevelTypeName = fullTypeName.TopLevelTypeName;
for (int i = 0; i < KnownTypeReference.KnownTypeCodeCount; i++) {
var ktr = KnownTypeReference.Get((KnownTypeCode)i);
@ -301,7 +306,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -301,7 +306,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
}
foreach (var h in interfaceImplCollection) {
var iface = metadata.GetInterfaceImplementation(h);
baseTypes.Add(module.ResolveType(iface.Interface, context, iface.GetCustomAttributes()));
baseTypes.Add(module.ResolveType(iface.Interface, context, iface.GetCustomAttributes(), Nullability.Oblivious));
}
return LazyInit.GetOrSet(ref this.directBaseTypes, baseTypes);
}

10
ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeParameter.cs

@ -177,13 +177,21 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -177,13 +177,21 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
{
var metadata = module.metadata;
var gp = metadata.GetGenericParameter(handle);
Nullability nullableContext;
if (Owner is ITypeDefinition typeDef) {
nullableContext = typeDef.NullableContext;
} else if (Owner is MetadataMethod method) {
nullableContext = method.NullableContext;
} else {
nullableContext = Nullability.Oblivious;
}
var constraintHandleCollection = gp.GetConstraints();
List<IType> result = new List<IType>(constraintHandleCollection.Count + 1);
bool hasNonInterfaceConstraint = false;
foreach (var constraintHandle in constraintHandleCollection) {
var constraint = metadata.GetGenericParameterConstraint(constraintHandle);
var ty = module.ResolveType(constraint.Type, new GenericContext(Owner), constraint.GetCustomAttributes());
var ty = module.ResolveType(constraint.Type, new GenericContext(Owner), constraint.GetCustomAttributes(), nullableContext);
result.Add(ty);
hasNonInterfaceConstraint |= (ty.Kind != TypeKind.Interface);
}

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

@ -177,6 +177,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -177,6 +177,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
bool IType.IsByRefLike => false;
Nullability IType.Nullability => Nullability.Oblivious;
Nullability ITypeDefinition.NullableContext => Nullability.Oblivious;
IType IType.ChangeNullability(Nullability nullability)
{

13
ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs

@ -19,6 +19,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -19,6 +19,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
// the NullabilityAnnotatedType wrapper only in some limited places.
Debug.Assert(type is ITypeDefinition
|| type.Kind == TypeKind.Dynamic
|| type.Kind == TypeKind.Unknown
|| (type is ITypeParameter && this is ITypeParameter));
this.nullability = nullability;
}
@ -50,10 +51,16 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -50,10 +51,16 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public override IType VisitChildren(TypeVisitor visitor)
{
IType newBase = baseType.AcceptVisitor(visitor);
if (newBase != baseType)
if (newBase != baseType) {
if (newBase.Nullability == Nullability.Nullable) {
// T?! -> T?
// This happens during type substitution for generic methods.
return newBase;
}
return newBase.ChangeNullability(nullability);
else
} else {
return this;
}
}
public override string ToString()
@ -80,6 +87,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -80,6 +87,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
this.baseType = type;
}
public ITypeParameter OriginalTypeParameter => baseType;
SymbolKind ITypeParameter.OwnerType => baseType.OwnerType;
IEntity ITypeParameter.Owner => baseType.Owner;
int ITypeParameter.Index => baseType.Index;

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

@ -102,11 +102,15 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -102,11 +102,15 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
return specializedTypeParameters ?? methodDefinition.TypeParameters;
}
}
public bool IsExtensionMethod {
get { return methodDefinition.IsExtensionMethod; }
}
public bool IsLocalFunction {
get { return methodDefinition.IsLocalFunction; }
}
public bool IsConstructor {
get { return methodDefinition.IsConstructor; }
}
@ -199,7 +203,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -199,7 +203,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
b.Append('[');
for (int i = 0; i < this.TypeArguments.Count; i++) {
if (i > 0) b.Append(", ");
b.Append(this.TypeArguments[i].ReflectionName);
b.Append(this.TypeArguments[i].ToString());
}
b.Append(']');
} else if (this.TypeParameters.Count > 0) {
@ -212,7 +216,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -212,7 +216,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
b.Append(this.Parameters[i].ToString());
}
b.Append("):");
b.Append(this.ReturnType.ReflectionName);
b.Append(this.ReturnType.ToString());
b.Append(']');
return b.ToString();
}

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

@ -46,7 +46,12 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -46,7 +46,12 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public override string ReflectionName {
get { return elementType.ReflectionName + NameSuffix; }
}
public override string ToString()
{
return elementType.ToString() + NameSuffix;
}
public abstract string NameSuffix { get; }
public IType ElementType {

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

@ -94,7 +94,15 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -94,7 +94,15 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public override bool? IsReferenceType {
get { return isReferenceType; }
}
public override IType ChangeNullability(Nullability nullability)
{
if (nullability == Nullability.Oblivious)
return this;
else
return new NullabilityAnnotatedType(this, nullability);
}
public override int GetHashCode()
{
return (namespaceKnown ? 812571 : 12651) ^ fullTypeName.GetHashCode();

14
ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs

@ -40,6 +40,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -40,6 +40,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
internal readonly MetadataReader metadata;
readonly TypeSystemOptions options;
internal readonly TypeProvider TypeProvider;
internal readonly Nullability NullableContext;
readonly MetadataNamespace rootNamespace;
readonly MetadataTypeDefinition[] typeDefs;
@ -66,6 +67,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -66,6 +67,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
this.AssemblyName = metadata.GetString(moddef.Name);
this.FullAssemblyName = this.AssemblyName;
}
this.NullableContext = metadata.GetModuleDefinition().GetCustomAttributes().GetNullableContext(metadata) ?? Nullability.Oblivious;
this.rootNamespace = new MetadataNamespace(this, null, string.Empty, metadata.GetNamespaceDefinitionRoot());
if (!options.HasFlag(TypeSystemOptions.Uncached)) {
@ -267,12 +269,12 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -267,12 +269,12 @@ namespace ICSharpCode.Decompiler.TypeSystem
#endregion
#region Resolve Type
public IType ResolveType(EntityHandle typeRefDefSpec, GenericContext context, CustomAttributeHandleCollection? typeAttributes = null)
public IType ResolveType(EntityHandle typeRefDefSpec, GenericContext context, CustomAttributeHandleCollection? typeAttributes = null, Nullability nullableContext = Nullability.Oblivious)
{
return ResolveType(typeRefDefSpec, context, options, typeAttributes);
return ResolveType(typeRefDefSpec, context, options, typeAttributes, nullableContext);
}
public IType ResolveType(EntityHandle typeRefDefSpec, GenericContext context, TypeSystemOptions customOptions, CustomAttributeHandleCollection? typeAttributes = null)
public IType ResolveType(EntityHandle typeRefDefSpec, GenericContext context, TypeSystemOptions customOptions, CustomAttributeHandleCollection? typeAttributes = null, Nullability nullableContext = Nullability.Oblivious)
{
if (typeRefDefSpec.IsNil)
return SpecialType.UnknownType;
@ -293,7 +295,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -293,7 +295,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
default:
throw new BadImageFormatException("Not a type handle");
}
ty = ApplyAttributeTypeVisitor.ApplyAttributesToType(ty, Compilation, typeAttributes, metadata, customOptions);
ty = ApplyAttributeTypeVisitor.ApplyAttributesToType(ty, Compilation, typeAttributes, metadata, customOptions, nullableContext);
return ty;
}
@ -303,14 +305,14 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -303,14 +305,14 @@ namespace ICSharpCode.Decompiler.TypeSystem
var ty = ResolveType(declaringTypeReference, context,
options & ~(TypeSystemOptions.Dynamic | TypeSystemOptions.Tuple | TypeSystemOptions.NullabilityAnnotations));
// but substitute tuple types in type arguments:
ty = ApplyAttributeTypeVisitor.ApplyAttributesToType(ty, Compilation, null, metadata, options, typeChildrenOnly: true);
ty = ApplyAttributeTypeVisitor.ApplyAttributesToType(ty, Compilation, null, metadata, options, Nullability.Oblivious, typeChildrenOnly: true);
return ty;
}
IType IntroduceTupleTypes(IType ty)
{
// run ApplyAttributeTypeVisitor without attributes, in order to introduce tuple types
return ApplyAttributeTypeVisitor.ApplyAttributesToType(ty, Compilation, null, metadata, options);
return ApplyAttributeTypeVisitor.ApplyAttributesToType(ty, Compilation, null, metadata, options, Nullability.Oblivious);
}
#endregion

10
ICSharpCode.Decompiler/TypeSystem/NormalizeTypeVisitor.cs

@ -21,6 +21,16 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -21,6 +21,16 @@ namespace ICSharpCode.Decompiler.TypeSystem
RemoveNullability = true,
};
internal static readonly NormalizeTypeVisitor RemoveModifiersAndNullability = new NormalizeTypeVisitor {
ReplaceClassTypeParametersWithDummy = false,
ReplaceMethodTypeParametersWithDummy = false,
DynamicAndObject = false,
TupleToUnderlyingType = false,
RemoveModOpt = true,
RemoveModReq = true,
RemoveNullability = true,
};
public bool EquivalentTypes(IType a, IType b)
{
a = a.AcceptVisitor(this);

4
ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs

@ -129,6 +129,10 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -129,6 +129,10 @@ namespace ICSharpCode.Decompiler.TypeSystem
get { return baseMethod.IsExtensionMethod; }
}
bool IMethod.IsLocalFunction {
get { return baseMethod.IsLocalFunction; }
}
public bool IsConstructor {
get { return baseMethod.IsConstructor; }
}

2
ILSpy.BamlDecompiler/Rewrite/ConnectionIdRewritePass.cs

@ -94,7 +94,7 @@ namespace ILSpy.BamlDecompiler.Rewrite @@ -94,7 +94,7 @@ namespace ILSpy.BamlDecompiler.Rewrite
// decompile method and optimize the switch
var ilReader = new ILReader(ctx.TypeSystem.MainModule);
var function = ilReader.ReadIL((MethodDefinitionHandle)method.MetadataToken, body, genericContext, ctx.CancellationToken);
var function = ilReader.ReadIL((MethodDefinitionHandle)method.MetadataToken, body, genericContext, ILFunctionKind.TopLevelFunction, ctx.CancellationToken);
var context = new ILTransformContext(function, ctx.TypeSystem, null) {
CancellationToken = ctx.CancellationToken

2
ILSpy/Languages/ILAstLanguage.cs

@ -119,7 +119,7 @@ namespace ICSharpCode.ILSpy @@ -119,7 +119,7 @@ namespace ICSharpCode.ILSpy
var reader = new ILReader(typeSystem.MainModule);
reader.UseDebugSymbols = options.DecompilerSettings.UseDebugSymbols;
var methodBody = module.Reader.GetMethodBody(methodDef.RelativeVirtualAddress);
ILFunction il = reader.ReadIL((SRM.MethodDefinitionHandle)method.MetadataToken, methodBody, cancellationToken: options.CancellationToken);
ILFunction il = reader.ReadIL((SRM.MethodDefinitionHandle)method.MetadataToken, methodBody, kind: ILFunctionKind.TopLevelFunction, cancellationToken: options.CancellationToken);
var namespaces = new HashSet<string>();
var decompiler = new CSharpDecompiler(typeSystem, options.DecompilerSettings) { CancellationToken = options.CancellationToken };
ILTransformContext context = decompiler.CreateILTransformContext(il);

6
ILSpy/Properties/Resources.Designer.cs generated

@ -709,11 +709,11 @@ namespace ICSharpCode.ILSpy.Properties { @@ -709,11 +709,11 @@ namespace ICSharpCode.ILSpy.Properties {
}
/// <summary>
/// Looks up a localized string similar to Introduce local functions (NOT IMPLEMENTED!).
/// Looks up a localized string similar to Introduce local functions.
/// </summary>
public static string DecompilerSettings_IntroduceLocalFunctionsNOTIMPLEMENTED {
public static string DecompilerSettings_IntroduceLocalFunctions {
get {
return ResourceManager.GetString("DecompilerSettings.IntroduceLocalFunctionsNOTIMPLEMENTED", resourceCulture);
return ResourceManager.GetString("DecompilerSettings.IntroduceLocalFunctions", resourceCulture);
}
}

4
ILSpy/Properties/Resources.resx

@ -702,8 +702,8 @@ @@ -702,8 +702,8 @@
<data name="DecompilerSettings.RemoveOptionalArgumentsIfPossible" xml:space="preserve">
<value>Remove optional arguments, if possible</value>
</data>
<data name="DecompilerSettings.IntroduceLocalFunctionsNOTIMPLEMENTED" xml:space="preserve">
<value>Introduce local functions (NOT IMPLEMENTED!)</value>
<data name="DecompilerSettings.IntroduceLocalFunctions" xml:space="preserve">
<value>Introduce local functions</value>
</data>
<data name="DecompilerSettings.NullableReferenceTypes" xml:space="preserve">
<value>Nullable reference types</value>

6
ILSpy/Properties/Resources.zh-Hans.resx

@ -676,7 +676,7 @@ @@ -676,7 +676,7 @@
<value>IsByRefLikeAttribute应替换为结构上的 "ref" 修饰符</value>
</data>
<data name="DecompilerSettings.IsReadOnlyAttributeShouldBeReplacedWithReadonlyInModifiersOnStructsParameters" xml:space="preserve">
<value>IsReadOnlyAttribute 应替为结构参数上的 "readonly"/"中的修饰符</value>
<value>IsReadOnlyAttribute 应替为结构参数上的 "readonly"/"in"中的修饰符</value>
</data>
<data name="DecompilerSettings.IsUnmanagedAttributeOnTypeParametersShouldBeReplacedWithUnmanagedConstraints" xml:space="preserve">
<value>类型参数上的IsUnmanagedAttribute 应替换为 "非托管" 约束</value>
@ -702,8 +702,8 @@ @@ -702,8 +702,8 @@
<data name="DecompilerSettings.RemoveOptionalArgumentsIfPossible" xml:space="preserve">
<value>如果可能, 删除可选参数</value>
</data>
<data name="DecompilerSettings.IntroduceLocalFunctionsNOTIMPLEMENTED" xml:space="preserve">
<value>引入本地功能 (未实现!)</value>
<data name="DecompilerSettings.IntroduceLocalFunctions" xml:space="preserve">
<value>引入本地功能 </value>
</data>
<data name="DecompilerSettings.C70LocalFunctionsAreNotImplemented" xml:space="preserve">
<value>C# 7.0 本地函数未实现!</value>

Loading…
Cancel
Save