diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
index f14b1077e..546cd2ea7 100644
--- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
+++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
@@ -92,6 +92,8 @@
+
+
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs
index 013111e78..1f73795e2 100644
--- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs
@@ -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)delegate {
+ t0 = 0;
+ t2 = 0;
+ return ZZZ_1();
+ })();
+ }
+ int ZZZ_1()
+ {
+ t0 = 0;
+ int t1 = t0;
+#if !OPT
+ Func func = delegate {
+#else
+ return ((Func)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)delegate {
+ t0 = 0;
+ t2 = 0;
+ return ZZZ_1();
+ })();
+ }
+ int ZZZ_1()
+ {
+ t0 = 0;
+ int t1 = t0;
+#if !OPT
+ Func func = delegate {
+#else
+ return ((Func)delegate {
+#endif
+ t0 = 0;
+ t1 = 0;
+ return 0;
+#if !OPT
+ };
+ return func();
+#else
+ })();
+#endif
+ }
+ }
+ }
}
}
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.Expected.cs b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.Expected.cs
new file mode 100644
index 000000000..fb7860794
--- /dev/null
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.Expected.cs
@@ -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);
+// }
+ }
+}
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.cs b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.cs
new file mode 100644
index 000000000..0fb451750
--- /dev/null
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.cs
@@ -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);
+// }
+ }
+}
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.opt.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.opt.roslyn.il
new file mode 100644
index 000000000..c2ec15ad8
--- /dev/null
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.opt.roslyn.il
@@ -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 ***********************
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.roslyn.il
new file mode 100644
index 000000000..a51b839f8
--- /dev/null
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.roslyn.il
@@ -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 ***********************
diff --git a/ICSharpCode.Decompiler.Tests/UglyTestRunner.cs b/ICSharpCode.Decompiler.Tests/UglyTestRunner.cs
index b69e33bae..db5f8c4da 100644
--- a/ICSharpCode.Decompiler.Tests/UglyTestRunner.cs
+++ b/ICSharpCode.Decompiler.Tests/UglyTestRunner.cs
@@ -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);
diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs
index 68a3daf8e..9eeec424c 100644
--- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs
+++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs
@@ -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 {
diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs b/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs
index e531b23d0..2aad6a16d 100644
--- a/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs
+++ b/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs
@@ -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
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() 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
} else {
v = new VariableToDeclare(variable, variable.HasInitialValue,
newPoint, identExpr, sourceOrder: variableDict.Count);
- variableDict.Add(rr.Variable, v);
+ variableDict.Add(variable, v);
}
}
}
diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs
index 8dd738273..3e8349bd2 100644
--- a/ICSharpCode.Decompiler/DecompilerSettings.cs
+++ b/ICSharpCode.Decompiler/DecompilerSettings.cs
@@ -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)]
diff --git a/ICSharpCode.Decompiler/IL/ILVariable.cs b/ICSharpCode.Decompiler/IL/ILVariable.cs
index 60423625c..77b6dbae5 100644
--- a/ICSharpCode.Decompiler/IL/ILVariable.cs
+++ b/ICSharpCode.Decompiler/IL/ILVariable.cs
@@ -337,6 +337,17 @@ namespace ICSharpCode.Decompiler.IL
}
}
+ ///
+ /// Gets whether the variable is dead - unused.
+ ///
+ public bool IsDead {
+ get {
+ return StoreCount == (HasInitialValue ? 1 : 0)
+ && LoadCount == 0
+ && AddressCount == 0;
+ }
+ }
+
///
/// The field which was converted to a local variable.
/// Set when the variable is from a 'yield return' or 'async' state machine.
diff --git a/ICSharpCode.Decompiler/IL/Instructions/Block.cs b/ICSharpCode.Decompiler/IL/Instructions/Block.cs
index 5bb1a2dae..87ba58072 100644
--- a/ICSharpCode.Decompiler/IL/Instructions/Block.cs
+++ b/ICSharpCode.Decompiler/IL/Instructions/Block.cs
@@ -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
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;
}
}
diff --git a/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs b/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
index ab3caa5cf..7291484b0 100644
--- a/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
+++ b/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
@@ -73,6 +73,12 @@ namespace ICSharpCode.Decompiler.IL
///
public BlockContainer DeclarationScope { get; internal set; }
+ ///
+ /// Gets the set of captured variables by this ILFunction.
+ ///
+ /// This is populated by the step.
+ public HashSet CapturedVariables { get; } = new HashSet();
+
///
/// List of warnings of ILReader.
///
diff --git a/ICSharpCode.Decompiler/IL/Instructions/ILVariableCollection.cs b/ICSharpCode.Decompiler/IL/Instructions/ILVariableCollection.cs
index c3c53092f..f27d962a7 100644
--- a/ICSharpCode.Decompiler/IL/Instructions/ILVariableCollection.cs
+++ b/ICSharpCode.Decompiler/IL/Instructions/ILVariableCollection.cs
@@ -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++;
diff --git a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs
index 5d68b4131..b5dc0423f 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs
@@ -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
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
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
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 GetTransforms()
{
yield return new CombineExitsTransform();
@@ -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) {
diff --git a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs
index 073fe8f8f..1f59d6df5 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs
@@ -46,6 +46,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
public List UseSites;
public IMethod Method;
public ILFunction Definition;
+ ///
+ /// 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.
+ ///
+ public Dictionary> LocalFunctionArguments;
}
///
@@ -78,69 +84,89 @@ namespace ICSharpCode.Decompiler.IL.Transforms
this.context = context;
this.resolveContext = new SimpleTypeResolveContext(function.Method);
var localFunctions = new Dictionary();
- 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.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.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().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.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
}
}
+ private void PropagateClosureParameterArguments(Dictionary localFunctions)
+ {
+ foreach (var localFunction in context.Function.Descendants.OfType()) {
+ 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();
+ info.LocalFunctionArguments.Add(index, arguments);
+ }
+ arguments.Add(argument);
+ }
+ }
+ }
+
+ private ILInstruction FindCompatibleArgument(LocalFunctionInfo info, IList 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().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
context.StepStartGroup($"Read local function '{targetMethod.Name}'", inst);
info = new LocalFunctionInfo() {
UseSites = new List() { inst },
+ LocalFunctionArguments = new Dictionary>(),
Method = (IMethod)targetMethod.MemberDefinition,
};
var rootFunction = context.Function;
@@ -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
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
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(closureVar.CaptureScope, additionalScope);
+ else {
+ BlockContainer combinedScope = FindCommonAncestorInstruction(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().First()))
function.DeclarationScope = FindCommonAncestorInstruction(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)
diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs
index 894194b43..ee619d6a8 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs
@@ -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
public override string ToString() => $"[{Member}, {Indices}]";
public static (AccessPathKind Kind, List Path, List Values, ILVariable Target) GetAccessPath(
- ILInstruction instruction, IType rootType, DecompilerSettings settings,
+ ILInstruction instruction, IType rootType, DecompilerSettings settings = null,
CSharpTypeResolveContext resolveContext = null,
Dictionary possibleIndexVariables = null)
{
@@ -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()) {
@@ -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)
diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs
index 5592a588d..3f11ee01d 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs
@@ -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
///
/// Transforms closure fields to local variables.
///
- /// This is a post-processing step of , and .
+ /// This is a post-processing step of ,
+ /// and .
+ ///
+ /// 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.
///
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 Initializers { get; } = new HashSet();
+
+ 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 Variables;
+ public readonly ILVariable Variable;
+ public readonly ITypeDefinition Type;
+ public readonly Dictionary VariablesToDeclare;
public BlockContainer CaptureScope;
- public ILFunction DeclaringFunction;
+ public ILInstruction Initializer;
+
+ public DisplayClass(ILVariable variable, ITypeDefinition type)
+ {
+ Variable = variable;
+ Type = type;
+ VariablesToDeclare = new Dictionary();
+ }
}
ILTransformContext context;
+ ITypeResolveContext decompilationContext;
readonly Dictionary displayClasses = new Dictionary();
- readonly List instructionsToRemove = new List();
- readonly MultiDictionary fieldAssignmentsWithVariableValue = new MultiDictionary();
- 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()) {
- 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().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)
+ ///
+ /// 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.
+ ///
+ 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(),
- CaptureScope = (isMono && IsMonoNestedCaptureScope(closureType)) || localFunctionClosureParameter ? null : v.CaptureScope,
- DeclaringFunction = localFunctionClosureParameter ? f.DeclarationScope.Ancestors.OfType().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().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
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
/// mcs likes to optimize closures in yield state machines away by moving the captured variables' fields into the state machine type,
/// We construct a that spans the whole method body.
///
- 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(),
- 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
}
}
- 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().Last().Method);
@@ -292,63 +619,87 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true;
}
- ILFunction currentFunction;
+ readonly Stack currentFunctions = new Stack();
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
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
// 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);
}
}
}
diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs
index 2ed286a3b..14d847ba9 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs
@@ -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().FirstOrDefault();
+ if (f != null) {
+ f.CapturedVariables.Add(ldloc.Variable);
+ }
}
return ldloc;
} else {
diff --git a/ILSpy/Properties/Resources.Designer.cs b/ILSpy/Properties/Resources.Designer.cs
index 3f8e88ef2..50e640b22 100644
--- a/ILSpy/Properties/Resources.Designer.cs
+++ b/ILSpy/Properties/Resources.Designer.cs
@@ -576,6 +576,15 @@ namespace ICSharpCode.ILSpy.Properties {
}
}
+ ///
+ /// Looks up a localized string similar to Aggressively perform Scalar Replacement Of Aggregates (SROA).
+ ///
+ public static string DecompilerSettings_AggressiveScalarReplacementOfAggregates {
+ get {
+ return ResourceManager.GetString("DecompilerSettings.AggressiveScalarReplacementOfAggregates", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Allow extension 'Add' methods in collection initializer expressions.
///