From 7017d998d0dc426efe639b10e6d6c556e78636c4 Mon Sep 17 00:00:00 2001 From: Chicken-Bones Date: Tue, 24 Jul 2018 14:43:19 +1000 Subject: [PATCH] Improve switch decompilation in loops via early detection of continue blocks. --- .../PrettyTestRunner.cs | 5 +- .../TestCases/Pretty/Switch.cs | 301 ++++++ .../TestCases/Pretty/Switch.il | 832 +++++++++++++++- .../TestCases/Pretty/Switch.opt.il | 560 ++++++++++- .../TestCases/Pretty/Switch.opt.roslyn.il | 578 ++++++++++++ .../TestCases/Pretty/Switch.roslyn.il | 885 ++++++++++++++++++ .../IL/ControlFlow/ConditionDetection.cs | 23 +- .../IL/ControlFlow/LoopDetection.cs | 61 +- .../IL/ControlFlow/SwitchDetection.cs | 59 +- .../IL/Transforms/HighLevelLoopTransform.cs | 19 + .../Util/CollectionExtensions.cs | 16 + 11 files changed, 3283 insertions(+), 56 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs index c156299b2..285f3a86c 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -121,7 +121,10 @@ namespace ICSharpCode.Decompiler.Tests [Test] public void Switch([ValueSource("defaultOptions")] CSharpCompilerOptions cscOptions) { - RunForLibrary(cscOptions: cscOptions); + RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings { + // legacy csc generates a dead store in debug builds + RemoveDeadCode = (cscOptions == CSharpCompilerOptions.None) + }); } [Test] diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs index 5a3c7b1f7..bb69d1cb4 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Reflection; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -460,6 +461,46 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } Console.WriteLine("End of method"); } + + public static void SwitchWithGotoComplex(string s) + { + Console.WriteLine("SwitchWithGotoComplex: " + s); + switch (s) { + case "1": + Console.WriteLine("one"); + goto case "8"; + case "2": + Console.WriteLine("two"); + goto case "3"; + case "3": + Console.WriteLine("three"); + if (s.Length != 2) { + break; + } + goto case "5"; + case "4": + Console.WriteLine("four"); + goto case "5"; + case "5": + Console.WriteLine("five"); + goto case "8"; + case "6": + Console.WriteLine("six"); + goto case "5"; + case "8": + Console.WriteLine("eight"); + return; + // add a default case so that case "7": isn't redundant + default: + Console.WriteLine("default"); + break; + // note that goto case "7" will decompile as break; + // cases with a single break have the highest IL offset and are moved to the bottom + case "7": + break; + } + Console.WriteLine("End of method"); + } private static SetProperty[] GetProperties() { @@ -540,6 +581,266 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty Console.WriteLine("end"); } + public static void SwitchWithContinue1(int i, bool b) + { + while (true) { + switch (i) { +#if OPT + case 1: + continue; +#endif + case 0: + if (b) { + continue; + } + break; + case 2: + if (!b) { + continue; + } + break; +#if !OPT + case 1: + continue; +#endif + } + Console.WriteLine(); + } + } + + // while condition, return and break cases + public static void SwitchWithContinue2(int i, bool b) + { + while (i < 10) { + switch (i) { + case 0: + if (b) { + Console.WriteLine("0b"); + continue; + } + Console.WriteLine("0!b"); + break; + case 2: +#if OPT + if (b) { + Console.WriteLine("2b"); + return; + } + Console.WriteLine("2!b"); + continue; +#else + if (!b) { + Console.WriteLine("2!b"); + continue; + } + Console.WriteLine("2b"); + return; +#endif + default: + Console.WriteLine("default"); + break; + case 3: + break; + case 1: + continue; + } + Console.WriteLine("loop-tail"); + i++; + } + } + + // for loop version + public static void SwitchWithContinue3(bool b) + { + for (int i = 0; i < 10; i++) { + switch (i) { + case 0: + if (b) { + Console.WriteLine("0b"); + continue; + } + Console.WriteLine("0!b"); + break; + case 2: +#if OPT + if (b) { + Console.WriteLine("2b"); + return; + } + Console.WriteLine("2!b"); + continue; +#else + if (!b) { + Console.WriteLine("2!b"); + continue; + } + Console.WriteLine("2b"); + return; +#endif + default: + Console.WriteLine("default"); + break; + case 3: + break; + case 1: + continue; + } + Console.WriteLine("loop-tail"); + } + } + + // foreach version + public static void SwitchWithContinue4(bool b) + { + foreach (int item in Enumerable.Range(0, 10)) { + Console.WriteLine("loop: " + item); + switch (item) { + case 1: + if (b) { + continue; + } + break; + case 3: + if (!b) { + continue; + } + return; + case 4: + Console.WriteLine(4); + goto case 7; + case 5: + Console.WriteLine(5); + goto default; + case 6: + if (b) { + continue; + } + goto case 3; + case 7: + if (item % 2 == 0) { + goto case 3; + } + if (!b) { + continue; + } + goto case 8; + case 8: + if (b) { + continue; + } + goto case 5; + default: + Console.WriteLine("default"); + break; + case 2: + continue; + } + Console.WriteLine("break: " + item); + } + } + // internal if statement, loop increment block not dominated by the switch head + public static void SwitchWithContinue5(bool b) + { + for (int i = 0; i < 10; i++) { + if (i < 5) { + switch (i) { + case 0: + if (b) { + Console.WriteLine("0b"); + continue; + } + Console.WriteLine("0!b"); + break; + case 2: +#if OPT + if (b) { + Console.WriteLine("2b"); + return; + } + Console.WriteLine("2!b"); + continue; +#else + if (!b) { + Console.WriteLine("2!b"); + continue; + } + Console.WriteLine("2b"); + return; +#endif + default: + Console.WriteLine("default"); + break; + case 3: + break; + case 1: + continue; + } + Console.WriteLine("break-target"); + } + Console.WriteLine("loop-tail"); + } + } + + // do-while loop version + public static void SwitchWithContinue6(int i, bool b) + { + do { + switch (i) { + case 0: + if (!b) { + Console.WriteLine("0!b"); + break; + } + Console.WriteLine("0b"); + // ConditionDetection doesn't recognise Do-While continues yet + continue; + case 2: + if (b) { + Console.WriteLine("2b"); + return; + } + Console.WriteLine("2!b"); + continue; + default: + Console.WriteLine("default"); + break; + case 3: + break; + case 1: + continue; + } + Console.WriteLine("loop-tail"); + } while (++i < 10); + } + + public static void SwitchLoopNesting() + { + for (int i = 0; i < 10; i++) { + switch (i) { + case 0: + Console.WriteLine(0); + break; + case 1: + Console.WriteLine(1); + break; + default: + if (i % 2 == 0) { + while (i % 3 != 0) { + Console.WriteLine(i++); + } + } + Console.WriteLine(); + break; + } + + if (i > 4) { + Console.WriteLine("high"); + } else { + Console.WriteLine("low"); + } + } + } + // These decompile poorly into switch statements and should be left as is #region Overagressive Switch Use diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il index 8bfc4217b..b1fbbe782 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il @@ -8,6 +8,11 @@ .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } +.assembly extern System.Core +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} .assembly Switch { .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 @@ -1585,6 +1590,152 @@ IL_016a: ret } // end of method Switch::SwitchWithGotoString + .method public hidebysig static void SwitchWithGotoComplex(string s) cil managed + { + // Code size 338 (0x152) + .maxstack 4 + .locals init (string V_0, + int32 V_1, + bool V_2) + IL_0000: nop + IL_0001: ldstr "SwitchWithGotoComplex: " + IL_0006: ldarg.0 + IL_0007: call string [mscorlib]System.String::Concat(string, + string) + IL_000c: call void [mscorlib]System.Console::WriteLine(string) + IL_0011: nop + IL_0012: ldarg.0 + IL_0013: stloc.0 + IL_0014: ldloc.0 + IL_0015: brfalse IL_0137 + + IL_001a: volatile. + IL_001c: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x6000016-1' + IL_0021: brtrue.s IL_0090 + + IL_0023: ldc.i4.8 + IL_0024: newobj instance void class [mscorlib]System.Collections.Generic.Dictionary`2::.ctor(int32) + IL_0029: dup + IL_002a: ldstr "1" + IL_002f: ldc.i4.0 + IL_0030: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_0035: dup + IL_0036: ldstr "2" + IL_003b: ldc.i4.1 + IL_003c: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_0041: dup + IL_0042: ldstr "3" + IL_0047: ldc.i4.2 + IL_0048: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_004d: dup + IL_004e: ldstr "4" + IL_0053: ldc.i4.3 + IL_0054: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_0059: dup + IL_005a: ldstr "5" + IL_005f: ldc.i4.4 + IL_0060: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_0065: dup + IL_0066: ldstr "6" + IL_006b: ldc.i4.5 + IL_006c: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_0071: dup + IL_0072: ldstr "8" + IL_0077: ldc.i4.6 + IL_0078: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_007d: dup + IL_007e: ldstr "7" + IL_0083: ldc.i4.7 + IL_0084: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_0089: volatile. + IL_008b: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x6000016-1' + IL_0090: volatile. + IL_0092: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x6000016-1' + IL_0097: ldloc.0 + IL_0098: ldloca.s V_1 + IL_009a: call instance bool class [mscorlib]System.Collections.Generic.Dictionary`2::TryGetValue(!0, + !1&) + IL_009f: brfalse IL_0137 + + IL_00a4: ldloc.1 + IL_00a5: switch ( + IL_00cc, + IL_00d9, + IL_00e6, + IL_0103, + IL_0110, + IL_011d, + IL_012a, + IL_0144) + IL_00ca: br.s IL_0137 + + IL_00cc: ldstr "one" + IL_00d1: call void [mscorlib]System.Console::WriteLine(string) + IL_00d6: nop + IL_00d7: br.s IL_012a + + IL_00d9: ldstr "two" + IL_00de: call void [mscorlib]System.Console::WriteLine(string) + IL_00e3: nop + IL_00e4: br.s IL_00e6 + + IL_00e6: ldstr "three" + IL_00eb: call void [mscorlib]System.Console::WriteLine(string) + IL_00f0: nop + IL_00f1: ldarg.0 + IL_00f2: callvirt instance int32 [mscorlib]System.String::get_Length() + IL_00f7: ldc.i4.2 + IL_00f8: ceq + IL_00fa: stloc.2 + IL_00fb: ldloc.2 + IL_00fc: brtrue.s IL_0101 + + IL_00fe: nop + IL_00ff: br.s IL_0146 + + IL_0101: br.s IL_0110 + + IL_0103: ldstr "four" + IL_0108: call void [mscorlib]System.Console::WriteLine(string) + IL_010d: nop + IL_010e: br.s IL_0110 + + IL_0110: ldstr "five" + IL_0115: call void [mscorlib]System.Console::WriteLine(string) + IL_011a: nop + IL_011b: br.s IL_012a + + IL_011d: ldstr "six" + IL_0122: call void [mscorlib]System.Console::WriteLine(string) + IL_0127: nop + IL_0128: br.s IL_0110 + + IL_012a: ldstr "eight" + IL_012f: call void [mscorlib]System.Console::WriteLine(string) + IL_0134: nop + IL_0135: br.s IL_0151 + + IL_0137: ldstr "default" + IL_013c: call void [mscorlib]System.Console::WriteLine(string) + IL_0141: nop + IL_0142: br.s IL_0146 + + IL_0144: br.s IL_0146 + + IL_0146: ldstr "End of method" + IL_014b: call void [mscorlib]System.Console::WriteLine(string) + IL_0150: nop + IL_0151: ret + } // end of method Switch::SwitchWithGotoComplex + .method private hidebysig static class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty[] GetProperties() cil managed { @@ -1640,7 +1791,7 @@ IL_003b: brfalse IL_012c IL_0040: volatile. - IL_0042: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x6000017-1' + IL_0042: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x6000018-1' IL_0047: brtrue.s IL_009e IL_0049: ldc.i4.6 @@ -1676,9 +1827,9 @@ IL_0092: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, !1) IL_0097: volatile. - IL_0099: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x6000017-1' + IL_0099: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x6000018-1' IL_009e: volatile. - IL_00a0: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x6000017-1' + IL_00a0: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x6000018-1' IL_00a5: ldloc.s V_5 IL_00a7: ldloca.s V_6 IL_00a9: call instance bool class [mscorlib]System.Collections.Generic.Dictionary`2::TryGetValue(!0, @@ -1903,6 +2054,678 @@ IL_007d: ret } // end of method Switch::SwitchWithArray + .method public hidebysig static void SwitchWithContinue1(int32 i, + bool b) cil managed + { + // Code size 62 (0x3e) + .maxstack 2 + .locals init (int32 V_0, + bool V_1) + IL_0000: nop + IL_0001: br.s IL_003a + + IL_0003: nop + IL_0004: ldarg.0 + IL_0005: stloc.0 + IL_0006: ldloc.0 + IL_0007: switch ( + IL_001a, + IL_0031, + IL_0027) + IL_0018: br.s IL_0033 + + IL_001a: ldarg.1 + IL_001b: ldc.i4.0 + IL_001c: ceq + IL_001e: stloc.1 + IL_001f: ldloc.1 + IL_0020: brtrue.s IL_0025 + + IL_0022: nop + IL_0023: br.s IL_003a + + IL_0025: br.s IL_0033 + + IL_0027: ldarg.1 + IL_0028: stloc.1 + IL_0029: ldloc.1 + IL_002a: brtrue.s IL_002f + + IL_002c: nop + IL_002d: br.s IL_003a + + IL_002f: br.s IL_0033 + + IL_0031: br.s IL_003a + + IL_0033: call void [mscorlib]System.Console::WriteLine() + IL_0038: nop + IL_0039: nop + IL_003a: ldc.i4.1 + IL_003b: stloc.1 + IL_003c: br.s IL_0003 + } // end of method Switch::SwitchWithContinue1 + + .method public hidebysig static void SwitchWithContinue2(int32 i, + bool b) cil managed + { + // Code size 147 (0x93) + .maxstack 2 + .locals init (int32 V_0, + bool V_1) + IL_0000: nop + IL_0001: br IL_0086 + + IL_0006: nop + IL_0007: ldarg.0 + IL_0008: stloc.0 + IL_0009: ldloc.0 + IL_000a: switch ( + IL_0021, + IL_0073, + IL_0044, + IL_0071) + IL_001f: br.s IL_0064 + + IL_0021: ldarg.1 + IL_0022: ldc.i4.0 + IL_0023: ceq + IL_0025: stloc.1 + IL_0026: ldloc.1 + IL_0027: brtrue.s IL_0037 + + IL_0029: nop + IL_002a: ldstr "0b" + IL_002f: call void [mscorlib]System.Console::WriteLine(string) + IL_0034: nop + IL_0035: br.s IL_0086 + + IL_0037: ldstr "0!b" + IL_003c: call void [mscorlib]System.Console::WriteLine(string) + IL_0041: nop + IL_0042: br.s IL_0075 + + IL_0044: ldarg.1 + IL_0045: stloc.1 + IL_0046: ldloc.1 + IL_0047: brtrue.s IL_0057 + + IL_0049: nop + IL_004a: ldstr "2!b" + IL_004f: call void [mscorlib]System.Console::WriteLine(string) + IL_0054: nop + IL_0055: br.s IL_0086 + + IL_0057: ldstr "2b" + IL_005c: call void [mscorlib]System.Console::WriteLine(string) + IL_0061: nop + IL_0062: br.s IL_0092 + + IL_0064: ldstr "default" + IL_0069: call void [mscorlib]System.Console::WriteLine(string) + IL_006e: nop + IL_006f: br.s IL_0075 + + IL_0071: br.s IL_0075 + + IL_0073: br.s IL_0086 + + IL_0075: ldstr "loop-tail" + IL_007a: call void [mscorlib]System.Console::WriteLine(string) + IL_007f: nop + IL_0080: ldarg.0 + IL_0081: ldc.i4.1 + IL_0082: add + IL_0083: starg.s i + IL_0085: nop + IL_0086: ldarg.0 + IL_0087: ldc.i4.s 10 + IL_0089: clt + IL_008b: stloc.1 + IL_008c: ldloc.1 + IL_008d: brtrue IL_0006 + + IL_0092: ret + } // end of method Switch::SwitchWithContinue2 + + .method public hidebysig static void SwitchWithContinue3(bool b) cil managed + { + // Code size 145 (0x91) + .maxstack 2 + .locals init (int32 V_0, + int32 V_1, + bool V_2) + IL_0000: nop + IL_0001: ldc.i4.0 + IL_0002: stloc.0 + IL_0003: br.s IL_0084 + + IL_0005: nop + IL_0006: ldloc.0 + IL_0007: stloc.1 + IL_0008: ldloc.1 + IL_0009: switch ( + IL_0020, + IL_0072, + IL_0043, + IL_0070) + IL_001e: br.s IL_0063 + + IL_0020: ldarg.0 + IL_0021: ldc.i4.0 + IL_0022: ceq + IL_0024: stloc.2 + IL_0025: ldloc.2 + IL_0026: brtrue.s IL_0036 + + IL_0028: nop + IL_0029: ldstr "0b" + IL_002e: call void [mscorlib]System.Console::WriteLine(string) + IL_0033: nop + IL_0034: br.s IL_0080 + + IL_0036: ldstr "0!b" + IL_003b: call void [mscorlib]System.Console::WriteLine(string) + IL_0040: nop + IL_0041: br.s IL_0074 + + IL_0043: ldarg.0 + IL_0044: stloc.2 + IL_0045: ldloc.2 + IL_0046: brtrue.s IL_0056 + + IL_0048: nop + IL_0049: ldstr "2!b" + IL_004e: call void [mscorlib]System.Console::WriteLine(string) + IL_0053: nop + IL_0054: br.s IL_0080 + + IL_0056: ldstr "2b" + IL_005b: call void [mscorlib]System.Console::WriteLine(string) + IL_0060: nop + IL_0061: br.s IL_0090 + + IL_0063: ldstr "default" + IL_0068: call void [mscorlib]System.Console::WriteLine(string) + IL_006d: nop + IL_006e: br.s IL_0074 + + IL_0070: br.s IL_0074 + + IL_0072: br.s IL_0080 + + IL_0074: ldstr "loop-tail" + IL_0079: call void [mscorlib]System.Console::WriteLine(string) + IL_007e: nop + IL_007f: nop + IL_0080: ldloc.0 + IL_0081: ldc.i4.1 + IL_0082: add + IL_0083: stloc.0 + IL_0084: ldloc.0 + IL_0085: ldc.i4.s 10 + IL_0087: clt + IL_0089: stloc.2 + IL_008a: ldloc.2 + IL_008b: brtrue IL_0005 + + IL_0090: ret + } // end of method Switch::SwitchWithContinue3 + + .method public hidebysig static void SwitchWithContinue4(bool b) cil managed + { + // Code size 263 (0x107) + .maxstack 2 + .locals init (int32 V_0, + class [mscorlib]System.Collections.Generic.IEnumerator`1 V_1, + int32 V_2, + bool V_3) + IL_0000: nop + IL_0001: nop + IL_0002: ldc.i4.0 + IL_0003: ldc.i4.s 10 + IL_0005: call class [mscorlib]System.Collections.Generic.IEnumerable`1 [System.Core]System.Linq.Enumerable::Range(int32, + int32) + IL_000a: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1 class [mscorlib]System.Collections.Generic.IEnumerable`1::GetEnumerator() + IL_000f: stloc.1 + .try + { + IL_0010: br IL_00e5 + + IL_0015: ldloc.1 + IL_0016: callvirt instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1::get_Current() + IL_001b: stloc.0 + IL_001c: nop + IL_001d: ldstr "loop: " + IL_0022: ldloc.0 + IL_0023: box [mscorlib]System.Int32 + IL_0028: call string [mscorlib]System.String::Concat(object, + object) + IL_002d: call void [mscorlib]System.Console::WriteLine(string) + IL_0032: nop + IL_0033: ldloc.0 + IL_0034: stloc.2 + IL_0035: ldloc.2 + IL_0036: ldc.i4.1 + IL_0037: sub + IL_0038: switch ( + IL_005f, + IL_00cc, + IL_006c, + IL_0079, + IL_0082, + IL_008b, + IL_0098, + IL_00b2) + IL_005d: br.s IL_00bf + + IL_005f: ldarg.0 + IL_0060: ldc.i4.0 + IL_0061: ceq + IL_0063: stloc.3 + IL_0064: ldloc.3 + IL_0065: brtrue.s IL_006a + + IL_0067: nop + IL_0068: br.s IL_00e5 + + IL_006a: br.s IL_00ce + + IL_006c: ldarg.0 + IL_006d: stloc.3 + IL_006e: ldloc.3 + IL_006f: brtrue.s IL_0074 + + IL_0071: nop + IL_0072: br.s IL_00e5 + + IL_0074: leave IL_0105 + + IL_0079: ldc.i4.4 + IL_007a: call void [mscorlib]System.Console::WriteLine(int32) + IL_007f: nop + IL_0080: br.s IL_0098 + + IL_0082: ldc.i4.5 + IL_0083: call void [mscorlib]System.Console::WriteLine(int32) + IL_0088: nop + IL_0089: br.s IL_00bf + + IL_008b: ldarg.0 + IL_008c: ldc.i4.0 + IL_008d: ceq + IL_008f: stloc.3 + IL_0090: ldloc.3 + IL_0091: brtrue.s IL_0096 + + IL_0093: nop + IL_0094: br.s IL_00e5 + + IL_0096: br.s IL_006c + + IL_0098: ldloc.0 + IL_0099: ldc.i4.2 + IL_009a: rem + IL_009b: ldc.i4.0 + IL_009c: ceq + IL_009e: ldc.i4.0 + IL_009f: ceq + IL_00a1: stloc.3 + IL_00a2: ldloc.3 + IL_00a3: brtrue.s IL_00a8 + + IL_00a5: nop + IL_00a6: br.s IL_006c + + IL_00a8: ldarg.0 + IL_00a9: stloc.3 + IL_00aa: ldloc.3 + IL_00ab: brtrue.s IL_00b0 + + IL_00ad: nop + IL_00ae: br.s IL_00e5 + + IL_00b0: br.s IL_00b2 + + IL_00b2: ldarg.0 + IL_00b3: ldc.i4.0 + IL_00b4: ceq + IL_00b6: stloc.3 + IL_00b7: ldloc.3 + IL_00b8: brtrue.s IL_00bd + + IL_00ba: nop + IL_00bb: br.s IL_00e5 + + IL_00bd: br.s IL_0082 + + IL_00bf: ldstr "default" + IL_00c4: call void [mscorlib]System.Console::WriteLine(string) + IL_00c9: nop + IL_00ca: br.s IL_00ce + + IL_00cc: br.s IL_00e5 + + IL_00ce: ldstr "break: " + IL_00d3: ldloc.0 + IL_00d4: box [mscorlib]System.Int32 + IL_00d9: call string [mscorlib]System.String::Concat(object, + object) + IL_00de: call void [mscorlib]System.Console::WriteLine(string) + IL_00e3: nop + IL_00e4: nop + IL_00e5: ldloc.1 + IL_00e6: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext() + IL_00eb: stloc.3 + IL_00ec: ldloc.3 + IL_00ed: brtrue IL_0015 + + IL_00f2: leave.s IL_0104 + + } // end .try + finally + { + IL_00f4: ldloc.1 + IL_00f5: ldnull + IL_00f6: ceq + IL_00f8: stloc.3 + IL_00f9: ldloc.3 + IL_00fa: brtrue.s IL_0103 + + IL_00fc: ldloc.1 + IL_00fd: callvirt instance void [mscorlib]System.IDisposable::Dispose() + IL_0102: nop + IL_0103: endfinally + } // end handler + IL_0104: nop + IL_0105: nop + IL_0106: ret + } // end of method Switch::SwitchWithContinue4 + + .method public hidebysig static void SwitchWithContinue5(bool b) cil managed + { + // Code size 172 (0xac) + .maxstack 2 + .locals init (int32 V_0, + bool V_1, + int32 V_2) + IL_0000: nop + IL_0001: ldc.i4.0 + IL_0002: stloc.0 + IL_0003: br IL_009f + + IL_0008: nop + IL_0009: ldloc.0 + IL_000a: ldc.i4.5 + IL_000b: clt + IL_000d: ldc.i4.0 + IL_000e: ceq + IL_0010: stloc.1 + IL_0011: ldloc.1 + IL_0012: brtrue.s IL_008f + + IL_0014: nop + IL_0015: ldloc.0 + IL_0016: stloc.2 + IL_0017: ldloc.2 + IL_0018: switch ( + IL_002f, + IL_0081, + IL_0052, + IL_007f) + IL_002d: br.s IL_0072 + + IL_002f: ldarg.0 + IL_0030: ldc.i4.0 + IL_0031: ceq + IL_0033: stloc.1 + IL_0034: ldloc.1 + IL_0035: brtrue.s IL_0045 + + IL_0037: nop + IL_0038: ldstr "0b" + IL_003d: call void [mscorlib]System.Console::WriteLine(string) + IL_0042: nop + IL_0043: br.s IL_009b + + IL_0045: ldstr "0!b" + IL_004a: call void [mscorlib]System.Console::WriteLine(string) + IL_004f: nop + IL_0050: br.s IL_0083 + + IL_0052: ldarg.0 + IL_0053: stloc.1 + IL_0054: ldloc.1 + IL_0055: brtrue.s IL_0065 + + IL_0057: nop + IL_0058: ldstr "2!b" + IL_005d: call void [mscorlib]System.Console::WriteLine(string) + IL_0062: nop + IL_0063: br.s IL_009b + + IL_0065: ldstr "2b" + IL_006a: call void [mscorlib]System.Console::WriteLine(string) + IL_006f: nop + IL_0070: br.s IL_00ab + + IL_0072: ldstr "default" + IL_0077: call void [mscorlib]System.Console::WriteLine(string) + IL_007c: nop + IL_007d: br.s IL_0083 + + IL_007f: br.s IL_0083 + + IL_0081: br.s IL_009b + + IL_0083: ldstr "break-target" + IL_0088: call void [mscorlib]System.Console::WriteLine(string) + IL_008d: nop + IL_008e: nop + IL_008f: ldstr "loop-tail" + IL_0094: call void [mscorlib]System.Console::WriteLine(string) + IL_0099: nop + IL_009a: nop + IL_009b: ldloc.0 + IL_009c: ldc.i4.1 + IL_009d: add + IL_009e: stloc.0 + IL_009f: ldloc.0 + IL_00a0: ldc.i4.s 10 + IL_00a2: clt + IL_00a4: stloc.1 + IL_00a5: ldloc.1 + IL_00a6: brtrue IL_0008 + + IL_00ab: ret + } // end of method Switch::SwitchWithContinue5 + + .method public hidebysig static void SwitchWithContinue6(int32 i, + bool b) cil managed + { + // Code size 142 (0x8e) + .maxstack 2 + .locals init (int32 V_0, + bool V_1) + IL_0000: nop + IL_0001: nop + IL_0002: ldarg.0 + IL_0003: stloc.0 + IL_0004: ldloc.0 + IL_0005: switch ( + IL_001c, + IL_006e, + IL_003c, + IL_006c) + IL_001a: br.s IL_005f + + IL_001c: ldarg.1 + IL_001d: stloc.1 + IL_001e: ldloc.1 + IL_001f: brtrue.s IL_002f + + IL_0021: nop + IL_0022: ldstr "0!b" + IL_0027: call void [mscorlib]System.Console::WriteLine(string) + IL_002c: nop + IL_002d: br.s IL_0070 + + IL_002f: ldstr "0b" + IL_0034: call void [mscorlib]System.Console::WriteLine(string) + IL_0039: nop + IL_003a: br.s IL_007c + + IL_003c: ldarg.1 + IL_003d: ldc.i4.0 + IL_003e: ceq + IL_0040: stloc.1 + IL_0041: ldloc.1 + IL_0042: brtrue.s IL_0052 + + IL_0044: nop + IL_0045: ldstr "2b" + IL_004a: call void [mscorlib]System.Console::WriteLine(string) + IL_004f: nop + IL_0050: br.s IL_008d + + IL_0052: ldstr "2!b" + IL_0057: call void [mscorlib]System.Console::WriteLine(string) + IL_005c: nop + IL_005d: br.s IL_007c + + IL_005f: ldstr "default" + IL_0064: call void [mscorlib]System.Console::WriteLine(string) + IL_0069: nop + IL_006a: br.s IL_0070 + + IL_006c: br.s IL_0070 + + IL_006e: br.s IL_007c + + IL_0070: ldstr "loop-tail" + IL_0075: call void [mscorlib]System.Console::WriteLine(string) + IL_007a: nop + IL_007b: nop + IL_007c: ldarg.0 + IL_007d: ldc.i4.1 + IL_007e: add + IL_007f: dup + IL_0080: starg.s i + IL_0082: ldc.i4.s 10 + IL_0084: clt + IL_0086: stloc.1 + IL_0087: ldloc.1 + IL_0088: brtrue IL_0001 + + IL_008d: ret + } // end of method Switch::SwitchWithContinue6 + + .method public hidebysig static void SwitchLoopNesting() cil managed + { + // Code size 153 (0x99) + .maxstack 3 + .locals init (int32 V_0, + int32 V_1, + bool V_2) + IL_0000: nop + IL_0001: ldc.i4.0 + IL_0002: stloc.0 + IL_0003: br IL_008c + + IL_0008: nop + IL_0009: ldloc.0 + IL_000a: stloc.1 + IL_000b: ldloc.1 + IL_000c: switch ( + IL_001b, + IL_0024) + IL_0019: br.s IL_002d + + IL_001b: ldc.i4.0 + IL_001c: call void [mscorlib]System.Console::WriteLine(int32) + IL_0021: nop + IL_0022: br.s IL_0060 + + IL_0024: ldc.i4.1 + IL_0025: call void [mscorlib]System.Console::WriteLine(int32) + IL_002a: nop + IL_002b: br.s IL_0060 + + IL_002d: ldloc.0 + IL_002e: ldc.i4.2 + IL_002f: rem + IL_0030: ldc.i4.0 + IL_0031: ceq + IL_0033: ldc.i4.0 + IL_0034: ceq + IL_0036: stloc.2 + IL_0037: ldloc.2 + IL_0038: brtrue.s IL_0058 + + IL_003a: nop + IL_003b: br.s IL_004a + + IL_003d: nop + IL_003e: ldloc.0 + IL_003f: dup + IL_0040: ldc.i4.1 + IL_0041: add + IL_0042: stloc.0 + IL_0043: call void [mscorlib]System.Console::WriteLine(int32) + IL_0048: nop + IL_0049: nop + IL_004a: ldloc.0 + IL_004b: ldc.i4.3 + IL_004c: rem + IL_004d: ldc.i4.0 + IL_004e: ceq + IL_0050: ldc.i4.0 + IL_0051: ceq + IL_0053: stloc.2 + IL_0054: ldloc.2 + IL_0055: brtrue.s IL_003d + + IL_0057: nop + IL_0058: call void [mscorlib]System.Console::WriteLine() + IL_005d: nop + IL_005e: br.s IL_0060 + + IL_0060: ldloc.0 + IL_0061: ldc.i4.4 + IL_0062: cgt + IL_0064: ldc.i4.0 + IL_0065: ceq + IL_0067: stloc.2 + IL_0068: ldloc.2 + IL_0069: brtrue.s IL_007a + + IL_006b: nop + IL_006c: ldstr "high" + IL_0071: call void [mscorlib]System.Console::WriteLine(string) + IL_0076: nop + IL_0077: nop + IL_0078: br.s IL_0087 + + IL_007a: nop + IL_007b: ldstr "low" + IL_0080: call void [mscorlib]System.Console::WriteLine(string) + IL_0085: nop + IL_0086: nop + IL_0087: nop + IL_0088: ldloc.0 + IL_0089: ldc.i4.1 + IL_008a: add + IL_008b: stloc.0 + IL_008c: ldloc.0 + IL_008d: ldc.i4.s 10 + IL_008f: clt + IL_0091: stloc.2 + IL_0092: ldloc.2 + IL_0093: brtrue IL_0008 + + IL_0098: ret + } // end of method Switch::SwitchLoopNesting + .method public hidebysig static void SingleIf2(int32 i, bool a, bool b) cil managed @@ -2676,7 +3499,8 @@ .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000010-1' .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000011-1' .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000015-1' - .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000017-1' + .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000016-1' + .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000018-1' } // end of class '' diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il index 39e9f2ba7..701af0ff0 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il @@ -8,6 +8,11 @@ .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } +.assembly extern System.Core +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} .assembly Switch.opt { .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 @@ -1351,6 +1356,126 @@ IL_0153: ret } // end of method Switch::SwitchWithGotoString + .method public hidebysig static void SwitchWithGotoComplex(string s) cil managed + { + // Code size 311 (0x137) + .maxstack 4 + .locals init (string V_0, + int32 V_1) + IL_0000: ldstr "SwitchWithGotoComplex: " + IL_0005: ldarg.0 + IL_0006: call string [mscorlib]System.String::Concat(string, + string) + IL_000b: call void [mscorlib]System.Console::WriteLine(string) + IL_0010: ldarg.0 + IL_0011: dup + IL_0012: stloc.0 + IL_0013: brfalse IL_0122 + + IL_0018: volatile. + IL_001a: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x6000016-1' + IL_001f: brtrue.s IL_008e + + IL_0021: ldc.i4.8 + IL_0022: newobj instance void class [mscorlib]System.Collections.Generic.Dictionary`2::.ctor(int32) + IL_0027: dup + IL_0028: ldstr "1" + IL_002d: ldc.i4.0 + IL_002e: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_0033: dup + IL_0034: ldstr "2" + IL_0039: ldc.i4.1 + IL_003a: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_003f: dup + IL_0040: ldstr "3" + IL_0045: ldc.i4.2 + IL_0046: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_004b: dup + IL_004c: ldstr "4" + IL_0051: ldc.i4.3 + IL_0052: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_0057: dup + IL_0058: ldstr "5" + IL_005d: ldc.i4.4 + IL_005e: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_0063: dup + IL_0064: ldstr "6" + IL_0069: ldc.i4.5 + IL_006a: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_006f: dup + IL_0070: ldstr "8" + IL_0075: ldc.i4.6 + IL_0076: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_007b: dup + IL_007c: ldstr "7" + IL_0081: ldc.i4.7 + IL_0082: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_0087: volatile. + IL_0089: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x6000016-1' + IL_008e: volatile. + IL_0090: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x6000016-1' + IL_0095: ldloc.0 + IL_0096: ldloca.s V_1 + IL_0098: call instance bool class [mscorlib]System.Collections.Generic.Dictionary`2::TryGetValue(!0, + !1&) + IL_009d: brfalse IL_0122 + + IL_00a2: ldloc.1 + IL_00a3: switch ( + IL_00ca, + IL_00d6, + IL_00e0, + IL_00f5, + IL_00ff, + IL_010b, + IL_0117, + IL_012c) + IL_00c8: br.s IL_0122 + + IL_00ca: ldstr "one" + IL_00cf: call void [mscorlib]System.Console::WriteLine(string) + IL_00d4: br.s IL_0117 + + IL_00d6: ldstr "two" + IL_00db: call void [mscorlib]System.Console::WriteLine(string) + IL_00e0: ldstr "three" + IL_00e5: call void [mscorlib]System.Console::WriteLine(string) + IL_00ea: ldarg.0 + IL_00eb: callvirt instance int32 [mscorlib]System.String::get_Length() + IL_00f0: ldc.i4.2 + IL_00f1: beq.s IL_00ff + + IL_00f3: br.s IL_012c + + IL_00f5: ldstr "four" + IL_00fa: call void [mscorlib]System.Console::WriteLine(string) + IL_00ff: ldstr "five" + IL_0104: call void [mscorlib]System.Console::WriteLine(string) + IL_0109: br.s IL_0117 + + IL_010b: ldstr "six" + IL_0110: call void [mscorlib]System.Console::WriteLine(string) + IL_0115: br.s IL_00ff + + IL_0117: ldstr "eight" + IL_011c: call void [mscorlib]System.Console::WriteLine(string) + IL_0121: ret + + IL_0122: ldstr "default" + IL_0127: call void [mscorlib]System.Console::WriteLine(string) + IL_012c: ldstr "End of method" + IL_0131: call void [mscorlib]System.Console::WriteLine(string) + IL_0136: ret + } // end of method Switch::SwitchWithGotoComplex + .method private hidebysig static class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty[] GetProperties() cil managed { @@ -1396,7 +1521,7 @@ IL_0037: brfalse IL_011f IL_003c: volatile. - IL_003e: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x6000017-1' + IL_003e: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x6000018-1' IL_0043: brtrue.s IL_009a IL_0045: ldc.i4.6 @@ -1432,9 +1557,9 @@ IL_008e: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, !1) IL_0093: volatile. - IL_0095: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x6000017-1' + IL_0095: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x6000018-1' IL_009a: volatile. - IL_009c: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x6000017-1' + IL_009c: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 ''::'$$method0x6000018-1' IL_00a1: ldloc.s V_5 IL_00a3: ldloca.s V_6 IL_00a5: call instance bool class [mscorlib]System.Collections.Generic.Dictionary`2::TryGetValue(!0, @@ -1626,6 +1751,432 @@ IL_0075: ret } // end of method Switch::SwitchWithArray + .method public hidebysig static void SwitchWithContinue1(int32 i, + bool b) cil managed + { + // Code size 37 (0x25) + .maxstack 1 + .locals init (int32 V_0) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: switch ( + IL_0016, + IL_0000, + IL_001b) + IL_0014: br.s IL_001e + + IL_0016: ldarg.1 + IL_0017: brfalse.s IL_001e + + IL_0019: br.s IL_0000 + + IL_001b: ldarg.1 + IL_001c: brfalse.s IL_0000 + + IL_001e: call void [mscorlib]System.Console::WriteLine() + IL_0023: br.s IL_0000 + } // end of method Switch::SwitchWithContinue1 + + .method public hidebysig static void SwitchWithContinue2(int32 i, + bool b) cil managed + { + // Code size 112 (0x70) + .maxstack 2 + .locals init (int32 V_0) + IL_0000: br.s IL_006a + + IL_0002: ldarg.0 + IL_0003: stloc.0 + IL_0004: ldloc.0 + IL_0005: switch ( + IL_001c, + IL_006a, + IL_0037, + IL_005b) + IL_001a: br.s IL_0051 + + IL_001c: ldarg.1 + IL_001d: brfalse.s IL_002b + + IL_001f: ldstr "0b" + IL_0024: call void [mscorlib]System.Console::WriteLine(string) + IL_0029: br.s IL_006a + + IL_002b: ldstr "0!b" + IL_0030: call void [mscorlib]System.Console::WriteLine(string) + IL_0035: br.s IL_005b + + IL_0037: ldarg.1 + IL_0038: brfalse.s IL_0045 + + IL_003a: ldstr "2b" + IL_003f: call void [mscorlib]System.Console::WriteLine(string) + IL_0044: ret + + IL_0045: ldstr "2!b" + IL_004a: call void [mscorlib]System.Console::WriteLine(string) + IL_004f: br.s IL_006a + + IL_0051: ldstr "default" + IL_0056: call void [mscorlib]System.Console::WriteLine(string) + IL_005b: ldstr "loop-tail" + IL_0060: call void [mscorlib]System.Console::WriteLine(string) + IL_0065: ldarg.0 + IL_0066: ldc.i4.1 + IL_0067: add + IL_0068: starg.s i + IL_006a: ldarg.0 + IL_006b: ldc.i4.s 10 + IL_006d: blt.s IL_0002 + + IL_006f: ret + } // end of method Switch::SwitchWithContinue2 + + .method public hidebysig static void SwitchWithContinue3(bool b) cil managed + { + // Code size 113 (0x71) + .maxstack 2 + .locals init (int32 V_0, + int32 V_1) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + IL_0002: br.s IL_006b + + IL_0004: ldloc.0 + IL_0005: stloc.1 + IL_0006: ldloc.1 + IL_0007: switch ( + IL_001e, + IL_0067, + IL_0039, + IL_005d) + IL_001c: br.s IL_0053 + + IL_001e: ldarg.0 + IL_001f: brfalse.s IL_002d + + IL_0021: ldstr "0b" + IL_0026: call void [mscorlib]System.Console::WriteLine(string) + IL_002b: br.s IL_0067 + + IL_002d: ldstr "0!b" + IL_0032: call void [mscorlib]System.Console::WriteLine(string) + IL_0037: br.s IL_005d + + IL_0039: ldarg.0 + IL_003a: brfalse.s IL_0047 + + IL_003c: ldstr "2b" + IL_0041: call void [mscorlib]System.Console::WriteLine(string) + IL_0046: ret + + IL_0047: ldstr "2!b" + IL_004c: call void [mscorlib]System.Console::WriteLine(string) + IL_0051: br.s IL_0067 + + IL_0053: ldstr "default" + IL_0058: call void [mscorlib]System.Console::WriteLine(string) + IL_005d: ldstr "loop-tail" + IL_0062: call void [mscorlib]System.Console::WriteLine(string) + IL_0067: ldloc.0 + IL_0068: ldc.i4.1 + IL_0069: add + IL_006a: stloc.0 + IL_006b: ldloc.0 + IL_006c: ldc.i4.s 10 + IL_006e: blt.s IL_0004 + + IL_0070: ret + } // end of method Switch::SwitchWithContinue3 + + .method public hidebysig static void SwitchWithContinue4(bool b) cil managed + { + // Code size 190 (0xbe) + .maxstack 2 + .locals init (int32 V_0, + class [mscorlib]System.Collections.Generic.IEnumerator`1 V_1, + int32 V_2) + IL_0000: ldc.i4.0 + IL_0001: ldc.i4.s 10 + IL_0003: call class [mscorlib]System.Collections.Generic.IEnumerable`1 [System.Core]System.Linq.Enumerable::Range(int32, + int32) + IL_0008: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1 class [mscorlib]System.Collections.Generic.IEnumerable`1::GetEnumerator() + IL_000d: stloc.1 + .try + { + IL_000e: br IL_00a6 + + IL_0013: ldloc.1 + IL_0014: callvirt instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1::get_Current() + IL_0019: stloc.0 + IL_001a: ldstr "loop: " + IL_001f: ldloc.0 + IL_0020: box [mscorlib]System.Int32 + IL_0025: call string [mscorlib]System.String::Concat(object, + object) + IL_002a: call void [mscorlib]System.Console::WriteLine(string) + IL_002f: ldloc.0 + IL_0030: stloc.2 + IL_0031: ldloc.2 + IL_0032: ldc.i4.1 + IL_0033: sub + IL_0034: switch ( + IL_005b, + IL_00a6, + IL_0060, + IL_0065, + IL_006d, + IL_0075, + IL_007a, + IL_0082) + IL_0059: br.s IL_0087 + + IL_005b: ldarg.0 + IL_005c: brfalse.s IL_0091 + + IL_005e: br.s IL_00a6 + + IL_0060: ldarg.0 + IL_0061: brfalse.s IL_00a6 + + IL_0063: leave.s IL_00bd + + IL_0065: ldc.i4.4 + IL_0066: call void [mscorlib]System.Console::WriteLine(int32) + IL_006b: br.s IL_007a + + IL_006d: ldc.i4.5 + IL_006e: call void [mscorlib]System.Console::WriteLine(int32) + IL_0073: br.s IL_0087 + + IL_0075: ldarg.0 + IL_0076: brfalse.s IL_0060 + + IL_0078: br.s IL_00a6 + + IL_007a: ldloc.0 + IL_007b: ldc.i4.2 + IL_007c: rem + IL_007d: brfalse.s IL_0060 + + IL_007f: ldarg.0 + IL_0080: brfalse.s IL_00a6 + + IL_0082: ldarg.0 + IL_0083: brfalse.s IL_006d + + IL_0085: br.s IL_00a6 + + IL_0087: ldstr "default" + IL_008c: call void [mscorlib]System.Console::WriteLine(string) + IL_0091: ldstr "break: " + IL_0096: ldloc.0 + IL_0097: box [mscorlib]System.Int32 + IL_009c: call string [mscorlib]System.String::Concat(object, + object) + IL_00a1: call void [mscorlib]System.Console::WriteLine(string) + IL_00a6: ldloc.1 + IL_00a7: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext() + IL_00ac: brtrue IL_0013 + + IL_00b1: leave.s IL_00bd + + } // end .try + finally + { + IL_00b3: ldloc.1 + IL_00b4: brfalse.s IL_00bc + + IL_00b6: ldloc.1 + IL_00b7: callvirt instance void [mscorlib]System.IDisposable::Dispose() + IL_00bc: endfinally + } // end handler + IL_00bd: ret + } // end of method Switch::SwitchWithContinue4 + + .method public hidebysig static void SwitchWithContinue5(bool b) cil managed + { + // Code size 127 (0x7f) + .maxstack 2 + .locals init (int32 V_0, + int32 V_1) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + IL_0002: br.s IL_0079 + + IL_0004: ldloc.0 + IL_0005: ldc.i4.5 + IL_0006: bge.s IL_006b + + IL_0008: ldloc.0 + IL_0009: stloc.1 + IL_000a: ldloc.1 + IL_000b: switch ( + IL_0022, + IL_0075, + IL_003d, + IL_0061) + IL_0020: br.s IL_0057 + + IL_0022: ldarg.0 + IL_0023: brfalse.s IL_0031 + + IL_0025: ldstr "0b" + IL_002a: call void [mscorlib]System.Console::WriteLine(string) + IL_002f: br.s IL_0075 + + IL_0031: ldstr "0!b" + IL_0036: call void [mscorlib]System.Console::WriteLine(string) + IL_003b: br.s IL_0061 + + IL_003d: ldarg.0 + IL_003e: brfalse.s IL_004b + + IL_0040: ldstr "2b" + IL_0045: call void [mscorlib]System.Console::WriteLine(string) + IL_004a: ret + + IL_004b: ldstr "2!b" + IL_0050: call void [mscorlib]System.Console::WriteLine(string) + IL_0055: br.s IL_0075 + + IL_0057: ldstr "default" + IL_005c: call void [mscorlib]System.Console::WriteLine(string) + IL_0061: ldstr "break-target" + IL_0066: call void [mscorlib]System.Console::WriteLine(string) + IL_006b: ldstr "loop-tail" + IL_0070: call void [mscorlib]System.Console::WriteLine(string) + IL_0075: ldloc.0 + IL_0076: ldc.i4.1 + IL_0077: add + IL_0078: stloc.0 + IL_0079: ldloc.0 + IL_007a: ldc.i4.s 10 + IL_007c: blt.s IL_0004 + + IL_007e: ret + } // end of method Switch::SwitchWithContinue5 + + .method public hidebysig static void SwitchWithContinue6(int32 i, + bool b) cil managed + { + // Code size 110 (0x6e) + .maxstack 2 + .locals init (int32 V_0) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: switch ( + IL_001a, + IL_0063, + IL_0035, + IL_0059) + IL_0018: br.s IL_004f + + IL_001a: ldarg.1 + IL_001b: brtrue.s IL_0029 + + IL_001d: ldstr "0!b" + IL_0022: call void [mscorlib]System.Console::WriteLine(string) + IL_0027: br.s IL_0059 + + IL_0029: ldstr "0b" + IL_002e: call void [mscorlib]System.Console::WriteLine(string) + IL_0033: br.s IL_0063 + + IL_0035: ldarg.1 + IL_0036: brfalse.s IL_0043 + + IL_0038: ldstr "2b" + IL_003d: call void [mscorlib]System.Console::WriteLine(string) + IL_0042: ret + + IL_0043: ldstr "2!b" + IL_0048: call void [mscorlib]System.Console::WriteLine(string) + IL_004d: br.s IL_0063 + + IL_004f: ldstr "default" + IL_0054: call void [mscorlib]System.Console::WriteLine(string) + IL_0059: ldstr "loop-tail" + IL_005e: call void [mscorlib]System.Console::WriteLine(string) + IL_0063: ldarg.0 + IL_0064: ldc.i4.1 + IL_0065: add + IL_0066: dup + IL_0067: starg.s i + IL_0069: ldc.i4.s 10 + IL_006b: blt.s IL_0000 + + IL_006d: ret + } // end of method Switch::SwitchWithContinue6 + + .method public hidebysig static void SwitchLoopNesting() cil managed + { + // Code size 101 (0x65) + .maxstack 3 + .locals init (int32 V_0, + int32 V_1) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + IL_0002: br.s IL_005f + + IL_0004: ldloc.0 + IL_0005: stloc.1 + IL_0006: ldloc.1 + IL_0007: switch ( + IL_0016, + IL_001e) + IL_0014: br.s IL_0026 + + IL_0016: ldc.i4.0 + IL_0017: call void [mscorlib]System.Console::WriteLine(int32) + IL_001c: br.s IL_0041 + + IL_001e: ldc.i4.1 + IL_001f: call void [mscorlib]System.Console::WriteLine(int32) + IL_0024: br.s IL_0041 + + IL_0026: ldloc.0 + IL_0027: ldc.i4.2 + IL_0028: rem + IL_0029: brtrue.s IL_003c + + IL_002b: br.s IL_0037 + + IL_002d: ldloc.0 + IL_002e: dup + IL_002f: ldc.i4.1 + IL_0030: add + IL_0031: stloc.0 + IL_0032: call void [mscorlib]System.Console::WriteLine(int32) + IL_0037: ldloc.0 + IL_0038: ldc.i4.3 + IL_0039: rem + IL_003a: brtrue.s IL_002d + + IL_003c: call void [mscorlib]System.Console::WriteLine() + IL_0041: ldloc.0 + IL_0042: ldc.i4.4 + IL_0043: ble.s IL_0051 + + IL_0045: ldstr "high" + IL_004a: call void [mscorlib]System.Console::WriteLine(string) + IL_004f: br.s IL_005b + + IL_0051: ldstr "low" + IL_0056: call void [mscorlib]System.Console::WriteLine(string) + IL_005b: ldloc.0 + IL_005c: ldc.i4.1 + IL_005d: add + IL_005e: stloc.0 + IL_005f: ldloc.0 + IL_0060: ldc.i4.s 10 + IL_0062: blt.s IL_0004 + + IL_0064: ret + } // end of method Switch::SwitchLoopNesting + .method public hidebysig static void SingleIf1(int32 i, bool a) cil managed { @@ -2092,7 +2643,8 @@ .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000010-1' .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000011-1' .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000015-1' - .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000017-1' + .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000016-1' + .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000018-1' } // end of class '' diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il index 6d379b5d6..311376262 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il @@ -8,6 +8,11 @@ .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } +.assembly extern System.Core +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} .assembly Switch { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) @@ -1512,6 +1517,171 @@ IL_01ba: ret } // end of method Switch::SwitchWithGotoString + .method public hidebysig static void SwitchWithGotoComplex(string s) cil managed + { + // Code size 387 (0x183) + .maxstack 2 + .locals init (uint32 V_0) + IL_0000: ldstr "SwitchWithGotoComplex: " + IL_0005: ldarg.0 + IL_0006: call string [mscorlib]System.String::Concat(string, + string) + IL_000b: call void [mscorlib]System.Console::WriteLine(string) + IL_0010: ldarg.0 + IL_0011: call uint32 ''::ComputeStringHash(string) + IL_0016: stloc.0 + IL_0017: ldloc.0 + IL_0018: ldc.i4 0x330ca589 + IL_001d: bgt.un.s IL_005d + + IL_001f: ldloc.0 + IL_0020: ldc.i4 0x310ca263 + IL_0025: bgt.un.s IL_0042 + + IL_0027: ldloc.0 + IL_0028: ldc.i4 0x300ca0d0 + IL_002d: beq IL_00d7 + + IL_0032: ldloc.0 + IL_0033: ldc.i4 0x310ca263 + IL_0038: beq IL_00c5 + + IL_003d: br IL_016e + + IL_0042: ldloc.0 + IL_0043: ldc.i4 0x320ca3f6 + IL_0048: beq IL_0107 + + IL_004d: ldloc.0 + IL_004e: ldc.i4 0x330ca589 + IL_0053: beq IL_00e9 + + IL_0058: br IL_016e + + IL_005d: ldloc.0 + IL_005e: ldc.i4 0x360caa42 + IL_0063: bgt.un.s IL_007a + + IL_0065: ldloc.0 + IL_0066: ldc.i4 0x340ca71c + IL_006b: beq.s IL_008f + + IL_006d: ldloc.0 + IL_006e: ldc.i4 0x360caa42 + IL_0073: beq.s IL_00b3 + + IL_0075: br IL_016e + + IL_007a: ldloc.0 + IL_007b: ldc.i4 0x370cabd5 + IL_0080: beq.s IL_00a1 + + IL_0082: ldloc.0 + IL_0083: ldc.i4 0x3d0cb547 + IL_0088: beq.s IL_00f8 + + IL_008a: br IL_016e + + IL_008f: ldarg.0 + IL_0090: ldstr "1" + IL_0095: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_009a: brtrue.s IL_0116 + + IL_009c: br IL_016e + + IL_00a1: ldarg.0 + IL_00a2: ldstr "2" + IL_00a7: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_00ac: brtrue.s IL_0122 + + IL_00ae: br IL_016e + + IL_00b3: ldarg.0 + IL_00b4: ldstr "3" + IL_00b9: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_00be: brtrue.s IL_012c + + IL_00c0: br IL_016e + + IL_00c5: ldarg.0 + IL_00c6: ldstr "4" + IL_00cb: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_00d0: brtrue.s IL_0141 + + IL_00d2: br IL_016e + + IL_00d7: ldarg.0 + IL_00d8: ldstr "5" + IL_00dd: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_00e2: brtrue.s IL_014b + + IL_00e4: br IL_016e + + IL_00e9: ldarg.0 + IL_00ea: ldstr "6" + IL_00ef: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_00f4: brtrue.s IL_0157 + + IL_00f6: br.s IL_016e + + IL_00f8: ldarg.0 + IL_00f9: ldstr "8" + IL_00fe: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0103: brtrue.s IL_0163 + + IL_0105: br.s IL_016e + + IL_0107: ldarg.0 + IL_0108: ldstr "7" + IL_010d: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0112: brtrue.s IL_0178 + + IL_0114: br.s IL_016e + + IL_0116: ldstr "one" + IL_011b: call void [mscorlib]System.Console::WriteLine(string) + IL_0120: br.s IL_0163 + + IL_0122: ldstr "two" + IL_0127: call void [mscorlib]System.Console::WriteLine(string) + IL_012c: ldstr "three" + IL_0131: call void [mscorlib]System.Console::WriteLine(string) + IL_0136: ldarg.0 + IL_0137: callvirt instance int32 [mscorlib]System.String::get_Length() + IL_013c: ldc.i4.2 + IL_013d: beq.s IL_014b + + IL_013f: br.s IL_0178 + + IL_0141: ldstr "four" + IL_0146: call void [mscorlib]System.Console::WriteLine(string) + IL_014b: ldstr "five" + IL_0150: call void [mscorlib]System.Console::WriteLine(string) + IL_0155: br.s IL_0163 + + IL_0157: ldstr "six" + IL_015c: call void [mscorlib]System.Console::WriteLine(string) + IL_0161: br.s IL_014b + + IL_0163: ldstr "eight" + IL_0168: call void [mscorlib]System.Console::WriteLine(string) + IL_016d: ret + + IL_016e: ldstr "default" + IL_0173: call void [mscorlib]System.Console::WriteLine(string) + IL_0178: ldstr "End of method" + IL_017d: call void [mscorlib]System.Console::WriteLine(string) + IL_0182: ret + } // end of method Switch::SwitchWithGotoComplex + .method private hidebysig static class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty[] GetProperties() cil managed { @@ -1758,6 +1928,414 @@ IL_0072: ret } // end of method Switch::SwitchWithArray + .method public hidebysig static void SwitchWithContinue1(int32 i, + bool b) cil managed + { + // Code size 35 (0x23) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: switch ( + IL_0014, + IL_0000, + IL_0019) + IL_0012: br.s IL_001c + + IL_0014: ldarg.1 + IL_0015: brfalse.s IL_001c + + IL_0017: br.s IL_0000 + + IL_0019: ldarg.1 + IL_001a: brfalse.s IL_0000 + + IL_001c: call void [mscorlib]System.Console::WriteLine() + IL_0021: br.s IL_0000 + } // end of method Switch::SwitchWithContinue1 + + .method public hidebysig static void SwitchWithContinue2(int32 i, + bool b) cil managed + { + // Code size 110 (0x6e) + .maxstack 2 + IL_0000: br.s IL_0068 + + IL_0002: ldarg.0 + IL_0003: switch ( + IL_001a, + IL_0068, + IL_0035, + IL_0059) + IL_0018: br.s IL_004f + + IL_001a: ldarg.1 + IL_001b: brfalse.s IL_0029 + + IL_001d: ldstr "0b" + IL_0022: call void [mscorlib]System.Console::WriteLine(string) + IL_0027: br.s IL_0068 + + IL_0029: ldstr "0!b" + IL_002e: call void [mscorlib]System.Console::WriteLine(string) + IL_0033: br.s IL_0059 + + IL_0035: ldarg.1 + IL_0036: brfalse.s IL_0043 + + IL_0038: ldstr "2b" + IL_003d: call void [mscorlib]System.Console::WriteLine(string) + IL_0042: ret + + IL_0043: ldstr "2!b" + IL_0048: call void [mscorlib]System.Console::WriteLine(string) + IL_004d: br.s IL_0068 + + IL_004f: ldstr "default" + IL_0054: call void [mscorlib]System.Console::WriteLine(string) + IL_0059: ldstr "loop-tail" + IL_005e: call void [mscorlib]System.Console::WriteLine(string) + IL_0063: ldarg.0 + IL_0064: ldc.i4.1 + IL_0065: add + IL_0066: starg.s i + IL_0068: ldarg.0 + IL_0069: ldc.i4.s 10 + IL_006b: blt.s IL_0002 + + IL_006d: ret + } // end of method Switch::SwitchWithContinue2 + + .method public hidebysig static void SwitchWithContinue3(bool b) cil managed + { + // Code size 111 (0x6f) + .maxstack 2 + .locals init (int32 V_0) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + IL_0002: br.s IL_0069 + + IL_0004: ldloc.0 + IL_0005: switch ( + IL_001c, + IL_0065, + IL_0037, + IL_005b) + IL_001a: br.s IL_0051 + + IL_001c: ldarg.0 + IL_001d: brfalse.s IL_002b + + IL_001f: ldstr "0b" + IL_0024: call void [mscorlib]System.Console::WriteLine(string) + IL_0029: br.s IL_0065 + + IL_002b: ldstr "0!b" + IL_0030: call void [mscorlib]System.Console::WriteLine(string) + IL_0035: br.s IL_005b + + IL_0037: ldarg.0 + IL_0038: brfalse.s IL_0045 + + IL_003a: ldstr "2b" + IL_003f: call void [mscorlib]System.Console::WriteLine(string) + IL_0044: ret + + IL_0045: ldstr "2!b" + IL_004a: call void [mscorlib]System.Console::WriteLine(string) + IL_004f: br.s IL_0065 + + IL_0051: ldstr "default" + IL_0056: call void [mscorlib]System.Console::WriteLine(string) + IL_005b: ldstr "loop-tail" + IL_0060: call void [mscorlib]System.Console::WriteLine(string) + IL_0065: ldloc.0 + IL_0066: ldc.i4.1 + IL_0067: add + IL_0068: stloc.0 + IL_0069: ldloc.0 + IL_006a: ldc.i4.s 10 + IL_006c: blt.s IL_0004 + + IL_006e: ret + } // end of method Switch::SwitchWithContinue3 + + .method public hidebysig static void SwitchWithContinue4(bool b) cil managed + { + // Code size 188 (0xbc) + .maxstack 2 + .locals init (class [mscorlib]System.Collections.Generic.IEnumerator`1 V_0, + int32 V_1) + IL_0000: ldc.i4.0 + IL_0001: ldc.i4.s 10 + IL_0003: call class [mscorlib]System.Collections.Generic.IEnumerable`1 [System.Core]System.Linq.Enumerable::Range(int32, + int32) + IL_0008: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1 class [mscorlib]System.Collections.Generic.IEnumerable`1::GetEnumerator() + IL_000d: stloc.0 + .try + { + IL_000e: br IL_00a4 + + IL_0013: ldloc.0 + IL_0014: callvirt instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1::get_Current() + IL_0019: stloc.1 + IL_001a: ldstr "loop: " + IL_001f: ldloc.1 + IL_0020: box [mscorlib]System.Int32 + IL_0025: call string [mscorlib]System.String::Concat(object, + object) + IL_002a: call void [mscorlib]System.Console::WriteLine(string) + IL_002f: ldloc.1 + IL_0030: ldc.i4.1 + IL_0031: sub + IL_0032: switch ( + IL_0059, + IL_00a4, + IL_005e, + IL_0063, + IL_006b, + IL_0073, + IL_0078, + IL_0080) + IL_0057: br.s IL_0085 + + IL_0059: ldarg.0 + IL_005a: brfalse.s IL_008f + + IL_005c: br.s IL_00a4 + + IL_005e: ldarg.0 + IL_005f: brfalse.s IL_00a4 + + IL_0061: leave.s IL_00bb + + IL_0063: ldc.i4.4 + IL_0064: call void [mscorlib]System.Console::WriteLine(int32) + IL_0069: br.s IL_0078 + + IL_006b: ldc.i4.5 + IL_006c: call void [mscorlib]System.Console::WriteLine(int32) + IL_0071: br.s IL_0085 + + IL_0073: ldarg.0 + IL_0074: brfalse.s IL_005e + + IL_0076: br.s IL_00a4 + + IL_0078: ldloc.1 + IL_0079: ldc.i4.2 + IL_007a: rem + IL_007b: brfalse.s IL_005e + + IL_007d: ldarg.0 + IL_007e: brfalse.s IL_00a4 + + IL_0080: ldarg.0 + IL_0081: brfalse.s IL_006b + + IL_0083: br.s IL_00a4 + + IL_0085: ldstr "default" + IL_008a: call void [mscorlib]System.Console::WriteLine(string) + IL_008f: ldstr "break: " + IL_0094: ldloc.1 + IL_0095: box [mscorlib]System.Int32 + IL_009a: call string [mscorlib]System.String::Concat(object, + object) + IL_009f: call void [mscorlib]System.Console::WriteLine(string) + IL_00a4: ldloc.0 + IL_00a5: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext() + IL_00aa: brtrue IL_0013 + + IL_00af: leave.s IL_00bb + + } // end .try + finally + { + IL_00b1: ldloc.0 + IL_00b2: brfalse.s IL_00ba + + IL_00b4: ldloc.0 + IL_00b5: callvirt instance void [mscorlib]System.IDisposable::Dispose() + IL_00ba: endfinally + } // end handler + IL_00bb: ret + } // end of method Switch::SwitchWithContinue4 + + .method public hidebysig static void SwitchWithContinue5(bool b) cil managed + { + // Code size 125 (0x7d) + .maxstack 2 + .locals init (int32 V_0) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + IL_0002: br.s IL_0077 + + IL_0004: ldloc.0 + IL_0005: ldc.i4.5 + IL_0006: bge.s IL_0069 + + IL_0008: ldloc.0 + IL_0009: switch ( + IL_0020, + IL_0073, + IL_003b, + IL_005f) + IL_001e: br.s IL_0055 + + IL_0020: ldarg.0 + IL_0021: brfalse.s IL_002f + + IL_0023: ldstr "0b" + IL_0028: call void [mscorlib]System.Console::WriteLine(string) + IL_002d: br.s IL_0073 + + IL_002f: ldstr "0!b" + IL_0034: call void [mscorlib]System.Console::WriteLine(string) + IL_0039: br.s IL_005f + + IL_003b: ldarg.0 + IL_003c: brfalse.s IL_0049 + + IL_003e: ldstr "2b" + IL_0043: call void [mscorlib]System.Console::WriteLine(string) + IL_0048: ret + + IL_0049: ldstr "2!b" + IL_004e: call void [mscorlib]System.Console::WriteLine(string) + IL_0053: br.s IL_0073 + + IL_0055: ldstr "default" + IL_005a: call void [mscorlib]System.Console::WriteLine(string) + IL_005f: ldstr "break-target" + IL_0064: call void [mscorlib]System.Console::WriteLine(string) + IL_0069: ldstr "loop-tail" + IL_006e: call void [mscorlib]System.Console::WriteLine(string) + IL_0073: ldloc.0 + IL_0074: ldc.i4.1 + IL_0075: add + IL_0076: stloc.0 + IL_0077: ldloc.0 + IL_0078: ldc.i4.s 10 + IL_007a: blt.s IL_0004 + + IL_007c: ret + } // end of method Switch::SwitchWithContinue5 + + .method public hidebysig static void SwitchWithContinue6(int32 i, + bool b) cil managed + { + // Code size 108 (0x6c) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: switch ( + IL_0018, + IL_0061, + IL_0033, + IL_0057) + IL_0016: br.s IL_004d + + IL_0018: ldarg.1 + IL_0019: brtrue.s IL_0027 + + IL_001b: ldstr "0!b" + IL_0020: call void [mscorlib]System.Console::WriteLine(string) + IL_0025: br.s IL_0057 + + IL_0027: ldstr "0b" + IL_002c: call void [mscorlib]System.Console::WriteLine(string) + IL_0031: br.s IL_0061 + + IL_0033: ldarg.1 + IL_0034: brfalse.s IL_0041 + + IL_0036: ldstr "2b" + IL_003b: call void [mscorlib]System.Console::WriteLine(string) + IL_0040: ret + + IL_0041: ldstr "2!b" + IL_0046: call void [mscorlib]System.Console::WriteLine(string) + IL_004b: br.s IL_0061 + + IL_004d: ldstr "default" + IL_0052: call void [mscorlib]System.Console::WriteLine(string) + IL_0057: ldstr "loop-tail" + IL_005c: call void [mscorlib]System.Console::WriteLine(string) + IL_0061: ldarg.0 + IL_0062: ldc.i4.1 + IL_0063: add + IL_0064: dup + IL_0065: starg.s i + IL_0067: ldc.i4.s 10 + IL_0069: blt.s IL_0000 + + IL_006b: ret + } // end of method Switch::SwitchWithContinue6 + + .method public hidebysig static void SwitchLoopNesting() cil managed + { + // Code size 92 (0x5c) + .maxstack 3 + .locals init (int32 V_0) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + IL_0002: br.s IL_0056 + + IL_0004: ldloc.0 + IL_0005: brfalse.s IL_000d + + IL_0007: ldloc.0 + IL_0008: ldc.i4.1 + IL_0009: beq.s IL_0015 + + IL_000b: br.s IL_001d + + IL_000d: ldc.i4.0 + IL_000e: call void [mscorlib]System.Console::WriteLine(int32) + IL_0013: br.s IL_0038 + + IL_0015: ldc.i4.1 + IL_0016: call void [mscorlib]System.Console::WriteLine(int32) + IL_001b: br.s IL_0038 + + IL_001d: ldloc.0 + IL_001e: ldc.i4.2 + IL_001f: rem + IL_0020: brtrue.s IL_0033 + + IL_0022: br.s IL_002e + + IL_0024: ldloc.0 + IL_0025: dup + IL_0026: ldc.i4.1 + IL_0027: add + IL_0028: stloc.0 + IL_0029: call void [mscorlib]System.Console::WriteLine(int32) + IL_002e: ldloc.0 + IL_002f: ldc.i4.3 + IL_0030: rem + IL_0031: brtrue.s IL_0024 + + IL_0033: call void [mscorlib]System.Console::WriteLine() + IL_0038: ldloc.0 + IL_0039: ldc.i4.4 + IL_003a: ble.s IL_0048 + + IL_003c: ldstr "high" + IL_0041: call void [mscorlib]System.Console::WriteLine(string) + IL_0046: br.s IL_0052 + + IL_0048: ldstr "low" + IL_004d: call void [mscorlib]System.Console::WriteLine(string) + IL_0052: ldloc.0 + IL_0053: ldc.i4.1 + IL_0054: add + IL_0055: stloc.0 + IL_0056: ldloc.0 + IL_0057: ldc.i4.s 10 + IL_0059: blt.s IL_0004 + + IL_005b: ret + } // end of method Switch::SwitchLoopNesting + .method public hidebysig static void SingleIf1(int32 i, bool a) cil managed { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il index 819f8252b..4757aba98 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il @@ -8,6 +8,11 @@ .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } +.assembly extern System.Core +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} .assembly Switch { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) @@ -1895,6 +1900,210 @@ IL_01e3: ret } // end of method Switch::SwitchWithGotoString + .method public hidebysig static void SwitchWithGotoComplex(string s) cil managed + { + // Code size 436 (0x1b4) + .maxstack 2 + .locals init (string V_0, + uint32 V_1, + bool V_2) + IL_0000: nop + IL_0001: ldstr "SwitchWithGotoComplex: " + IL_0006: ldarg.0 + IL_0007: call string [mscorlib]System.String::Concat(string, + string) + IL_000c: call void [mscorlib]System.Console::WriteLine(string) + IL_0011: nop + IL_0012: ldarg.0 + IL_0013: stloc.0 + IL_0014: ldloc.0 + IL_0015: call uint32 ''::ComputeStringHash(string) + IL_001a: stloc.1 + IL_001b: ldloc.1 + IL_001c: ldc.i4 0x330ca589 + IL_0021: bgt.un.s IL_0065 + + IL_0023: ldloc.1 + IL_0024: ldc.i4 0x310ca263 + IL_0029: bgt.un.s IL_0048 + + IL_002b: ldloc.1 + IL_002c: ldc.i4 0x300ca0d0 + IL_0031: beq IL_00e9 + + IL_0036: br.s IL_0038 + + IL_0038: ldloc.1 + IL_0039: ldc.i4 0x310ca263 + IL_003e: beq IL_00d4 + + IL_0043: br IL_0199 + + IL_0048: ldloc.1 + IL_0049: ldc.i4 0x320ca3f6 + IL_004e: beq IL_011c + + IL_0053: br.s IL_0055 + + IL_0055: ldloc.1 + IL_0056: ldc.i4 0x330ca589 + IL_005b: beq IL_00fb + + IL_0060: br IL_0199 + + IL_0065: ldloc.1 + IL_0066: ldc.i4 0x360caa42 + IL_006b: bgt.un.s IL_0084 + + IL_006d: ldloc.1 + IL_006e: ldc.i4 0x340ca71c + IL_0073: beq.s IL_009b + + IL_0075: br.s IL_0077 + + IL_0077: ldloc.1 + IL_0078: ldc.i4 0x360caa42 + IL_007d: beq.s IL_00c2 + + IL_007f: br IL_0199 + + IL_0084: ldloc.1 + IL_0085: ldc.i4 0x370cabd5 + IL_008a: beq.s IL_00b0 + + IL_008c: br.s IL_008e + + IL_008e: ldloc.1 + IL_008f: ldc.i4 0x3d0cb547 + IL_0094: beq.s IL_010d + + IL_0096: br IL_0199 + + IL_009b: ldloc.0 + IL_009c: ldstr "1" + IL_00a1: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_00a6: brtrue IL_012b + + IL_00ab: br IL_0199 + + IL_00b0: ldloc.0 + IL_00b1: ldstr "2" + IL_00b6: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_00bb: brtrue.s IL_0138 + + IL_00bd: br IL_0199 + + IL_00c2: ldloc.0 + IL_00c3: ldstr "3" + IL_00c8: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_00cd: brtrue.s IL_0145 + + IL_00cf: br IL_0199 + + IL_00d4: ldloc.0 + IL_00d5: ldstr "4" + IL_00da: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_00df: brtrue IL_0165 + + IL_00e4: br IL_0199 + + IL_00e9: ldloc.0 + IL_00ea: ldstr "5" + IL_00ef: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_00f4: brtrue.s IL_0172 + + IL_00f6: br IL_0199 + + IL_00fb: ldloc.0 + IL_00fc: ldstr "6" + IL_0101: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0106: brtrue.s IL_017f + + IL_0108: br IL_0199 + + IL_010d: ldloc.0 + IL_010e: ldstr "8" + IL_0113: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0118: brtrue.s IL_018c + + IL_011a: br.s IL_0199 + + IL_011c: ldloc.0 + IL_011d: ldstr "7" + IL_0122: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0127: brtrue.s IL_01a6 + + IL_0129: br.s IL_0199 + + IL_012b: ldstr "one" + IL_0130: call void [mscorlib]System.Console::WriteLine(string) + IL_0135: nop + IL_0136: br.s IL_018c + + IL_0138: ldstr "two" + IL_013d: call void [mscorlib]System.Console::WriteLine(string) + IL_0142: nop + IL_0143: br.s IL_0145 + + IL_0145: ldstr "three" + IL_014a: call void [mscorlib]System.Console::WriteLine(string) + IL_014f: nop + IL_0150: ldarg.0 + IL_0151: callvirt instance int32 [mscorlib]System.String::get_Length() + IL_0156: ldc.i4.2 + IL_0157: ceq + IL_0159: ldc.i4.0 + IL_015a: ceq + IL_015c: stloc.2 + IL_015d: ldloc.2 + IL_015e: brfalse.s IL_0163 + + IL_0160: nop + IL_0161: br.s IL_01a8 + + IL_0163: br.s IL_0172 + + IL_0165: ldstr "four" + IL_016a: call void [mscorlib]System.Console::WriteLine(string) + IL_016f: nop + IL_0170: br.s IL_0172 + + IL_0172: ldstr "five" + IL_0177: call void [mscorlib]System.Console::WriteLine(string) + IL_017c: nop + IL_017d: br.s IL_018c + + IL_017f: ldstr "six" + IL_0184: call void [mscorlib]System.Console::WriteLine(string) + IL_0189: nop + IL_018a: br.s IL_0172 + + IL_018c: ldstr "eight" + IL_0191: call void [mscorlib]System.Console::WriteLine(string) + IL_0196: nop + IL_0197: br.s IL_01b3 + + IL_0199: ldstr "default" + IL_019e: call void [mscorlib]System.Console::WriteLine(string) + IL_01a3: nop + IL_01a4: br.s IL_01a8 + + IL_01a6: br.s IL_01a8 + + IL_01a8: ldstr "End of method" + IL_01ad: call void [mscorlib]System.Console::WriteLine(string) + IL_01b2: nop + IL_01b3: ret + } // end of method Switch::SwitchWithGotoComplex + .method private hidebysig static class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty[] GetProperties() cil managed { @@ -2183,6 +2392,682 @@ IL_007a: ret } // end of method Switch::SwitchWithArray + .method public hidebysig static void SwitchWithContinue1(int32 i, + bool b) cil managed + { + // Code size 62 (0x3e) + .maxstack 2 + .locals init (int32 V_0, + bool V_1, + bool V_2, + bool V_3) + IL_0000: nop + IL_0001: br.s IL_003a + + IL_0003: nop + IL_0004: ldarg.0 + IL_0005: stloc.0 + IL_0006: ldloc.0 + IL_0007: switch ( + IL_001a, + IL_0031, + IL_0024) + IL_0018: br.s IL_0033 + + IL_001a: ldarg.1 + IL_001b: stloc.1 + IL_001c: ldloc.1 + IL_001d: brfalse.s IL_0022 + + IL_001f: nop + IL_0020: br.s IL_003a + + IL_0022: br.s IL_0033 + + IL_0024: ldarg.1 + IL_0025: ldc.i4.0 + IL_0026: ceq + IL_0028: stloc.2 + IL_0029: ldloc.2 + IL_002a: brfalse.s IL_002f + + IL_002c: nop + IL_002d: br.s IL_003a + + IL_002f: br.s IL_0033 + + IL_0031: br.s IL_003a + + IL_0033: call void [mscorlib]System.Console::WriteLine() + IL_0038: nop + IL_0039: nop + IL_003a: ldc.i4.1 + IL_003b: stloc.3 + IL_003c: br.s IL_0003 + } // end of method Switch::SwitchWithContinue1 + + .method public hidebysig static void SwitchWithContinue2(int32 i, + bool b) cil managed + { + // Code size 147 (0x93) + .maxstack 2 + .locals init (int32 V_0, + bool V_1, + bool V_2, + bool V_3) + IL_0000: nop + IL_0001: br IL_0086 + + IL_0006: nop + IL_0007: ldarg.0 + IL_0008: stloc.0 + IL_0009: ldloc.0 + IL_000a: switch ( + IL_0021, + IL_0073, + IL_0041, + IL_0071) + IL_001f: br.s IL_0064 + + IL_0021: ldarg.1 + IL_0022: stloc.1 + IL_0023: ldloc.1 + IL_0024: brfalse.s IL_0034 + + IL_0026: nop + IL_0027: ldstr "0b" + IL_002c: call void [mscorlib]System.Console::WriteLine(string) + IL_0031: nop + IL_0032: br.s IL_0086 + + IL_0034: ldstr "0!b" + IL_0039: call void [mscorlib]System.Console::WriteLine(string) + IL_003e: nop + IL_003f: br.s IL_0075 + + IL_0041: ldarg.1 + IL_0042: ldc.i4.0 + IL_0043: ceq + IL_0045: stloc.2 + IL_0046: ldloc.2 + IL_0047: brfalse.s IL_0057 + + IL_0049: nop + IL_004a: ldstr "2!b" + IL_004f: call void [mscorlib]System.Console::WriteLine(string) + IL_0054: nop + IL_0055: br.s IL_0086 + + IL_0057: ldstr "2b" + IL_005c: call void [mscorlib]System.Console::WriteLine(string) + IL_0061: nop + IL_0062: br.s IL_0092 + + IL_0064: ldstr "default" + IL_0069: call void [mscorlib]System.Console::WriteLine(string) + IL_006e: nop + IL_006f: br.s IL_0075 + + IL_0071: br.s IL_0075 + + IL_0073: br.s IL_0086 + + IL_0075: ldstr "loop-tail" + IL_007a: call void [mscorlib]System.Console::WriteLine(string) + IL_007f: nop + IL_0080: ldarg.0 + IL_0081: ldc.i4.1 + IL_0082: add + IL_0083: starg.s i + IL_0085: nop + IL_0086: ldarg.0 + IL_0087: ldc.i4.s 10 + IL_0089: clt + IL_008b: stloc.3 + IL_008c: ldloc.3 + IL_008d: brtrue IL_0006 + + IL_0092: ret + } // end of method Switch::SwitchWithContinue2 + + .method public hidebysig static void SwitchWithContinue3(bool b) cil managed + { + // Code size 147 (0x93) + .maxstack 2 + .locals init (int32 V_0, + int32 V_1, + bool V_2, + bool V_3, + bool V_4) + IL_0000: nop + IL_0001: ldc.i4.0 + IL_0002: stloc.0 + IL_0003: br.s IL_0084 + + IL_0005: nop + IL_0006: ldloc.0 + IL_0007: stloc.1 + IL_0008: ldloc.1 + IL_0009: switch ( + IL_0020, + IL_0072, + IL_0040, + IL_0070) + IL_001e: br.s IL_0063 + + IL_0020: ldarg.0 + IL_0021: stloc.2 + IL_0022: ldloc.2 + IL_0023: brfalse.s IL_0033 + + IL_0025: nop + IL_0026: ldstr "0b" + IL_002b: call void [mscorlib]System.Console::WriteLine(string) + IL_0030: nop + IL_0031: br.s IL_0080 + + IL_0033: ldstr "0!b" + IL_0038: call void [mscorlib]System.Console::WriteLine(string) + IL_003d: nop + IL_003e: br.s IL_0074 + + IL_0040: ldarg.0 + IL_0041: ldc.i4.0 + IL_0042: ceq + IL_0044: stloc.3 + IL_0045: ldloc.3 + IL_0046: brfalse.s IL_0056 + + IL_0048: nop + IL_0049: ldstr "2!b" + IL_004e: call void [mscorlib]System.Console::WriteLine(string) + IL_0053: nop + IL_0054: br.s IL_0080 + + IL_0056: ldstr "2b" + IL_005b: call void [mscorlib]System.Console::WriteLine(string) + IL_0060: nop + IL_0061: br.s IL_0092 + + IL_0063: ldstr "default" + IL_0068: call void [mscorlib]System.Console::WriteLine(string) + IL_006d: nop + IL_006e: br.s IL_0074 + + IL_0070: br.s IL_0074 + + IL_0072: br.s IL_0080 + + IL_0074: ldstr "loop-tail" + IL_0079: call void [mscorlib]System.Console::WriteLine(string) + IL_007e: nop + IL_007f: nop + IL_0080: ldloc.0 + IL_0081: ldc.i4.1 + IL_0082: add + IL_0083: stloc.0 + IL_0084: ldloc.0 + IL_0085: ldc.i4.s 10 + IL_0087: clt + IL_0089: stloc.s V_4 + IL_008b: ldloc.s V_4 + IL_008d: brtrue IL_0005 + + IL_0092: ret + } // end of method Switch::SwitchWithContinue3 + + .method public hidebysig static void SwitchWithContinue4(bool b) cil managed + { + // Code size 261 (0x105) + .maxstack 2 + .locals init (class [mscorlib]System.Collections.Generic.IEnumerator`1 V_0, + int32 V_1, + int32 V_2, + bool V_3, + bool V_4, + bool V_5, + bool V_6, + bool V_7, + bool V_8) + IL_0000: nop + IL_0001: nop + IL_0002: ldc.i4.0 + IL_0003: ldc.i4.s 10 + IL_0005: call class [mscorlib]System.Collections.Generic.IEnumerable`1 [System.Core]System.Linq.Enumerable::Range(int32, + int32) + IL_000a: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1 class [mscorlib]System.Collections.Generic.IEnumerable`1::GetEnumerator() + IL_000f: stloc.0 + .try + { + IL_0010: br IL_00ec + + IL_0015: ldloc.0 + IL_0016: callvirt instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1::get_Current() + IL_001b: stloc.1 + IL_001c: nop + IL_001d: ldstr "loop: " + IL_0022: ldloc.1 + IL_0023: box [mscorlib]System.Int32 + IL_0028: call string [mscorlib]System.String::Concat(object, + object) + IL_002d: call void [mscorlib]System.Console::WriteLine(string) + IL_0032: nop + IL_0033: ldloc.1 + IL_0034: stloc.2 + IL_0035: ldloc.2 + IL_0036: ldc.i4.1 + IL_0037: sub + IL_0038: switch ( + IL_005f, + IL_00d3, + IL_006c, + IL_007e, + IL_0087, + IL_0090, + IL_009c, + IL_00ba) + IL_005d: br.s IL_00c6 + + IL_005f: ldarg.0 + IL_0060: stloc.3 + IL_0061: ldloc.3 + IL_0062: brfalse.s IL_006a + + IL_0064: nop + IL_0065: br IL_00ec + + IL_006a: br.s IL_00d5 + + IL_006c: ldarg.0 + IL_006d: ldc.i4.0 + IL_006e: ceq + IL_0070: stloc.s V_4 + IL_0072: ldloc.s V_4 + IL_0074: brfalse.s IL_0079 + + IL_0076: nop + IL_0077: br.s IL_00ec + + IL_0079: leave IL_0104 + + IL_007e: ldc.i4.4 + IL_007f: call void [mscorlib]System.Console::WriteLine(int32) + IL_0084: nop + IL_0085: br.s IL_009c + + IL_0087: ldc.i4.5 + IL_0088: call void [mscorlib]System.Console::WriteLine(int32) + IL_008d: nop + IL_008e: br.s IL_00c6 + + IL_0090: ldarg.0 + IL_0091: stloc.s V_5 + IL_0093: ldloc.s V_5 + IL_0095: brfalse.s IL_009a + + IL_0097: nop + IL_0098: br.s IL_00ec + + IL_009a: br.s IL_006c + + IL_009c: ldloc.1 + IL_009d: ldc.i4.2 + IL_009e: rem + IL_009f: ldc.i4.0 + IL_00a0: ceq + IL_00a2: stloc.s V_6 + IL_00a4: ldloc.s V_6 + IL_00a6: brfalse.s IL_00ab + + IL_00a8: nop + IL_00a9: br.s IL_006c + + IL_00ab: ldarg.0 + IL_00ac: ldc.i4.0 + IL_00ad: ceq + IL_00af: stloc.s V_7 + IL_00b1: ldloc.s V_7 + IL_00b3: brfalse.s IL_00b8 + + IL_00b5: nop + IL_00b6: br.s IL_00ec + + IL_00b8: br.s IL_00ba + + IL_00ba: ldarg.0 + IL_00bb: stloc.s V_8 + IL_00bd: ldloc.s V_8 + IL_00bf: brfalse.s IL_00c4 + + IL_00c1: nop + IL_00c2: br.s IL_00ec + + IL_00c4: br.s IL_0087 + + IL_00c6: ldstr "default" + IL_00cb: call void [mscorlib]System.Console::WriteLine(string) + IL_00d0: nop + IL_00d1: br.s IL_00d5 + + IL_00d3: br.s IL_00ec + + IL_00d5: ldstr "break: " + IL_00da: ldloc.1 + IL_00db: box [mscorlib]System.Int32 + IL_00e0: call string [mscorlib]System.String::Concat(object, + object) + IL_00e5: call void [mscorlib]System.Console::WriteLine(string) + IL_00ea: nop + IL_00eb: nop + IL_00ec: ldloc.0 + IL_00ed: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext() + IL_00f2: brtrue IL_0015 + + IL_00f7: leave.s IL_0104 + + } // end .try + finally + { + IL_00f9: ldloc.0 + IL_00fa: brfalse.s IL_0103 + + IL_00fc: ldloc.0 + IL_00fd: callvirt instance void [mscorlib]System.IDisposable::Dispose() + IL_0102: nop + IL_0103: endfinally + } // end handler + IL_0104: ret + } // end of method Switch::SwitchWithContinue4 + + .method public hidebysig static void SwitchWithContinue5(bool b) cil managed + { + // Code size 173 (0xad) + .maxstack 2 + .locals init (int32 V_0, + bool V_1, + int32 V_2, + bool V_3, + bool V_4, + bool V_5) + IL_0000: nop + IL_0001: ldc.i4.0 + IL_0002: stloc.0 + IL_0003: br IL_009e + + IL_0008: nop + IL_0009: ldloc.0 + IL_000a: ldc.i4.5 + IL_000b: clt + IL_000d: stloc.1 + IL_000e: ldloc.1 + IL_000f: brfalse.s IL_008e + + IL_0011: nop + IL_0012: ldloc.0 + IL_0013: stloc.2 + IL_0014: ldloc.2 + IL_0015: switch ( + IL_002c, + IL_0080, + IL_004c, + IL_007e) + IL_002a: br.s IL_0071 + + IL_002c: ldarg.0 + IL_002d: stloc.3 + IL_002e: ldloc.3 + IL_002f: brfalse.s IL_003f + + IL_0031: nop + IL_0032: ldstr "0b" + IL_0037: call void [mscorlib]System.Console::WriteLine(string) + IL_003c: nop + IL_003d: br.s IL_009a + + IL_003f: ldstr "0!b" + IL_0044: call void [mscorlib]System.Console::WriteLine(string) + IL_0049: nop + IL_004a: br.s IL_0082 + + IL_004c: ldarg.0 + IL_004d: ldc.i4.0 + IL_004e: ceq + IL_0050: stloc.s V_4 + IL_0052: ldloc.s V_4 + IL_0054: brfalse.s IL_0064 + + IL_0056: nop + IL_0057: ldstr "2!b" + IL_005c: call void [mscorlib]System.Console::WriteLine(string) + IL_0061: nop + IL_0062: br.s IL_009a + + IL_0064: ldstr "2b" + IL_0069: call void [mscorlib]System.Console::WriteLine(string) + IL_006e: nop + IL_006f: br.s IL_00ac + + IL_0071: ldstr "default" + IL_0076: call void [mscorlib]System.Console::WriteLine(string) + IL_007b: nop + IL_007c: br.s IL_0082 + + IL_007e: br.s IL_0082 + + IL_0080: br.s IL_009a + + IL_0082: ldstr "break-target" + IL_0087: call void [mscorlib]System.Console::WriteLine(string) + IL_008c: nop + IL_008d: nop + IL_008e: ldstr "loop-tail" + IL_0093: call void [mscorlib]System.Console::WriteLine(string) + IL_0098: nop + IL_0099: nop + IL_009a: ldloc.0 + IL_009b: ldc.i4.1 + IL_009c: add + IL_009d: stloc.0 + IL_009e: ldloc.0 + IL_009f: ldc.i4.s 10 + IL_00a1: clt + IL_00a3: stloc.s V_5 + IL_00a5: ldloc.s V_5 + IL_00a7: brtrue IL_0008 + + IL_00ac: ret + } // end of method Switch::SwitchWithContinue5 + + .method public hidebysig static void SwitchWithContinue6(int32 i, + bool b) cil managed + { + // Code size 142 (0x8e) + .maxstack 2 + .locals init (int32 V_0, + bool V_1, + bool V_2, + bool V_3) + IL_0000: nop + IL_0001: nop + IL_0002: ldarg.0 + IL_0003: stloc.0 + IL_0004: ldloc.0 + IL_0005: switch ( + IL_001c, + IL_006e, + IL_003f, + IL_006c) + IL_001a: br.s IL_005f + + IL_001c: ldarg.1 + IL_001d: ldc.i4.0 + IL_001e: ceq + IL_0020: stloc.1 + IL_0021: ldloc.1 + IL_0022: brfalse.s IL_0032 + + IL_0024: nop + IL_0025: ldstr "0!b" + IL_002a: call void [mscorlib]System.Console::WriteLine(string) + IL_002f: nop + IL_0030: br.s IL_0070 + + IL_0032: ldstr "0b" + IL_0037: call void [mscorlib]System.Console::WriteLine(string) + IL_003c: nop + IL_003d: br.s IL_007c + + IL_003f: ldarg.1 + IL_0040: stloc.2 + IL_0041: ldloc.2 + IL_0042: brfalse.s IL_0052 + + IL_0044: nop + IL_0045: ldstr "2b" + IL_004a: call void [mscorlib]System.Console::WriteLine(string) + IL_004f: nop + IL_0050: br.s IL_008d + + IL_0052: ldstr "2!b" + IL_0057: call void [mscorlib]System.Console::WriteLine(string) + IL_005c: nop + IL_005d: br.s IL_007c + + IL_005f: ldstr "default" + IL_0064: call void [mscorlib]System.Console::WriteLine(string) + IL_0069: nop + IL_006a: br.s IL_0070 + + IL_006c: br.s IL_0070 + + IL_006e: br.s IL_007c + + IL_0070: ldstr "loop-tail" + IL_0075: call void [mscorlib]System.Console::WriteLine(string) + IL_007a: nop + IL_007b: nop + IL_007c: ldarg.0 + IL_007d: ldc.i4.1 + IL_007e: add + IL_007f: dup + IL_0080: starg.s i + IL_0082: ldc.i4.s 10 + IL_0084: clt + IL_0086: stloc.3 + IL_0087: ldloc.3 + IL_0088: brtrue IL_0001 + + IL_008d: ret + } // end of method Switch::SwitchWithContinue6 + + .method public hidebysig static void SwitchLoopNesting() cil managed + { + // Code size 140 (0x8c) + .maxstack 3 + .locals init (int32 V_0, + int32 V_1, + bool V_2, + bool V_3, + bool V_4, + bool V_5) + IL_0000: nop + IL_0001: ldc.i4.0 + IL_0002: stloc.0 + IL_0003: br.s IL_007d + + IL_0005: nop + IL_0006: ldloc.0 + IL_0007: stloc.1 + IL_0008: ldloc.1 + IL_0009: brfalse.s IL_0013 + + IL_000b: br.s IL_000d + + IL_000d: ldloc.1 + IL_000e: ldc.i4.1 + IL_000f: beq.s IL_001c + + IL_0011: br.s IL_0025 + + IL_0013: ldc.i4.0 + IL_0014: call void [mscorlib]System.Console::WriteLine(int32) + IL_0019: nop + IL_001a: br.s IL_0052 + + IL_001c: ldc.i4.1 + IL_001d: call void [mscorlib]System.Console::WriteLine(int32) + IL_0022: nop + IL_0023: br.s IL_0052 + + IL_0025: ldloc.0 + IL_0026: ldc.i4.2 + IL_0027: rem + IL_0028: ldc.i4.0 + IL_0029: ceq + IL_002b: stloc.2 + IL_002c: ldloc.2 + IL_002d: brfalse.s IL_004a + + IL_002f: nop + IL_0030: br.s IL_003f + + IL_0032: nop + IL_0033: ldloc.0 + IL_0034: dup + IL_0035: ldc.i4.1 + IL_0036: add + IL_0037: stloc.0 + IL_0038: call void [mscorlib]System.Console::WriteLine(int32) + IL_003d: nop + IL_003e: nop + IL_003f: ldloc.0 + IL_0040: ldc.i4.3 + IL_0041: rem + IL_0042: ldc.i4.0 + IL_0043: cgt.un + IL_0045: stloc.3 + IL_0046: ldloc.3 + IL_0047: brtrue.s IL_0032 + + IL_0049: nop + IL_004a: call void [mscorlib]System.Console::WriteLine() + IL_004f: nop + IL_0050: br.s IL_0052 + + IL_0052: ldloc.0 + IL_0053: ldc.i4.4 + IL_0054: cgt + IL_0056: stloc.s V_4 + IL_0058: ldloc.s V_4 + IL_005a: brfalse.s IL_006b + + IL_005c: nop + IL_005d: ldstr "high" + IL_0062: call void [mscorlib]System.Console::WriteLine(string) + IL_0067: nop + IL_0068: nop + IL_0069: br.s IL_0078 + + IL_006b: nop + IL_006c: ldstr "low" + IL_0071: call void [mscorlib]System.Console::WriteLine(string) + IL_0076: nop + IL_0077: nop + IL_0078: nop + IL_0079: ldloc.0 + IL_007a: ldc.i4.1 + IL_007b: add + IL_007c: stloc.0 + IL_007d: ldloc.0 + IL_007e: ldc.i4.s 10 + IL_0080: clt + IL_0082: stloc.s V_5 + IL_0084: ldloc.s V_5 + IL_0086: brtrue IL_0005 + + IL_008b: ret + } // end of method Switch::SwitchLoopNesting + .method public hidebysig static void SingleIf1(int32 i, bool a) cil managed { diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs index ed8f0da97..56e98fcf0 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs @@ -47,7 +47,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow private BlockTransformContext context; private ControlFlowNode cfgNode; private BlockContainer currentContainer; - private Block continueBlock; /// /// Builds structured control flow for the block associated with the control flow node. @@ -61,9 +60,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow this.context = context; currentContainer = (BlockContainer)block.Parent; - // for detection of continue statements - continueBlock = GuessContinueBlock(); - // We only embed blocks into this block if they aren't referenced anywhere else, // so those blocks are dominated by this block. // BlockILTransform thus guarantees that the blocks being embedded are already @@ -488,7 +484,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow // breaks have highest priority in a switch if ((keyword1 == Keyword.Break) != (keyword2 == Keyword.Break)) return keyword1 == Keyword.Break ? 1 : -1; - } else { // breaks have lowest priority if ((keyword1 == Keyword.Break) != (keyword2 == Keyword.Break)) @@ -537,7 +532,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow keyword = Keyword.Other; switch (exitInst) { case Branch branch: - if (branch.TargetBlock == continueBlock) { + if (IsContinueBlock(branch.TargetContainer, branch.TargetBlock)) { keyword = Keyword.Continue; return true; } @@ -599,19 +594,19 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow /// Used to identify branches targetting this block as continue statements, for ordering priority. /// /// - private Block GuessContinueBlock() + private static bool IsContinueBlock(BlockContainer container, Block block) { - if (currentContainer.Kind != ContainerKind.Loop) - return null; + if (container.Kind != ContainerKind.Loop) + return false; - // continue blocks have exactly 2 incoming edges - if (currentContainer.EntryPoint.IncomingEdgeCount == 2) { - var forIncrement = HighLevelLoopTransform.GetIncrementBlock(currentContainer, currentContainer.EntryPoint); + // increment blocks have exactly 2 incoming edges + if (container.EntryPoint.IncomingEdgeCount == 2) { + var forIncrement = HighLevelLoopTransform.GetIncrementBlock(container, container.EntryPoint); if (forIncrement != null) - return forIncrement; + return block == forIncrement; } - return currentContainer.EntryPoint; + return block == container.EntryPoint; } /// diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs index 45c04f25d..f80610834 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs @@ -243,8 +243,8 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow // outside the loop, or we are in case 2 and found an exit point via post-dominance. // Note that if exitPoint == NoExitPoint, we end up adding all dominated blocks to the loop. var ep = exitPoint; - foreach (var node in TreeTraversal.PreOrder(loopHead, n => (n != ep) ? n.DominatorTreeChildren : null)) { - if (node != exitPoint && !node.Visited) { + foreach (var node in TreeTraversal.PreOrder(loopHead, n => DominatorTreeChildren(n, ep, isSwitch))) { + if (!node.Visited) { node.Visited = true; loop.Add(node); } @@ -272,14 +272,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow /// 3) otherwise (exit point unknown, heuristically extend loop): null /// /// This method must not write to the Visited flags on the CFG. - ControlFlowNode FindExitPoint(ControlFlowNode loopHead, IReadOnlyList naturalLoop, bool treatBackEdgesAsExits) + internal ControlFlowNode FindExitPoint(ControlFlowNode loopHead, IReadOnlyList naturalLoop, bool isSwitch) { - bool hasReachableExit = context.ControlFlowGraph.HasReachableExit(loopHead); - if (!hasReachableExit && treatBackEdgesAsExits) { - // If we're analyzing the switch, there's no reachable exit, but the loopHead (=switchHead) block - // is also a loop head, we consider the back-edge a reachable exit for the switch. - hasReachableExit = loopHead.Predecessors.Any(p => loopHead.Dominates(p)); - } + bool hasReachableExit = HasReachableExit(loopHead, isSwitch); if (!hasReachableExit) { // Case 1: // There are no nodes n so that loopHead dominates a predecessor of n but not n itself @@ -297,7 +292,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow ControlFlowNode exitPoint = null; int exitPointILOffset = -1; foreach (var node in loopHead.DominatorTreeChildren) { - PickExitPoint(node, ref exitPoint, ref exitPointILOffset); + PickExitPoint(node, isSwitch, ref exitPoint, ref exitPointILOffset); } return exitPoint; } else { @@ -305,7 +300,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow // We need to pick our exit point so that all paths from the loop head // to the reachable exits run through that exit point. var cfg = context.ControlFlowGraph.cfg; - var revCfg = PrepareReverseCFG(loopHead, treatBackEdgesAsExits, out int exitNodeArity); + var revCfg = PrepareReverseCFG(loopHead, isSwitch, out int exitNodeArity); //ControlFlowNode.ExportGraph(cfg).Show("cfg"); //ControlFlowNode.ExportGraph(revCfg).Show("rev"); ControlFlowNode commonAncestor = revCfg[loopHead.UserIndex]; @@ -338,13 +333,15 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow // * The loop has a single exit point that wasn't considered during post-dominance analysis. // (which means the single exit isn't dominated by the loop head) // -> we should return NoExitPoint so that all code dominated by the loop head is included into the loop - if (exitNodeArity > 1) { + if (exitNodeArity > 1) return null; - } else { - // If exitNodeArity == 0, we should maybe look test if our exits out of the block container are all compatible? - // but I don't think it hurts to have a bit too much code inside the loop in this rare case. - return NoExitPoint; - } + + if (exitNodeArity == 1 && isSwitch) + return SwitchDetection.GetBreakTargets(loopHead, loopHead).Distinct().Single(); + + // If exitNodeArity == 0, we should maybe look test if our exits out of the block container are all compatible? + // but I don't think it hurts to have a bit too much code inside the loop in this rare case. + return NoExitPoint; } } @@ -385,6 +382,13 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow } } + bool HasReachableExit(ControlFlowNode node, bool isSwitch) => isSwitch + ? SwitchDetection.GetBreakTargets(context.ControlFlowNode, node).Any() + : context.ControlFlowGraph.HasReachableExit(node); + + IEnumerable DominatorTreeChildren(ControlFlowNode n, ControlFlowNode exitPoint, bool isSwitch) => + n.DominatorTreeChildren.Where(c => c != exitPoint && (!isSwitch || !SwitchDetection.IsContinue(context.ControlFlowNode, c))); + /// /// Pick exit point by picking any node that has no reachable exits. /// @@ -395,11 +399,14 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow /// /// Code amount in and its dominated nodes. /// This method must not write to the Visited flags on the CFG. - void PickExitPoint(ControlFlowNode node, ref ControlFlowNode exitPoint, ref int exitPointILOffset) + void PickExitPoint(ControlFlowNode node, bool isSwitch, ref ControlFlowNode exitPoint, ref int exitPointILOffset) { + if (isSwitch && SwitchDetection.IsContinue(context.ControlFlowNode, node)) + return; + Block block = (Block)node.UserData; if (block.ILRange.Start > exitPointILOffset - && !context.ControlFlowGraph.HasReachableExit(node) + && !HasReachableExit(node, isSwitch) && ((Block)node.UserData).Parent == currentBlockContainer) { // HasReachableExit(node) == false @@ -418,7 +425,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow // moving almost all of the code into the loop. } foreach (var child in node.DominatorTreeChildren) { - PickExitPoint(child, ref exitPoint, ref exitPointILOffset); + PickExitPoint(child, isSwitch, ref exitPoint, ref exitPointILOffset); } } @@ -431,7 +438,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow /// or that leave the block Container. /// /// Entry point of the loop. - /// Whether to treat loop back edges as exit points. + /// Whether to ignore branches that map to C# 'continue' statements. /// out: The number of different CFG nodes. /// Possible values: /// 0 = no CFG nodes used as exit nodes (although edges leaving the block container might still be exits); @@ -439,7 +446,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow /// 2 = more than one CFG node (not dominated by loopHead) was used as an exit node. /// /// - ControlFlowNode[] PrepareReverseCFG(ControlFlowNode loopHead, bool treatBackEdgesAsExits, out int exitNodeArity) + ControlFlowNode[] PrepareReverseCFG(ControlFlowNode loopHead, bool isSwitch, out int exitNodeArity) { ControlFlowNode[] cfg = context.ControlFlowGraph.cfg; ControlFlowNode[] rev = new ControlFlowNode[cfg.Length + 1]; @@ -451,11 +458,15 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow ControlFlowNode exitNode = new ControlFlowNode { UserIndex = -1 }; rev[cfg.Length] = exitNode; for (int i = 0; i < cfg.Length; i++) { - if (!loopHead.Dominates(cfg[i])) + if (!loopHead.Dominates(cfg[i]) || isSwitch && SwitchDetection.IsContinue(loopHead, cfg[i])) continue; + // Add reverse edges for all edges in cfg foreach (var succ in cfg[i].Successors) { - if (loopHead.Dominates(succ) && (!treatBackEdgesAsExits || loopHead != succ)) { + if (isSwitch && SwitchDetection.IsContinue(loopHead, succ)) + continue; + + if (loopHead.Dominates(succ)) { rev[succ.UserIndex].AddEdgeTo(rev[i]); } else { if (nodeTreatedAsExitNode == null) @@ -662,7 +673,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow nodesInSwitch.Add(h); h.Visited = true; ExtendLoop(h, nodesInSwitch, out var exitPoint, isSwitch: true); - if (exitPoint != null && exitPoint.Predecessors.Count == 1 && !context.ControlFlowGraph.HasReachableExit(exitPoint)) { + if (exitPoint != null && exitPoint.Predecessors.Count == 1 && !HasReachableExit(exitPoint, true)) { // If the exit point is reachable from just one single "break;", // it's better to move the code into the switch. // (unlike loops which should not be nested unless necessary, diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs index 42682e831..1a5a17551 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs @@ -171,7 +171,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow private bool UseCSharpSwitch(out KeyValuePair defaultSection) { if (!analysis.InnerBlocks.Any()) { - defaultSection = default(KeyValuePair); + defaultSection = default; return false; } defaultSection = analysis.Sections.FirstOrDefault(s => s.Key.Count() > MaxValuesPerSection); @@ -214,7 +214,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow // The switch has a single break target and there is one more hint // The break target cannot be inlined, and should have the highest IL offset of everything targetted by the switch - return breakBlock.ChildIndex >= analysis.Sections.Select(s => s.Value.MatchBranch(out var b) ? b.ChildIndex : -1).Max(); + return breakBlock.ILRange.Start >= analysis.Sections.Select(s => s.Value.MatchBranch(out var b) ? b.ILRange.Start : -1).Max(); } /// @@ -238,6 +238,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow if (controlFlowGraph == null) controlFlowGraph = new ControlFlowGraph(currentContainer, context.CancellationToken); + var switchHead = controlFlowGraph.GetNode(analysis.RootBlock); // grab the control flow nodes for blocks targetted by each section var caseNodes = new List(); foreach (var s in analysis.Sections) { @@ -245,7 +246,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow continue; var node = controlFlowGraph.GetNode(block); - if (!IsContinue(node)) + if (!IsContinue(switchHead, node)) caseNodes.Add(node); } @@ -262,7 +263,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow return true; // cannot have more than one break case without gotos // check that case nodes flow through a single point - var breakTargets = caseNodes.Except(externalCases).SelectMany(GetBreakTargets).ToHashSet(); + var breakTargets = caseNodes.Except(externalCases).SelectMany(n => GetBreakTargets(switchHead, n)).ToHashSet(); // if there are multiple break targets, then gotos are required // if there are none, then the external case (if any) can be the break target @@ -307,7 +308,48 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow flowBlocks.Add(nullableBlock); } - internal static bool IsContinue(ControlFlowNode node) => false; + internal static bool IsContinue(ControlFlowNode innerLoopHead, ControlFlowNode node) => + IsContinue(node, out var outerLoopHead) && outerLoopHead.Dominates(innerLoopHead); + + private static bool IsContinue(ControlFlowNode node, out ControlFlowNode loopHead) + { + bool IsLoopHead(ControlFlowNode n) => n.Predecessors.Any(n.Dominates); + ControlFlowNode OnlyInloopPred(ControlFlowNode n) => n.Predecessors.OnlyOrDefault(p => p != n && n.Dominates(p)); + + loopHead = null; + + // loop head + if (IsLoopHead(node)) { + var preBlock = OnlyInloopPred(node); + if (preBlock != null && IsContinue(preBlock, out var preHead) && preHead == node) + return false; + + loopHead = node; + return true; + } + + // match for loop increment block + if (node.Successors.Count == 1) { + // potential loop head + loopHead = node.Successors.SingleOrDefault(s => IsLoopHead(s) && OnlyInloopPred(s) == node); + if (loopHead != null && + HighLevelLoopTransform.MatchIncrementBlock((Block)node.UserData, out var target) && + target == loopHead.UserData) + return true; + } + + // match do-while condition + if (node.Successors.Count <= 2) { + // potential loop head + loopHead = node.Successors.OnlyOrDefault(s => IsLoopHead(s) && OnlyInloopPred(s) == node); + if (loopHead != null && + HighLevelLoopTransform.MatchDoWhileConditionBlock((Block)node.UserData, out var t1, out var t2) && + (t1 == loopHead.UserData || t2 == loopHead.UserData)) + return true; + } + + return false; + } /// /// Lists all potential targets for break; statements from a domination tree, @@ -318,8 +360,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow /// /// Note that node will be returned once for every outgoing edge /// - internal static IEnumerable GetBreakTargets(ControlFlowNode dominator) => - TreeTraversal.PreOrder(dominator, n => n.DominatorTreeChildren.Where(c => !IsContinue(c))) - .SelectMany(n => n.Successors.Where(c => !dominator.Dominates(c) && !IsContinue(c))); + internal static IEnumerable GetBreakTargets(ControlFlowNode loopHead, ControlFlowNode dominator) => + TreeTraversal.PreOrder(dominator, n => n.DominatorTreeChildren.Where(c => !IsContinue(loopHead, c))) + .SelectMany(n => n.Successors) + .Where(n => !dominator.Dominates(n) && !IsContinue(loopHead, n)); } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs index 96743bcc1..61b4f2f48 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs @@ -325,11 +325,30 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; } + // early match before block containers have been constructed + internal static bool MatchDoWhileConditionBlock(Block block, out Block target1, out Block target2) + { + target1 = target2 = null; + if (block.Instructions.Count < 2) + return false; + + var last = block.Instructions.Last(); + if (!(block.Instructions.SecondToLastOrDefault() is IfInstruction ifInstruction) || !ifInstruction.FalseInst.MatchNop()) + return false; + + return (ifInstruction.TrueInst.MatchBranch(out target1) || ifInstruction.TrueInst.MatchReturn(out var _)) && + (last.MatchBranch(out target2) || last.MatchReturn(out var _)); + } + internal static Block GetIncrementBlock(BlockContainer loop, Block whileLoopBody) => loop.Blocks.SingleOrDefault(b => b != whileLoopBody && b.Instructions.Last().MatchBranch(loop.EntryPoint) && b.Instructions.SkipLast(1).All(IsSimpleStatement)); + internal static bool MatchIncrementBlock(Block block, out Block loopHead) => + block.Instructions.Last().MatchBranch(out loopHead) + && block.Instructions.SkipLast(1).All(IsSimpleStatement); + bool MatchForLoop(BlockContainer loop, IfInstruction whileCondition, Block whileLoopBody) { // for loops have exactly two incoming edges at the entry point. diff --git a/ICSharpCode.Decompiler/Util/CollectionExtensions.cs b/ICSharpCode.Decompiler/Util/CollectionExtensions.cs index 31e0eebee..590f8027a 100644 --- a/ICSharpCode.Decompiler/Util/CollectionExtensions.cs +++ b/ICSharpCode.Decompiler/Util/CollectionExtensions.cs @@ -306,6 +306,22 @@ namespace ICSharpCode.Decompiler.Util list.RemoveAt(list.Count - 1); } + public static T OnlyOrDefault(this IEnumerable source, Func predicate) => OnlyOrDefault(source.Where(predicate)); + + public static T OnlyOrDefault(this IEnumerable source) + { + bool any = false; + T first = default; + foreach (var t in source) { + if (any) + return default(T); + first = t; + any = true; + } + + return first; + } + #region Aliases/shortcuts for Enumerable extension methods public static bool Any(this ICollection list) => list.Count > 0; public static bool Any(this T[] array, Predicate match) => Array.Exists(array, match);