Browse Source

Merge pull request #2005 from icsharpcode/fix-1981

Fix #1981: Ensure correctness of TDCU
pull/2044/head
Siegfried Pammer 5 years ago committed by GitHub
parent
commit
52fce78b46
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  2. 86
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs
  3. 168
      ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.Expected.cs
  4. 184
      ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.cs
  5. 509
      ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.opt.roslyn.il
  6. 577
      ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.roslyn.il
  7. 8
      ICSharpCode.Decompiler.Tests/UglyTestRunner.cs
  8. 1
      ICSharpCode.Decompiler/CSharp/CallBuilder.cs
  9. 14
      ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs
  10. 18
      ICSharpCode.Decompiler/DecompilerSettings.cs
  11. 11
      ICSharpCode.Decompiler/IL/ILVariable.cs
  12. 40
      ICSharpCode.Decompiler/IL/Instructions/Block.cs
  13. 6
      ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
  14. 6
      ICSharpCode.Decompiler/IL/Instructions/ILVariableCollection.cs
  15. 63
      ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs
  16. 252
      ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs
  17. 10
      ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs
  18. 714
      ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs
  19. 4
      ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs
  20. 9
      ILSpy/Properties/Resources.Designer.cs

2
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -92,6 +92,8 @@ @@ -92,6 +92,8 @@
<Compile Include="TestAssemblyResolver.cs" />
<Compile Include="TestCases\Correctness\StringConcat.cs" />
<Compile Include="TestCases\ILPretty\Issue1681.cs" />
<Compile Include="TestCases\Ugly\AggressiveScalarReplacementOfAggregates.cs" />
<None Include="TestCases\Ugly\AggressiveScalarReplacementOfAggregates.Expected.cs" />
<None Include="TestCases\Pretty\IndexRangeTest.cs" />
<None Include="TestCases\ILPretty\Issue1918.cs" />
<Compile Include="TestCases\ILPretty\Issue1922.cs" />

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

@ -719,5 +719,91 @@ namespace LocalFunctions @@ -719,5 +719,91 @@ namespace LocalFunctions
}
}
}
public int Issue1798_NestedCapture2()
{
return Method();
#if CS80
static int Method()
#else
int Method()
#endif
{
int t0 = 0;
return ZZZ_0();
int ZZZ_0()
{
t0 = 0;
int t2 = t0;
return ((Func<int>)delegate {
t0 = 0;
t2 = 0;
return ZZZ_1();
})();
}
int ZZZ_1()
{
t0 = 0;
int t1 = t0;
#if !OPT
Func<int> func = delegate {
#else
return ((Func<int>)delegate {
#endif
t0 = 0;
t1 = 0;
return 0;
#if !OPT
};
return func();
#else
})();
#endif
}
}
}
public int Issue1798_NestedCapture2b()
{
return Method();
#if CS80
static int Method()
#else
int Method()
#endif
{
int t0 = 0;
return ZZZ_0() + ZZZ_1();
int ZZZ_0()
{
t0 = 0;
int t2 = t0;
return ((Func<int>)delegate {
t0 = 0;
t2 = 0;
return ZZZ_1();
})();
}
int ZZZ_1()
{
t0 = 0;
int t1 = t0;
#if !OPT
Func<int> func = delegate {
#else
return ((Func<int>)delegate {
#endif
t0 = 0;
t1 = 0;
return 0;
#if !OPT
};
return func();
#else
})();
#endif
}
}
}
}
}

168
ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.Expected.cs

@ -0,0 +1,168 @@ @@ -0,0 +1,168 @@
using System;
namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly
{
public class DisplayClass
{
public Program thisField;
public int field1;
public string field2;
}
public class NestedDisplayClass
{
public DisplayClass field3;
public int field1;
public string field2;
}
public class Program
{
public int Rand()
{
throw new NotImplementedException();
}
public void Test1()
{
int field1 = 42;
string field2 = "Hello World!";
Console.WriteLine("{0} {1}", field1, field2);
}
public void Test2()
{
DisplayClass displayClass = new DisplayClass {
field1 = 42,
field2 = "Hello World!"
};
Console.WriteLine("{0} {1}", displayClass.field1, displayClass.GetHashCode());
}
public void Test3()
{
DisplayClass displayClass = new DisplayClass {
field1 = 42,
field2 = "Hello World!"
};
Console.WriteLine("{0} {1}", displayClass.field1, displayClass);
}
public void Test4()
{
DisplayClass displayClass = new DisplayClass {
thisField = this,
field1 = 42,
field2 = "Hello World!"
};
int field1 = 4711;
string field2 = "ILSpy";
DisplayClass field3;
if (displayClass.field1 > 100) {
field3 = displayClass;
} else {
field3 = null;
}
Console.WriteLine("{0} {1}", displayClass, field3);
}
public void Test5()
{
DisplayClass displayClass = new DisplayClass {
thisField = this,
field1 = 42,
field2 = "Hello World!"
};
int field1 = 4711;
string field2 = "ILSpy";
DisplayClass field3;
if (displayClass.field1 > 100) {
field3 = displayClass;
} else {
field3 = null;
}
Console.WriteLine("{0} {1}", field2 + field1, field3);
}
public void Issue1898(int i)
{
DisplayClass displayClass = new DisplayClass {
thisField = this,
field1 = i
};
int field1 = default(int);
string field2 = default(string);
DisplayClass field3 = default(DisplayClass);
while (true) {
switch (Rand()) {
case 1:
field1 = Rand();
continue;
case 2:
field2 = Rand().ToString();
continue;
case 3:
field3 = displayClass;
continue;
}
Console.WriteLine(field1);
Console.WriteLine(field2);
Console.WriteLine(field3);
}
}
public void Test6(int i)
{
int field1 = i;
string field2 = "Hello World!";
if (i < 0) {
i = -i;
}
Console.WriteLine("{0} {1}", field1, field2);
}
public void Test6b(int i)
{
int num = i;
int field1 = num;
string field2 = "Hello World!";
if (num < 0) {
num = -num;
}
Console.WriteLine("{0} {1}", field1, field2);
}
public void Test7(int i)
{
int field1 = i;
string field2 = "Hello World!";
Console.WriteLine("{0} {1} {2}", field1++, field2, i);
}
public void Test8(int i)
{
int field1 = i;
string field2 = "Hello World!";
i = 42;
Console.WriteLine("{0} {1}", field1, field2);
}
public void Test8b(int i)
{
int num = i;
int field1 = num;
string field2 = "Hello World!";
num = 42;
Console.WriteLine("{0} {1}", field1, field2);
}
// public void Test9()
// {
// Program thisField = this;
// int field1 = 1;
// string field2 = "Hello World!";
// thisField = new Program();
// Console.WriteLine("{0} {1}", this, thisField);
// }
}
}

184
ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.cs

@ -0,0 +1,184 @@ @@ -0,0 +1,184 @@
using System;
namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly
{
public class DisplayClass
{
public Program thisField;
public int field1;
public string field2;
}
public class NestedDisplayClass
{
public DisplayClass field3;
public int field1;
public string field2;
}
public class Program
{
public int Rand()
{
throw new NotImplementedException();
}
public void Test1()
{
DisplayClass displayClass = new DisplayClass {
field1 = 42,
field2 = "Hello World!"
};
Console.WriteLine("{0} {1}", displayClass.field1, displayClass.field2);
}
public void Test2()
{
DisplayClass displayClass = new DisplayClass {
field1 = 42,
field2 = "Hello World!"
};
Console.WriteLine("{0} {1}", displayClass.field1, displayClass.GetHashCode());
}
public void Test3()
{
DisplayClass displayClass = new DisplayClass {
field1 = 42,
field2 = "Hello World!"
};
Console.WriteLine("{0} {1}", displayClass.field1, displayClass);
}
public void Test4()
{
DisplayClass displayClass = new DisplayClass {
thisField = this,
field1 = 42,
field2 = "Hello World!"
};
NestedDisplayClass nested = new NestedDisplayClass {
field1 = 4711,
field2 = "ILSpy"
};
if (displayClass.field1 > 100) {
nested.field3 = displayClass;
} else {
nested.field3 = null;
}
Console.WriteLine("{0} {1}", displayClass, nested.field3);
}
public void Test5()
{
DisplayClass displayClass = new DisplayClass {
thisField = this,
field1 = 42,
field2 = "Hello World!"
};
NestedDisplayClass nested = new NestedDisplayClass {
field1 = 4711,
field2 = "ILSpy"
};
if (displayClass.field1 > 100) {
nested.field3 = displayClass;
} else {
nested.field3 = null;
}
Console.WriteLine("{0} {1}", nested.field2 + nested.field1, nested.field3);
}
public void Issue1898(int i)
{
DisplayClass displayClass = new DisplayClass {
thisField = this,
field1 = i
};
NestedDisplayClass nested = new NestedDisplayClass();
while (true) {
switch (Rand()) {
case 1:
nested.field1 = Rand();
break;
case 2:
nested.field2 = Rand().ToString();
break;
case 3:
nested.field3 = displayClass;
break;
default:
Console.WriteLine(nested.field1);
Console.WriteLine(nested.field2);
Console.WriteLine(nested.field3);
break;
}
}
}
public void Test6(int i)
{
DisplayClass displayClass = new DisplayClass {
field1 = i,
field2 = "Hello World!"
};
if (i < 0) {
i = -i;
}
Console.WriteLine("{0} {1}", displayClass.field1, displayClass.field2);
}
public void Test6b(int i)
{
int num = i;
DisplayClass displayClass = new DisplayClass {
field1 = num,
field2 = "Hello World!"
};
if (num < 0) {
num = -num;
}
Console.WriteLine("{0} {1}", displayClass.field1, displayClass.field2);
}
public void Test7(int i)
{
DisplayClass displayClass = new DisplayClass {
field1 = i,
field2 = "Hello World!"
};
Console.WriteLine("{0} {1} {2}", displayClass.field1++, displayClass.field2, i);
}
public void Test8(int i)
{
DisplayClass displayClass = new DisplayClass {
field1 = i,
field2 = "Hello World!"
};
i = 42;
Console.WriteLine("{0} {1}", displayClass.field1, displayClass.field2);
}
public void Test8b(int i)
{
int num = i;
DisplayClass displayClass = new DisplayClass {
field1 = num,
field2 = "Hello World!"
};
num = 42;
Console.WriteLine("{0} {1}", displayClass.field1, displayClass.field2);
}
// public void Test9()
// {
// DisplayClass displayClass = new DisplayClass {
// thisField = this,
// field1 = 1,
// field2 = "Hello World!"
// };
// displayClass.thisField = new Program();
// Console.WriteLine("{0} {1}", this, displayClass.thisField);
// }
}
}

509
ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.opt.roslyn.il

