diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index 9227073bd..490bc4c50 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -105,6 +105,7 @@ + diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs index 483e1f3b0..6e340a2d3 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -97,6 +97,12 @@ namespace ICSharpCode.Decompiler.Tests Run(cscOptions: cscOptions); } + [Test] + public void Switch([ValueSource("defaultOptions")] CompilerOptions cscOptions) + { + Run(cscOptions: cscOptions); + } + [Test] public void DelegateConstruction([ValueSource("defaultOptions")] CompilerOptions cscOptions) { diff --git a/ICSharpCode.Decompiler.Tests/Switch.cs b/ICSharpCode.Decompiler.Tests/Switch.cs deleted file mode 100644 index 4a20a6392..000000000 --- a/ICSharpCode.Decompiler.Tests/Switch.cs +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this -// software and associated documentation files (the "Software"), to deal in the Software -// without restriction, including without limitation the rights to use, copy, modify, merge, -// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons -// to whom the Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE -// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -using System; - -public static class Switch -{ - public static string ShortSwitchOverString(string text) - { - switch (text) { - case "First case": - return "Text"; - default: - return "Default"; - } - } - - public static string SwitchOverString1(string text) - { - switch (text) - { - case "First case": - return "Text1"; - case "Second case": - case "2nd case": - return "Text2"; - case "Third case": - return "Text3"; - case "Fourth case": - return "Text4"; - case "Fifth case": - return "Text5"; - case "Sixth case": - return "Text6"; - case null: - return null; - default: - return "Default"; - } - } - - public static string SwitchOverString2() - { - switch (Environment.UserName) - { - case "First case": - return "Text1"; - case "Second case": - return "Text2"; - case "Third case": - return "Text3"; - case "Fourth case": - return "Text4"; - case "Fifth case": - return "Text5"; - case "Sixth case": - return "Text6"; - default: - return "Default"; - } - } - - public static string SwitchOverBool(bool b) - { - switch (b) { - case true: - return bool.TrueString; - case false: - return bool.FalseString; - default: - return null; - } - } -} diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/LINQRaytracer.cs b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/LINQRaytracer.cs index 3a2808a6c..c18201788 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/LINQRaytracer.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/LINQRaytracer.cs @@ -10,8 +10,8 @@ namespace RayTracer { static void Main() { - const int width = 600; - const int height = 600; + const int width = 50; + const int height = 50; RayTracer rayTracer = new RayTracer(width, height, (int x, int y, Color color) => { Console.Write("{0},{1}:{2};", x, y, color); diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Switch.cs b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Switch.cs index 61ed2f347..f28e691cf 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Switch.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Switch.cs @@ -26,10 +26,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { TestCase(SparseIntegerSwitch, -100, 1, 2, 3, 4); TestCase(ShortSwitchOverString, "First case", "Else"); + TestCase(ShortSwitchOverString2, "First case", "Second case", "Third case", "Else"); + TestCase(ShortSwitchOverStringNoExplicitDefault, "First case", "Second case", "Third case", "Else"); TestCase(SwitchOverString1, "First case", "Second case", "2nd case", "Third case", "Fourth case", "Fifth case", "Sixth case", null, "default", "else"); Console.WriteLine(SwitchOverString2()); Console.WriteLine(SwitchOverBool(true)); Console.WriteLine(SwitchOverBool(false)); + SwitchInLoop(0); + SwitchWithGoto(1); + SwitchWithGoto(2); + SwitchWithGoto(3); + SwitchWithGoto(4); } static void TestCase(Func target, params T[] args) @@ -41,6 +48,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness public static string SparseIntegerSwitch(int i) { + Console.WriteLine("SparseIntegerSwitch: " + i); switch (i) { case -10000000: return "-10 mln"; case -100: return "-hundred"; @@ -59,6 +67,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness public static string ShortSwitchOverString(string text) { + Console.WriteLine("ShortSwitchOverString: " + text); switch (text) { case "First case": return "Text"; @@ -67,8 +76,38 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness } } + public static string ShortSwitchOverString2(string text) + { + Console.WriteLine("ShortSwitchOverString2: " + text); + switch (text) { + case "First case": + return "Text1"; + case "Second case": + return "Text2"; + case "Third case": + return "Text3"; + default: + return "Default"; + } + } + + public static string ShortSwitchOverStringNoExplicitDefault(string text) + { + Console.WriteLine("ShortSwitchOverStringNoExplicitDefault: " + text); + switch (text) { + case "First case": + return "Text1"; + case "Second case": + return "Text2"; + case "Third case": + return "Text3"; + } + return "Default"; + } + public static string SwitchOverString1(string text) { + Console.WriteLine("SwitchOverString1: " + text); switch (text) { case "First case": return "Text1"; @@ -92,6 +131,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness public static string SwitchOverString2() { + Console.WriteLine("SwitchOverString2:"); switch (Environment.UserName) { case "First case": return "Text1"; @@ -105,6 +145,16 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness return "Text5"; case "Sixth case": return "Text6"; + case "Seventh case": + return "Text7"; + case "Eighth case": + return "Text8"; + case "Ninth case": + return "Text9"; + case "Tenth case": + return "Text10"; + case "Eleventh case": + return "Text11"; default: return "Default"; } @@ -112,6 +162,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness public static string SwitchOverBool(bool b) { + Console.WriteLine("SwitchOverBool: " + b); switch (b) { case true: return bool.TrueString; @@ -124,6 +175,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness public static void SwitchInLoop(int i) { + Console.WriteLine("SwitchInLoop: " + i); while (true) { switch (i) { case 1: @@ -141,10 +193,32 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness default: Console.WriteLine("default"); Console.WriteLine("more code"); - throw new ArgumentException(); + return; } i++; } } + + public static void SwitchWithGoto(int i) + { + Console.WriteLine("SwitchWithGoto: " + i); + switch (i) { + case 1: + Console.WriteLine("one"); + goto default; + case 2: + Console.WriteLine("two"); + goto case 3; + case 3: + Console.WriteLine("three"); + break; + case 4: + Console.WriteLine("four"); + return; + default: + Console.WriteLine("default"); + break; + } + } } } \ No newline at end of file diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs new file mode 100644 index 000000000..1f6ec6f17 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs @@ -0,0 +1,415 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty +{ + public static class Switch + { + public class SetProperty + { + public readonly PropertyInfo Property; + + public int Set { + get; + set; + } + + public SetProperty(PropertyInfo property) + { + this.Property = property; + } + } + + public static string SparseIntegerSwitch(int i) + { + Console.WriteLine("SparseIntegerSwitch: " + i); + switch (i) { + case -10000000: + return "-10 mln"; + case -100: + return "-hundred"; + case -1: + return "-1"; + case 0: + return "0"; + case 1: + return "1"; + case 2: + return "2"; + case 4: + return "4"; + case 100: + return "hundred"; + case 10000: + return "ten thousand"; + case 10001: + return "ten thousand and one"; + case 2147483647: + return "int.MaxValue"; + default: + return "something else"; + } + } + + public static string SwitchOverNullableInt(int? i) + { + switch (i) { + case null: + return "null"; + case 0: + return "zero"; + case 5: + return "five"; + case 10: + return "ten"; + default: + return "large"; + } + } + + public static string SwitchOverNullableIntNullCaseCombined(int? i) + { + switch (i) { + case null: + case 0: + return "zero"; + case 5: + return "five"; + case 10: + return "ten"; + default: + return "large"; + } + } + + public static string SwitchOverNullableIntShifted(int? i) + { + switch (i + 5) { + case null: + return "null"; + case 0: + return "zero"; + case 5: + return "five"; + case 10: + return "ten"; + default: + return "large"; + } + } + + public static string SwitchOverNullableIntShiftedNullCaseCombined(int? i) + { + switch (i + 5) { + case null: + case 0: + return "zero"; + case 5: + return "five"; + case 10: + return "ten"; + default: + return "large"; + } + } + + public static string SwitchOverNullableIntNoNullCase(int? i) + { + switch (i) { + case 0: + return "zero"; + case 5: + return "five"; + case 10: + return "ten"; + default: + return "other"; + } + } + + public static string SwitchOverNullableIntNoNullCaseShifted(int? i) + { + switch (i + 5) { + case 0: + return "zero"; + case 5: + return "five"; + case 10: + return "ten"; + default: + return "other"; + } + } + + public static void SwitchOverInt(int i) + { + switch (i) { + case 0: + Console.WriteLine("zero"); + break; + case 5: + Console.WriteLine("five"); + break; + case 10: + Console.WriteLine("ten"); + break; + case 15: + Console.WriteLine("fifteen"); + break; + case 20: + Console.WriteLine("twenty"); + break; + case 25: + Console.WriteLine("twenty-five"); + break; + case 30: + Console.WriteLine("thirty"); + break; + } + } + + public static string ShortSwitchOverString(string text) + { + Console.WriteLine("ShortSwitchOverString: " + text); + switch (text) { + case "First case": + return "Text1"; + case "Second case": + return "Text2"; + case "Third case": + return "Text3"; + default: + return "Default"; + } + } + + public static string ShortSwitchOverStringWithNullCase(string text) + { + Console.WriteLine("ShortSwitchOverStringWithNullCase: " + text); + switch (text) { + case "First case": + return "Text1"; + case "Second case": + return "Text2"; + case null: + return "null"; + default: + return "Default"; + } + } + + public static string SwitchOverString1(string text) + { + Console.WriteLine("SwitchOverString1: " + text); + switch (text) { + case "First case": + return "Text1"; + case "Second case": + case "2nd case": + return "Text2"; + case "Third case": + return "Text3"; + case "Fourth case": + return "Text4"; + case "Fifth case": + return "Text5"; + case "Sixth case": + return "Text6"; + case null: + return null; + default: + return "Default"; + } + } + + public static string SwitchOverString2() + { + Console.WriteLine("SwitchOverString2:"); + switch (Environment.UserName) { + case "First case": + return "Text1"; + case "Second case": + return "Text2"; + case "Third case": + return "Text3"; + case "Fourth case": + return "Text4"; + case "Fifth case": + return "Text5"; + case "Sixth case": + return "Text6"; + case "Seventh case": + return "Text7"; + case "Eighth case": + return "Text8"; + case "Ninth case": + return "Text9"; + case "Tenth case": + return "Text10"; + case "Eleventh case": + return "Text11"; + default: + return "Default"; + } + } + + public static string SwitchOverBool(bool b) + { + Console.WriteLine("SwitchOverBool: " + b.ToString()); + switch (b) { + case true: + return bool.TrueString; + case false: + return bool.FalseString; + default: + return null; + } + } + + public static void SwitchInLoop(int i) + { + Console.WriteLine("SwitchInLoop: " + i); + while (true) { + switch (i) { + case 1: + Console.WriteLine("one"); + break; + case 2: + Console.WriteLine("two"); + break; + //case 3: + // Console.WriteLine("three"); + // continue; + case 4: + Console.WriteLine("four"); + return; + default: + Console.WriteLine("default"); + Console.WriteLine("more code"); + return; + } + i++; + } + } + + public static void SwitchWithGoto(int i) + { + Console.WriteLine("SwitchWithGoto: " + i); + switch (i) { + case 1: + Console.WriteLine("one"); + goto default; + case 2: + Console.WriteLine("two"); + goto case 3; + case 3: + Console.WriteLine("three"); + break; + case 4: + Console.WriteLine("four"); + return; + default: + Console.WriteLine("default"); + break; + } + Console.WriteLine("End of method"); + } + + private static SetProperty[] GetProperties() + { + return new SetProperty[0]; + } + + public static void SwitchOnStringInForLoop() + { + List list = new List(); + List list2 = new List(); + SetProperty[] properties = Switch.GetProperties(); + for (int i = 0; i < properties.Length; i++) { + SetProperty setProperty = properties[i]; + switch (setProperty.Property.Name) { + case "Name1": + setProperty.Set = 1; + list.Add(setProperty); + break; + case "Name2": + setProperty.Set = 2; + list.Add(setProperty); + break; + case "Name3": + setProperty.Set = 3; + list.Add(setProperty); + break; + case "Name4": + setProperty.Set = 4; + list.Add(setProperty); + break; + case "Name5": + case "Name6": + list.Add(setProperty); + break; + default: + list2.Add(setProperty); + break; + } + } + } + + public static void SwitchWithComplexCondition(string[] args) + { + switch ((args.Length == 0) ? "dummy" : args[0]) { + case "a": + Console.WriteLine("a"); + break; + case "b": + Console.WriteLine("b"); + break; + case "c": + Console.WriteLine("c"); + break; + case "d": + Console.WriteLine("d"); + break; + } + Console.WriteLine("end"); + } + + public static void SwitchWithArray(string[] args) + { + switch (args[0]) { + case "a": + Console.WriteLine("a"); + break; + case "b": + Console.WriteLine("b"); + break; + case "c": + Console.WriteLine("c"); + break; + case "d": + Console.WriteLine("d"); + break; + } + Console.WriteLine("end"); + } + } +} \ No newline at end of file diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il new file mode 100644 index 000000000..40a21bc93 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il @@ -0,0 +1,1529 @@ + +// Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0 +// Copyright (c) Microsoft Corporation. Alle Rechte vorbehalten. + + + +// Metadata version: v4.0.30319 +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} +.assembly '4doqvnxq' +{ + .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 + 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) + .permissionset reqmin + = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +.module '4doqvnxq.dll' +// MVID: {5E65DF13-3C5E-44CA-97E7-5B58ACBAF9BF} +.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) +.imagebase 0x10000000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 // WINDOWS_CUI +.corflags 0x00000001 // ILONLY +// Image base: 0x03320000 + + +// =============== CLASS MEMBERS DECLARATION =================== + +.class public abstract auto ansi sealed beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch + extends [mscorlib]System.Object +{ + .class auto ansi nested public beforefieldinit SetProperty + extends [mscorlib]System.Object + { + .field public initonly class [mscorlib]System.Reflection.PropertyInfo Property + .field private int32 'k__BackingField' + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .method public hidebysig specialname + instance int32 get_Set() cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 11 (0xb) + .maxstack 1 + .locals init (int32 V_0) + IL_0000: ldarg.0 + IL_0001: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::'k__BackingField' + IL_0006: stloc.0 + IL_0007: br.s IL_0009 + + IL_0009: ldloc.0 + IL_000a: ret + } // end of method SetProperty::get_Set + + .method public hidebysig specialname + instance void set_Set(int32 'value') cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::'k__BackingField' + IL_0007: ret + } // end of method SetProperty::set_Set + + .method public hidebysig specialname rtspecialname + instance void .ctor(class [mscorlib]System.Reflection.PropertyInfo 'property') cil managed + { + // Code size 17 (0x11) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: nop + IL_0008: ldarg.0 + IL_0009: ldarg.1 + IL_000a: stfld class [mscorlib]System.Reflection.PropertyInfo ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::Property + IL_000f: nop + IL_0010: ret + } // end of method SetProperty::.ctor + + .property instance int32 Set() + { + .get instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::get_Set() + .set instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + } // end of property SetProperty::Set + } // end of class SetProperty + + .method public hidebysig static string + SparseIntegerSwitch(int32 i) cil managed + { + // Code size 209 (0xd1) + .maxstack 2 + .locals init (string V_0, + int32 V_1) + IL_0000: nop + IL_0001: ldstr "SparseIntegerSwitch: " + IL_0006: ldarg.0 + IL_0007: box [mscorlib]System.Int32 + IL_000c: call string [mscorlib]System.String::Concat(object, + object) + IL_0011: call void [mscorlib]System.Console::WriteLine(string) + IL_0016: nop + IL_0017: ldarg.0 + IL_0018: stloc.1 + IL_0019: ldloc.1 + IL_001a: ldc.i4.4 + IL_001b: bgt.s IL_004c + + IL_001d: ldloc.1 + IL_001e: ldc.i4 0xff676980 + IL_0023: beq.s IL_006f + + IL_0025: ldloc.1 + IL_0026: ldc.i4.s -100 + IL_0028: beq.s IL_0077 + + IL_002a: ldloc.1 + IL_002b: ldc.i4.m1 + IL_002c: sub + IL_002d: switch ( + IL_007f, + IL_0087, + IL_008f, + IL_0097, + IL_00c7, + IL_009f) + IL_004a: br.s IL_00c7 + + IL_004c: ldloc.1 + IL_004d: ldc.i4.s 100 + IL_004f: beq.s IL_00a7 + + IL_0051: ldloc.1 + IL_0052: ldc.i4 0x2710 + IL_0057: sub + IL_0058: switch ( + IL_00af, + IL_00b7) + IL_0065: ldloc.1 + IL_0066: ldc.i4 0x7fffffff + IL_006b: beq.s IL_00bf + + IL_006d: br.s IL_00c7 + + IL_006f: ldstr "-10 mln" + IL_0074: stloc.0 + IL_0075: br.s IL_00cf + + IL_0077: ldstr "-hundred" + IL_007c: stloc.0 + IL_007d: br.s IL_00cf + + IL_007f: ldstr "-1" + IL_0084: stloc.0 + IL_0085: br.s IL_00cf + + IL_0087: ldstr "0" + IL_008c: stloc.0 + IL_008d: br.s IL_00cf + + IL_008f: ldstr "1" + IL_0094: stloc.0 + IL_0095: br.s IL_00cf + + IL_0097: ldstr "2" + IL_009c: stloc.0 + IL_009d: br.s IL_00cf + + IL_009f: ldstr "4" + IL_00a4: stloc.0 + IL_00a5: br.s IL_00cf + + IL_00a7: ldstr "hundred" + IL_00ac: stloc.0 + IL_00ad: br.s IL_00cf + + IL_00af: ldstr "ten thousand" + IL_00b4: stloc.0 + IL_00b5: br.s IL_00cf + + IL_00b7: ldstr "ten thousand and one" + IL_00bc: stloc.0 + IL_00bd: br.s IL_00cf + + IL_00bf: ldstr "int.MaxValue" + IL_00c4: stloc.0 + IL_00c5: br.s IL_00cf + + IL_00c7: ldstr "something else" + IL_00cc: stloc.0 + IL_00cd: br.s IL_00cf + + IL_00cf: ldloc.0 + IL_00d0: ret + } // end of method Switch::SparseIntegerSwitch + + .method public hidebysig static string + SwitchOverNullableInt(valuetype [mscorlib]System.Nullable`1 i) cil managed + { + // Code size 74 (0x4a) + .maxstack 2 + .locals init (string V_0, + int32 V_1) + IL_0000: nop + IL_0001: ldarga.s i + IL_0003: dup + IL_0004: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() + IL_0009: stloc.1 + IL_000a: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue() + IL_000f: brfalse.s IL_0020 + + IL_0011: ldloc.1 + IL_0012: ldc.i4.0 + IL_0013: beq.s IL_0028 + + IL_0015: ldloc.1 + IL_0016: ldc.i4.5 + IL_0017: beq.s IL_0030 + + IL_0019: ldloc.1 + IL_001a: ldc.i4.s 10 + IL_001c: beq.s IL_0038 + + IL_001e: br.s IL_0040 + + IL_0020: ldstr "null" + IL_0025: stloc.0 + IL_0026: br.s IL_0048 + + IL_0028: ldstr "zero" + IL_002d: stloc.0 + IL_002e: br.s IL_0048 + + IL_0030: ldstr "five" + IL_0035: stloc.0 + IL_0036: br.s IL_0048 + + IL_0038: ldstr "ten" + IL_003d: stloc.0 + IL_003e: br.s IL_0048 + + IL_0040: ldstr "large" + IL_0045: stloc.0 + IL_0046: br.s IL_0048 + + IL_0048: ldloc.0 + IL_0049: ret + } // end of method Switch::SwitchOverNullableInt + + .method public hidebysig static string + SwitchOverNullableIntNullCaseCombined(valuetype [mscorlib]System.Nullable`1 i) cil managed + { + // Code size 66 (0x42) + .maxstack 2 + .locals init (string V_0, + int32 V_1) + IL_0000: nop + IL_0001: ldarga.s i + IL_0003: dup + IL_0004: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() + IL_0009: stloc.1 + IL_000a: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue() + IL_000f: brfalse.s IL_0020 + + IL_0011: ldloc.1 + IL_0012: ldc.i4.0 + IL_0013: beq.s IL_0020 + + IL_0015: ldloc.1 + IL_0016: ldc.i4.5 + IL_0017: beq.s IL_0028 + + IL_0019: ldloc.1 + IL_001a: ldc.i4.s 10 + IL_001c: beq.s IL_0030 + + IL_001e: br.s IL_0038 + + IL_0020: ldstr "zero" + IL_0025: stloc.0 + IL_0026: br.s IL_0040 + + IL_0028: ldstr "five" + IL_002d: stloc.0 + IL_002e: br.s IL_0040 + + IL_0030: ldstr "ten" + IL_0035: stloc.0 + IL_0036: br.s IL_0040 + + IL_0038: ldstr "large" + IL_003d: stloc.0 + IL_003e: br.s IL_0040 + + IL_0040: ldloc.0 + IL_0041: ret + } // end of method Switch::SwitchOverNullableIntNullCaseCombined + + .method public hidebysig static string + SwitchOverNullableIntShifted(valuetype [mscorlib]System.Nullable`1 i) cil managed + { + // Code size 112 (0x70) + .maxstack 2 + .locals init (string V_0, + valuetype [mscorlib]System.Nullable`1 V_1, + valuetype [mscorlib]System.Nullable`1 V_2, + int32 V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.1 + IL_0003: ldloca.s V_1 + IL_0005: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue() + IL_000a: brtrue.s IL_0017 + + IL_000c: ldloca.s V_2 + IL_000e: initobj valuetype [mscorlib]System.Nullable`1 + IL_0014: ldloc.2 + IL_0015: br.s IL_0025 + + IL_0017: ldloca.s V_1 + IL_0019: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() + IL_001e: ldc.i4.5 + IL_001f: add + IL_0020: newobj instance void valuetype [mscorlib]System.Nullable`1::.ctor(!0) + IL_0025: nop + IL_0026: stloc.2 + IL_0027: ldloca.s V_2 + IL_0029: dup + IL_002a: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() + IL_002f: stloc.3 + IL_0030: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue() + IL_0035: brfalse.s IL_0046 + + IL_0037: ldloc.3 + IL_0038: ldc.i4.0 + IL_0039: beq.s IL_004e + + IL_003b: ldloc.3 + IL_003c: ldc.i4.5 + IL_003d: beq.s IL_0056 + + IL_003f: ldloc.3 + IL_0040: ldc.i4.s 10 + IL_0042: beq.s IL_005e + + IL_0044: br.s IL_0066 + + IL_0046: ldstr "null" + IL_004b: stloc.0 + IL_004c: br.s IL_006e + + IL_004e: ldstr "zero" + IL_0053: stloc.0 + IL_0054: br.s IL_006e + + IL_0056: ldstr "five" + IL_005b: stloc.0 + IL_005c: br.s IL_006e + + IL_005e: ldstr "ten" + IL_0063: stloc.0 + IL_0064: br.s IL_006e + + IL_0066: ldstr "large" + IL_006b: stloc.0 + IL_006c: br.s IL_006e + + IL_006e: ldloc.0 + IL_006f: ret + } // end of method Switch::SwitchOverNullableIntShifted + + .method public hidebysig static string + SwitchOverNullableIntShiftedNullCaseCombined(valuetype [mscorlib]System.Nullable`1 i) cil managed + { + // Code size 104 (0x68) + .maxstack 2 + .locals init (string V_0, + valuetype [mscorlib]System.Nullable`1 V_1, + valuetype [mscorlib]System.Nullable`1 V_2, + int32 V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.1 + IL_0003: ldloca.s V_1 + IL_0005: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue() + IL_000a: brtrue.s IL_0017 + + IL_000c: ldloca.s V_2 + IL_000e: initobj valuetype [mscorlib]System.Nullable`1 + IL_0014: ldloc.2 + IL_0015: br.s IL_0025 + + IL_0017: ldloca.s V_1 + IL_0019: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() + IL_001e: ldc.i4.5 + IL_001f: add + IL_0020: newobj instance void valuetype [mscorlib]System.Nullable`1::.ctor(!0) + IL_0025: nop + IL_0026: stloc.2 + IL_0027: ldloca.s V_2 + IL_0029: dup + IL_002a: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() + IL_002f: stloc.3 + IL_0030: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue() + IL_0035: brfalse.s IL_0046 + + IL_0037: ldloc.3 + IL_0038: ldc.i4.0 + IL_0039: beq.s IL_0046 + + IL_003b: ldloc.3 + IL_003c: ldc.i4.5 + IL_003d: beq.s IL_004e + + IL_003f: ldloc.3 + IL_0040: ldc.i4.s 10 + IL_0042: beq.s IL_0056 + + IL_0044: br.s IL_005e + + IL_0046: ldstr "zero" + IL_004b: stloc.0 + IL_004c: br.s IL_0066 + + IL_004e: ldstr "five" + IL_0053: stloc.0 + IL_0054: br.s IL_0066 + + IL_0056: ldstr "ten" + IL_005b: stloc.0 + IL_005c: br.s IL_0066 + + IL_005e: ldstr "large" + IL_0063: stloc.0 + IL_0064: br.s IL_0066 + + IL_0066: ldloc.0 + IL_0067: ret + } // end of method Switch::SwitchOverNullableIntShiftedNullCaseCombined + + .method public hidebysig static string + SwitchOverNullableIntNoNullCase(valuetype [mscorlib]System.Nullable`1 i) cil managed + { + // Code size 66 (0x42) + .maxstack 2 + .locals init (string V_0, + int32 V_1) + IL_0000: nop + IL_0001: ldarga.s i + IL_0003: dup + IL_0004: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() + IL_0009: stloc.1 + IL_000a: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue() + IL_000f: brfalse.s IL_0038 + + IL_0011: ldloc.1 + IL_0012: ldc.i4.0 + IL_0013: beq.s IL_0020 + + IL_0015: ldloc.1 + IL_0016: ldc.i4.5 + IL_0017: beq.s IL_0028 + + IL_0019: ldloc.1 + IL_001a: ldc.i4.s 10 + IL_001c: beq.s IL_0030 + + IL_001e: br.s IL_0038 + + IL_0020: ldstr "zero" + IL_0025: stloc.0 + IL_0026: br.s IL_0040 + + IL_0028: ldstr "five" + IL_002d: stloc.0 + IL_002e: br.s IL_0040 + + IL_0030: ldstr "ten" + IL_0035: stloc.0 + IL_0036: br.s IL_0040 + + IL_0038: ldstr "other" + IL_003d: stloc.0 + IL_003e: br.s IL_0040 + + IL_0040: ldloc.0 + IL_0041: ret + } // end of method Switch::SwitchOverNullableIntNoNullCase + + .method public hidebysig static string + SwitchOverNullableIntNoNullCaseShifted(valuetype [mscorlib]System.Nullable`1 i) cil managed + { + // Code size 104 (0x68) + .maxstack 2 + .locals init (string V_0, + valuetype [mscorlib]System.Nullable`1 V_1, + valuetype [mscorlib]System.Nullable`1 V_2, + int32 V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.1 + IL_0003: ldloca.s V_1 + IL_0005: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue() + IL_000a: brtrue.s IL_0017 + + IL_000c: ldloca.s V_2 + IL_000e: initobj valuetype [mscorlib]System.Nullable`1 + IL_0014: ldloc.2 + IL_0015: br.s IL_0025 + + IL_0017: ldloca.s V_1 + IL_0019: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() + IL_001e: ldc.i4.5 + IL_001f: add + IL_0020: newobj instance void valuetype [mscorlib]System.Nullable`1::.ctor(!0) + IL_0025: nop + IL_0026: stloc.2 + IL_0027: ldloca.s V_2 + IL_0029: dup + IL_002a: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() + IL_002f: stloc.3 + IL_0030: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue() + IL_0035: brfalse.s IL_005e + + IL_0037: ldloc.3 + IL_0038: ldc.i4.0 + IL_0039: beq.s IL_0046 + + IL_003b: ldloc.3 + IL_003c: ldc.i4.5 + IL_003d: beq.s IL_004e + + IL_003f: ldloc.3 + IL_0040: ldc.i4.s 10 + IL_0042: beq.s IL_0056 + + IL_0044: br.s IL_005e + + IL_0046: ldstr "zero" + IL_004b: stloc.0 + IL_004c: br.s IL_0066 + + IL_004e: ldstr "five" + IL_0053: stloc.0 + IL_0054: br.s IL_0066 + + IL_0056: ldstr "ten" + IL_005b: stloc.0 + IL_005c: br.s IL_0066 + + IL_005e: ldstr "other" + IL_0063: stloc.0 + IL_0064: br.s IL_0066 + + IL_0066: ldloc.0 + IL_0067: ret + } // end of method Switch::SwitchOverNullableIntNoNullCaseShifted + + .method public hidebysig static void SwitchOverInt(int32 i) cil managed + { + // Code size 144 (0x90) + .maxstack 2 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: ldc.i4.s 10 + IL_0006: bgt.s IL_0017 + + IL_0008: ldloc.0 + IL_0009: ldc.i4.0 + IL_000a: beq.s IL_0034 + + IL_000c: ldloc.0 + IL_000d: ldc.i4.5 + IL_000e: beq.s IL_0041 + + IL_0010: ldloc.0 + IL_0011: ldc.i4.s 10 + IL_0013: beq.s IL_004e + + IL_0015: br.s IL_008f + + IL_0017: ldloc.0 + IL_0018: ldc.i4.s 20 + IL_001a: bgt.s IL_0028 + + IL_001c: ldloc.0 + IL_001d: ldc.i4.s 15 + IL_001f: beq.s IL_005b + + IL_0021: ldloc.0 + IL_0022: ldc.i4.s 20 + IL_0024: beq.s IL_0068 + + IL_0026: br.s IL_008f + + IL_0028: ldloc.0 + IL_0029: ldc.i4.s 25 + IL_002b: beq.s IL_0075 + + IL_002d: ldloc.0 + IL_002e: ldc.i4.s 30 + IL_0030: beq.s IL_0082 + + IL_0032: br.s IL_008f + + IL_0034: ldstr "zero" + IL_0039: call void [mscorlib]System.Console::WriteLine(string) + IL_003e: nop + IL_003f: br.s IL_008f + + IL_0041: ldstr "five" + IL_0046: call void [mscorlib]System.Console::WriteLine(string) + IL_004b: nop + IL_004c: br.s IL_008f + + IL_004e: ldstr "ten" + IL_0053: call void [mscorlib]System.Console::WriteLine(string) + IL_0058: nop + IL_0059: br.s IL_008f + + IL_005b: ldstr "fifteen" + IL_0060: call void [mscorlib]System.Console::WriteLine(string) + IL_0065: nop + IL_0066: br.s IL_008f + + IL_0068: ldstr "twenty" + IL_006d: call void [mscorlib]System.Console::WriteLine(string) + IL_0072: nop + IL_0073: br.s IL_008f + + IL_0075: ldstr "twenty-five" + IL_007a: call void [mscorlib]System.Console::WriteLine(string) + IL_007f: nop + IL_0080: br.s IL_008f + + IL_0082: ldstr "thirty" + IL_0087: call void [mscorlib]System.Console::WriteLine(string) + IL_008c: nop + IL_008d: br.s IL_008f + + IL_008f: ret + } // end of method Switch::SwitchOverInt + + .method public hidebysig static string + ShortSwitchOverString(string text) cil managed + { + // Code size 98 (0x62) + .maxstack 2 + .locals init (string V_0, + string V_1) + IL_0000: nop + IL_0001: ldstr "ShortSwitchOverString: " + 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.1 + IL_0014: ldloc.1 + IL_0015: brfalse.s IL_0058 + + IL_0017: ldloc.1 + IL_0018: ldstr "First case" + IL_001d: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0022: brtrue.s IL_0040 + + IL_0024: ldloc.1 + IL_0025: ldstr "Second case" + IL_002a: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_002f: brtrue.s IL_0048 + + IL_0031: ldloc.1 + IL_0032: ldstr "Third case" + IL_0037: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_003c: brtrue.s IL_0050 + + IL_003e: br.s IL_0058 + + IL_0040: ldstr "Text1" + IL_0045: stloc.0 + IL_0046: br.s IL_0060 + + IL_0048: ldstr "Text2" + IL_004d: stloc.0 + IL_004e: br.s IL_0060 + + IL_0050: ldstr "Text3" + IL_0055: stloc.0 + IL_0056: br.s IL_0060 + + IL_0058: ldstr "Default" + IL_005d: stloc.0 + IL_005e: br.s IL_0060 + + IL_0060: ldloc.0 + IL_0061: ret + } // end of method Switch::ShortSwitchOverString + + .method public hidebysig static string + ShortSwitchOverStringWithNullCase(string text) cil managed + { + // Code size 85 (0x55) + .maxstack 2 + .locals init (string V_0, + string V_1) + IL_0000: nop + IL_0001: ldstr "ShortSwitchOverStringWithNullCase: " + 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.1 + IL_0014: ldloc.1 + IL_0015: brfalse.s IL_0043 + + IL_0017: ldloc.1 + IL_0018: ldstr "First case" + IL_001d: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0022: brtrue.s IL_0033 + + IL_0024: ldloc.1 + IL_0025: ldstr "Second case" + IL_002a: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_002f: brtrue.s IL_003b + + IL_0031: br.s IL_004b + + IL_0033: ldstr "Text1" + IL_0038: stloc.0 + IL_0039: br.s IL_0053 + + IL_003b: ldstr "Text2" + IL_0040: stloc.0 + IL_0041: br.s IL_0053 + + IL_0043: ldstr "null" + IL_0048: stloc.0 + IL_0049: br.s IL_0053 + + IL_004b: ldstr "Default" + IL_0050: stloc.0 + IL_0051: br.s IL_0053 + + IL_0053: ldloc.0 + IL_0054: ret + } // end of method Switch::ShortSwitchOverStringWithNullCase + + .method public hidebysig static string + SwitchOverString1(string text) cil managed + { + // Code size 247 (0xf7) + .maxstack 4 + .locals init (string V_0, + string V_1, + int32 V_2) + IL_0000: nop + IL_0001: ldstr "SwitchOverString1: " + 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.1 + IL_0014: ldloc.1 + IL_0015: brfalse IL_00e9 + + IL_001a: volatile. + IL_001c: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{5E65DF13-3C5E-44CA-97E7-5B58ACBAF9BF}'::'$$method0x600000b-1' + IL_0021: brtrue.s IL_0084 + + IL_0023: ldc.i4.7 + IL_0024: newobj instance void class [mscorlib]System.Collections.Generic.Dictionary`2::.ctor(int32) + IL_0029: dup + IL_002a: ldstr "First case" + 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 "Second case" + 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 "2nd case" + 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 "Third case" + 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 "Fourth case" + 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 "Fifth case" + 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 "Sixth case" + IL_0077: ldc.i4.6 + IL_0078: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_007d: volatile. + IL_007f: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{5E65DF13-3C5E-44CA-97E7-5B58ACBAF9BF}'::'$$method0x600000b-1' + IL_0084: volatile. + IL_0086: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{5E65DF13-3C5E-44CA-97E7-5B58ACBAF9BF}'::'$$method0x600000b-1' + IL_008b: ldloc.1 + IL_008c: ldloca.s V_2 + IL_008e: call instance bool class [mscorlib]System.Collections.Generic.Dictionary`2::TryGetValue(!0, + !1&) + IL_0093: brfalse.s IL_00ed + + IL_0095: ldloc.2 + IL_0096: switch ( + IL_00b9, + IL_00c1, + IL_00c1, + IL_00c9, + IL_00d1, + IL_00d9, + IL_00e1) + IL_00b7: br.s IL_00ed + + IL_00b9: ldstr "Text1" + IL_00be: stloc.0 + IL_00bf: br.s IL_00f5 + + IL_00c1: ldstr "Text2" + IL_00c6: stloc.0 + IL_00c7: br.s IL_00f5 + + IL_00c9: ldstr "Text3" + IL_00ce: stloc.0 + IL_00cf: br.s IL_00f5 + + IL_00d1: ldstr "Text4" + IL_00d6: stloc.0 + IL_00d7: br.s IL_00f5 + + IL_00d9: ldstr "Text5" + IL_00de: stloc.0 + IL_00df: br.s IL_00f5 + + IL_00e1: ldstr "Text6" + IL_00e6: stloc.0 + IL_00e7: br.s IL_00f5 + + IL_00e9: ldnull + IL_00ea: stloc.0 + IL_00eb: br.s IL_00f5 + + IL_00ed: ldstr "Default" + IL_00f2: stloc.0 + IL_00f3: br.s IL_00f5 + + IL_00f5: ldloc.0 + IL_00f6: ret + } // end of method Switch::SwitchOverString1 + + .method public hidebysig static string + SwitchOverString2() cil managed + { + // Code size 354 (0x162) + .maxstack 4 + .locals init (string V_0, + string V_1, + int32 V_2) + IL_0000: nop + IL_0001: ldstr "SwitchOverString2:" + IL_0006: call void [mscorlib]System.Console::WriteLine(string) + IL_000b: nop + IL_000c: call string [mscorlib]System.Environment::get_UserName() + IL_0011: stloc.1 + IL_0012: ldloc.1 + IL_0013: brfalse IL_0158 + + IL_0018: volatile. + IL_001a: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{5E65DF13-3C5E-44CA-97E7-5B58ACBAF9BF}'::'$$method0x600000c-1' + IL_001f: brtrue IL_00b8 + + IL_0024: ldc.i4.s 11 + IL_0026: newobj instance void class [mscorlib]System.Collections.Generic.Dictionary`2::.ctor(int32) + IL_002b: dup + IL_002c: ldstr "First case" + IL_0031: ldc.i4.0 + IL_0032: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_0037: dup + IL_0038: ldstr "Second case" + IL_003d: ldc.i4.1 + IL_003e: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_0043: dup + IL_0044: ldstr "Third case" + IL_0049: ldc.i4.2 + IL_004a: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_004f: dup + IL_0050: ldstr "Fourth case" + IL_0055: ldc.i4.3 + IL_0056: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_005b: dup + IL_005c: ldstr "Fifth case" + IL_0061: ldc.i4.4 + IL_0062: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_0067: dup + IL_0068: ldstr "Sixth case" + IL_006d: ldc.i4.5 + IL_006e: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_0073: dup + IL_0074: ldstr "Seventh case" + IL_0079: ldc.i4.6 + IL_007a: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_007f: dup + IL_0080: ldstr "Eighth case" + IL_0085: ldc.i4.7 + IL_0086: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_008b: dup + IL_008c: ldstr "Ninth case" + IL_0091: ldc.i4.8 + IL_0092: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_0097: dup + IL_0098: ldstr "Tenth case" + IL_009d: ldc.i4.s 9 + IL_009f: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_00a4: dup + IL_00a5: ldstr "Eleventh case" + IL_00aa: ldc.i4.s 10 + IL_00ac: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_00b1: volatile. + IL_00b3: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{5E65DF13-3C5E-44CA-97E7-5B58ACBAF9BF}'::'$$method0x600000c-1' + IL_00b8: volatile. + IL_00ba: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{5E65DF13-3C5E-44CA-97E7-5B58ACBAF9BF}'::'$$method0x600000c-1' + IL_00bf: ldloc.1 + IL_00c0: ldloca.s V_2 + IL_00c2: call instance bool class [mscorlib]System.Collections.Generic.Dictionary`2::TryGetValue(!0, + !1&) + IL_00c7: brfalse IL_0158 + + IL_00cc: ldloc.2 + IL_00cd: switch ( + IL_0100, + IL_0108, + IL_0110, + IL_0118, + IL_0120, + IL_0128, + IL_0130, + IL_0138, + IL_0140, + IL_0148, + IL_0150) + IL_00fe: br.s IL_0158 + + IL_0100: ldstr "Text1" + IL_0105: stloc.0 + IL_0106: br.s IL_0160 + + IL_0108: ldstr "Text2" + IL_010d: stloc.0 + IL_010e: br.s IL_0160 + + IL_0110: ldstr "Text3" + IL_0115: stloc.0 + IL_0116: br.s IL_0160 + + IL_0118: ldstr "Text4" + IL_011d: stloc.0 + IL_011e: br.s IL_0160 + + IL_0120: ldstr "Text5" + IL_0125: stloc.0 + IL_0126: br.s IL_0160 + + IL_0128: ldstr "Text6" + IL_012d: stloc.0 + IL_012e: br.s IL_0160 + + IL_0130: ldstr "Text7" + IL_0135: stloc.0 + IL_0136: br.s IL_0160 + + IL_0138: ldstr "Text8" + IL_013d: stloc.0 + IL_013e: br.s IL_0160 + + IL_0140: ldstr "Text9" + IL_0145: stloc.0 + IL_0146: br.s IL_0160 + + IL_0148: ldstr "Text10" + IL_014d: stloc.0 + IL_014e: br.s IL_0160 + + IL_0150: ldstr "Text11" + IL_0155: stloc.0 + IL_0156: br.s IL_0160 + + IL_0158: ldstr "Default" + IL_015d: stloc.0 + IL_015e: br.s IL_0160 + + IL_0160: ldloc.0 + IL_0161: ret + } // end of method Switch::SwitchOverString2 + + .method public hidebysig static string + SwitchOverBool(bool b) cil managed + { + // Code size 64 (0x40) + .maxstack 2 + .locals init (string V_0, + bool V_1) + IL_0000: nop + IL_0001: ldstr "SwitchOverBool: " + IL_0006: ldarga.s b + IL_0008: call instance string [mscorlib]System.Boolean::ToString() + IL_000d: call string [mscorlib]System.String::Concat(string, + string) + IL_0012: call void [mscorlib]System.Console::WriteLine(string) + IL_0017: nop + IL_0018: ldarg.0 + IL_0019: stloc.1 + IL_001a: ldloc.1 + IL_001b: switch ( + IL_0032, + IL_002a) + IL_0028: br.s IL_003a + + IL_002a: ldsfld string [mscorlib]System.Boolean::TrueString + IL_002f: stloc.0 + IL_0030: br.s IL_003e + + IL_0032: ldsfld string [mscorlib]System.Boolean::FalseString + IL_0037: stloc.0 + IL_0038: br.s IL_003e + + IL_003a: ldnull + IL_003b: stloc.0 + IL_003c: br.s IL_003e + + IL_003e: ldloc.0 + IL_003f: ret + } // end of method Switch::SwitchOverBool + + .method public hidebysig static void SwitchInLoop(int32 i) cil managed + { + // Code size 128 (0x80) + .maxstack 2 + .locals init (int32 V_0, + bool V_1) + IL_0000: nop + IL_0001: ldstr "SwitchInLoop: " + IL_0006: ldarg.0 + IL_0007: box [mscorlib]System.Int32 + IL_000c: call string [mscorlib]System.String::Concat(object, + object) + IL_0011: call void [mscorlib]System.Console::WriteLine(string) + IL_0016: nop + IL_0017: br.s IL_007b + + IL_0019: nop + IL_001a: ldarg.0 + IL_001b: stloc.0 + IL_001c: ldloc.0 + IL_001d: ldc.i4.1 + IL_001e: sub + IL_001f: switch ( + IL_0036, + IL_0043, + IL_005d, + IL_0050) + IL_0034: br.s IL_005d + + IL_0036: ldstr "one" + IL_003b: call void [mscorlib]System.Console::WriteLine(string) + IL_0040: nop + IL_0041: br.s IL_0075 + + IL_0043: ldstr "two" + IL_0048: call void [mscorlib]System.Console::WriteLine(string) + IL_004d: nop + IL_004e: br.s IL_0075 + + IL_0050: ldstr "four" + IL_0055: call void [mscorlib]System.Console::WriteLine(string) + IL_005a: nop + IL_005b: br.s IL_007f + + IL_005d: ldstr "default" + IL_0062: call void [mscorlib]System.Console::WriteLine(string) + IL_0067: nop + IL_0068: ldstr "more code" + IL_006d: call void [mscorlib]System.Console::WriteLine(string) + IL_0072: nop + IL_0073: br.s IL_007f + + IL_0075: ldarg.0 + IL_0076: ldc.i4.1 + IL_0077: add + IL_0078: starg.s i + IL_007a: nop + IL_007b: ldc.i4.1 + IL_007c: stloc.1 + IL_007d: br.s IL_0019 + + IL_007f: ret + } // end of method Switch::SwitchInLoop + + .method public hidebysig static void SwitchWithGoto(int32 i) cil managed + { + // Code size 128 (0x80) + .maxstack 2 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldstr "SwitchWithGoto: " + IL_0006: ldarg.0 + IL_0007: box [mscorlib]System.Int32 + IL_000c: call string [mscorlib]System.String::Concat(object, + object) + IL_0011: call void [mscorlib]System.Console::WriteLine(string) + IL_0016: nop + IL_0017: ldarg.0 + IL_0018: stloc.0 + IL_0019: ldloc.0 + IL_001a: ldc.i4.1 + IL_001b: sub + IL_001c: switch ( + IL_0033, + IL_0040, + IL_004d, + IL_005a) + IL_0031: br.s IL_0067 + + IL_0033: ldstr "one" + IL_0038: call void [mscorlib]System.Console::WriteLine(string) + IL_003d: nop + IL_003e: br.s IL_0067 + + IL_0040: ldstr "two" + IL_0045: call void [mscorlib]System.Console::WriteLine(string) + IL_004a: nop + IL_004b: br.s IL_004d + + IL_004d: ldstr "three" + IL_0052: call void [mscorlib]System.Console::WriteLine(string) + IL_0057: nop + IL_0058: br.s IL_0074 + + IL_005a: ldstr "four" + IL_005f: call void [mscorlib]System.Console::WriteLine(string) + IL_0064: nop + IL_0065: br.s IL_007f + + IL_0067: ldstr "default" + IL_006c: call void [mscorlib]System.Console::WriteLine(string) + IL_0071: nop + IL_0072: br.s IL_0074 + + IL_0074: ldstr "End of method" + IL_0079: call void [mscorlib]System.Console::WriteLine(string) + IL_007e: nop + IL_007f: ret + } // end of method Switch::SwitchWithGoto + + .method private hidebysig static class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty[] + GetProperties() cil managed + { + // Code size 12 (0xc) + .maxstack 1 + .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty[] V_0) + IL_0000: nop + IL_0001: ldc.i4.0 + IL_0002: newarr ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty + IL_0007: stloc.0 + IL_0008: br.s IL_000a + + IL_000a: ldloc.0 + IL_000b: ret + } // end of method Switch::GetProperties + + .method public hidebysig static void SwitchOnStringInForLoop() cil managed + { + // Code size 321 (0x141) + .maxstack 4 + .locals init (class [mscorlib]System.Collections.Generic.List`1 V_0, + class [mscorlib]System.Collections.Generic.List`1 V_1, + class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty[] V_2, + int32 V_3, + class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty V_4, + string V_5, + int32 V_6, + bool V_7) + IL_0000: nop + IL_0001: newobj instance void class [mscorlib]System.Collections.Generic.List`1::.ctor() + IL_0006: stloc.0 + IL_0007: newobj instance void class [mscorlib]System.Collections.Generic.List`1::.ctor() + IL_000c: stloc.1 + IL_000d: call class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty[] ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch::GetProperties() + IL_0012: stloc.2 + IL_0013: ldc.i4.0 + IL_0014: stloc.3 + IL_0015: br IL_0131 + + IL_001a: nop + IL_001b: ldloc.2 + IL_001c: ldloc.3 + IL_001d: ldelem.ref + IL_001e: stloc.s V_4 + IL_0020: ldloc.s V_4 + IL_0022: ldfld class [mscorlib]System.Reflection.PropertyInfo ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::Property + IL_0027: callvirt instance string [mscorlib]System.Reflection.MemberInfo::get_Name() + IL_002c: stloc.s V_5 + IL_002e: ldloc.s V_5 + IL_0030: brfalse IL_0121 + + IL_0035: volatile. + IL_0037: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{5E65DF13-3C5E-44CA-97E7-5B58ACBAF9BF}'::'$$method0x6000011-1' + IL_003c: brtrue.s IL_0093 + + IL_003e: ldc.i4.6 + IL_003f: newobj instance void class [mscorlib]System.Collections.Generic.Dictionary`2::.ctor(int32) + IL_0044: dup + IL_0045: ldstr "Name1" + IL_004a: ldc.i4.0 + IL_004b: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_0050: dup + IL_0051: ldstr "Name2" + IL_0056: ldc.i4.1 + IL_0057: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_005c: dup + IL_005d: ldstr "Name3" + IL_0062: ldc.i4.2 + IL_0063: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_0068: dup + IL_0069: ldstr "Name4" + IL_006e: ldc.i4.3 + IL_006f: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_0074: dup + IL_0075: ldstr "Name5" + IL_007a: ldc.i4.4 + IL_007b: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_0080: dup + IL_0081: ldstr "Name6" + IL_0086: ldc.i4.5 + IL_0087: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_008c: volatile. + IL_008e: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{5E65DF13-3C5E-44CA-97E7-5B58ACBAF9BF}'::'$$method0x6000011-1' + IL_0093: volatile. + IL_0095: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{5E65DF13-3C5E-44CA-97E7-5B58ACBAF9BF}'::'$$method0x6000011-1' + IL_009a: ldloc.s V_5 + IL_009c: ldloca.s V_6 + IL_009e: call instance bool class [mscorlib]System.Collections.Generic.Dictionary`2::TryGetValue(!0, + !1&) + IL_00a3: brfalse.s IL_0121 + + IL_00a5: ldloc.s V_6 + IL_00a7: switch ( + IL_00c6, + IL_00da, + IL_00ee, + IL_0102, + IL_0116, + IL_0116) + IL_00c4: br.s IL_0121 + + IL_00c6: ldloc.s V_4 + IL_00c8: ldc.i4.1 + IL_00c9: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + IL_00ce: nop + IL_00cf: ldloc.0 + IL_00d0: ldloc.s V_4 + IL_00d2: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_00d7: nop + IL_00d8: br.s IL_012c + + IL_00da: ldloc.s V_4 + IL_00dc: ldc.i4.2 + IL_00dd: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + IL_00e2: nop + IL_00e3: ldloc.0 + IL_00e4: ldloc.s V_4 + IL_00e6: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_00eb: nop + IL_00ec: br.s IL_012c + + IL_00ee: ldloc.s V_4 + IL_00f0: ldc.i4.3 + IL_00f1: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + IL_00f6: nop + IL_00f7: ldloc.0 + IL_00f8: ldloc.s V_4 + IL_00fa: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_00ff: nop + IL_0100: br.s IL_012c + + IL_0102: ldloc.s V_4 + IL_0104: ldc.i4.4 + IL_0105: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + IL_010a: nop + IL_010b: ldloc.0 + IL_010c: ldloc.s V_4 + IL_010e: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_0113: nop + IL_0114: br.s IL_012c + + IL_0116: ldloc.0 + IL_0117: ldloc.s V_4 + IL_0119: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_011e: nop + IL_011f: br.s IL_012c + + IL_0121: ldloc.1 + IL_0122: ldloc.s V_4 + IL_0124: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_0129: nop + IL_012a: br.s IL_012c + + IL_012c: nop + IL_012d: ldloc.3 + IL_012e: ldc.i4.1 + IL_012f: add + IL_0130: stloc.3 + IL_0131: ldloc.3 + IL_0132: ldloc.2 + IL_0133: ldlen + IL_0134: conv.i4 + IL_0135: clt + IL_0137: stloc.s V_7 + IL_0139: ldloc.s V_7 + IL_013b: brtrue IL_001a + + IL_0140: ret + } // end of method Switch::SwitchOnStringInForLoop + + .method public hidebysig static void SwitchWithComplexCondition(string[] args) cil managed + { + // Code size 139 (0x8b) + .maxstack 2 + .locals init (string V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldlen + IL_0003: conv.i4 + IL_0004: brfalse.s IL_000b + + IL_0006: ldarg.0 + IL_0007: ldc.i4.0 + IL_0008: ldelem.ref + IL_0009: br.s IL_0010 + + IL_000b: ldstr "dummy" + IL_0010: nop + IL_0011: stloc.0 + IL_0012: ldloc.0 + IL_0013: brfalse.s IL_007f + + IL_0015: ldloc.0 + IL_0016: ldstr "a" + IL_001b: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0020: brtrue.s IL_004b + + IL_0022: ldloc.0 + IL_0023: ldstr "b" + IL_0028: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_002d: brtrue.s IL_0058 + + IL_002f: ldloc.0 + IL_0030: ldstr "c" + IL_0035: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_003a: brtrue.s IL_0065 + + IL_003c: ldloc.0 + IL_003d: ldstr "d" + IL_0042: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0047: brtrue.s IL_0072 + + IL_0049: br.s IL_007f + + IL_004b: ldstr "a" + IL_0050: call void [mscorlib]System.Console::WriteLine(string) + IL_0055: nop + IL_0056: br.s IL_007f + + IL_0058: ldstr "b" + IL_005d: call void [mscorlib]System.Console::WriteLine(string) + IL_0062: nop + IL_0063: br.s IL_007f + + IL_0065: ldstr "c" + IL_006a: call void [mscorlib]System.Console::WriteLine(string) + IL_006f: nop + IL_0070: br.s IL_007f + + IL_0072: ldstr "d" + IL_0077: call void [mscorlib]System.Console::WriteLine(string) + IL_007c: nop + IL_007d: br.s IL_007f + + IL_007f: ldstr "end" + IL_0084: call void [mscorlib]System.Console::WriteLine(string) + IL_0089: nop + IL_008a: ret + } // end of method Switch::SwitchWithComplexCondition + + .method public hidebysig static void SwitchWithArray(string[] args) cil managed + { + // Code size 126 (0x7e) + .maxstack 2 + .locals init (string V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldc.i4.0 + IL_0003: ldelem.ref + IL_0004: stloc.0 + IL_0005: ldloc.0 + IL_0006: brfalse.s IL_0072 + + IL_0008: ldloc.0 + IL_0009: ldstr "a" + IL_000e: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0013: brtrue.s IL_003e + + IL_0015: ldloc.0 + IL_0016: ldstr "b" + IL_001b: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0020: brtrue.s IL_004b + + IL_0022: ldloc.0 + IL_0023: ldstr "c" + IL_0028: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_002d: brtrue.s IL_0058 + + IL_002f: ldloc.0 + IL_0030: ldstr "d" + IL_0035: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_003a: brtrue.s IL_0065 + + IL_003c: br.s IL_0072 + + IL_003e: ldstr "a" + IL_0043: call void [mscorlib]System.Console::WriteLine(string) + IL_0048: nop + IL_0049: br.s IL_0072 + + IL_004b: ldstr "b" + IL_0050: call void [mscorlib]System.Console::WriteLine(string) + IL_0055: nop + IL_0056: br.s IL_0072 + + IL_0058: ldstr "c" + IL_005d: call void [mscorlib]System.Console::WriteLine(string) + IL_0062: nop + IL_0063: br.s IL_0072 + + IL_0065: ldstr "d" + IL_006a: call void [mscorlib]System.Console::WriteLine(string) + IL_006f: nop + IL_0070: br.s IL_0072 + + IL_0072: ldstr "end" + IL_0077: call void [mscorlib]System.Console::WriteLine(string) + IL_007c: nop + IL_007d: ret + } // end of method Switch::SwitchWithArray + +} // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch + +.class private auto ansi '{5E65DF13-3C5E-44CA-97E7-5B58ACBAF9BF}' + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x600000b-1' + .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x600000c-1' + .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000011-1' +} // end of class '{5E65DF13-3C5E-44CA-97E7-5B58ACBAF9BF}' + + +// ============================================================= + +// *********** DISASSEMBLY COMPLETE *********************** +// Warnung: Win32-Ressourcendatei "../../../TestCases/Pretty\Switch.res" wurde erstellt. diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il new file mode 100644 index 000000000..44acd173c --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il @@ -0,0 +1,1306 @@ + +// Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0 +// Copyright (c) Microsoft Corporation. Alle Rechte vorbehalten. + + + +// Metadata version: v4.0.30319 +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} +.assembly jfao3dmb +{ + .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 + 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) + .permissionset reqmin + = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +.module jfao3dmb.dll +// MVID: {96F356C7-71A4-48B4-BE55-B48554E94654} +.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) +.imagebase 0x10000000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 // WINDOWS_CUI +.corflags 0x00000001 // ILONLY +// Image base: 0x01900000 + + +// =============== CLASS MEMBERS DECLARATION =================== + +.class public abstract auto ansi sealed beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch + extends [mscorlib]System.Object +{ + .class auto ansi nested public beforefieldinit SetProperty + extends [mscorlib]System.Object + { + .field public initonly class [mscorlib]System.Reflection.PropertyInfo Property + .field private int32 'k__BackingField' + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .method public hidebysig specialname + instance int32 get_Set() cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::'k__BackingField' + IL_0006: ret + } // end of method SetProperty::get_Set + + .method public hidebysig specialname + instance void set_Set(int32 'value') cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::'k__BackingField' + IL_0007: ret + } // end of method SetProperty::set_Set + + .method public hidebysig specialname rtspecialname + instance void .ctor(class [mscorlib]System.Reflection.PropertyInfo 'property') cil managed + { + // Code size 14 (0xe) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ldarg.0 + IL_0007: ldarg.1 + IL_0008: stfld class [mscorlib]System.Reflection.PropertyInfo ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::Property + IL_000d: ret + } // end of method SetProperty::.ctor + + .property instance int32 Set() + { + .get instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::get_Set() + .set instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + } // end of property SetProperty::Set + } // end of class SetProperty + + .method public hidebysig static string + SparseIntegerSwitch(int32 i) cil managed + { + // Code size 181 (0xb5) + .maxstack 2 + .locals init (int32 V_0) + IL_0000: ldstr "SparseIntegerSwitch: " + IL_0005: ldarg.0 + IL_0006: box [mscorlib]System.Int32 + IL_000b: call string [mscorlib]System.String::Concat(object, + object) + IL_0010: call void [mscorlib]System.Console::WriteLine(string) + IL_0015: ldarg.0 + IL_0016: stloc.0 + IL_0017: ldloc.0 + IL_0018: ldc.i4.4 + IL_0019: bgt.s IL_004a + + IL_001b: ldloc.0 + IL_001c: ldc.i4 0xff676980 + IL_0021: beq.s IL_006d + + IL_0023: ldloc.0 + IL_0024: ldc.i4.s -100 + IL_0026: beq.s IL_0073 + + IL_0028: ldloc.0 + IL_0029: ldc.i4.m1 + IL_002a: sub + IL_002b: switch ( + IL_0079, + IL_007f, + IL_0085, + IL_008b, + IL_00af, + IL_0091) + IL_0048: br.s IL_00af + + IL_004a: ldloc.0 + IL_004b: ldc.i4.s 100 + IL_004d: beq.s IL_0097 + + IL_004f: ldloc.0 + IL_0050: ldc.i4 0x2710 + IL_0055: sub + IL_0056: switch ( + IL_009d, + IL_00a3) + IL_0063: ldloc.0 + IL_0064: ldc.i4 0x7fffffff + IL_0069: beq.s IL_00a9 + + IL_006b: br.s IL_00af + + IL_006d: ldstr "-10 mln" + IL_0072: ret + + IL_0073: ldstr "-hundred" + IL_0078: ret + + IL_0079: ldstr "-1" + IL_007e: ret + + IL_007f: ldstr "0" + IL_0084: ret + + IL_0085: ldstr "1" + IL_008a: ret + + IL_008b: ldstr "2" + IL_0090: ret + + IL_0091: ldstr "4" + IL_0096: ret + + IL_0097: ldstr "hundred" + IL_009c: ret + + IL_009d: ldstr "ten thousand" + IL_00a2: ret + + IL_00a3: ldstr "ten thousand and one" + IL_00a8: ret + + IL_00a9: ldstr "int.MaxValue" + IL_00ae: ret + + IL_00af: ldstr "something else" + IL_00b4: ret + } // end of method Switch::SparseIntegerSwitch + + .method public hidebysig static string + SwitchOverNullableInt(valuetype [mscorlib]System.Nullable`1 i) cil managed + { + // Code size 61 (0x3d) + .maxstack 2 + .locals init (int32 V_0) + IL_0000: ldarga.s i + IL_0002: dup + IL_0003: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() + IL_0008: stloc.0 + IL_0009: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue() + IL_000e: brfalse.s IL_001f + + IL_0010: ldloc.0 + IL_0011: ldc.i4.0 + IL_0012: beq.s IL_0025 + + IL_0014: ldloc.0 + IL_0015: ldc.i4.5 + IL_0016: beq.s IL_002b + + IL_0018: ldloc.0 + IL_0019: ldc.i4.s 10 + IL_001b: beq.s IL_0031 + + IL_001d: br.s IL_0037 + + IL_001f: ldstr "null" + IL_0024: ret + + IL_0025: ldstr "zero" + IL_002a: ret + + IL_002b: ldstr "five" + IL_0030: ret + + IL_0031: ldstr "ten" + IL_0036: ret + + IL_0037: ldstr "large" + IL_003c: ret + } // end of method Switch::SwitchOverNullableInt + + .method public hidebysig static string + SwitchOverNullableIntNullCaseCombined(valuetype [mscorlib]System.Nullable`1 i) cil managed + { + // Code size 55 (0x37) + .maxstack 2 + .locals init (int32 V_0) + IL_0000: ldarga.s i + IL_0002: dup + IL_0003: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() + IL_0008: stloc.0 + IL_0009: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue() + IL_000e: brfalse.s IL_001f + + IL_0010: ldloc.0 + IL_0011: ldc.i4.0 + IL_0012: beq.s IL_001f + + IL_0014: ldloc.0 + IL_0015: ldc.i4.5 + IL_0016: beq.s IL_0025 + + IL_0018: ldloc.0 + IL_0019: ldc.i4.s 10 + IL_001b: beq.s IL_002b + + IL_001d: br.s IL_0031 + + IL_001f: ldstr "zero" + IL_0024: ret + + IL_0025: ldstr "five" + IL_002a: ret + + IL_002b: ldstr "ten" + IL_0030: ret + + IL_0031: ldstr "large" + IL_0036: ret + } // end of method Switch::SwitchOverNullableIntNullCaseCombined + + .method public hidebysig static string + SwitchOverNullableIntShifted(valuetype [mscorlib]System.Nullable`1 i) cil managed + { + // Code size 98 (0x62) + .maxstack 2 + .locals init (valuetype [mscorlib]System.Nullable`1 V_0, + valuetype [mscorlib]System.Nullable`1 V_1, + valuetype [mscorlib]System.Nullable`1 V_2, + int32 V_3) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue() + IL_0009: brtrue.s IL_0016 + + IL_000b: ldloca.s V_1 + IL_000d: initobj valuetype [mscorlib]System.Nullable`1 + IL_0013: ldloc.1 + IL_0014: br.s IL_0024 + + IL_0016: ldloca.s V_0 + IL_0018: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() + IL_001d: ldc.i4.5 + IL_001e: add + IL_001f: newobj instance void valuetype [mscorlib]System.Nullable`1::.ctor(!0) + IL_0024: stloc.2 + IL_0025: ldloca.s V_2 + IL_0027: dup + IL_0028: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() + IL_002d: stloc.3 + IL_002e: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue() + IL_0033: brfalse.s IL_0044 + + IL_0035: ldloc.3 + IL_0036: ldc.i4.0 + IL_0037: beq.s IL_004a + + IL_0039: ldloc.3 + IL_003a: ldc.i4.5 + IL_003b: beq.s IL_0050 + + IL_003d: ldloc.3 + IL_003e: ldc.i4.s 10 + IL_0040: beq.s IL_0056 + + IL_0042: br.s IL_005c + + IL_0044: ldstr "null" + IL_0049: ret + + IL_004a: ldstr "zero" + IL_004f: ret + + IL_0050: ldstr "five" + IL_0055: ret + + IL_0056: ldstr "ten" + IL_005b: ret + + IL_005c: ldstr "large" + IL_0061: ret + } // end of method Switch::SwitchOverNullableIntShifted + + .method public hidebysig static string + SwitchOverNullableIntShiftedNullCaseCombined(valuetype [mscorlib]System.Nullable`1 i) cil managed + { + // Code size 92 (0x5c) + .maxstack 2 + .locals init (valuetype [mscorlib]System.Nullable`1 V_0, + valuetype [mscorlib]System.Nullable`1 V_1, + valuetype [mscorlib]System.Nullable`1 V_2, + int32 V_3) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue() + IL_0009: brtrue.s IL_0016 + + IL_000b: ldloca.s V_1 + IL_000d: initobj valuetype [mscorlib]System.Nullable`1 + IL_0013: ldloc.1 + IL_0014: br.s IL_0024 + + IL_0016: ldloca.s V_0 + IL_0018: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() + IL_001d: ldc.i4.5 + IL_001e: add + IL_001f: newobj instance void valuetype [mscorlib]System.Nullable`1::.ctor(!0) + IL_0024: stloc.2 + IL_0025: ldloca.s V_2 + IL_0027: dup + IL_0028: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() + IL_002d: stloc.3 + IL_002e: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue() + IL_0033: brfalse.s IL_0044 + + IL_0035: ldloc.3 + IL_0036: ldc.i4.0 + IL_0037: beq.s IL_0044 + + IL_0039: ldloc.3 + IL_003a: ldc.i4.5 + IL_003b: beq.s IL_004a + + IL_003d: ldloc.3 + IL_003e: ldc.i4.s 10 + IL_0040: beq.s IL_0050 + + IL_0042: br.s IL_0056 + + IL_0044: ldstr "zero" + IL_0049: ret + + IL_004a: ldstr "five" + IL_004f: ret + + IL_0050: ldstr "ten" + IL_0055: ret + + IL_0056: ldstr "large" + IL_005b: ret + } // end of method Switch::SwitchOverNullableIntShiftedNullCaseCombined + + .method public hidebysig static string + SwitchOverNullableIntNoNullCase(valuetype [mscorlib]System.Nullable`1 i) cil managed + { + // Code size 55 (0x37) + .maxstack 2 + .locals init (int32 V_0) + IL_0000: ldarga.s i + IL_0002: dup + IL_0003: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() + IL_0008: stloc.0 + IL_0009: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue() + IL_000e: brfalse.s IL_0031 + + IL_0010: ldloc.0 + IL_0011: ldc.i4.0 + IL_0012: beq.s IL_001f + + IL_0014: ldloc.0 + IL_0015: ldc.i4.5 + IL_0016: beq.s IL_0025 + + IL_0018: ldloc.0 + IL_0019: ldc.i4.s 10 + IL_001b: beq.s IL_002b + + IL_001d: br.s IL_0031 + + IL_001f: ldstr "zero" + IL_0024: ret + + IL_0025: ldstr "five" + IL_002a: ret + + IL_002b: ldstr "ten" + IL_0030: ret + + IL_0031: ldstr "other" + IL_0036: ret + } // end of method Switch::SwitchOverNullableIntNoNullCase + + .method public hidebysig static string + SwitchOverNullableIntNoNullCaseShifted(valuetype [mscorlib]System.Nullable`1 i) cil managed + { + // Code size 92 (0x5c) + .maxstack 2 + .locals init (valuetype [mscorlib]System.Nullable`1 V_0, + valuetype [mscorlib]System.Nullable`1 V_1, + valuetype [mscorlib]System.Nullable`1 V_2, + int32 V_3) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue() + IL_0009: brtrue.s IL_0016 + + IL_000b: ldloca.s V_1 + IL_000d: initobj valuetype [mscorlib]System.Nullable`1 + IL_0013: ldloc.1 + IL_0014: br.s IL_0024 + + IL_0016: ldloca.s V_0 + IL_0018: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() + IL_001d: ldc.i4.5 + IL_001e: add + IL_001f: newobj instance void valuetype [mscorlib]System.Nullable`1::.ctor(!0) + IL_0024: stloc.2 + IL_0025: ldloca.s V_2 + IL_0027: dup + IL_0028: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() + IL_002d: stloc.3 + IL_002e: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue() + IL_0033: brfalse.s IL_0056 + + IL_0035: ldloc.3 + IL_0036: ldc.i4.0 + IL_0037: beq.s IL_0044 + + IL_0039: ldloc.3 + IL_003a: ldc.i4.5 + IL_003b: beq.s IL_004a + + IL_003d: ldloc.3 + IL_003e: ldc.i4.s 10 + IL_0040: beq.s IL_0050 + + IL_0042: br.s IL_0056 + + IL_0044: ldstr "zero" + IL_0049: ret + + IL_004a: ldstr "five" + IL_004f: ret + + IL_0050: ldstr "ten" + IL_0055: ret + + IL_0056: ldstr "other" + IL_005b: ret + } // end of method Switch::SwitchOverNullableIntNoNullCaseShifted + + .method public hidebysig static void SwitchOverInt(int32 i) cil managed + { + // Code size 125 (0x7d) + .maxstack 2 + .locals init (int32 V_0) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: ldc.i4.s 10 + IL_0005: bgt.s IL_0015 + + IL_0007: ldloc.0 + IL_0008: ldc.i4.0 + IL_0009: beq.s IL_0030 + + IL_000b: ldloc.0 + IL_000c: ldc.i4.5 + IL_000d: beq.s IL_003b + + IL_000f: ldloc.0 + IL_0010: ldc.i4.s 10 + IL_0012: beq.s IL_0046 + + IL_0014: ret + + IL_0015: ldloc.0 + IL_0016: ldc.i4.s 20 + IL_0018: bgt.s IL_0025 + + IL_001a: ldloc.0 + IL_001b: ldc.i4.s 15 + IL_001d: beq.s IL_0051 + + IL_001f: ldloc.0 + IL_0020: ldc.i4.s 20 + IL_0022: beq.s IL_005c + + IL_0024: ret + + IL_0025: ldloc.0 + IL_0026: ldc.i4.s 25 + IL_0028: beq.s IL_0067 + + IL_002a: ldloc.0 + IL_002b: ldc.i4.s 30 + IL_002d: beq.s IL_0072 + + IL_002f: ret + + IL_0030: ldstr "zero" + IL_0035: call void [mscorlib]System.Console::WriteLine(string) + IL_003a: ret + + IL_003b: ldstr "five" + IL_0040: call void [mscorlib]System.Console::WriteLine(string) + IL_0045: ret + + IL_0046: ldstr "ten" + IL_004b: call void [mscorlib]System.Console::WriteLine(string) + IL_0050: ret + + IL_0051: ldstr "fifteen" + IL_0056: call void [mscorlib]System.Console::WriteLine(string) + IL_005b: ret + + IL_005c: ldstr "twenty" + IL_0061: call void [mscorlib]System.Console::WriteLine(string) + IL_0066: ret + + IL_0067: ldstr "twenty-five" + IL_006c: call void [mscorlib]System.Console::WriteLine(string) + IL_0071: ret + + IL_0072: ldstr "thirty" + IL_0077: call void [mscorlib]System.Console::WriteLine(string) + IL_007c: ret + } // end of method Switch::SwitchOverInt + + .method public hidebysig static string + ShortSwitchOverString(string text) cil managed + { + // Code size 86 (0x56) + .maxstack 2 + .locals init (string V_0) + IL_0000: ldstr "ShortSwitchOverString: " + 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.s IL_0050 + + IL_0015: ldloc.0 + IL_0016: ldstr "First case" + IL_001b: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0020: brtrue.s IL_003e + + IL_0022: ldloc.0 + IL_0023: ldstr "Second case" + IL_0028: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_002d: brtrue.s IL_0044 + + IL_002f: ldloc.0 + IL_0030: ldstr "Third case" + IL_0035: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_003a: brtrue.s IL_004a + + IL_003c: br.s IL_0050 + + IL_003e: ldstr "Text1" + IL_0043: ret + + IL_0044: ldstr "Text2" + IL_0049: ret + + IL_004a: ldstr "Text3" + IL_004f: ret + + IL_0050: ldstr "Default" + IL_0055: ret + } // end of method Switch::ShortSwitchOverString + + .method public hidebysig static string + ShortSwitchOverStringWithNullCase(string text) cil managed + { + // Code size 73 (0x49) + .maxstack 2 + .locals init (string V_0) + IL_0000: ldstr "ShortSwitchOverStringWithNullCase: " + 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.s IL_003d + + IL_0015: ldloc.0 + IL_0016: ldstr "First case" + IL_001b: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0020: brtrue.s IL_0031 + + IL_0022: ldloc.0 + IL_0023: ldstr "Second case" + IL_0028: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_002d: brtrue.s IL_0037 + + IL_002f: br.s IL_0043 + + IL_0031: ldstr "Text1" + IL_0036: ret + + IL_0037: ldstr "Text2" + IL_003c: ret + + IL_003d: ldstr "null" + IL_0042: ret + + IL_0043: ldstr "Default" + IL_0048: ret + } // end of method Switch::ShortSwitchOverStringWithNullCase + + .method public hidebysig static string + SwitchOverString1(string text) cil managed + { + // Code size 227 (0xe3) + .maxstack 4 + .locals init (string V_0, + int32 V_1) + IL_0000: ldstr "SwitchOverString1: " + 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_00db + + IL_0018: volatile. + IL_001a: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{96F356C7-71A4-48B4-BE55-B48554E94654}'::'$$method0x600000b-1' + IL_001f: brtrue.s IL_0082 + + IL_0021: ldc.i4.7 + IL_0022: newobj instance void class [mscorlib]System.Collections.Generic.Dictionary`2::.ctor(int32) + IL_0027: dup + IL_0028: ldstr "First case" + 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 "Second case" + 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 "2nd case" + 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 "Third case" + 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 "Fourth case" + 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 "Fifth case" + 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 "Sixth case" + IL_0075: ldc.i4.6 + IL_0076: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_007b: volatile. + IL_007d: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{96F356C7-71A4-48B4-BE55-B48554E94654}'::'$$method0x600000b-1' + IL_0082: volatile. + IL_0084: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{96F356C7-71A4-48B4-BE55-B48554E94654}'::'$$method0x600000b-1' + IL_0089: ldloc.0 + IL_008a: ldloca.s V_1 + IL_008c: call instance bool class [mscorlib]System.Collections.Generic.Dictionary`2::TryGetValue(!0, + !1&) + IL_0091: brfalse.s IL_00dd + + IL_0093: ldloc.1 + IL_0094: switch ( + IL_00b7, + IL_00bd, + IL_00bd, + IL_00c3, + IL_00c9, + IL_00cf, + IL_00d5) + IL_00b5: br.s IL_00dd + + IL_00b7: ldstr "Text1" + IL_00bc: ret + + IL_00bd: ldstr "Text2" + IL_00c2: ret + + IL_00c3: ldstr "Text3" + IL_00c8: ret + + IL_00c9: ldstr "Text4" + IL_00ce: ret + + IL_00cf: ldstr "Text5" + IL_00d4: ret + + IL_00d5: ldstr "Text6" + IL_00da: ret + + IL_00db: ldnull + IL_00dc: ret + + IL_00dd: ldstr "Default" + IL_00e2: ret + } // end of method Switch::SwitchOverString1 + + .method public hidebysig static string + SwitchOverString2() cil managed + { + // Code size 323 (0x143) + .maxstack 4 + .locals init (string V_0, + int32 V_1) + IL_0000: ldstr "SwitchOverString2:" + IL_0005: call void [mscorlib]System.Console::WriteLine(string) + IL_000a: call string [mscorlib]System.Environment::get_UserName() + IL_000f: dup + IL_0010: stloc.0 + IL_0011: brfalse IL_013d + + IL_0016: volatile. + IL_0018: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{96F356C7-71A4-48B4-BE55-B48554E94654}'::'$$method0x600000c-1' + IL_001d: brtrue IL_00b6 + + IL_0022: ldc.i4.s 11 + IL_0024: newobj instance void class [mscorlib]System.Collections.Generic.Dictionary`2::.ctor(int32) + IL_0029: dup + IL_002a: ldstr "First case" + 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 "Second case" + 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 "Third case" + 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 "Fourth case" + 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 "Fifth case" + 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 "Sixth case" + 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 "Seventh case" + 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 "Eighth case" + IL_0083: ldc.i4.7 + IL_0084: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_0089: dup + IL_008a: ldstr "Ninth case" + IL_008f: ldc.i4.8 + IL_0090: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_0095: dup + IL_0096: ldstr "Tenth case" + IL_009b: ldc.i4.s 9 + IL_009d: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_00a2: dup + IL_00a3: ldstr "Eleventh case" + IL_00a8: ldc.i4.s 10 + IL_00aa: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_00af: volatile. + IL_00b1: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{96F356C7-71A4-48B4-BE55-B48554E94654}'::'$$method0x600000c-1' + IL_00b6: volatile. + IL_00b8: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{96F356C7-71A4-48B4-BE55-B48554E94654}'::'$$method0x600000c-1' + IL_00bd: ldloc.0 + IL_00be: ldloca.s V_1 + IL_00c0: call instance bool class [mscorlib]System.Collections.Generic.Dictionary`2::TryGetValue(!0, + !1&) + IL_00c5: brfalse.s IL_013d + + IL_00c7: ldloc.1 + IL_00c8: switch ( + IL_00fb, + IL_0101, + IL_0107, + IL_010d, + IL_0113, + IL_0119, + IL_011f, + IL_0125, + IL_012b, + IL_0131, + IL_0137) + IL_00f9: br.s IL_013d + + IL_00fb: ldstr "Text1" + IL_0100: ret + + IL_0101: ldstr "Text2" + IL_0106: ret + + IL_0107: ldstr "Text3" + IL_010c: ret + + IL_010d: ldstr "Text4" + IL_0112: ret + + IL_0113: ldstr "Text5" + IL_0118: ret + + IL_0119: ldstr "Text6" + IL_011e: ret + + IL_011f: ldstr "Text7" + IL_0124: ret + + IL_0125: ldstr "Text8" + IL_012a: ret + + IL_012b: ldstr "Text9" + IL_0130: ret + + IL_0131: ldstr "Text10" + IL_0136: ret + + IL_0137: ldstr "Text11" + IL_013c: ret + + IL_013d: ldstr "Default" + IL_0142: ret + } // end of method Switch::SwitchOverString2 + + .method public hidebysig static string + SwitchOverBool(bool b) cil managed + { + // Code size 54 (0x36) + .maxstack 2 + .locals init (bool V_0) + IL_0000: ldstr "SwitchOverBool: " + IL_0005: ldarga.s b + IL_0007: call instance string [mscorlib]System.Boolean::ToString() + IL_000c: call string [mscorlib]System.String::Concat(string, + string) + IL_0011: call void [mscorlib]System.Console::WriteLine(string) + IL_0016: ldarg.0 + IL_0017: stloc.0 + IL_0018: ldloc.0 + IL_0019: switch ( + IL_002e, + IL_0028) + IL_0026: br.s IL_0034 + + IL_0028: ldsfld string [mscorlib]System.Boolean::TrueString + IL_002d: ret + + IL_002e: ldsfld string [mscorlib]System.Boolean::FalseString + IL_0033: ret + + IL_0034: ldnull + IL_0035: ret + } // end of method Switch::SwitchOverBool + + .method public hidebysig static void SwitchInLoop(int32 i) cil managed + { + // Code size 112 (0x70) + .maxstack 2 + .locals init (int32 V_0) + IL_0000: ldstr "SwitchInLoop: " + IL_0005: ldarg.0 + IL_0006: box [mscorlib]System.Int32 + IL_000b: call string [mscorlib]System.String::Concat(object, + object) + IL_0010: call void [mscorlib]System.Console::WriteLine(string) + IL_0015: ldarg.0 + IL_0016: stloc.0 + IL_0017: ldloc.0 + IL_0018: ldc.i4.1 + IL_0019: sub + IL_001a: switch ( + IL_0031, + IL_003d, + IL_0054, + IL_0049) + IL_002f: br.s IL_0054 + + IL_0031: ldstr "one" + IL_0036: call void [mscorlib]System.Console::WriteLine(string) + IL_003b: br.s IL_0069 + + IL_003d: ldstr "two" + IL_0042: call void [mscorlib]System.Console::WriteLine(string) + IL_0047: br.s IL_0069 + + IL_0049: ldstr "four" + IL_004e: call void [mscorlib]System.Console::WriteLine(string) + IL_0053: ret + + IL_0054: ldstr "default" + IL_0059: call void [mscorlib]System.Console::WriteLine(string) + IL_005e: ldstr "more code" + IL_0063: call void [mscorlib]System.Console::WriteLine(string) + IL_0068: ret + + IL_0069: ldarg.0 + IL_006a: ldc.i4.1 + IL_006b: add + IL_006c: starg.s i + IL_006e: br.s IL_0015 + } // end of method Switch::SwitchInLoop + + .method public hidebysig static void SwitchWithGoto(int32 i) cil managed + { + // Code size 115 (0x73) + .maxstack 2 + .locals init (int32 V_0) + IL_0000: ldstr "SwitchWithGoto: " + IL_0005: ldarg.0 + IL_0006: box [mscorlib]System.Int32 + IL_000b: call string [mscorlib]System.String::Concat(object, + object) + IL_0010: call void [mscorlib]System.Console::WriteLine(string) + IL_0015: ldarg.0 + IL_0016: stloc.0 + IL_0017: ldloc.0 + IL_0018: ldc.i4.1 + IL_0019: sub + IL_001a: switch ( + IL_0031, + IL_003d, + IL_0047, + IL_0053) + IL_002f: br.s IL_005e + + IL_0031: ldstr "one" + IL_0036: call void [mscorlib]System.Console::WriteLine(string) + IL_003b: br.s IL_005e + + IL_003d: ldstr "two" + IL_0042: call void [mscorlib]System.Console::WriteLine(string) + IL_0047: ldstr "three" + IL_004c: call void [mscorlib]System.Console::WriteLine(string) + IL_0051: br.s IL_0068 + + IL_0053: ldstr "four" + IL_0058: call void [mscorlib]System.Console::WriteLine(string) + IL_005d: ret + + IL_005e: ldstr "default" + IL_0063: call void [mscorlib]System.Console::WriteLine(string) + IL_0068: ldstr "End of method" + IL_006d: call void [mscorlib]System.Console::WriteLine(string) + IL_0072: ret + } // end of method Switch::SwitchWithGoto + + .method private hidebysig static class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty[] + GetProperties() cil managed + { + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldc.i4.0 + IL_0001: newarr ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty + IL_0006: ret + } // end of method Switch::GetProperties + + .method public hidebysig static void SwitchOnStringInForLoop() cil managed + { + // Code size 299 (0x12b) + .maxstack 4 + .locals init (class [mscorlib]System.Collections.Generic.List`1 V_0, + class [mscorlib]System.Collections.Generic.List`1 V_1, + class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty[] V_2, + int32 V_3, + class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty V_4, + string V_5, + int32 V_6) + IL_0000: newobj instance void class [mscorlib]System.Collections.Generic.List`1::.ctor() + IL_0005: stloc.0 + IL_0006: newobj instance void class [mscorlib]System.Collections.Generic.List`1::.ctor() + IL_000b: stloc.1 + IL_000c: call class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty[] ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch::GetProperties() + IL_0011: stloc.2 + IL_0012: ldc.i4.0 + IL_0013: stloc.3 + IL_0014: br IL_0121 + + IL_0019: ldloc.2 + IL_001a: ldloc.3 + IL_001b: ldelem.ref + IL_001c: stloc.s V_4 + IL_001e: ldloc.s V_4 + IL_0020: ldfld class [mscorlib]System.Reflection.PropertyInfo ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::Property + IL_0025: callvirt instance string [mscorlib]System.Reflection.MemberInfo::get_Name() + IL_002a: dup + IL_002b: stloc.s V_5 + IL_002d: brfalse IL_0115 + + IL_0032: volatile. + IL_0034: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{96F356C7-71A4-48B4-BE55-B48554E94654}'::'$$method0x6000011-1' + IL_0039: brtrue.s IL_0090 + + IL_003b: ldc.i4.6 + IL_003c: newobj instance void class [mscorlib]System.Collections.Generic.Dictionary`2::.ctor(int32) + IL_0041: dup + IL_0042: ldstr "Name1" + IL_0047: ldc.i4.0 + IL_0048: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_004d: dup + IL_004e: ldstr "Name2" + IL_0053: ldc.i4.1 + IL_0054: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_0059: dup + IL_005a: ldstr "Name3" + IL_005f: ldc.i4.2 + IL_0060: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_0065: dup + IL_0066: ldstr "Name4" + IL_006b: ldc.i4.3 + IL_006c: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_0071: dup + IL_0072: ldstr "Name5" + IL_0077: ldc.i4.4 + IL_0078: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_007d: dup + IL_007e: ldstr "Name6" + IL_0083: ldc.i4.5 + 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 '{96F356C7-71A4-48B4-BE55-B48554E94654}'::'$$method0x6000011-1' + IL_0090: volatile. + IL_0092: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{96F356C7-71A4-48B4-BE55-B48554E94654}'::'$$method0x6000011-1' + IL_0097: ldloc.s V_5 + IL_0099: ldloca.s V_6 + IL_009b: call instance bool class [mscorlib]System.Collections.Generic.Dictionary`2::TryGetValue(!0, + !1&) + IL_00a0: brfalse.s IL_0115 + + IL_00a2: ldloc.s V_6 + IL_00a4: switch ( + IL_00c3, + IL_00d5, + IL_00e7, + IL_00f9, + IL_010b, + IL_010b) + IL_00c1: br.s IL_0115 + + IL_00c3: ldloc.s V_4 + IL_00c5: ldc.i4.1 + IL_00c6: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + IL_00cb: ldloc.0 + IL_00cc: ldloc.s V_4 + IL_00ce: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_00d3: br.s IL_011d + + IL_00d5: ldloc.s V_4 + IL_00d7: ldc.i4.2 + IL_00d8: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + IL_00dd: ldloc.0 + IL_00de: ldloc.s V_4 + IL_00e0: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_00e5: br.s IL_011d + + IL_00e7: ldloc.s V_4 + IL_00e9: ldc.i4.3 + IL_00ea: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + IL_00ef: ldloc.0 + IL_00f0: ldloc.s V_4 + IL_00f2: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_00f7: br.s IL_011d + + IL_00f9: ldloc.s V_4 + IL_00fb: ldc.i4.4 + IL_00fc: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + IL_0101: ldloc.0 + IL_0102: ldloc.s V_4 + IL_0104: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_0109: br.s IL_011d + + IL_010b: ldloc.0 + IL_010c: ldloc.s V_4 + IL_010e: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_0113: br.s IL_011d + + IL_0115: ldloc.1 + IL_0116: ldloc.s V_4 + IL_0118: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_011d: ldloc.3 + IL_011e: ldc.i4.1 + IL_011f: add + IL_0120: stloc.3 + IL_0121: ldloc.3 + IL_0122: ldloc.2 + IL_0123: ldlen + IL_0124: conv.i4 + IL_0125: blt IL_0019 + + IL_012a: ret + } // end of method Switch::SwitchOnStringInForLoop + + .method public hidebysig static void SwitchWithComplexCondition(string[] args) cil managed + { + // Code size 130 (0x82) + .maxstack 2 + .locals init (string V_0) + IL_0000: ldarg.0 + IL_0001: ldlen + IL_0002: conv.i4 + IL_0003: brfalse.s IL_000a + + IL_0005: ldarg.0 + IL_0006: ldc.i4.0 + IL_0007: ldelem.ref + IL_0008: br.s IL_000f + + IL_000a: ldstr "dummy" + IL_000f: dup + IL_0010: stloc.0 + IL_0011: brfalse.s IL_0077 + + IL_0013: ldloc.0 + IL_0014: ldstr "a" + IL_0019: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_001e: brtrue.s IL_0049 + + IL_0020: ldloc.0 + IL_0021: ldstr "b" + IL_0026: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_002b: brtrue.s IL_0055 + + IL_002d: ldloc.0 + IL_002e: ldstr "c" + IL_0033: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0038: brtrue.s IL_0061 + + IL_003a: ldloc.0 + IL_003b: ldstr "d" + IL_0040: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0045: brtrue.s IL_006d + + IL_0047: br.s IL_0077 + + IL_0049: ldstr "a" + IL_004e: call void [mscorlib]System.Console::WriteLine(string) + IL_0053: br.s IL_0077 + + IL_0055: ldstr "b" + IL_005a: call void [mscorlib]System.Console::WriteLine(string) + IL_005f: br.s IL_0077 + + IL_0061: ldstr "c" + IL_0066: call void [mscorlib]System.Console::WriteLine(string) + IL_006b: br.s IL_0077 + + IL_006d: ldstr "d" + IL_0072: call void [mscorlib]System.Console::WriteLine(string) + IL_0077: ldstr "end" + IL_007c: call void [mscorlib]System.Console::WriteLine(string) + IL_0081: ret + } // end of method Switch::SwitchWithComplexCondition + + .method public hidebysig static void SwitchWithArray(string[] args) cil managed + { + // Code size 118 (0x76) + .maxstack 2 + .locals init (string V_0) + IL_0000: ldarg.0 + IL_0001: ldc.i4.0 + IL_0002: ldelem.ref + IL_0003: dup + IL_0004: stloc.0 + IL_0005: brfalse.s IL_006b + + IL_0007: ldloc.0 + IL_0008: ldstr "a" + IL_000d: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0012: brtrue.s IL_003d + + IL_0014: ldloc.0 + IL_0015: ldstr "b" + IL_001a: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_001f: brtrue.s IL_0049 + + IL_0021: ldloc.0 + IL_0022: ldstr "c" + IL_0027: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_002c: brtrue.s IL_0055 + + IL_002e: ldloc.0 + IL_002f: ldstr "d" + IL_0034: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0039: brtrue.s IL_0061 + + IL_003b: br.s IL_006b + + IL_003d: ldstr "a" + IL_0042: call void [mscorlib]System.Console::WriteLine(string) + IL_0047: br.s IL_006b + + IL_0049: ldstr "b" + IL_004e: call void [mscorlib]System.Console::WriteLine(string) + IL_0053: br.s IL_006b + + IL_0055: ldstr "c" + IL_005a: call void [mscorlib]System.Console::WriteLine(string) + IL_005f: br.s IL_006b + + IL_0061: ldstr "d" + IL_0066: call void [mscorlib]System.Console::WriteLine(string) + IL_006b: ldstr "end" + IL_0070: call void [mscorlib]System.Console::WriteLine(string) + IL_0075: ret + } // end of method Switch::SwitchWithArray + +} // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch + +.class private auto ansi '{96F356C7-71A4-48B4-BE55-B48554E94654}' + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x600000b-1' + .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x600000c-1' + .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000011-1' +} // end of class '{96F356C7-71A4-48B4-BE55-B48554E94654}' + + +// ============================================================= + +// *********** DISASSEMBLY COMPLETE *********************** +// Warnung: Win32-Ressourcendatei "../../../TestCases/Pretty\Switch.opt.res" wurde erstellt. diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il new file mode 100644 index 000000000..3c4b7ec5b --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il @@ -0,0 +1,1414 @@ + +// Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0 +// Copyright (c) Microsoft Corporation. Alle Rechte vorbehalten. + + + +// Metadata version: v4.0.30319 +.assembly extern mscorlib +{ + .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 ) + .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 + 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. + + // --- The following custom attribute is added automatically, do not uncomment ------- + // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 02 00 00 00 00 00 ) + + .permissionset reqmin + = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +.module Switch.dll +// MVID: {4366DF41-DCD0-42E1-B99D-7B67787ECEA9} +.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) +.imagebase 0x10000000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 // WINDOWS_CUI +.corflags 0x00000001 // ILONLY +// Image base: 0x00690000 + + +// =============== CLASS MEMBERS DECLARATION =================== + +.class public abstract auto ansi sealed beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch + extends [mscorlib]System.Object +{ + .class auto ansi nested public beforefieldinit SetProperty + extends [mscorlib]System.Object + { + .field public initonly class [mscorlib]System.Reflection.PropertyInfo Property + .field private int32 'k__BackingField' + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .method public hidebysig specialname + instance int32 get_Set() cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::'k__BackingField' + IL_0006: ret + } // end of method SetProperty::get_Set + + .method public hidebysig specialname + instance void set_Set(int32 'value') cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::'k__BackingField' + IL_0007: ret + } // end of method SetProperty::set_Set + + .method public hidebysig specialname rtspecialname + instance void .ctor(class [mscorlib]System.Reflection.PropertyInfo 'property') cil managed + { + // Code size 14 (0xe) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ldarg.0 + IL_0007: ldarg.1 + IL_0008: stfld class [mscorlib]System.Reflection.PropertyInfo ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::Property + IL_000d: ret + } // end of method SetProperty::.ctor + + .property instance int32 Set() + { + .get instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::get_Set() + .set instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + } // end of property SetProperty::Set + } // end of class SetProperty + + .method public hidebysig static string + SparseIntegerSwitch(int32 i) cil managed + { + // Code size 185 (0xb9) + .maxstack 2 + IL_0000: ldstr "SparseIntegerSwitch: " + IL_0005: ldarg.0 + IL_0006: box [mscorlib]System.Int32 + IL_000b: call string [mscorlib]System.String::Concat(object, + object) + IL_0010: call void [mscorlib]System.Console::WriteLine(string) + IL_0015: ldarg.0 + IL_0016: ldc.i4.4 + IL_0017: bgt.s IL_0048 + + IL_0019: ldarg.0 + IL_001a: ldc.i4 0xff676980 + IL_001f: beq.s IL_0071 + + IL_0021: ldarg.0 + IL_0022: ldc.i4.s -100 + IL_0024: beq.s IL_0077 + + IL_0026: ldarg.0 + IL_0027: ldc.i4.m1 + IL_0028: sub + IL_0029: switch ( + IL_007d, + IL_0083, + IL_0089, + IL_008f, + IL_00b3, + IL_0095) + IL_0046: br.s IL_00b3 + + IL_0048: ldarg.0 + IL_0049: ldc.i4 0x2710 + IL_004e: bgt.s IL_005f + + IL_0050: ldarg.0 + IL_0051: ldc.i4.s 100 + IL_0053: beq.s IL_009b + + IL_0055: ldarg.0 + IL_0056: ldc.i4 0x2710 + IL_005b: beq.s IL_00a1 + + IL_005d: br.s IL_00b3 + + IL_005f: ldarg.0 + IL_0060: ldc.i4 0x2711 + IL_0065: beq.s IL_00a7 + + IL_0067: ldarg.0 + IL_0068: ldc.i4 0x7fffffff + IL_006d: beq.s IL_00ad + + IL_006f: br.s IL_00b3 + + IL_0071: ldstr "-10 mln" + IL_0076: ret + + IL_0077: ldstr "-hundred" + IL_007c: ret + + IL_007d: ldstr "-1" + IL_0082: ret + + IL_0083: ldstr "0" + IL_0088: ret + + IL_0089: ldstr "1" + IL_008e: ret + + IL_008f: ldstr "2" + IL_0094: ret + + IL_0095: ldstr "4" + IL_009a: ret + + IL_009b: ldstr "hundred" + IL_00a0: ret + + IL_00a1: ldstr "ten thousand" + IL_00a6: ret + + IL_00a7: ldstr "ten thousand and one" + IL_00ac: ret + + IL_00ad: ldstr "int.MaxValue" + IL_00b2: ret + + IL_00b3: ldstr "something else" + IL_00b8: ret + } // end of method Switch::SparseIntegerSwitch + + .method public hidebysig static string + SwitchOverNullableInt(valuetype [mscorlib]System.Nullable`1 i) cil managed + { + // Code size 63 (0x3f) + .maxstack 2 + .locals init (valuetype [mscorlib]System.Nullable`1 V_0, + int32 V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue() + IL_0009: brfalse.s IL_0021 + + IL_000b: ldloca.s V_0 + IL_000d: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() + IL_0012: stloc.1 + IL_0013: ldloc.1 + IL_0014: brfalse.s IL_0027 + + IL_0016: ldloc.1 + IL_0017: ldc.i4.5 + IL_0018: beq.s IL_002d + + IL_001a: ldloc.1 + IL_001b: ldc.i4.s 10 + IL_001d: beq.s IL_0033 + + IL_001f: br.s IL_0039 + + IL_0021: ldstr "null" + IL_0026: ret + + IL_0027: ldstr "zero" + IL_002c: ret + + IL_002d: ldstr "five" + IL_0032: ret + + IL_0033: ldstr "ten" + IL_0038: ret + + IL_0039: ldstr "large" + IL_003e: ret + } // end of method Switch::SwitchOverNullableInt + + .method public hidebysig static string + SwitchOverNullableIntNullCaseCombined(valuetype [mscorlib]System.Nullable`1 i) cil managed + { + // Code size 57 (0x39) + .maxstack 2 + .locals init (valuetype [mscorlib]System.Nullable`1 V_0, + int32 V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue() + IL_0009: brfalse.s IL_0021 + + IL_000b: ldloca.s V_0 + IL_000d: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() + IL_0012: stloc.1 + IL_0013: ldloc.1 + IL_0014: brfalse.s IL_0021 + + IL_0016: ldloc.1 + IL_0017: ldc.i4.5 + IL_0018: beq.s IL_0027 + + IL_001a: ldloc.1 + IL_001b: ldc.i4.s 10 + IL_001d: beq.s IL_002d + + IL_001f: br.s IL_0033 + + IL_0021: ldstr "zero" + IL_0026: ret + + IL_0027: ldstr "five" + IL_002c: ret + + IL_002d: ldstr "ten" + IL_0032: ret + + IL_0033: ldstr "large" + IL_0038: ret + } // end of method Switch::SwitchOverNullableIntNullCaseCombined + + .method public hidebysig static string + SwitchOverNullableIntShifted(valuetype [mscorlib]System.Nullable`1 i) cil managed + { + // Code size 98 (0x62) + .maxstack 2 + .locals init (valuetype [mscorlib]System.Nullable`1 V_0, + valuetype [mscorlib]System.Nullable`1 V_1, + valuetype [mscorlib]System.Nullable`1 V_2, + int32 V_3) + IL_0000: ldarg.0 + IL_0001: stloc.1 + IL_0002: ldloca.s V_1 + IL_0004: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue() + IL_0009: brtrue.s IL_0016 + + IL_000b: ldloca.s V_2 + IL_000d: initobj valuetype [mscorlib]System.Nullable`1 + IL_0013: ldloc.2 + IL_0014: br.s IL_0024 + + IL_0016: ldloca.s V_1 + IL_0018: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() + IL_001d: ldc.i4.5 + IL_001e: add + IL_001f: newobj instance void valuetype [mscorlib]System.Nullable`1::.ctor(!0) + IL_0024: stloc.0 + IL_0025: ldloca.s V_0 + IL_0027: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue() + IL_002c: brfalse.s IL_0044 + + IL_002e: ldloca.s V_0 + IL_0030: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() + IL_0035: stloc.3 + IL_0036: ldloc.3 + IL_0037: brfalse.s IL_004a + + IL_0039: ldloc.3 + IL_003a: ldc.i4.5 + IL_003b: beq.s IL_0050 + + IL_003d: ldloc.3 + IL_003e: ldc.i4.s 10 + IL_0040: beq.s IL_0056 + + IL_0042: br.s IL_005c + + IL_0044: ldstr "null" + IL_0049: ret + + IL_004a: ldstr "zero" + IL_004f: ret + + IL_0050: ldstr "five" + IL_0055: ret + + IL_0056: ldstr "ten" + IL_005b: ret + + IL_005c: ldstr "large" + IL_0061: ret + } // end of method Switch::SwitchOverNullableIntShifted + + .method public hidebysig static string + SwitchOverNullableIntShiftedNullCaseCombined(valuetype [mscorlib]System.Nullable`1 i) cil managed + { + // Code size 92 (0x5c) + .maxstack 2 + .locals init (valuetype [mscorlib]System.Nullable`1 V_0, + valuetype [mscorlib]System.Nullable`1 V_1, + valuetype [mscorlib]System.Nullable`1 V_2, + int32 V_3) + IL_0000: ldarg.0 + IL_0001: stloc.1 + IL_0002: ldloca.s V_1 + IL_0004: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue() + IL_0009: brtrue.s IL_0016 + + IL_000b: ldloca.s V_2 + IL_000d: initobj valuetype [mscorlib]System.Nullable`1 + IL_0013: ldloc.2 + IL_0014: br.s IL_0024 + + IL_0016: ldloca.s V_1 + IL_0018: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() + IL_001d: ldc.i4.5 + IL_001e: add + IL_001f: newobj instance void valuetype [mscorlib]System.Nullable`1::.ctor(!0) + IL_0024: stloc.0 + IL_0025: ldloca.s V_0 + IL_0027: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue() + IL_002c: brfalse.s IL_0044 + + IL_002e: ldloca.s V_0 + IL_0030: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() + IL_0035: stloc.3 + IL_0036: ldloc.3 + IL_0037: brfalse.s IL_0044 + + IL_0039: ldloc.3 + IL_003a: ldc.i4.5 + IL_003b: beq.s IL_004a + + IL_003d: ldloc.3 + IL_003e: ldc.i4.s 10 + IL_0040: beq.s IL_0050 + + IL_0042: br.s IL_0056 + + IL_0044: ldstr "zero" + IL_0049: ret + + IL_004a: ldstr "five" + IL_004f: ret + + IL_0050: ldstr "ten" + IL_0055: ret + + IL_0056: ldstr "large" + IL_005b: ret + } // end of method Switch::SwitchOverNullableIntShiftedNullCaseCombined + + .method public hidebysig static string + SwitchOverNullableIntNoNullCase(valuetype [mscorlib]System.Nullable`1 i) cil managed + { + // Code size 57 (0x39) + .maxstack 2 + .locals init (valuetype [mscorlib]System.Nullable`1 V_0, + int32 V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue() + IL_0009: brfalse.s IL_0033 + + IL_000b: ldloca.s V_0 + IL_000d: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() + IL_0012: stloc.1 + IL_0013: ldloc.1 + IL_0014: brfalse.s IL_0021 + + IL_0016: ldloc.1 + IL_0017: ldc.i4.5 + IL_0018: beq.s IL_0027 + + IL_001a: ldloc.1 + IL_001b: ldc.i4.s 10 + IL_001d: beq.s IL_002d + + IL_001f: br.s IL_0033 + + IL_0021: ldstr "zero" + IL_0026: ret + + IL_0027: ldstr "five" + IL_002c: ret + + IL_002d: ldstr "ten" + IL_0032: ret + + IL_0033: ldstr "other" + IL_0038: ret + } // end of method Switch::SwitchOverNullableIntNoNullCase + + .method public hidebysig static string + SwitchOverNullableIntNoNullCaseShifted(valuetype [mscorlib]System.Nullable`1 i) cil managed + { + // Code size 92 (0x5c) + .maxstack 2 + .locals init (valuetype [mscorlib]System.Nullable`1 V_0, + valuetype [mscorlib]System.Nullable`1 V_1, + valuetype [mscorlib]System.Nullable`1 V_2, + int32 V_3) + IL_0000: ldarg.0 + IL_0001: stloc.1 + IL_0002: ldloca.s V_1 + IL_0004: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue() + IL_0009: brtrue.s IL_0016 + + IL_000b: ldloca.s V_2 + IL_000d: initobj valuetype [mscorlib]System.Nullable`1 + IL_0013: ldloc.2 + IL_0014: br.s IL_0024 + + IL_0016: ldloca.s V_1 + IL_0018: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() + IL_001d: ldc.i4.5 + IL_001e: add + IL_001f: newobj instance void valuetype [mscorlib]System.Nullable`1::.ctor(!0) + IL_0024: stloc.0 + IL_0025: ldloca.s V_0 + IL_0027: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue() + IL_002c: brfalse.s IL_0056 + + IL_002e: ldloca.s V_0 + IL_0030: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() + IL_0035: stloc.3 + IL_0036: ldloc.3 + IL_0037: brfalse.s IL_0044 + + IL_0039: ldloc.3 + IL_003a: ldc.i4.5 + IL_003b: beq.s IL_004a + + IL_003d: ldloc.3 + IL_003e: ldc.i4.s 10 + IL_0040: beq.s IL_0050 + + IL_0042: br.s IL_0056 + + IL_0044: ldstr "zero" + IL_0049: ret + + IL_004a: ldstr "five" + IL_004f: ret + + IL_0050: ldstr "ten" + IL_0055: ret + + IL_0056: ldstr "other" + IL_005b: ret + } // end of method Switch::SwitchOverNullableIntNoNullCaseShifted + + .method public hidebysig static void SwitchOverInt(int32 i) cil managed + { + // Code size 122 (0x7a) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldc.i4.s 10 + IL_0003: bgt.s IL_0012 + + IL_0005: ldarg.0 + IL_0006: brfalse.s IL_002d + + IL_0008: ldarg.0 + IL_0009: ldc.i4.5 + IL_000a: beq.s IL_0038 + + IL_000c: ldarg.0 + IL_000d: ldc.i4.s 10 + IL_000f: beq.s IL_0043 + + IL_0011: ret + + IL_0012: ldarg.0 + IL_0013: ldc.i4.s 20 + IL_0015: bgt.s IL_0022 + + IL_0017: ldarg.0 + IL_0018: ldc.i4.s 15 + IL_001a: beq.s IL_004e + + IL_001c: ldarg.0 + IL_001d: ldc.i4.s 20 + IL_001f: beq.s IL_0059 + + IL_0021: ret + + IL_0022: ldarg.0 + IL_0023: ldc.i4.s 25 + IL_0025: beq.s IL_0064 + + IL_0027: ldarg.0 + IL_0028: ldc.i4.s 30 + IL_002a: beq.s IL_006f + + IL_002c: ret + + IL_002d: ldstr "zero" + IL_0032: call void [mscorlib]System.Console::WriteLine(string) + IL_0037: ret + + IL_0038: ldstr "five" + IL_003d: call void [mscorlib]System.Console::WriteLine(string) + IL_0042: ret + + IL_0043: ldstr "ten" + IL_0048: call void [mscorlib]System.Console::WriteLine(string) + IL_004d: ret + + IL_004e: ldstr "fifteen" + IL_0053: call void [mscorlib]System.Console::WriteLine(string) + IL_0058: ret + + IL_0059: ldstr "twenty" + IL_005e: call void [mscorlib]System.Console::WriteLine(string) + IL_0063: ret + + IL_0064: ldstr "twenty-five" + IL_0069: call void [mscorlib]System.Console::WriteLine(string) + IL_006e: ret + + IL_006f: ldstr "thirty" + IL_0074: call void [mscorlib]System.Console::WriteLine(string) + IL_0079: ret + } // end of method Switch::SwitchOverInt + + .method public hidebysig static string + ShortSwitchOverString(string text) cil managed + { + // Code size 81 (0x51) + .maxstack 2 + IL_0000: ldstr "ShortSwitchOverString: " + 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: ldstr "First case" + IL_0016: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_001b: brtrue.s IL_0039 + + IL_001d: ldarg.0 + IL_001e: ldstr "Second case" + IL_0023: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0028: brtrue.s IL_003f + + IL_002a: ldarg.0 + IL_002b: ldstr "Third case" + IL_0030: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0035: brtrue.s IL_0045 + + IL_0037: br.s IL_004b + + IL_0039: ldstr "Text1" + IL_003e: ret + + IL_003f: ldstr "Text2" + IL_0044: ret + + IL_0045: ldstr "Text3" + IL_004a: ret + + IL_004b: ldstr "Default" + IL_0050: ret + } // end of method Switch::ShortSwitchOverString + + .method public hidebysig static string + ShortSwitchOverStringWithNullCase(string text) cil managed + { + // Code size 71 (0x47) + .maxstack 2 + IL_0000: ldstr "ShortSwitchOverStringWithNullCase: " + 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: ldstr "First case" + IL_0016: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_001b: brtrue.s IL_002f + + IL_001d: ldarg.0 + IL_001e: ldstr "Second case" + IL_0023: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0028: brtrue.s IL_0035 + + IL_002a: ldarg.0 + IL_002b: brfalse.s IL_003b + + IL_002d: br.s IL_0041 + + IL_002f: ldstr "Text1" + IL_0034: ret + + IL_0035: ldstr "Text2" + IL_003a: ret + + IL_003b: ldstr "null" + IL_0040: ret + + IL_0041: ldstr "Default" + IL_0046: ret + } // end of method Switch::ShortSwitchOverStringWithNullCase + + .method public hidebysig static string + SwitchOverString1(string text) cil managed + { + // Code size 289 (0x121) + .maxstack 2 + .locals init (uint32 V_0) + IL_0000: ldstr "SwitchOverString1: " + 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 0xf3d44a6 + IL_001d: bgt.un.s IL_0052 + + IL_001f: ldloc.0 + IL_0020: ldc.i4 0x8861b86 + IL_0025: bgt.un.s IL_003d + + IL_0027: ldloc.0 + IL_0028: brfalse IL_00f0 + + IL_002d: ldloc.0 + IL_002e: ldc.i4 0x8861b86 + IL_0033: beq IL_00d2 + + IL_0038: br IL_011b + + IL_003d: ldloc.0 + IL_003e: ldc.i4 0xc9a8f4f + IL_0043: beq.s IL_0084 + + IL_0045: ldloc.0 + IL_0046: ldc.i4 0xf3d44a6 + IL_004b: beq.s IL_00b4 + + IL_004d: br IL_011b + + IL_0052: ldloc.0 + IL_0053: ldc.i4 0x652a1179 + IL_0058: bgt.un.s IL_006f + + IL_005a: ldloc.0 + IL_005b: ldc.i4 0x51650fb9 + IL_0060: beq.s IL_00e1 + + IL_0062: ldloc.0 + IL_0063: ldc.i4 0x652a1179 + IL_0068: beq.s IL_00a5 + + IL_006a: br IL_011b + + IL_006f: ldloc.0 + IL_0070: ldc.i4 0xea3d096b + IL_0075: beq.s IL_0096 + + IL_0077: ldloc.0 + IL_0078: ldc.i4 0xf701cc7f + IL_007d: beq.s IL_00c3 + + IL_007f: br IL_011b + + IL_0084: ldarg.0 + IL_0085: ldstr "First case" + IL_008a: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_008f: brtrue.s IL_00f5 + + IL_0091: br IL_011b + + IL_0096: ldarg.0 + IL_0097: ldstr "Second case" + IL_009c: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_00a1: brtrue.s IL_00fb + + IL_00a3: br.s IL_011b + + IL_00a5: ldarg.0 + IL_00a6: ldstr "2nd case" + IL_00ab: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_00b0: brtrue.s IL_00fb + + IL_00b2: br.s IL_011b + + IL_00b4: ldarg.0 + IL_00b5: ldstr "Third case" + IL_00ba: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_00bf: brtrue.s IL_0101 + + IL_00c1: br.s IL_011b + + IL_00c3: ldarg.0 + IL_00c4: ldstr "Fourth case" + IL_00c9: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_00ce: brtrue.s IL_0107 + + IL_00d0: br.s IL_011b + + IL_00d2: ldarg.0 + IL_00d3: ldstr "Fifth case" + IL_00d8: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_00dd: brtrue.s IL_010d + + IL_00df: br.s IL_011b + + IL_00e1: ldarg.0 + IL_00e2: ldstr "Sixth case" + IL_00e7: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_00ec: brtrue.s IL_0113 + + IL_00ee: br.s IL_011b + + IL_00f0: ldarg.0 + IL_00f1: brfalse.s IL_0119 + + IL_00f3: br.s IL_011b + + IL_00f5: ldstr "Text1" + IL_00fa: ret + + IL_00fb: ldstr "Text2" + IL_0100: ret + + IL_0101: ldstr "Text3" + IL_0106: ret + + IL_0107: ldstr "Text4" + IL_010c: ret + + IL_010d: ldstr "Text5" + IL_0112: ret + + IL_0113: ldstr "Text6" + IL_0118: ret + + IL_0119: ldnull + IL_011a: ret + + IL_011b: ldstr "Default" + IL_0120: ret + } // end of method Switch::SwitchOverString1 + + .method public hidebysig static string + SwitchOverString2() cil managed + { + // Code size 446 (0x1be) + .maxstack 2 + .locals init (string V_0, + uint32 V_1) + IL_0000: ldstr "SwitchOverString2:" + IL_0005: call void [mscorlib]System.Console::WriteLine(string) + IL_000a: call string [mscorlib]System.Environment::get_UserName() + IL_000f: stloc.0 + IL_0010: ldloc.0 + IL_0011: call uint32 ''::ComputeStringHash(string) + IL_0016: stloc.1 + IL_0017: ldloc.1 + IL_0018: ldc.i4 0x4c7c71f6 + IL_001d: bgt.un.s IL_0065 + + IL_001f: ldloc.1 + IL_0020: ldc.i4 0xc9a8f4f + IL_0025: bgt.un.s IL_003f + + IL_0027: ldloc.1 + IL_0028: ldc.i4 0x8861b86 + IL_002d: beq IL_0107 + + IL_0032: ldloc.1 + IL_0033: ldc.i4 0xc9a8f4f + IL_0038: beq.s IL_00b3 + + IL_003a: br IL_01b8 + + IL_003f: ldloc.1 + IL_0040: ldc.i4 0xf3d44a6 + IL_0045: beq IL_00dd + + IL_004a: ldloc.1 + IL_004b: ldc.i4 0x20289804 + IL_0050: beq IL_013a + + IL_0055: ldloc.1 + IL_0056: ldc.i4 0x4c7c71f6 + IL_005b: beq IL_0149 + + IL_0060: br IL_01b8 + + IL_0065: ldloc.1 + IL_0066: ldc.i4 0xa151b28a + IL_006b: bgt.un.s IL_0093 + + IL_006d: ldloc.1 + IL_006e: ldc.i4 0x4d0cea48 + IL_0073: beq IL_0167 + + IL_0078: ldloc.1 + IL_0079: ldc.i4 0x51650fb9 + IL_007e: beq IL_0119 + + IL_0083: ldloc.1 + IL_0084: ldc.i4 0xa151b28a + IL_0089: beq IL_012b + + IL_008e: br IL_01b8 + + IL_0093: ldloc.1 + IL_0094: ldc.i4 0xea3d096b + IL_0099: beq.s IL_00c8 + + IL_009b: ldloc.1 + IL_009c: ldc.i4 0xed5134d4 + IL_00a1: beq IL_0158 + + IL_00a6: ldloc.1 + IL_00a7: ldc.i4 0xf701cc7f + IL_00ac: beq.s IL_00f2 + + IL_00ae: br IL_01b8 + + IL_00b3: ldloc.0 + IL_00b4: ldstr "First case" + IL_00b9: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_00be: brtrue IL_0176 + + IL_00c3: br IL_01b8 + + IL_00c8: ldloc.0 + IL_00c9: ldstr "Second case" + IL_00ce: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_00d3: brtrue IL_017c + + IL_00d8: br IL_01b8 + + IL_00dd: ldloc.0 + IL_00de: ldstr "Third case" + IL_00e3: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_00e8: brtrue IL_0182 + + IL_00ed: br IL_01b8 + + IL_00f2: ldloc.0 + IL_00f3: ldstr "Fourth case" + IL_00f8: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_00fd: brtrue IL_0188 + + IL_0102: br IL_01b8 + + IL_0107: ldloc.0 + IL_0108: ldstr "Fifth case" + IL_010d: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0112: brtrue.s IL_018e + + IL_0114: br IL_01b8 + + IL_0119: ldloc.0 + IL_011a: ldstr "Sixth case" + IL_011f: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0124: brtrue.s IL_0194 + + IL_0126: br IL_01b8 + + IL_012b: ldloc.0 + IL_012c: ldstr "Seventh case" + IL_0131: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0136: brtrue.s IL_019a + + IL_0138: br.s IL_01b8 + + IL_013a: ldloc.0 + IL_013b: ldstr "Eighth case" + IL_0140: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0145: brtrue.s IL_01a0 + + IL_0147: br.s IL_01b8 + + IL_0149: ldloc.0 + IL_014a: ldstr "Ninth case" + IL_014f: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0154: brtrue.s IL_01a6 + + IL_0156: br.s IL_01b8 + + IL_0158: ldloc.0 + IL_0159: ldstr "Tenth case" + IL_015e: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0163: brtrue.s IL_01ac + + IL_0165: br.s IL_01b8 + + IL_0167: ldloc.0 + IL_0168: ldstr "Eleventh case" + IL_016d: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0172: brtrue.s IL_01b2 + + IL_0174: br.s IL_01b8 + + IL_0176: ldstr "Text1" + IL_017b: ret + + IL_017c: ldstr "Text2" + IL_0181: ret + + IL_0182: ldstr "Text3" + IL_0187: ret + + IL_0188: ldstr "Text4" + IL_018d: ret + + IL_018e: ldstr "Text5" + IL_0193: ret + + IL_0194: ldstr "Text6" + IL_0199: ret + + IL_019a: ldstr "Text7" + IL_019f: ret + + IL_01a0: ldstr "Text8" + IL_01a5: ret + + IL_01a6: ldstr "Text9" + IL_01ab: ret + + IL_01ac: ldstr "Text10" + IL_01b1: ret + + IL_01b2: ldstr "Text11" + IL_01b7: ret + + IL_01b8: ldstr "Default" + IL_01bd: ret + } // end of method Switch::SwitchOverString2 + + .method public hidebysig static string + SwitchOverBool(bool b) cil managed + { + // Code size 43 (0x2b) + .maxstack 8 + IL_0000: ldstr "SwitchOverBool: " + IL_0005: ldarga.s b + IL_0007: call instance string [mscorlib]System.Boolean::ToString() + IL_000c: call string [mscorlib]System.String::Concat(string, + string) + IL_0011: call void [mscorlib]System.Console::WriteLine(string) + IL_0016: ldarg.0 + IL_0017: brfalse.s IL_0023 + + IL_0019: ldarg.0 + IL_001a: ldc.i4.1 + IL_001b: bne.un.s IL_0029 + + IL_001d: ldsfld string [mscorlib]System.Boolean::TrueString + IL_0022: ret + + IL_0023: ldsfld string [mscorlib]System.Boolean::FalseString + IL_0028: ret + + IL_0029: ldnull + IL_002a: ret + } // end of method Switch::SwitchOverBool + + .method public hidebysig static void SwitchInLoop(int32 i) cil managed + { + // Code size 110 (0x6e) + .maxstack 2 + IL_0000: ldstr "SwitchInLoop: " + IL_0005: ldarg.0 + IL_0006: box [mscorlib]System.Int32 + IL_000b: call string [mscorlib]System.String::Concat(object, + object) + IL_0010: call void [mscorlib]System.Console::WriteLine(string) + IL_0015: ldarg.0 + IL_0016: ldc.i4.1 + IL_0017: sub + IL_0018: switch ( + IL_002f, + IL_003b, + IL_0052, + IL_0047) + IL_002d: br.s IL_0052 + + IL_002f: ldstr "one" + IL_0034: call void [mscorlib]System.Console::WriteLine(string) + IL_0039: br.s IL_0067 + + IL_003b: ldstr "two" + IL_0040: call void [mscorlib]System.Console::WriteLine(string) + IL_0045: br.s IL_0067 + + IL_0047: ldstr "four" + IL_004c: call void [mscorlib]System.Console::WriteLine(string) + IL_0051: ret + + IL_0052: ldstr "default" + IL_0057: call void [mscorlib]System.Console::WriteLine(string) + IL_005c: ldstr "more code" + IL_0061: call void [mscorlib]System.Console::WriteLine(string) + IL_0066: ret + + IL_0067: ldarg.0 + IL_0068: ldc.i4.1 + IL_0069: add + IL_006a: starg.s i + IL_006c: br.s IL_0015 + } // end of method Switch::SwitchInLoop + + .method public hidebysig static void SwitchWithGoto(int32 i) cil managed + { + // Code size 113 (0x71) + .maxstack 2 + IL_0000: ldstr "SwitchWithGoto: " + IL_0005: ldarg.0 + IL_0006: box [mscorlib]System.Int32 + IL_000b: call string [mscorlib]System.String::Concat(object, + object) + IL_0010: call void [mscorlib]System.Console::WriteLine(string) + IL_0015: ldarg.0 + IL_0016: ldc.i4.1 + IL_0017: sub + IL_0018: switch ( + IL_002f, + IL_003b, + IL_0045, + IL_0051) + IL_002d: br.s IL_005c + + IL_002f: ldstr "one" + IL_0034: call void [mscorlib]System.Console::WriteLine(string) + IL_0039: br.s IL_005c + + IL_003b: ldstr "two" + IL_0040: call void [mscorlib]System.Console::WriteLine(string) + IL_0045: ldstr "three" + IL_004a: call void [mscorlib]System.Console::WriteLine(string) + IL_004f: br.s IL_0066 + + IL_0051: ldstr "four" + IL_0056: call void [mscorlib]System.Console::WriteLine(string) + IL_005b: ret + + IL_005c: ldstr "default" + IL_0061: call void [mscorlib]System.Console::WriteLine(string) + IL_0066: ldstr "End of method" + IL_006b: call void [mscorlib]System.Console::WriteLine(string) + IL_0070: ret + } // end of method Switch::SwitchWithGoto + + .method private hidebysig static class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty[] + GetProperties() cil managed + { + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldc.i4.0 + IL_0001: newarr ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty + IL_0006: ret + } // end of method Switch::GetProperties + + .method public hidebysig static void SwitchOnStringInForLoop() cil managed + { + // Code size 234 (0xea) + .maxstack 2 + .locals init (class [mscorlib]System.Collections.Generic.List`1 V_0, + class [mscorlib]System.Collections.Generic.List`1 V_1, + class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty[] V_2, + int32 V_3, + class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty V_4, + string V_5) + IL_0000: newobj instance void class [mscorlib]System.Collections.Generic.List`1::.ctor() + IL_0005: stloc.0 + IL_0006: newobj instance void class [mscorlib]System.Collections.Generic.List`1::.ctor() + IL_000b: stloc.1 + IL_000c: call class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty[] ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch::GetProperties() + IL_0011: stloc.2 + IL_0012: ldc.i4.0 + IL_0013: stloc.3 + IL_0014: br IL_00e0 + + IL_0019: ldloc.2 + IL_001a: ldloc.3 + IL_001b: ldelem.ref + IL_001c: stloc.s V_4 + IL_001e: ldloc.s V_4 + IL_0020: ldfld class [mscorlib]System.Reflection.PropertyInfo ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::Property + IL_0025: callvirt instance string [mscorlib]System.Reflection.MemberInfo::get_Name() + IL_002a: stloc.s V_5 + IL_002c: ldloc.s V_5 + IL_002e: ldstr "Name1" + IL_0033: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0038: brtrue.s IL_0082 + + IL_003a: ldloc.s V_5 + IL_003c: ldstr "Name2" + IL_0041: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0046: brtrue.s IL_0094 + + IL_0048: ldloc.s V_5 + IL_004a: ldstr "Name3" + IL_004f: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0054: brtrue.s IL_00a6 + + IL_0056: ldloc.s V_5 + IL_0058: ldstr "Name4" + IL_005d: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0062: brtrue.s IL_00b8 + + IL_0064: ldloc.s V_5 + IL_0066: ldstr "Name5" + IL_006b: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0070: brtrue.s IL_00ca + + IL_0072: ldloc.s V_5 + IL_0074: ldstr "Name6" + IL_0079: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_007e: brtrue.s IL_00ca + + IL_0080: br.s IL_00d4 + + IL_0082: ldloc.s V_4 + IL_0084: ldc.i4.1 + IL_0085: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + IL_008a: ldloc.0 + IL_008b: ldloc.s V_4 + IL_008d: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_0092: br.s IL_00dc + + IL_0094: ldloc.s V_4 + IL_0096: ldc.i4.2 + IL_0097: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + IL_009c: ldloc.0 + IL_009d: ldloc.s V_4 + IL_009f: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_00a4: br.s IL_00dc + + IL_00a6: ldloc.s V_4 + IL_00a8: ldc.i4.3 + IL_00a9: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + IL_00ae: ldloc.0 + IL_00af: ldloc.s V_4 + IL_00b1: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_00b6: br.s IL_00dc + + IL_00b8: ldloc.s V_4 + IL_00ba: ldc.i4.4 + IL_00bb: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + IL_00c0: ldloc.0 + IL_00c1: ldloc.s V_4 + IL_00c3: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_00c8: br.s IL_00dc + + IL_00ca: ldloc.0 + IL_00cb: ldloc.s V_4 + IL_00cd: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_00d2: br.s IL_00dc + + IL_00d4: ldloc.1 + IL_00d5: ldloc.s V_4 + IL_00d7: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_00dc: ldloc.3 + IL_00dd: ldc.i4.1 + IL_00de: add + IL_00df: stloc.3 + IL_00e0: ldloc.3 + IL_00e1: ldloc.2 + IL_00e2: ldlen + IL_00e3: conv.i4 + IL_00e4: blt IL_0019 + + IL_00e9: ret + } // end of method Switch::SwitchOnStringInForLoop + + .method public hidebysig static void SwitchWithComplexCondition(string[] args) cil managed + { + // Code size 126 (0x7e) + .maxstack 2 + .locals init (string V_0) + IL_0000: ldarg.0 + IL_0001: ldlen + IL_0002: brfalse.s IL_0009 + + IL_0004: ldarg.0 + IL_0005: ldc.i4.0 + IL_0006: ldelem.ref + IL_0007: br.s IL_000e + + IL_0009: ldstr "dummy" + IL_000e: stloc.0 + IL_000f: ldloc.0 + IL_0010: ldstr "a" + IL_0015: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_001a: brtrue.s IL_0045 + + IL_001c: ldloc.0 + IL_001d: ldstr "b" + IL_0022: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0027: brtrue.s IL_0051 + + IL_0029: ldloc.0 + IL_002a: ldstr "c" + IL_002f: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0034: brtrue.s IL_005d + + IL_0036: ldloc.0 + IL_0037: ldstr "d" + IL_003c: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0041: brtrue.s IL_0069 + + IL_0043: br.s IL_0073 + + IL_0045: ldstr "a" + IL_004a: call void [mscorlib]System.Console::WriteLine(string) + IL_004f: br.s IL_0073 + + IL_0051: ldstr "b" + IL_0056: call void [mscorlib]System.Console::WriteLine(string) + IL_005b: br.s IL_0073 + + IL_005d: ldstr "c" + IL_0062: call void [mscorlib]System.Console::WriteLine(string) + IL_0067: br.s IL_0073 + + IL_0069: ldstr "d" + IL_006e: call void [mscorlib]System.Console::WriteLine(string) + IL_0073: ldstr "end" + IL_0078: call void [mscorlib]System.Console::WriteLine(string) + IL_007d: ret + } // end of method Switch::SwitchWithComplexCondition + + .method public hidebysig static void SwitchWithArray(string[] args) cil managed + { + // Code size 115 (0x73) + .maxstack 2 + .locals init (string V_0) + IL_0000: ldarg.0 + IL_0001: ldc.i4.0 + IL_0002: ldelem.ref + IL_0003: stloc.0 + IL_0004: ldloc.0 + IL_0005: ldstr "a" + IL_000a: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_000f: brtrue.s IL_003a + + IL_0011: ldloc.0 + IL_0012: ldstr "b" + IL_0017: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_001c: brtrue.s IL_0046 + + IL_001e: ldloc.0 + IL_001f: ldstr "c" + IL_0024: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0029: brtrue.s IL_0052 + + IL_002b: ldloc.0 + IL_002c: ldstr "d" + IL_0031: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0036: brtrue.s IL_005e + + IL_0038: br.s IL_0068 + + IL_003a: ldstr "a" + IL_003f: call void [mscorlib]System.Console::WriteLine(string) + IL_0044: br.s IL_0068 + + IL_0046: ldstr "b" + IL_004b: call void [mscorlib]System.Console::WriteLine(string) + IL_0050: br.s IL_0068 + + IL_0052: ldstr "c" + IL_0057: call void [mscorlib]System.Console::WriteLine(string) + IL_005c: br.s IL_0068 + + IL_005e: ldstr "d" + IL_0063: call void [mscorlib]System.Console::WriteLine(string) + IL_0068: ldstr "end" + IL_006d: call void [mscorlib]System.Console::WriteLine(string) + IL_0072: ret + } // end of method Switch::SwitchWithArray + +} // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch + +.class private auto ansi sealed '' + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .method assembly hidebysig static uint32 + ComputeStringHash(string s) cil managed + { + // Code size 44 (0x2c) + .maxstack 2 + .locals init (uint32 V_0, + int32 V_1) + IL_0000: ldarg.0 + IL_0001: brfalse.s IL_002a + + IL_0003: ldc.i4 0x811c9dc5 + IL_0008: stloc.0 + IL_0009: ldc.i4.0 + IL_000a: stloc.1 + IL_000b: br.s IL_0021 + + IL_000d: ldarg.0 + IL_000e: ldloc.1 + IL_000f: callvirt instance char [mscorlib]System.String::get_Chars(int32) + IL_0014: ldloc.0 + IL_0015: xor + IL_0016: ldc.i4 0x1000193 + IL_001b: mul + IL_001c: stloc.0 + IL_001d: ldloc.1 + IL_001e: ldc.i4.1 + IL_001f: add + IL_0020: stloc.1 + IL_0021: ldloc.1 + IL_0022: ldarg.0 + IL_0023: callvirt instance int32 [mscorlib]System.String::get_Length() + IL_0028: blt.s IL_000d + + IL_002a: ldloc.0 + IL_002b: ret + } // end of method ''::ComputeStringHash + +} // end of class '' + + +// ============================================================= + +// *********** DISASSEMBLY COMPLETE *********************** diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il new file mode 100644 index 000000000..47d88ae31 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il @@ -0,0 +1,1741 @@ + +// Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0 +// Copyright (c) Microsoft Corporation. Alle Rechte vorbehalten. + + + +// Metadata version: v4.0.30319 +.assembly extern mscorlib +{ + .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 ) + .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 + 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. + + // --- The following custom attribute is added automatically, do not uncomment ------- + // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) + + .permissionset reqmin + = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +.module Switch.dll +// MVID: {0EBEAA6B-55A7-4255-9CA2-CE888E0F54BA} +.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) +.imagebase 0x10000000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 // WINDOWS_CUI +.corflags 0x00000001 // ILONLY +// Image base: 0x00690000 + + +// =============== CLASS MEMBERS DECLARATION =================== + +.class public abstract auto ansi sealed beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch + extends [mscorlib]System.Object +{ + .class auto ansi nested public beforefieldinit SetProperty + extends [mscorlib]System.Object + { + .field public initonly class [mscorlib]System.Reflection.PropertyInfo Property + .field private int32 'k__BackingField' + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void [mscorlib]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggerBrowsableState) = ( 01 00 00 00 00 00 00 00 ) + .method public hidebysig specialname + instance int32 get_Set() cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::'k__BackingField' + IL_0006: ret + } // end of method SetProperty::get_Set + + .method public hidebysig specialname + instance void set_Set(int32 'value') cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::'k__BackingField' + IL_0007: ret + } // end of method SetProperty::set_Set + + .method public hidebysig specialname rtspecialname + instance void .ctor(class [mscorlib]System.Reflection.PropertyInfo 'property') cil managed + { + // Code size 16 (0x10) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: nop + IL_0008: ldarg.0 + IL_0009: ldarg.1 + IL_000a: stfld class [mscorlib]System.Reflection.PropertyInfo ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::Property + IL_000f: ret + } // end of method SetProperty::.ctor + + .property instance int32 Set() + { + .get instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::get_Set() + .set instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + } // end of property SetProperty::Set + } // end of class SetProperty + + .method public hidebysig static string + SparseIntegerSwitch(int32 i) cil managed + { + // Code size 226 (0xe2) + .maxstack 2 + .locals init (int32 V_0, + string V_1) + IL_0000: nop + IL_0001: ldstr "SparseIntegerSwitch: " + IL_0006: ldarg.0 + IL_0007: box [mscorlib]System.Int32 + IL_000c: call string [mscorlib]System.String::Concat(object, + object) + IL_0011: call void [mscorlib]System.Console::WriteLine(string) + IL_0016: nop + IL_0017: ldarg.0 + IL_0018: stloc.0 + IL_0019: ldloc.0 + IL_001a: ldc.i4.4 + IL_001b: bgt.s IL_0053 + + IL_001d: ldloc.0 + IL_001e: ldc.i4 0xff676980 + IL_0023: beq.s IL_0080 + + IL_0025: br.s IL_0027 + + IL_0027: ldloc.0 + IL_0028: ldc.i4.s -100 + IL_002a: beq.s IL_0088 + + IL_002c: br.s IL_002e + + IL_002e: ldloc.0 + IL_002f: ldc.i4.m1 + IL_0030: sub + IL_0031: switch ( + IL_0090, + IL_0098, + IL_00a0, + IL_00a8, + IL_00d8, + IL_00b0) + IL_004e: br IL_00d8 + + IL_0053: ldloc.0 + IL_0054: ldc.i4 0x2710 + IL_0059: bgt.s IL_006c + + IL_005b: ldloc.0 + IL_005c: ldc.i4.s 100 + IL_005e: beq.s IL_00b8 + + IL_0060: br.s IL_0062 + + IL_0062: ldloc.0 + IL_0063: ldc.i4 0x2710 + IL_0068: beq.s IL_00c0 + + IL_006a: br.s IL_00d8 + + IL_006c: ldloc.0 + IL_006d: ldc.i4 0x2711 + IL_0072: beq.s IL_00c8 + + IL_0074: br.s IL_0076 + + IL_0076: ldloc.0 + IL_0077: ldc.i4 0x7fffffff + IL_007c: beq.s IL_00d0 + + IL_007e: br.s IL_00d8 + + IL_0080: ldstr "-10 mln" + IL_0085: stloc.1 + IL_0086: br.s IL_00e0 + + IL_0088: ldstr "-hundred" + IL_008d: stloc.1 + IL_008e: br.s IL_00e0 + + IL_0090: ldstr "-1" + IL_0095: stloc.1 + IL_0096: br.s IL_00e0 + + IL_0098: ldstr "0" + IL_009d: stloc.1 + IL_009e: br.s IL_00e0 + + IL_00a0: ldstr "1" + IL_00a5: stloc.1 + IL_00a6: br.s IL_00e0 + + IL_00a8: ldstr "2" + IL_00ad: stloc.1 + IL_00ae: br.s IL_00e0 + + IL_00b0: ldstr "4" + IL_00b5: stloc.1 + IL_00b6: br.s IL_00e0 + + IL_00b8: ldstr "hundred" + IL_00bd: stloc.1 + IL_00be: br.s IL_00e0 + + IL_00c0: ldstr "ten thousand" + IL_00c5: stloc.1 + IL_00c6: br.s IL_00e0 + + IL_00c8: ldstr "ten thousand and one" + IL_00cd: stloc.1 + IL_00ce: br.s IL_00e0 + + IL_00d0: ldstr "int.MaxValue" + IL_00d5: stloc.1 + IL_00d6: br.s IL_00e0 + + IL_00d8: ldstr "something else" + IL_00dd: stloc.1 + IL_00de: br.s IL_00e0 + + IL_00e0: ldloc.1 + IL_00e1: ret + } // end of method Switch::SparseIntegerSwitch + + .method public hidebysig static string + SwitchOverNullableInt(valuetype [mscorlib]System.Nullable`1 i) cil managed + { + // Code size 82 (0x52) + .maxstack 2 + .locals init (valuetype [mscorlib]System.Nullable`1 V_0, + valuetype [mscorlib]System.Nullable`1 V_1, + int32 V_2, + string V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.1 + IL_0003: ldloc.1 + IL_0004: stloc.0 + IL_0005: ldloca.s V_0 + IL_0007: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue() + IL_000c: brfalse.s IL_0028 + + IL_000e: ldloca.s V_0 + IL_0010: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() + IL_0015: stloc.2 + IL_0016: ldloc.2 + IL_0017: brfalse.s IL_0030 + + IL_0019: br.s IL_001b + + IL_001b: ldloc.2 + IL_001c: ldc.i4.5 + IL_001d: beq.s IL_0038 + + IL_001f: br.s IL_0021 + + IL_0021: ldloc.2 + IL_0022: ldc.i4.s 10 + IL_0024: beq.s IL_0040 + + IL_0026: br.s IL_0048 + + IL_0028: ldstr "null" + IL_002d: stloc.3 + IL_002e: br.s IL_0050 + + IL_0030: ldstr "zero" + IL_0035: stloc.3 + IL_0036: br.s IL_0050 + + IL_0038: ldstr "five" + IL_003d: stloc.3 + IL_003e: br.s IL_0050 + + IL_0040: ldstr "ten" + IL_0045: stloc.3 + IL_0046: br.s IL_0050 + + IL_0048: ldstr "large" + IL_004d: stloc.3 + IL_004e: br.s IL_0050 + + IL_0050: ldloc.3 + IL_0051: ret + } // end of method Switch::SwitchOverNullableInt + + .method public hidebysig static string + SwitchOverNullableIntNullCaseCombined(valuetype [mscorlib]System.Nullable`1 i) cil managed + { + // Code size 74 (0x4a) + .maxstack 2 + .locals init (valuetype [mscorlib]System.Nullable`1 V_0, + valuetype [mscorlib]System.Nullable`1 V_1, + int32 V_2, + string V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.1 + IL_0003: ldloc.1 + IL_0004: stloc.0 + IL_0005: ldloca.s V_0 + IL_0007: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue() + IL_000c: brfalse.s IL_0028 + + IL_000e: ldloca.s V_0 + IL_0010: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() + IL_0015: stloc.2 + IL_0016: ldloc.2 + IL_0017: brfalse.s IL_0028 + + IL_0019: br.s IL_001b + + IL_001b: ldloc.2 + IL_001c: ldc.i4.5 + IL_001d: beq.s IL_0030 + + IL_001f: br.s IL_0021 + + IL_0021: ldloc.2 + IL_0022: ldc.i4.s 10 + IL_0024: beq.s IL_0038 + + IL_0026: br.s IL_0040 + + IL_0028: ldstr "zero" + IL_002d: stloc.3 + IL_002e: br.s IL_0048 + + IL_0030: ldstr "five" + IL_0035: stloc.3 + IL_0036: br.s IL_0048 + + IL_0038: ldstr "ten" + IL_003d: stloc.3 + IL_003e: br.s IL_0048 + + IL_0040: ldstr "large" + IL_0045: stloc.3 + IL_0046: br.s IL_0048 + + IL_0048: ldloc.3 + IL_0049: ret + } // end of method Switch::SwitchOverNullableIntNullCaseCombined + + .method public hidebysig static string + SwitchOverNullableIntShifted(valuetype [mscorlib]System.Nullable`1 i) cil managed + { + // Code size 127 (0x7f) + .maxstack 2 + .locals init (valuetype [mscorlib]System.Nullable`1 V_0, + valuetype [mscorlib]System.Nullable`1 V_1, + valuetype [mscorlib]System.Nullable`1 V_2, + valuetype [mscorlib]System.Nullable`1 V_3, + int32 V_4, + string V_5) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.2 + IL_0003: ldloca.s V_2 + IL_0005: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue() + IL_000a: brtrue.s IL_0017 + + IL_000c: ldloca.s V_3 + IL_000e: initobj valuetype [mscorlib]System.Nullable`1 + IL_0014: ldloc.3 + IL_0015: br.s IL_0025 + + IL_0017: ldloca.s V_2 + IL_0019: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() + IL_001e: ldc.i4.5 + IL_001f: add + IL_0020: newobj instance void valuetype [mscorlib]System.Nullable`1::.ctor(!0) + IL_0025: stloc.1 + IL_0026: ldloc.1 + IL_0027: stloc.0 + IL_0028: ldloca.s V_0 + IL_002a: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue() + IL_002f: brfalse.s IL_004f + + IL_0031: ldloca.s V_0 + IL_0033: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() + IL_0038: stloc.s V_4 + IL_003a: ldloc.s V_4 + IL_003c: brfalse.s IL_0058 + + IL_003e: br.s IL_0040 + + IL_0040: ldloc.s V_4 + IL_0042: ldc.i4.5 + IL_0043: beq.s IL_0061 + + IL_0045: br.s IL_0047 + + IL_0047: ldloc.s V_4 + IL_0049: ldc.i4.s 10 + IL_004b: beq.s IL_006a + + IL_004d: br.s IL_0073 + + IL_004f: ldstr "null" + IL_0054: stloc.s V_5 + IL_0056: br.s IL_007c + + IL_0058: ldstr "zero" + IL_005d: stloc.s V_5 + IL_005f: br.s IL_007c + + IL_0061: ldstr "five" + IL_0066: stloc.s V_5 + IL_0068: br.s IL_007c + + IL_006a: ldstr "ten" + IL_006f: stloc.s V_5 + IL_0071: br.s IL_007c + + IL_0073: ldstr "large" + IL_0078: stloc.s V_5 + IL_007a: br.s IL_007c + + IL_007c: ldloc.s V_5 + IL_007e: ret + } // end of method Switch::SwitchOverNullableIntShifted + + .method public hidebysig static string + SwitchOverNullableIntShiftedNullCaseCombined(valuetype [mscorlib]System.Nullable`1 i) cil managed + { + // Code size 118 (0x76) + .maxstack 2 + .locals init (valuetype [mscorlib]System.Nullable`1 V_0, + valuetype [mscorlib]System.Nullable`1 V_1, + valuetype [mscorlib]System.Nullable`1 V_2, + valuetype [mscorlib]System.Nullable`1 V_3, + int32 V_4, + string V_5) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.2 + IL_0003: ldloca.s V_2 + IL_0005: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue() + IL_000a: brtrue.s IL_0017 + + IL_000c: ldloca.s V_3 + IL_000e: initobj valuetype [mscorlib]System.Nullable`1 + IL_0014: ldloc.3 + IL_0015: br.s IL_0025 + + IL_0017: ldloca.s V_2 + IL_0019: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() + IL_001e: ldc.i4.5 + IL_001f: add + IL_0020: newobj instance void valuetype [mscorlib]System.Nullable`1::.ctor(!0) + IL_0025: stloc.1 + IL_0026: ldloc.1 + IL_0027: stloc.0 + IL_0028: ldloca.s V_0 + IL_002a: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue() + IL_002f: brfalse.s IL_004f + + IL_0031: ldloca.s V_0 + IL_0033: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() + IL_0038: stloc.s V_4 + IL_003a: ldloc.s V_4 + IL_003c: brfalse.s IL_004f + + IL_003e: br.s IL_0040 + + IL_0040: ldloc.s V_4 + IL_0042: ldc.i4.5 + IL_0043: beq.s IL_0058 + + IL_0045: br.s IL_0047 + + IL_0047: ldloc.s V_4 + IL_0049: ldc.i4.s 10 + IL_004b: beq.s IL_0061 + + IL_004d: br.s IL_006a + + IL_004f: ldstr "zero" + IL_0054: stloc.s V_5 + IL_0056: br.s IL_0073 + + IL_0058: ldstr "five" + IL_005d: stloc.s V_5 + IL_005f: br.s IL_0073 + + IL_0061: ldstr "ten" + IL_0066: stloc.s V_5 + IL_0068: br.s IL_0073 + + IL_006a: ldstr "large" + IL_006f: stloc.s V_5 + IL_0071: br.s IL_0073 + + IL_0073: ldloc.s V_5 + IL_0075: ret + } // end of method Switch::SwitchOverNullableIntShiftedNullCaseCombined + + .method public hidebysig static string + SwitchOverNullableIntNoNullCase(valuetype [mscorlib]System.Nullable`1 i) cil managed + { + // Code size 74 (0x4a) + .maxstack 2 + .locals init (valuetype [mscorlib]System.Nullable`1 V_0, + valuetype [mscorlib]System.Nullable`1 V_1, + int32 V_2, + string V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.1 + IL_0003: ldloc.1 + IL_0004: stloc.0 + IL_0005: ldloca.s V_0 + IL_0007: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue() + IL_000c: brfalse.s IL_0040 + + IL_000e: ldloca.s V_0 + IL_0010: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() + IL_0015: stloc.2 + IL_0016: ldloc.2 + IL_0017: brfalse.s IL_0028 + + IL_0019: br.s IL_001b + + IL_001b: ldloc.2 + IL_001c: ldc.i4.5 + IL_001d: beq.s IL_0030 + + IL_001f: br.s IL_0021 + + IL_0021: ldloc.2 + IL_0022: ldc.i4.s 10 + IL_0024: beq.s IL_0038 + + IL_0026: br.s IL_0040 + + IL_0028: ldstr "zero" + IL_002d: stloc.3 + IL_002e: br.s IL_0048 + + IL_0030: ldstr "five" + IL_0035: stloc.3 + IL_0036: br.s IL_0048 + + IL_0038: ldstr "ten" + IL_003d: stloc.3 + IL_003e: br.s IL_0048 + + IL_0040: ldstr "other" + IL_0045: stloc.3 + IL_0046: br.s IL_0048 + + IL_0048: ldloc.3 + IL_0049: ret + } // end of method Switch::SwitchOverNullableIntNoNullCase + + .method public hidebysig static string + SwitchOverNullableIntNoNullCaseShifted(valuetype [mscorlib]System.Nullable`1 i) cil managed + { + // Code size 118 (0x76) + .maxstack 2 + .locals init (valuetype [mscorlib]System.Nullable`1 V_0, + valuetype [mscorlib]System.Nullable`1 V_1, + valuetype [mscorlib]System.Nullable`1 V_2, + valuetype [mscorlib]System.Nullable`1 V_3, + int32 V_4, + string V_5) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.2 + IL_0003: ldloca.s V_2 + IL_0005: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue() + IL_000a: brtrue.s IL_0017 + + IL_000c: ldloca.s V_3 + IL_000e: initobj valuetype [mscorlib]System.Nullable`1 + IL_0014: ldloc.3 + IL_0015: br.s IL_0025 + + IL_0017: ldloca.s V_2 + IL_0019: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() + IL_001e: ldc.i4.5 + IL_001f: add + IL_0020: newobj instance void valuetype [mscorlib]System.Nullable`1::.ctor(!0) + IL_0025: stloc.1 + IL_0026: ldloc.1 + IL_0027: stloc.0 + IL_0028: ldloca.s V_0 + IL_002a: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue() + IL_002f: brfalse.s IL_006a + + IL_0031: ldloca.s V_0 + IL_0033: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() + IL_0038: stloc.s V_4 + IL_003a: ldloc.s V_4 + IL_003c: brfalse.s IL_004f + + IL_003e: br.s IL_0040 + + IL_0040: ldloc.s V_4 + IL_0042: ldc.i4.5 + IL_0043: beq.s IL_0058 + + IL_0045: br.s IL_0047 + + IL_0047: ldloc.s V_4 + IL_0049: ldc.i4.s 10 + IL_004b: beq.s IL_0061 + + IL_004d: br.s IL_006a + + IL_004f: ldstr "zero" + IL_0054: stloc.s V_5 + IL_0056: br.s IL_0073 + + IL_0058: ldstr "five" + IL_005d: stloc.s V_5 + IL_005f: br.s IL_0073 + + IL_0061: ldstr "ten" + IL_0066: stloc.s V_5 + IL_0068: br.s IL_0073 + + IL_006a: ldstr "other" + IL_006f: stloc.s V_5 + IL_0071: br.s IL_0073 + + IL_0073: ldloc.s V_5 + IL_0075: ret + } // end of method Switch::SwitchOverNullableIntNoNullCaseShifted + + .method public hidebysig static void SwitchOverInt(int32 i) cil managed + { + // Code size 151 (0x97) + .maxstack 2 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: ldc.i4.s 10 + IL_0006: bgt.s IL_001a + + IL_0008: ldloc.0 + IL_0009: brfalse.s IL_003b + + IL_000b: br.s IL_000d + + IL_000d: ldloc.0 + IL_000e: ldc.i4.5 + IL_000f: beq.s IL_0048 + + IL_0011: br.s IL_0013 + + IL_0013: ldloc.0 + IL_0014: ldc.i4.s 10 + IL_0016: beq.s IL_0055 + + IL_0018: br.s IL_0096 + + IL_001a: ldloc.0 + IL_001b: ldc.i4.s 20 + IL_001d: bgt.s IL_002d + + IL_001f: ldloc.0 + IL_0020: ldc.i4.s 15 + IL_0022: beq.s IL_0062 + + IL_0024: br.s IL_0026 + + IL_0026: ldloc.0 + IL_0027: ldc.i4.s 20 + IL_0029: beq.s IL_006f + + IL_002b: br.s IL_0096 + + IL_002d: ldloc.0 + IL_002e: ldc.i4.s 25 + IL_0030: beq.s IL_007c + + IL_0032: br.s IL_0034 + + IL_0034: ldloc.0 + IL_0035: ldc.i4.s 30 + IL_0037: beq.s IL_0089 + + IL_0039: br.s IL_0096 + + IL_003b: ldstr "zero" + IL_0040: call void [mscorlib]System.Console::WriteLine(string) + IL_0045: nop + IL_0046: br.s IL_0096 + + IL_0048: ldstr "five" + IL_004d: call void [mscorlib]System.Console::WriteLine(string) + IL_0052: nop + IL_0053: br.s IL_0096 + + IL_0055: ldstr "ten" + IL_005a: call void [mscorlib]System.Console::WriteLine(string) + IL_005f: nop + IL_0060: br.s IL_0096 + + IL_0062: ldstr "fifteen" + IL_0067: call void [mscorlib]System.Console::WriteLine(string) + IL_006c: nop + IL_006d: br.s IL_0096 + + IL_006f: ldstr "twenty" + IL_0074: call void [mscorlib]System.Console::WriteLine(string) + IL_0079: nop + IL_007a: br.s IL_0096 + + IL_007c: ldstr "twenty-five" + IL_0081: call void [mscorlib]System.Console::WriteLine(string) + IL_0086: nop + IL_0087: br.s IL_0096 + + IL_0089: ldstr "thirty" + IL_008e: call void [mscorlib]System.Console::WriteLine(string) + IL_0093: nop + IL_0094: br.s IL_0096 + + IL_0096: ret + } // end of method Switch::SwitchOverInt + + .method public hidebysig static string + ShortSwitchOverString(string text) cil managed + { + // Code size 95 (0x5f) + .maxstack 2 + .locals init (string V_0, + string V_1) + IL_0000: nop + IL_0001: ldstr "ShortSwitchOverString: " + 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: ldstr "First case" + IL_001a: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_001f: brtrue.s IL_003d + + IL_0021: ldloc.0 + IL_0022: ldstr "Second case" + IL_0027: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_002c: brtrue.s IL_0045 + + IL_002e: ldloc.0 + IL_002f: ldstr "Third case" + IL_0034: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0039: brtrue.s IL_004d + + IL_003b: br.s IL_0055 + + IL_003d: ldstr "Text1" + IL_0042: stloc.1 + IL_0043: br.s IL_005d + + IL_0045: ldstr "Text2" + IL_004a: stloc.1 + IL_004b: br.s IL_005d + + IL_004d: ldstr "Text3" + IL_0052: stloc.1 + IL_0053: br.s IL_005d + + IL_0055: ldstr "Default" + IL_005a: stloc.1 + IL_005b: br.s IL_005d + + IL_005d: ldloc.1 + IL_005e: ret + } // end of method Switch::ShortSwitchOverString + + .method public hidebysig static string + ShortSwitchOverStringWithNullCase(string text) cil managed + { + // Code size 85 (0x55) + .maxstack 2 + .locals init (string V_0, + string V_1) + IL_0000: nop + IL_0001: ldstr "ShortSwitchOverStringWithNullCase: " + 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: ldstr "First case" + IL_001a: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_001f: brtrue.s IL_0033 + + IL_0021: ldloc.0 + IL_0022: ldstr "Second case" + IL_0027: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_002c: brtrue.s IL_003b + + IL_002e: ldloc.0 + IL_002f: brfalse.s IL_0043 + + IL_0031: br.s IL_004b + + IL_0033: ldstr "Text1" + IL_0038: stloc.1 + IL_0039: br.s IL_0053 + + IL_003b: ldstr "Text2" + IL_0040: stloc.1 + IL_0041: br.s IL_0053 + + IL_0043: ldstr "null" + IL_0048: stloc.1 + IL_0049: br.s IL_0053 + + IL_004b: ldstr "Default" + IL_0050: stloc.1 + IL_0051: br.s IL_0053 + + IL_0053: ldloc.1 + IL_0054: ret + } // end of method Switch::ShortSwitchOverStringWithNullCase + + .method public hidebysig static string + SwitchOverString1(string text) cil managed + { + // Code size 325 (0x145) + .maxstack 2 + .locals init (string V_0, + uint32 V_1, + string V_2) + IL_0000: nop + IL_0001: ldstr "SwitchOverString1: " + 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 0xf3d44a6 + IL_0021: bgt.un.s IL_005a + + IL_0023: ldloc.1 + IL_0024: ldc.i4 0x8861b86 + IL_0029: bgt.un.s IL_0043 + + IL_002b: ldloc.1 + IL_002c: brfalse IL_0102 + + IL_0031: br.s IL_0033 + + IL_0033: ldloc.1 + IL_0034: ldc.i4 0x8861b86 + IL_0039: beq IL_00e4 + + IL_003e: br IL_013b + + IL_0043: ldloc.1 + IL_0044: ldc.i4 0xc9a8f4f + IL_0049: beq.s IL_0093 + + IL_004b: br.s IL_004d + + IL_004d: ldloc.1 + IL_004e: ldc.i4 0xf3d44a6 + IL_0053: beq.s IL_00c6 + + IL_0055: br IL_013b + + IL_005a: ldloc.1 + IL_005b: ldc.i4 0x652a1179 + IL_0060: bgt.un.s IL_007c + + IL_0062: ldloc.1 + IL_0063: ldc.i4 0x51650fb9 + IL_0068: beq IL_00f3 + + IL_006d: br.s IL_006f + + IL_006f: ldloc.1 + IL_0070: ldc.i4 0x652a1179 + IL_0075: beq.s IL_00b7 + + IL_0077: br IL_013b + + IL_007c: ldloc.1 + IL_007d: ldc.i4 0xea3d096b + IL_0082: beq.s IL_00a5 + + IL_0084: br.s IL_0086 + + IL_0086: ldloc.1 + IL_0087: ldc.i4 0xf701cc7f + IL_008c: beq.s IL_00d5 + + IL_008e: br IL_013b + + IL_0093: ldloc.0 + IL_0094: ldstr "First case" + IL_0099: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_009e: brtrue.s IL_0107 + + IL_00a0: br IL_013b + + IL_00a5: ldloc.0 + IL_00a6: ldstr "Second case" + IL_00ab: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_00b0: brtrue.s IL_010f + + IL_00b2: br IL_013b + + IL_00b7: ldloc.0 + IL_00b8: ldstr "2nd case" + IL_00bd: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_00c2: brtrue.s IL_010f + + IL_00c4: br.s IL_013b + + IL_00c6: ldloc.0 + IL_00c7: ldstr "Third case" + IL_00cc: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_00d1: brtrue.s IL_0117 + + IL_00d3: br.s IL_013b + + IL_00d5: ldloc.0 + IL_00d6: ldstr "Fourth case" + IL_00db: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_00e0: brtrue.s IL_011f + + IL_00e2: br.s IL_013b + + IL_00e4: ldloc.0 + IL_00e5: ldstr "Fifth case" + IL_00ea: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_00ef: brtrue.s IL_0127 + + IL_00f1: br.s IL_013b + + IL_00f3: ldloc.0 + IL_00f4: ldstr "Sixth case" + IL_00f9: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_00fe: brtrue.s IL_012f + + IL_0100: br.s IL_013b + + IL_0102: ldloc.0 + IL_0103: brfalse.s IL_0137 + + IL_0105: br.s IL_013b + + IL_0107: ldstr "Text1" + IL_010c: stloc.2 + IL_010d: br.s IL_0143 + + IL_010f: ldstr "Text2" + IL_0114: stloc.2 + IL_0115: br.s IL_0143 + + IL_0117: ldstr "Text3" + IL_011c: stloc.2 + IL_011d: br.s IL_0143 + + IL_011f: ldstr "Text4" + IL_0124: stloc.2 + IL_0125: br.s IL_0143 + + IL_0127: ldstr "Text5" + IL_012c: stloc.2 + IL_012d: br.s IL_0143 + + IL_012f: ldstr "Text6" + IL_0134: stloc.2 + IL_0135: br.s IL_0143 + + IL_0137: ldnull + IL_0138: stloc.2 + IL_0139: br.s IL_0143 + + IL_013b: ldstr "Default" + IL_0140: stloc.2 + IL_0141: br.s IL_0143 + + IL_0143: ldloc.2 + IL_0144: ret + } // end of method Switch::SwitchOverString1 + + .method public hidebysig static string + SwitchOverString2() cil managed + { + // Code size 500 (0x1f4) + .maxstack 2 + .locals init (string V_0, + uint32 V_1, + string V_2) + IL_0000: nop + IL_0001: ldstr "SwitchOverString2:" + IL_0006: call void [mscorlib]System.Console::WriteLine(string) + IL_000b: nop + IL_000c: call string [mscorlib]System.Environment::get_UserName() + IL_0011: stloc.0 + IL_0012: ldloc.0 + IL_0013: call uint32 ''::ComputeStringHash(string) + IL_0018: stloc.1 + IL_0019: ldloc.1 + IL_001a: ldc.i4 0x4c7c71f6 + IL_001f: bgt.un.s IL_0070 + + IL_0021: ldloc.1 + IL_0022: ldc.i4 0xc9a8f4f + IL_0027: bgt.un.s IL_0046 + + IL_0029: ldloc.1 + IL_002a: ldc.i4 0x8861b86 + IL_002f: beq IL_011a + + IL_0034: br.s IL_0036 + + IL_0036: ldloc.1 + IL_0037: ldc.i4 0xc9a8f4f + IL_003c: beq IL_00c6 + + IL_0041: br IL_01ea + + IL_0046: ldloc.1 + IL_0047: ldc.i4 0xf3d44a6 + IL_004c: beq IL_00f0 + + IL_0051: br.s IL_0053 + + IL_0053: ldloc.1 + IL_0054: ldc.i4 0x20289804 + IL_0059: beq IL_0153 + + IL_005e: br.s IL_0060 + + IL_0060: ldloc.1 + IL_0061: ldc.i4 0x4c7c71f6 + IL_0066: beq IL_0165 + + IL_006b: br IL_01ea + + IL_0070: ldloc.1 + IL_0071: ldc.i4 0xa151b28a + IL_0076: bgt.un.s IL_00a2 + + IL_0078: ldloc.1 + IL_0079: ldc.i4 0x4d0cea48 + IL_007e: beq IL_0183 + + IL_0083: br.s IL_0085 + + IL_0085: ldloc.1 + IL_0086: ldc.i4 0x51650fb9 + IL_008b: beq IL_012f + + IL_0090: br.s IL_0092 + + IL_0092: ldloc.1 + IL_0093: ldc.i4 0xa151b28a + IL_0098: beq IL_0141 + + IL_009d: br IL_01ea + + IL_00a2: ldloc.1 + IL_00a3: ldc.i4 0xea3d096b + IL_00a8: beq.s IL_00db + + IL_00aa: br.s IL_00ac + + IL_00ac: ldloc.1 + IL_00ad: ldc.i4 0xed5134d4 + IL_00b2: beq IL_0174 + + IL_00b7: br.s IL_00b9 + + IL_00b9: ldloc.1 + IL_00ba: ldc.i4 0xf701cc7f + IL_00bf: beq.s IL_0105 + + IL_00c1: br IL_01ea + + IL_00c6: ldloc.0 + IL_00c7: ldstr "First case" + IL_00cc: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_00d1: brtrue IL_0192 + + IL_00d6: br IL_01ea + + IL_00db: ldloc.0 + IL_00dc: ldstr "Second case" + IL_00e1: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_00e6: brtrue IL_019a + + IL_00eb: br IL_01ea + + IL_00f0: ldloc.0 + IL_00f1: ldstr "Third case" + IL_00f6: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_00fb: brtrue IL_01a2 + + IL_0100: br IL_01ea + + IL_0105: ldloc.0 + IL_0106: ldstr "Fourth case" + IL_010b: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0110: brtrue IL_01aa + + IL_0115: br IL_01ea + + IL_011a: ldloc.0 + IL_011b: ldstr "Fifth case" + IL_0120: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0125: brtrue IL_01b2 + + IL_012a: br IL_01ea + + IL_012f: ldloc.0 + IL_0130: ldstr "Sixth case" + IL_0135: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_013a: brtrue.s IL_01ba + + IL_013c: br IL_01ea + + IL_0141: ldloc.0 + IL_0142: ldstr "Seventh case" + IL_0147: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_014c: brtrue.s IL_01c2 + + IL_014e: br IL_01ea + + IL_0153: ldloc.0 + IL_0154: ldstr "Eighth case" + IL_0159: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_015e: brtrue.s IL_01ca + + IL_0160: br IL_01ea + + IL_0165: ldloc.0 + IL_0166: ldstr "Ninth case" + IL_016b: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0170: brtrue.s IL_01d2 + + IL_0172: br.s IL_01ea + + IL_0174: ldloc.0 + IL_0175: ldstr "Tenth case" + IL_017a: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_017f: brtrue.s IL_01da + + IL_0181: br.s IL_01ea + + IL_0183: ldloc.0 + IL_0184: ldstr "Eleventh case" + IL_0189: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_018e: brtrue.s IL_01e2 + + IL_0190: br.s IL_01ea + + IL_0192: ldstr "Text1" + IL_0197: stloc.2 + IL_0198: br.s IL_01f2 + + IL_019a: ldstr "Text2" + IL_019f: stloc.2 + IL_01a0: br.s IL_01f2 + + IL_01a2: ldstr "Text3" + IL_01a7: stloc.2 + IL_01a8: br.s IL_01f2 + + IL_01aa: ldstr "Text4" + IL_01af: stloc.2 + IL_01b0: br.s IL_01f2 + + IL_01b2: ldstr "Text5" + IL_01b7: stloc.2 + IL_01b8: br.s IL_01f2 + + IL_01ba: ldstr "Text6" + IL_01bf: stloc.2 + IL_01c0: br.s IL_01f2 + + IL_01c2: ldstr "Text7" + IL_01c7: stloc.2 + IL_01c8: br.s IL_01f2 + + IL_01ca: ldstr "Text8" + IL_01cf: stloc.2 + IL_01d0: br.s IL_01f2 + + IL_01d2: ldstr "Text9" + IL_01d7: stloc.2 + IL_01d8: br.s IL_01f2 + + IL_01da: ldstr "Text10" + IL_01df: stloc.2 + IL_01e0: br.s IL_01f2 + + IL_01e2: ldstr "Text11" + IL_01e7: stloc.2 + IL_01e8: br.s IL_01f2 + + IL_01ea: ldstr "Default" + IL_01ef: stloc.2 + IL_01f0: br.s IL_01f2 + + IL_01f2: ldloc.2 + IL_01f3: ret + } // end of method Switch::SwitchOverString2 + + .method public hidebysig static string + SwitchOverBool(bool b) cil managed + { + // Code size 59 (0x3b) + .maxstack 2 + .locals init (bool V_0, + string V_1) + IL_0000: nop + IL_0001: ldstr "SwitchOverBool: " + IL_0006: ldarga.s b + IL_0008: call instance string [mscorlib]System.Boolean::ToString() + IL_000d: call string [mscorlib]System.String::Concat(string, + string) + IL_0012: call void [mscorlib]System.Console::WriteLine(string) + IL_0017: nop + IL_0018: ldarg.0 + IL_0019: stloc.0 + IL_001a: ldloc.0 + IL_001b: brfalse.s IL_002d + + IL_001d: br.s IL_001f + + IL_001f: ldloc.0 + IL_0020: ldc.i4.1 + IL_0021: beq.s IL_0025 + + IL_0023: br.s IL_0035 + + IL_0025: ldsfld string [mscorlib]System.Boolean::TrueString + IL_002a: stloc.1 + IL_002b: br.s IL_0039 + + IL_002d: ldsfld string [mscorlib]System.Boolean::FalseString + IL_0032: stloc.1 + IL_0033: br.s IL_0039 + + IL_0035: ldnull + IL_0036: stloc.1 + IL_0037: br.s IL_0039 + + IL_0039: ldloc.1 + IL_003a: ret + } // end of method Switch::SwitchOverBool + + .method public hidebysig static void SwitchInLoop(int32 i) cil managed + { + // Code size 128 (0x80) + .maxstack 2 + .locals init (int32 V_0, + bool V_1) + IL_0000: nop + IL_0001: ldstr "SwitchInLoop: " + IL_0006: ldarg.0 + IL_0007: box [mscorlib]System.Int32 + IL_000c: call string [mscorlib]System.String::Concat(object, + object) + IL_0011: call void [mscorlib]System.Console::WriteLine(string) + IL_0016: nop + IL_0017: br.s IL_007b + + IL_0019: nop + IL_001a: ldarg.0 + IL_001b: stloc.0 + IL_001c: ldloc.0 + IL_001d: ldc.i4.1 + IL_001e: sub + IL_001f: switch ( + IL_0036, + IL_0043, + IL_005d, + IL_0050) + IL_0034: br.s IL_005d + + IL_0036: ldstr "one" + IL_003b: call void [mscorlib]System.Console::WriteLine(string) + IL_0040: nop + IL_0041: br.s IL_0075 + + IL_0043: ldstr "two" + IL_0048: call void [mscorlib]System.Console::WriteLine(string) + IL_004d: nop + IL_004e: br.s IL_0075 + + IL_0050: ldstr "four" + IL_0055: call void [mscorlib]System.Console::WriteLine(string) + IL_005a: nop + IL_005b: br.s IL_007f + + IL_005d: ldstr "default" + IL_0062: call void [mscorlib]System.Console::WriteLine(string) + IL_0067: nop + IL_0068: ldstr "more code" + IL_006d: call void [mscorlib]System.Console::WriteLine(string) + IL_0072: nop + IL_0073: br.s IL_007f + + IL_0075: ldarg.0 + IL_0076: ldc.i4.1 + IL_0077: add + IL_0078: starg.s i + IL_007a: nop + IL_007b: ldc.i4.1 + IL_007c: stloc.1 + IL_007d: br.s IL_0019 + + IL_007f: ret + } // end of method Switch::SwitchInLoop + + .method public hidebysig static void SwitchWithGoto(int32 i) cil managed + { + // Code size 128 (0x80) + .maxstack 2 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldstr "SwitchWithGoto: " + IL_0006: ldarg.0 + IL_0007: box [mscorlib]System.Int32 + IL_000c: call string [mscorlib]System.String::Concat(object, + object) + IL_0011: call void [mscorlib]System.Console::WriteLine(string) + IL_0016: nop + IL_0017: ldarg.0 + IL_0018: stloc.0 + IL_0019: ldloc.0 + IL_001a: ldc.i4.1 + IL_001b: sub + IL_001c: switch ( + IL_0033, + IL_0040, + IL_004d, + IL_005a) + IL_0031: br.s IL_0067 + + IL_0033: ldstr "one" + IL_0038: call void [mscorlib]System.Console::WriteLine(string) + IL_003d: nop + IL_003e: br.s IL_0067 + + IL_0040: ldstr "two" + IL_0045: call void [mscorlib]System.Console::WriteLine(string) + IL_004a: nop + IL_004b: br.s IL_004d + + IL_004d: ldstr "three" + IL_0052: call void [mscorlib]System.Console::WriteLine(string) + IL_0057: nop + IL_0058: br.s IL_0074 + + IL_005a: ldstr "four" + IL_005f: call void [mscorlib]System.Console::WriteLine(string) + IL_0064: nop + IL_0065: br.s IL_007f + + IL_0067: ldstr "default" + IL_006c: call void [mscorlib]System.Console::WriteLine(string) + IL_0071: nop + IL_0072: br.s IL_0074 + + IL_0074: ldstr "End of method" + IL_0079: call void [mscorlib]System.Console::WriteLine(string) + IL_007e: nop + IL_007f: ret + } // end of method Switch::SwitchWithGoto + + .method private hidebysig static class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty[] + GetProperties() cil managed + { + // Code size 12 (0xc) + .maxstack 1 + .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty[] V_0) + IL_0000: nop + IL_0001: ldc.i4.0 + IL_0002: newarr ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty + IL_0007: stloc.0 + IL_0008: br.s IL_000a + + IL_000a: ldloc.0 + IL_000b: ret + } // end of method Switch::GetProperties + + .method public hidebysig static void SwitchOnStringInForLoop() cil managed + { + // Code size 255 (0xff) + .maxstack 2 + .locals init (class [mscorlib]System.Collections.Generic.List`1 V_0, + class [mscorlib]System.Collections.Generic.List`1 V_1, + class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty[] V_2, + int32 V_3, + class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty V_4, + string V_5, + bool V_6) + IL_0000: nop + IL_0001: newobj instance void class [mscorlib]System.Collections.Generic.List`1::.ctor() + IL_0006: stloc.0 + IL_0007: newobj instance void class [mscorlib]System.Collections.Generic.List`1::.ctor() + IL_000c: stloc.1 + IL_000d: call class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty[] ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch::GetProperties() + IL_0012: stloc.2 + IL_0013: ldc.i4.0 + IL_0014: stloc.3 + IL_0015: br IL_00ef + + IL_001a: nop + IL_001b: ldloc.2 + IL_001c: ldloc.3 + IL_001d: ldelem.ref + IL_001e: stloc.s V_4 + IL_0020: ldloc.s V_4 + IL_0022: ldfld class [mscorlib]System.Reflection.PropertyInfo ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::Property + IL_0027: callvirt instance string [mscorlib]System.Reflection.MemberInfo::get_Name() + IL_002c: stloc.s V_5 + IL_002e: ldloc.s V_5 + IL_0030: ldstr "Name1" + IL_0035: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_003a: brtrue.s IL_0084 + + IL_003c: ldloc.s V_5 + IL_003e: ldstr "Name2" + IL_0043: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0048: brtrue.s IL_0098 + + IL_004a: ldloc.s V_5 + IL_004c: ldstr "Name3" + IL_0051: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0056: brtrue.s IL_00ac + + IL_0058: ldloc.s V_5 + IL_005a: ldstr "Name4" + IL_005f: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0064: brtrue.s IL_00c0 + + IL_0066: ldloc.s V_5 + IL_0068: ldstr "Name5" + IL_006d: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0072: brtrue.s IL_00d4 + + IL_0074: ldloc.s V_5 + IL_0076: ldstr "Name6" + IL_007b: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0080: brtrue.s IL_00d4 + + IL_0082: br.s IL_00df + + IL_0084: ldloc.s V_4 + IL_0086: ldc.i4.1 + IL_0087: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + IL_008c: nop + IL_008d: ldloc.0 + IL_008e: ldloc.s V_4 + IL_0090: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_0095: nop + IL_0096: br.s IL_00ea + + IL_0098: ldloc.s V_4 + IL_009a: ldc.i4.2 + IL_009b: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + IL_00a0: nop + IL_00a1: ldloc.0 + IL_00a2: ldloc.s V_4 + IL_00a4: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_00a9: nop + IL_00aa: br.s IL_00ea + + IL_00ac: ldloc.s V_4 + IL_00ae: ldc.i4.3 + IL_00af: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + IL_00b4: nop + IL_00b5: ldloc.0 + IL_00b6: ldloc.s V_4 + IL_00b8: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_00bd: nop + IL_00be: br.s IL_00ea + + IL_00c0: ldloc.s V_4 + IL_00c2: ldc.i4.4 + IL_00c3: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + IL_00c8: nop + IL_00c9: ldloc.0 + IL_00ca: ldloc.s V_4 + IL_00cc: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_00d1: nop + IL_00d2: br.s IL_00ea + + IL_00d4: ldloc.0 + IL_00d5: ldloc.s V_4 + IL_00d7: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_00dc: nop + IL_00dd: br.s IL_00ea + + IL_00df: ldloc.1 + IL_00e0: ldloc.s V_4 + IL_00e2: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_00e7: nop + IL_00e8: br.s IL_00ea + + IL_00ea: nop + IL_00eb: ldloc.3 + IL_00ec: ldc.i4.1 + IL_00ed: add + IL_00ee: stloc.3 + IL_00ef: ldloc.3 + IL_00f0: ldloc.2 + IL_00f1: ldlen + IL_00f2: conv.i4 + IL_00f3: clt + IL_00f5: stloc.s V_6 + IL_00f7: ldloc.s V_6 + IL_00f9: brtrue IL_001a + + IL_00fe: ret + } // end of method Switch::SwitchOnStringInForLoop + + .method public hidebysig static void SwitchWithComplexCondition(string[] args) cil managed + { + // Code size 134 (0x86) + .maxstack 2 + .locals init (string V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldlen + IL_0003: brfalse.s IL_000a + + IL_0005: ldarg.0 + IL_0006: ldc.i4.0 + IL_0007: ldelem.ref + IL_0008: br.s IL_000f + + IL_000a: ldstr "dummy" + IL_000f: stloc.0 + IL_0010: ldloc.0 + IL_0011: ldstr "a" + IL_0016: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_001b: brtrue.s IL_0046 + + IL_001d: ldloc.0 + IL_001e: ldstr "b" + IL_0023: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0028: brtrue.s IL_0053 + + IL_002a: ldloc.0 + IL_002b: ldstr "c" + IL_0030: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0035: brtrue.s IL_0060 + + IL_0037: ldloc.0 + IL_0038: ldstr "d" + IL_003d: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0042: brtrue.s IL_006d + + IL_0044: br.s IL_007a + + IL_0046: ldstr "a" + IL_004b: call void [mscorlib]System.Console::WriteLine(string) + IL_0050: nop + IL_0051: br.s IL_007a + + IL_0053: ldstr "b" + IL_0058: call void [mscorlib]System.Console::WriteLine(string) + IL_005d: nop + IL_005e: br.s IL_007a + + IL_0060: ldstr "c" + IL_0065: call void [mscorlib]System.Console::WriteLine(string) + IL_006a: nop + IL_006b: br.s IL_007a + + IL_006d: ldstr "d" + IL_0072: call void [mscorlib]System.Console::WriteLine(string) + IL_0077: nop + IL_0078: br.s IL_007a + + IL_007a: ldstr "end" + IL_007f: call void [mscorlib]System.Console::WriteLine(string) + IL_0084: nop + IL_0085: ret + } // end of method Switch::SwitchWithComplexCondition + + .method public hidebysig static void SwitchWithArray(string[] args) cil managed + { + // Code size 123 (0x7b) + .maxstack 2 + .locals init (string V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldc.i4.0 + IL_0003: ldelem.ref + IL_0004: stloc.0 + IL_0005: ldloc.0 + IL_0006: ldstr "a" + IL_000b: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0010: brtrue.s IL_003b + + IL_0012: ldloc.0 + IL_0013: ldstr "b" + IL_0018: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_001d: brtrue.s IL_0048 + + IL_001f: ldloc.0 + IL_0020: ldstr "c" + IL_0025: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_002a: brtrue.s IL_0055 + + IL_002c: ldloc.0 + IL_002d: ldstr "d" + IL_0032: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0037: brtrue.s IL_0062 + + IL_0039: br.s IL_006f + + IL_003b: ldstr "a" + IL_0040: call void [mscorlib]System.Console::WriteLine(string) + IL_0045: nop + IL_0046: br.s IL_006f + + IL_0048: ldstr "b" + IL_004d: call void [mscorlib]System.Console::WriteLine(string) + IL_0052: nop + IL_0053: br.s IL_006f + + IL_0055: ldstr "c" + IL_005a: call void [mscorlib]System.Console::WriteLine(string) + IL_005f: nop + IL_0060: br.s IL_006f + + IL_0062: ldstr "d" + IL_0067: call void [mscorlib]System.Console::WriteLine(string) + IL_006c: nop + IL_006d: br.s IL_006f + + IL_006f: ldstr "end" + IL_0074: call void [mscorlib]System.Console::WriteLine(string) + IL_0079: nop + IL_007a: ret + } // end of method Switch::SwitchWithArray + +} // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch + +.class private auto ansi sealed '' + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .method assembly hidebysig static uint32 + ComputeStringHash(string s) cil managed + { + // Code size 46 (0x2e) + .maxstack 2 + .locals init (uint32 V_0, + int32 V_1) + IL_0000: ldarg.0 + IL_0001: brfalse.s IL_002c + + IL_0003: ldc.i4 0x811c9dc5 + IL_0008: stloc.0 + IL_0009: ldc.i4.0 + IL_000a: stloc.1 + IL_000b: br.s IL_0021 + + IL_000d: ldarg.0 + IL_000e: ldloc.1 + IL_000f: callvirt instance char [mscorlib]System.String::get_Chars(int32) + IL_0014: ldloc.0 + IL_0015: xor + IL_0016: ldc.i4 0x1000193 + IL_001b: mul + IL_001c: stloc.0 + IL_001d: ldloc.1 + IL_001e: ldc.i4.1 + IL_001f: add + IL_0020: stloc.1 + IL_0021: ldloc.1 + IL_0022: ldarg.0 + IL_0023: callvirt instance int32 [mscorlib]System.String::get_Length() + IL_0028: bge.s IL_002c + + IL_002a: br.s IL_000d + + IL_002c: ldloc.0 + IL_002d: ret + } // end of method ''::ComputeStringHash + +} // end of class '' + + +// ============================================================= + +// *********** DISASSEMBLY COMPLETE *********************** diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Using.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Using.cs index 3f615c56b..a531888f0 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Using.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Using.cs @@ -58,7 +58,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public void SimpleUsingExpressionStatementWithDeclaration() { using (MemoryStream memoryStream = new MemoryStream()) { - memoryStream.WriteByte((byte)42); + memoryStream.WriteByte(42); Console.WriteLine("using-body: " + memoryStream.Position); } } diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 5810602fe..801b95b7c 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -71,6 +71,7 @@ namespace ICSharpCode.Decompiler.CSharp new SplitVariables(), new ILInlining(), new DetectPinnedRegions(), // must run after inlining but before non-critical control flow transforms + new InlineReturnTransform(), new YieldReturnDecompiler(), // must run after inlining but before loop detection new AsyncAwaitDecompiler(), // must run after inlining but before loop detection new DetectCatchWhenConditionBlocks(), // must run after inlining but before loop detection @@ -81,6 +82,9 @@ namespace ICSharpCode.Decompiler.CSharp new RemoveDeadVariableInit(), new SplitVariables(), // split variables once again, because the stobj(ldloca V, ...) may open up new replacements new SwitchDetection(), + new SwitchOnStringTransform(), + new SwitchOnNullableTransform(), + new SplitVariables(), // split variables once again, because SwitchOnNullableTransform eliminates ldloca new BlockILTransform { // per-block transforms PostOrderTransforms = { // Even though it's a post-order block-transform as most other transforms, @@ -140,7 +144,7 @@ namespace ICSharpCode.Decompiler.CSharp new IntroduceExtensionMethods(), // must run after IntroduceUsingDeclarations new IntroduceQueryExpressions(), // must run after IntroduceExtensionMethods new CombineQueryExpressions(), - //new FlattenSwitchBlocks(), + new FlattenSwitchBlocks(), new FixNameCollisions(), new AddXmlDocumentationTransform(), }; @@ -197,8 +201,8 @@ namespace ICSharpCode.Decompiler.CSharp if (settings.AsyncAwait && AsyncAwaitDecompiler.IsCompilerGeneratedStateMachine(type)) return true; } else if (type.IsCompilerGenerated()) { -// if (type.Name.StartsWith("", StringComparison.Ordinal)) -// return true; + if (type.Name.StartsWith("", StringComparison.Ordinal)) + return true; if (settings.AnonymousTypes && type.IsAnonymousType()) return true; } @@ -211,8 +215,8 @@ namespace ICSharpCode.Decompiler.CSharp return true; if (settings.AutomaticProperties && IsAutomaticPropertyBackingField(field)) return true; -// if (settings.SwitchStatementOnString && IsSwitchOnStringCache(field)) -// return true; + if (settings.SwitchStatementOnString && IsSwitchOnStringCache(field)) + return true; } // event-fields are not [CompilerGenerated] if (settings.AutomaticEvents && field.DeclaringType.Events.Any(ev => ev.Name == field.Name)) diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 2b75c6983..25ed0ddb3 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -93,19 +93,21 @@ namespace ICSharpCode.Decompiler.CSharp return astType; } - public ExpressionWithResolveResult ConvertConstantValue(ResolveResult rr) + public ExpressionWithResolveResult ConvertConstantValue(ResolveResult rr, bool allowImplicitConversion = false) { var expr = astBuilder.ConvertConstantValue(rr); - if (expr is NullReferenceExpression && rr.Type.Kind != TypeKind.Null) { - expr = new CastExpression(ConvertType(rr.Type), expr); - } else { - switch (rr.Type.GetDefinition()?.KnownTypeCode) { - case KnownTypeCode.SByte: - case KnownTypeCode.Byte: - case KnownTypeCode.Int16: - case KnownTypeCode.UInt16: - expr = new CastExpression(new PrimitiveType(KnownTypeReference.GetCSharpNameByTypeCode(rr.Type.GetDefinition().KnownTypeCode)), expr); - break; + if (!allowImplicitConversion) { + if (expr is NullReferenceExpression && rr.Type.Kind != TypeKind.Null) { + expr = new CastExpression(ConvertType(rr.Type), expr); + } else { + switch (rr.Type.GetDefinition()?.KnownTypeCode) { + case KnownTypeCode.SByte: + case KnownTypeCode.Byte: + case KnownTypeCode.Int16: + case KnownTypeCode.UInt16: + expr = new CastExpression(new PrimitiveType(KnownTypeReference.GetCSharpNameByTypeCode(rr.Type.GetDefinition().KnownTypeCode)), expr); + break; + } } } var exprRR = expr.Annotation(); diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs index 45849368c..909339d6d 100644 --- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs @@ -83,42 +83,134 @@ namespace ICSharpCode.Decompiler.CSharp return new IfElseStatement(condition, trueStatement, falseStatement); } - CaseLabel CreateTypedCaseLabel(long i, IType type) + ConstantResolveResult CreateTypedCaseLabel(long i, IType type, string[] map = null) { object value; if (type.IsKnownType(KnownTypeCode.Boolean)) { value = i != 0; + } else if (type.IsKnownType(KnownTypeCode.String) && map != null) { + value = map[i]; } else if (type.Kind == TypeKind.Enum) { var enumType = type.GetDefinition().EnumUnderlyingType; value = CSharpPrimitiveCast.Cast(ReflectionHelper.GetTypeCode(enumType), i, false); + } else if (type.IsKnownType(KnownTypeCode.NullableOfT)) { + var nullableType = NullableType.GetUnderlyingType(type); + value = CSharpPrimitiveCast.Cast(ReflectionHelper.GetTypeCode(nullableType), i, false); } else { value = CSharpPrimitiveCast.Cast(ReflectionHelper.GetTypeCode(type), i, false); } - return new CaseLabel(exprBuilder.ConvertConstantValue(new ConstantResolveResult(type, value))); + return new ConstantResolveResult(type, value); } - + protected internal override Statement VisitSwitchInstruction(SwitchInstruction inst) { + return TranslateSwitch(null, inst); + } + + SwitchStatement TranslateSwitch(BlockContainer switchContainer, SwitchInstruction inst) + { + Debug.Assert(switchContainer.EntryPoint.IncomingEdgeCount == 1); var oldBreakTarget = breakTarget; - breakTarget = null; // 'break' within a switch would only leave the switch - - var value = exprBuilder.Translate(inst.Value); - var stmt = new SwitchStatement() { Expression = value }; + breakTarget = switchContainer; // 'break' within a switch would only leave the switch + var oldCaseLabelMapping = caseLabelMapping; + caseLabelMapping = new Dictionary(); + + TranslatedExpression value; + var strToInt = inst.Value as StringToInt; + if (strToInt != null) { + value = exprBuilder.Translate(strToInt.Argument); + } else { + value = exprBuilder.Translate(inst.Value); + } + + // Pick the section with the most labels as default section. + IL.SwitchSection defaultSection = inst.Sections.First(); foreach (var section in inst.Sections) { - var astSection = new Syntax.SwitchSection(); - astSection.CaseLabels.AddRange(section.Labels.Values.Select(i => CreateTypedCaseLabel(i, value.Type))); - ConvertSwitchSectionBody(astSection, section.Body); - stmt.SwitchSections.Add(astSection); + if (section.Labels.Count() > defaultSection.Labels.Count()) { + defaultSection = section; + } } - - if (inst.DefaultBody.OpCode != OpCode.Nop) { + + var stmt = new SwitchStatement() { Expression = value }; + Dictionary translationDictionary = new Dictionary(); + // initialize C# switch sections. + foreach (var section in inst.Sections) { + // This is used in the block-label mapping. + ConstantResolveResult firstValueResolveResult; var astSection = new Syntax.SwitchSection(); - astSection.CaseLabels.Add(new CaseLabel()); - ConvertSwitchSectionBody(astSection, inst.DefaultBody); + // Create case labels: + if (section == defaultSection) { + astSection.CaseLabels.Add(new CaseLabel()); + firstValueResolveResult = null; + } else { + var values = section.Labels.Values.Select(i => CreateTypedCaseLabel(i, value.Type, strToInt?.Map)).ToArray(); + if (section.HasNullLabel) { + astSection.CaseLabels.Add(new CaseLabel(new NullReferenceExpression())); + firstValueResolveResult = new ConstantResolveResult(SpecialType.NullType, null); + } else { + Debug.Assert(values.Length > 0); + firstValueResolveResult = values[0]; + } + astSection.CaseLabels.AddRange(values.Select(label => new CaseLabel(exprBuilder.ConvertConstantValue(label, allowImplicitConversion: true)))); + } + switch (section.Body) { + case Branch br: + // we can only inline the block, if all branches are in the switchContainer. + if (br.TargetBlock.Parent == switchContainer && switchContainer.Descendants.OfType().Where(b => b.TargetBlock == br.TargetBlock).All(b => BlockContainer.FindClosestSwitchContainer(b) == switchContainer)) + caseLabelMapping.Add(br.TargetBlock, firstValueResolveResult); + break; + default: + break; + } + translationDictionary.Add(section, astSection); stmt.SwitchSections.Add(astSection); } + foreach (var section in inst.Sections) { + var astSection = translationDictionary[section]; + switch (section.Body) { + case Branch br: + // we can only inline the block, if all branches are in the switchContainer. + if (br.TargetBlock.Parent == switchContainer && switchContainer.Descendants.OfType().Where(b => b.TargetBlock == br.TargetBlock).All(b => BlockContainer.FindClosestSwitchContainer(b) == switchContainer)) + ConvertSwitchSectionBody(astSection, br.TargetBlock); + else + ConvertSwitchSectionBody(astSection, section.Body); + break; + case Leave leave: + if (astSection.CaseLabels.Count == 1 && astSection.CaseLabels.First().Expression.IsNull && leave.TargetContainer == switchContainer) { + stmt.SwitchSections.Remove(astSection); + break; + } + goto default; + default: + ConvertSwitchSectionBody(astSection, section.Body); + break; + } + } + if (switchContainer != null) { + // Translate any remaining blocks: + var lastSectionStatements = stmt.SwitchSections.Last().Statements; + foreach (var block in switchContainer.Blocks.Skip(1)) { + if (caseLabelMapping.ContainsKey(block)) continue; + lastSectionStatements.Add(new LabelStatement { Label = block.Label }); + foreach (var nestedInst in block.Instructions) { + var nestedStmt = Convert(nestedInst); + if (nestedStmt is BlockStatement b) { + foreach (var nested in b.Statements) + lastSectionStatements.Add(nested.Detach()); + } else { + lastSectionStatements.Add(nestedStmt); + } + } + Debug.Assert(block.FinalInstruction.OpCode == OpCode.Nop); + } + if (endContainerLabels.TryGetValue(switchContainer, out string label)) { + lastSectionStatements.Add(new LabelStatement { Label = label }); + lastSectionStatements.Add(new BreakStatement()); + } + } breakTarget = oldBreakTarget; + caseLabelMapping = oldCaseLabelMapping; return stmt; } @@ -141,6 +233,8 @@ namespace ICSharpCode.Decompiler.CSharp Block continueTarget; /// Number of ContinueStatements that were created for the current continueTarget int continueCount; + /// Maps blocks to cases. + Dictionary caseLabelMapping; protected internal override Statement VisitBranch(Branch inst) { @@ -148,6 +242,11 @@ namespace ICSharpCode.Decompiler.CSharp continueCount++; return new ContinueStatement(); } + if (caseLabelMapping != null && caseLabelMapping.TryGetValue(inst.TargetBlock, out var label)) { + if (label == null) + return new GotoDefaultStatement(); + return new GotoCaseStatement() { LabelExpression = exprBuilder.ConvertConstantValue(label, allowImplicitConversion: true) }; + } return new GotoStatement(inst.TargetLabel); } @@ -608,6 +707,8 @@ namespace ICSharpCode.Decompiler.CSharp continueCount = oldContinueCount; breakTarget = oldBreakTarget; return loop; + } else if (container.EntryPoint.Instructions.Count == 1 && container.EntryPoint.Instructions[0] is SwitchInstruction switchInst) { + return TranslateSwitch(container, switchInst); } else { var blockStmt = ConvertBlockContainer(container, false); blockStmt.AddAnnotation(container); diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/FlattenSwitchBlocks.cs b/ICSharpCode.Decompiler/CSharp/Transforms/FlattenSwitchBlocks.cs index 4eed055f4..68bb7c47f 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/FlattenSwitchBlocks.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/FlattenSwitchBlocks.cs @@ -2,15 +2,15 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using ICSharpCode.NRefactory.CSharp; +using ICSharpCode.Decompiler.CSharp.Syntax; namespace ICSharpCode.Decompiler.CSharp.Transforms { class FlattenSwitchBlocks : IAstTransform { - public void Run(AstNode compilationUnit) + public void Run(AstNode rootNode, TransformContext context) { - foreach (var switchSection in compilationUnit.Descendants.OfType()) + foreach (var switchSection in rootNode.Descendants.OfType()) { if (switchSection.Statements.Count != 1) continue; diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs index d0684ee4b..a8e29430e 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs @@ -96,11 +96,6 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms public override AstNode VisitIfElseStatement(IfElseStatement ifElseStatement) { - if (context.Settings.SwitchStatementOnString) { - AstNode result = TransformSwitchOnString(ifElseStatement); - if (result != null) - return result; - } AstNode simplifiedIfElse = SimplifyCascadingIfElseStatements(ifElseStatement); if (simplifiedIfElse != null) return simplifiedIfElse; @@ -256,174 +251,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms return null; } #endregion - - #region switch on strings - static readonly IfElseStatement switchOnStringPattern = new IfElseStatement { - Condition = new BinaryOperatorExpression { - Left = new AnyNode("switchExpr"), - Operator = BinaryOperatorType.InEquality, - Right = new NullReferenceExpression() - }, - TrueStatement = new BlockStatement { - new IfElseStatement { - Condition = new BinaryOperatorExpression { - Left = new AnyNode("cachedDict"), - Operator = BinaryOperatorType.Equality, - Right = new NullReferenceExpression() - }, - TrueStatement = new AnyNode("dictCreation") - }, - new IfElseStatement { - Condition = new InvocationExpression(new MemberReferenceExpression(new Backreference("cachedDict").ToExpression(), "TryGetValue"), - new NamedNode("switchVar", new IdentifierExpression(Pattern.AnyString)), - new DirectionExpression { - FieldDirection = FieldDirection.Out, - Expression = new IdentifierExpression(Pattern.AnyString).WithName("intVar") - }), - TrueStatement = new BlockStatement { - Statements = { - new NamedNode( - "switch", new SwitchStatement { - Expression = new IdentifierExpressionBackreference("intVar"), - SwitchSections = { new Repeat(new AnyNode()) } - }) - } - } - }, - new Repeat(new AnyNode("nonNullDefaultStmt")).ToStatement() - }, - FalseStatement = new OptionalNode("nullStmt", new BlockStatement { Statements = { new Repeat(new AnyNode()) } }) - }; - - public SwitchStatement TransformSwitchOnString(IfElseStatement node) - { - Match m = switchOnStringPattern.Match(node); - if (!m.Success) - return null; - // switchVar must be the same as switchExpr; or switchExpr must be an assignment and switchVar the left side of that assignment - if (!m.Get("switchVar").Single().IsMatch(m.Get("switchExpr").Single())) { - AssignmentExpression assign = m.Get("switchExpr").Single() as AssignmentExpression; - if (!(assign != null && m.Get("switchVar").Single().IsMatch(assign.Left))) - return null; - } - FieldReference cachedDictField = m.Get("cachedDict").Single().Annotation(); - if (cachedDictField == null) - return null; - List dictCreation = m.Get("dictCreation").Single().Statements.ToList(); - List> dict = BuildDictionary(dictCreation); - SwitchStatement sw = m.Get("switch").Single(); - sw.Expression = m.Get("switchExpr").Single().Detach(); - foreach (SwitchSection section in sw.SwitchSections) { - List labels = section.CaseLabels.ToList(); - section.CaseLabels.Clear(); - foreach (CaseLabel label in labels) { - PrimitiveExpression expr = label.Expression as PrimitiveExpression; - if (expr == null || !(expr.Value is int)) - continue; - int val = (int)expr.Value; - foreach (var pair in dict) { - if (pair.Value == val) - section.CaseLabels.Add(new CaseLabel { Expression = new PrimitiveExpression(pair.Key) }); - } - } - } - if (m.Has("nullStmt")) { - SwitchSection section = new SwitchSection(); - section.CaseLabels.Add(new CaseLabel { Expression = new NullReferenceExpression() }); - BlockStatement block = m.Get("nullStmt").Single(); - block.Statements.Add(new BreakStatement()); - section.Statements.Add(block.Detach()); - sw.SwitchSections.Add(section); - } else if (m.Has("nonNullDefaultStmt")) { - sw.SwitchSections.Add( - new SwitchSection { - CaseLabels = { new CaseLabel { Expression = new NullReferenceExpression() } }, - Statements = { new BlockStatement { new BreakStatement() } } - }); - } - if (m.Has("nonNullDefaultStmt")) { - SwitchSection section = new SwitchSection(); - section.CaseLabels.Add(new CaseLabel()); - BlockStatement block = new BlockStatement(); - block.Statements.AddRange(m.Get("nonNullDefaultStmt").Select(s => s.Detach())); - block.Add(new BreakStatement()); - section.Statements.Add(block); - sw.SwitchSections.Add(section); - } - node.ReplaceWith(sw); - return sw; - } - - List> BuildDictionary(List dictCreation) - { - if (context.Settings.ObjectOrCollectionInitializers && dictCreation.Count == 1) - return BuildDictionaryFromInitializer(dictCreation[0]); - return BuildDictionaryFromAddMethodCalls(dictCreation); - } - - static readonly Statement assignInitializedDictionary = new ExpressionStatement { - Expression = new AssignmentExpression { - Left = new AnyNode().ToExpression(), - Right = new ObjectCreateExpression { - Type = new AnyNode(), - Arguments = { new Repeat(new AnyNode()) }, - Initializer = new ArrayInitializerExpression { - Elements = { new Repeat(new AnyNode("dictJumpTable")) } - } - }, - }, - }; - - private List> BuildDictionaryFromInitializer(Statement statement) - { - List> dict = new List>(); - Match m = assignInitializedDictionary.Match(statement); - if (!m.Success) - return dict; - - foreach (ArrayInitializerExpression initializer in m.Get("dictJumpTable")) { - KeyValuePair pair; - if (TryGetPairFrom(initializer.Elements, out pair)) - dict.Add(pair); - } - - return dict; - } - - private static List> BuildDictionaryFromAddMethodCalls(List dictCreation) - { - List> dict = new List>(); - for (int i = 0; i < dictCreation.Count; i++) { - ExpressionStatement es = dictCreation[i] as ExpressionStatement; - if (es == null) - continue; - InvocationExpression ie = es.Expression as InvocationExpression; - if (ie == null) - continue; - - KeyValuePair pair; - if (TryGetPairFrom(ie.Arguments, out pair)) - dict.Add(pair); - } - return dict; - } - - private static bool TryGetPairFrom(AstNodeCollection expressions, out KeyValuePair pair) - { - PrimitiveExpression arg1 = expressions.ElementAtOrDefault(0) as PrimitiveExpression; - PrimitiveExpression arg2 = expressions.ElementAtOrDefault(1) as PrimitiveExpression; - if (arg1 != null && arg2 != null && arg1.Value is string && arg2.Value is int) { - pair = new KeyValuePair((string)arg1.Value, (int)arg2.Value); - return true; - } - - pair = default(KeyValuePair); - return false; - } - - #endregion - #region Automatic Properties static readonly PropertyDeclaration automaticPropertyPattern = new PropertyDeclaration { Attributes = { new Repeat(new AnyNode()) }, diff --git a/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs b/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs index 0a64e70ca..3c1c86d84 100644 --- a/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs @@ -303,7 +303,7 @@ namespace ICSharpCode.Decompiler.CSharp } var rr = expressionBuilder.resolver.WithCheckForOverflow(checkForOverflow).ResolveCast(targetType, ResolveResult); if (rr.IsCompileTimeConstant && !rr.IsError) { - return expressionBuilder.ConvertConstantValue(rr) + return expressionBuilder.ConvertConstantValue(rr, allowImplicitConversion) .WithILInstruction(this.ILInstructions); } if (targetType.Kind == TypeKind.Pointer && (0.Equals(ResolveResult.ConstantValue) || 0u.Equals(ResolveResult.ConstantValue))) { diff --git a/ICSharpCode.Decompiler/DotNetCore/DotNetCorePathFinderExtensions.cs b/ICSharpCode.Decompiler/DotNetCore/DotNetCorePathFinderExtensions.cs index ced78ebf2..0fe9b4d63 100644 --- a/ICSharpCode.Decompiler/DotNetCore/DotNetCorePathFinderExtensions.cs +++ b/ICSharpCode.Decompiler/DotNetCore/DotNetCorePathFinderExtensions.cs @@ -8,7 +8,6 @@ using Newtonsoft.Json.Linq; namespace ICSharpCode.Decompiler { - public static class DotNetCorePathFinderExtensions { public static string DetectTargetFrameworkId(this AssemblyDefinition assembly) diff --git a/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowNode.cs b/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowNode.cs index f9aaabb30..f0a72bf20 100644 --- a/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowNode.cs +++ b/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowNode.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.FlowAnalysis { @@ -118,35 +119,37 @@ namespace ICSharpCode.Decompiler.FlowAnalysis } return false; } - - //public static GraphVizGraph ExportGraph(IReadOnlyList nodes, Func labelFunc = null) - //{ - // if (labelFunc == null) { - // labelFunc = node => { - // var block = node.UserData as IL.Block; - // return block != null ? block.Label : node.UserData?.ToString(); - // }; - // } - // GraphVizGraph g = new GraphVizGraph(); - // GraphVizNode[] n = new GraphVizNode[nodes.Count]; - // for (int i = 0; i < n.Length; i++) { - // n[i] = new GraphVizNode(nodes[i].UserIndex); - // n[i].shape = "box"; - // n[i].label = labelFunc(nodes[i]); - // g.AddNode(n[i]); - // } - // foreach (var source in nodes) { - // foreach (var target in source.Successors) { - // g.AddEdge(new GraphVizEdge(source.UserIndex, target.UserIndex)); - // } - // if (source.ImmediateDominator != null) { - // g.AddEdge( - // new GraphVizEdge(source.ImmediateDominator.UserIndex, source.UserIndex) { - // color = "green" - // }); - // } - // } - // return g; - //} + +#if DEBUG + internal static GraphVizGraph ExportGraph(IReadOnlyList nodes, Func labelFunc = null) + { + if (labelFunc == null) { + labelFunc = node => { + var block = node.UserData as IL.Block; + return block != null ? block.Label : node.UserData?.ToString(); + }; + } + GraphVizGraph g = new GraphVizGraph(); + GraphVizNode[] n = new GraphVizNode[nodes.Count]; + for (int i = 0; i < n.Length; i++) { + n[i] = new GraphVizNode(nodes[i].UserIndex); + n[i].shape = "box"; + n[i].label = labelFunc(nodes[i]); + g.AddNode(n[i]); + } + foreach (var source in nodes) { + foreach (var target in source.Successors) { + g.AddEdge(new GraphVizEdge(source.UserIndex, target.UserIndex)); + } + if (source.ImmediateDominator != null) { + g.AddEdge( + new GraphVizEdge(source.ImmediateDominator.UserIndex, source.UserIndex) { + color = "green" + }); + } + } + return g; + } +#endif } } diff --git a/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs b/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs index 2ac18d313..034e33798 100644 --- a/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs +++ b/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs @@ -614,17 +614,17 @@ namespace ICSharpCode.Decompiler.FlowAnalysis DebugStartPoint(inst); inst.Value.AcceptVisitor(this); State beforeSections = state.Clone(); - inst.DefaultBody.AcceptVisitor(this); + inst.Sections[0].AcceptVisitor(this); State afterSections = state.Clone(); - foreach (var section in inst.Sections) { + for (int i = 1; i < inst.Sections.Count; ++i) { state.ReplaceWith(beforeSections); - section.AcceptVisitor(this); + inst.Sections[i].AcceptVisitor(this); afterSections.JoinWith(state); } state = afterSections; DebugEndPoint(inst); } - + protected internal override void VisitYieldReturn(YieldReturn inst) { DebugStartPoint(inst); diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index e68291245..a1e17edfd 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -224,6 +224,7 @@ + @@ -274,6 +275,11 @@ + + + + + @@ -295,6 +301,7 @@ + diff --git a/ICSharpCode.Decompiler/IL/BlockBuilder.cs b/ICSharpCode.Decompiler/IL/BlockBuilder.cs index 40ded7446..63d02a971 100644 --- a/ICSharpCode.Decompiler/IL/BlockBuilder.cs +++ b/ICSharpCode.Decompiler/IL/BlockBuilder.cs @@ -163,8 +163,15 @@ namespace ICSharpCode.Decompiler.IL if (currentBlock == null) return; currentBlock.ILRange = new Interval(currentBlock.ILRange.Start, currentILOffset); - if (fallthrough) - currentBlock.Instructions.Add(new Branch(currentILOffset)); + if (fallthrough) { + if (currentBlock.Instructions.LastOrDefault() is SwitchInstruction switchInst && switchInst.Sections.Last().Body.MatchNop()) { + // Instead of putting the default branch after the switch instruction + switchInst.Sections.Last().Body = new Branch(currentILOffset); + Debug.Assert(switchInst.HasFlag(InstructionFlags.EndPointUnreachable)); + } else { + currentBlock.Instructions.Add(new Branch(currentILOffset)); + } + } currentBlock = null; } diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs index 268b9a429..7a05ecd92 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs @@ -63,18 +63,14 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow // Last instruction is one with unreachable endpoint // (guaranteed by combination of BlockContainer and Block invariants) Debug.Assert(block.Instructions.Last().HasFlag(InstructionFlags.EndPointUnreachable)); - ILInstruction exitInst = block.Instructions.Last(); + ILInstruction exitInst = block.Instructions.Last(); + // Previous-to-last instruction might have conditional control flow, // usually an IfInstruction with a branch: IfInstruction ifInst = block.Instructions.SecondToLastOrDefault() as IfInstruction; if (ifInst != null && ifInst.FalseInst.OpCode == OpCode.Nop) { HandleIfInstruction(cfgNode, block, ifInst, ref exitInst); - } else { - SwitchInstruction switchInst = block.Instructions.SecondToLastOrDefault() as SwitchInstruction; - if (switchInst != null) { - HandleSwitchInstruction(cfgNode, block, switchInst, ref exitInst); - } } if (IsUsableBranchToChild(cfgNode, exitInst)) { // "...; goto usableblock;" @@ -288,48 +284,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow && targetBlock.IncomingEdgeCount == 1 && targetBlock.FinalInstruction.OpCode == OpCode.Nop && cfgNode.Dominates(context.ControlFlowGraph.GetNode(targetBlock)); } - - private void HandleSwitchInstruction(ControlFlowNode cfgNode, Block block, SwitchInstruction sw, ref ILInstruction exitInst) - { - Debug.Assert(sw.DefaultBody is Nop); - // First, move blocks into the switch section - foreach (var section in sw.Sections) { - if (IsUsableBranchToChild(cfgNode, section.Body)) { - // case ...: goto targetBlock; - var targetBlock = ((Branch)section.Body).TargetBlock; - targetBlock.Remove(); - section.Body = targetBlock; - } - } - // Move the code following the switch into the default section - if (IsUsableBranchToChild(cfgNode, exitInst)) { - // switch(...){} goto targetBlock; - // ---> switch(..) { default: { targetBlock } } - var targetBlock = ((Branch)exitInst).TargetBlock; - targetBlock.Remove(); - sw.DefaultBody = targetBlock; - if (IsBranchOrLeave(targetBlock.Instructions.Last())) { - exitInst = block.Instructions[block.Instructions.Count - 1] = targetBlock.Instructions.Last(); - targetBlock.Instructions.RemoveAt(targetBlock.Instructions.Count - 1); - } else { - exitInst = null; - block.Instructions.RemoveAt(block.Instructions.Count - 1); - } - } - // Remove compatible exitInsts from switch sections: - foreach (var section in sw.Sections) { - Block sectionBlock = section.Body as Block; - if (sectionBlock != null && exitInst == null && IsBranchOrLeave(sectionBlock.Instructions.Last())) { - exitInst = sectionBlock.Instructions.Last(); - sectionBlock.Instructions.RemoveAt(sectionBlock.Instructions.Count - 1); - block.Instructions.Add(exitInst); - } else if (sectionBlock != null && DetectExitPoints.CompatibleExitInstruction(exitInst, sectionBlock.Instructions.Last())) { - sectionBlock.Instructions.RemoveAt(sectionBlock.Instructions.Count - 1); - } - } - sw.Sections.ReplaceList(sw.Sections.OrderBy(s => s.Body.ILRange.Start)); - } - + private bool IsBranchOrLeave(ILInstruction inst) { switch (inst) { diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs b/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs index 695af22a4..a896ca11a 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs @@ -19,6 +19,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.IL.Transforms; +using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL.ControlFlow { @@ -52,7 +53,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow SwitchDetection.SimplifySwitchInstruction(block); } SimplifyBranchChains(function, context); - CleanUpEmptyBlocks(function); + CleanUpEmptyBlocks(function, context); } void InlineVariableInReturnBlock(Block block, ILTransformContext context) @@ -129,13 +130,13 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow } } - void CleanUpEmptyBlocks(ILFunction function) + void CleanUpEmptyBlocks(ILFunction function, ILTransformContext context) { foreach (var container in function.Descendants.OfType()) { foreach (var block in container.Blocks) { if (block.Instructions.Count == 0) continue; // block is already marked for deletion - while (CombineBlockWithNextBlock(container, block)) { + while (CombineBlockWithNextBlock(container, block, context)) { // repeat combining blocks until it is no longer possible // (this loop terminates because a block is deleted in every iteration) } @@ -153,7 +154,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow return targetBlock.Instructions[0].MatchReturn(out var value) && value is LdLoc; } - static bool CombineBlockWithNextBlock(BlockContainer container, Block block) + static bool CombineBlockWithNextBlock(BlockContainer container, Block block, ILTransformContext context) { Debug.Assert(container == block.Parent); // Ensure the block will stay a basic block -- we don't want extended basic blocks prior to LoopDetection. @@ -165,12 +166,31 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow return false; if (br.TargetBlock == block) return false; // don't inline block into itself + context.Step("CombineBlockWithNextBlock", br); var targetBlock = br.TargetBlock; + if (targetBlock.ILRange.Start < block.ILRange.Start && IsDeadTrueStore(block)) { + // The C# compiler generates a dead store for the condition of while (true) loops. + block.Instructions.RemoveRange(block.Instructions.Count - 3, 2); + } block.Instructions.Remove(br); block.Instructions.AddRange(targetBlock.Instructions); targetBlock.Instructions.Clear(); // mark targetBlock for deletion return true; } - + + /// + /// Returns true if the last two instructions before the branch are storing the value 'true' into an unused variable. + /// + private static bool IsDeadTrueStore(Block block) + { + if (block.Instructions.Count < 3) return false; + if (!(block.Instructions.SecondToLastOrDefault() is StLoc deadStore && block.Instructions[block.Instructions.Count - 3] is StLoc tempStore)) + return false; + if (!(deadStore.Variable.LoadCount == 0 && deadStore.Variable.AddressCount == 0)) + return false; + if (!(deadStore.Value.MatchLdLoc(tempStore.Variable) && tempStore.Variable.IsSingleDefinition && tempStore.Variable.LoadCount == 1)) + return false; + return tempStore.Value.MatchLdcI4(1) && deadStore.Variable.Type.IsKnownType(KnownTypeCode.Boolean); + } } } diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs index b8086bee0..ce3d0f43d 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs @@ -16,6 +16,7 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -57,6 +58,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow // Because this is a post-order block transform, we can assume that // any nested loops within this loop have already been constructed. + if (block.Instructions.Last() is SwitchInstruction switchInst) { + // Switch instructions support "break;" just like loops + DetectSwitchBody(block, switchInst); + } + ControlFlowNode h = context.ControlFlowNode; // CFG node for our potential loop head Debug.Assert(h.UserData == block); Debug.Assert(!TreeTraversal.PreOrder(h, n => n.DominatorTreeChildren).Any(n => n.Visited)); @@ -101,7 +107,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow ConstructLoop(loop, exitPoint); } } - + /// /// Recurse into the dominator tree and find back edges/natural loops. /// @@ -202,9 +208,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow /// /// Precondition: Requires that a node is marked as visited iff it is contained in the loop. /// - void ExtendLoop(ControlFlowNode loopHead, List loop, out ControlFlowNode exitPoint) + void ExtendLoop(ControlFlowNode loopHead, List loop, out ControlFlowNode exitPoint, bool isSwitch=false) { - exitPoint = FindExitPoint(loopHead, loop); + exitPoint = FindExitPoint(loopHead, loop, isSwitch); Debug.Assert(!loop.Contains(exitPoint), "Cannot pick an exit point that is part of the natural loop"); if (exitPoint != null) { // Either we are in case 1 and just picked an exit that maximizes the amount of code @@ -227,9 +233,15 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow /// Finds a suitable single exit point for the specified loop. /// /// This method must not write to the Visited flags on the CFG. - ControlFlowNode FindExitPoint(ControlFlowNode loopHead, IReadOnlyList naturalLoop) + ControlFlowNode FindExitPoint(ControlFlowNode loopHead, IReadOnlyList naturalLoop, bool treatBackEdgesAsExits) { - if (!context.ControlFlowGraph.HasReachableExit(loopHead)) { + 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)); + } + if (!hasReachableExit) { // Case 1: // There are no nodes n so that loopHead dominates a predecessor of n but not n itself // -> we could build a loop with zero exit points. @@ -244,7 +256,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); + var revCfg = PrepareReverseCFG(loopHead, treatBackEdgesAsExits); //ControlFlowNode.ExportGraph(cfg).Show("cfg"); //ControlFlowNode.ExportGraph(revCfg).Show("rev"); ControlFlowNode commonAncestor = revCfg[loopHead.UserIndex]; @@ -255,22 +267,63 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow commonAncestor = Dominance.FindCommonDominator(commonAncestor, revNode); } } + // All paths from within the loop to a reachable exit run through 'commonAncestor'. + // However, this doesn't mean that 'commonAncestor' is valid as an exit point. + // We walk up the post-dominator tree until we've got a valid exit point: ControlFlowNode exitPoint; while (commonAncestor.UserIndex >= 0) { exitPoint = cfg[commonAncestor.UserIndex]; Debug.Assert(exitPoint.Visited == naturalLoop.Contains(exitPoint)); - if (exitPoint.Visited) { - commonAncestor = commonAncestor.ImmediateDominator; - continue; - } else { + // It's possible that 'commonAncestor' is itself part of the natural loop. + // If so, it's not a valid exit point. + if (!exitPoint.Visited && ValidateExitPoint(loopHead, exitPoint)) { + // we found an exit point return exitPoint; } + commonAncestor = commonAncestor.ImmediateDominator; } - // least common dominator is the artificial exit node + // least common post-dominator is the artificial exit node return null; } } + /// + /// Validates an exit point. + /// + /// An exit point is invalid iff there is a node reachable from the exit point that + /// is dominated by the loop head, but not by the exit point. + /// (i.e. this method returns false iff the exit point's dominance frontier contains + /// a node dominated by the loop head. but we implement this the slow way because + /// we don't have dominance frontiers precomputed) + /// + /// + /// We need this because it's possible that there's a return block (thus reverse-unreachable node ignored by post-dominance) + /// that is reachable both directly from the loop, and from the exit point. + /// + bool ValidateExitPoint(ControlFlowNode loopHead, ControlFlowNode exitPoint) + { + var cfg = context.ControlFlowGraph; + return IsValid(exitPoint); + + bool IsValid(ControlFlowNode node) + { + if (!cfg.HasReachableExit(node)) { + // Optimization: if the dominance frontier is empty, we don't need + // to check every node. + return true; + } + foreach (var succ in node.Successors) { + if (loopHead != succ && loopHead.Dominates(succ) && !exitPoint.Dominates(succ)) + return false; + } + foreach (var child in node.DominatorTreeChildren) { + if (!IsValid(child)) + return false; + } + return true; + } + } + /// /// Pick exit point by picking any node that has no reachable exits. /// @@ -308,7 +361,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow } } - ControlFlowNode[] PrepareReverseCFG(ControlFlowNode loopHead) + ControlFlowNode[] PrepareReverseCFG(ControlFlowNode loopHead, bool treatBackEdgesAsExits) { ControlFlowNode[] cfg = context.ControlFlowGraph.cfg; ControlFlowNode[] rev = new ControlFlowNode[cfg.Length + 1]; @@ -322,7 +375,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow continue; // Add reverse edges for all edges in cfg foreach (var succ in cfg[i].Successors) { - if (loopHead.Dominates(succ)) { + if (loopHead.Dominates(succ) && (!treatBackEdgesAsExits || loopHead != succ)) { rev[succ.UserIndex].AddEdgeTo(rev[i]); } else { exitNode.AddEdgeTo(rev[i]); @@ -412,13 +465,25 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow // Move contents of oldEntryPoint to newEntryPoint // (we can't move the block itself because it might be the target of branch instructions outside the loop) newEntryPoint.Instructions.ReplaceList(oldEntryPoint.Instructions); - newEntryPoint.FinalInstruction = oldEntryPoint.FinalInstruction; newEntryPoint.ILRange = oldEntryPoint.ILRange; oldEntryPoint.Instructions.ReplaceList(new[] { loopContainer }); if (exitTargetBlock != null) oldEntryPoint.Instructions.Add(new Branch(exitTargetBlock)); - oldEntryPoint.FinalInstruction = new Nop(); - + + MoveBlocksIntoContainer(loop, loopContainer); + + // Rewrite branches within the loop from oldEntryPoint to newEntryPoint: + foreach (var branch in loopContainer.Descendants.OfType()) { + if (branch.TargetBlock == oldEntryPoint) { + branch.TargetBlock = newEntryPoint; + } else if (branch.TargetBlock == exitTargetBlock) { + branch.ReplaceWith(new Leave(loopContainer) { ILRange = branch.ILRange }); + } + } + } + + private void MoveBlocksIntoContainer(List loop, BlockContainer loopContainer) + { // Move other blocks into the loop body: they're all dominated by the loop header, // and thus cannot be the target of branch instructions outside the loop. for (int i = 1; i < loop.Count; i++) { @@ -440,12 +505,56 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow Block block = (Block)loop[i].UserData; Debug.Assert(block.IsDescendantOf(loopContainer)); } + } + + private void DetectSwitchBody(Block block, SwitchInstruction switchInst) + { + Debug.Assert(block.Instructions.Last() == switchInst); + ControlFlowNode h = context.ControlFlowNode; // CFG node for our switch head + Debug.Assert(h.UserData == block); + Debug.Assert(!TreeTraversal.PreOrder(h, n => n.DominatorTreeChildren).Any(n => n.Visited)); + + var nodesInSwitch = new List(); + 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 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, + // nesting switches makes it clearer in which cases a piece of code is reachable) + nodesInSwitch.AddRange(TreeTraversal.PreOrder(exitPoint, p => p.DominatorTreeChildren)); + exitPoint = null; + } + + context.Step("Create BlockContainer for switch", switchInst); + // Sort blocks in the loop in reverse post-order to make the output look a bit nicer. + // (if the loop doesn't contain nested loops, this is a topological sort) + nodesInSwitch.Sort((a, b) => b.PostOrderNumber.CompareTo(a.PostOrderNumber)); + Debug.Assert(nodesInSwitch[0] == h); + foreach (var node in nodesInSwitch) { + node.Visited = false; // reset visited flag so that we can find outer loops + Debug.Assert(h.Dominates(node) || !node.IsReachable, "The switch body must be dominated by the switch head"); + } + + BlockContainer switchContainer = new BlockContainer(); + Block newEntryPoint = new Block(); + newEntryPoint.ILRange = switchInst.ILRange; + switchContainer.Blocks.Add(newEntryPoint); + newEntryPoint.Instructions.Add(switchInst); + block.Instructions[block.Instructions.Count - 1] = switchContainer; + + Block exitTargetBlock = (Block)exitPoint?.UserData; + if (exitTargetBlock != null) { + block.Instructions.Add(new Branch(exitTargetBlock)); + } + + MoveBlocksIntoContainer(nodesInSwitch, switchContainer); + // Rewrite branches within the loop from oldEntryPoint to newEntryPoint: - foreach (var branch in loopContainer.Descendants.OfType()) { - if (branch.TargetBlock == oldEntryPoint) { - branch.TargetBlock = newEntryPoint; - } else if (branch.TargetBlock == exitTargetBlock) { - branch.ReplaceWith(new Leave(loopContainer) { ILRange = branch.ILRange }); + foreach (var branch in switchContainer.Descendants.OfType()) { + if (branch.TargetBlock == exitTargetBlock) { + branch.ReplaceWith(new Leave(switchContainer) { ILRange = branch.ILRange }); } } } diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/StateRangeAnalysis.cs b/ICSharpCode.Decompiler/IL/ControlFlow/StateRangeAnalysis.cs index fa4fa097c..9ef815526 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/StateRangeAnalysis.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/StateRangeAnalysis.cs @@ -140,7 +140,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow SymbolicValue val = evalContext.Eval(switchInst.Value); if (val.Type != SymbolicValueType.State) goto default; - List allSectionLabels = new List(); List exitIntervals = new List(); foreach (var section in switchInst.Sections) { // switch (state + Constant) @@ -148,12 +147,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow // iff (state + Constant == value) // iff (state == value - Constant) var effectiveLabels = section.Labels.AddOffset(unchecked(-val.Constant)); - allSectionLabels.AddRange(effectiveLabels.Intervals); var result = AssignStateRanges(section.Body, stateRange.IntersectWith(effectiveLabels)); exitIntervals.AddRange(result.Intervals); } - var defaultSectionLabels = stateRange.ExceptWith(new LongSet(allSectionLabels)); - exitIntervals.AddRange(AssignStateRanges(switchInst.DefaultBody, defaultSectionLabels).Intervals); // exitIntervals = union of exits of all sections return new LongSet(exitIntervals); case IfInstruction ifInst: diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/SwitchAnalysis.cs b/ICSharpCode.Decompiler/IL/ControlFlow/SwitchAnalysis.cs index 928b934c6..20767b136 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/SwitchAnalysis.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/SwitchAnalysis.cs @@ -42,8 +42,18 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow /// public readonly List> Sections = new List>(); + /// + /// Used to de-duplicate sections with a branch instruction. + /// Invariant: (Sections[targetBlockToSectionIndex[branch.TargetBlock]].Instruction as Branch).TargetBlock == branch.TargetBlock + /// readonly Dictionary targetBlockToSectionIndex = new Dictionary(); + /// + /// Used to de-duplicate sections with a value-less leave instruction. + /// Invariant: (Sections[targetBlockToSectionIndex[leave.TargetContainer]].Instruction as Leave).TargetContainer == leave.TargetContainer + /// + readonly Dictionary targetContainerToSectionIndex = new Dictionary(); + /// /// Blocks that can be deleted if the tail of the initial block is replaced with a switch instruction. /// @@ -61,6 +71,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow switchVar = null; rootBlock = block; targetBlockToSectionIndex.Clear(); + targetContainerToSectionIndex.Clear(); Sections.Clear(); InnerBlocks.Clear(); ContainsILSwitch = false; @@ -83,25 +94,26 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow /// If false, analyze the whole block. bool AnalyzeBlock(Block block, LongSet inputValues, bool tailOnly = false) { + if (block.Instructions.Count == 0) { + // might happen if the block was already marked for deletion in SwitchDetection + return false; + } if (tailOnly) { Debug.Assert(block == rootBlock); - if (block.Instructions.Count < 2) - return false; } else { Debug.Assert(switchVar != null); // switchVar should always be determined by the top-level call if (block.IncomingEdgeCount != 1 || block == rootBlock) return false; // for now, let's only consider if-structures that form a tree - if (block.Instructions.Count != 2) - return false; if (block.Parent != rootBlock.Parent) return false; // all blocks should belong to the same container } - var inst = block.Instructions[block.Instructions.Count - 2]; - ILInstruction condition, trueInst; LongSet trueValues; - if (inst.MatchIfInstruction(out condition, out trueInst) + if (block.Instructions.Count >= 2 + && block.Instructions[block.Instructions.Count - 2].MatchIfInstruction(out var condition, out var trueInst) && AnalyzeCondition(condition, out trueValues) ) { + if (!(tailOnly || block.Instructions.Count == 2)) + return false; trueValues = trueValues.IntersectWith(inputValues); Block trueBlock; if (trueInst.MatchBranch(out trueBlock) && AnalyzeBlock(trueBlock, trueValues)) { @@ -111,9 +123,12 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow // Create switch section for trueInst. AddSection(trueValues, trueInst); } - } else if (inst.OpCode == OpCode.SwitchInstruction) { - if (AnalyzeSwitch((SwitchInstruction)inst, inputValues, out trueValues)) { + } else if (block.Instructions.Last() is SwitchInstruction switchInst) { + if (!(tailOnly || block.Instructions.Count == 1)) + return false; + if (AnalyzeSwitch(switchInst, inputValues)) { ContainsILSwitch = true; // OK + return true; } else { // switch analysis failed (e.g. switchVar mismatch) return false; } @@ -134,10 +149,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow return true; } - private bool AnalyzeSwitch(SwitchInstruction inst, LongSet inputValues, out LongSet anyMatchValues) + private bool AnalyzeSwitch(SwitchInstruction inst, LongSet inputValues) { - Debug.Assert(inst.DefaultBody is Nop); - anyMatchValues = LongSet.Empty; + Debug.Assert(!inst.IsLifted); long offset; if (MatchSwitchVar(inst.Value)) { offset = 0; @@ -163,8 +177,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow } foreach (var section in inst.Sections) { var matchValues = section.Labels.AddOffset(offset).IntersectWith(inputValues); - AddSection(matchValues, section.Body); - anyMatchValues = anyMatchValues.UnionWith(matchValues); + if (matchValues.Count() > 1 && section.Body.MatchBranch(out var targetBlock) && AnalyzeBlock(targetBlock, matchValues)) { + InnerBlocks.Add(targetBlock); + } else { + AddSection(matchValues, section.Body); + } } return true; } @@ -180,10 +197,8 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow if (values.IsEmpty) { return; } - Block targetBlock; - if (inst.MatchBranch(out targetBlock)) { - int index; - if (targetBlockToSectionIndex.TryGetValue(targetBlock, out index)) { + if (inst.MatchBranch(out Block targetBlock)) { + if (targetBlockToSectionIndex.TryGetValue(targetBlock, out int index)) { Sections[index] = new KeyValuePair( Sections[index].Key.UnionWith(values), inst @@ -192,6 +207,16 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow targetBlockToSectionIndex.Add(targetBlock, Sections.Count); Sections.Add(new KeyValuePair(values, inst)); } + } else if (inst.MatchLeave(out BlockContainer targetContainer)) { + if (targetContainerToSectionIndex.TryGetValue(targetContainer, out int index)) { + Sections[index] = new KeyValuePair( + Sections[index].Key.UnionWith(values), + inst + ); + } else { + targetContainerToSectionIndex.Add(targetContainer, Sections.Count); + Sections.Add(new KeyValuePair(values, inst)); + } } else { Sections.Add(new KeyValuePair(values, inst)); } diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs index 897d41e7e..8473c17bf 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs @@ -60,16 +60,20 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow var sw = new SwitchInstruction(new LdLoc(analysis.SwitchVariable)); foreach (var section in analysis.Sections) { - if (!section.Key.SetEquals(defaultSection.Key)) { - sw.Sections.Add(new SwitchSection - { - Labels = section.Key, - Body = section.Value - }); - } + sw.Sections.Add(new SwitchSection { + Labels = section.Key, + Body = section.Value + }); + } + if (block.Instructions.Last() is SwitchInstruction) { + // we'll replace the switch + } else { + Debug.Assert(block.Instructions.SecondToLastOrDefault() is IfInstruction); + // Remove branch/leave after if; it's getting moved into a section. + block.Instructions.RemoveAt(block.Instructions.Count - 1); } - block.Instructions[block.Instructions.Count - 2] = sw; - block.Instructions[block.Instructions.Count - 1] = defaultSection.Value; + block.Instructions[block.Instructions.Count - 1] = sw; + // mark all inner blocks that were converted to the switch statement for deletion foreach (var innerBlock in analysis.InnerBlocks) { Debug.Assert(innerBlock.Parent == block.Parent); @@ -77,6 +81,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow innerBlock.Instructions.Clear(); } blockContainerNeedsCleanup = true; + SortSwitchSections(sw); } else { // 2nd pass of SimplifySwitchInstruction (after duplicating return blocks), // (1st pass was in ControlFlowSimplification) @@ -87,8 +92,8 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow internal static void SimplifySwitchInstruction(Block block) { // due to our of of basic blocks at this point, - // switch instructions can only appear as second-to-last insturction - var sw = block.Instructions.SecondToLastOrDefault() as SwitchInstruction; + // switch instructions can only appear as last insturction + var sw = block.Instructions.LastOrDefault() as SwitchInstruction; if (sw == null) return; @@ -96,19 +101,13 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow // Any switch instructions will only have branch instructions in the sections. // Combine sections with identical branch target: - block.Instructions.Last().MatchBranch(out Block defaultTarget); var dict = new Dictionary(); // branch target -> switch section sw.Sections.RemoveAll( section => { - Block target; - if (section.Body.MatchBranch(out target)) { - SwitchSection primarySection; - if (target == defaultTarget) { - // This section is just an alternative for 'default'. - Debug.Assert(sw.DefaultBody is Nop); - return true; // remove this section - } else if (dict.TryGetValue(target, out primarySection)) { + if (section.Body.MatchBranch(out Block target)) { + if (dict.TryGetValue(target, out SwitchSection primarySection)) { primarySection.Labels = primarySection.Labels.UnionWith(section.Labels); + primarySection.HasNullLabel |= section.HasNullLabel; return true; // remove this section } else { dict.Add(target, section); @@ -117,6 +116,12 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow return false; }); AdjustLabels(sw); + SortSwitchSections(sw); + } + + static void SortSwitchSections(SwitchInstruction sw) + { + sw.Sections.ReplaceList(sw.Sections.OrderBy(s => (s.Body as Branch)?.TargetILOffset).ThenBy(s => s.Labels.Values.FirstOrDefault())); } static void AdjustLabels(SwitchInstruction sw) diff --git a/ICSharpCode.Decompiler/IL/DetectedLoop.cs b/ICSharpCode.Decompiler/IL/DetectedLoop.cs index 933d7e7d1..4d234be29 100644 --- a/ICSharpCode.Decompiler/IL/DetectedLoop.cs +++ b/ICSharpCode.Decompiler/IL/DetectedLoop.cs @@ -76,7 +76,7 @@ namespace ICSharpCode.Decompiler.IL conditions.Add(ifInst.Condition); i--; } - if (i == -1) { + if (i == -1 && conditions.Any()) { return b; } } diff --git a/ICSharpCode.Decompiler/IL/ILReader.cs b/ICSharpCode.Decompiler/IL/ILReader.cs index 3700399b3..866fe7afd 100644 --- a/ICSharpCode.Decompiler/IL/ILReader.cs +++ b/ICSharpCode.Decompiler/IL/ILReader.cs @@ -1264,7 +1264,10 @@ namespace ICSharpCode.Decompiler.IL section.Body = new Branch(target); instr.Sections.Add(section); } - + var defaultSection = new SwitchSection(); + defaultSection.Labels = new LongSet(new LongInterval(0, labels.Length)).Invert(); + defaultSection.Body = new Nop(); + instr.Sections.Add(defaultSection); return instr; } diff --git a/ICSharpCode.Decompiler/IL/Instructions.cs b/ICSharpCode.Decompiler/IL/Instructions.cs index a98f573bf..2afb023df 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions.cs @@ -164,6 +164,8 @@ namespace ICSharpCode.Decompiler.IL /// Converts an array pointer (O) to a reference to the first element, or to a null reference if the array is null or empty. /// Also used to convert a string to a reference to the first character. ArrayToPointer, + /// Maps a string value to an integer. This is used in switch(string). + StringToInt, /// Push a typed reference of type class onto the stack. MakeRefAny, /// Push the type token stored in a typed reference. @@ -1018,6 +1020,15 @@ namespace ICSharpCode.Decompiler.IL public sealed partial class Branch : SimpleInstruction { public override StackType ResultType { get { return StackType.Void; } } + protected override InstructionFlags ComputeFlags() + { + return InstructionFlags.EndPointUnreachable | InstructionFlags.MayBranch; + } + public override InstructionFlags DirectFlags { + get { + return InstructionFlags.EndPointUnreachable | InstructionFlags.MayBranch; + } + } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitBranch(this); @@ -1323,7 +1334,7 @@ namespace ICSharpCode.Decompiler.IL protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match) { var o = other as SwitchInstruction; - return o != null && Value.PerformMatch(o.Value, ref match) && DefaultBody.PerformMatch(o.DefaultBody, ref match) && Patterns.ListMatch.DoMatch(this.Sections, o.Sections, ref match); + return o != null && IsLifted == o.IsLifted && Value.PerformMatch(o.Value, ref match) && Patterns.ListMatch.DoMatch(this.Sections, o.Sections, ref match); } } } @@ -1395,7 +1406,7 @@ namespace ICSharpCode.Decompiler.IL protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match) { var o = other as SwitchSection; - return o != null && this.body.PerformMatch(o.body, ref match) && this.Labels.Intervals.SequenceEqual(o.Labels.Intervals); + return o != null && this.body.PerformMatch(o.body, ref match) && this.Labels.SetEquals(o.Labels) && this.HasNullLabel == o.HasNullLabel; } } } @@ -4332,6 +4343,87 @@ namespace ICSharpCode.Decompiler.IL } } namespace ICSharpCode.Decompiler.IL +{ + /// Maps a string value to an integer. This is used in switch(string). + public sealed partial class StringToInt : ILInstruction + { + public static readonly SlotInfo ArgumentSlot = new SlotInfo("Argument", canInlineInto: true); + ILInstruction argument; + public ILInstruction Argument { + get { return this.argument; } + set { + ValidateChild(value); + SetChildInstruction(ref this.argument, value, 0); + } + } + protected sealed override int GetChildCount() + { + return 1; + } + protected sealed override ILInstruction GetChild(int index) + { + switch (index) { + case 0: + return this.argument; + default: + throw new IndexOutOfRangeException(); + } + } + protected sealed override void SetChild(int index, ILInstruction value) + { + switch (index) { + case 0: + this.Argument = value; + break; + default: + throw new IndexOutOfRangeException(); + } + } + protected sealed override SlotInfo GetChildSlot(int index) + { + switch (index) { + case 0: + return ArgumentSlot; + default: + throw new IndexOutOfRangeException(); + } + } + public sealed override ILInstruction Clone() + { + var clone = (StringToInt)ShallowClone(); + clone.Argument = this.argument.Clone(); + return clone; + } + public override StackType ResultType { get { return StackType.I4; } } + protected override InstructionFlags ComputeFlags() + { + return argument.Flags; + } + public override InstructionFlags DirectFlags { + get { + return InstructionFlags.None; + } + } + public override void AcceptVisitor(ILVisitor visitor) + { + visitor.VisitStringToInt(this); + } + public override T AcceptVisitor(ILVisitor visitor) + { + return visitor.VisitStringToInt(this); + } + public override T AcceptVisitor(ILVisitor visitor, C context) + { + return visitor.VisitStringToInt(this, context); + } + protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match) + { + var o = other as StringToInt; + return o != null && this.argument.PerformMatch(o.argument, ref match); + } + } +} +namespace ICSharpCode.Decompiler.IL { /// Push a typed reference of type class onto the stack. public sealed partial class MakeRefAny : UnaryInstruction @@ -4956,6 +5048,10 @@ namespace ICSharpCode.Decompiler.IL { Default(inst); } + protected internal virtual void VisitStringToInt(StringToInt inst) + { + Default(inst); + } protected internal virtual void VisitMakeRefAny(MakeRefAny inst) { Default(inst); @@ -5254,6 +5350,10 @@ namespace ICSharpCode.Decompiler.IL { return Default(inst); } + protected internal virtual T VisitStringToInt(StringToInt inst) + { + return Default(inst); + } protected internal virtual T VisitMakeRefAny(MakeRefAny inst) { return Default(inst); @@ -5552,6 +5652,10 @@ namespace ICSharpCode.Decompiler.IL { return Default(inst, context); } + protected internal virtual T VisitStringToInt(StringToInt inst, C context) + { + return Default(inst, context); + } protected internal virtual T VisitMakeRefAny(MakeRefAny inst, C context) { return Default(inst, context); @@ -5644,6 +5748,7 @@ namespace ICSharpCode.Decompiler.IL "ldlen", "ldelema", "array.to.pointer", + "string.to.int", "mkrefany", "refanytype", "refanyval", diff --git a/ICSharpCode.Decompiler/IL/Instructions.tt b/ICSharpCode.Decompiler/IL/Instructions.tt index eacecfe0f..598763260 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.tt +++ b/ICSharpCode.Decompiler/IL/Instructions.tt @@ -73,7 +73,7 @@ new OpCode("bit.not", "Bitwise NOT", Unary, CustomConstructor, MatchCondition("IsLifted == o.IsLifted && UnderlyingResultType == o.UnderlyingResultType")), new OpCode("arglist", "Retrieves the RuntimeArgumentHandle.", NoArguments, ResultType("O")), new OpCode("br", "Unconditional branch. goto target;", - CustomClassName("Branch"), NoArguments, CustomConstructor, UnconditionalBranch, MayBranch, CustomComputeFlags, + CustomClassName("Branch"), NoArguments, CustomConstructor, UnconditionalBranch, MayBranch, MatchCondition("this.TargetBlock == o.TargetBlock")), new OpCode("leave", "Unconditional branch to end of block container. Return is represented using IsLeavingFunction and an (optional) return value. The block container evaluates to the value produced by the argument of the leave instruction.", CustomConstructor, CustomArguments("value"), UnconditionalBranch, MayBranch, CustomWriteTo, CustomComputeFlags, @@ -93,11 +93,11 @@ }), CustomConstructor, CustomComputeFlags, CustomWriteTo), new OpCode("switch", "Switch statement", CustomClassName("SwitchInstruction"), CustomConstructor, CustomComputeFlags, CustomWriteTo, ResultType("Void"), - MatchCondition("Value.PerformMatch(o.Value, ref match) && DefaultBody.PerformMatch(o.DefaultBody, ref match) && Patterns.ListMatch.DoMatch(this.Sections, o.Sections, ref match)")), + MatchCondition("IsLifted == o.IsLifted && Value.PerformMatch(o.Value, ref match) && Patterns.ListMatch.DoMatch(this.Sections, o.Sections, ref match)")), new OpCode("switch.section", "Switch section within a switch statement", CustomClassName("SwitchSection"), CustomChildren(new [] { new ChildInfo("body") }), CustomConstructor, CustomComputeFlags, CustomWriteTo, ResultType("Void"), - MatchCondition("this.Labels.Intervals.SequenceEqual(o.Labels.Intervals)")), + MatchCondition("this.Labels.SetEquals(o.Labels) && this.HasNullLabel == o.HasNullLabel")), new OpCode("try.catch", "Try-catch statement.", BaseClass("TryInstruction"), CustomConstructor, CustomComputeFlags, CustomWriteTo, MatchCondition("TryBlock.PerformMatch(o.TryBlock, ref match)"), @@ -234,6 +234,8 @@ new OpCode("array.to.pointer", "Converts an array pointer (O) to a reference to the first element, or to a null reference if the array is null or empty." + Environment.NewLine + "Also used to convert a string to a reference to the first character.", CustomArguments("array"), ResultType("Ref")), + new OpCode("string.to.int", "Maps a string value to an integer. This is used in switch(string).", + CustomArguments("argument"), CustomConstructor, CustomWriteTo, ResultType("I4")), new OpCode("mkrefany", "Push a typed reference of type class onto the stack.", CustomClassName("MakeRefAny"), Unary, HasTypeOperand, ResultType("O")), diff --git a/ICSharpCode.Decompiler/IL/Instructions/Block.cs b/ICSharpCode.Decompiler/IL/Instructions/Block.cs index 3931beece..0c5f9b7cc 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/Block.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/Block.cs @@ -108,6 +108,9 @@ namespace ICSharpCode.Decompiler.IL // only the last instruction may have an unreachable endpoint Debug.Assert(!Instructions[i].HasFlag(InstructionFlags.EndPointUnreachable)); } + if (this.Type == BlockType.ControlFlow) { + Debug.Assert(finalInstruction.OpCode == OpCode.Nop); + } } public override StackType ResultType { diff --git a/ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs b/ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs index 5cf84221c..b70056841 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs @@ -154,7 +154,7 @@ namespace ICSharpCode.Decompiler.IL Debug.Assert(EntryPoint == Blocks[0]); Debug.Assert(!IsConnected || EntryPoint.IncomingEdgeCount >= 1); Debug.Assert(Blocks.All(b => b.HasFlag(InstructionFlags.EndPointUnreachable))); - Debug.Assert(Blocks.All(b => b.FinalInstruction.OpCode == OpCode.Nop)); + Debug.Assert(Blocks.All(b => b.Type == BlockType.ControlFlow)); // this also implies that the blocks don't use FinalInstruction } protected override InstructionFlags ComputeFlags() @@ -226,5 +226,15 @@ namespace ICSharpCode.Decompiler.IL } return null; } + + public static BlockContainer FindClosestSwitchContainer(ILInstruction inst) + { + while (inst != null) { + if (inst is BlockContainer bc && bc.entryPoint.Instructions.FirstOrDefault() is SwitchInstruction) + return bc; + inst = inst.Parent; + } + return null; + } } } diff --git a/ICSharpCode.Decompiler/IL/Instructions/Branch.cs b/ICSharpCode.Decompiler/IL/Instructions/Branch.cs index 8cfab388b..7085ab6a1 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/Branch.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/Branch.cs @@ -39,23 +39,10 @@ namespace ICSharpCode.Decompiler.IL public Branch(Block targetBlock) : base(OpCode.Branch) { - if (targetBlock == null) - throw new ArgumentNullException(nameof(targetBlock)); - this.targetBlock = targetBlock; + this.targetBlock = targetBlock ?? throw new ArgumentNullException(nameof(targetBlock)); this.targetILOffset = targetBlock.ILRange.Start; } - protected override InstructionFlags ComputeFlags() - { - return InstructionFlags.MayBranch | InstructionFlags.EndPointUnreachable; - } - - public override InstructionFlags DirectFlags { - get { - return InstructionFlags.MayBranch | InstructionFlags.EndPointUnreachable; - } - } - public int TargetILOffset { get { return targetBlock != null ? targetBlock.ILRange.Start : targetILOffset; } } diff --git a/ICSharpCode.Decompiler/IL/Instructions/LockInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/LockInstruction.cs index f0c1b1ebc..36cd8380e 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/LockInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/LockInstruction.cs @@ -38,36 +38,4 @@ namespace ICSharpCode.Decompiler.IL output.Write("}"); } } - - /// - /// IL using instruction. - /// Equivalent to: - /// - /// stloc v(resourceExpression) - /// try { - /// body - /// } finally { - /// v?.Dispose(); - /// } - /// - /// - /// - /// The value of v is undefined after the end of the body block. - /// - partial class UsingInstruction - { - public override void WriteTo(ITextOutput output, ILAstWritingOptions options) - { - output.Write("using ("); - Variable.WriteTo(output); - output.Write(" = "); - ResourceExpression.WriteTo(output, options); - output.WriteLine(") {"); - output.Indent(); - Body.WriteTo(output, options); - output.Unindent(); - output.WriteLine(); - output.Write("}"); - } - } } diff --git a/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs b/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs index 26b33c0f6..3382d6926 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs @@ -27,6 +27,11 @@ namespace ICSharpCode.Decompiler.IL return OpCode == OpCode.LdcI4 && ((LdcI4)this).Value == val; } + public bool MatchLdcF(double value) + { + return MatchLdcF(out var v) && v == value; + } + /// /// Matches either LdcI4 or LdcI8. /// @@ -321,6 +326,27 @@ namespace ICSharpCode.Decompiler.IL } } + /// + /// Matches 'comp(arg == ldnull)' + /// + public bool MatchCompEqualsNull(out ILInstruction arg) + { + if (!MatchCompEquals(out var left, out var right)) { + arg = null; + return false; + } + if (right.MatchLdNull()) { + arg = left; + return true; + } else if (left.MatchLdNull()) { + arg = right; + return true; + } else { + arg = null; + return false; + } + } + /// /// Matches comp(left != right) or logic.not(comp(left == right)). /// diff --git a/ICSharpCode.Decompiler/IL/Instructions/StringToInt.cs b/ICSharpCode.Decompiler/IL/Instructions/StringToInt.cs new file mode 100644 index 000000000..f804a3928 --- /dev/null +++ b/ICSharpCode.Decompiler/IL/Instructions/StringToInt.cs @@ -0,0 +1,45 @@ +// Copyright (c) 2017 Siegfried Pammer +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + + +namespace ICSharpCode.Decompiler.IL +{ + partial class StringToInt + { + public string[] Map { get; } + + public StringToInt(ILInstruction argument, string[] map) + : base(OpCode.StringToInt) + { + this.Argument = argument; + this.Map = map; + } + + public override void WriteTo(ITextOutput output, ILAstWritingOptions options) + { + output.Write("string.to.int ("); + Argument.WriteTo(output, options); + output.Write(", { "); + for (int i = 0; i < Map.Length; i++) { + if (i > 0) output.Write(", "); + output.Write($"[{i}] = \"{Map[i]}\""); + } + output.Write(" })"); + } + } +} diff --git a/ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs index 8e22ffe20..ae828983f 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs @@ -16,6 +16,7 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System; using System.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.Util; @@ -32,15 +33,19 @@ namespace ICSharpCode.Decompiler.IL partial class SwitchInstruction { public static readonly SlotInfo ValueSlot = new SlotInfo("Value", canInlineInto: true); - public static readonly SlotInfo DefaultBodySlot = new SlotInfo("DefaultBody"); public static readonly SlotInfo SectionSlot = new SlotInfo("Section", isCollection: true); - + + /// + /// If the switch instruction is lifted, the value instruction produces a value of type Nullable{T} for some + /// integral type T. The section with SwitchSection.HasNullLabel is called if the value is null. + /// + public bool IsLifted; + public SwitchInstruction(ILInstruction value) : base(OpCode.SwitchInstruction) { this.Value = value; - this.DefaultBody = new Nop(); - this.Sections = new InstructionCollection(this, 2); + this.Sections = new InstructionCollection(this, 1); } ILInstruction value; @@ -52,21 +57,11 @@ namespace ICSharpCode.Decompiler.IL } } - ILInstruction defaultBody; - - public ILInstruction DefaultBody { - get { return this.defaultBody; } - set { - ValidateChild(value); - SetChildInstruction(ref this.defaultBody, value, 1); - } - } - public readonly InstructionCollection Sections; protected override InstructionFlags ComputeFlags() { - var sectionFlags = defaultBody.Flags; + var sectionFlags = InstructionFlags.EndPointUnreachable; // neutral element for CombineBranches() foreach (var section in Sections) { sectionFlags = SemanticHelper.CombineBranches(sectionFlags, section.Flags); } @@ -81,15 +76,15 @@ namespace ICSharpCode.Decompiler.IL public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { - output.Write("switch ("); + output.Write("switch"); + if (IsLifted) + output.Write(".lifted"); + output.Write(" ("); value.WriteTo(output, options); output.Write(") "); output.MarkFoldStart("{...}"); output.WriteLine("{"); output.Indent(); - output.Write("default: "); - defaultBody.WriteTo(output, options); - output.WriteLine(); foreach (var section in this.Sections) { section.WriteTo(output, options); output.WriteLine(); @@ -101,34 +96,28 @@ namespace ICSharpCode.Decompiler.IL protected override int GetChildCount() { - return 2 + Sections.Count; + return 1 + Sections.Count; } protected override ILInstruction GetChild(int index) { if (index == 0) return value; - else if (index == 1) - return defaultBody; - return Sections[index - 2]; + return Sections[index - 1]; } protected override void SetChild(int index, ILInstruction value) { if (index == 0) Value = value; - else if (index == 1) - DefaultBody = value; else - Sections[index - 2] = (SwitchSection)value; + Sections[index - 1] = (SwitchSection)value; } protected override SlotInfo GetChildSlot(int index) { if (index == 0) return ValueSlot; - else if (index == 1) - return DefaultBodySlot; return SectionSlot; } @@ -137,7 +126,6 @@ namespace ICSharpCode.Decompiler.IL var clone = new SwitchInstruction(value.Clone()); clone.ILRange = this.ILRange; clone.Value = value.Clone(); - this.DefaultBody = defaultBody.Clone(); clone.Sections.AddRange(this.Sections.Select(h => (SwitchSection)h.Clone())); return clone; } @@ -145,12 +133,19 @@ namespace ICSharpCode.Decompiler.IL internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); + bool expectNullSection = this.IsLifted; LongSet sets = LongSet.Empty; foreach (var section in Sections) { - Debug.Assert(!section.Labels.IsEmpty); + if (section.HasNullLabel) { + Debug.Assert(expectNullSection, "Duplicate 'case null' or 'case null' in non-lifted switch."); + expectNullSection = false; + } + Debug.Assert(!section.Labels.IsEmpty || section.HasNullLabel); Debug.Assert(!section.Labels.Overlaps(sets)); sets = sets.UnionWith(section.Labels); } + Debug.Assert(sets.SetEquals(LongSet.Universe), "switch does not handle all possible cases"); + Debug.Assert(!expectNullSection, "Lifted switch is missing 'case null'"); } } @@ -162,6 +157,14 @@ namespace ICSharpCode.Decompiler.IL this.Labels = LongSet.Empty; } + /// + /// If true, serves as 'case null' in a lifted switch. + /// + public bool HasNullLabel { get; set; } + + /// + /// The set of labels that cause execution to jump to this switch section. + /// public LongSet Labels { get; set; } protected override InstructionFlags ComputeFlags() @@ -177,8 +180,17 @@ namespace ICSharpCode.Decompiler.IL public override void WriteTo(ITextOutput output, ILAstWritingOptions options) { - output.Write("case "); - output.Write(Labels.ToString()); + output.WriteDefinition("case", this, isLocal: true); + output.Write(' '); + if (HasNullLabel) { + output.Write("null"); + if (!Labels.IsEmpty) { + output.Write(", "); + output.Write(Labels.ToString()); + } + } else { + output.Write(Labels.ToString()); + } output.Write(": "); body.WriteTo(output, options); diff --git a/ICSharpCode.Decompiler/IL/Instructions/UsingInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/UsingInstruction.cs new file mode 100644 index 000000000..795be5914 --- /dev/null +++ b/ICSharpCode.Decompiler/IL/Instructions/UsingInstruction.cs @@ -0,0 +1,53 @@ +// Copyright (c) 2017 Siegfried Pammer +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + + +namespace ICSharpCode.Decompiler.IL +{ + /// + /// IL using instruction. + /// Equivalent to: + /// + /// stloc v(resourceExpression) + /// try { + /// body + /// } finally { + /// v?.Dispose(); + /// } + /// + /// + /// + /// The value of v is undefined after the end of the body block. + /// + partial class UsingInstruction + { + public override void WriteTo(ITextOutput output, ILAstWritingOptions options) + { + output.Write("using ("); + Variable.WriteTo(output); + output.Write(" = "); + ResourceExpression.WriteTo(output, options); + output.WriteLine(") {"); + output.Indent(); + Body.WriteTo(output, options); + output.Unindent(); + output.WriteLine(); + output.Write("}"); + } + } +} diff --git a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs index c248a457d..42de10379 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs @@ -45,9 +45,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (f != null) { call.Arguments[0].ReplaceWith(new Nop()); call.Arguments[1].ReplaceWith(f); - if (target is IInstructionWithVariableOperand && !target.MatchLdThis()) - targetsToReplace.Add((IInstructionWithVariableOperand)target); - } + if (target is IInstructionWithVariableOperand && !target.MatchLdThis()) + targetsToReplace.Add((IInstructionWithVariableOperand)target); + } } if (block.Instructions[i].MatchStLoc(out ILVariable targetVariable, out ILInstruction value)) { var newObj = value as NewObj; @@ -314,7 +314,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms base.VisitCompoundAssignmentInstruction(inst); if (inst.Target.MatchLdLoc(out var v)) { inst.ReplaceWith(new StLoc(v, new BinaryNumericInstruction(inst.Operator, inst.Target, inst.Value, inst.CheckForOverflow, inst.Sign))); - } + } } } #endregion diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index 5256f7da7..f0011a9cf 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -111,16 +111,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms && inst.Sign == Sign.Unsigned && (inst.Kind == ComparisonKind.GreaterThan || inst.Kind == ComparisonKind.LessThanOrEqual)) { - ILInstruction array; - if (inst.Left.MatchLdLen(StackType.I, out array)) { - // comp.unsigned(ldlen array > conv i4->i(ldc.i4 0)) - // => comp(ldlen.i4 array > ldc.i4 0) - // This is a special case where the C# compiler doesn't generate conv.i4 after ldlen. - context.Step("comp(ldlen.i4 array > ldc.i4 0)", inst); - inst.InputType = StackType.I4; - inst.Left.ReplaceWith(new LdLen(StackType.I4, array) { ILRange = inst.Left.ILRange }); - inst.Right = rightWithoutConv; - } if (inst.Kind == ComparisonKind.GreaterThan) { context.Step("comp.unsigned(left > ldc.i4 0) => comp(left != ldc.i4 0)", inst); inst.Kind = ComparisonKind.Inequality; @@ -132,6 +122,16 @@ namespace ICSharpCode.Decompiler.IL.Transforms VisitComp(inst); return; } + } else if (rightWithoutConv.MatchLdcI4(0) && inst.Kind.IsEqualityOrInequality()) { + if (inst.Left.MatchLdLen(StackType.I, out ILInstruction array)) { + // comp.unsigned(ldlen array == conv i4->i(ldc.i4 0)) + // => comp(ldlen.i4 array == ldc.i4 0) + // This is a special case where the C# compiler doesn't generate conv.i4 after ldlen. + context.Step("comp(ldlen.i4 array == ldc.i4 0)", inst); + inst.InputType = StackType.I4; + inst.Left.ReplaceWith(new LdLen(StackType.I4, array) { ILRange = inst.Left.ILRange }); + inst.Right = rightWithoutConv; + } } if (inst.Right.MatchLdNull() && inst.Left.MatchBox(out arg, out var type) && type.Kind == TypeKind.TypeParameter) { diff --git a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs index 571434836..70d7e5f9f 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs @@ -293,6 +293,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms parent = parent.Parent; } return parent == next; + case OpCode.BlockContainer: + if (((BlockContainer)next).EntryPoint.Instructions[0] is SwitchInstruction switchInst) { + next = switchInst; + goto case OpCode.SwitchInstruction; + } else { + return false; + } case OpCode.SwitchInstruction: return parent == next || (parent.MatchBinaryNumericInstruction(BinaryNumericOperator.Sub) && parent.Parent == next); default: @@ -334,6 +341,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms default: return false; } + } else if (expr is BlockContainer container && container.EntryPoint.IncomingEdgeCount == 1) { + // Possibly a switch-container, allow inlining into the switch instruction: + return FindLoadInNext(container.EntryPoint.Instructions[0], v, expressionBeingMoved, out loadInst) ?? false; + // If FindLoadInNext() returns null, we still can't continue searching + // because we can't inline over the remainder of the blockcontainer. } foreach (var child in expr.Children) { if (!child.SlotInfo.CanInlineInto) diff --git a/ICSharpCode.Decompiler/IL/Transforms/InlineReturnTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/InlineReturnTransform.cs new file mode 100644 index 000000000..e9c53f8ca --- /dev/null +++ b/ICSharpCode.Decompiler/IL/Transforms/InlineReturnTransform.cs @@ -0,0 +1,95 @@ +// Copyright (c) 2017 Siegfried Pammer +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ICSharpCode.Decompiler.IL.Transforms +{ + /// + /// This transform duplicates return blocks if they return a local variable that was assigned right before the return. + /// + class InlineReturnTransform : IILTransform + { + public void Run(ILFunction function, ILTransformContext context) + { + var instructionsToModify = new List<(BlockContainer, Block, Branch)>(); + + // Process all leave instructions in a leave-block, that is a block consisting solely of a leave instruction. + foreach (var leave in function.Descendants.OfType()) { + if (!(leave.Parent is Block leaveBlock && leaveBlock.Instructions.Count == 1)) + continue; + // Skip, if the leave instruction has no value or the value is not a load of a local variable. + if (!leave.Value.MatchLdLoc(out var returnVar) || returnVar.Kind != VariableKind.Local) + continue; + // If all instructions can be modified, add item to the global list. + if (CanModifyInstructions(returnVar, leaveBlock, out var list)) + instructionsToModify.AddRange(list); + } + + foreach (var (container, b, br) in instructionsToModify) { + Block block = b; + // if there is only one branch to this return block, move it to the matching container. + // otherwise duplicate the return block. + if (block.IncomingEdgeCount == 1) { + block.Remove(); + } else { + block = (Block)block.Clone(); + } + container.Blocks.Add(block); + // adjust the target of the branch to the newly created block. + br.TargetBlock = block; + } + } + + /// + /// Determines a list of all store instructions that write to a given . + /// Returns false if any of these instructions does not meet the following criteria: + /// - must be a stloc + /// - must be a direct child of a block + /// - must be the penultimate instruction + /// - must be followed by a branch instruction to + /// - must have a BlockContainer as ancestor. + /// Returns true, if all instructions meet these criteria, and contains a list of 3-tuples. + /// Each tuple consists of the target block container, the leave block, and the branch instruction that should be modified. + /// + static bool CanModifyInstructions(ILVariable returnVar, Block leaveBlock, out List<(BlockContainer, Block, Branch)> instructionsToModify) + { + instructionsToModify = new List<(BlockContainer, Block, Branch)>(); + foreach (var inst in returnVar.StoreInstructions) { + if (!(inst is StLoc store)) + return false; + if (!(store.Parent is Block storeBlock)) + return false; + if (store.ChildIndex + 2 != storeBlock.Instructions.Count) + return false; + if (!(storeBlock.Instructions[store.ChildIndex + 1] is Branch br)) + return false; + if (br.TargetBlock != leaveBlock) + return false; + var targetBlockContainer = BlockContainer.FindClosestContainer(store); + if (targetBlockContainer == null) + return false; + instructionsToModify.Add((targetBlockContainer, leaveBlock, br)); + } + + return true; + } + } +} diff --git a/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs index 8d11720be..20c63b6c8 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs @@ -102,7 +102,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms #region AnalyzeCondition bool AnalyzeCondition(ILInstruction condition) { - if (MatchHasValueCall(condition, out var v)) { + if (MatchHasValueCall(condition, out ILVariable v)) { if (nullableVars == null) nullableVars = new List(); nullableVars.Add(v); @@ -387,9 +387,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms // Comparing two nullables: HasValue comparison must be the same operator as the Value comparison if ((hasValueTestNegated ? hasValueComp.Kind.Negate() : hasValueComp.Kind) != newComparisonKind) return null; - if (!MatchHasValueCall(hasValueComp.Left, out var leftVar)) + if (!MatchHasValueCall(hasValueComp.Left, out ILVariable leftVar)) return null; - if (!MatchHasValueCall(hasValueComp.Right, out var rightVar)) + if (!MatchHasValueCall(hasValueComp.Right, out ILVariable rightVar)) return null; nullableVars = new List { leftVar }; var (left, leftBits) = DoLift(valueComp.Left); @@ -401,7 +401,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms context.Step("NullableLiftingTransform: C# (in)equality comparison", valueComp.Instruction); return valueComp.MakeLifted(newComparisonKind, left, right); } - } else if (newComparisonKind == ComparisonKind.Equality && !hasValueTestNegated && MatchHasValueCall(hasValueTest, out var v)) { + } else if (newComparisonKind == ComparisonKind.Equality && !hasValueTestNegated && MatchHasValueCall(hasValueTest, out ILVariable v)) { // Comparing nullable with non-nullable -> we can fall back to the normal comparison code. nullableVars = new List { v }; return LiftCSharpComparison(valueComp, newComparisonKind); @@ -461,13 +461,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms // else // ldc.i4 0 - if (!MatchHasValueCall(hasValueComp.Left, out var nullable1)) + if (!MatchHasValueCall(hasValueComp.Left, out ILVariable nullable1)) return null; - if (!MatchHasValueCall(hasValueComp.Right, out var nullable2)) + if (!MatchHasValueCall(hasValueComp.Right, out ILVariable nullable2)) return null; if (!nestedIfInst.MatchIfInstructionPositiveCondition(out var condition, out var trueInst, out var falseInst)) return null; - if (!MatchHasValueCall(condition, out var nullable)) + if (!MatchHasValueCall(condition, out ILVariable nullable)) return null; if (nullable != nullable1 && nullable != nullable2) return null; @@ -516,7 +516,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (trueInst.MatchIfInstructionPositiveCondition(out var nestedCondition, out var nestedTrue, out var nestedFalse)) { // Sometimes Roslyn generates pointless conditions like: // if (nullable.HasValue && (!nullable.HasValue || nullable.GetValueOrDefault() == b)) - if (MatchHasValueCall(nestedCondition, out var v) && nullableVars.Contains(v)) { + if (MatchHasValueCall(nestedCondition, out ILVariable v) && nullableVars.Contains(v)) { trueInst = nestedTrue; } } @@ -758,11 +758,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms #region Match...Call /// - /// Matches 'call get_HasValue(ldloca v)' + /// Matches 'call get_HasValue(arg)' /// - internal static bool MatchHasValueCall(ILInstruction inst, out ILVariable v) + internal static bool MatchHasValueCall(ILInstruction inst, out ILInstruction arg) { - v = null; + arg = null; if (!(inst is Call call)) return false; if (call.Arguments.Count != 1) @@ -771,7 +771,20 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (call.Method.DeclaringTypeDefinition?.KnownTypeCode != KnownTypeCode.NullableOfT) return false; - return call.Arguments[0].MatchLdLoca(out v); + arg = call.Arguments[0]; + return true; + } + + /// + /// Matches 'call get_HasValue(ldloca v)' + /// + internal static bool MatchHasValueCall(ILInstruction inst, out ILVariable v) + { + if (MatchHasValueCall(inst, out ILInstruction arg)) { + return arg.MatchLdLoca(out v); + } + v = null; + return false; } /// @@ -779,7 +792,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// internal static bool MatchHasValueCall(ILInstruction inst, ILVariable v) { - return MatchHasValueCall(inst, out var v2) && v == v2; + return MatchHasValueCall(inst, out ILVariable v2) && v == v2; } /// @@ -811,7 +824,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// /// Matches 'call Nullable{T}.GetValueOrDefault(arg)' /// - static bool MatchGetValueOrDefault(ILInstruction inst, out ILInstruction arg) + internal static bool MatchGetValueOrDefault(ILInstruction inst, out ILInstruction arg) { arg = null; if (!(inst is Call call)) diff --git a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnNullableTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnNullableTransform.cs new file mode 100644 index 000000000..ed538ca8b --- /dev/null +++ b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnNullableTransform.cs @@ -0,0 +1,165 @@ +// Copyright (c) 2017 Siegfried Pammer +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; +using ICSharpCode.Decompiler.IL.ControlFlow; +using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.Decompiler.Util; + +namespace ICSharpCode.Decompiler.IL.Transforms +{ + /// + /// Detects switch-on-nullable patterns employed by the C# compiler and transforms them to an ILAst-switch-instruction. + /// + class SwitchOnNullableTransform : IILTransform + { + public void Run(ILFunction function, ILTransformContext context) + { + if (!context.Settings.LiftNullables) + return; + + HashSet changedContainers = new HashSet(); + + foreach (var block in function.Descendants.OfType()) { + bool changed = false; + for (int i = block.Instructions.Count - 1; i >= 0; i--) { + SwitchInstruction newSwitch; + if (MatchSwitchOnNullable(block.Instructions, i, out newSwitch)) { + block.Instructions[i + 1].ReplaceWith(newSwitch); + block.Instructions.RemoveRange(i - 2, 3); + i -= 2; + changed = true; + continue; + } + if (MatchRoslynSwitchOnNullable(block.Instructions, i, out newSwitch)) { + block.Instructions[i - 1].ReplaceWith(newSwitch); + block.Instructions.RemoveRange(i, 2); + i--; + changed = true; + continue; + } + } + if (!changed) continue; + SwitchDetection.SimplifySwitchInstruction(block); + if (block.Parent is BlockContainer container) + changedContainers.Add(container); + } + + foreach (var container in changedContainers) + container.SortBlocks(deleteUnreachableBlocks: true); + } + + /// + /// Matches legacy C# switch on nullable. + /// + bool MatchSwitchOnNullable(InstructionCollection instructions, int i, out SwitchInstruction newSwitch) + { + newSwitch = null; + // match first block: + // stloc tmp(ldloca switchValueVar) + // stloc switchVariable(call GetValueOrDefault(ldloc tmp)) + // if (logic.not(call get_HasValue(ldloc tmp))) br nullCaseBlock + // br switchBlock + if (i < 2) return false; + if (!instructions[i - 2].MatchStLoc(out var tmp, out var ldloca) || + !instructions[i - 1].MatchStLoc(out var switchVariable, out var getValueOrDefault) || + !instructions[i].MatchIfInstruction(out var condition, out var trueInst)) + return false; + if (!tmp.IsSingleDefinition || tmp.LoadCount != 2) + return false; + if (!switchVariable.IsSingleDefinition || switchVariable.LoadCount != 1) + return false; + if (!instructions[i + 1].MatchBranch(out var switchBlock) || !trueInst.MatchBranch(out var nullCaseBlock)) + return false; + if (!ldloca.MatchLdLoca(out var switchValueVar)) + return false; + if (!condition.MatchLogicNot(out var getHasValue)) + return false; + if (!NullableLiftingTransform.MatchGetValueOrDefault(getValueOrDefault, out ILInstruction getValueOrDefaultArg)) + return false; + if (!NullableLiftingTransform.MatchHasValueCall(getHasValue, out ILInstruction getHasValueArg)) + return false; + if (!(getHasValueArg.MatchLdLoc(tmp) && getValueOrDefaultArg.MatchLdLoc(tmp))) + return false; + // match second block: switchBlock + // switch (ldloc switchVariable) { + // case [0..1): br caseBlock1 + // ... more cases ... + // case [long.MinValue..0),[1..5),[6..10),[11..long.MaxValue]: br defaultBlock + // } + if (switchBlock.Instructions.Count != 1 || switchBlock.IncomingEdgeCount != 1) + return false; + if (!(switchBlock.Instructions[0] is SwitchInstruction switchInst)) + return false; + newSwitch = BuildLiftedSwitch(nullCaseBlock, switchInst, new LdLoc(switchValueVar)); + return true; + } + + static SwitchInstruction BuildLiftedSwitch(Block nullCaseBlock, SwitchInstruction switchInst, ILInstruction switchValue) + { + SwitchInstruction newSwitch = new SwitchInstruction(switchValue); + newSwitch.IsLifted = true; + newSwitch.Sections.AddRange(switchInst.Sections); + newSwitch.Sections.Add(new SwitchSection { Body = new Branch(nullCaseBlock), HasNullLabel = true }); + return newSwitch; + } + + /// + /// Matches Roslyn C# switch on nullable. + /// + bool MatchRoslynSwitchOnNullable(InstructionCollection instructions, int i, out SwitchInstruction newSwitch) + { + newSwitch = null; + // match first block: + // stloc tmp(ldloc switchValueVar) + // if (logic.not(call get_HasValue(ldloca tmp))) br nullCaseBlock + // br switchBlock + if (i < 1) return false; + if (!instructions[i - 1].MatchStLoc(out var tmp, out var switchValue) || + !instructions[i].MatchIfInstruction(out var condition, out var trueInst)) + return false; + if (tmp.StoreCount != 1 || tmp.AddressCount != 2 || tmp.LoadCount != 0) + return false; + if (!instructions[i + 1].MatchBranch(out var switchBlock) || !trueInst.MatchBranch(out var nullCaseBlock)) + return false; + if (!condition.MatchLogicNot(out var getHasValue) || !NullableLiftingTransform.MatchHasValueCall(getHasValue, out ILVariable target1) || target1 != tmp) + return false; + // match second block: switchBlock + // stloc switchVar(call GetValueOrDefault(ldloca tmp)) + // switch (ldloc switchVar) { + // case [0..1): br caseBlock1 + // ... more cases ... + // case [long.MinValue..0),[1..5),[6..10),[11..long.MaxValue]: br defaultBlock + // } + if (switchBlock.Instructions.Count != 2 || switchBlock.IncomingEdgeCount != 1) + return false; + if (!switchBlock.Instructions[0].MatchStLoc(out var switchVar, out var getValueOrDefault)) + return false; + if (!switchVar.IsSingleDefinition || switchVar.LoadCount != 1) + return false; + if (!NullableLiftingTransform.MatchGetValueOrDefault(getValueOrDefault, tmp)) + return false; + if (!(switchBlock.Instructions[1] is SwitchInstruction switchInst)) + return false; + newSwitch = BuildLiftedSwitch(nullCaseBlock, switchInst, switchValue); + return true; + } + } +} diff --git a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs new file mode 100644 index 000000000..7dffaf2b4 --- /dev/null +++ b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs @@ -0,0 +1,614 @@ +// Copyright (c) 2017 Siegfried Pammer +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; +using ICSharpCode.Decompiler.IL.ControlFlow; +using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.Decompiler.Util; + +namespace ICSharpCode.Decompiler.IL.Transforms +{ + /// + /// Detects switch-on-string patterns employed by the C# compiler and transforms them to an ILAst-switch-instruction. + /// + class SwitchOnStringTransform : IILTransform + { + public void Run(ILFunction function, ILTransformContext context) + { + if (!context.Settings.SwitchStatementOnString) + return; + + HashSet changedContainers = new HashSet(); + + foreach (var block in function.Descendants.OfType()) { + bool changed = false; + for (int i = block.Instructions.Count - 1; i >= 0; i--) { + if (SimplifyCascadingIfStatements(block.Instructions, ref i)) { + changed = true; + continue; + } + if (MatchLegacySwitchOnStringWithHashtable(block.Instructions, ref i)) { + changed = true; + continue; + } + if (MatchLegacySwitchOnStringWithDict(block.Instructions, ref i)) { + changed = true; + continue; + } + if (MatchRoslynSwitchOnString(block.Instructions, ref i)) { + changed = true; + continue; + } + } + if (!changed) continue; + SwitchDetection.SimplifySwitchInstruction(block); + if (block.Parent is BlockContainer container) + changedContainers.Add(container); + } + + foreach (var container in changedContainers) + container.SortBlocks(deleteUnreachableBlocks: true); + } + + bool SimplifyCascadingIfStatements(InstructionCollection instructions, ref int i) + { + if (i < 1) return false; + // match first block: checking switch-value for null or first value (Roslyn) + // if (call op_Equality(ldloc switchValueVar, ldstr value)) br firstBlock + // -or- + // if (comp(ldloc switchValueVar == ldnull)) br defaultBlock + if (!(instructions[i].MatchIfInstruction(out var condition, out var firstBlockJump))) + return false; + if (!firstBlockJump.MatchBranch(out var firstBlock)) + return false; + List<(string, Block)> values = new List<(string, Block)>(); + ILInstruction switchValue = null; + + // match call to operator ==(string, string) + if (!MatchStringEqualityComparison(condition, out var switchValueVar, out string firstBlockValue)) + return false; + values.Add((firstBlockValue, firstBlock)); + + bool extraLoad = false; + if (instructions[i - 1].MatchStLoc(switchValueVar, out switchValue)) { + // stloc switchValueVar(switchValue) + // if (call op_Equality(ldloc switchValueVar, ldstr value)) br firstBlock + } else if (instructions[i - 1] is StLoc stloc && stloc.Value.MatchLdLoc(switchValueVar)) { + // in case of optimized legacy code there are two stlocs: + // stloc otherSwitchValueVar(ldloc switchValue) + // stloc switchValueVar(ldloc otherSwitchValueVar) + // if (call op_Equality(ldloc otherSwitchValueVar, ldstr value)) br firstBlock + var otherSwitchValueVar = switchValueVar; + switchValueVar = stloc.Variable; + if (i >= 2 && instructions[i - 2].MatchStLoc(otherSwitchValueVar, out switchValue) + && otherSwitchValueVar.IsSingleDefinition && otherSwitchValueVar.LoadCount == 2) + { + extraLoad = true; + } else { + switchValue = new LdLoc(otherSwitchValueVar); + } + } else { + switchValue = new LdLoc(switchValueVar); + } + // if instruction must be followed by a branch to the next case + if (!(instructions.ElementAtOrDefault(i + 1) is Branch nextCaseJump)) + return false; + // extract all cases and add them to the values list. + Block currentCaseBlock = nextCaseJump.TargetBlock; + Block nextCaseBlock; + while ((nextCaseBlock = MatchCaseBlock(currentCaseBlock, switchValueVar, out string value, out Block block)) != null) { + values.Add((value, block)); + currentCaseBlock = nextCaseBlock; + } + // We didn't find enough cases, exit + if (values.Count < 3) + return false; + // if the switchValueVar is used in other places as well, do not eliminate the store. + bool keepAssignmentBefore = false; + if (switchValueVar.LoadCount > values.Count) { + keepAssignmentBefore = true; + switchValue = new LdLoc(switchValueVar); + } + var sections = new List(values.SelectWithIndex((index, b) => new SwitchSection { Labels = new LongSet(index), Body = new Branch(b.Item2) })); + sections.Add(new SwitchSection { Labels = new LongSet(new LongInterval(0, sections.Count)).Invert(), Body = new Branch(currentCaseBlock) }); + var stringToInt = new StringToInt(switchValue, values.SelectArray(item => item.Item1)); + var inst = new SwitchInstruction(stringToInt); + inst.Sections.AddRange(sections); + if (extraLoad) { + instructions[i - 2].ReplaceWith(inst); + instructions.RemoveRange(i - 1, 3); + i -= 2; + } else { + if (keepAssignmentBefore) { + instructions[i].ReplaceWith(inst); + instructions.RemoveAt(i + 1); + } else { + instructions[i - 1].ReplaceWith(inst); + instructions.RemoveRange(i, 2); + i--; + } + } + return true; + } + + /// + /// Each case consists of two blocks: + /// 1. block: + /// if (call op_Equality(ldloc switchVariable, ldstr value)) br caseBlock + /// br nextBlock + /// -or- + /// if (comp(ldloc switchValueVar == ldnull)) br nextBlock + /// br caseBlock + /// 2. block is caseBlock + /// This method matches the above pattern or its inverted form: + /// the call to ==(string, string) is wrapped in logic.not and the branch targets are reversed. + /// Returns the next block that follows in the block-chain. + /// The is updated if the value gets copied to a different variable. + /// See comments below for more info. + /// + Block MatchCaseBlock(Block currentBlock, ILVariable switchVariable, out string value, out Block caseBlock) + { + value = null; + caseBlock = null; + + if (currentBlock.IncomingEdgeCount != 1 || currentBlock.Instructions.Count != 2) + return null; + if (!currentBlock.Instructions[0].MatchIfInstruction(out var condition, out var caseBlockBranch)) + return null; + if (!caseBlockBranch.MatchBranch(out caseBlock)) + return null; + Block nextBlock; + if (condition.MatchLogicNot(out var inner)) { + condition = inner; + nextBlock = caseBlock; + if (!currentBlock.Instructions[1].MatchBranch(out caseBlock)) + return null; + } else { + if (!currentBlock.Instructions[1].MatchBranch(out nextBlock)) + return null; + } + if (!MatchStringEqualityComparison(condition, switchVariable, out value)) { + return null; + } + return nextBlock; + } + + /// + /// Matches the C# 2.0 switch-on-string pattern, which uses Dictionary<string, int>. + /// + bool MatchLegacySwitchOnStringWithDict(InstructionCollection instructions, ref int i) + { + if (i < 1) return false; + // match first block: checking switch-value for null + // stloc switchValueVar(switchValue) + // if (comp(ldloc switchValueVar == ldnull)) br nullCase + // br nextBlock + if (!(instructions[i].MatchIfInstruction(out var condition, out var exitBlockJump) && + instructions[i - 1].MatchStLoc(out var switchValueVar, out var switchValue) && switchValueVar.Type.IsKnownType(KnownTypeCode.String))) + return false; + if (!switchValueVar.IsSingleDefinition) + return false; + if (!exitBlockJump.MatchBranch(out var nullValueCaseBlock)) + return false; + if (!(condition.MatchCompEquals(out var left, out var right) && right.MatchLdNull() + && ((SemanticHelper.IsPure(switchValue.Flags) && left.Match(switchValue).Success) || left.MatchLdLoc(switchValueVar)))) + return false; + var nextBlockJump = instructions.ElementAtOrDefault(i + 1) as Branch; + if (nextBlockJump == null || nextBlockJump.TargetBlock.IncomingEdgeCount != 1) + return false; + // match second block: checking compiler-generated Dictionary for null + // if (comp(volatile.ldobj System.Collections.Generic.Dictionary`2[[System.String],[System.Int32]](ldsflda $$method0x600000c-1) != ldnull)) br caseNullBlock + // br dictInitBlock + var nextBlock = nextBlockJump.TargetBlock; + if (nextBlock.Instructions.Count != 2 || !nextBlock.Instructions[0].MatchIfInstruction(out condition, out var tryGetValueBlockJump)) + return false; + if (!tryGetValueBlockJump.MatchBranch(out var tryGetValueBlock)) + return false; + if (!nextBlock.Instructions[1].MatchBranch(out var dictInitBlock) || dictInitBlock.IncomingEdgeCount != 1) + return false; + if (!(condition.MatchCompNotEquals(out left, out right) && right.MatchLdNull() && + MatchDictionaryFieldLoad(left, IsStringToIntDictionary, out var dictField, out var dictionaryType))) + return false; + // match third block: initialization of compiler-generated Dictionary + // stloc dict(newobj Dictionary..ctor(ldc.i4 valuesLength)) + // call Add(ldloc dict, ldstr value, ldc.i4 index) + // ... more calls to Add ... + // volatile.stobj System.Collections.Generic.Dictionary`2[[System.String],[System.Int32]](ldsflda $$method0x600003f-1, ldloc dict) + // br switchHeadBlock + if (dictInitBlock.IncomingEdgeCount != 1 || dictInitBlock.Instructions.Count < 3) + return false; + if (!ExtractStringValuesFromInitBlock(dictInitBlock, out var stringValues, tryGetValueBlock, dictionaryType, dictField)) + return false; + // match fourth block: TryGetValue on compiler-generated Dictionary + // if (logic.not(call TryGetValue(volatile.ldobj System.Collections.Generic.Dictionary`2[[System.String],[System.Int32]](ldsflda $$method0x600000c-1), ldloc switchValueVar, ldloca switchIndexVar))) br defaultBlock + // br switchBlock + if (tryGetValueBlock.IncomingEdgeCount != 2 || tryGetValueBlock.Instructions.Count != 2) + return false; + if (!tryGetValueBlock.Instructions[0].MatchIfInstruction(out condition, out var defaultBlockJump)) + return false; + if (!defaultBlockJump.MatchBranch(out var defaultBlock)) + return false; + if (!(condition.MatchLogicNot(out var arg) && arg is Call c && c.Method.Name == "TryGetValue" && + MatchDictionaryFieldLoad(c.Arguments[0], IsStringToIntDictionary, out var dictField2, out _) && dictField2.Equals(dictField))) + return false; + if (!c.Arguments[1].MatchLdLoc(switchValueVar) || !c.Arguments[2].MatchLdLoca(out var switchIndexVar)) + return false; + if (!tryGetValueBlock.Instructions[1].MatchBranch(out var switchBlock)) + return false; + // match fifth block: switch-instruction block + // switch (ldloc switchVariable) { + // case [0..1): br caseBlock1 + // ... more cases ... + // case [long.MinValue..0),[13..long.MaxValue]: br defaultBlock + // } + if (switchBlock.IncomingEdgeCount != 1 || switchBlock.Instructions.Count != 1) + return false; + if (!(switchBlock.Instructions[0] is SwitchInstruction switchInst && switchInst.Value.MatchLdLoc(switchIndexVar))) + return false; + var sections = new List(switchInst.Sections); + // switch contains case null: + if (nullValueCaseBlock != defaultBlock) { + if (!AddNullSection(sections, stringValues, nullValueCaseBlock)) { + return false; + } + } + bool keepAssignmentBefore = false; + if (switchValueVar.LoadCount > 2) { + switchValue = new LdLoc(switchValueVar); + keepAssignmentBefore = true; + } + var stringToInt = new StringToInt(switchValue, stringValues.ToArray()); + var inst = new SwitchInstruction(stringToInt); + inst.Sections.AddRange(sections); + instructions[i + 1].ReplaceWith(inst); + if (keepAssignmentBefore) { + // delete if (comp(ldloc switchValueVar == ldnull)) + instructions.RemoveAt(i); + i--; + } else { + // delete both the if and the assignment before + instructions.RemoveRange(i - 1, 2); + i -= 2; + } + return true; + } + + private bool AddNullSection(List sections, List stringValues, Block nullValueCaseBlock) + { + var label = new LongSet(sections.Count); + var possibleConflicts = sections.Where(sec => sec.Labels.Overlaps(label)).ToArray(); + if (possibleConflicts.Length > 1) + return false; + else if (possibleConflicts.Length == 1) { + if (possibleConflicts[0].Labels.Count() == 1) + return false; // cannot remove only label + possibleConflicts[0].Labels = possibleConflicts[0].Labels.ExceptWith(label); + } + stringValues.Add(null); + sections.Add(new SwitchSection() { Labels = label, Body = new Branch(nullValueCaseBlock) }); + return true; + } + + /// + /// Matches 'volatile.ldobj dictionaryType(ldsflda dictField)' + /// + bool MatchDictionaryFieldLoad(ILInstruction inst, Func typeMatcher, out IField dictField, out IType dictionaryType) + { + dictField = null; + dictionaryType = null; + return inst.MatchLdObj(out var dictionaryFieldLoad, out dictionaryType) && + typeMatcher(dictionaryType) && + dictionaryFieldLoad.MatchLdsFlda(out dictField) && + (dictField.IsCompilerGeneratedOrIsInCompilerGeneratedClass() || dictField.Name.StartsWith("$$method", StringComparison.Ordinal)); + } + + /// + /// Matches and extracts values from Add-call sequences. + /// + bool ExtractStringValuesFromInitBlock(Block block, out List values, Block targetBlock, IType dictionaryType, IField dictionaryField) + { + values = null; + // stloc dictVar(newobj Dictionary..ctor(ldc.i4 valuesLength)) + // -or- + // stloc dictVar(newobj Hashtable..ctor(ldc.i4 capacity, ldc.f loadFactor)) + if (!(block.Instructions[0].MatchStLoc(out var dictVar, out var newObjDict) && newObjDict is NewObj newObj)) + return false; + if (!newObj.Method.DeclaringType.Equals(dictionaryType)) + return false; + int valuesLength = 0; + if (newObj.Arguments.Count == 2) { + if (!newObj.Arguments[0].MatchLdcI4(out valuesLength)) + return false; + if (!newObj.Arguments[1].MatchLdcF(0.5)) + return false; + } else if (newObj.Arguments.Count == 1) { + if (!newObj.Arguments[0].MatchLdcI4(out valuesLength)) + return false; + } + values = new List(valuesLength); + int i = 0; + while (MatchAddCall(dictionaryType, block.Instructions[i + 1], dictVar, i, out var value)) { + values.Add(value); + i++; + } + // final store to compiler-generated variable: + // volatile.stobj dictionaryType(ldsflda dictionaryField, ldloc dictVar) + if (!(block.Instructions[i + 1].MatchStObj(out var loadField, out var dictVarLoad, out var dictType) && + dictType.Equals(dictionaryType) && loadField.MatchLdsFlda(out var dictField) && dictField.Equals(dictionaryField) && + dictVarLoad.MatchLdLoc(dictVar))) + return false; + return block.Instructions[i + 2].MatchBranch(targetBlock); + } + + /// + /// call Add(ldloc dictVar, ldstr value, ldc.i4 index) + /// -or- + /// call Add(ldloc dictVar, ldstr value, box System.Int32(ldc.i4 index)) + /// + bool MatchAddCall(IType dictionaryType, ILInstruction inst, ILVariable dictVar, int index, out string value) + { + value = null; + if (!(inst is Call c && c.Method.Name == "Add" && c.Arguments.Count == 3)) + return false; + if (!(c.Arguments[0].MatchLdLoc(dictVar) && c.Arguments[1].MatchLdStr(out value))) + return false; + if (!(c.Method.DeclaringType.Equals(dictionaryType) && !c.Method.IsStatic)) + return false; + return (c.Arguments[2].MatchLdcI4(index) || (c.Arguments[2].MatchBox(out var arg, out _) && arg.MatchLdcI4(index))); + } + + bool IsStringToIntDictionary(IType dictionaryType) + { + if (dictionaryType.FullName != "System.Collections.Generic.Dictionary") + return false; + if (dictionaryType.TypeArguments.Count != 2) + return false; + return dictionaryType.TypeArguments[0].IsKnownType(KnownTypeCode.String) && + dictionaryType.TypeArguments[1].IsKnownType(KnownTypeCode.Int32); + } + + bool IsNonGenericHashtable(IType dictionaryType) + { + if (dictionaryType.FullName != "System.Collections.Hashtable") + return false; + if (dictionaryType.TypeArguments.Count != 0) + return false; + return true; + } + + bool MatchLegacySwitchOnStringWithHashtable(InstructionCollection instructions, ref int i) + { + // match first block: checking compiler-generated Hashtable for null + // if (comp(volatile.ldobj System.Collections.Hashtable(ldsflda $$method0x600003f-1) != ldnull)) br switchHeadBlock + // br tableInitBlock + if (!(instructions[i].MatchIfInstruction(out var condition, out var branchToSwitchHead) && i + 1 < instructions.Count)) + return false; + if (!instructions[i + 1].MatchBranch(out var tableInitBlock) || tableInitBlock.IncomingEdgeCount != 1) + return false; + if (!(condition.MatchCompNotEquals(out var left, out var right) && right.MatchLdNull() && + MatchDictionaryFieldLoad(left, IsNonGenericHashtable, out var dictField, out var dictionaryType))) + return false; + if (!branchToSwitchHead.MatchBranch(out var switchHead)) + return false; + // match second block: initialization of compiler-generated Hashtable + // stloc table(newobj Hashtable..ctor(ldc.i4 capacity, ldc.f loadFactor)) + // call Add(ldloc table, ldstr value, box System.Int32(ldc.i4 index)) + // ... more calls to Add ... + // volatile.stobj System.Collections.Hashtable(ldsflda $$method0x600003f - 1, ldloc table) + // br switchHeadBlock + if (tableInitBlock.IncomingEdgeCount != 1 || tableInitBlock.Instructions.Count < 3) + return false; + if (!ExtractStringValuesFromInitBlock(tableInitBlock, out var stringValues, switchHead, dictionaryType, dictField)) + return false; + // match third block: checking switch-value for null + // stloc tmp(ldloc switch-value) + // stloc switchVariable(ldloc tmp) + // if (comp(ldloc tmp == ldnull)) br nullCaseBlock + // br getItemBlock + if (switchHead.Instructions.Count != 4 || switchHead.IncomingEdgeCount != 2) + return false; + if (!switchHead.Instructions[0].MatchStLoc(out var tmp, out var switchValue)) + return false; + if (!switchHead.Instructions[1].MatchStLoc(out var switchVariable, out var tmpLoad) || !tmpLoad.MatchLdLoc(tmp)) + return false; + if (!switchHead.Instructions[2].MatchIfInstruction(out condition, out var nullCaseBlockBranch)) + return false; + if (!switchHead.Instructions[3].MatchBranch(out var getItemBlock) || !nullCaseBlockBranch.MatchBranch(out var nullCaseBlock)) + return false; + if (!(condition.MatchCompEquals(out left, out right) && right.MatchLdNull() && left.MatchLdLoc(tmp))) + return false; + // match fourth block: get_Item on compiler-generated Hashtable + // stloc tmp2(call get_Item(volatile.ldobj System.Collections.Hashtable(ldsflda $$method0x600003f - 1), ldloc switchVariable)) + // stloc switchVariable(ldloc tmp2) + // if (comp(ldloc tmp2 == ldnull)) br defaultCaseBlock + // br switchBlock + if (getItemBlock.IncomingEdgeCount != 1 || getItemBlock.Instructions.Count != 4) + return false; + if (!(getItemBlock.Instructions[0].MatchStLoc(out var tmp2, out var getItem) && getItem is Call getItemCall && getItemCall.Method.Name == "get_Item")) + return false; + if (!getItemBlock.Instructions[1].MatchStLoc(out var switchVariable2, out var tmp2Load) || !tmp2Load.MatchLdLoc(tmp2)) + return false; + if (!ILVariableEqualityComparer.Instance.Equals(switchVariable, switchVariable2)) + return false; + if (!getItemBlock.Instructions[2].MatchIfInstruction(out condition, out var defaultBlockBranch)) + return false; + if (!getItemBlock.Instructions[3].MatchBranch(out var switchBlock) || !defaultBlockBranch.MatchBranch(out var defaultBlock)) + return false; + if (!(condition.MatchCompEquals(out left, out right) && right.MatchLdNull() && left.MatchLdLoc(tmp2))) + return false; + if (!(getItemCall.Arguments.Count == 2 && MatchDictionaryFieldLoad(getItemCall.Arguments[0], IsStringToIntDictionary, out var dictField2, out _) && dictField2.Equals(dictField)) && + getItemCall.Arguments[1].MatchLdLoc(switchVariable2)) + return false; + // match fifth block: switch-instruction block + // switch (ldobj System.Int32(unbox System.Int32(ldloc switchVariable))) { + // case [0..1): br caseBlock1 + // ... more cases ... + // case [long.MinValue..0),[13..long.MaxValue]: br defaultBlock + // } + if (switchBlock.IncomingEdgeCount != 1 || switchBlock.Instructions.Count != 1) + return false; + if (!(switchBlock.Instructions[0] is SwitchInstruction switchInst && switchInst.Value.MatchLdObj(out var target, out var ldobjType) && + target.MatchUnbox(out var arg, out var unboxType) && arg.MatchLdLoc(switchVariable2) && ldobjType.IsKnownType(KnownTypeCode.Int32) && unboxType.Equals(ldobjType))) + return false; + var sections = new List(switchInst.Sections); + // switch contains case null: + if (nullCaseBlock != defaultBlock) { + if (!AddNullSection(sections, stringValues, nullCaseBlock)) { + return false; + } + } + var stringToInt = new StringToInt(switchValue, stringValues.ToArray()); + var inst = new SwitchInstruction(stringToInt); + inst.Sections.AddRange(sections); + instructions[i + 1].ReplaceWith(inst); + instructions.RemoveAt(i); + return true; + } + + bool MatchRoslynSwitchOnString(InstructionCollection instructions, ref int i) + { + if (i < 1) return false; + // stloc switchValueVar(call ComputeStringHash(switchValue)) + // switch (ldloc switchValueVar) { + // case [211455823..211455824): br caseBlock1 + // ... more cases ... + // case [long.MinValue..-365098645),...,[1697255802..long.MaxValue]: br defaultBlock + // } + if (!(instructions[i] is SwitchInstruction switchInst && switchInst.Value.MatchLdLoc(out var switchValueVar) && + MatchComputeStringHashCall(instructions[i - 1], switchValueVar, out LdLoc switchValueLoad))) + return false; + + var stringValues = new List<(int, string, Block)>(); + int index = 0; + SwitchSection defaultSection = switchInst.Sections.MaxBy(s => s.Labels.Count()); + foreach (var section in switchInst.Sections) { + if (section == defaultSection) continue; + // extract target block + if (!section.Body.MatchBranch(out Block target)) + return false; + if (!MatchRoslynCaseBlockHead(target, switchValueLoad.Variable, out Block body, out string stringValue)) + return false; + stringValues.Add((index++, stringValue, body)); + } + ILInstruction switchValueInst = switchValueLoad; + // stloc switchValueLoadVariable(switchValue) + // stloc switchValueVar(call ComputeStringHash(ldloc switchValueLoadVariable)) + // switch (ldloc switchValueVar) { + bool keepAssignmentBefore; + // if the switchValueLoad.Variable is only used in the compiler generated case equality checks, we can remove it. + if (i > 1 && instructions[i - 2].MatchStLoc(switchValueLoad.Variable, out var switchValueTmp) && + switchValueLoad.Variable.IsSingleDefinition && switchValueLoad.Variable.LoadCount == switchInst.Sections.Count) + { + switchValueInst = switchValueTmp; + keepAssignmentBefore = false; + } else { + keepAssignmentBefore = true; + } + var defaultLabel = new LongSet(new LongInterval(0, index)).Invert(); + var newSwitch = new SwitchInstruction(new StringToInt(switchValueInst, stringValues.Select(item => item.Item2).ToArray())); + newSwitch.Sections.AddRange(stringValues.Select(section => new SwitchSection { Labels = new Util.LongSet(section.Item1), Body = new Branch(section.Item3) })); + newSwitch.Sections.Add(new SwitchSection { Labels = defaultLabel, Body = defaultSection.Body }); + instructions[i].ReplaceWith(newSwitch); + if (keepAssignmentBefore) { + instructions.RemoveAt(i - 1); + i--; + } else { + instructions.RemoveRange(i - 2, 2); + i -= 2; + } + return true; + } + + /// + /// Matches and the negated version: + /// if (call op_Equality(ldloc V_0, ldstr "Fifth case")) br body + /// br exit + /// + bool MatchRoslynCaseBlockHead(Block target, ILVariable switchValueVar, out Block body, out string stringValue) + { + body = null; + stringValue = null; + if (target.Instructions.Count != 2) + return false; + if (!target.Instructions[0].MatchIfInstruction(out var condition, out var bodyBranch)) + return false; + if (!bodyBranch.MatchBranch(out body)) + return false; + if (MatchStringEqualityComparison(condition, switchValueVar, out stringValue)) { + return body != null; + } else if (condition.MatchLogicNot(out condition) && MatchStringEqualityComparison(condition, switchValueVar, out stringValue)) { + if (!target.Instructions[1].MatchBranch(out Block exit)) + return false; + body = exit; + return true; + } else { + return false; + } + } + + /// + /// Matches 'stloc(targetVar, call ComputeStringHash(ldloc switchValue))' + /// + bool MatchComputeStringHashCall(ILInstruction inst, ILVariable targetVar, out LdLoc switchValue) + { + switchValue = null; + if (!inst.MatchStLoc(targetVar, out var value)) + return false; + if (!(value is Call c && c.Arguments.Count == 1 && c.Method.Name == "ComputeStringHash" && c.Method.IsCompilerGeneratedOrIsInCompilerGeneratedClass())) + return false; + if (!(c.Arguments[0] is LdLoc)) + return false; + switchValue = (LdLoc)c.Arguments[0]; + return true; + } + + /// + /// Matches 'call string.op_Equality(ldloc(variable), ldstr(stringValue))' + /// or 'comp(ldloc(variable) == ldnull)' + /// + bool MatchStringEqualityComparison(ILInstruction condition, ILVariable variable, out string stringValue) + { + return MatchStringEqualityComparison(condition, out var v, out stringValue) && v == variable; + } + + /// + /// Matches 'call string.op_Equality(ldloc(variable), ldstr(stringValue))' + /// or 'comp(ldloc(variable) == ldnull)' + /// + bool MatchStringEqualityComparison(ILInstruction condition, out ILVariable variable, out string stringValue) + { + stringValue = null; + variable = null; + ILInstruction left, right; + if (condition is Call c && c.Method.IsOperator && c.Method.Name == "op_Equality" + && c.Method.DeclaringType.IsKnownType(KnownTypeCode.String) && c.Arguments.Count == 2) + { + left = c.Arguments[0]; + right = c.Arguments[1]; + return left.MatchLdLoc(out variable) && right.MatchLdStr(out stringValue); + } else if (condition.MatchCompEqualsNull(out var arg)) { + stringValue = null; + return arg.MatchLdLoc(out variable); + } else { + return false; + } + } + } +} diff --git a/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs index e0993d6ce..1c64d2983 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs @@ -188,7 +188,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (objVar.Type.IsKnownType(KnownTypeCode.NullableOfT)) { if (!entryPoint.Instructions[checkIndex].MatchIfInstruction(out var condition, out var disposeInst)) return false; - if (!(NullableLiftingTransform.MatchHasValueCall(condition, out var v) && v == objVar)) + if (!NullableLiftingTransform.MatchHasValueCall(condition, objVar)) return false; if (!(disposeInst is Block disposeBlock) || disposeBlock.Instructions.Count != 1) return false; diff --git a/ICSharpCode.Decompiler/Util/CollectionExtensions.cs b/ICSharpCode.Decompiler/Util/CollectionExtensions.cs index 0223d4f1e..d6f67cae8 100644 --- a/ICSharpCode.Decompiler/Util/CollectionExtensions.cs +++ b/ICSharpCode.Decompiler/Util/CollectionExtensions.cs @@ -114,5 +114,81 @@ namespace ICSharpCode.Decompiler.Util moreB = enumB.MoveNext(); } } + + /// + /// Returns the minimum element. + /// + /// The input sequence is empty + public static T MinBy(this IEnumerable source, Func keySelector) where K : IComparable + { + return source.MinBy(keySelector, Comparer.Default); + } + + /// + /// Returns the minimum element. + /// + /// The input sequence is empty + public static T MinBy(this IEnumerable source, Func keySelector, IComparer keyComparer) + { + if (source == null) + throw new ArgumentNullException(nameof(source)); + if (keySelector == null) + throw new ArgumentNullException(nameof(keySelector)); + if (keyComparer == null) + keyComparer = Comparer.Default; + using (var enumerator = source.GetEnumerator()) { + if (!enumerator.MoveNext()) + throw new InvalidOperationException("Sequence contains no elements"); + T minElement = enumerator.Current; + K minKey = keySelector(minElement); + while (enumerator.MoveNext()) { + T element = enumerator.Current; + K key = keySelector(element); + if (keyComparer.Compare(key, minKey) < 0) { + minElement = element; + minKey = key; + } + } + return minElement; + } + } + + /// + /// Returns the maximum element. + /// + /// The input sequence is empty + public static T MaxBy(this IEnumerable source, Func keySelector) where K : IComparable + { + return source.MaxBy(keySelector, Comparer.Default); + } + + /// + /// Returns the maximum element. + /// + /// The input sequence is empty + public static T MaxBy(this IEnumerable source, Func keySelector, IComparer keyComparer) + { + if (source == null) + throw new ArgumentNullException(nameof(source)); + if (keySelector == null) + throw new ArgumentNullException(nameof(keySelector)); + if (keyComparer == null) + keyComparer = Comparer.Default; + using (var enumerator = source.GetEnumerator()) { + if (!enumerator.MoveNext()) + throw new InvalidOperationException("Sequence contains no elements"); + T maxElement = enumerator.Current; + K maxKey = keySelector(maxElement); + while (enumerator.MoveNext()) { + T element = enumerator.Current; + K key = keySelector(element); + if (keyComparer.Compare(key, maxKey) > 0) { + maxElement = element; + maxKey = key; + } + } + return maxElement; + } + } } } diff --git a/ICSharpCode.Decompiler/Util/GraphVizGraph.cs b/ICSharpCode.Decompiler/Util/GraphVizGraph.cs new file mode 100644 index 000000000..3488a52f1 --- /dev/null +++ b/ICSharpCode.Decompiler/Util/GraphVizGraph.cs @@ -0,0 +1,221 @@ +// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Text.RegularExpressions; + +namespace ICSharpCode.Decompiler.Util +{ +#if DEBUG + /// + /// GraphViz graph. + /// + sealed class GraphVizGraph + { + List nodes = new List(); + List edges = new List(); + + public string rankdir; + public string Title; + + public void AddEdge(GraphVizEdge edge) + { + edges.Add(edge); + } + + public void AddNode(GraphVizNode node) + { + nodes.Add(node); + } + + public void Save(string fileName) + { + using (StreamWriter writer = new StreamWriter(fileName)) + Save(writer); + } + + public void Show() + { + Show(null); + } + + public void Show(string name) + { + if (name == null) + name = Title; + if (name != null) + foreach (char c in Path.GetInvalidFileNameChars()) + name = name.Replace(c, '-'); + string fileName = name != null ? Path.Combine(Path.GetTempPath(), name) : Path.GetTempFileName(); + Save(fileName + ".gv"); + Process.Start("dot", "\"" + fileName + ".gv\" -Tpng -o \"" + fileName + ".png\"").WaitForExit(); + Process.Start(fileName + ".png"); + } + + static string Escape(string text) + { + if (Regex.IsMatch(text, @"^[\w\d]+$")) { + return text; + } else { + return "\"" + text.Replace("\\", "\\\\").Replace("\r", "").Replace("\n", "\\n").Replace("\"", "\\\"") + "\""; + } + } + + static void WriteGraphAttribute(TextWriter writer, string name, string value) + { + if (value != null) + writer.WriteLine("{0}={1};", name, Escape(value)); + } + + internal static void WriteAttribute(TextWriter writer, string name, double? value, ref bool isFirst) + { + if (value != null) { + WriteAttribute(writer, name, value.Value.ToString(CultureInfo.InvariantCulture), ref isFirst); + } + } + + internal static void WriteAttribute(TextWriter writer, string name, bool? value, ref bool isFirst) + { + if (value != null) { + WriteAttribute(writer, name, value.Value ? "true" : "false", ref isFirst); + } + } + + internal static void WriteAttribute(TextWriter writer, string name, string value, ref bool isFirst) + { + if (value != null) { + if (isFirst) + isFirst = false; + else + writer.Write(','); + writer.Write("{0}={1}", name, Escape(value)); + } + } + + public void Save(TextWriter writer) + { + if (writer == null) + throw new ArgumentNullException("writer"); + writer.WriteLine("digraph G {"); + writer.WriteLine("node [fontsize = 16];"); + WriteGraphAttribute(writer, "rankdir", rankdir); + foreach (GraphVizNode node in nodes) { + node.Save(writer); + } + foreach (GraphVizEdge edge in edges) { + edge.Save(writer); + } + writer.WriteLine("}"); + } + } + + sealed class GraphVizEdge + { + public readonly string Source, Target; + + /// edge stroke color + public string color; + /// use edge to affect node ranking + public bool? constraint; + + public string label; + + public string style; + + /// point size of label + public int? fontsize; + + public GraphVizEdge(string source, string target) + { + if (source == null) + throw new ArgumentNullException("source"); + if (target == null) + throw new ArgumentNullException("target"); + this.Source = source; + this.Target = target; + } + + public GraphVizEdge(int source, int target) + { + this.Source = source.ToString(CultureInfo.InvariantCulture); + this.Target = target.ToString(CultureInfo.InvariantCulture); + } + + public void Save(TextWriter writer) + { + writer.Write("{0} -> {1} [", Source, Target); + bool isFirst = true; + GraphVizGraph.WriteAttribute(writer, "label", label, ref isFirst); + GraphVizGraph.WriteAttribute(writer, "style", style, ref isFirst); + GraphVizGraph.WriteAttribute(writer, "fontsize", fontsize, ref isFirst); + GraphVizGraph.WriteAttribute(writer, "color", color, ref isFirst); + GraphVizGraph.WriteAttribute(writer, "constraint", constraint, ref isFirst); + writer.WriteLine("];"); + } + } + + sealed class GraphVizNode + { + public readonly string ID; + public string label; + + public string labelloc; + + /// point size of label + public int? fontsize; + + /// minimum height in inches + public double? height; + + /// space around label + public string margin; + + /// node shape + public string shape; + + public GraphVizNode(string id) + { + if (id == null) + throw new ArgumentNullException("id"); + this.ID = id; + } + + public GraphVizNode(int id) + { + this.ID = id.ToString(CultureInfo.InvariantCulture); + } + + public void Save(TextWriter writer) + { + writer.Write(ID); + writer.Write(" ["); + bool isFirst = true; + GraphVizGraph.WriteAttribute(writer, "label", label, ref isFirst); + GraphVizGraph.WriteAttribute(writer, "labelloc", labelloc, ref isFirst); + GraphVizGraph.WriteAttribute(writer, "fontsize", fontsize, ref isFirst); + GraphVizGraph.WriteAttribute(writer, "margin", margin, ref isFirst); + GraphVizGraph.WriteAttribute(writer, "shape", shape, ref isFirst); + writer.WriteLine("];"); + } + } +#endif +} diff --git a/ILSpy.BamlDecompiler/BamlResourceEntryNode.cs b/ILSpy.BamlDecompiler/BamlResourceEntryNode.cs index 9b0a515dd..30444af81 100644 --- a/ILSpy.BamlDecompiler/BamlResourceEntryNode.cs +++ b/ILSpy.BamlDecompiler/BamlResourceEntryNode.cs @@ -10,6 +10,7 @@ using System.Threading.Tasks; using System.Xml.Linq; using ICSharpCode.AvalonEdit.Highlighting; +using ICSharpCode.Decompiler.Util; using ICSharpCode.ILSpy; using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.TreeNodes; @@ -135,7 +136,7 @@ namespace ILSpy.BamlDecompiler } } - static void RemoveConnectionIds(XElement element, Dictionary eventMappings) + static void RemoveConnectionIds(XElement element, List<(LongSet key, EventRegistration[] value)> eventMappings) { foreach (var child in element.Elements()) RemoveConnectionIds(child, eventMappings); @@ -143,10 +144,9 @@ namespace ILSpy.BamlDecompiler var removableAttrs = new List(); var addableAttrs = new List(); foreach (var attr in element.Attributes(XName.Get("ConnectionId", XmlBamlReader.XWPFNamespace))) { - int id; - if (int.TryParse(attr.Value, out id) && eventMappings.ContainsKey(id)) { - var map = eventMappings[id]; - foreach (var entry in map) { + int id, index; + if (int.TryParse(attr.Value, out id) && (index = eventMappings.FindIndex(item => item.key.Contains(id))) > -1) { + foreach (var entry in eventMappings[index].value) { string xmlns = ""; // TODO : implement xmlns resolver! addableAttrs.Add(new XAttribute(xmlns + entry.EventName, entry.MethodName)); } diff --git a/ILSpy.BamlDecompiler/ConnectMethodDecompiler.cs b/ILSpy.BamlDecompiler/ConnectMethodDecompiler.cs index 7c970ea54..39ddba883 100644 --- a/ILSpy.BamlDecompiler/ConnectMethodDecompiler.cs +++ b/ILSpy.BamlDecompiler/ConnectMethodDecompiler.cs @@ -10,6 +10,7 @@ using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.Decompiler.Util; using Mono.Cecil; namespace ILSpy.BamlDecompiler @@ -34,9 +35,9 @@ namespace ILSpy.BamlDecompiler this.assembly = assembly; } - public Dictionary DecompileEventMappings(string fullTypeName, CancellationToken cancellationToken) + public List<(LongSet, EventRegistration[])> DecompileEventMappings(string fullTypeName, CancellationToken cancellationToken) { - var result = new Dictionary(); + var result = new List<(LongSet, EventRegistration[])>(); TypeDefinition type = this.assembly.MainModule.GetType(fullTypeName); if (type == null) @@ -65,13 +66,12 @@ namespace ILSpy.BamlDecompiler function.RunTransforms(CSharpDecompiler.GetILTransforms(), context); var block = function.Body.Children.OfType().First(); - var ilSwitch = block.Children.OfType().FirstOrDefault(); + var ilSwitch = block.Descendants.OfType().FirstOrDefault(); if (ilSwitch != null) { foreach (var section in ilSwitch.Sections) { var events = FindEvents(section.Body); - foreach (long id in section.Labels.Values) - result.Add(id, events); + result.Add((section.Labels, events)); } } else { foreach (var ifInst in function.Descendants.OfType()) { @@ -82,7 +82,7 @@ namespace ILSpy.BamlDecompiler if (!comp.Right.MatchLdcI4(out id)) continue; var events = FindEvents(comp.Kind == ComparisonKind.Inequality ? ifInst.FalseInst : ifInst.TrueInst); - result.Add(id, events); + result.Add((new LongSet(id), events)); } } return result; @@ -92,13 +92,18 @@ namespace ILSpy.BamlDecompiler { var events = new List(); - if (inst is Block) { - foreach (var node in ((Block)inst).Instructions) { - FindEvents(node, events); - } - FindEvents(((Block)inst).FinalInstruction, events); - } else { - FindEvents(inst, events); + switch (inst) { + case Block b: + foreach (var node in ((Block)inst).Instructions) { + FindEvents(node, events); + } + FindEvents(((Block)inst).FinalInstruction, events); + break; + case Branch br: + return FindEvents(br.TargetBlock); + default: + FindEvents(inst, events); + break; } return events.ToArray(); } diff --git a/ILSpy/Languages/ILAstLanguage.cs b/ILSpy/Languages/ILAstLanguage.cs index 488926a76..a812da2b5 100644 --- a/ILSpy/Languages/ILAstLanguage.cs +++ b/ILSpy/Languages/ILAstLanguage.cs @@ -169,6 +169,10 @@ namespace ICSharpCode.ILSpy try { il.RunTransforms(transforms, context); } catch (StepLimitReachedException) { + } catch (Exception ex) { + output.WriteLine(ex.ToString()); + output.WriteLine(); + output.WriteLine("ILAst after the crash:"); } finally { // update stepper even if a transform crashed unexpectedly if (options.StepLimit == int.MaxValue) { diff --git a/ILSpy/LoadedAssembly.cs b/ILSpy/LoadedAssembly.cs index 3b89c1951..e37683f02 100644 --- a/ILSpy/LoadedAssembly.cs +++ b/ILSpy/LoadedAssembly.cs @@ -51,13 +51,14 @@ namespace ICSharpCode.ILSpy this.assemblyTask = Task.Factory.StartNew(LoadAssembly, stream); // requires that this.fileName is set this.shortName = Path.GetFileNameWithoutExtension(fileName); - this.targetFrameworkId = new Lazy(AssemblyDefinition.DetectTargetFrameworkId, false); + this.targetFrameworkId = new Lazy(() => AssemblyDefinition?.DetectTargetFrameworkId(), false); } /// /// Returns a target framework identifier in the form '<framework>Version=v<version>'. + /// Returns an empty string if no TargetFrameworkAttribute was found or the file doesn't contain an assembly header, i.e., is only a module. /// - public string TargetFrameworkId => targetFrameworkId.Value; + public string TargetFrameworkId => targetFrameworkId.Value ?? string.Empty; public Dictionary LoadedAssemblyReferencesInfo => loadedAssemblyReferences;