Browse Source

Improve switch decompilation in loops via early detection of continue blocks.

pull/1258/head
Chicken-Bones 7 years ago
parent
commit
7017d998d0
  1. 5
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  2. 301
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs
  3. 832
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il
  4. 560
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il
  5. 578
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il
  6. 885
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il
  7. 23
      ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs
  8. 61
      ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs
  9. 59
      ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs
  10. 19
      ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs
  11. 16
      ICSharpCode.Decompiler/Util/CollectionExtensions.cs

5
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -121,7 +121,10 @@ namespace ICSharpCode.Decompiler.Tests @@ -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]

301
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs

@ -18,6 +18,7 @@ @@ -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 @@ -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 @@ -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

832
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il

@ -8,6 +8,11 @@ @@ -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 @@ @@ -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<string,int32> '<PrivateImplementationDetails>'::'$$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<string,int32>::.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<string,int32>::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<string,int32>::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<string,int32>::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<string,int32>::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<string,int32>::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<string,int32>::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<string,int32>::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<string,int32>::Add(!0,
!1)
IL_0089: volatile.
IL_008b: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32> '<PrivateImplementationDetails>'::'$$method0x6000016-1'
IL_0090: volatile.
IL_0092: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32> '<PrivateImplementationDetails>'::'$$method0x6000016-1'
IL_0097: ldloc.0
IL_0098: ldloca.s V_1
IL_009a: call instance bool class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32>::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 @@ @@ -1640,7 +1791,7 @@
IL_003b: brfalse IL_012c
IL_0040: volatile.
IL_0042: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32> '<PrivateImplementationDetails>'::'$$method0x6000017-1'
IL_0042: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32> '<PrivateImplementationDetails>'::'$$method0x6000018-1'
IL_0047: brtrue.s IL_009e
IL_0049: ldc.i4.6
@ -1676,9 +1827,9 @@ @@ -1676,9 +1827,9 @@
IL_0092: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32>::Add(!0,
!1)
IL_0097: volatile.
IL_0099: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32> '<PrivateImplementationDetails>'::'$$method0x6000017-1'
IL_0099: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32> '<PrivateImplementationDetails>'::'$$method0x6000018-1'
IL_009e: volatile.
IL_00a0: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32> '<PrivateImplementationDetails>'::'$$method0x6000017-1'
IL_00a0: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32> '<PrivateImplementationDetails>'::'$$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<string,int32>::TryGetValue(!0,
@ -1903,6 +2054,678 @@ @@ -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<int32> 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<int32> [System.Core]System.Linq.Enumerable::Range(int32,
int32)
IL_000a: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> class [mscorlib]System.Collections.Generic.IEnumerable`1<int32>::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<int32>::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 @@ @@ -2676,7 +3499,8 @@
.field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32> '$$method0x6000010-1'
.field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32> '$$method0x6000011-1'
.field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32> '$$method0x6000015-1'
.field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32> '$$method0x6000017-1'
.field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32> '$$method0x6000016-1'
.field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32> '$$method0x6000018-1'
} // end of class '<PrivateImplementationDetails>'

560
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il

@ -8,6 +8,11 @@ @@ -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 @@ @@ -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<string,int32> '<PrivateImplementationDetails>'::'$$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<string,int32>::.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<string,int32>::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<string,int32>::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<string,int32>::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<string,int32>::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<string,int32>::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<string,int32>::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<string,int32>::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<string,int32>::Add(!0,
!1)
IL_0087: volatile.
IL_0089: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32> '<PrivateImplementationDetails>'::'$$method0x6000016-1'
IL_008e: volatile.
IL_0090: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32> '<PrivateImplementationDetails>'::'$$method0x6000016-1'
IL_0095: ldloc.0
IL_0096: ldloca.s V_1
IL_0098: call instance bool class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32>::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 @@ @@ -1396,7 +1521,7 @@
IL_0037: brfalse IL_011f
IL_003c: volatile.
IL_003e: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32> '<PrivateImplementationDetails>'::'$$method0x6000017-1'
IL_003e: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32> '<PrivateImplementationDetails>'::'$$method0x6000018-1'
IL_0043: brtrue.s IL_009a
IL_0045: ldc.i4.6
@ -1432,9 +1557,9 @@ @@ -1432,9 +1557,9 @@
IL_008e: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32>::Add(!0,
!1)
IL_0093: volatile.
IL_0095: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32> '<PrivateImplementationDetails>'::'$$method0x6000017-1'
IL_0095: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32> '<PrivateImplementationDetails>'::'$$method0x6000018-1'
IL_009a: volatile.
IL_009c: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32> '<PrivateImplementationDetails>'::'$$method0x6000017-1'
IL_009c: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32> '<PrivateImplementationDetails>'::'$$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<string,int32>::TryGetValue(!0,
@ -1626,6 +1751,432 @@ @@ -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<int32> 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<int32> [System.Core]System.Linq.Enumerable::Range(int32,
int32)
IL_0008: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> class [mscorlib]System.Collections.Generic.IEnumerable`1<int32>::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<int32>::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 @@ @@ -2092,7 +2643,8 @@
.field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32> '$$method0x6000010-1'
.field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32> '$$method0x6000011-1'
.field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32> '$$method0x6000015-1'
.field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32> '$$method0x6000017-1'
.field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32> '$$method0x6000016-1'
.field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2<string,int32> '$$method0x6000018-1'
} // end of class '<PrivateImplementationDetails>'

