From d424d93dce114e0778f2daaa82b27b8f4a54b054 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 11 Nov 2017 12:18:23 +0100 Subject: [PATCH] Fix invalid variable inlining in foreach (fixes #889) --- .../TestCases/Pretty/Loops.cs | 45 ++- .../TestCases/Pretty/Loops.il | 345 ++++++++++++++---- .../TestCases/Pretty/Loops.opt.il | 172 ++++++++- .../TestCases/Pretty/Loops.opt.roslyn.il | 178 ++++++++- .../TestCases/Pretty/Loops.roslyn.il | 341 +++++++++++++---- .../CSharp/StatementBuilder.cs | 68 +++- 6 files changed, 986 insertions(+), 163 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.cs index 52705b885..ca003d34c 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.cs @@ -252,6 +252,18 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } + public struct DataItem + { + public int Property { + get; + set; + } + + public void TestCall() + { + } + } + private IEnumerable alternatives; private static void Operation(ref int item) @@ -390,7 +402,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { foreach (int item in items) { #if ROSLYN && OPT - // The variable names differs based on whether roslyn optimizes out the 'item' variable + // The variable name differs based on whether roslyn optimizes out the 'item' variable int current = item; Loops.Operation(ref current); #else @@ -441,6 +453,37 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } } + + public void ForEachOverListOfStruct(List items, int value) + { + foreach (DataItem item in items) { + DataItem dataItem = item; + dataItem.Property = value; + } + } + + public void ForEachOverListOfStruct2(List items, int value) + { + foreach (DataItem item in items) { +#if ROSLYN && OPT + // The variable name differs based on whether roslyn optimizes out the 'item' variable + DataItem current = item; + current.TestCall(); + current.Property = value; +#else + DataItem dataItem = item; + dataItem.TestCall(); + dataItem.Property = value; +#endif + } + } + + public void ForEachOverListOfStruct3(List items, int value) + { + foreach (DataItem item in items) { + item.TestCall(); + } + } #endregion public void ForOverArray(string[] array) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.il index f976c663a..35154948d 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.il @@ -10,7 +10,7 @@ .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } -.assembly n3qdq2uj +.assembly dky1g03y { .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 n3qdq2uj.dll -// MVID: {98B4F05B-5E30-48EB-AAF5-A90EA42A94A2} +.module dky1g03y.dll +// MVID: {4ADF70C5-8EC0-41AF-A9B1-6DF95B943C1A} .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: 0x02AD0000 +// Image base: 0x03580000 // =============== CLASS MEMBERS DECLARATION =================== @@ -584,6 +584,55 @@ } // end of property CustomStructEnumeratorWithIDisposable`1::Current } // end of class CustomStructEnumeratorWithIDisposable`1 + .class sequential ansi sealed nested public beforefieldinit DataItem + extends [mscorlib]System.ValueType + { + .field private int32 'k__BackingField' + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .method public hidebysig specialname + instance int32 get_Property() cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 11 (0xb) + .maxstack 1 + .locals init (int32 V_0) + IL_0000: ldarg.0 + IL_0001: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::'k__BackingField' + IL_0006: stloc.0 + IL_0007: br.s IL_0009 + + IL_0009: ldloc.0 + IL_000a: ret + } // end of method DataItem::get_Property + + .method public hidebysig specialname + instance void set_Property(int32 'value') cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::'k__BackingField' + IL_0007: ret + } // end of method DataItem::set_Property + + .method public hidebysig instance void + TestCall() cil managed + { + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method DataItem::TestCall + + .property instance int32 Property() + { + .get instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::get_Property() + .set instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::set_Property(int32) + } // end of property DataItem::Property + } // end of class DataItem + .class auto ansi sealed nested private beforefieldinit '<>c__DisplayClass1' extends [mscorlib]System.Object { @@ -1269,7 +1318,7 @@ .method public hidebysig static void NonGenericForeachWithReturnFallbackTest(class [mscorlib]System.Collections.IEnumerable e) cil managed { - // Code size 111 (0x6f) + // Code size 113 (0x71) .maxstack 2 .locals init (class [mscorlib]System.Collections.IEnumerator V_0, object V_1, @@ -1308,7 +1357,7 @@ IL_0044: nop IL_0045: nop IL_0046: nop - IL_0047: leave.s IL_0062 + IL_0047: leave.s IL_0064 } // end .try finally @@ -1322,19 +1371,21 @@ IL_0053: ceq IL_0055: stloc.3 IL_0056: ldloc.3 - IL_0057: brtrue.s IL_0060 + IL_0057: brtrue.s IL_0062 - IL_0059: ldloc.2 - IL_005a: callvirt instance void [mscorlib]System.IDisposable::Dispose() - IL_005f: nop + IL_0059: nop + IL_005a: ldloc.2 + IL_005b: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_0060: nop - IL_0061: endfinally + IL_0061: nop + IL_0062: nop + IL_0063: endfinally } // end handler - IL_0062: nop - IL_0063: ldstr "After finally!" - IL_0068: call void [mscorlib]System.Console::WriteLine(string) - IL_006d: nop - IL_006e: ret + IL_0064: nop + IL_0065: ldstr "After finally!" + IL_006a: call void [mscorlib]System.Console::WriteLine(string) + IL_006f: nop + IL_0070: ret } // end of method Loops::NonGenericForeachWithReturnFallbackTest .method public hidebysig static void ForeachWithRefUsage(class [mscorlib]System.Collections.Generic.List`1 items) cil managed @@ -1669,6 +1720,158 @@ IL_0069: ret } // end of method Loops::ForEachBreakWhenFound + .method public hidebysig instance void + ForEachOverListOfStruct(class [mscorlib]System.Collections.Generic.List`1 items, + int32 'value') cil managed + { + // Code size 62 (0x3e) + .maxstack 2 + .locals init (valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem V_0, + valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem V_1, + valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator V_2, + bool V_3) + IL_0000: nop + IL_0001: nop + IL_0002: ldarg.1 + IL_0003: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator class [mscorlib]System.Collections.Generic.List`1::GetEnumerator() + IL_0008: stloc.2 + .try + { + IL_0009: br.s IL_0020 + + IL_000b: ldloca.s V_2 + IL_000d: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator::get_Current() + IL_0012: stloc.0 + IL_0013: nop + IL_0014: ldloc.0 + IL_0015: stloc.1 + IL_0016: ldloca.s V_1 + IL_0018: ldarg.2 + IL_0019: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::set_Property(int32) + IL_001e: nop + IL_001f: nop + IL_0020: ldloca.s V_2 + IL_0022: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator::MoveNext() + IL_0027: stloc.3 + IL_0028: ldloc.3 + IL_0029: brtrue.s IL_000b + + IL_002b: leave.s IL_003c + + } // end .try + finally + { + IL_002d: ldloca.s V_2 + IL_002f: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator + IL_0035: callvirt instance void [mscorlib]System.IDisposable::Dispose() + IL_003a: nop + IL_003b: endfinally + } // end handler + IL_003c: nop + IL_003d: ret + } // end of method Loops::ForEachOverListOfStruct + + .method public hidebysig instance void + ForEachOverListOfStruct2(class [mscorlib]System.Collections.Generic.List`1 items, + int32 'value') cil managed + { + // Code size 70 (0x46) + .maxstack 2 + .locals init (valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem V_0, + valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem V_1, + valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator V_2, + bool V_3) + IL_0000: nop + IL_0001: nop + IL_0002: ldarg.1 + IL_0003: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator class [mscorlib]System.Collections.Generic.List`1::GetEnumerator() + IL_0008: stloc.2 + .try + { + IL_0009: br.s IL_0028 + + IL_000b: ldloca.s V_2 + IL_000d: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator::get_Current() + IL_0012: stloc.0 + IL_0013: nop + IL_0014: ldloc.0 + IL_0015: stloc.1 + IL_0016: ldloca.s V_1 + IL_0018: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::TestCall() + IL_001d: nop + IL_001e: ldloca.s V_1 + IL_0020: ldarg.2 + IL_0021: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::set_Property(int32) + IL_0026: nop + IL_0027: nop + IL_0028: ldloca.s V_2 + IL_002a: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator::MoveNext() + IL_002f: stloc.3 + IL_0030: ldloc.3 + IL_0031: brtrue.s IL_000b + + IL_0033: leave.s IL_0044 + + } // end .try + finally + { + IL_0035: ldloca.s V_2 + IL_0037: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator + IL_003d: callvirt instance void [mscorlib]System.IDisposable::Dispose() + IL_0042: nop + IL_0043: endfinally + } // end handler + IL_0044: nop + IL_0045: ret + } // end of method Loops::ForEachOverListOfStruct2 + + .method public hidebysig instance void + ForEachOverListOfStruct3(class [mscorlib]System.Collections.Generic.List`1 items, + int32 'value') cil managed + { + // Code size 59 (0x3b) + .maxstack 1 + .locals init (valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem V_0, + valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator V_1, + bool V_2) + IL_0000: nop + IL_0001: nop + IL_0002: ldarg.1 + IL_0003: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator class [mscorlib]System.Collections.Generic.List`1::GetEnumerator() + IL_0008: stloc.1 + .try + { + IL_0009: br.s IL_001d + + IL_000b: ldloca.s V_1 + IL_000d: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator::get_Current() + IL_0012: stloc.0 + IL_0013: nop + IL_0014: ldloca.s V_0 + IL_0016: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::TestCall() + IL_001b: nop + IL_001c: nop + IL_001d: ldloca.s V_1 + IL_001f: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator::MoveNext() + IL_0024: stloc.2 + IL_0025: ldloc.2 + IL_0026: brtrue.s IL_000b + + IL_0028: leave.s IL_0039 + + } // end .try + finally + { + IL_002a: ldloca.s V_1 + IL_002c: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator + IL_0032: callvirt instance void [mscorlib]System.IDisposable::Dispose() + IL_0037: nop + IL_0038: endfinally + } // end handler + IL_0039: nop + IL_003a: ret + } // end of method Loops::ForEachOverListOfStruct3 + .method public hidebysig instance void ForOverArray(string[] 'array') cil managed { @@ -1848,7 +2051,7 @@ .method public hidebysig instance void WhileLoop() cil managed { - // Code size 154 (0x9a) + // Code size 155 (0x9b) .maxstack 2 .locals init (bool V_0) IL_0000: nop @@ -1862,10 +2065,10 @@ IL_0018: ceq IL_001a: stloc.0 IL_001b: ldloc.0 - IL_001c: brtrue.s IL_008e + IL_001c: brtrue.s IL_008f IL_001e: nop - IL_001f: br.s IL_0073 + IL_001f: br.s IL_0074 IL_0021: nop IL_0022: ldstr "Loop Body" @@ -1878,7 +2081,7 @@ IL_0039: ceq IL_003b: stloc.0 IL_003c: ldloc.0 - IL_003d: brtrue.s IL_0067 + IL_003d: brtrue.s IL_0068 IL_003f: nop IL_0040: ldarg.0 @@ -1891,43 +2094,44 @@ IL_0050: brtrue.s IL_0055 IL_0052: nop - IL_0053: br.s IL_0073 + IL_0053: br.s IL_0074 IL_0055: ldarg.0 IL_0056: ldstr "break" IL_005b: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string) IL_0060: stloc.0 IL_0061: ldloc.0 - IL_0062: brtrue.s IL_0066 + IL_0062: brtrue.s IL_0067 - IL_0064: br.s IL_0082 + IL_0064: nop + IL_0065: br.s IL_0083 - IL_0066: nop - IL_0067: ldstr "End of loop body" - IL_006c: call void [mscorlib]System.Console::WriteLine(string) - IL_0071: nop + IL_0067: nop + IL_0068: ldstr "End of loop body" + IL_006d: call void [mscorlib]System.Console::WriteLine(string) IL_0072: nop - IL_0073: ldarg.0 - IL_0074: ldstr "while" - IL_0079: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string) - IL_007e: stloc.0 - IL_007f: ldloc.0 - IL_0080: brtrue.s IL_0021 - - IL_0082: ldstr "After loop" - IL_0087: call void [mscorlib]System.Console::WriteLine(string) - IL_008c: nop + IL_0073: nop + IL_0074: ldarg.0 + IL_0075: ldstr "while" + IL_007a: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string) + IL_007f: stloc.0 + IL_0080: ldloc.0 + IL_0081: brtrue.s IL_0021 + + IL_0083: ldstr "After loop" + IL_0088: call void [mscorlib]System.Console::WriteLine(string) IL_008d: nop - IL_008e: ldstr "End of method" - IL_0093: call void [mscorlib]System.Console::WriteLine(string) - IL_0098: nop - IL_0099: ret + IL_008e: nop + IL_008f: ldstr "End of method" + IL_0094: call void [mscorlib]System.Console::WriteLine(string) + IL_0099: nop + IL_009a: ret } // end of method Loops::WhileLoop .method public hidebysig instance void ForLoop() cil managed { - // Code size 160 (0xa0) + // Code size 161 (0xa1) .maxstack 2 .locals init (int32 V_0, bool V_1) @@ -1942,12 +2146,12 @@ IL_0018: ceq IL_001a: stloc.1 IL_001b: ldloc.1 - IL_001c: brtrue.s IL_0094 + IL_001c: brtrue.s IL_0095 IL_001e: nop IL_001f: ldc.i4.0 IL_0020: stloc.0 - IL_0021: br.s IL_0079 + IL_0021: br.s IL_007a IL_0023: nop IL_0024: ldstr "Loop Body" @@ -1960,7 +2164,7 @@ IL_003b: ceq IL_003d: stloc.1 IL_003e: ldloc.1 - IL_003f: brtrue.s IL_0069 + IL_003f: brtrue.s IL_006a IL_0041: nop IL_0042: ldarg.0 @@ -1973,41 +2177,42 @@ IL_0052: brtrue.s IL_0057 IL_0054: nop - IL_0055: br.s IL_0075 + IL_0055: br.s IL_0076 IL_0057: ldarg.0 IL_0058: ldstr "not-break" IL_005d: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string) IL_0062: stloc.1 IL_0063: ldloc.1 - IL_0064: brtrue.s IL_0068 + IL_0064: brtrue.s IL_0069 - IL_0066: br.s IL_0088 + IL_0066: nop + IL_0067: br.s IL_0089 - IL_0068: nop - IL_0069: ldstr "End of loop body" - IL_006e: call void [mscorlib]System.Console::WriteLine(string) - IL_0073: nop + IL_0069: nop + IL_006a: ldstr "End of loop body" + IL_006f: call void [mscorlib]System.Console::WriteLine(string) IL_0074: nop - IL_0075: ldloc.0 - IL_0076: ldc.i4.1 - IL_0077: add - IL_0078: stloc.0 - IL_0079: ldarg.0 - IL_007a: ldstr "for" - IL_007f: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string) - IL_0084: stloc.1 - IL_0085: ldloc.1 - IL_0086: brtrue.s IL_0023 - - IL_0088: ldstr "After loop" - IL_008d: call void [mscorlib]System.Console::WriteLine(string) - IL_0092: nop + IL_0075: nop + IL_0076: ldloc.0 + IL_0077: ldc.i4.1 + IL_0078: add + IL_0079: stloc.0 + IL_007a: ldarg.0 + IL_007b: ldstr "for" + IL_0080: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string) + IL_0085: stloc.1 + IL_0086: ldloc.1 + IL_0087: brtrue.s IL_0023 + + IL_0089: ldstr "After loop" + IL_008e: call void [mscorlib]System.Console::WriteLine(string) IL_0093: nop - IL_0094: ldstr "End of method" - IL_0099: call void [mscorlib]System.Console::WriteLine(string) - IL_009e: nop - IL_009f: ret + IL_0094: nop + IL_0095: ldstr "End of method" + IL_009a: call void [mscorlib]System.Console::WriteLine(string) + IL_009f: nop + IL_00a0: ret } // end of method Loops::ForLoop .method public hidebysig specialname rtspecialname diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.opt.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.opt.il index 7e47d77b2..49f97f432 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.opt.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.opt.il @@ -10,7 +10,7 @@ .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } -.assembly spdzj2hg +.assembly trg3dx5v { .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 spdzj2hg.dll -// MVID: {D8CD3A33-FD06-4791-A4E6-AC836DA34D06} +.module trg3dx5v.dll +// MVID: {39502A20-4431-466D-88D2-F47B21FB8C58} .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: 0x002F0000 +// Image base: 0x00B30000 // =============== CLASS MEMBERS DECLARATION =================== @@ -506,6 +506,49 @@ } // end of property CustomStructEnumeratorWithIDisposable`1::Current } // end of class CustomStructEnumeratorWithIDisposable`1 + .class sequential ansi sealed nested public beforefieldinit DataItem + extends [mscorlib]System.ValueType + { + .field private int32 'k__BackingField' + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .method public hidebysig specialname + instance int32 get_Property() cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::'k__BackingField' + IL_0006: ret + } // end of method DataItem::get_Property + + .method public hidebysig specialname + instance void set_Property(int32 'value') cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::'k__BackingField' + IL_0007: ret + } // end of method DataItem::set_Property + + .method public hidebysig instance void + TestCall() cil managed + { + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method DataItem::TestCall + + .property instance int32 Property() + { + .get instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::get_Property() + .set instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::set_Property(int32) + } // end of property DataItem::Property + } // end of class DataItem + .class auto ansi sealed nested private beforefieldinit '<>c__DisplayClass1' extends [mscorlib]System.Object { @@ -1336,6 +1379,127 @@ IL_0056: ret } // end of method Loops::ForEachBreakWhenFound + .method public hidebysig instance void + ForEachOverListOfStruct(class [mscorlib]System.Collections.Generic.List`1 items, + int32 'value') cil managed + { + // Code size 53 (0x35) + .maxstack 2 + .locals init (valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem V_0, + valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem V_1, + valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator V_2) + IL_0000: ldarg.1 + IL_0001: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator class [mscorlib]System.Collections.Generic.List`1::GetEnumerator() + IL_0006: stloc.2 + .try + { + IL_0007: br.s IL_001b + + IL_0009: ldloca.s V_2 + IL_000b: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator::get_Current() + IL_0010: stloc.0 + IL_0011: ldloc.0 + IL_0012: stloc.1 + IL_0013: ldloca.s V_1 + IL_0015: ldarg.2 + IL_0016: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::set_Property(int32) + IL_001b: ldloca.s V_2 + IL_001d: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator::MoveNext() + IL_0022: brtrue.s IL_0009 + + IL_0024: leave.s IL_0034 + + } // end .try + finally + { + IL_0026: ldloca.s V_2 + IL_0028: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator + IL_002e: callvirt instance void [mscorlib]System.IDisposable::Dispose() + IL_0033: endfinally + } // end handler + IL_0034: ret + } // end of method Loops::ForEachOverListOfStruct + + .method public hidebysig instance void + ForEachOverListOfStruct2(class [mscorlib]System.Collections.Generic.List`1 items, + int32 'value') cil managed + { + // Code size 60 (0x3c) + .maxstack 2 + .locals init (valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem V_0, + valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem V_1, + valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator V_2) + IL_0000: ldarg.1 + IL_0001: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator class [mscorlib]System.Collections.Generic.List`1::GetEnumerator() + IL_0006: stloc.2 + .try + { + IL_0007: br.s IL_0022 + + IL_0009: ldloca.s V_2 + IL_000b: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator::get_Current() + IL_0010: stloc.0 + IL_0011: ldloc.0 + IL_0012: stloc.1 + IL_0013: ldloca.s V_1 + IL_0015: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::TestCall() + IL_001a: ldloca.s V_1 + IL_001c: ldarg.2 + IL_001d: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::set_Property(int32) + IL_0022: ldloca.s V_2 + IL_0024: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator::MoveNext() + IL_0029: brtrue.s IL_0009 + + IL_002b: leave.s IL_003b + + } // end .try + finally + { + IL_002d: ldloca.s V_2 + IL_002f: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator + IL_0035: callvirt instance void [mscorlib]System.IDisposable::Dispose() + IL_003a: endfinally + } // end handler + IL_003b: ret + } // end of method Loops::ForEachOverListOfStruct2 + + .method public hidebysig instance void + ForEachOverListOfStruct3(class [mscorlib]System.Collections.Generic.List`1 items, + int32 'value') cil managed + { + // Code size 50 (0x32) + .maxstack 1 + .locals init (valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem V_0, + valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator V_1) + IL_0000: ldarg.1 + IL_0001: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator class [mscorlib]System.Collections.Generic.List`1::GetEnumerator() + IL_0006: stloc.1 + .try + { + IL_0007: br.s IL_0018 + + IL_0009: ldloca.s V_1 + IL_000b: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator::get_Current() + IL_0010: stloc.0 + IL_0011: ldloca.s V_0 + IL_0013: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::TestCall() + IL_0018: ldloca.s V_1 + IL_001a: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator::MoveNext() + IL_001f: brtrue.s IL_0009 + + IL_0021: leave.s IL_0031 + + } // end .try + finally + { + IL_0023: ldloca.s V_1 + IL_0025: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator + IL_002b: callvirt instance void [mscorlib]System.IDisposable::Dispose() + IL_0030: endfinally + } // end handler + IL_0031: ret + } // end of method Loops::ForEachOverListOfStruct3 + .method public hidebysig instance void ForOverArray(string[] 'array') cil managed { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.opt.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.opt.roslyn.il index eb172c6d5..d10c64239 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.opt.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.opt.roslyn.il @@ -25,14 +25,14 @@ .ver 0:0:0:0 } .module Loops.dll -// MVID: {B3F85A6A-79B4-41C0-9146-2088985BFC34} +// MVID: {F193AB7E-127F-4EFE-9BF9-ADF41756C267} .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: 0x02550000 +// Image base: 0x03460000 // =============== CLASS MEMBERS DECLARATION =================== @@ -510,7 +510,50 @@ } // end of property CustomStructEnumeratorWithIDisposable`1::Current } // end of class CustomStructEnumeratorWithIDisposable`1 - .class auto ansi sealed nested private beforefieldinit '<>c__DisplayClass25_0' + .class sequential ansi sealed nested public beforefieldinit DataItem + extends [mscorlib]System.ValueType + { + .field private int32 'k__BackingField' + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .method public hidebysig specialname + instance int32 get_Property() cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::'k__BackingField' + IL_0006: ret + } // end of method DataItem::get_Property + + .method public hidebysig specialname + instance void set_Property(int32 'value') cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::'k__BackingField' + IL_0007: ret + } // end of method DataItem::set_Property + + .method public hidebysig instance void + TestCall() cil managed + { + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method DataItem::TestCall + + .property instance int32 Property() + { + .get instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::get_Property() + .set instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::set_Property(int32) + } // end of property DataItem::Property + } // end of class DataItem + + .class auto ansi sealed nested private beforefieldinit '<>c__DisplayClass26_0' extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) @@ -523,7 +566,7 @@ IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret - } // end of method '<>c__DisplayClass25_0'::.ctor + } // end of method '<>c__DisplayClass26_0'::.ctor .method assembly hidebysig instance bool 'b__0'() cil managed @@ -531,13 +574,13 @@ // Code size 10 (0xa) .maxstack 8 IL_0000: ldarg.0 - IL_0001: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass25_0'::c + IL_0001: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass26_0'::c IL_0006: ldc.i4.5 IL_0007: ceq IL_0009: ret - } // end of method '<>c__DisplayClass25_0'::'b__0' + } // end of method '<>c__DisplayClass26_0'::'b__0' - } // end of class '<>c__DisplayClass25_0' + } // end of class '<>c__DisplayClass26_0' .field private class [mscorlib]System.Collections.Generic.IEnumerable`1 alternatives .method private hidebysig static void Operation(int32& item) cil managed @@ -1101,11 +1144,11 @@ IL_0009: ldloca.s V_0 IL_000b: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator::get_Current() IL_0010: stloc.1 - IL_0011: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass25_0'::.ctor() + IL_0011: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass26_0'::.ctor() IL_0016: dup IL_0017: ldloc.1 - IL_0018: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass25_0'::c - IL_001d: ldftn instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass25_0'::'b__0'() + IL_0018: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass26_0'::c + IL_001d: ldftn instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass26_0'::'b__0'() IL_0023: newobj instance void class [mscorlib]System.Func`1::.ctor(object, native int) IL_0028: call void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Operation(class [mscorlib]System.Func`1) @@ -1295,6 +1338,121 @@ IL_0058: ret } // end of method Loops::ForEachBreakWhenFound + .method public hidebysig instance void + ForEachOverListOfStruct(class [mscorlib]System.Collections.Generic.List`1 items, + int32 'value') cil managed + { + // Code size 51 (0x33) + .maxstack 2 + .locals init (valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator V_0, + valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem V_1) + IL_0000: ldarg.1 + IL_0001: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator class [mscorlib]System.Collections.Generic.List`1::GetEnumerator() + IL_0006: stloc.0 + .try + { + IL_0007: br.s IL_0019 + + IL_0009: ldloca.s V_0 + IL_000b: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator::get_Current() + IL_0010: stloc.1 + IL_0011: ldloca.s V_1 + IL_0013: ldarg.2 + IL_0014: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::set_Property(int32) + IL_0019: ldloca.s V_0 + IL_001b: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator::MoveNext() + IL_0020: brtrue.s IL_0009 + + IL_0022: leave.s IL_0032 + + } // end .try + finally + { + IL_0024: ldloca.s V_0 + IL_0026: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator + IL_002c: callvirt instance void [mscorlib]System.IDisposable::Dispose() + IL_0031: endfinally + } // end handler + IL_0032: ret + } // end of method Loops::ForEachOverListOfStruct + + .method public hidebysig instance void + ForEachOverListOfStruct2(class [mscorlib]System.Collections.Generic.List`1 items, + int32 'value') cil managed + { + // Code size 58 (0x3a) + .maxstack 2 + .locals init (valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator V_0, + valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem V_1) + IL_0000: ldarg.1 + IL_0001: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator class [mscorlib]System.Collections.Generic.List`1::GetEnumerator() + IL_0006: stloc.0 + .try + { + IL_0007: br.s IL_0020 + + IL_0009: ldloca.s V_0 + IL_000b: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator::get_Current() + IL_0010: stloc.1 + IL_0011: ldloca.s V_1 + IL_0013: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::TestCall() + IL_0018: ldloca.s V_1 + IL_001a: ldarg.2 + IL_001b: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::set_Property(int32) + IL_0020: ldloca.s V_0 + IL_0022: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator::MoveNext() + IL_0027: brtrue.s IL_0009 + + IL_0029: leave.s IL_0039 + + } // end .try + finally + { + IL_002b: ldloca.s V_0 + IL_002d: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator + IL_0033: callvirt instance void [mscorlib]System.IDisposable::Dispose() + IL_0038: endfinally + } // end handler + IL_0039: ret + } // end of method Loops::ForEachOverListOfStruct2 + + .method public hidebysig instance void + ForEachOverListOfStruct3(class [mscorlib]System.Collections.Generic.List`1 items, + int32 'value') cil managed + { + // Code size 50 (0x32) + .maxstack 1 + .locals init (valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator V_0, + valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem V_1) + IL_0000: ldarg.1 + IL_0001: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator class [mscorlib]System.Collections.Generic.List`1::GetEnumerator() + IL_0006: stloc.0 + .try + { + IL_0007: br.s IL_0018 + + IL_0009: ldloca.s V_0 + IL_000b: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator::get_Current() + IL_0010: stloc.1 + IL_0011: ldloca.s V_1 + IL_0013: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::TestCall() + IL_0018: ldloca.s V_0 + IL_001a: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator::MoveNext() + IL_001f: brtrue.s IL_0009 + + IL_0021: leave.s IL_0031 + + } // end .try + finally + { + IL_0023: ldloca.s V_0 + IL_0025: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator + IL_002b: callvirt instance void [mscorlib]System.IDisposable::Dispose() + IL_0030: endfinally + } // end handler + IL_0031: ret + } // end of method Loops::ForEachOverListOfStruct3 + .method public hidebysig instance void ForOverArray(string[] 'array') cil managed { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.roslyn.il index 570e97dad..98fe40405 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.roslyn.il @@ -25,14 +25,14 @@ .ver 0:0:0:0 } .module Loops.dll -// MVID: {8261F08F-23F6-43C1-9650-C5B6A10A0F30} +// MVID: {8F6E41CD-7AE5-437C-BACB-4A672A0014D0} .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: 0x00720000 +// Image base: 0x00C80000 // =============== CLASS MEMBERS DECLARATION =================== @@ -592,7 +592,52 @@ } // end of property CustomStructEnumeratorWithIDisposable`1::Current } // end of class CustomStructEnumeratorWithIDisposable`1 - .class auto ansi sealed nested private beforefieldinit '<>c__DisplayClass25_0' + .class sequential ansi sealed nested public beforefieldinit DataItem + extends [mscorlib]System.ValueType + { + .field private int32 'k__BackingField' + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void [mscorlib]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggerBrowsableState) = ( 01 00 00 00 00 00 00 00 ) + .method public hidebysig specialname + instance int32 get_Property() cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::'k__BackingField' + IL_0006: ret + } // end of method DataItem::get_Property + + .method public hidebysig specialname + instance void set_Property(int32 'value') cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::'k__BackingField' + IL_0007: ret + } // end of method DataItem::set_Property + + .method public hidebysig instance void + TestCall() cil managed + { + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method DataItem::TestCall + + .property instance int32 Property() + { + .get instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::get_Property() + .set instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::set_Property(int32) + } // end of property DataItem::Property + } // end of class DataItem + + .class auto ansi sealed nested private beforefieldinit '<>c__DisplayClass26_0' extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) @@ -606,7 +651,7 @@ IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret - } // end of method '<>c__DisplayClass25_0'::.ctor + } // end of method '<>c__DisplayClass26_0'::.ctor .method assembly hidebysig instance bool 'b__0'() cil managed @@ -614,13 +659,13 @@ // Code size 10 (0xa) .maxstack 8 IL_0000: ldarg.0 - IL_0001: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass25_0'::c + IL_0001: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass26_0'::c IL_0006: ldc.i4.5 IL_0007: ceq IL_0009: ret - } // end of method '<>c__DisplayClass25_0'::'b__0' + } // end of method '<>c__DisplayClass26_0'::'b__0' - } // end of class '<>c__DisplayClass25_0' + } // end of class '<>c__DisplayClass26_0' .field private class [mscorlib]System.Collections.Generic.IEnumerable`1 alternatives .method private hidebysig static void Operation(int32& item) cil managed @@ -1189,7 +1234,7 @@ .method public hidebysig static void NonGenericForeachWithReturnFallbackTest(class [mscorlib]System.Collections.IEnumerable e) cil managed { - // Code size 109 (0x6d) + // Code size 111 (0x6f) .maxstack 2 .locals init (class [mscorlib]System.Collections.IEnumerator V_0, bool V_1, @@ -1227,7 +1272,7 @@ IL_0041: nop IL_0042: nop IL_0043: nop - IL_0044: leave.s IL_0061 + IL_0044: leave.s IL_0063 } // end .try finally @@ -1241,18 +1286,20 @@ IL_0050: cgt.un IL_0052: stloc.s V_4 IL_0054: ldloc.s V_4 - IL_0056: brfalse.s IL_005f + IL_0056: brfalse.s IL_0061 - IL_0058: ldloc.3 - IL_0059: callvirt instance void [mscorlib]System.IDisposable::Dispose() - IL_005e: nop + IL_0058: nop + IL_0059: ldloc.3 + IL_005a: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_005f: nop - IL_0060: endfinally + IL_0060: nop + IL_0061: nop + IL_0062: endfinally } // end handler - IL_0061: ldstr "After finally!" - IL_0066: call void [mscorlib]System.Console::WriteLine(string) - IL_006b: nop - IL_006c: ret + IL_0063: ldstr "After finally!" + IL_0068: call void [mscorlib]System.Console::WriteLine(string) + IL_006d: nop + IL_006e: ret } // end of method Loops::NonGenericForeachWithReturnFallbackTest .method public hidebysig static void ForeachWithRefUsage(class [mscorlib]System.Collections.Generic.List`1 items) cil managed @@ -1305,7 +1352,7 @@ .maxstack 2 .locals init (valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator V_0, int32 V_1, - class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass25_0' V_2) + class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass26_0' V_2) IL_0000: nop IL_0001: nop IL_0002: ldarg.0 @@ -1318,14 +1365,14 @@ IL_000b: ldloca.s V_0 IL_000d: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator::get_Current() IL_0012: stloc.1 - IL_0013: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass25_0'::.ctor() + IL_0013: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass26_0'::.ctor() IL_0018: stloc.2 IL_0019: nop IL_001a: ldloc.2 IL_001b: ldloc.1 - IL_001c: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass25_0'::c + IL_001c: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass26_0'::c IL_0021: ldloc.2 - IL_0022: ldftn instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass25_0'::'b__0'() + IL_0022: ldftn instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass26_0'::'b__0'() IL_0028: newobj instance void class [mscorlib]System.Func`1::.ctor(object, native int) IL_002d: call void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Operation(class [mscorlib]System.Func`1) @@ -1554,6 +1601,146 @@ IL_0060: ret } // end of method Loops::ForEachBreakWhenFound + .method public hidebysig instance void + ForEachOverListOfStruct(class [mscorlib]System.Collections.Generic.List`1 items, + int32 'value') cil managed + { + // Code size 59 (0x3b) + .maxstack 2 + .locals init (valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator V_0, + valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem V_1, + valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem V_2) + IL_0000: nop + IL_0001: nop + IL_0002: ldarg.1 + IL_0003: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator class [mscorlib]System.Collections.Generic.List`1::GetEnumerator() + IL_0008: stloc.0 + .try + { + IL_0009: br.s IL_0020 + + IL_000b: ldloca.s V_0 + IL_000d: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator::get_Current() + IL_0012: stloc.1 + IL_0013: nop + IL_0014: ldloc.1 + IL_0015: stloc.2 + IL_0016: ldloca.s V_2 + IL_0018: ldarg.2 + IL_0019: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::set_Property(int32) + IL_001e: nop + IL_001f: nop + IL_0020: ldloca.s V_0 + IL_0022: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator::MoveNext() + IL_0027: brtrue.s IL_000b + + IL_0029: leave.s IL_003a + + } // end .try + finally + { + IL_002b: ldloca.s V_0 + IL_002d: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator + IL_0033: callvirt instance void [mscorlib]System.IDisposable::Dispose() + IL_0038: nop + IL_0039: endfinally + } // end handler + IL_003a: ret + } // end of method Loops::ForEachOverListOfStruct + + .method public hidebysig instance void + ForEachOverListOfStruct2(class [mscorlib]System.Collections.Generic.List`1 items, + int32 'value') cil managed + { + // Code size 67 (0x43) + .maxstack 2 + .locals init (valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator V_0, + valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem V_1, + valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem V_2) + IL_0000: nop + IL_0001: nop + IL_0002: ldarg.1 + IL_0003: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator class [mscorlib]System.Collections.Generic.List`1::GetEnumerator() + IL_0008: stloc.0 + .try + { + IL_0009: br.s IL_0028 + + IL_000b: ldloca.s V_0 + IL_000d: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator::get_Current() + IL_0012: stloc.1 + IL_0013: nop + IL_0014: ldloc.1 + IL_0015: stloc.2 + IL_0016: ldloca.s V_2 + IL_0018: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::TestCall() + IL_001d: nop + IL_001e: ldloca.s V_2 + IL_0020: ldarg.2 + IL_0021: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::set_Property(int32) + IL_0026: nop + IL_0027: nop + IL_0028: ldloca.s V_0 + IL_002a: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator::MoveNext() + IL_002f: brtrue.s IL_000b + + IL_0031: leave.s IL_0042 + + } // end .try + finally + { + IL_0033: ldloca.s V_0 + IL_0035: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator + IL_003b: callvirt instance void [mscorlib]System.IDisposable::Dispose() + IL_0040: nop + IL_0041: endfinally + } // end handler + IL_0042: ret + } // end of method Loops::ForEachOverListOfStruct2 + + .method public hidebysig instance void + ForEachOverListOfStruct3(class [mscorlib]System.Collections.Generic.List`1 items, + int32 'value') cil managed + { + // Code size 56 (0x38) + .maxstack 1 + .locals init (valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator V_0, + valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem V_1) + IL_0000: nop + IL_0001: nop + IL_0002: ldarg.1 + IL_0003: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator class [mscorlib]System.Collections.Generic.List`1::GetEnumerator() + IL_0008: stloc.0 + .try + { + IL_0009: br.s IL_001d + + IL_000b: ldloca.s V_0 + IL_000d: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator::get_Current() + IL_0012: stloc.1 + IL_0013: nop + IL_0014: ldloca.s V_1 + IL_0016: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/DataItem::TestCall() + IL_001b: nop + IL_001c: nop + IL_001d: ldloca.s V_0 + IL_001f: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator::MoveNext() + IL_0024: brtrue.s IL_000b + + IL_0026: leave.s IL_0037 + + } // end .try + finally + { + IL_0028: ldloca.s V_0 + IL_002a: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator + IL_0030: callvirt instance void [mscorlib]System.IDisposable::Dispose() + IL_0035: nop + IL_0036: endfinally + } // end handler + IL_0037: ret + } // end of method Loops::ForEachOverListOfStruct3 + .method public hidebysig instance void ForOverArray(string[] 'array') cil managed { @@ -1732,7 +1919,7 @@ .method public hidebysig instance void WhileLoop() cil managed { - // Code size 150 (0x96) + // Code size 151 (0x97) .maxstack 2 .locals init (bool V_0, bool V_1, @@ -1748,10 +1935,10 @@ IL_0012: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string) IL_0017: stloc.0 IL_0018: ldloc.0 - IL_0019: brfalse.s IL_008a + IL_0019: brfalse.s IL_008b IL_001b: nop - IL_001c: br.s IL_006d + IL_001c: br.s IL_006e IL_001e: nop IL_001f: ldstr "Loop Body" @@ -1762,7 +1949,7 @@ IL_0030: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string) IL_0035: stloc.1 IL_0036: ldloc.1 - IL_0037: brfalse.s IL_0061 + IL_0037: brfalse.s IL_0062 IL_0039: nop IL_003a: ldarg.0 @@ -1773,7 +1960,7 @@ IL_0047: brfalse.s IL_004c IL_0049: nop - IL_004a: br.s IL_006d + IL_004a: br.s IL_006e IL_004c: ldarg.0 IL_004d: ldstr "break" @@ -1782,36 +1969,37 @@ IL_0058: ceq IL_005a: stloc.3 IL_005b: ldloc.3 - IL_005c: brfalse.s IL_0060 + IL_005c: brfalse.s IL_0061 - IL_005e: br.s IL_007e + IL_005e: nop + IL_005f: br.s IL_007f - IL_0060: nop - IL_0061: ldstr "End of loop body" - IL_0066: call void [mscorlib]System.Console::WriteLine(string) - IL_006b: nop + IL_0061: nop + IL_0062: ldstr "End of loop body" + IL_0067: call void [mscorlib]System.Console::WriteLine(string) IL_006c: nop - IL_006d: ldarg.0 - IL_006e: ldstr "while" - IL_0073: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string) - IL_0078: stloc.s V_4 - IL_007a: ldloc.s V_4 - IL_007c: brtrue.s IL_001e - - IL_007e: ldstr "After loop" - IL_0083: call void [mscorlib]System.Console::WriteLine(string) - IL_0088: nop + IL_006d: nop + IL_006e: ldarg.0 + IL_006f: ldstr "while" + IL_0074: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string) + IL_0079: stloc.s V_4 + IL_007b: ldloc.s V_4 + IL_007d: brtrue.s IL_001e + + IL_007f: ldstr "After loop" + IL_0084: call void [mscorlib]System.Console::WriteLine(string) IL_0089: nop - IL_008a: ldstr "End of method" - IL_008f: call void [mscorlib]System.Console::WriteLine(string) - IL_0094: nop - IL_0095: ret + IL_008a: nop + IL_008b: ldstr "End of method" + IL_0090: call void [mscorlib]System.Console::WriteLine(string) + IL_0095: nop + IL_0096: ret } // end of method Loops::WhileLoop .method public hidebysig instance void ForLoop() cil managed { - // Code size 158 (0x9e) + // Code size 159 (0x9f) .maxstack 2 .locals init (bool V_0, int32 V_1, @@ -1828,12 +2016,12 @@ IL_0012: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string) IL_0017: stloc.0 IL_0018: ldloc.0 - IL_0019: brfalse.s IL_0092 + IL_0019: brfalse.s IL_0093 IL_001b: nop IL_001c: ldc.i4.0 IL_001d: stloc.1 - IL_001e: br.s IL_0075 + IL_001e: br.s IL_0076 IL_0020: nop IL_0021: ldstr "Loop Body" @@ -1844,7 +2032,7 @@ IL_0032: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string) IL_0037: stloc.2 IL_0038: ldloc.2 - IL_0039: brfalse.s IL_0065 + IL_0039: brfalse.s IL_0066 IL_003b: nop IL_003c: ldarg.0 @@ -1855,7 +2043,7 @@ IL_0049: brfalse.s IL_004e IL_004b: nop - IL_004c: br.s IL_0071 + IL_004c: br.s IL_0072 IL_004e: ldarg.0 IL_004f: ldstr "not-break" @@ -1864,34 +2052,35 @@ IL_005a: ceq IL_005c: stloc.s V_4 IL_005e: ldloc.s V_4 - IL_0060: brfalse.s IL_0064 + IL_0060: brfalse.s IL_0065 - IL_0062: br.s IL_0086 + IL_0062: nop + IL_0063: br.s IL_0087 - IL_0064: nop - IL_0065: ldstr "End of loop body" - IL_006a: call void [mscorlib]System.Console::WriteLine(string) - IL_006f: nop + IL_0065: nop + IL_0066: ldstr "End of loop body" + IL_006b: call void [mscorlib]System.Console::WriteLine(string) IL_0070: nop - IL_0071: ldloc.1 - IL_0072: ldc.i4.1 - IL_0073: add - IL_0074: stloc.1 - IL_0075: ldarg.0 - IL_0076: ldstr "for" - IL_007b: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string) - IL_0080: stloc.s V_5 - IL_0082: ldloc.s V_5 - IL_0084: brtrue.s IL_0020 - - IL_0086: ldstr "After loop" - IL_008b: call void [mscorlib]System.Console::WriteLine(string) - IL_0090: nop + IL_0071: nop + IL_0072: ldloc.1 + IL_0073: ldc.i4.1 + IL_0074: add + IL_0075: stloc.1 + IL_0076: ldarg.0 + IL_0077: ldstr "for" + IL_007c: call instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::Condition(string) + IL_0081: stloc.s V_5 + IL_0083: ldloc.s V_5 + IL_0085: brtrue.s IL_0020 + + IL_0087: ldstr "After loop" + IL_008c: call void [mscorlib]System.Console::WriteLine(string) IL_0091: nop - IL_0092: ldstr "End of method" - IL_0097: call void [mscorlib]System.Console::WriteLine(string) - IL_009c: nop - IL_009d: ret + IL_0092: nop + IL_0093: ldstr "End of method" + IL_0098: call void [mscorlib]System.Console::WriteLine(string) + IL_009d: nop + IL_009e: ret } // end of method Loops::ForLoop .method public hidebysig specialname rtspecialname diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs index a4ab57808..5168cdede 100644 --- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs @@ -471,6 +471,19 @@ namespace ICSharpCode.Decompiler.CSharp instToReplace.ReplaceWith(new LdLoc(foreachVariable)); body.Instructions.Insert(0, new StLoc(foreachVariable, instToReplace)); break; + case RequiredGetCurrentTransformation.IntroduceNewVariableAndLocalCopy: + foreachVariable = currentFunction.RegisterVariable( + VariableKind.ForeachLocal, type, + AssignVariableNames.GenerateForeachVariableName(currentFunction, collectionExpr.Annotation()) + ); + var localCopyVariable = currentFunction.RegisterVariable( + VariableKind.Local, type, + AssignVariableNames.GenerateVariableName(currentFunction, type) + ); + instToReplace.Parent.ReplaceWith(new LdLoca(localCopyVariable)); + body.Instructions.Insert(0, new StLoc(localCopyVariable, new LdLoc(foreachVariable))); + body.Instructions.Insert(0, new StLoc(foreachVariable, instToReplace)); + break; } // Convert the modified body to C# AST: var whileLoop = (WhileStatement)ConvertAsBlock(container).First(); @@ -563,7 +576,20 @@ namespace ICSharpCode.Decompiler.CSharp /// ... (ldloc foreachVar) ... /// /// - IntroduceNewVariable + IntroduceNewVariable, + /// + /// No store was found, thus create a new variable and use it as foreach variable. + /// Additionally it is necessary to copy the value of the foreach variable to another local + /// to allow safe modification of its value. + /// + /// ... addressof(call get_Current()) ... + /// => + /// stloc foreachVar(call get_Current()) + /// stloc copy(ldloc foreachVar) + /// ... (ldloca copy) ... + /// + /// + IntroduceNewVariableAndLocalCopy } /// @@ -603,6 +629,11 @@ namespace ICSharpCode.Decompiler.CSharp return RequiredGetCurrentTransformation.UseExistingVariable; } } + // In optimized Roslyn code it can happen that the foreach variable is referenced via addressof + // We only do this unwrapping if where dealing with a custom struct type. + if (CurrentIsStructSetterTarget(inst, singleGetter)) { + return RequiredGetCurrentTransformation.IntroduceNewVariableAndLocalCopy; + } // No suitable variable was found: we need a new one. return RequiredGetCurrentTransformation.IntroduceNewVariable; } @@ -616,13 +647,46 @@ namespace ICSharpCode.Decompiler.CSharp { if (storeInst.Variable.LoadInstructions.Any(ld => !ld.IsDescendantOf(usingContainer))) return false; - if (storeInst.Variable.AddressInstructions.Any(la => !la.IsDescendantOf(usingContainer) || !ILInlining.IsUsedAsThisPointerInCall(la))) + if (storeInst.Variable.AddressInstructions.Any(la => !la.IsDescendantOf(usingContainer) || !ILInlining.IsUsedAsThisPointerInCall(la) || IsTargetOfSetterCall(la, la.Variable.Type))) return false; if (storeInst.Variable.StoreInstructions.OfType().Any(st => st != storeInst)) return false; return true; } + /// + /// Returns true if singleGetter is a value type and its address is used as setter target. + /// + bool CurrentIsStructSetterTarget(ILInstruction inst, CallInstruction singleGetter) + { + if (!(inst.Parent is AddressOf addr)) + return false; + return IsTargetOfSetterCall(addr, singleGetter.Method.ReturnType); + } + + bool IsTargetOfSetterCall(ILInstruction inst, IType targetType) + { + if (inst.ChildIndex != 0) + return false; + if (targetType.IsReferenceType ?? false) + return false; + switch (inst.Parent.OpCode) { + case OpCode.Call: + case OpCode.CallVirt: + var targetMethod = ((CallInstruction)inst.Parent).Method; + if (!targetMethod.IsAccessor || targetMethod.IsStatic) + return false; + switch (targetMethod.AccessorOwner) { + case IProperty p: + return p.Setter == targetMethod; + default: + return true; + } + default: + return false; + } + } + bool ParentIsCurrentGetter(ILInstruction inst) { return inst.Parent is CallInstruction cv && cv.Method.IsAccessor &&