Browse Source

Fix initializer block detection of display structs.

pull/2005/head
Siegfried Pammer 5 years ago
parent
commit
d4fd92bf0f
  1. 114
      ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.Expected.cs
  2. 118
      ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.cs
  3. 344
      ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.opt.roslyn.il
  4. 390
      ICSharpCode.Decompiler.Tests/TestCases/Ugly/AggressiveScalarReplacementOfAggregates.roslyn.il
  5. 8
      ICSharpCode.Decompiler.Tests/UglyTestRunner.cs
  6. 122
      ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs

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

@ -0,0 +1,114 @@
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);
}
}
}
}

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

@ -0,0 +1,118 @@
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;
}
}
}
}
}

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

@ -0,0 +1,344 @@
// 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 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 ***********************

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

@ -0,0 +1,390 @@
// 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 specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: nop
IL_0007: ret
} // end of method Program::.ctor
} // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.Program
// =============================================================
// *********** DISASSEMBLY COMPLETE ***********************

8
ICSharpCode.Decompiler.Tests/UglyTestRunner.cs

@ -105,6 +105,14 @@ namespace ICSharpCode.Decompiler.Tests
RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings(CSharp.LanguageVersion.CSharp1)); 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) 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); Run(testName, asmOptions | AssemblerOptions.Library, cscOptions | CompilerOptions.Library, decompilerSettings);

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

