From ed8fc0de8c9f05459876c2e248b1c86289590dca Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 12 Nov 2017 21:15:18 +0100 Subject: [PATCH] Fix #968: foreach loop with early return --- .../TestCases/Pretty/Loops.cs | 10 ++ .../TestCases/Pretty/Loops.il | 69 +++++++++++++- .../TestCases/Pretty/Loops.opt.il | 52 +++++++++- .../TestCases/Pretty/Loops.opt.roslyn.il | 94 ++++++++++++++----- .../TestCases/Pretty/Loops.roslyn.il | 78 ++++++++++++--- .../IL/Transforms/HighLevelLoopTransform.cs | 29 ++++++ 6 files changed, 288 insertions(+), 44 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.cs index 65f322102..9b45c0f30 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.cs @@ -270,6 +270,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } private IEnumerable alternatives; + private object someObject; private void TryGetItem(int id, out Item item) { @@ -675,5 +676,14 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } } + + public void ForeachLoopWithEarlyReturn(List items) + { + foreach (object item in items) { + if ((this.someObject = item) == null) { + break; + } + } + } } } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.il index f9e7ed392..36e65e870 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 pk2dgaaq +.assembly gc5hv5wh { .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 pk2dgaaq.dll -// MVID: {85B97923-960B-4CE8-8A96-24E1123E3B2C} +.module gc5hv5wh.dll +// MVID: {062C7BAF-1AEF-4973-83C5-A0DC279703B8} .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: 0x00C10000 +// Image base: 0x038E0000 // =============== CLASS MEMBERS DECLARATION =================== @@ -683,6 +683,7 @@ } // end of class '<>c__DisplayClass1' .field private class [mscorlib]System.Collections.Generic.IEnumerable`1 alternatives + .field private object someObject .method private hidebysig instance void TryGetItem(int32 id, [out] class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/Item& item) cil managed @@ -2348,6 +2349,66 @@ IL_0031: ret } // end of method Loops::ForLoopWithEarlyReturn + .method public hidebysig instance void + ForeachLoopWithEarlyReturn(class [mscorlib]System.Collections.Generic.List`1 items) cil managed + { + // Code size 73 (0x49) + .maxstack 3 + .locals init (object V_0, + valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator V_1, + object 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.1 + .try + { + IL_0009: br.s IL_002b + + 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: ldarg.0 + IL_0015: ldloc.0 + IL_0016: dup + IL_0017: stloc.2 + IL_0018: stfld object ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::someObject + IL_001d: ldloc.2 + IL_001e: ldnull + IL_001f: ceq + IL_0021: ldc.i4.0 + IL_0022: ceq + IL_0024: stloc.3 + IL_0025: ldloc.3 + IL_0026: brtrue.s IL_002a + + IL_0028: br.s IL_0036 + + IL_002a: nop + IL_002b: ldloca.s V_1 + IL_002d: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator::MoveNext() + IL_0032: stloc.3 + IL_0033: ldloc.3 + IL_0034: brtrue.s IL_000b + + IL_0036: leave.s IL_0047 + + } // end .try + finally + { + IL_0038: ldloca.s V_1 + IL_003a: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator + IL_0040: callvirt instance void [mscorlib]System.IDisposable::Dispose() + IL_0045: nop + IL_0046: endfinally + } // end handler + IL_0047: nop + IL_0048: ret + } // end of method Loops::ForeachLoopWithEarlyReturn + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.opt.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.opt.il index 1fb397a53..ded149edc 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 nk23jmlw +.assembly iyvgltxy { .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 nk23jmlw.dll -// MVID: {AA004973-46E8-4F60-871C-9F7A919FE9EF} +.module iyvgltxy.dll +// MVID: {29F6B869-DC6A-4F23-A073-750A25BF2F78} .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: 0x01190000 +// Image base: 0x01130000 // =============== CLASS MEMBERS DECLARATION =================== @@ -594,6 +594,7 @@ } // end of class '<>c__DisplayClass1' .field private class [mscorlib]System.Collections.Generic.IEnumerable`1 alternatives + .field private object someObject .method private hidebysig instance void TryGetItem(int32 id, [out] class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/Item& item) cil managed @@ -1819,6 +1820,49 @@ IL_001f: ret } // end of method Loops::ForLoopWithEarlyReturn + .method public hidebysig instance void + ForeachLoopWithEarlyReturn(class [mscorlib]System.Collections.Generic.List`1 items) cil managed + { + // Code size 55 (0x37) + .maxstack 3 + .locals init (object V_0, + valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator V_1, + object 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.1 + .try + { + IL_0007: br.s IL_001d + + 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: ldarg.0 + IL_0012: ldloc.0 + IL_0013: dup + IL_0014: stloc.2 + IL_0015: stfld object ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::someObject + IL_001a: ldloc.2 + IL_001b: brfalse.s IL_0026 + + IL_001d: ldloca.s V_1 + IL_001f: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator::MoveNext() + IL_0024: brtrue.s IL_0009 + + IL_0026: leave.s IL_0036 + + } // end .try + finally + { + IL_0028: ldloca.s V_1 + IL_002a: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator + IL_0030: callvirt instance void [mscorlib]System.IDisposable::Dispose() + IL_0035: endfinally + } // end handler + IL_0036: ret + } // end of method Loops::ForeachLoopWithEarlyReturn + .method public hidebysig specialname rtspecialname instance void .ctor() 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 6105e24d4..40c9a5c84 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: {C5E69070-3D7F-4B1F-B9FD-172AC1104EF4} +// MVID: {781CB7F6-E31A-4804-B6CA-CB8C7FDF34DA} .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: 0x00D80000 +// Image base: 0x03730000 // =============== CLASS MEMBERS DECLARATION =================== @@ -568,7 +568,7 @@ } // end of class Item - .class auto ansi sealed nested private beforefieldinit '<>c__DisplayClass28_0' + .class auto ansi sealed nested private beforefieldinit '<>c__DisplayClass29_0' extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) @@ -581,7 +581,7 @@ IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret - } // end of method '<>c__DisplayClass28_0'::.ctor + } // end of method '<>c__DisplayClass29_0'::.ctor .method assembly hidebysig instance bool 'b__0'() cil managed @@ -589,15 +589,16 @@ // Code size 10 (0xa) .maxstack 8 IL_0000: ldarg.0 - IL_0001: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass28_0'::c + IL_0001: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass29_0'::c IL_0006: ldc.i4.5 IL_0007: ceq IL_0009: ret - } // end of method '<>c__DisplayClass28_0'::'b__0' + } // end of method '<>c__DisplayClass29_0'::'b__0' - } // end of class '<>c__DisplayClass28_0' + } // end of class '<>c__DisplayClass29_0' .field private class [mscorlib]System.Collections.Generic.IEnumerable`1 alternatives + .field private object someObject .method private hidebysig instance void TryGetItem(int32 id, [out] class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/Item& item) cil managed @@ -1171,11 +1172,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__DisplayClass28_0'::.ctor() + IL_0011: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass29_0'::.ctor() IL_0016: dup IL_0017: ldloc.1 - IL_0018: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass28_0'::c - IL_001d: ldftn instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass28_0'::'b__0'() + IL_0018: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass29_0'::c + IL_001d: ldftn instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass29_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) @@ -1737,13 +1738,13 @@ .method public hidebysig instance void ForLoopWithEarlyReturn(int32[] ids) cil managed { - // Code size 32 (0x20) + // Code size 31 (0x1f) .maxstack 3 .locals init (int32 V_0, class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/Item V_1) IL_0000: ldc.i4.0 IL_0001: stloc.0 - IL_0002: br.s IL_0019 + IL_0002: br.s IL_0018 IL_0004: ldnull IL_0005: stloc.1 @@ -1755,22 +1756,65 @@ IL_000c: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::TryGetItem(int32, class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/Item&) IL_0011: ldloc.1 - IL_0012: brtrue.s IL_0015 + IL_0012: brfalse.s IL_001e + + IL_0014: ldloc.0 + IL_0015: ldc.i4.1 + IL_0016: add + IL_0017: stloc.0 + IL_0018: ldloc.0 + IL_0019: ldarg.1 + IL_001a: ldlen + IL_001b: conv.i4 + IL_001c: blt.s IL_0004 + + IL_001e: ret + } // end of method Loops::ForLoopWithEarlyReturn - IL_0014: ret + .method public hidebysig instance void + ForeachLoopWithEarlyReturn(class [mscorlib]System.Collections.Generic.List`1 items) cil managed + { + // Code size 57 (0x39) + .maxstack 3 + .locals init (valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator V_0, + object V_1, + object 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.0 + .try + { + IL_0007: br.s IL_001f - IL_0015: ldloc.0 - IL_0016: ldc.i4.1 - IL_0017: add - IL_0018: stloc.0 - IL_0019: ldloc.0 - IL_001a: ldarg.1 - IL_001b: ldlen - IL_001c: conv.i4 - IL_001d: blt.s IL_0004 + 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: ldarg.0 + IL_0012: ldloc.1 + IL_0013: dup + IL_0014: stloc.2 + IL_0015: stfld object ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::someObject + IL_001a: ldloc.2 + IL_001b: brtrue.s IL_001f - IL_001f: ret - } // end of method Loops::ForLoopWithEarlyReturn + IL_001d: leave.s IL_0038 + + IL_001f: ldloca.s V_0 + IL_0021: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator::MoveNext() + IL_0026: brtrue.s IL_0009 + + IL_0028: leave.s IL_0038 + + } // end .try + finally + { + IL_002a: ldloca.s V_0 + IL_002c: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator + IL_0032: callvirt instance void [mscorlib]System.IDisposable::Dispose() + IL_0037: endfinally + } // end handler + IL_0038: ret + } // end of method Loops::ForeachLoopWithEarlyReturn .method public hidebysig specialname rtspecialname instance void .ctor() cil managed diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.roslyn.il index 4db3b1776..21100b415 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: {3E422539-E52E-4CE4-9C67-E7DBCEF4A91A} +// MVID: {673E7ABD-594F-4CEA-B653-2C92D7588EE4} .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: 0x03640000 +// Image base: 0x033F0000 // =============== CLASS MEMBERS DECLARATION =================== @@ -653,7 +653,7 @@ } // end of class Item - .class auto ansi sealed nested private beforefieldinit '<>c__DisplayClass28_0' + .class auto ansi sealed nested private beforefieldinit '<>c__DisplayClass29_0' extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) @@ -667,7 +667,7 @@ IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret - } // end of method '<>c__DisplayClass28_0'::.ctor + } // end of method '<>c__DisplayClass29_0'::.ctor .method assembly hidebysig instance bool 'b__0'() cil managed @@ -675,15 +675,16 @@ // Code size 10 (0xa) .maxstack 8 IL_0000: ldarg.0 - IL_0001: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass28_0'::c + IL_0001: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass29_0'::c IL_0006: ldc.i4.5 IL_0007: ceq IL_0009: ret - } // end of method '<>c__DisplayClass28_0'::'b__0' + } // end of method '<>c__DisplayClass29_0'::'b__0' - } // end of class '<>c__DisplayClass28_0' + } // end of class '<>c__DisplayClass29_0' .field private class [mscorlib]System.Collections.Generic.IEnumerable`1 alternatives + .field private object someObject .method private hidebysig instance void TryGetItem(int32 id, [out] class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/Item& item) cil managed @@ -1381,7 +1382,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__DisplayClass28_0' V_2) + class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass29_0' V_2) IL_0000: nop IL_0001: nop IL_0002: ldarg.0 @@ -1394,14 +1395,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__DisplayClass28_0'::.ctor() + IL_0013: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass29_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__DisplayClass28_0'::c + IL_001c: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass29_0'::c IL_0021: ldloc.2 - IL_0022: ldftn instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass28_0'::'b__0'() + IL_0022: ldftn instance bool ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops/'<>c__DisplayClass29_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) @@ -2213,6 +2214,61 @@ IL_002e: ret } // end of method Loops::ForLoopWithEarlyReturn + .method public hidebysig instance void + ForeachLoopWithEarlyReturn(class [mscorlib]System.Collections.Generic.List`1 items) cil managed + { + // Code size 67 (0x43) + .maxstack 3 + .locals init (valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator V_0, + object V_1, + bool V_2, + object 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.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: ldarg.0 + IL_0015: ldloc.1 + IL_0016: dup + IL_0017: stloc.3 + IL_0018: stfld object ICSharpCode.Decompiler.Tests.TestCases.Pretty.Loops::someObject + IL_001d: ldloc.3 + IL_001e: ldnull + IL_001f: ceq + IL_0021: stloc.2 + IL_0022: ldloc.2 + IL_0023: brfalse.s IL_0027 + + IL_0025: br.s IL_0031 + + 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::ForeachLoopWithEarlyReturn + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { diff --git a/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs index 466f399f8..a6e2739f3 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs @@ -86,6 +86,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms } else { return false; } + // Analyze conditions and decide whether to move some of them out of the condition block: + var conditions = new List(); + SplitConditions(condition.Condition, conditions); + // Break apart conditions that could be a MoveNext call followed by a Current accessor call: + if (MightBeHeaderOfForEach(loop, conditions)) { + ifInstruction.Condition = conditions[0]; + foreach (var cond in conditions.Skip(1).Reverse()) { + IfInstruction inst; + loopBody.Instructions.Insert(0, inst = new IfInstruction(Comp.LogicNot(cond), new Leave(loop))); + ExpressionTransforms.RunOnSingleStatment(inst, context); + } + } // move the branch/leave instruction into the condition block ifInstruction.FalseInst = loop.EntryPoint.Instructions[1]; loop.EntryPoint.Instructions.RemoveAt(1); @@ -112,6 +124,23 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; } + bool MightBeHeaderOfForEach(BlockContainer loop, List conditions) + { + if (conditions.Count <= 1) + return false; + if (!(conditions[0] is CallInstruction moveNextCall && moveNextCall.Method.Name == "MoveNext" + && conditions[1].Descendants.Any(IsGetCurrentCall))) + return false; + return loop.Parent?.Parent?.Parent is UsingInstruction; + + bool IsGetCurrentCall(ILInstruction inst) + { + return inst is CallInstruction getterCall + && getterCall.Method.IsAccessor + && getterCall.Method.Name == "get_Current"; + } + } + void SplitConditions(ILInstruction expression, List conditions) { if (expression.MatchLogicAnd(out var l, out var r)) {