578
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il

@ -8,6 +8,11 @@ @@ -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 @@ @@ -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 '<PrivateImplementationDetails>'::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 @@ @@ -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<int32> 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<int32> [System.Core]System.Linq.Enumerable::Range(int32,
int32)
IL_0008: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> class [mscorlib]System.Collections.Generic.IEnumerable`1<int32>::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<int32>::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
{

885
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il

@ -8,6 +8,11 @@ @@ -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 @@ @@ -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 '<PrivateImplementationDetails>'::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 @@ @@ -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<int32> 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<int32> [System.Core]System.Linq.Enumerable::Range(int32,
int32)
IL_000a: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> class [mscorlib]System.Collections.Generic.IEnumerable`1<int32>::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<int32>::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
{

23
ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs

@ -47,7 +47,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -47,7 +47,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
private BlockTransformContext context;
private ControlFlowNode cfgNode;
private BlockContainer currentContainer;
private Block continueBlock;
/// <summary>
/// Builds structured control flow for the block associated with the control flow node.
@ -61,9 +60,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -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 @@ -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 @@ -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 @@ -599,19 +594,19 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// Used to identify branches targetting this block as continue statements, for ordering priority.
/// </summary>
/// <returns></returns>
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;
}
/// <summary>

61
ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs

@ -243,8 +243,8 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -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 @@ -272,14 +272,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// 3) otherwise (exit point unknown, heuristically extend loop): null
/// </returns>
/// <remarks>This method must not write to the Visited flags on the CFG.</remarks>
ControlFlowNode FindExitPoint(ControlFlowNode loopHead, IReadOnlyList<ControlFlowNode> naturalLoop, bool treatBackEdgesAsExits)
internal ControlFlowNode FindExitPoint(ControlFlowNode loopHead, IReadOnlyList<ControlFlowNode> 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 @@ -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 @@ -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 @@ -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 @@ -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<ControlFlowNode> DominatorTreeChildren(ControlFlowNode n, ControlFlowNode exitPoint, bool isSwitch) =>
n.DominatorTreeChildren.Where(c => c != exitPoint && (!isSwitch || !SwitchDetection.IsContinue(context.ControlFlowNode, c)));
/// <summary>
/// Pick exit point by picking any node that has no reachable exits.
///
@ -395,11 +399,14 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -395,11 +399,14 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// </summary>
/// <returns>Code amount in <paramref name="node"/> and its dominated nodes.</returns>
/// <remarks>This method must not write to the Visited flags on the CFG.</remarks>
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 @@ -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 @@ -431,7 +438,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// or that leave the block Container.
/// </summary>
/// <param name="loopHead">Entry point of the loop.</param>
/// <param name="treatBackEdgesAsExits">Whether to treat loop back edges as exit points.</param>
/// <param name="isSwitch">Whether to ignore branches that map to C# 'continue' statements.</param>
/// <param name="exitNodeArity">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 @@ -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.
/// </param>
/// <returns></returns>
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 @@ -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 @@ -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,

59
ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs

@ -171,7 +171,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -171,7 +171,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
private bool UseCSharpSwitch(out KeyValuePair<LongSet, ILInstruction> defaultSection)
{
if (!analysis.InnerBlocks.Any()) {
defaultSection = default(KeyValuePair<LongSet, ILInstruction>);
defaultSection = default;
return false;
}
defaultSection = analysis.Sections.FirstOrDefault(s => s.Key.Count() > MaxValuesPerSection);
@ -214,7 +214,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -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();
}
/// <summary>
@ -238,6 +238,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -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<ControlFlowNode>();
foreach (var s in analysis.Sections) {
@ -245,7 +246,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -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 @@ -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 @@ -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;
}
/// <summary>
/// Lists all potential targets for break; statements from a domination tree,
@ -318,8 +360,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -318,8 +360,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
///
/// Note that node will be returned once for every outgoing edge
/// </summary>
internal static IEnumerable<ControlFlowNode> 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<ControlFlowNode> 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));
}
}

19
ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs

@ -325,11 +325,30 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -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.

16
ICSharpCode.Decompiler/Util/CollectionExtensions.cs

@ -306,6 +306,22 @@ namespace ICSharpCode.Decompiler.Util @@ -306,6 +306,22 @@ namespace ICSharpCode.Decompiler.Util
list.RemoveAt(list.Count - 1);
}
public static T OnlyOrDefault<T>(this IEnumerable<T> source, Func<T, bool> predicate) => OnlyOrDefault(source.Where(predicate));
public static T OnlyOrDefault<T>(this IEnumerable<T> 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<T>(this ICollection<T> list) => list.Count > 0;
public static bool Any<T>(this T[] array, Predicate<T> match) => Array.Exists(array, match);

Loading…
Cancel
Save