@ -55,7 +55,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
public bool CanPropagate { get; private set; } public bool CanPropagate { get; private set; }
public ILInstruction Initializer { get; set; } public HashSet<ILInstruction> Initializers { get; } = new HashSet<ILInstruction>();
public VariableToDeclare(DisplayClass container, IField field, ILVariable declaredVariable = null) public VariableToDeclare(DisplayClass container, IField field, ILVariable declaredVariable = null)
{ {
@ -149,10 +149,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
foreach (var f in function.Descendants.OfType<ILFunction>()) { foreach (var f in function.Descendants.OfType<ILFunction>()) {
foreach (var v in f.Variables.ToArray()) { foreach (var v in f.Variables.ToArray()) {
var result = AnalyzeVariable(v); var result = AnalyzeVariable(v);
if (result == null) if (result == null || displayClasses.ContainsKey(result.Variable))
continue; continue;
context.Step($"Detected display-class {v}", result.Initializer ?? f.Body); context.Step($"Detected display-class {result.Variable}", result.Initializer ?? f.Body);
displayClasses.Add(v, result); displayClasses.Add(result.Variable, result);
} }
} }
@ -196,10 +196,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
case VariableKind.Local: case VariableKind.Local:
case VariableKind.DisplayClassLocal: case VariableKind.DisplayClassLocal:
return DetectDisplayClass(v); return DetectDisplayClass(v);
case VariableKind.InitializerTarget:
return DetectDisplayClassInitializer(v);
case VariableKind.PinnedLocal: case VariableKind.PinnedLocal:
case VariableKind.UsingLocal: case VariableKind.UsingLocal:
case VariableKind.ForeachLocal: case VariableKind.ForeachLocal:
case VariableKind.InitializerTarget:
case VariableKind.NamedArgument: case VariableKind.NamedArgument:
case VariableKind.ExceptionStackSlot: case VariableKind.ExceptionStackSlot:
case VariableKind.ExceptionLocal: case VariableKind.ExceptionLocal:
@ -254,7 +255,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (v.StoreCount != 1) if (v.StoreCount != 1)
return null; return null;
result = new DisplayClass(v, definition) { CaptureScope = v.CaptureScope }; result = new DisplayClass(v, definition) { CaptureScope = v.CaptureScope };
initBlock = (v.Function.Body as BlockContainer)?.EntryPoint; initBlock = FindDisplayStructInitBlock(v);
startIndex = 0; startIndex = 0;
break; break;
default: default:
@ -281,6 +282,78 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return result; return result;
} }
private Block FindDisplayStructInitBlock(ILVariable v)
{
var root = v.Function.Body;
var block = Visit(root)?.Ancestors.OfType<Block>().FirstOrDefault();
return block;
ILInstruction Visit(ILInstruction inst)
{
switch (inst) {
case LdLoc l:
return l.Variable == v ? l : null;
case StLoc s:
return s.Variable == v ? s : null;
case LdLoca la:
return la.Variable == v ? la : null;
default:
return VisitChildren(inst);
}
}
ILInstruction VisitChildren(ILInstruction inst)
{
ILInstruction result = null;
foreach (var child in inst.Children) {
var newResult = Visit(child);
if (result == null) {
result = newResult;
} else if (newResult != null) {
return inst;
}
}
return result;
}
}
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 (definition == null)
return null;
if (!context.Settings.AggressiveScalarReplacementOfAggregates) {
if (definition.DeclaringTypeDefinition == null || definition.ParentModule.PEFile != context.PEFile)
return null;
if (!IsPotentialClosure(context, definition))
return null;
}
if (!ValidateConstructor(newObj.Method))
return null;
if (!initializerBlock.Parent.MatchStLoc(out var referenceVariable))
return null;
var result = new DisplayClass(referenceVariable, definition) {
CaptureScope = referenceVariable.CaptureScope,
Initializer = initializerBlock.Parent
};
for (int i = 1; i < initializerBlock.Instructions.Count; i++) {
var init = initializerBlock.Instructions[i];
if (!init.MatchStFld(out var target, out var field, out _))
break;
if (!target.MatchLdLocRef(v))
break;
if (result.VariablesToDeclare.ContainsKey((IField)field.MemberDefinition))
break;
var variable = AddVariable(result, (StObj)init, field);
result.VariablesToDeclare[(IField)field.MemberDefinition] = variable;
}
return result;
}
private bool ValidateConstructor(IMethod method) private bool ValidateConstructor(IMethod method)
{ {
try { try {
@ -339,7 +412,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
&& (variableToPropagate.Kind == VariableKind.StackSlot || variableToPropagate.Type.Equals(field.Type))) && (variableToPropagate.Kind == VariableKind.StackSlot || variableToPropagate.Type.Equals(field.Type)))
{ {
variable.Propagate(variableToPropagate); variable.Propagate(variableToPropagate);
variable.Initializer = statement; variable.Initializers.Add(statement);
} }
return variable; return variable;
} }
@ -351,11 +424,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
case LdFlda ldflda when ldflda.MatchLdFlda(out _, out field): case LdFlda ldflda when ldflda.MatchLdFlda(out _, out field):
var keyField = (IField)field.MemberDefinition; var keyField = (IField)field.MemberDefinition;
if (!container.VariablesToDeclare.TryGetValue(keyField, out VariableToDeclare variable) || variable == null) { if (!container.VariablesToDeclare.TryGetValue(keyField, out VariableToDeclare variable) || variable == null) {
StObj stobj = ldflda.Parent as StObj; variable = AddVariable(container, null, field);
if (!(stobj != null && stobj.Value.MatchLdLocRef(out var v) && displayClasses.ContainsKey(v))) {
stobj = null;
}
variable = AddVariable(container, stobj, field);
} }
container.VariablesToDeclare[keyField] = variable; container.VariablesToDeclare[keyField] = variable;
return variable != null; return variable != null;
@ -375,8 +444,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
VisitILFunction(function); VisitILFunction(function);
foreach (var displayClass in displayClasses.Values) { foreach (var displayClass in displayClasses.Values) {
foreach (var v in displayClass.VariablesToDeclare.Values) { foreach (var v in displayClass.VariablesToDeclare.Values) {
if (v.CanPropagate && v.Initializer != null) { if (v.CanPropagate && v.Initializers.Count == 1) {
instructionsToRemove.Add(v.Initializer); instructionsToRemove.Add(v.Initializers.First());
} }
} }
} }
@ -531,7 +600,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
protected override void Default(ILInstruction inst) 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); child.AcceptVisitor(this);
} }
} }
@ -540,7 +611,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{ {
base.VisitStLoc(inst); base.VisitStLoc(inst);
if (inst.Parent is Block && inst.Variable.IsSingleDefinition) { if (inst.Parent is Block parentBlock && inst.Variable.IsSingleDefinition) {
if ((inst.Variable.Kind == VariableKind.Local || inst.Variable.Kind == VariableKind.StackSlot) && inst.Variable.LoadCount == 0 && (inst.Value is StLoc || inst.Value is CompoundAssignmentInstruction)) { if ((inst.Variable.Kind == VariableKind.Local || inst.Variable.Kind == VariableKind.StackSlot) && inst.Variable.LoadCount == 0 && (inst.Value is StLoc || inst.Value is CompoundAssignmentInstruction)) {
context.Step($"Remove unused variable assignment {inst.Variable.Name}", inst); context.Step($"Remove unused variable assignment {inst.Variable.Name}", inst);
inst.ReplaceWith(inst.Value); inst.ReplaceWith(inst.Value);
@ -550,7 +621,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms
instructionsToRemove.Add(inst); instructionsToRemove.Add(inst);
return; return;
} }
if (inst.Value.MatchLdLocRef(out var otherVariable) && displayClasses.TryGetValue(otherVariable, out var displayClass)) { if (displayClasses.TryGetValue(inst.Variable, out var displayClass) && inst.Value is Block initBlock && initBlock.Kind == BlockKind.ObjectInitializer) {
instructionsToRemove.Add(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));
}
return;
}
// TODO : this is dangerous!
if (inst.Value.MatchLdLocRef(out var otherVariable) && displayClasses.TryGetValue(otherVariable, out displayClass)) {
instructionsToRemove.Add(inst); instructionsToRemove.Add(inst);
displayClasses.Add(inst.Variable, displayClass); displayClasses.Add(inst.Variable, displayClass);
} }
@ -566,8 +647,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (inst != instructions[childIndex]) { if (inst != instructions[childIndex]) {
foreach (var displayClass in displayClasses.Values) { foreach (var displayClass in displayClasses.Values) {
foreach (var v in displayClass.VariablesToDeclare.Values) { foreach (var v in displayClass.VariablesToDeclare.Values) {
if (v.CanPropagate && v.Initializer == inst) { if (v.CanPropagate) {
v.Initializer = instructions[childIndex]; if (v.Initializers.Contains(inst)) {
v.Initializers.Add(instructions[childIndex]);
v.Initializers.Remove(inst);
}
} }
} }
} }

Loading…
Cancel
Save