@ -0,0 +1,509 @@ @@ -0,0 +1,509 @@
// Metadata version: v4.0.30319
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
.assembly AggressiveScalarReplacementOfAggregates
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
.custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx
63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows.
// --- The following custom attribute is added automatically, do not uncomment -------
// .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 02 00 00 00 00 00 )
.permissionset reqmin
= {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}}
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module AggressiveScalarReplacementOfAggregates.dll
.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x10000000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// =============== CLASS MEMBERS DECLARATION ===================
.class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass
extends [mscorlib]System.Object
{
.field public class ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program thisField
.field public int32 field1
.field public string field2
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method DisplayClass::.ctor
} // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass
.class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass
extends [mscorlib]System.Object
{
.field public class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass field3
.field public int32 field1
.field public string field2
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method NestedDisplayClass::.ctor
} // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass
.class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program
extends [mscorlib]System.Object
{
.method public hidebysig instance int32
Rand() cil managed
{
// Code size 6 (0x6)
.maxstack 8
IL_0000: newobj instance void [mscorlib]System.NotImplementedException::.ctor()
IL_0005: throw
} // end of method Program::Rand
.method public hidebysig instance void
Test1() cil managed
{
// Code size 53 (0x35)
.maxstack 3
.locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0)
IL_0000: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor()
IL_0005: dup
IL_0006: ldc.i4.s 42
IL_0008: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_000d: dup
IL_000e: ldstr "Hello World!"
IL_0013: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2
IL_0018: stloc.0
IL_0019: ldstr "{0} {1}"
IL_001e: ldloc.0
IL_001f: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_0024: box [mscorlib]System.Int32
IL_0029: ldloc.0
IL_002a: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2
IL_002f: call void [mscorlib]System.Console::WriteLine(string,
object,
object)
IL_0034: ret
} // end of method Program::Test1
.method public hidebysig instance void
Test2() cil managed
{
// Code size 58 (0x3a)
.maxstack 3
.locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0)
IL_0000: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor()
IL_0005: dup
IL_0006: ldc.i4.s 42
IL_0008: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_000d: dup
IL_000e: ldstr "Hello World!"
IL_0013: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2
IL_0018: stloc.0
IL_0019: ldstr "{0} {1}"
IL_001e: ldloc.0
IL_001f: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_0024: box [mscorlib]System.Int32
IL_0029: ldloc.0
IL_002a: callvirt instance int32 [mscorlib]System.Object::GetHashCode()
IL_002f: box [mscorlib]System.Int32
IL_0034: call void [mscorlib]System.Console::WriteLine(string,
object,
object)
IL_0039: ret
} // end of method Program::Test2
.method public hidebysig instance void
Test3() cil managed
{
// Code size 48 (0x30)
.maxstack 3
.locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0)
IL_0000: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor()
IL_0005: dup
IL_0006: ldc.i4.s 42
IL_0008: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_000d: dup
IL_000e: ldstr "Hello World!"
IL_0013: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2
IL_0018: stloc.0
IL_0019: ldstr "{0} {1}"
IL_001e: ldloc.0
IL_001f: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_0024: box [mscorlib]System.Int32
IL_0029: ldloc.0
IL_002a: call void [mscorlib]System.Console::WriteLine(string,
object,
object)
IL_002f: ret
} // end of method Program::Test3
.method public hidebysig instance void
Test4() cil managed
{
// Code size 104 (0x68)
.maxstack 3
.locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0,
class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass V_1)
IL_0000: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor()
IL_0005: dup
IL_0006: ldarg.0
IL_0007: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::thisField
IL_000c: dup
IL_000d: ldc.i4.s 42
IL_000f: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_0014: dup
IL_0015: ldstr "Hello World!"
IL_001a: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2
IL_001f: stloc.0
IL_0020: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::.ctor()
IL_0025: dup
IL_0026: ldc.i4 0x1267
IL_002b: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field1
IL_0030: dup
IL_0031: ldstr "ILSpy"
IL_0036: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field2
IL_003b: stloc.1
IL_003c: ldloc.0
IL_003d: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_0042: ldc.i4.s 100
IL_0044: ble.s IL_004f
IL_0046: ldloc.1
IL_0047: ldloc.0
IL_0048: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3
IL_004d: br.s IL_0056
IL_004f: ldloc.1
IL_0050: ldnull
IL_0051: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3
IL_0056: ldstr "{0} {1}"
IL_005b: ldloc.0
IL_005c: ldloc.1
IL_005d: ldfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3
IL_0062: call void [mscorlib]System.Console::WriteLine(string,
object,
object)
IL_0067: ret
} // end of method Program::Test4
.method public hidebysig instance void
Test5() cil managed
{
// Code size 125 (0x7d)
.maxstack 3
.locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0,
class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass V_1)
IL_0000: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor()
IL_0005: dup
IL_0006: ldarg.0
IL_0007: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::thisField
IL_000c: dup
IL_000d: ldc.i4.s 42
IL_000f: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_0014: dup
IL_0015: ldstr "Hello World!"
IL_001a: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2
IL_001f: stloc.0
IL_0020: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::.ctor()
IL_0025: dup
IL_0026: ldc.i4 0x1267
IL_002b: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field1
IL_0030: dup
IL_0031: ldstr "ILSpy"
IL_0036: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field2
IL_003b: stloc.1
IL_003c: ldloc.0
IL_003d: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_0042: ldc.i4.s 100
IL_0044: ble.s IL_004f
IL_0046: ldloc.1
IL_0047: ldloc.0
IL_0048: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3
IL_004d: br.s IL_0056
IL_004f: ldloc.1
IL_0050: ldnull
IL_0051: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3
IL_0056: ldstr "{0} {1}"
IL_005b: ldloc.1
IL_005c: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field2
IL_0061: ldloc.1
IL_0062: ldflda int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field1
IL_0067: call instance string [mscorlib]System.Int32::ToString()
IL_006c: call string [mscorlib]System.String::Concat(string,
string)
IL_0071: ldloc.1
IL_0072: ldfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3
IL_0077: call void [mscorlib]System.Console::WriteLine(string,
object,
object)
IL_007c: ret
} // end of method Program::Test5
.method public hidebysig instance void
Issue1898(int32 i) cil managed
{
// Code size 135 (0x87)
.maxstack 3
.locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0,
class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass V_1,
int32 V_2,
int32 V_3)
IL_0000: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor()
IL_0005: dup
IL_0006: ldarg.0
IL_0007: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::thisField
IL_000c: dup
IL_000d: ldarg.1
IL_000e: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_0013: stloc.0
IL_0014: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::.ctor()
IL_0019: stloc.1
IL_001a: ldarg.0
IL_001b: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program::Rand()
IL_0020: stloc.2
IL_0021: ldloc.2
IL_0022: ldc.i4.1
IL_0023: sub
IL_0024: switch (
IL_0037,
IL_0045,
IL_005b)
IL_0035: br.s IL_0064
IL_0037: ldloc.1
IL_0038: ldarg.0
IL_0039: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program::Rand()
IL_003e: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field1
IL_0043: br.s IL_001a
IL_0045: ldloc.1
IL_0046: ldarg.0
IL_0047: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program::Rand()
IL_004c: stloc.3
IL_004d: ldloca.s V_3
IL_004f: call instance string [mscorlib]System.Int32::ToString()
IL_0054: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field2
IL_0059: br.s IL_001a
IL_005b: ldloc.1
IL_005c: ldloc.0
IL_005d: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3
IL_0062: br.s IL_001a
IL_0064: ldloc.1
IL_0065: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field1
IL_006a: call void [mscorlib]System.Console::WriteLine(int32)
IL_006f: ldloc.1
IL_0070: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field2
IL_0075: call void [mscorlib]System.Console::WriteLine(string)
IL_007a: ldloc.1
IL_007b: ldfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3
IL_0080: call void [mscorlib]System.Console::WriteLine(object)
IL_0085: br.s IL_001a
} // end of method Program::Issue1898
.method public hidebysig instance void
Test6(int32 i) cil managed
{
// Code size 60 (0x3c)
.maxstack 3
.locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0)
IL_0000: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor()
IL_0005: dup
IL_0006: ldarg.1
IL_0007: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_000c: dup
IL_000d: ldstr "Hello World!"
IL_0012: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2
IL_0017: stloc.0
IL_0018: ldarg.1
IL_0019: ldc.i4.0
IL_001a: bge.s IL_0020
IL_001c: ldarg.1
IL_001d: neg
IL_001e: starg.s i
IL_0020: ldstr "{0} {1}"
IL_0025: ldloc.0
IL_0026: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_002b: box [mscorlib]System.Int32
IL_0030: ldloc.0
IL_0031: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2
IL_0036: call void [mscorlib]System.Console::WriteLine(string,
object,
object)
IL_003b: ret
} // end of method Program::Test6
.method public hidebysig instance void
Test6b(int32 i) cil managed
{
// Code size 61 (0x3d)
.maxstack 3
.locals init (int32 V_0,
class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_1)
IL_0000: ldarg.1
IL_0001: stloc.0
IL_0002: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor()
IL_0007: dup
IL_0008: ldloc.0
IL_0009: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_000e: dup
IL_000f: ldstr "Hello World!"
IL_0014: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2
IL_0019: stloc.1
IL_001a: ldloc.0
IL_001b: ldc.i4.0
IL_001c: bge.s IL_0021
IL_001e: ldloc.0
IL_001f: neg
IL_0020: stloc.0
IL_0021: ldstr "{0} {1}"
IL_0026: ldloc.1
IL_0027: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_002c: box [mscorlib]System.Int32
IL_0031: ldloc.1
IL_0032: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2
IL_0037: call void [mscorlib]System.Console::WriteLine(string,
object,
object)
IL_003c: ret
} // end of method Program::Test6b
.method public hidebysig instance void
Test7(int32 i) cil managed
{
// Code size 69 (0x45)
.maxstack 4
.locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0,
int32 V_1)
IL_0000: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor()
IL_0005: dup
IL_0006: ldarg.1
IL_0007: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_000c: dup
IL_000d: ldstr "Hello World!"
IL_0012: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2
IL_0017: stloc.0
IL_0018: ldstr "{0} {1} {2}"
IL_001d: ldloc.0
IL_001e: dup
IL_001f: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_0024: stloc.1
IL_0025: ldloc.1
IL_0026: ldc.i4.1
IL_0027: add
IL_0028: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_002d: ldloc.1
IL_002e: box [mscorlib]System.Int32
IL_0033: ldloc.0
IL_0034: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2
IL_0039: ldarg.1
IL_003a: box [mscorlib]System.Int32
IL_003f: call void [mscorlib]System.Console::WriteLine(string,
object,
object,
object)
IL_0044: ret
} // end of method Program::Test7
.method public hidebysig instance void
Test8(int32 i) cil managed
{
// Code size 56 (0x38)
.maxstack 3
.locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0)
IL_0000: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor()
IL_0005: dup
IL_0006: ldarg.1
IL_0007: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_000c: dup
IL_000d: ldstr "Hello World!"
IL_0012: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2
IL_0017: stloc.0
IL_0018: ldc.i4.s 42
IL_001a: starg.s i
IL_001c: ldstr "{0} {1}"
IL_0021: ldloc.0
IL_0022: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_0027: box [mscorlib]System.Int32
IL_002c: ldloc.0
IL_002d: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2
IL_0032: call void [mscorlib]System.Console::WriteLine(string,
object,
object)
IL_0037: ret
} // end of method Program::Test8
.method public hidebysig instance void
Test8b(int32 i) cil managed
{
// Code size 57 (0x39)
.maxstack 3
.locals init (int32 V_0,
class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_1)
IL_0000: ldarg.1
IL_0001: stloc.0
IL_0002: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor()
IL_0007: dup
IL_0008: ldloc.0
IL_0009: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_000e: dup
IL_000f: ldstr "Hello World!"
IL_0014: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2
IL_0019: stloc.1
IL_001a: ldc.i4.s 42
IL_001c: stloc.0
IL_001d: ldstr "{0} {1}"
IL_0022: ldloc.1
IL_0023: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_0028: box [mscorlib]System.Int32
IL_002d: ldloc.1
IL_002e: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2
IL_0033: call void [mscorlib]System.Console::WriteLine(string,
object,
object)
IL_0038: ret
} // end of method Program::Test8b
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method Program::.ctor
} // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program
// =============================================================
// *********** DISASSEMBLY COMPLETE ***********************

577
ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.roslyn.il

