diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index 6b5055438..0572fe08c 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -32,8 +32,8 @@ - - + + diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/CompoundAssignment.cs b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/CompoundAssignment.cs index 246d45af0..096ae9b17 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/CompoundAssignment.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/CompoundAssignment.cs @@ -26,6 +26,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness static void Main() { PreIncrementProperty(); + PreIncrementIndexer(); + CallTwice(); + UnsignedShiftRightInstanceField(); + UnsignedShiftRightStaticProperty(); } static void Test(int a, int b) @@ -41,16 +45,19 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness return ++x; } + static int instanceCount; + int instanceNumber = ++instanceCount; + int instanceField; public int InstanceProperty { get { - Console.WriteLine("In get_InstanceProperty"); + Console.WriteLine("In {0}.get_InstanceProperty", instanceNumber); return instanceField; } set { - Console.WriteLine("In set_InstanceProperty, value=" + value); + Console.WriteLine("In {0}.set_InstanceProperty, value=" + value, instanceNumber); instanceField = value; } } @@ -72,7 +79,13 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness public static Dictionary GetDict() { Console.WriteLine("In GetDict()"); - return new Dictionary(); + return new Dictionary() { { GetString(), 5 } }; + } + + static CompoundAssignment GetObject() + { + Console.WriteLine("In GetObject() (instance #)"); + return new CompoundAssignment(); } static string GetString() @@ -93,5 +106,32 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness Console.WriteLine("PreIncrementIndexer:"); Test(X(), ++GetDict()[GetString()]); } + + static void CallTwice() + { + Console.WriteLine("CallTwice: instanceField:"); + GetObject().instanceField = GetObject().instanceField + 1; + Test(X(), GetObject().instanceField = GetObject().instanceField + 1); + Console.WriteLine("CallTwice: InstanceProperty:"); + GetObject().InstanceProperty = GetObject().InstanceProperty + 1; + Test(X(), GetObject().InstanceProperty = GetObject().InstanceProperty + 1); + Console.WriteLine("CallTwice: dict indexer:"); + GetDict()[GetString()] = GetDict()[GetString()] + 1; + Test(X(), GetDict()[GetString()] = GetDict()[GetString()] + 1); + } + + static void UnsignedShiftRightInstanceField() + { +#if !LEGACY_CSC + ref int f = ref new CompoundAssignment().instanceField; + Test(X(), f = (int)((uint)f >> 2)); +#endif + } + + static void UnsignedShiftRightStaticProperty() + { + StaticProperty = -15; + Test(X(), StaticProperty = (int)((uint)StaticProperty >> 2)); + } } } \ No newline at end of file diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs index d28ca3a29..4730703aa 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs @@ -219,32 +219,92 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { return --array[pos]; } - + + public int PostIncrementArrayElement(int[] array, int pos) + { + return array[pos]++; + } + + public void IncrementArrayElement(int[] array, int pos) + { + array[pos]++; + } + public int PreIncrementInstanceField() { return ++this.M().Field; } + //public int PostIncrementInstanceField() + //{ + // return this.M().Field++; + //} + + public void IncrementInstanceField() + { + this.M().Field++; + } + public int PreIncrementInstanceField2(MutableClass m) { return ++m.Field; } + //public int PostIncrementInstanceField2(MutableClass m) + //{ + // return m.Field++; + //} + + public void IncrementInstanceField2(MutableClass m) + { + m.Field++; + } + public int PreIncrementInstanceProperty() { return ++this.M().Property; } - + + //public int PostIncrementInstanceProperty() + //{ + // return this.M().Property++; + //} + + public void IncrementInstanceProperty() + { + this.M().Property++; + } + public int PreIncrementStaticField() { return ++CompoundAssignmentTest.StaticField; } - + + public int PostIncrementStaticField() + { + return CompoundAssignmentTest.StaticField++; + } + + public void IncrementStaticField() + { + CompoundAssignmentTest.StaticField++; + } + public int PreIncrementStaticProperty() { return ++CompoundAssignmentTest.StaticProperty; } + //public int PostIncrementStaticProperty() + //{ + // return CompoundAssignmentTest.StaticProperty++; + //} + + public void IncrementStaticProperty() + { + CompoundAssignmentTest.StaticProperty++; + } + private static Item GetItem(object obj) { return null; diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.il index 9943dec1c..dfc206ce2 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.il @@ -1,5 +1,5 @@ -// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.17929 +// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.18020 // Copyright (c) Microsoft Corporation. All rights reserved. @@ -10,7 +10,7 @@ .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } -.assembly wswrh1ww +.assembly kiryblux { .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 @@ -20,15 +20,15 @@ .hash algorithm 0x00008004 .ver 0:0:0:0 } -.module wswrh1ww.dll -// MVID: {0FA9B26D-B874-4697-B526-76EF6D2DAD90} +.module kiryblux.dll +// MVID: {34EC794D-1FFD-488F-9BDC-2D5D9C15854D} .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 -// Image base: 0x029E0000 +// Image base: 0x02A30000 // =============== CLASS MEMBERS DECLARATION =================== @@ -762,6 +762,51 @@ IL_001c: ret } // end of method CompoundAssignmentTest::PreIncrementArrayElement + .method public hidebysig instance int32 + PostIncrementArrayElement(int32[] 'array', + int32 pos) cil managed + { + // Code size 29 (0x1d) + .maxstack 3 + .locals init (int32 V_0, + int32 V_1) + IL_0000: nop + IL_0001: ldarg.1 + IL_0002: ldarg.2 + IL_0003: ldelema [mscorlib]System.Int32 + IL_0008: dup + IL_0009: ldobj [mscorlib]System.Int32 + IL_000e: dup + IL_000f: stloc.1 + IL_0010: ldc.i4.1 + IL_0011: add + IL_0012: stobj [mscorlib]System.Int32 + IL_0017: ldloc.1 + IL_0018: stloc.0 + IL_0019: br.s IL_001b + + IL_001b: ldloc.0 + IL_001c: ret + } // end of method CompoundAssignmentTest::PostIncrementArrayElement + + .method public hidebysig instance void + IncrementArrayElement(int32[] 'array', + int32 pos) cil managed + { + // Code size 22 (0x16) + .maxstack 8 + IL_0000: nop + IL_0001: ldarg.1 + IL_0002: ldarg.2 + IL_0003: ldelema [mscorlib]System.Int32 + IL_0008: dup + IL_0009: ldobj [mscorlib]System.Int32 + IL_000e: ldc.i4.1 + IL_000f: add + IL_0010: stobj [mscorlib]System.Int32 + IL_0015: ret + } // end of method CompoundAssignmentTest::IncrementArrayElement + .method public hidebysig instance int32 PreIncrementInstanceField() cil managed { @@ -787,6 +832,22 @@ IL_001b: ret } // end of method CompoundAssignmentTest::PreIncrementInstanceField + .method public hidebysig instance void + IncrementInstanceField() cil managed + { + // Code size 21 (0x15) + .maxstack 8 + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::M() + IL_0007: dup + IL_0008: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::Field + IL_000d: ldc.i4.1 + IL_000e: add + IL_000f: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::Field + IL_0014: ret + } // end of method CompoundAssignmentTest::IncrementInstanceField + .method public hidebysig instance int32 PreIncrementInstanceField2(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass m) cil managed { @@ -811,6 +872,21 @@ IL_0016: ret } // end of method CompoundAssignmentTest::PreIncrementInstanceField2 + .method public hidebysig instance void + IncrementInstanceField2(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass m) cil managed + { + // Code size 16 (0x10) + .maxstack 8 + IL_0000: nop + IL_0001: ldarg.1 + IL_0002: dup + IL_0003: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::Field + IL_0008: ldc.i4.1 + IL_0009: add + IL_000a: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::Field + IL_000f: ret + } // end of method CompoundAssignmentTest::IncrementInstanceField2 + .method public hidebysig instance int32 PreIncrementInstanceProperty() cil managed { @@ -837,6 +913,23 @@ IL_001c: ret } // end of method CompoundAssignmentTest::PreIncrementInstanceProperty + .method public hidebysig instance void + IncrementInstanceProperty() cil managed + { + // Code size 22 (0x16) + .maxstack 8 + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::M() + IL_0007: dup + IL_0008: callvirt instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::get_Property() + IL_000d: ldc.i4.1 + IL_000e: add + IL_000f: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::set_Property(int32) + IL_0014: nop + IL_0015: ret + } // end of method CompoundAssignmentTest::IncrementInstanceProperty + .method public hidebysig instance int32 PreIncrementStaticField() cil managed { @@ -856,6 +949,38 @@ IL_0012: ret } // end of method CompoundAssignmentTest::PreIncrementStaticField + .method public hidebysig instance int32 + PostIncrementStaticField() cil managed + { + // Code size 19 (0x13) + .maxstack 3 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldsfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::StaticField + IL_0006: dup + IL_0007: ldc.i4.1 + IL_0008: add + IL_0009: stsfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::StaticField + IL_000e: stloc.0 + IL_000f: br.s IL_0011 + + IL_0011: ldloc.0 + IL_0012: ret + } // end of method CompoundAssignmentTest::PostIncrementStaticField + + .method public hidebysig instance void + IncrementStaticField() cil managed + { + // Code size 14 (0xe) + .maxstack 8 + IL_0000: nop + IL_0001: ldsfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::StaticField + IL_0006: ldc.i4.1 + IL_0007: add + IL_0008: stsfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::StaticField + IL_000d: ret + } // end of method CompoundAssignmentTest::IncrementStaticField + .method public hidebysig instance int32 PreIncrementStaticProperty() cil managed { @@ -876,6 +1001,20 @@ IL_0013: ret } // end of method CompoundAssignmentTest::PreIncrementStaticProperty + .method public hidebysig instance void + IncrementStaticProperty() cil managed + { + // Code size 15 (0xf) + .maxstack 8 + IL_0000: nop + IL_0001: call int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::get_StaticProperty() + IL_0006: ldc.i4.1 + IL_0007: add + IL_0008: call void ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::set_StaticProperty(int32) + IL_000d: nop + IL_000e: ret + } // end of method CompoundAssignmentTest::IncrementStaticProperty + .method private hidebysig static class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/Item GetItem(object obj) cil managed { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.opt.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.opt.il index 96ae56094..2dea51f56 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.opt.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.opt.il @@ -1,5 +1,5 @@ -// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.17929 +// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.18020 // Copyright (c) Microsoft Corporation. All rights reserved. @@ -10,7 +10,7 @@ .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } -.assembly ag42vicc +.assembly d4bhqxbe { .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 @@ -20,15 +20,15 @@ .hash algorithm 0x00008004 .ver 0:0:0:0 } -.module ag42vicc.dll -// MVID: {EB752380-6A62-4B20-9497-B68A784C5BD7} +.module d4bhqxbe.dll +// MVID: {B884BD61-6FE3-4893-B161-02EE675C0DF0} .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 -// Image base: 0x00DE0000 +// Image base: 0x003A0000 // =============== CLASS MEMBERS DECLARATION =================== @@ -647,6 +647,44 @@ IL_0017: ret } // end of method CompoundAssignmentTest::PreIncrementArrayElement + .method public hidebysig instance int32 + PostIncrementArrayElement(int32[] 'array', + int32 pos) cil managed + { + // Code size 24 (0x18) + .maxstack 3 + .locals init (int32 V_0) + IL_0000: ldarg.1 + IL_0001: ldarg.2 + IL_0002: ldelema [mscorlib]System.Int32 + IL_0007: dup + IL_0008: ldobj [mscorlib]System.Int32 + IL_000d: dup + IL_000e: stloc.0 + IL_000f: ldc.i4.1 + IL_0010: add + IL_0011: stobj [mscorlib]System.Int32 + IL_0016: ldloc.0 + IL_0017: ret + } // end of method CompoundAssignmentTest::PostIncrementArrayElement + + .method public hidebysig instance void + IncrementArrayElement(int32[] 'array', + int32 pos) cil managed + { + // Code size 21 (0x15) + .maxstack 8 + IL_0000: ldarg.1 + IL_0001: ldarg.2 + IL_0002: ldelema [mscorlib]System.Int32 + IL_0007: dup + IL_0008: ldobj [mscorlib]System.Int32 + IL_000d: ldc.i4.1 + IL_000e: add + IL_000f: stobj [mscorlib]System.Int32 + IL_0014: ret + } // end of method CompoundAssignmentTest::IncrementArrayElement + .method public hidebysig instance int32 PreIncrementInstanceField() cil managed { @@ -666,6 +704,21 @@ IL_0016: ret } // end of method CompoundAssignmentTest::PreIncrementInstanceField + .method public hidebysig instance void + IncrementInstanceField() cil managed + { + // Code size 20 (0x14) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::M() + IL_0006: dup + IL_0007: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::Field + IL_000c: ldc.i4.1 + IL_000d: add + IL_000e: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::Field + IL_0013: ret + } // end of method CompoundAssignmentTest::IncrementInstanceField + .method public hidebysig instance int32 PreIncrementInstanceField2(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass m) cil managed { @@ -684,6 +737,20 @@ IL_0011: ret } // end of method CompoundAssignmentTest::PreIncrementInstanceField2 + .method public hidebysig instance void + IncrementInstanceField2(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass m) cil managed + { + // Code size 15 (0xf) + .maxstack 8 + IL_0000: ldarg.1 + IL_0001: dup + IL_0002: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::Field + IL_0007: ldc.i4.1 + IL_0008: add + IL_0009: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::Field + IL_000e: ret + } // end of method CompoundAssignmentTest::IncrementInstanceField2 + .method public hidebysig instance int32 PreIncrementInstanceProperty() cil managed { @@ -703,6 +770,21 @@ IL_0016: ret } // end of method CompoundAssignmentTest::PreIncrementInstanceProperty + .method public hidebysig instance void + IncrementInstanceProperty() cil managed + { + // Code size 20 (0x14) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::M() + IL_0006: dup + IL_0007: callvirt instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::get_Property() + IL_000c: ldc.i4.1 + IL_000d: add + IL_000e: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::set_Property(int32) + IL_0013: ret + } // end of method CompoundAssignmentTest::IncrementInstanceProperty + .method public hidebysig instance int32 PreIncrementStaticField() cil managed { @@ -716,6 +798,31 @@ IL_000d: ret } // end of method CompoundAssignmentTest::PreIncrementStaticField + .method public hidebysig instance int32 + PostIncrementStaticField() cil managed + { + // Code size 14 (0xe) + .maxstack 8 + IL_0000: ldsfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::StaticField + IL_0005: dup + IL_0006: ldc.i4.1 + IL_0007: add + IL_0008: stsfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::StaticField + IL_000d: ret + } // end of method CompoundAssignmentTest::PostIncrementStaticField + + .method public hidebysig instance void + IncrementStaticField() cil managed + { + // Code size 13 (0xd) + .maxstack 8 + IL_0000: ldsfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::StaticField + IL_0005: ldc.i4.1 + IL_0006: add + IL_0007: stsfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::StaticField + IL_000c: ret + } // end of method CompoundAssignmentTest::IncrementStaticField + .method public hidebysig instance int32 PreIncrementStaticProperty() cil managed { @@ -729,6 +836,18 @@ IL_000d: ret } // end of method CompoundAssignmentTest::PreIncrementStaticProperty + .method public hidebysig instance void + IncrementStaticProperty() cil managed + { + // Code size 13 (0xd) + .maxstack 8 + IL_0000: call int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::get_StaticProperty() + IL_0005: ldc.i4.1 + IL_0006: add + IL_0007: call void ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::set_StaticProperty(int32) + IL_000c: ret + } // end of method CompoundAssignmentTest::IncrementStaticProperty + .method private hidebysig static class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/Item GetItem(object obj) cil managed { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.opt.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.opt.roslyn.il index a68694af0..36f83b082 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.opt.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.opt.roslyn.il @@ -1,5 +1,5 @@ -// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.17929 +// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.18020 // Copyright (c) Microsoft Corporation. All rights reserved. @@ -25,14 +25,14 @@ .ver 0:0:0:0 } .module CompoundAssignmentTest.dll -// MVID: {104CDF7A-FBAC-406D-AA60-4C777388A178} +// MVID: {6E5749EE-F4C5-4B86-96FC-F4A665551BA6} .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 -// Image base: 0x008E0000 +// Image base: 0x028A0000 // =============== CLASS MEMBERS DECLARATION =================== @@ -650,6 +650,44 @@ IL_000f: ret } // end of method CompoundAssignmentTest::PreIncrementArrayElement + .method public hidebysig instance int32 + PostIncrementArrayElement(int32[] 'array', + int32 pos) cil managed + { + // Code size 16 (0x10) + .maxstack 3 + .locals init (int32 V_0) + IL_0000: ldarg.1 + IL_0001: ldarg.2 + IL_0002: ldelema [mscorlib]System.Int32 + IL_0007: dup + IL_0008: ldind.i4 + IL_0009: stloc.0 + IL_000a: ldloc.0 + IL_000b: ldc.i4.1 + IL_000c: add + IL_000d: stind.i4 + IL_000e: ldloc.0 + IL_000f: ret + } // end of method CompoundAssignmentTest::PostIncrementArrayElement + + .method public hidebysig instance void + IncrementArrayElement(int32[] 'array', + int32 pos) cil managed + { + // Code size 13 (0xd) + .maxstack 8 + IL_0000: ldarg.1 + IL_0001: ldarg.2 + IL_0002: ldelema [mscorlib]System.Int32 + IL_0007: dup + IL_0008: ldind.i4 + IL_0009: ldc.i4.1 + IL_000a: add + IL_000b: stind.i4 + IL_000c: ret + } // end of method CompoundAssignmentTest::IncrementArrayElement + .method public hidebysig instance int32 PreIncrementInstanceField() cil managed { @@ -669,6 +707,21 @@ IL_0016: ret } // end of method CompoundAssignmentTest::PreIncrementInstanceField + .method public hidebysig instance void + IncrementInstanceField() cil managed + { + // Code size 20 (0x14) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::M() + IL_0006: dup + IL_0007: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::Field + IL_000c: ldc.i4.1 + IL_000d: add + IL_000e: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::Field + IL_0013: ret + } // end of method CompoundAssignmentTest::IncrementInstanceField + .method public hidebysig instance int32 PreIncrementInstanceField2(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass m) cil managed { @@ -687,6 +740,20 @@ IL_0011: ret } // end of method CompoundAssignmentTest::PreIncrementInstanceField2 + .method public hidebysig instance void + IncrementInstanceField2(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass m) cil managed + { + // Code size 15 (0xf) + .maxstack 8 + IL_0000: ldarg.1 + IL_0001: dup + IL_0002: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::Field + IL_0007: ldc.i4.1 + IL_0008: add + IL_0009: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::Field + IL_000e: ret + } // end of method CompoundAssignmentTest::IncrementInstanceField2 + .method public hidebysig instance int32 PreIncrementInstanceProperty() cil managed { @@ -706,6 +773,24 @@ IL_0016: ret } // end of method CompoundAssignmentTest::PreIncrementInstanceProperty + .method public hidebysig instance void + IncrementInstanceProperty() cil managed + { + // Code size 22 (0x16) + .maxstack 3 + .locals init (int32 V_0) + IL_0000: ldarg.0 + IL_0001: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::M() + IL_0006: dup + IL_0007: callvirt instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::get_Property() + IL_000c: stloc.0 + IL_000d: ldloc.0 + IL_000e: ldc.i4.1 + IL_000f: add + IL_0010: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::set_Property(int32) + IL_0015: ret + } // end of method CompoundAssignmentTest::IncrementInstanceProperty + .method public hidebysig instance int32 PreIncrementStaticField() cil managed { @@ -719,6 +804,31 @@ IL_000d: ret } // end of method CompoundAssignmentTest::PreIncrementStaticField + .method public hidebysig instance int32 + PostIncrementStaticField() cil managed + { + // Code size 14 (0xe) + .maxstack 8 + IL_0000: ldsfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::StaticField + IL_0005: dup + IL_0006: ldc.i4.1 + IL_0007: add + IL_0008: stsfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::StaticField + IL_000d: ret + } // end of method CompoundAssignmentTest::PostIncrementStaticField + + .method public hidebysig instance void + IncrementStaticField() cil managed + { + // Code size 13 (0xd) + .maxstack 8 + IL_0000: ldsfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::StaticField + IL_0005: ldc.i4.1 + IL_0006: add + IL_0007: stsfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::StaticField + IL_000c: ret + } // end of method CompoundAssignmentTest::IncrementStaticField + .method public hidebysig instance int32 PreIncrementStaticProperty() cil managed { @@ -732,6 +842,18 @@ IL_000d: ret } // end of method CompoundAssignmentTest::PreIncrementStaticProperty + .method public hidebysig instance void + IncrementStaticProperty() cil managed + { + // Code size 13 (0xd) + .maxstack 8 + IL_0000: call int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::get_StaticProperty() + IL_0005: ldc.i4.1 + IL_0006: add + IL_0007: call void ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::set_StaticProperty(int32) + IL_000c: ret + } // end of method CompoundAssignmentTest::IncrementStaticProperty + .method private hidebysig static class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/Item GetItem(object obj) cil managed { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.roslyn.il index 9b1077169..1404a451f 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.roslyn.il @@ -1,5 +1,5 @@ -// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.17929 +// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.18020 // Copyright (c) Microsoft Corporation. All rights reserved. @@ -25,14 +25,14 @@ .ver 0:0:0:0 } .module CompoundAssignmentTest.dll -// MVID: {E4D6BDFE-1178-4790-A328-6755C028F1BE} +// MVID: {391C1968-5381-4AC7-9C19-7B5F6ED36AF8} .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 -// Image base: 0x00D10000 +// Image base: 0x003D0000 // =============== CLASS MEMBERS DECLARATION =================== @@ -759,6 +759,51 @@ IL_0014: ret } // end of method CompoundAssignmentTest::PreIncrementArrayElement + .method public hidebysig instance int32 + PostIncrementArrayElement(int32[] 'array', + int32 pos) cil managed + { + // Code size 21 (0x15) + .maxstack 3 + .locals init (int32 V_0, + int32 V_1) + IL_0000: nop + IL_0001: ldarg.1 + IL_0002: ldarg.2 + IL_0003: ldelema [mscorlib]System.Int32 + IL_0008: dup + IL_0009: ldind.i4 + IL_000a: stloc.0 + IL_000b: ldloc.0 + IL_000c: ldc.i4.1 + IL_000d: add + IL_000e: stind.i4 + IL_000f: ldloc.0 + IL_0010: stloc.1 + IL_0011: br.s IL_0013 + + IL_0013: ldloc.1 + IL_0014: ret + } // end of method CompoundAssignmentTest::PostIncrementArrayElement + + .method public hidebysig instance void + IncrementArrayElement(int32[] 'array', + int32 pos) cil managed + { + // Code size 14 (0xe) + .maxstack 8 + IL_0000: nop + IL_0001: ldarg.1 + IL_0002: ldarg.2 + IL_0003: ldelema [mscorlib]System.Int32 + IL_0008: dup + IL_0009: ldind.i4 + IL_000a: ldc.i4.1 + IL_000b: add + IL_000c: stind.i4 + IL_000d: ret + } // end of method CompoundAssignmentTest::IncrementArrayElement + .method public hidebysig instance int32 PreIncrementInstanceField() cil managed { @@ -784,6 +829,22 @@ IL_001b: ret } // end of method CompoundAssignmentTest::PreIncrementInstanceField + .method public hidebysig instance void + IncrementInstanceField() cil managed + { + // Code size 21 (0x15) + .maxstack 8 + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::M() + IL_0007: dup + IL_0008: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::Field + IL_000d: ldc.i4.1 + IL_000e: add + IL_000f: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::Field + IL_0014: ret + } // end of method CompoundAssignmentTest::IncrementInstanceField + .method public hidebysig instance int32 PreIncrementInstanceField2(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass m) cil managed { @@ -808,6 +869,21 @@ IL_0016: ret } // end of method CompoundAssignmentTest::PreIncrementInstanceField2 + .method public hidebysig instance void + IncrementInstanceField2(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass m) cil managed + { + // Code size 16 (0x10) + .maxstack 8 + IL_0000: nop + IL_0001: ldarg.1 + IL_0002: dup + IL_0003: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::Field + IL_0008: ldc.i4.1 + IL_0009: add + IL_000a: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::Field + IL_000f: ret + } // end of method CompoundAssignmentTest::IncrementInstanceField2 + .method public hidebysig instance int32 PreIncrementInstanceProperty() cil managed { @@ -834,6 +910,26 @@ IL_001c: ret } // end of method CompoundAssignmentTest::PreIncrementInstanceProperty + .method public hidebysig instance void + IncrementInstanceProperty() cil managed + { + // Code size 24 (0x18) + .maxstack 3 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::M() + IL_0007: dup + IL_0008: callvirt instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::get_Property() + IL_000d: stloc.0 + IL_000e: ldloc.0 + IL_000f: ldc.i4.1 + IL_0010: add + IL_0011: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::set_Property(int32) + IL_0016: nop + IL_0017: ret + } // end of method CompoundAssignmentTest::IncrementInstanceProperty + .method public hidebysig instance int32 PreIncrementStaticField() cil managed { @@ -853,6 +949,38 @@ IL_0012: ret } // end of method CompoundAssignmentTest::PreIncrementStaticField + .method public hidebysig instance int32 + PostIncrementStaticField() cil managed + { + // Code size 19 (0x13) + .maxstack 3 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldsfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::StaticField + IL_0006: dup + IL_0007: ldc.i4.1 + IL_0008: add + IL_0009: stsfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::StaticField + IL_000e: stloc.0 + IL_000f: br.s IL_0011 + + IL_0011: ldloc.0 + IL_0012: ret + } // end of method CompoundAssignmentTest::PostIncrementStaticField + + .method public hidebysig instance void + IncrementStaticField() cil managed + { + // Code size 14 (0xe) + .maxstack 8 + IL_0000: nop + IL_0001: ldsfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::StaticField + IL_0006: ldc.i4.1 + IL_0007: add + IL_0008: stsfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::StaticField + IL_000d: ret + } // end of method CompoundAssignmentTest::IncrementStaticField + .method public hidebysig instance int32 PreIncrementStaticProperty() cil managed { @@ -873,6 +1001,20 @@ IL_0013: ret } // end of method CompoundAssignmentTest::PreIncrementStaticProperty + .method public hidebysig instance void + IncrementStaticProperty() cil managed + { + // Code size 15 (0xf) + .maxstack 8 + IL_0000: nop + IL_0001: call int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::get_StaticProperty() + IL_0006: ldc.i4.1 + IL_0007: add + IL_0008: call void ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::set_StaticProperty(int32) + IL_000d: nop + IL_000e: ret + } // end of method CompoundAssignmentTest::IncrementStaticProperty + .method private hidebysig static class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/Item GetItem(object obj) cil managed { diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 1503561f0..1d3990401 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -75,12 +75,8 @@ namespace ICSharpCode.Decompiler.CSharp new AsyncAwaitDecompiler(), // must run after inlining but before loop detection new DetectCatchWhenConditionBlocks(), // must run after inlining but before loop detection new DetectExitPoints(canIntroduceExitForReturn: false), - new BlockILTransform { - PostOrderTransforms = { - new ExpressionTransforms() // for RemoveDeadVariableInit - } - }, - // RemoveDeadVariableInit must run after ExpressionTransforms so that stobj(ldloca V, ...) + new EarlyExpressionTransforms(), + // RemoveDeadVariableInit must run after EarlyExpressionTransforms so that stobj(ldloca V, ...) // is already collapsed into stloc(V, ...). new RemoveDeadVariableInit(), new SplitVariables(), // split variables once again, because the stobj(ldloca V, ...) may open up new replacements @@ -107,8 +103,7 @@ namespace ICSharpCode.Decompiler.CSharp // and must run before NullCoalescingTransform new CachedDelegateInitialization(), new ILInlining(), - new TransformAssignment(), - new NullableLiftingBlockTransform(), + new TransformAssignment(), // must run before CopyPropagation new CopyPropagation(), new StatementTransform( // per-block transforms that depend on each other, and thus need to @@ -120,6 +115,7 @@ namespace ICSharpCode.Decompiler.CSharp // Any other transform that opens up new inlining opportunities should call RequestRerun(). new ExpressionTransforms(), new NullCoalescingTransform(), + new NullableLiftingStatementTransform(), new TransformArrayInitializers(), new TransformCollectionAndObjectInitializers() ) diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 387d453b5..581380e6d 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -49,8 +49,8 @@ - - + + @@ -271,6 +271,7 @@ + diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs b/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs index ec64edf8c..bc3a92038 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs @@ -127,7 +127,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow context.StepStartGroup("CleanUpBodyOfMoveNext", function); // Simplify stobj(ldloca) -> stloc foreach (var stobj in function.Descendants.OfType()) { - ExpressionTransforms.StObjToStLoc(stobj, context); + EarlyExpressionTransforms.StObjToStLoc(stobj, context); } // Copy-propagate temporaries holding a copy of 'this'. diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs index 48ecdf218..268b9a429 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs @@ -146,7 +146,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow targetBlock.Instructions.AddRange(nestedTrueBlock.Instructions); // add falseInsts to inner block nestedTrueBlock.Instructions.ReplaceList(falseInsts); - nestedIfInst.Condition.AcceptVisitor(new ExpressionTransforms { context = context }); + nestedIfInst.Condition.AcceptVisitor(new ExpressionTransforms { context = new StatementTransformContext(context) }); } break; } diff --git a/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs index 3289afc05..7d372c5cf 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs @@ -58,6 +58,7 @@ namespace ICSharpCode.Decompiler.IL public CompoundAssignmentInstruction(BinaryNumericInstruction binary, ILInstruction target, ILInstruction value, IType type, CompoundAssignmentType compoundAssignmentType) : base(OpCode.CompoundAssignmentInstruction) { + Debug.Assert(IsBinaryCompatibleWithType(binary, type)); this.CheckForOverflow = binary.CheckForOverflow; this.Sign = binary.Sign; this.LeftInputType = binary.LeftInputType; @@ -69,10 +70,28 @@ namespace ICSharpCode.Decompiler.IL this.Target = target; this.type = type; this.Value = value; + this.ILRange = binary.ILRange; Debug.Assert(compoundAssignmentType == CompoundAssignmentType.EvaluatesToNewValue || (Operator == BinaryNumericOperator.Add || Operator == BinaryNumericOperator.Sub)); Debug.Assert(IsValidCompoundAssignmentTarget(Target)); } + /// + /// Gets whether the specific binary instruction is compatible with a compound operation on the specified type. + /// + internal static bool IsBinaryCompatibleWithType(BinaryNumericInstruction binary, IType type) + { + if (binary.IsLifted) { + if (!NullableType.IsNullable(type)) + return false; + type = NullableType.GetUnderlyingType(type); + } + if (binary.Sign != Sign.None) { + if (type.GetSign() != binary.Sign) + return false; + } + return true; + } + internal static bool IsValidCompoundAssignmentTarget(ILInstruction inst) { switch (inst.OpCode) { diff --git a/ICSharpCode.Decompiler/IL/Transforms/EarlyExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/EarlyExpressionTransforms.cs new file mode 100644 index 000000000..c48bc0655 --- /dev/null +++ b/ICSharpCode.Decompiler/IL/Transforms/EarlyExpressionTransforms.cs @@ -0,0 +1,93 @@ +// Copyright (c) 2017 Daniel Grunwald +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using ICSharpCode.Decompiler.TypeSystem; + +namespace ICSharpCode.Decompiler.IL.Transforms +{ + class EarlyExpressionTransforms : ILVisitor, IILTransform + { + ILTransformContext context; + + public void Run(ILFunction function, ILTransformContext context) + { + this.context = context; + Default(function); + } + + protected override void Default(ILInstruction inst) + { + foreach (var child in inst.Children) { + child.AcceptVisitor(this); + } + } + + protected internal override void VisitStObj(StObj inst) + { + base.VisitStObj(inst); + StObjToStLoc(inst, context); + } + + // This transform is required because ILInlining only works with stloc/ldloc + internal static bool StObjToStLoc(StObj inst, ILTransformContext context) + { + if (inst.Target.MatchLdLoca(out ILVariable v) + && TypeUtils.IsCompatibleTypeForMemoryAccess(new ByReferenceType(v.Type), inst.Type) + && inst.UnalignedPrefix == 0 + && !inst.IsVolatile) { + context.Step($"stobj(ldloca {v.Name}, ...) => stloc {v.Name}(...)", inst); + inst.ReplaceWith(new StLoc(v, inst.Value)); + return true; + } + return false; + } + + protected internal override void VisitCall(Call inst) + { + var expr = HandleCall(inst, context); + if (expr != null) { + // The resulting expression may trigger further rules, so continue visiting the replacement: + expr.AcceptVisitor(this); + } else { + base.VisitCall(inst); + } + } + + internal static ILInstruction HandleCall(Call inst, ILTransformContext context) + { + if (inst.Method.IsConstructor && !inst.Method.IsStatic && inst.Method.DeclaringType.Kind == TypeKind.Struct) { + Debug.Assert(inst.Arguments.Count == inst.Method.Parameters.Count + 1); + context.Step("Transform call to struct constructor", inst); + // call(ref, ...) + // => stobj(ref, newobj(...)) + var newObj = new NewObj(inst.Method); + newObj.ILRange = inst.ILRange; + newObj.Arguments.AddRange(inst.Arguments.Skip(1)); + var expr = new StObj(inst.Arguments[0], newObj, inst.Method.DeclaringType); + inst.ReplaceWith(expr); + return expr; + } + return null; + } + } +} diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index bb786a765..4b97e7ac0 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2014 Daniel Grunwald +// Copyright (c) 2014-2017 Daniel Grunwald // // Permission is hereby granted, free of charge, to any person obtaining a copy of this // software and associated documentation files (the "Software"), to deal in the Software @@ -29,16 +29,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// /// Should run after inlining so that the expression patterns can be detected. /// - public class ExpressionTransforms : ILVisitor, IBlockTransform, IStatementTransform + public class ExpressionTransforms : ILVisitor, IStatementTransform { - internal ILTransformContext context; - - public void Run(Block block, BlockTransformContext context) - { - this.context = context; - Default(block); - } - + internal StatementTransformContext context; + public void Run(Block block, int pos, StatementTransformContext context) { this.context = context; @@ -229,23 +223,22 @@ namespace ICSharpCode.Decompiler.IL.Transforms protected internal override void VisitCall(Call inst) { - if (inst.Method.IsConstructor && !inst.Method.IsStatic && inst.Method.DeclaringType.Kind == TypeKind.Struct) { - Debug.Assert(inst.Arguments.Count == inst.Method.Parameters.Count + 1); - context.Step("Transform call to struct constructor", inst); - // call(ref, ...) - // => stobj(ref, newobj(...)) - var newObj = new NewObj(inst.Method); - newObj.ILRange = inst.ILRange; - newObj.Arguments.AddRange(inst.Arguments.Skip(1)); - var expr = new StObj(inst.Arguments[0], newObj, inst.Method.DeclaringType); - inst.ReplaceWith(expr); - // Both the StObj and the NewObj may trigger further rules, so continue visiting the replacement: - VisitStObj(expr); + var expr = EarlyExpressionTransforms.HandleCall(inst, context); + if (expr != null) { + // The resulting expression may trigger further rules, so continue visiting the replacement: + expr.AcceptVisitor(this); } else { base.VisitCall(inst); + TransformAssignment.HandleCallCompoundAssign(inst, context); } } - + + protected internal override void VisitCallVirt(CallVirt inst) + { + base.VisitCallVirt(inst); + TransformAssignment.HandleCallCompoundAssign(inst, context); + } + protected internal override void VisitNewObj(NewObj inst) { LdcDecimal decimalConstant; @@ -286,13 +279,16 @@ namespace ICSharpCode.Decompiler.IL.Transforms protected internal override void VisitStObj(StObj inst) { base.VisitStObj(inst); - if (StObjToStLoc(inst, context)) { + if (EarlyExpressionTransforms.StObjToStLoc(inst, context)) { + context.RequestRerun(); return; } if (inst.Value is BinaryNumericInstruction binary && binary.Left.MatchLdObj(out ILInstruction target, out IType t) - && inst.Target.Match(target).Success) + && inst.Target.Match(target).Success + && SemanticHelper.IsPure(target.Flags) + && CompoundAssignmentInstruction.IsBinaryCompatibleWithType(binary, t)) { context.Step("compound assignment", inst); // stobj(target, binary.op(ldobj(target), ...)) @@ -303,20 +299,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } - // This transform is required because ILInlining only works with stloc/ldloc - internal static bool StObjToStLoc(StObj inst, ILTransformContext context) - { - if (inst.Target.MatchLdLoca(out ILVariable v) - && TypeUtils.IsCompatibleTypeForMemoryAccess(new ByReferenceType(v.Type), inst.Type) - && inst.UnalignedPrefix == 0 - && !inst.IsVolatile) { - context.Step($"stobj(ldloca {v.Name}, ...) => stloc {v.Name}(...)", inst); - inst.ReplaceWith(new StLoc(v, inst.Value)); - return true; - } - return false; - } - protected internal override void VisitIfInstruction(IfInstruction inst) { inst.TrueInst.AcceptVisitor(this); @@ -360,7 +342,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms var newIf = new IfInstruction(Comp.LogicNot(inst.Condition), value2, value1); newIf.ILRange = inst.ILRange; inst.ReplaceWith(new StLoc(v, newIf)); - (context as StatementTransformContext)?.RequestRerun(); // trigger potential inlining of the newly created StLoc + context.RequestRerun(); // trigger potential inlining of the newly created StLoc return newIf; } return inst; diff --git a/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs index e09216067..0ee050f4a 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs @@ -64,7 +64,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; } - public bool RunBlock(Block block) + public bool RunStatements(Block block, int pos) { if (!context.Settings.LiftNullables) return false; @@ -73,18 +73,20 @@ namespace ICSharpCode.Decompiler.IL.Transforms // leave IL_0000 (default.value System.Nullable`1[[System.Int64]]) // } // leave IL_0000 (newobj .ctor(exprToLift)) - IfInstruction ifInst; - if (block.Instructions.Last() is Leave elseLeave) { - ifInst = block.Instructions.SecondToLastOrDefault() as IfInstruction; - if (ifInst == null || !ifInst.FalseInst.MatchNop()) - return false; - } else { + if (pos != block.Instructions.Count - 2) + return false; + if (!(block.Instructions[pos] is IfInstruction ifInst)) return false; - } if (!(Block.Unwrap(ifInst.TrueInst) is Leave thenLeave)) return false; + if (!ifInst.FalseInst.MatchNop()) + return false; + + if (!(block.Instructions[pos + 1] is Leave elseLeave)) + return false; if (elseLeave.TargetContainer != thenLeave.TargetContainer) return false; + var lifted = Lift(ifInst, thenLeave.Value, elseLeave.Value); if (lifted != null) { thenLeave.Value = lifted; @@ -149,7 +151,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms // => a != b return LiftCSharpEqualityComparison(comp, ComparisonKind.Inequality, trueInst) ?? LiftCSharpUserEqualityComparison(comp, ComparisonKind.Inequality, trueInst); - } else if (IsGenericNewPattern(condition, trueInst, falseInst)) { + } else if (IsGenericNewPattern(comp.Left, comp.Right, trueInst, falseInst)) { // (default(T) == null) ? Activator.CreateInstance() : default(T) // => Activator.CreateInstance() return trueInst; @@ -245,16 +247,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms return null; } - private bool IsGenericNewPattern(ILInstruction condition, ILInstruction trueInst, ILInstruction falseInst) + private bool IsGenericNewPattern(ILInstruction compLeft, ILInstruction compRight, ILInstruction trueInst, ILInstruction falseInst) { // (default(T) == null) ? Activator.CreateInstance() : default(T) return falseInst.MatchDefaultValue(out var type) && (trueInst is Call c && c.Method.FullName == "System.Activator.CreateInstance" && c.Method.TypeArguments.Count == 1) && type.Kind == TypeKind.TypeParameter && - condition.MatchCompEquals(out var left, out var right) && - left.MatchDefaultValue(out var type2) && + compLeft.MatchDefaultValue(out var type2) && type.Equals(type2) && - right.MatchLdNull(); + compRight.MatchLdNull(); } private bool MatchThreeValuedLogicConditionPattern(ILInstruction condition, out ILVariable nullable1, out ILVariable nullable2) @@ -847,11 +848,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms #endregion } - class NullableLiftingBlockTransform : IBlockTransform + class NullableLiftingStatementTransform : IStatementTransform { - public void Run(Block block, BlockTransformContext context) + public void Run(Block block, int pos, StatementTransformContext context) { - new NullableLiftingTransform(context).RunBlock(block); + new NullableLiftingTransform(context).RunStatements(block, pos); } } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs index 3d029d661..5d41bb402 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs @@ -16,13 +16,14 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System; using System.Linq; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL.Transforms { /// - /// Description of TransformAssignment. + /// Constructs compound assignments and inline assignments. /// public class TransformAssignment : IBlockTransform { @@ -42,7 +43,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms } if (TransformInlineAssignmentStObj(block, i)) continue; - if (TransformInlineAssignmentCall(block, i)) + if (TransformInlineCompoundAssignmentCall(block, i)) + continue; + if (TransformRoslynCompoundAssignmentCall(block, i)) continue; } } @@ -82,8 +85,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; replacement = new StObj(fieldStore.Target, inst.Value, fieldStore.Type); } else { // otherwise it must be local - TransformInlineAssignmentLocal(block, i); - return false; + return TransformInlineAssignmentLocal(block, i); } context.Step("Inline assignment to instance field", fieldStore); local = localStore.Variable; @@ -106,76 +108,151 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// /// stloc s(binary(callvirt(getter), value)) /// callvirt (setter, ldloc s) - /// ... usage of s ... - /// --> - /// ... compound.op.new(callvirt(getter), value) ... - /// - /// -or- - /// - /// stloc s(stloc v(binary(callvirt(getter), value))) - /// callvirt (setter, ldloc s) - /// ... usage of v ... + /// (followed by single usage of s in next instruction) /// --> - /// ... compound.op.new(callvirt(getter), value) ... + /// stloc s(compound.op.new(callvirt(getter), value)) /// - bool TransformInlineAssignmentCall(Block block, int i) + bool TransformInlineCompoundAssignmentCall(Block block, int i) { - var inst = block.Instructions[i] as StLoc; + var mainStLoc = block.Instructions[i] as StLoc; // in some cases it can be a compiler-generated local - if (inst == null || (inst.Variable.Kind != VariableKind.StackSlot && inst.Variable.Kind != VariableKind.Local)) + if (mainStLoc == null || (mainStLoc.Variable.Kind != VariableKind.StackSlot && mainStLoc.Variable.Kind != VariableKind.Local)) + return false; + BinaryNumericInstruction binary = mainStLoc.Value as BinaryNumericInstruction; + ILVariable localVariable = mainStLoc.Variable; + if (!localVariable.IsSingleDefinition) + return false; + if (localVariable.LoadCount != 2) return false; - BinaryNumericInstruction binary; - ILVariable localVariable; - if (inst.Value is StLoc) { - var tmp = (StLoc)inst.Value; - binary = tmp.Value as BinaryNumericInstruction; - localVariable = tmp.Variable; - } else { - binary = inst.Value as BinaryNumericInstruction; - localVariable = inst.Variable; - } var getterCall = binary?.Left as CallInstruction; var setterCall = block.Instructions.ElementAtOrDefault(i + 1) as CallInstruction; - if (getterCall == null || setterCall == null || !IsSameMember(getterCall.Method.AccessorOwner, setterCall.Method.AccessorOwner)) + if (!MatchingGetterAndSetterCalls(getterCall, setterCall)) return false; - var owner = getterCall.Method.AccessorOwner as IProperty; - if (owner == null || !IsSameMember(getterCall.Method, owner.Getter) || !IsSameMember(setterCall.Method, owner.Setter)) + if (!setterCall.Arguments.Last().MatchLdLoc(localVariable)) return false; + var next = block.Instructions.ElementAtOrDefault(i + 2); if (next == null) return false; - var usages = next.Descendants.Where(d => d.MatchLdLoc(localVariable)).ToArray(); - if (usages.Length != 1) + if (next.Descendants.Where(d => d.MatchLdLoc(localVariable)).Count() != 1) return false; - context.Step($"Compound assignment to '{owner.Name}'", setterCall); - block.Instructions.RemoveAt(i + 1); - block.Instructions.RemoveAt(i); - usages[0].ReplaceWith(new CompoundAssignmentInstruction( + if (!CompoundAssignmentInstruction.IsBinaryCompatibleWithType(binary, getterCall.Method.ReturnType)) + return false; + context.Step($"Inline compound assignment to '{getterCall.Method.AccessorOwner.Name}'", setterCall); + block.Instructions.RemoveAt(i + 1); // remove setter call + binary.ReplaceWith(new CompoundAssignmentInstruction( binary, getterCall, binary.Right, getterCall.Method.ReturnType, CompoundAssignmentType.EvaluatesToNewValue)); return true; } + /// + /// Roslyn compound assignment that's not inline within another instruction. + /// + bool TransformRoslynCompoundAssignmentCall(Block block, int i) + { + // stloc variable(callvirt get_Property(ldloc obj)) + // callvirt set_Property(ldloc obj, binary.op(ldloc variable, ldc.i4 1)) + // => compound.op.new(callvirt get_Property(ldloc obj), ldc.i4 1) + if (!(block.Instructions[i] is StLoc stloc)) + return false; + if (!(stloc.Variable.IsSingleDefinition && stloc.Variable.LoadCount == 1)) + return false; + var getterCall = stloc.Value as CallInstruction; + var setterCall = block.Instructions[i + 1] as CallInstruction; + if (!(MatchingGetterAndSetterCalls(getterCall, setterCall))) + return false; + var binary = setterCall.Arguments.Last() as BinaryNumericInstruction; + if (binary == null || !binary.Left.MatchLdLoc(stloc.Variable)) + return false; + if (!CompoundAssignmentInstruction.IsBinaryCompatibleWithType(binary, getterCall.Method.ReturnType)) + return false; + context.Step($"Compound assignment to '{getterCall.Method.AccessorOwner.Name}'", setterCall); + block.Instructions.RemoveAt(i + 1); // remove setter call + stloc.ReplaceWith(new CompoundAssignmentInstruction( + binary, getterCall, binary.Right, + getterCall.Method.ReturnType, CompoundAssignmentType.EvaluatesToNewValue)); + return true; + } + + static bool MatchingGetterAndSetterCalls(CallInstruction getterCall, CallInstruction setterCall) + { + if (getterCall == null || setterCall == null || !IsSameMember(getterCall.Method.AccessorOwner, setterCall.Method.AccessorOwner)) + return false; + var owner = getterCall.Method.AccessorOwner as IProperty; + if (owner == null || !IsSameMember(getterCall.Method, owner.Getter) || !IsSameMember(setterCall.Method, owner.Setter)) + return false; + if (setterCall.Arguments.Count != getterCall.Arguments.Count + 1) + return false; + // Ensure that same arguments are passed to getterCall and setterCall: + for (int j = 0; j < getterCall.Arguments.Count; j++) { + if (!SemanticHelper.IsPure(getterCall.Arguments[j].Flags)) + return false; + if (!getterCall.Arguments[j].Match(setterCall.Arguments[j]).Success) + return false; + } + return true; + } + + /// + /// Transform compound assignments where the return value is not being used, + /// or where there's an inlined assignment within the setter call. + /// + /// + /// Called by ExpressionTransforms. + /// + internal static bool HandleCallCompoundAssign(CallInstruction setterCall, StatementTransformContext context) + { + // callvirt set_Property(ldloc S_1, binary.op(callvirt get_Property(ldloc S_1), value)) + // ==> compound.op.new(callvirt(callvirt get_Property(ldloc S_1)), value) + var setterValue = setterCall.Arguments.LastOrDefault(); + var storeInSetter = setterValue as StLoc; + if (storeInSetter != null) { + // callvirt set_Property(ldloc S_1, stloc v(binary.op(callvirt get_Property(ldloc S_1), value))) + // ==> stloc v(compound.op.new(callvirt(callvirt get_Property(ldloc S_1)), value)) + setterValue = storeInSetter.Value; + } + if (!(setterValue is BinaryNumericInstruction binary)) + return false; + var getterCall = binary.Left as CallInstruction; + if (!MatchingGetterAndSetterCalls(getterCall, setterCall)) + return false; + if (!CompoundAssignmentInstruction.IsBinaryCompatibleWithType(binary, getterCall.Method.ReturnType)) + return false; + context.Step($"Compound assignment to '{getterCall.Method.AccessorOwner.Name}'", setterCall); + ILInstruction newInst = new CompoundAssignmentInstruction( + binary, getterCall, binary.Right, + getterCall.Method.ReturnType, CompoundAssignmentType.EvaluatesToNewValue); + if (storeInSetter != null) { + storeInSetter.Value = newInst; + newInst = storeInSetter; + context.RequestRerun(); // moving stloc to top-level might trigger inlining + } + setterCall.ReplaceWith(newInst); + return true; + } + /// /// stloc s(value) /// stloc l(ldloc s) /// --> /// stloc s(stloc l(value)) /// - void TransformInlineAssignmentLocal(Block block, int i) + bool TransformInlineAssignmentLocal(Block block, int i) { var inst = block.Instructions[i] as StLoc; var nextInst = block.Instructions.ElementAtOrDefault(i + 1) as StLoc; if (inst == null || nextInst == null) - return; + return false; if (nextInst.Variable.Kind == VariableKind.StackSlot || !nextInst.Value.MatchLdLoc(inst.Variable)) - return; + return false; context.Step("Inline assignment to local variable", inst); var value = inst.Value; var var = nextInst.Variable; var stackVar = inst.Variable; block.Instructions.RemoveAt(i); nextInst.ReplaceWith(new StLoc(stackVar, new StLoc(var, value))); + return true; } ///