@ -0,0 +1,577 @@ @@ -0,0 +1,577 @@
// Metadata version: v4.0.30319
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
.assembly AggressiveScalarReplacementOfAggregates
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
.custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx
63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows.
// --- The following custom attribute is added automatically, do not uncomment -------
// .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 )
.permissionset reqmin
= {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}}
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module AggressiveScalarReplacementOfAggregates.dll
.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x10000000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// =============== CLASS MEMBERS DECLARATION ===================
.class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass
extends [mscorlib]System.Object
{
.field public class ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program thisField
.field public int32 field1
.field public string field2
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: nop
IL_0007: ret
} // end of method DisplayClass::.ctor
} // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass
.class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass
extends [mscorlib]System.Object
{
.field public class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass field3
.field public int32 field1
.field public string field2
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: nop
IL_0007: ret
} // end of method NestedDisplayClass::.ctor
} // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass
.class public auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program
extends [mscorlib]System.Object
{
.method public hidebysig instance int32
Rand() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: nop
IL_0001: newobj instance void [mscorlib]System.NotImplementedException::.ctor()
IL_0006: throw
} // end of method Program::Rand
.method public hidebysig instance void
Test1() cil managed
{
// Code size 55 (0x37)
.maxstack 3
.locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0)
IL_0000: nop
IL_0001: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor()
IL_0006: dup
IL_0007: ldc.i4.s 42
IL_0009: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_000e: dup
IL_000f: ldstr "Hello World!"
IL_0014: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2
IL_0019: stloc.0
IL_001a: ldstr "{0} {1}"
IL_001f: ldloc.0
IL_0020: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_0025: box [mscorlib]System.Int32
IL_002a: ldloc.0
IL_002b: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2
IL_0030: call void [mscorlib]System.Console::WriteLine(string,
object,
object)
IL_0035: nop
IL_0036: ret
} // end of method Program::Test1
.method public hidebysig instance void
Test2() cil managed
{
// Code size 60 (0x3c)
.maxstack 3
.locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0)
IL_0000: nop
IL_0001: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor()
IL_0006: dup
IL_0007: ldc.i4.s 42
IL_0009: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_000e: dup
IL_000f: ldstr "Hello World!"
IL_0014: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2
IL_0019: stloc.0
IL_001a: ldstr "{0} {1}"
IL_001f: ldloc.0
IL_0020: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_0025: box [mscorlib]System.Int32
IL_002a: ldloc.0
IL_002b: callvirt instance int32 [mscorlib]System.Object::GetHashCode()
IL_0030: box [mscorlib]System.Int32
IL_0035: call void [mscorlib]System.Console::WriteLine(string,
object,
object)
IL_003a: nop
IL_003b: ret
} // end of method Program::Test2
.method public hidebysig instance void
Test3() cil managed
{
// Code size 50 (0x32)
.maxstack 3
.locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0)
IL_0000: nop
IL_0001: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor()
IL_0006: dup
IL_0007: ldc.i4.s 42
IL_0009: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_000e: dup
IL_000f: ldstr "Hello World!"
IL_0014: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2
IL_0019: stloc.0
IL_001a: ldstr "{0} {1}"
IL_001f: ldloc.0
IL_0020: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_0025: box [mscorlib]System.Int32
IL_002a: ldloc.0
IL_002b: call void [mscorlib]System.Console::WriteLine(string,
object,
object)
IL_0030: nop
IL_0031: ret
} // end of method Program::Test3
.method public hidebysig instance void
Test4() cil managed
{
// Code size 114 (0x72)
.maxstack 3
.locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0,
class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass V_1,
bool V_2)
IL_0000: nop
IL_0001: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor()
IL_0006: dup
IL_0007: ldarg.0
IL_0008: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::thisField
IL_000d: dup
IL_000e: ldc.i4.s 42
IL_0010: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_0015: dup
IL_0016: ldstr "Hello World!"
IL_001b: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2
IL_0020: stloc.0
IL_0021: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::.ctor()
IL_0026: dup
IL_0027: ldc.i4 0x1267
IL_002c: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field1
IL_0031: dup
IL_0032: ldstr "ILSpy"
IL_0037: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field2
IL_003c: stloc.1
IL_003d: ldloc.0
IL_003e: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_0043: ldc.i4.s 100
IL_0045: cgt
IL_0047: stloc.2
IL_0048: ldloc.2
IL_0049: brfalse.s IL_0056
IL_004b: nop
IL_004c: ldloc.1
IL_004d: ldloc.0
IL_004e: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3
IL_0053: nop
IL_0054: br.s IL_005f
IL_0056: nop
IL_0057: ldloc.1
IL_0058: ldnull
IL_0059: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3
IL_005e: nop
IL_005f: ldstr "{0} {1}"
IL_0064: ldloc.0
IL_0065: ldloc.1
IL_0066: ldfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3
IL_006b: call void [mscorlib]System.Console::WriteLine(string,
object,
object)
IL_0070: nop
IL_0071: ret
} // end of method Program::Test4
.method public hidebysig instance void
Test5() cil managed
{
// Code size 135 (0x87)
.maxstack 3
.locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0,
class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass V_1,
bool V_2)
IL_0000: nop
IL_0001: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor()
IL_0006: dup
IL_0007: ldarg.0
IL_0008: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::thisField
IL_000d: dup
IL_000e: ldc.i4.s 42
IL_0010: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_0015: dup
IL_0016: ldstr "Hello World!"
IL_001b: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2
IL_0020: stloc.0
IL_0021: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::.ctor()
IL_0026: dup
IL_0027: ldc.i4 0x1267
IL_002c: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field1
IL_0031: dup
IL_0032: ldstr "ILSpy"
IL_0037: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field2
IL_003c: stloc.1
IL_003d: ldloc.0
IL_003e: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_0043: ldc.i4.s 100
IL_0045: cgt
IL_0047: stloc.2
IL_0048: ldloc.2
IL_0049: brfalse.s IL_0056
IL_004b: nop
IL_004c: ldloc.1
IL_004d: ldloc.0
IL_004e: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3
IL_0053: nop
IL_0054: br.s IL_005f
IL_0056: nop
IL_0057: ldloc.1
IL_0058: ldnull
IL_0059: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3
IL_005e: nop
IL_005f: ldstr "{0} {1}"
IL_0064: ldloc.1
IL_0065: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field2
IL_006a: ldloc.1
IL_006b: ldflda int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field1
IL_0070: call instance string [mscorlib]System.Int32::ToString()
IL_0075: call string [mscorlib]System.String::Concat(string,
string)
IL_007a: ldloc.1
IL_007b: ldfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3
IL_0080: call void [mscorlib]System.Console::WriteLine(string,
object,
object)
IL_0085: nop
IL_0086: ret
} // end of method Program::Test5
.method public hidebysig instance void
Issue1898(int32 i) cil managed
{
// Code size 151 (0x97)
.maxstack 3
.locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0,
class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass V_1,
int32 V_2,
int32 V_3,
int32 V_4,
bool V_5)
IL_0000: nop
IL_0001: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor()
IL_0006: dup
IL_0007: ldarg.0
IL_0008: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::thisField
IL_000d: dup
IL_000e: ldarg.1
IL_000f: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_0014: stloc.0
IL_0015: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::.ctor()
IL_001a: stloc.1
IL_001b: br.s IL_0092
IL_001d: nop
IL_001e: ldarg.0
IL_001f: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program::Rand()
IL_0024: stloc.3
IL_0025: ldloc.3
IL_0026: stloc.2
IL_0027: ldloc.2
IL_0028: ldc.i4.1
IL_0029: sub
IL_002a: switch (
IL_003d,
IL_004b,
IL_0062)
IL_003b: br.s IL_006b
IL_003d: ldloc.1
IL_003e: ldarg.0
IL_003f: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program::Rand()
IL_0044: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field1
IL_0049: br.s IL_0091
IL_004b: ldloc.1
IL_004c: ldarg.0
IL_004d: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program::Rand()
IL_0052: stloc.s V_4
IL_0054: ldloca.s V_4
IL_0056: call instance string [mscorlib]System.Int32::ToString()
IL_005b: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field2
IL_0060: br.s IL_0091
IL_0062: ldloc.1
IL_0063: ldloc.0
IL_0064: stfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3
IL_0069: br.s IL_0091
IL_006b: ldloc.1
IL_006c: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field1
IL_0071: call void [mscorlib]System.Console::WriteLine(int32)
IL_0076: nop
IL_0077: ldloc.1
IL_0078: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field2
IL_007d: call void [mscorlib]System.Console::WriteLine(string)
IL_0082: nop
IL_0083: ldloc.1
IL_0084: ldfld class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass ICSharpCode.Decompiler.Tests.TestCases.Ugly.NestedDisplayClass::field3
IL_0089: call void [mscorlib]System.Console::WriteLine(object)
IL_008e: nop
IL_008f: br.s IL_0091
IL_0091: nop
IL_0092: ldc.i4.1
IL_0093: stloc.s V_5
IL_0095: br.s IL_001d
} // end of method Program::Issue1898
.method public hidebysig instance void
Test6(int32 i) cil managed
{
// Code size 68 (0x44)
.maxstack 3
.locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0,
bool V_1)
IL_0000: nop
IL_0001: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor()
IL_0006: dup
IL_0007: ldarg.1
IL_0008: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_000d: dup
IL_000e: ldstr "Hello World!"
IL_0013: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2
IL_0018: stloc.0
IL_0019: ldarg.1
IL_001a: ldc.i4.0
IL_001b: clt
IL_001d: stloc.1
IL_001e: ldloc.1
IL_001f: brfalse.s IL_0027
IL_0021: nop
IL_0022: ldarg.1
IL_0023: neg
IL_0024: starg.s i
IL_0026: nop
IL_0027: ldstr "{0} {1}"
IL_002c: ldloc.0
IL_002d: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_0032: box [mscorlib]System.Int32
IL_0037: ldloc.0
IL_0038: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2
IL_003d: call void [mscorlib]System.Console::WriteLine(string,
object,
object)
IL_0042: nop
IL_0043: ret
} // end of method Program::Test6
.method public hidebysig instance void
Test6b(int32 i) cil managed
{
// Code size 69 (0x45)
.maxstack 3
.locals init (int32 V_0,
class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_1,
bool V_2)
IL_0000: nop
IL_0001: ldarg.1
IL_0002: stloc.0
IL_0003: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor()
IL_0008: dup
IL_0009: ldloc.0
IL_000a: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_000f: dup
IL_0010: ldstr "Hello World!"
IL_0015: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2
IL_001a: stloc.1
IL_001b: ldloc.0
IL_001c: ldc.i4.0
IL_001d: clt
IL_001f: stloc.2
IL_0020: ldloc.2
IL_0021: brfalse.s IL_0028
IL_0023: nop
IL_0024: ldloc.0
IL_0025: neg
IL_0026: stloc.0
IL_0027: nop
IL_0028: ldstr "{0} {1}"
IL_002d: ldloc.1
IL_002e: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_0033: box [mscorlib]System.Int32
IL_0038: ldloc.1
IL_0039: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2
IL_003e: call void [mscorlib]System.Console::WriteLine(string,
object,
object)
IL_0043: nop
IL_0044: ret
} // end of method Program::Test6b
.method public hidebysig instance void
Test7(int32 i) cil managed
{
// Code size 71 (0x47)
.maxstack 4
.locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0,
int32 V_1)
IL_0000: nop
IL_0001: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor()
IL_0006: dup
IL_0007: ldarg.1
IL_0008: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_000d: dup
IL_000e: ldstr "Hello World!"
IL_0013: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2
IL_0018: stloc.0
IL_0019: ldstr "{0} {1} {2}"
IL_001e: ldloc.0
IL_001f: dup
IL_0020: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_0025: stloc.1
IL_0026: ldloc.1
IL_0027: ldc.i4.1
IL_0028: add
IL_0029: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_002e: ldloc.1
IL_002f: box [mscorlib]System.Int32
IL_0034: ldloc.0
IL_0035: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2
IL_003a: ldarg.1
IL_003b: box [mscorlib]System.Int32
IL_0040: call void [mscorlib]System.Console::WriteLine(string,
object,
object,
object)
IL_0045: nop
IL_0046: ret
} // end of method Program::Test7
.method public hidebysig instance void
Test8(int32 i) cil managed
{
// Code size 58 (0x3a)
.maxstack 3
.locals init (class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_0)
IL_0000: nop
IL_0001: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor()
IL_0006: dup
IL_0007: ldarg.1
IL_0008: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_000d: dup
IL_000e: ldstr "Hello World!"
IL_0013: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2
IL_0018: stloc.0
IL_0019: ldc.i4.s 42
IL_001b: starg.s i
IL_001d: ldstr "{0} {1}"
IL_0022: ldloc.0
IL_0023: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_0028: box [mscorlib]System.Int32
IL_002d: ldloc.0
IL_002e: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2
IL_0033: call void [mscorlib]System.Console::WriteLine(string,
object,
object)
IL_0038: nop
IL_0039: ret
} // end of method Program::Test8
.method public hidebysig instance void
Test8b(int32 i) cil managed
{
// Code size 59 (0x3b)
.maxstack 3
.locals init (int32 V_0,
class ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass V_1)
IL_0000: nop
IL_0001: ldarg.1
IL_0002: stloc.0
IL_0003: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::.ctor()
IL_0008: dup
IL_0009: ldloc.0
IL_000a: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_000f: dup
IL_0010: ldstr "Hello World!"
IL_0015: stfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2
IL_001a: stloc.1
IL_001b: ldc.i4.s 42
IL_001d: stloc.0
IL_001e: ldstr "{0} {1}"
IL_0023: ldloc.1
IL_0024: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field1
IL_0029: box [mscorlib]System.Int32
IL_002e: ldloc.1
IL_002f: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Ugly.DisplayClass::field2
IL_0034: call void [mscorlib]System.Console::WriteLine(string,
object,
object)
IL_0039: nop
IL_003a: ret
} // end of method Program::Test8b
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: nop
IL_0007: ret
} // end of method Program::.ctor
} // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program
// =============================================================
// *********** DISASSEMBLY COMPLETE ***********************

8
ICSharpCode.Decompiler.Tests/UglyTestRunner.cs

@ -105,6 +105,14 @@ namespace ICSharpCode.Decompiler.Tests @@ -105,6 +105,14 @@ namespace ICSharpCode.Decompiler.Tests
RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings(CSharp.LanguageVersion.CSharp1));
}
[Test]
public void AggressiveScalarReplacementOfAggregates([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings {
AggressiveScalarReplacementOfAggregates = true
});
}
void RunForLibrary([CallerMemberName] string testName = null, AssemblerOptions asmOptions = AssemblerOptions.None, CompilerOptions cscOptions = CompilerOptions.None, DecompilerSettings decompilerSettings = null)
{
Run(testName, asmOptions | AssemblerOptions.Library, cscOptions | CompilerOptions.Library, decompilerSettings);

1
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

@ -200,6 +200,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -200,6 +200,7 @@ namespace ICSharpCode.Decompiler.CSharp
int skipCount = localFunction.ReducedMethod.NumberOfCompilerGeneratedTypeParameters;
ide.TypeArguments.AddRange(method.TypeArguments.Skip(skipCount).Select(expressionBuilder.ConvertType));
}
ide.AddAnnotation(localFunction);
target = ide.WithoutILInstruction()
.WithRR(ToMethodGroup(method, localFunction));
} else {

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

@ -21,7 +21,6 @@ using System.Collections.Generic; @@ -21,7 +21,6 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching;
using ICSharpCode.Decompiler.IL;
using ICSharpCode.Decompiler.IL.Transforms;
using ICSharpCode.Decompiler.Semantics;
@ -257,7 +256,16 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -257,7 +256,16 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
if (node is IdentifierExpression identExpr) {
var rr = identExpr.GetResolveResult() as ILVariableResolveResult;
if (rr != null && VariableNeedsDeclaration(rr.Variable.Kind)) {
var variable = rr.Variable;
FindInsertionPointForVariable(rr.Variable);
} else if (identExpr.Annotation<ILFunction>() is ILFunction localFunction && localFunction.Kind == ILFunctionKind.LocalFunction) {
foreach (var v in localFunction.CapturedVariables) {
if (VariableNeedsDeclaration(v.Kind))
FindInsertionPointForVariable(v);
}
}
void FindInsertionPointForVariable(ILVariable variable)
{
InsertionPoint newPoint;
int startIndex = scopeTracking.Count - 1;
if (variable.CaptureScope != null && startIndex > 0 && variable.CaptureScope != scopeTracking[startIndex].Scope) {
@ -287,7 +295,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -287,7 +295,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
} else {
v = new VariableToDeclare(variable, variable.HasInitialValue,
newPoint, identExpr, sourceOrder: variableDict.Count);
variableDict.Add(rr.Variable, v);
variableDict.Add(variable, v);
}
}
}

18
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -1347,6 +1347,24 @@ namespace ICSharpCode.Decompiler @@ -1347,6 +1347,24 @@ namespace ICSharpCode.Decompiler
}
}
bool aggressiveScalarReplacementOfAggregates = false;
[Category("DecompilerSettings.Other")]
[Description("DecompilerSettings.AggressiveScalarReplacementOfAggregates")]
// TODO : Remove once https://github.com/icsharpcode/ILSpy/issues/2032 is fixed.
#if !DEBUG
[Browsable(false)]
#endif
public bool AggressiveScalarReplacementOfAggregates {
get { return aggressiveScalarReplacementOfAggregates; }
set {
if (aggressiveScalarReplacementOfAggregates != value) {
aggressiveScalarReplacementOfAggregates = value;
OnPropertyChanged();
}
}
}
CSharpFormattingOptions csharpFormattingOptions;
[Browsable(false)]

11
ICSharpCode.Decompiler/IL/ILVariable.cs

@ -337,6 +337,17 @@ namespace ICSharpCode.Decompiler.IL @@ -337,6 +337,17 @@ namespace ICSharpCode.Decompiler.IL
}
}
/// <summary>
/// Gets whether the variable is dead - unused.
/// </summary>
public bool IsDead {
get {
return StoreCount == (HasInitialValue ? 1 : 0)
&& LoadCount == 0
&& AddressCount == 0;
}
}
/// <summary>
/// The field which was converted to a local variable.
/// Set when the variable is from a 'yield return' or 'async' state machine.

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

@ -21,6 +21,7 @@ using System.Collections.Generic; @@ -21,6 +21,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.Decompiler.IL.Transforms;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL
{
@ -131,6 +132,45 @@ namespace ICSharpCode.Decompiler.IL @@ -131,6 +132,45 @@ namespace ICSharpCode.Decompiler.IL
Debug.Assert(call.Arguments[0].MatchLdLoc(v));
}
break;
case BlockKind.ArrayInitializer:
var final = finalInstruction as LdLoc;
Debug.Assert(final != null && final.Variable.IsSingleDefinition && final.Variable.Kind == VariableKind.InitializerTarget);
IType type = null;
Debug.Assert(Instructions[0].MatchStLoc(final.Variable, out var init) && init.MatchNewArr(out type));
for (int i = 1; i < Instructions.Count; i++) {
Debug.Assert(Instructions[i].MatchStObj(out ILInstruction target, out _, out var t) && type != null && type.Equals(t));
Debug.Assert(target.MatchLdElema(out t, out ILInstruction array) && type.Equals(t));
Debug.Assert(array.MatchLdLoc(out ILVariable v) && v == final.Variable);
}
break;
case BlockKind.CollectionInitializer:
case BlockKind.ObjectInitializer:
var final2 = finalInstruction as LdLoc;
Debug.Assert(final2 != null);
var initVar2 = final2.Variable;
Debug.Assert(initVar2.StoreCount == 1 && initVar2.Kind == VariableKind.InitializerTarget);
IType type2 = null;
bool condition = Instructions[0].MatchStLoc(final2.Variable, out var init2);
Debug.Assert(condition);
Debug.Assert(init2 is NewObj || init2 is DefaultValue || (init2 is Block named && named.Kind == BlockKind.CallWithNamedArgs));
switch (init2) {
case NewObj newObj:
type2 = newObj.Method.DeclaringType;
break;
case DefaultValue defaultValue:
type2 = defaultValue.Type;
break;
case Block callWithNamedArgs when callWithNamedArgs.Kind == BlockKind.CallWithNamedArgs:
type2 = ((CallInstruction)callWithNamedArgs.FinalInstruction).Method.ReturnType;
break;
default:
Debug.Assert(false);
break;
}
for (int i = 1; i < Instructions.Count; i++) {
Debug.Assert(Instructions[i] is StLoc || AccessPathElement.GetAccessPath(Instructions[i], type2).Kind != IL.Transforms.AccessPathKind.Invalid);
}
break;
}
}

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

@ -73,6 +73,12 @@ namespace ICSharpCode.Decompiler.IL @@ -73,6 +73,12 @@ namespace ICSharpCode.Decompiler.IL
/// </summary>
public BlockContainer DeclarationScope { get; internal set; }
/// <summary>
/// Gets the set of captured variables by this ILFunction.
/// </summary>
/// <remarks>This is populated by the <see cref="TransformDisplayClassUsage" /> step.</remarks>
public HashSet<ILVariable> CapturedVariables { get; } = new HashSet<ILVariable>();
/// <summary>
/// List of warnings of ILReader.
/// </summary>

6
ICSharpCode.Decompiler/IL/Instructions/ILVariableCollection.cs

@ -102,8 +102,10 @@ namespace ICSharpCode.Decompiler.IL @@ -102,8 +102,10 @@ namespace ICSharpCode.Decompiler.IL
{
for (int i = 0; i < list.Count;) {
var v = list[i];
int deadStoreCount = v.HasInitialValue ? 1 : 0;
if (v.StoreCount == deadStoreCount && v.LoadCount == 0 && v.AddressCount == 0 && v.Kind != VariableKind.DisplayClassLocal) {
// Note: we cannot remove display-class locals from the collection,
// even if they are unused - which is always the case, if TDCU succeeds,
// because they are necessary for PDB generation to produce correct results.
if (v.IsDead && v.Kind != VariableKind.DisplayClassLocal) {
RemoveAt(i);
} else {
i++;

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

@ -53,9 +53,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -53,9 +53,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (instWithVar.Variable.Kind == VariableKind.Local) {
instWithVar.Variable.Kind = VariableKind.DisplayClassLocal;
}
var displayClassTypeDef = instWithVar.Variable.Type.GetDefinition();
if (instWithVar.Variable.IsSingleDefinition && instWithVar.Variable.StoreInstructions.SingleOrDefault() is StLoc store) {
if (store.Value is NewObj newObj) {
if (store.Value is NewObj) {
instWithVar.Variable.CaptureScope = BlockContainer.FindClosestContainer(store);
}
}
@ -141,6 +140,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -141,6 +140,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (LocalFunctionDecompiler.IsLocalFunctionMethod(targetMethod, context))
return null;
target = value.Arguments[0];
if (!ValidateDelegateTarget(target))
return null;
var handle = (MethodDefinitionHandle)targetMethod.MetadataToken;
if (activeMethods.Contains(handle)) {
this.context.Function.Warnings.Add(" Found self-referencing delegate construction. Abort transformation to avoid stack overflow.");
@ -169,7 +170,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -169,7 +170,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
var nestedContext = new ILTransformContext(context, function);
function.RunTransforms(CSharpDecompiler.GetILTransforms().TakeWhile(t => !(t is DelegateConstruction)).Concat(GetTransforms()), nestedContext);
nestedContext.Step("DelegateConstruction (ReplaceDelegateTargetVisitor)", function);
function.AcceptVisitor(new ReplaceDelegateTargetVisitor(target, function.Variables.SingleOrDefault(v => v.Index == -1 && v.Kind == VariableKind.Parameter)));
function.AcceptVisitor(new ReplaceDelegateTargetVisitor(target, function.Variables.SingleOrDefault(VariableKindExtensions.IsThis)));
// handle nested lambdas
nestedContext.StepStartGroup("DelegateConstruction (nested lambdas)", function);
((IILTransform)this).Run(function, nestedContext);
@ -180,6 +181,37 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -180,6 +181,37 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return function;
}
private static bool ValidateDelegateTarget(ILInstruction inst)
{
switch (inst) {
case LdNull _:
return true;
case LdLoc ldloc:
return ldloc.Variable.IsSingleDefinition;
case LdObj ldobj:
// TODO : should make sure that the display-class 'this' is unused,
// if the delegate target is ldobj(ldsflda field).
if (ldobj.Target is LdsFlda)
return true;
// TODO : ldfld chains must be validated more thoroughly, i.e., we should make sure
// that the value of the field is never changed.
ILInstruction target = ldobj;
while (target is LdObj || target is LdFlda) {
if (target is LdObj o) {
target = o.Target;
continue;
}
if (target is LdFlda f) {
target = f.Target;
continue;
}
}
return target is LdLoc;
default:
return false;
}
}
private IEnumerable<IILTransform> GetTransforms()
{
yield return new CombineExitsTransform();
@ -206,7 +238,30 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -206,7 +238,30 @@ namespace ICSharpCode.Decompiler.IL.Transforms
child.AcceptVisitor(this);
}
}
protected internal override void VisitILFunction(ILFunction function)
{
if (function == thisVariable?.Function) {
ILVariable v = null;
switch (target) {
case LdLoc l:
v = l.Variable;
break;
case LdObj lo:
ILInstruction inner = lo.Target;
while (inner is LdFlda ldf) {
inner = ldf.Target;
}
if (inner is LdLoc l2)
v = l2.Variable;
break;
}
if (v != null)
function.CapturedVariables.Add(v);
}
base.VisitILFunction(function);
}
protected internal override void VisitLdLoc(LdLoc inst)
{
if (inst.Variable == thisVariable) {

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

@ -46,6 +46,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -46,6 +46,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
public List<CallInstruction> UseSites;
public IMethod Method;
public ILFunction Definition;
/// <summary>
/// Used to store all synthesized call-site arguments grouped by the parameter index.
/// We use a dictionary instead of a simple array, because -1 is used for the this parameter
/// and there might be many non-synthesized arguments in between.
/// </summary>
public Dictionary<int, List<ILInstruction>> LocalFunctionArguments;
}
/// <summary>
@ -78,69 +84,89 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -78,69 +84,89 @@ namespace ICSharpCode.Decompiler.IL.Transforms
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 (_, info) in localFunctions) {
cancellationToken.ThrowIfCancellationRequested();
ReplaceReferencesToDisplayClassThis(localFunctions.Values);
DetermineCaptureAndDeclarationScopes(localFunctions.Values);
PropagateClosureParameterArguments(localFunctions);
TransformUseSites(localFunctions.Values);
}
private void ReplaceReferencesToDisplayClassThis(Dictionary<MethodDefinitionHandle, LocalFunctionInfo>.ValueCollection localFunctions)
{
foreach (var info in localFunctions) {
var localFunction = info.Definition;
if (localFunction.Method.IsStatic)
continue;
var thisVar = localFunction.Variables.SingleOrDefault(VariableKindExtensions.IsThis);
if (thisVar == null)
continue;
var compatibleArgument = FindCompatibleArgument(info, info.UseSites.SelectArray(u => u.Arguments[0]), ignoreStructure: true);
Debug.Assert(compatibleArgument != null);
context.Step($"Replace 'this' with {compatibleArgument}", localFunction);
localFunction.AcceptVisitor(new DelegateConstruction.ReplaceDelegateTargetVisitor(compatibleArgument, thisVar));
DetermineCaptureAndDeclarationScope(info, -1, compatibleArgument);
}
}
private void DetermineCaptureAndDeclarationScopes(Dictionary<MethodDefinitionHandle, LocalFunctionInfo>.ValueCollection localFunctions)
{
foreach (var info in localFunctions) {
context.CancellationToken.ThrowIfCancellationRequested();
if (info.Definition == null) {
function.Warnings.Add($"Could not decode local function '{info.Method}'");
context.Function.Warnings.Add($"Could not decode local function '{info.Method}'");
continue;
}
context.StepStartGroup($"Transform " + info.Definition.Name, info.Definition);
context.StepStartGroup($"Determine and move to declaration scope of " + info.Definition.Name, info.Definition);
try {
var localFunction = info.Definition;
if (!localFunction.Method.IsStatic) {
var thisVar = localFunction.Variables.SingleOrDefault(VariableKindExtensions.IsThis);
var target = info.UseSites.Where(us => us.Arguments[0].MatchLdLoc(out _)).FirstOrDefault()?.Arguments[0];
if (target == null) {
target = info.UseSites[0].Arguments[0];
if (target.MatchLdFld(out var target1, out var field) && thisVar.Type.Equals(field.Type) && field.Type.Kind == TypeKind.Class && TransformDisplayClassUsage.IsPotentialClosure(context, field.Type.GetDefinition())) {
var variable = function.Descendants.OfType<ILFunction>().SelectMany(f => f.Variables).Where(v => !v.IsThis() && TransformDisplayClassUsage.IsClosure(context, v, out var varType, out _) && varType.Equals(field.Type)).OnlyOrDefault();
if (variable != null) {
target = new LdLoc(variable);
HandleArgument(localFunction, 1, 0, target);
}
}
}
context.Step($"Replace 'this' with {target}", localFunction);
localFunction.AcceptVisitor(new DelegateConstruction.ReplaceDelegateTargetVisitor(target, thisVar));
}
foreach (var useSite in info.UseSites) {
DetermineCaptureAndDeclarationScope(localFunction, useSite);
DetermineCaptureAndDeclarationScope(info, useSite);
if (function.Method.IsConstructor && localFunction.DeclarationScope == null) {
if (context.Function.Method.IsConstructor && localFunction.DeclarationScope == null) {
localFunction.DeclarationScope = BlockContainer.FindClosestContainer(useSite);
}
}
if (localFunction.DeclarationScope == null) {
localFunction.DeclarationScope = (BlockContainer)function.Body;
localFunction.DeclarationScope = (BlockContainer)context.Function.Body;
}
ILFunction declaringFunction = GetDeclaringFunction(localFunction);
if (declaringFunction != function) {
function.LocalFunctions.Remove(localFunction);
if (declaringFunction != context.Function) {
context.Step($"Move {localFunction.Name} from {context.Function.Name} to {declaringFunction.Name}", localFunction);
context.Function.LocalFunctions.Remove(localFunction);
declaringFunction.LocalFunctions.Add(localFunction);
}
if (TryValidateSkipCount(info, out int skipCount) && skipCount != localFunction.ReducedMethod.NumberOfCompilerGeneratedTypeParameters) {
Debug.Assert(false);
function.Warnings.Add($"Could not decode local function '{info.Method}'");
if (declaringFunction != function) {
context.Function.Warnings.Add($"Could not decode local function '{info.Method}'");
if (declaringFunction != context.Function) {
declaringFunction.LocalFunctions.Remove(localFunction);
}
continue;
}
} finally {
context.StepEndGroup(keepIfEmpty: true);
}
}
}
private void TransformUseSites(Dictionary<MethodDefinitionHandle, LocalFunctionInfo>.ValueCollection localFunctions)
{
foreach (var info in localFunctions) {
context.CancellationToken.ThrowIfCancellationRequested();
if (info.Definition == null) continue;
context.StepStartGroup($"TransformUseSites of " + info.Definition.Name, info.Definition);
try {
foreach (var useSite in info.UseSites) {
context.Step($"Transform use site at IL_{useSite.StartILOffset:x4}", useSite);
context.Step($"Transform use-site at IL_{useSite.StartILOffset:x4}", useSite);
if (useSite.OpCode == OpCode.NewObj) {
TransformToLocalFunctionReference(localFunction, useSite);
TransformToLocalFunctionReference(info.Definition, useSite);
} else {
TransformToLocalFunctionInvocation(localFunction.ReducedMethod, useSite);
TransformToLocalFunctionInvocation(info.Definition.ReducedMethod, useSite);
}
}
} finally {
@ -149,6 +175,106 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -149,6 +175,106 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
}
private void PropagateClosureParameterArguments(Dictionary<MethodDefinitionHandle, LocalFunctionInfo> localFunctions)
{
foreach (var localFunction in context.Function.Descendants.OfType<ILFunction>()) {
if (localFunction.Kind != ILFunctionKind.LocalFunction)
continue;
context.CancellationToken.ThrowIfCancellationRequested();
var token = (MethodDefinitionHandle)localFunction.Method.MetadataToken;
var info = localFunctions[token];
foreach (var useSite in info.UseSites) {
if (useSite is NewObj) {
AddAsArgument(-1, useSite.Arguments[0]);
} else {
int firstArgumentIndex;
if (info.Method.IsStatic) {
firstArgumentIndex = 0;
} else {
firstArgumentIndex = 1;
AddAsArgument(-1, useSite.Arguments[0]);
}
for (int i = useSite.Arguments.Count - 1; i >= firstArgumentIndex; i--) {
AddAsArgument(i - firstArgumentIndex, useSite.Arguments[i]);
}
}
}
context.StepStartGroup($"PropagateClosureParameterArguments of " + info.Definition.Name, info.Definition);
try {
foreach (var (index, arguments) in info.LocalFunctionArguments) {
var targetVariable = info.Definition.Variables.SingleOrDefault(p => p.Kind == VariableKind.Parameter && p.Index == index);
if (targetVariable == null)
continue;
var compatibleArgument = FindCompatibleArgument(info, arguments);
Debug.Assert(compatibleArgument != null);
context.Step($"Replace '{targetVariable}' with '{compatibleArgument}'", info.Definition);
info.Definition.AcceptVisitor(new DelegateConstruction.ReplaceDelegateTargetVisitor(compatibleArgument, targetVariable));
}
} finally {
context.StepEndGroup(keepIfEmpty: true);
}
void AddAsArgument(int index, ILInstruction argument)
{
switch (argument) {
case LdLoc _:
case LdLoca _:
case LdFlda _:
case LdObj _:
if (index >= 0 && !IsClosureParameter(info.Method.Parameters[index], resolveContext))
return;
break;
default:
if (index >= 0 && IsClosureParameter(info.Method.Parameters[index], resolveContext))
info.Definition.Warnings.Add("Could not transform parameter " + index + ": unsupported argument pattern");
return;
}
if (!info.LocalFunctionArguments.TryGetValue(index, out var arguments)) {
arguments = new List<ILInstruction>();
info.LocalFunctionArguments.Add(index, arguments);
}
arguments.Add(argument);
}
}
}
private ILInstruction FindCompatibleArgument(LocalFunctionInfo info, IList<ILInstruction> arguments, bool ignoreStructure = false)
{
foreach (var arg in arguments) {
if (arg is IInstructionWithVariableOperand ld2 && (ignoreStructure || info.Definition.IsDescendantOf(ld2.Variable.Function)))
return arg;
var v = ResolveAncestorScopeReference(arg);
if (v != null)
return new LdLoc(v);
}
return null;
}
private ILVariable ResolveAncestorScopeReference(ILInstruction inst)
{
if (!inst.MatchLdFld(out var target, out var field))
return null;
if (field.Type.Kind != TypeKind.Class)
return null;
if (!(TransformDisplayClassUsage.IsPotentialClosure(context, field.Type.GetDefinition()) || context.Function.Method.DeclaringType.Equals(field.Type)))
return null;
foreach (var v in context.Function.Descendants.OfType<ILFunction>().SelectMany(f => f.Variables)) {
if (v.Kind != VariableKind.Local && v.Kind != VariableKind.DisplayClassLocal && v.Kind != VariableKind.StackSlot) {
if (!(v.Kind == VariableKind.Parameter && v.Index == -1))
continue;
if (v.Type.Equals(field.Type))
return v;
}
if (!(TransformDisplayClassUsage.IsClosure(context, v, out var varType, out _) && varType.Equals(field.Type)))
continue;
return v;
}
return null;
}
private ILFunction GetDeclaringFunction(ILFunction localFunction)
{
if (localFunction.DeclarationScope == null)
@ -217,6 +343,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -217,6 +343,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
context.StepStartGroup($"Read local function '{targetMethod.Name}'", inst);
info = new LocalFunctionInfo() {
UseSites = new List<CallInstruction>() { inst },
LocalFunctionArguments = new Dictionary<int, List<ILInstruction>>(),
Method = (IMethod)targetMethod.MemberDefinition,
};
var rootFunction = context.Function;
@ -347,7 +474,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -347,7 +474,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return inst;
}
LocalFunctionMethod ReduceToLocalFunction(IMethod method, int skipCount)
LocalFunctionMethod ReduceToLocalFunction(IMethod method, int typeParametersToRemove)
{
int parametersToRemove = 0;
for (int i = method.Parameters.Count - 1; i >= 0; i--) {
@ -355,12 +482,16 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -355,12 +482,16 @@ namespace ICSharpCode.Decompiler.IL.Transforms
break;
parametersToRemove++;
}
return new LocalFunctionMethod(method, parametersToRemove, skipCount);
return new LocalFunctionMethod(method, parametersToRemove, typeParametersToRemove);
}
static void TransformToLocalFunctionReference(ILFunction function, CallInstruction useSite)
{
useSite.Arguments[0].ReplaceWith(new LdNull().WithILRange(useSite.Arguments[0]));
ILInstruction target = useSite.Arguments[0];
target.ReplaceWith(new LdNull().WithILRange(target));
if (target is IInstructionWithVariableOperand withVar && withVar.Variable.Kind == VariableKind.Local) {
withVar.Variable.Kind = VariableKind.DisplayClassLocal;
}
var fnptr = (IInstructionWithMethodOperand)useSite.Arguments[1];
var specializeMethod = function.ReducedMethod.Specialize(fnptr.Method.Substitution);
var replacement = new LdFtn(specializeMethod).WithILRange((ILInstruction)fnptr);
@ -396,41 +527,66 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -396,41 +527,66 @@ namespace ICSharpCode.Decompiler.IL.Transforms
useSite.ReplaceWith(replacement);
}
void DetermineCaptureAndDeclarationScope(ILFunction function, CallInstruction useSite)
void DetermineCaptureAndDeclarationScope(LocalFunctionInfo info, CallInstruction useSite)
{
int firstArgumentIndex = function.Method.IsStatic ? 0 : 1;
int firstArgumentIndex = info.Definition.Method.IsStatic ? 0 : 1;
for (int i = useSite.Arguments.Count - 1; i >= firstArgumentIndex; i--) {
if (!HandleArgument(function, firstArgumentIndex, i, useSite.Arguments[i]))
if (!DetermineCaptureAndDeclarationScope(info, i - firstArgumentIndex, useSite.Arguments[i]))
break;
}
if (firstArgumentIndex > 0) {
HandleArgument(function, firstArgumentIndex, 0, useSite.Arguments[0]);
DetermineCaptureAndDeclarationScope(info, -1, useSite.Arguments[0]);
}
}
bool HandleArgument(ILFunction function, int firstArgumentIndex, int i, ILInstruction arg)
bool DetermineCaptureAndDeclarationScope(LocalFunctionInfo info, int parameterIndex, ILInstruction arg)
{
ILFunction function = info.Definition;
ILVariable closureVar;
if (!(arg.MatchLdLoc(out closureVar) || arg.MatchLdLoca(out closureVar)))
return false;
if (parameterIndex >= 0) {
if (!(parameterIndex < function.Method.Parameters.Count && IsClosureParameter(function.Method.Parameters[parameterIndex], resolveContext)))
return false;
}
if (!(arg.MatchLdLoc(out closureVar) || arg.MatchLdLoca(out closureVar))) {
closureVar = ResolveAncestorScopeReference(arg);
if (closureVar == null)
return false;
}
if (closureVar.Kind == VariableKind.NamedArgument)
return false;
if (!TransformDisplayClassUsage.IsClosure(context, closureVar, out _, out var initializer))
var initializer = GetClosureInitializer(closureVar);
if (initializer == null)
return false;
if (i - firstArgumentIndex >= 0) {
Debug.Assert(i - firstArgumentIndex < function.Method.Parameters.Count && IsClosureParameter(function.Method.Parameters[i - firstArgumentIndex], resolveContext));
}
// determine the capture scope of closureVar and the declaration scope of the function
var additionalScope = BlockContainer.FindClosestContainer(initializer);
if (closureVar.CaptureScope == null)
closureVar.CaptureScope = additionalScope;
else
closureVar.CaptureScope = FindCommonAncestorInstruction<BlockContainer>(closureVar.CaptureScope, additionalScope);
else {
BlockContainer combinedScope = FindCommonAncestorInstruction<BlockContainer>(closureVar.CaptureScope, additionalScope);
Debug.Assert(combinedScope != null);
closureVar.CaptureScope = combinedScope;
}
if (closureVar.Kind == VariableKind.Local) {
closureVar.Kind = VariableKind.DisplayClassLocal;
}
if (function.DeclarationScope == null)
function.DeclarationScope = closureVar.CaptureScope;
else if (!IsInNestedLocalFunction(function.DeclarationScope, closureVar.CaptureScope.Ancestors.OfType<ILFunction>().First()))
function.DeclarationScope = FindCommonAncestorInstruction<BlockContainer>(function.DeclarationScope, closureVar.CaptureScope);
return true;
ILInstruction GetClosureInitializer(ILVariable variable)
{
var type = UnwrapByRef(variable.Type).GetDefinition();
if (type == null)
return null;
if (variable.Kind == VariableKind.Parameter)
return null;
if (type.Kind == TypeKind.Struct)
return GetStatement(variable.AddressInstructions.OrderBy(i => i.StartILOffset).First());
else
return (StLoc)variable.StoreInstructions[0];
}
}
bool IsInNestedLocalFunction(BlockContainer declarationScope, ILFunction function)

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

@ -60,10 +60,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -60,10 +60,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// for the base ctor call) or a compiler-generated delegate method, which might be used in a query expression.
return false;
}
// Do not try to transform display class usages or delegate construction.
// Do not try to transform delegate construction.
// DelegateConstruction transform cannot deal with this.
if (TransformDisplayClassUsage.IsSimpleDisplayClass(newObjInst.Method.DeclaringType))
return false;
if (DelegateConstruction.IsDelegateConstruction(newObjInst) || TransformDisplayClassUsage.IsPotentialClosure(context, newObjInst))
return false;
// Cannot build a collection/object initializer attached to an AnonymousTypeCreateExpression:s
@ -260,7 +258,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -260,7 +258,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
public override string ToString() => $"[{Member}, {Indices}]";
public static (AccessPathKind Kind, List<AccessPathElement> Path, List<ILInstruction> Values, ILVariable Target) GetAccessPath(
ILInstruction instruction, IType rootType, DecompilerSettings settings,
ILInstruction instruction, IType rootType, DecompilerSettings settings = null,
CSharpTypeResolveContext resolveContext = null,
Dictionary<ILVariable, (int Index, ILInstruction Value)> possibleIndexVariables = null)
{
@ -282,7 +280,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -282,7 +280,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (!CanBeUsedInInitializer(property, resolveContext, kind, path)) goto default;
var isGetter = method.Equals(property?.Getter);
var indices = call.Arguments.Skip(1).Take(call.Arguments.Count - (isGetter ? 1 : 2)).ToArray();
if (indices.Length > 0 && !settings.DictionaryInitializers) goto default;
if (indices.Length > 0 && settings?.DictionaryInitializers == false) goto default;
if (possibleIndexVariables != null) {
// Mark all index variables as used
foreach (var index in indices.OfType<IInstructionWithVariableOperand>()) {
@ -373,7 +371,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -373,7 +371,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (!"Add".Equals(method.Name, StringComparison.Ordinal) || arguments.Count == 0)
return false;
if (method.IsExtensionMethod)
return settings.ExtensionMethodsInCollectionInitializers
return settings?.ExtensionMethodsInCollectionInitializers != false
&& CSharp.Transforms.IntroduceExtensionMethods.CanTransformToExtensionMethodCall(method, resolveContext, ignoreTypeArguments: true);
var targetType = GetReturnTypeFromInstruction(arguments[0]) ?? rootType;
if (targetType == null)

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

@ -20,6 +20,9 @@ using System; @@ -20,6 +20,9 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection.Metadata;
using ICSharpCode.Decompiler.Disassembler;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
@ -28,135 +31,471 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -28,135 +31,471 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// <summary>
/// Transforms closure fields to local variables.
///
/// This is a post-processing step of <see cref="DelegateConstruction"/>, <see cref="LocalFunctionDecompiler"/> and <see cref="TransformExpressionTrees"/>.
/// This is a post-processing step of <see cref="DelegateConstruction"/>, <see cref="LocalFunctionDecompiler"/>
/// and <see cref="TransformExpressionTrees"/>.
///
/// In general we can perform SROA (scalar replacement of aggregates) on any variable that
/// satisfies the following conditions:
/// 1) It is initialized by an empty/default constructor call.
/// 2) The variable is never passed to another method.
/// 3) The variable is never the target of an invocation.
///
/// Note that 2) and 3) apply because declarations and uses of lambdas and local functions
/// are already transformed by the time this transform is applied.
/// </summary>
class TransformDisplayClassUsage : ILVisitor, IILTransform
{
class VariableToDeclare
{
private readonly DisplayClass container;
private readonly IField field;
private ILVariable declaredVariable;
public string Name => field.Name;
public bool CanPropagate { get; private set; }
public HashSet<ILInstruction> Initializers { get; } = new HashSet<ILInstruction>();
public VariableToDeclare(DisplayClass container, IField field, ILVariable declaredVariable = null)
{
this.container = container;
this.field = field;
this.declaredVariable = declaredVariable;
Debug.Assert(declaredVariable == null || declaredVariable.StateMachineField == field);
}
public void Propagate(ILVariable variable)
{
Debug.Assert(declaredVariable == null || (variable == null && declaredVariable.StateMachineField == null));
this.declaredVariable = variable;
this.CanPropagate = variable != null;
}
public ILVariable GetOrDeclare()
{
if (declaredVariable != null)
return declaredVariable;
declaredVariable = container.Variable.Function.RegisterVariable(VariableKind.Local, field.Type, field.Name);
declaredVariable.HasInitialValue = true;
declaredVariable.CaptureScope = container.CaptureScope;
return declaredVariable;
}
}
[DebuggerDisplay("[DisplayClass {Variable} : {Type}]")]
class DisplayClass
{
public bool IsMono;
public ILInstruction Initializer;
public ILVariable Variable;
public ITypeDefinition Definition;
public Dictionary<IField, ILVariable> Variables;
public readonly ILVariable Variable;
public readonly ITypeDefinition Type;
public readonly Dictionary<IField, VariableToDeclare> VariablesToDeclare;
public BlockContainer CaptureScope;
public ILFunction DeclaringFunction;
public ILInstruction Initializer;
public DisplayClass(ILVariable variable, ITypeDefinition type)
{
Variable = variable;
Type = type;
VariablesToDeclare = new Dictionary<IField, VariableToDeclare>();
}
}
ILTransformContext context;
ITypeResolveContext decompilationContext;
readonly Dictionary<ILVariable, DisplayClass> displayClasses = new Dictionary<ILVariable, DisplayClass>();
readonly List<ILInstruction> instructionsToRemove = new List<ILInstruction>();
readonly MultiDictionary<IField, StObj> fieldAssignmentsWithVariableValue = new MultiDictionary<IField, StObj>();
public void Run(ILFunction function, ILTransformContext context)
void IILTransform.Run(ILFunction function, ILTransformContext context)
{
if (this.context != null)
throw new InvalidOperationException("Reentrancy in " + nameof(TransformDisplayClassUsage));
try {
if (this.context != null)
throw new InvalidOperationException("Reentrancy in " + nameof(TransformDisplayClassUsage));
this.context = context;
var decompilationContext = new SimpleTypeResolveContext(context.Function.Method);
// Traverse nested functions in post-order:
// Inner functions are transformed before outer functions
foreach (var f in function.Descendants.OfType<ILFunction>()) {
foreach (var v in f.Variables.ToArray()) {
if (context.Settings.YieldReturn && HandleMonoStateMachine(function, v, decompilationContext, f))
continue;
if ((context.Settings.AnonymousMethods || context.Settings.ExpressionTrees) && IsClosure(context, v, out ITypeDefinition closureType, out var inst)) {
if (!CanRemoveAllReferencesTo(context, v))
continue;
if (inst is StObj || inst is StLoc)
instructionsToRemove.Add(inst);
AddOrUpdateDisplayClass(f, v, closureType, inst, localFunctionClosureParameter: false);
continue;
this.decompilationContext = new SimpleTypeResolveContext(context.Function.Method);
AnalyzeFunction(function);
Transform(function);
} finally {
ClearState();
}
}
void ClearState()
{
displayClasses.Clear();
this.decompilationContext = null;
this.context = null;
}
void AnalyzeFunction(ILFunction function)
{
void VisitFunction(ILFunction f)
{
foreach (var v in f.Variables.ToArray()) {
var result = AnalyzeVariable(v);
if (result == null || displayClasses.ContainsKey(result.Variable))
continue;
context.Step($"Detected display-class {result.Variable}", result.Initializer ?? f.Body);
displayClasses.Add(result.Variable, result);
}
}
void VisitChildren(ILInstruction inst)
{
foreach (var child in inst.Children) {
Visit(child);
}
}
void Visit(ILInstruction inst)
{
switch (inst) {
case ILFunction f:
VisitFunction(f);
VisitChildren(inst);
break;
default:
VisitChildren(inst);
break;
}
}
Visit(function);
foreach (var (v, displayClass) in displayClasses.ToArray()) {
if (!ValidateDisplayClassUses(v, displayClass))
displayClasses.Remove(v);
}
foreach (var displayClass in displayClasses.Values) {
foreach (var v in displayClass.VariablesToDeclare.Values) {
if (v.CanPropagate) {
var variableToPropagate = v.GetOrDeclare();
if (variableToPropagate.Kind != VariableKind.Parameter && !displayClasses.ContainsKey(variableToPropagate))
v.Propagate(null);
}
}
}
}
bool ValidateDisplayClassUses(ILVariable v, DisplayClass displayClass)
{
Debug.Assert(v == displayClass.Variable);
foreach (var ldloc in v.LoadInstructions) {
if (!ValidateUse(displayClass, ldloc))
return false;
}
foreach (var ldloca in v.AddressInstructions) {
if (!ValidateUse(displayClass, ldloca))
return false;
}
return true;
bool ValidateUse(DisplayClass container, ILInstruction use)
{
IField field;
switch (use.Parent) {
case LdFlda ldflda when ldflda.MatchLdFlda(out var target, out field) && target == use:
var keyField = (IField)field.MemberDefinition;
if (!container.VariablesToDeclare.TryGetValue(keyField, out VariableToDeclare variable) || variable == null) {
variable = AddVariable(container, null, field);
}
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);
continue;
container.VariablesToDeclare[keyField] = variable;
return true;
case StObj stobj when stobj.MatchStObj(out var target, out ILInstruction value, out _) && value == use:
if (target.MatchLdFlda(out var load, out field) && load.MatchLdLocRef(out var otherVariable) && displayClasses.TryGetValue(otherVariable, out var otherDisplayClass)) {
if (otherDisplayClass.VariablesToDeclare.TryGetValue((IField)field.MemberDefinition, out var declaredVar))
return declaredVar.CanPropagate;
}
AnalyzeUseSites(v);
return false;
default:
return false;
}
}
}
private DisplayClass AnalyzeVariable(ILVariable v)
{
switch (v.Kind) {
case VariableKind.Parameter:
if (context.Settings.YieldReturn && v.Function.StateMachineCompiledWithMono && v.IsThis())
return HandleMonoStateMachine(v.Function, v);
return null;
case VariableKind.StackSlot:
case VariableKind.Local:
case VariableKind.DisplayClassLocal:
return DetectDisplayClass(v);
case VariableKind.InitializerTarget:
return DetectDisplayClassInitializer(v);
default:
return null;
}
}
DisplayClass DetectDisplayClass(ILVariable v)
{
ITypeDefinition definition;
if (v.Kind != VariableKind.StackSlot) {
definition = v.Type.GetDefinition();
} else if (v.StoreInstructions.Count > 0 && v.StoreInstructions[0] is StLoc stloc) {
definition = stloc.Value.InferType(context.TypeSystem).GetDefinition();
} else {
definition = null;
}
if (!ValidateDisplayClassDefinition(definition))
return null;
DisplayClass result;
switch (definition.Kind) {
case TypeKind.Class:
if (!v.IsSingleDefinition)
return null;
if (!(v.StoreInstructions.SingleOrDefault() is StLoc stloc))
return null;
if (stloc.Value is NewObj newObj && ValidateConstructor(newObj.Method)) {
result = new DisplayClass(v, definition) {
CaptureScope = v.CaptureScope,
Initializer = stloc
};
} else {
return null;
}
HandleInitBlock(stloc.Parent as Block, stloc.ChildIndex + 1, result, result.Variable);
break;
case TypeKind.Struct:
if (!v.HasInitialValue)
return null;
if (v.StoreCount != 1)
return null;
Debug.Assert(v.StoreInstructions.Count == 0);
result = new DisplayClass(v, definition) { CaptureScope = v.CaptureScope };
HandleInitBlock(FindDisplayStructInitBlock(v), 0, result, result.Variable);
break;
default:
return null;
}
if (IsMonoNestedCaptureScope(definition)) {
result.CaptureScope = null;
}
return result;
}
void HandleInitBlock(Block initBlock, int startIndex, DisplayClass result, ILVariable targetVariable)
{
if (initBlock == null)
return;
for (int i = startIndex; i < initBlock.Instructions.Count; i++) {
var init = initBlock.Instructions[i];
if (!init.MatchStFld(out var target, out var field, out _))
break;
if (!target.MatchLdLocRef(targetVariable))
break;
if (result.VariablesToDeclare.ContainsKey((IField)field.MemberDefinition))
break;
var variable = AddVariable(result, (StObj)init, field);
result.VariablesToDeclare[(IField)field.MemberDefinition] = variable;
}
}
private Block FindDisplayStructInitBlock(ILVariable v)
{
var root = v.Function.Body;
return Visit(root)?.Ancestors.OfType<Block>().FirstOrDefault();
// Try to find a common ancestor of all uses of the variable v.
ILInstruction Visit(ILInstruction inst)
{
switch (inst) {
case LdLoc l when l.Variable == v:
return l;
case StLoc s when s.Variable == v:
return s;
case LdLoca la when la.Variable == v:
return la;
default:
return VisitChildren(inst);
}
VisitILFunction(function);
if (instructionsToRemove.Count > 0) {
context.Step($"Remove instructions", function);
foreach (var store in instructionsToRemove) {
if (store.Parent is Block containingBlock)
containingBlock.Instructions.Remove(store);
}
ILInstruction VisitChildren(ILInstruction inst)
{
// Visit all children of the instruction
ILInstruction result = null;
foreach (var child in inst.Children) {
var newResult = Visit(child);
// As soon as there is a second use of v in this sub-tree,
// we can skip all other children and just return this node.
if (result == null) {
result = newResult;
} else if (newResult != null) {
return inst;
}
}
foreach (var f in TreeTraversal.PostOrder(function, f => f.LocalFunctions))
RemoveDeadVariableInit.ResetHasInitialValueFlag(f, context);
} finally {
instructionsToRemove.Clear();
displayClasses.Clear();
fieldAssignmentsWithVariableValue.Clear();
this.context = null;
// returns null, if v is not used in this sub-tree;
// returns a descendant of inst, if it is the only use of v in this sub-tree.
return result;
}
}
private bool CanRemoveAllReferencesTo(ILTransformContext context, ILVariable v)
DisplayClass DetectDisplayClassInitializer(ILVariable v)
{
if (v.StoreInstructions.Count != 1 || !(v.StoreInstructions[0] is StLoc store && store.Parent is Block initializerBlock && initializerBlock.Kind == BlockKind.ObjectInitializer))
return null;
if (!(store.Value is NewObj newObj))
return null;
var definition = newObj.Method.DeclaringType.GetDefinition();
if (!ValidateDisplayClassDefinition(definition))
return null;
if (!ValidateConstructor(newObj.Method))
return null;
if (!initializerBlock.Parent.MatchStLoc(out var referenceVariable))
return null;
if (!referenceVariable.IsSingleDefinition)
return null;
if (!(referenceVariable.StoreInstructions.SingleOrDefault() is StLoc))
return null;
var result = new DisplayClass(referenceVariable, definition) {
CaptureScope = referenceVariable.CaptureScope,
Initializer = initializerBlock.Parent
};
HandleInitBlock(initializerBlock, 1, result, v);
return result;
}
private bool ValidateDisplayClassDefinition(ITypeDefinition definition)
{
foreach (var use in v.LoadInstructions) {
// we only accept stloc, stobj/ldobj and ld(s)flda instructions,
// as these are required by all patterns this transform understands.
if (!(use.Parent is StLoc || use.Parent is LdFlda || use.Parent is LdsFlda || use.Parent is StObj || use.Parent is LdObj)) {
if (definition == null)
return false;
if (definition.ParentModule.PEFile != context.PEFile)
return false;
if (!context.Settings.AggressiveScalarReplacementOfAggregates) {
if (definition.DeclaringTypeDefinition == null)
return false;
}
if (use.Parent.MatchStLoc(out var targetVar) && !IsClosure(context, targetVar, out _, out _)) {
if (!IsPotentialClosure(context, definition))
return false;
}
}
return true;
}
private void AnalyzeUseSites(ILVariable v)
private bool ValidateConstructor(IMethod method)
{
foreach (var use in v.LoadInstructions) {
if (!(use.Parent?.Parent is Block))
continue;
if (use.Parent.MatchStFld(out _, out var f, out var value) && value == use) {
fieldAssignmentsWithVariableValue.Add(f, (StObj)use.Parent);
}
if (use.Parent.MatchStsFld(out f, out value) && value == use) {
fieldAssignmentsWithVariableValue.Add(f, (StObj)use.Parent);
}
try {
if (method.Parameters.Count != 0)
return false;
var handle = (MethodDefinitionHandle)method.MetadataToken;
var module = (MetadataModule)method.ParentModule;
var file = module.PEFile;
if (handle.IsNil || file != context.PEFile)
return false;
var def = file.Metadata.GetMethodDefinition(handle);
if (def.RelativeVirtualAddress == 0)
return false;
var body = file.Reader.GetMethodBody(def.RelativeVirtualAddress);
if (!body.LocalSignature.IsNil || body.ExceptionRegions.Length != 0)
return false;
var reader = body.GetILReader();
if (reader.Length < 7)
return false;
// IL_0000: ldarg.0
// IL_0001: call instance void [mscorlib]System.Object::.ctor()
// IL_0006: ret
if (DecodeOpCodeSkipNop(ref reader) != ILOpCode.Ldarg_0)
return false;
if (DecodeOpCodeSkipNop(ref reader) != ILOpCode.Call)
return false;
var baseCtorHandle = MetadataTokenHelpers.EntityHandleOrNil(reader.ReadInt32());
if (baseCtorHandle.IsNil)
return false;
var objectCtor = module.ResolveMethod(baseCtorHandle, new TypeSystem.GenericContext());
if (!objectCtor.DeclaringType.IsKnownType(KnownTypeCode.Object))
return false;
if (!objectCtor.IsConstructor || objectCtor.Parameters.Count != 0)
return false;
return DecodeOpCodeSkipNop(ref reader) == ILOpCode.Ret;
} catch (BadImageFormatException) {
return false;
}
foreach (var use in v.AddressInstructions) {
if (!(use.Parent?.Parent is Block))
continue;
if (use.Parent.MatchStFld(out _, out var f, out var value) && value == use) {
fieldAssignmentsWithVariableValue.Add(f, (StObj)use.Parent);
}
if (use.Parent.MatchStsFld(out f, out value) && value == use) {
fieldAssignmentsWithVariableValue.Add(f, (StObj)use.Parent);
}
}
ILOpCode DecodeOpCodeSkipNop(ref BlobReader reader)
{
ILOpCode code;
do {
code = reader.DecodeOpCode();
} while (code == ILOpCode.Nop);
return code;
}
VariableToDeclare AddVariable(DisplayClass result, StObj statement, IField field)
{
VariableToDeclare variable = new VariableToDeclare(result, field);
if (statement != null) {
variable.Propagate(ResolveVariableToPropagate(statement.Value, field.Type));
variable.Initializers.Add(statement);
}
return variable;
}
private void AddOrUpdateDisplayClass(ILFunction f, ILVariable v, ITypeDefinition closureType, ILInstruction inst, bool localFunctionClosureParameter)
/// <summary>
/// Resolves references to variables that can be propagated.
/// If a value does not match either LdLoc or a LdObj LdLdFlda* LdLoc chain, null is returned.
/// The if any of the variables/fields in the chain cannot be propagated, null is returned.
/// </summary>
ILVariable ResolveVariableToPropagate(ILInstruction value, IType expectedType = null)
{
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, ILVariable>(),
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;
if (displayClass.CaptureScope != null && !localFunctionClosureParameter) {
displayClass.DeclaringFunction = displayClass.CaptureScope.Ancestors.OfType<ILFunction>().First();
}
displayClass.Variable = v;
displayClass.Initializer = inst;
displayClasses.Add(v, displayClass);
ILVariable v;
switch (value) {
case LdLoc load when load.Variable.StateMachineField == null:
v = load.Variable;
if (v.Kind == VariableKind.Parameter) {
if (v.LoadCount != 1 && !v.IsThis()) {
// If the variable is a parameter and it is used elsewhere, we cannot propagate it.
// "dc.field = v; dc.field.mutate(); use(v);" cannot turn to "v.mutate(); use(v)"
return null;
}
} else {
// Non-parameter propagation will later be checked, and will only be allowed for display classes
if (v.Type.IsReferenceType != true) {
// don't allow propagation for display structs (as used with local functions)
return null;
}
}
if (!v.IsSingleDefinition) {
// "dc.field = v; v = 42; use(dc.field)" cannot turn to "v = 42; use(v);"
return null;
}
if (!(expectedType == null || v.Kind == VariableKind.StackSlot || v.Type.Equals(expectedType)))
return null;
return v;
case LdObj ldfld:
DisplayClass currentDisplayClass = null;
foreach (var item in ldfld.Target.Descendants) {
if (IsDisplayClassLoad(item, out v)) {
if (!displayClasses.TryGetValue(v, out currentDisplayClass))
return null;
}
if (currentDisplayClass == null)
return null;
if (item is LdFlda ldf && currentDisplayClass.VariablesToDeclare.TryGetValue((IField)ldf.Field.MemberDefinition, out var vd)) {
if (!vd.CanPropagate)
return null;
if (!displayClasses.TryGetValue(vd.GetOrDeclare(), out currentDisplayClass))
return null;
}
}
return currentDisplayClass.Variable;
default:
return null;
}
}
private void Transform(ILFunction function)
{
VisitILFunction(function);
context.Step($"ResetHasInitialValueFlag", function);
foreach (var f in TreeTraversal.PostOrder(function, f => f.LocalFunctions)) {
RemoveDeadVariableInit.ResetHasInitialValueFlag(f, context);
f.CapturedVariables.RemoveWhere(v => v.IsDead);
}
}
@ -190,6 +529,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -190,6 +529,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
bool IsMonoNestedCaptureScope(ITypeDefinition closureType)
{
if (!closureType.Name.Contains("AnonStorey"))
return false;
var decompilationContext = new SimpleTypeResolveContext(context.Function.Method);
return closureType.Fields.Any(f => IsPotentialClosure(decompilationContext.CurrentTypeDefinition, f.ReturnType.GetDefinition()));
}
@ -198,45 +539,40 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -198,45 +539,40 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// mcs likes to optimize closures in yield state machines away by moving the captured variables' fields into the state machine type,
/// We construct a <see cref="DisplayClass"/> that spans the whole method body.
/// </summary>
bool HandleMonoStateMachine(ILFunction currentFunction, ILVariable thisVariable, SimpleTypeResolveContext decompilationContext, ILFunction nestedFunction)
DisplayClass HandleMonoStateMachine(ILFunction function, ILVariable thisVariable)
{
if (!(nestedFunction.StateMachineCompiledWithMono && thisVariable.IsThis()))
return false;
if (!(function.StateMachineCompiledWithMono && thisVariable.IsThis()))
return null;
// Special case for Mono-compiled yield state machines
ITypeDefinition closureType = thisVariable.Type.GetDefinition();
if (!(closureType != decompilationContext.CurrentTypeDefinition
&& IsPotentialClosure(decompilationContext.CurrentTypeDefinition, closureType, allowTypeImplementingInterfaces: true)))
return false;
return null;
var displayClass = new DisplayClass {
IsMono = true,
Initializer = nestedFunction.Body,
Variable = thisVariable,
Definition = thisVariable.Type.GetDefinition(),
Variables = new Dictionary<IField, ILVariable>(),
CaptureScope = (BlockContainer)nestedFunction.Body
};
displayClasses.Add(thisVariable, displayClass);
foreach (var stateMachineVariable in nestedFunction.Variables) {
if (stateMachineVariable.StateMachineField == null || displayClass.Variables.ContainsKey(stateMachineVariable.StateMachineField))
var displayClass = new DisplayClass(thisVariable, thisVariable.Type.GetDefinition());
displayClass.CaptureScope = (BlockContainer)function.Body;
foreach (var stateMachineVariable in function.Variables) {
if (stateMachineVariable.StateMachineField == null || displayClass.VariablesToDeclare.ContainsKey(stateMachineVariable.StateMachineField))
continue;
displayClass.Variables.Add(stateMachineVariable.StateMachineField, stateMachineVariable);
VariableToDeclare variableToDeclare = new VariableToDeclare(displayClass, stateMachineVariable.StateMachineField, stateMachineVariable);
displayClass.VariablesToDeclare.Add(stateMachineVariable.StateMachineField, variableToDeclare);
}
if (!currentFunction.Method.IsStatic && FindThisField(out var thisField)) {
var thisVar = currentFunction.Variables
if (!function.Method.IsStatic && FindThisField(out var thisField)) {
var thisVar = function.Variables
.FirstOrDefault(t => t.IsThis() && t.Type.GetDefinition() == decompilationContext.CurrentTypeDefinition);
if (thisVar == null) {
thisVar = new ILVariable(VariableKind.Parameter, decompilationContext.CurrentTypeDefinition, -1) { Name = "this" };
currentFunction.Variables.Add(thisVar);
function.Variables.Add(thisVar);
}
displayClass.Variables.Add(thisField, thisVar);
VariableToDeclare variableToDeclare = new VariableToDeclare(displayClass, thisField, thisVar);
displayClass.VariablesToDeclare.Add(thisField, variableToDeclare);
}
return true;
return displayClass;
bool FindThisField(out IField foundField)
{
foundField = null;
foreach (var field in closureType.GetFields(f2 => !f2.IsStatic && !displayClass.Variables.ContainsKey(f2) && f2.Type.GetDefinition() == decompilationContext.CurrentTypeDefinition)) {
foreach (var field in closureType.GetFields(f2 => !f2.IsStatic && !displayClass.VariablesToDeclare.ContainsKey(f2) && f2.Type.GetDefinition() == decompilationContext.CurrentTypeDefinition)) {
thisField = field;
return true;
}
@ -244,15 +580,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -244,15 +580,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
}
internal static bool IsSimpleDisplayClass(IType type)
{
if (!type.HasGeneratedName() || (!type.Name.Contains("DisplayClass") && !type.Name.Contains("AnonStorey")))
return false;
if (type.DirectBaseTypes.Any(t => !t.IsKnownType(KnownTypeCode.Object)))
return false;
return true;
}
internal static bool IsPotentialClosure(ILTransformContext context, NewObj inst)
{
var decompilationContext = new SimpleTypeResolveContext(context.Function.Ancestors.OfType<ILFunction>().Last().Method);
@ -292,63 +619,87 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -292,63 +619,87 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true;
}
ILFunction currentFunction;
readonly Stack<ILFunction> currentFunctions = new Stack<ILFunction>();
protected internal override void VisitILFunction(ILFunction function)
{
var oldFunction = this.currentFunction;
context.StepStartGroup("Visit " + function.Name);
try {
this.currentFunction = function;
this.currentFunctions.Push(function);
base.VisitILFunction(function);
} finally {
this.currentFunction = oldFunction;
this.currentFunctions.Pop();
context.StepEndGroup(keepIfEmpty: true);
}
}
protected override void Default(ILInstruction inst)
{
foreach (var child in inst.Children) {
ILInstruction next;
for (var child = inst.Children.FirstOrDefault(); child != null; child = next) {
next = child.GetNextSibling();
child.AcceptVisitor(this);
}
}
protected internal override void VisitStLoc(StLoc inst)
{
base.VisitStLoc(inst);
if (inst.Parent is Block && inst.Variable.IsSingleDefinition) {
if (inst.Variable.Kind == VariableKind.Local && inst.Variable.LoadCount == 0 && inst.Value is StLoc) {
context.Step($"Remove unused variable assignment {inst.Variable.Name}", inst);
inst.ReplaceWith(inst.Value);
DisplayClass displayClass;
if (inst.Parent is Block parentBlock && inst.Variable.IsSingleDefinition) {
if ((inst.Variable.Kind == VariableKind.Local || inst.Variable.Kind == VariableKind.StackSlot) && inst.Variable.LoadCount == 0) {
// traverse pre-order, so that we do not have to deal with more special cases afterwards
base.VisitStLoc(inst);
if (inst.Value is StLoc || inst.Value is CompoundAssignmentInstruction) {
context.Step($"Remove unused variable assignment {inst.Variable.Name}", inst);
inst.ReplaceWith(inst.Value);
}
return;
}
if (inst.Value.MatchLdLoc(out var displayClassVariable) && displayClasses.TryGetValue(displayClassVariable, out var displayClass)) {
context.Step($"Found copy-assignment of display-class variable {displayClassVariable.Name}", inst);
displayClasses.Add(inst.Variable, displayClass);
instructionsToRemove.Add(inst);
if (displayClasses.TryGetValue(inst.Variable, out displayClass) && displayClass.Initializer == inst) {
// inline contents of object initializer block
if (inst.Value is Block initBlock && initBlock.Kind == BlockKind.ObjectInitializer) {
context.Step($"Remove initializer of {inst.Variable.Name}", inst);
for (int i = 1; i < initBlock.Instructions.Count; i++) {
var stobj = (StObj)initBlock.Instructions[i];
var variable = displayClass.VariablesToDeclare[(IField)((LdFlda)stobj.Target).Field.MemberDefinition];
parentBlock.Instructions.Insert(inst.ChildIndex + i, new StLoc(variable.GetOrDeclare(), stobj.Value).WithILRange(stobj));
}
}
context.Step($"Remove initializer of {inst.Variable.Name}", inst);
parentBlock.Instructions.Remove(inst);
return;
}
if (inst.Value is LdLoc || inst.Value is LdObj) {
// in some cases (e.g. if inlining fails), there can be a reference to a display class in a stack slot,
// in that case it is necessary to resolve the reference and iff it can be propagated, replace all loads
// of the single-definition.
var referencedDisplayClass = ResolveVariableToPropagate(inst.Value);
if (referencedDisplayClass != null && displayClasses.TryGetValue(referencedDisplayClass, out _)) {
context.Step($"Propagate reference to {referencedDisplayClass.Name} in {inst.Variable}", inst);
foreach (var ld in inst.Variable.LoadInstructions.ToArray()) {
ld.ReplaceWith(new LdLoc(referencedDisplayClass).WithILRange(ld));
}
parentBlock.Instructions.Remove(inst);
return;
}
}
}
base.VisitStLoc(inst);
}
protected internal override void VisitStObj(StObj inst)
{
inst.Value.AcceptVisitor(this);
if (inst.Parent is Block) {
if (IsParameterAssignment(inst, out var displayClass, out var field, out var parameter)) {
context.Step($"Detected parameter assignment {parameter.Name}", inst);
displayClass.Variables.Add((IField)field.MemberDefinition, parameter);
instructionsToRemove.Add(inst);
return;
}
if (IsDisplayClassAssignment(inst, out displayClass, out field, out var variable)) {
context.Step($"Detected display-class assignment {variable.Name}", inst);
displayClass.Variables.Add((IField)field.MemberDefinition, variable);
instructionsToRemove.Add(inst);
return;
if (IsDisplayClassFieldAccess(inst.Target, out var v, out var displayClass, out var field)) {
VariableToDeclare vd = displayClass.VariablesToDeclare[(IField)field.MemberDefinition];
if (vd.CanPropagate && vd.Initializers.Contains(inst)) {
if (inst.Parent is Block containingBlock) {
context.Step($"Remove initializer of {v.Name}.{vd.Name} due to propagation", inst);
containingBlock.Instructions.Remove(inst);
return;
}
}
}
inst.Target.AcceptVisitor(this);
base.VisitStObj(inst);
EarlyExpressionTransforms.StObjToStLoc(inst, context);
}
@ -366,36 +717,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -366,36 +717,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false;
}
private bool IsDisplayClassAssignment(StObj inst, out DisplayClass displayClass, out IField field, out ILVariable variable)
{
variable = null;
if (!IsDisplayClassFieldAccess(inst.Target, out var displayClassVar, out displayClass, out field))
return false;
if (!(inst.Value.MatchLdLoc(out var v) && displayClasses.ContainsKey(v)))
return false;
if (displayClassVar.Function != currentFunction)
return false;
variable = v;
return true;
}
private bool IsParameterAssignment(StObj inst, out DisplayClass displayClass, out IField field, out ILVariable parameter)
{
parameter = null;
if (!IsDisplayClassFieldAccess(inst.Target, out var displayClassVar, out displayClass, out field))
return false;
if (fieldAssignmentsWithVariableValue[field].Count != 1)
return false;
if (!(inst.Value.MatchLdLoc(out var v) && v.Kind == VariableKind.Parameter && v.Function == currentFunction && v.Type.Equals(field.Type)))
return false;
if (displayClass.Variables.ContainsKey((IField)field.MemberDefinition))
return false;
if (displayClassVar.Function != currentFunction)
return false;
parameter = v;
return true;
}
private bool IsDisplayClassFieldAccess(ILInstruction inst, out ILVariable displayClassVar, out DisplayClass displayClass, out IField field)
{
displayClass = null;
@ -414,23 +735,16 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -414,23 +735,16 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// Get display class info
if (!IsDisplayClassFieldAccess(inst, out _, out DisplayClass displayClass, out IField field))
return;
// We want the specialized version, so that display-class type parameters are
// substituted with the type parameters from the use-site.
var fieldType = field.Type;
// However, use the unspecialized member definition to make reference comparisons in dictionary possible.
field = (IField)field.MemberDefinition;
if (!displayClass.Variables.TryGetValue(field, out var v)) {
context.Step($"Introduce captured variable for {field.FullName}", inst);
// Introduce a fresh variable for the display class field.
Debug.Assert(displayClass.Definition == field.DeclaringTypeDefinition);
v = displayClass.DeclaringFunction.RegisterVariable(VariableKind.Local, fieldType, field.Name);
v.HasInitialValue = true;
v.CaptureScope = displayClass.CaptureScope;
inst.ReplaceWith(new LdLoca(v).WithILRange(inst));
displayClass.Variables.Add(field, v);
} else {
context.Step($"Reuse captured variable {v.Name} for {field.FullName}", inst);
inst.ReplaceWith(new LdLoca(v).WithILRange(inst));
var keyField = (IField)field.MemberDefinition;
var v = displayClass.VariablesToDeclare[keyField];
context.Step($"Replace {field.Name} with captured variable {v.Name}", inst);
ILVariable variable = v.GetOrDeclare();
inst.ReplaceWith(new LdLoca(variable).WithILRange(inst));
// add captured variable to all descendant functions from the declaring function to this use-site function
foreach (var f in currentFunctions) {
if (f == variable.Function)
break;
f.CapturedVariables.Add(variable);
}
}
}

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

@ -1229,6 +1229,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -1229,6 +1229,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
if (ldloc.Variable.CaptureScope == null) {
ldloc.Variable.CaptureScope = BlockContainer.FindClosestContainer(context);
var f = ldloc.Variable.CaptureScope.Ancestors.OfType<ILFunction>().FirstOrDefault();
if (f != null) {
f.CapturedVariables.Add(ldloc.Variable);
}
}
return ldloc;
} else {

9
ILSpy/Properties/Resources.Designer.cs generated

@ -576,6 +576,15 @@ namespace ICSharpCode.ILSpy.Properties { @@ -576,6 +576,15 @@ namespace ICSharpCode.ILSpy.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Aggressively perform Scalar Replacement Of Aggregates (SROA).
/// </summary>
public static string DecompilerSettings_AggressiveScalarReplacementOfAggregates {
get {
return ResourceManager.GetString("DecompilerSettings.AggressiveScalarReplacementOfAggregates", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Allow extension &apos;Add&apos; methods in collection initializer expressions.
/// </summary>

Loading…
Cancel
Save