From d40ecb5e0ca4ff81ddb4c72dea9a8a2be098bdbe Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 29 Sep 2017 02:25:53 +0200 Subject: [PATCH 01/65] [switch] Add StringToInt instruction --- .../ICSharpCode.Decompiler.csproj | 2 + ICSharpCode.Decompiler/IL/Instructions.cs | 96 +++++++++++++++++++ ICSharpCode.Decompiler/IL/Instructions.tt | 2 + .../IL/Instructions/LockInstruction.cs | 32 ------- .../IL/Instructions/StringToInt.cs | 45 +++++++++ .../IL/Instructions/UsingInstruction.cs | 53 ++++++++++ 6 files changed, 198 insertions(+), 32 deletions(-) create mode 100644 ICSharpCode.Decompiler/IL/Instructions/StringToInt.cs create mode 100644 ICSharpCode.Decompiler/IL/Instructions/UsingInstruction.cs diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 387d453b5..dd2bd4a91 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -271,6 +271,8 @@ + + diff --git a/ICSharpCode.Decompiler/IL/Instructions.cs b/ICSharpCode.Decompiler/IL/Instructions.cs index 015b312b7..c6f361279 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions.cs @@ -158,6 +158,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. @@ -4021,6 +4023,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 @@ -4633,6 +4716,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); @@ -4919,6 +5006,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); @@ -5205,6 +5296,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); @@ -5294,6 +5389,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 2b28a9efe..f25e3fa0d 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.tt +++ b/ICSharpCode.Decompiler/IL/Instructions.tt @@ -220,6 +220,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/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/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/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("}"); + } + } +} From ddd5f43b412fd08689a6e048457084316a9fa6f2 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 29 Sep 2017 02:27:36 +0200 Subject: [PATCH 02/65] [switch] Add basic SwitchOnStringTransform for Roslyn switch on strings. --- ICSharpCode.Decompiler.Tests/Switch.cs | 89 -------------- .../TestCases/Correctness/Switch.cs | 38 +++++- .../CSharp/CSharpDecompiler.cs | 1 + .../CSharp/StatementBuilder.cs | 17 ++- .../ICSharpCode.Decompiler.csproj | 1 + .../IL/Transforms/SwitchOnStringTransform.cs | 114 ++++++++++++++++++ 6 files changed, 166 insertions(+), 94 deletions(-) delete mode 100644 ICSharpCode.Decompiler.Tests/Switch.cs create mode 100644 ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs 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/Switch.cs b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Switch.cs index 61ed2f347..7816d4697 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Switch.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Switch.cs @@ -30,6 +30,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness 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) @@ -105,6 +110,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"; } @@ -141,10 +156,31 @@ 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) + { + 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/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 1503561f0..b6ff80b98 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -85,6 +85,7 @@ 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 BlockILTransform { // per-block transforms PostOrderTransforms = { // Even though it's a post-order block-transform as most other transforms, diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs index e728c4905..bdb59f436 100644 --- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs @@ -83,11 +83,13 @@ namespace ICSharpCode.Decompiler.CSharp return new IfElseStatement(condition, trueStatement, falseStatement); } - CaseLabel CreateTypedCaseLabel(long i, IType type) + CaseLabel 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); @@ -101,12 +103,19 @@ namespace ICSharpCode.Decompiler.CSharp { var oldBreakTarget = breakTarget; breakTarget = null; // 'break' within a switch would only leave the switch - - var value = exprBuilder.Translate(inst.Value); + + TranslatedExpression value; + var strToInt = inst.Value as StringToInt; + if (strToInt != null) { + value = exprBuilder.Translate(strToInt.Argument); + } else { + value = exprBuilder.Translate(inst.Value); + } + var stmt = new SwitchStatement() { Expression = value }; foreach (var section in inst.Sections) { var astSection = new Syntax.SwitchSection(); - astSection.CaseLabels.AddRange(section.Labels.Values.Select(i => CreateTypedCaseLabel(i, value.Type))); + astSection.CaseLabels.AddRange(section.Labels.Values.Select(i => CreateTypedCaseLabel(i, value.Type, strToInt?.Map))); ConvertSwitchSectionBody(astSection, section.Body); stmt.SwitchSections.Add(astSection); } diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index dd2bd4a91..16409351a 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -273,6 +273,7 @@ + diff --git a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs new file mode 100644 index 000000000..7065ca5ac --- /dev/null +++ b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs @@ -0,0 +1,114 @@ +// 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 System.Text; + +namespace ICSharpCode.Decompiler.IL.Transforms +{ + class SwitchOnStringTransform : IILTransform + { + public void Run(ILFunction function, ILTransformContext context) + { + HashSet changedContainers = new HashSet(); + + foreach (var block in function.Descendants.OfType()) { + for (int i = block.Instructions.Count - 1; i >= 0; i--) { + if (!MatchRoslynSwitchOnString(block.Instructions, i, out var newSwitch)) + continue; + + block.Instructions[i].ReplaceWith(newSwitch); + block.Instructions.RemoveAt(i - 1); + + i--; + + // This happens in some cases: + // Use correct index after transformation. + if (i >= block.Instructions.Count) + i = block.Instructions.Count; + } + + if (block.Parent is BlockContainer container) + changedContainers.Add(container); + } + + foreach (var container in changedContainers) + container.SortBlocks(deleteUnreachableBlocks: true); + } + + bool MatchRoslynSwitchOnString(InstructionCollection instructions, int i, out SwitchInstruction inst) + { + inst = null; + if (i < 1) return false; + if (!(instructions[i] is SwitchInstruction switchInst && switchInst.Value.MatchLdLoc(out var targetVar) && + MatchComputeStringHashCall(instructions[i - 1], targetVar, out var switchValue))) + return false; + + var stringValues = new List<(int, string, Block)>(); + int index = 0; + foreach (var section in switchInst.Sections) { + if (!section.Body.MatchBranch(out Block target)) + return false; + if (target.IncomingEdgeCount != 1 || target.Instructions.Count == 0) + return false; + if (!target.Instructions[0].MatchIfInstruction(out var condition, out var bodyBranch)) + return false; + if (!MatchStringEqualityComparison(condition, switchValue.Variable, out string stringValue)) + return false; + if (!bodyBranch.MatchBranch(out Block body)) + return false; + stringValues.Add((index++, stringValue, body)); + } + + var value = new StringToInt(switchValue.Clone(), stringValues.Select(item => item.Item2).ToArray()); + inst = new SwitchInstruction(value); + inst.Sections.AddRange(stringValues.Select(section => new SwitchSection { Labels = new Util.LongSet(section.Item1), Body = new Branch(section.Item3) })); + + return true; + } + + 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; + } + + bool MatchStringEqualityComparison(ILInstruction condition, ILVariable variable, out string stringValue) + { + stringValue = null; + ILInstruction left, right; + if (condition is Call c && c.Method.IsOperator && c.Method.Name == "op_Equality" && c.Arguments.Count == 2) { + left = c.Arguments[0]; + right = c.Arguments[1]; + if (!right.MatchLdStr(out stringValue)) + return false; + } else if (condition.MatchCompEquals(out left, out right) && right.MatchLdNull()) { + } else return false; + return left.MatchLdLoc(variable); + } + } +} From 41aa4573d93c37cd565cc66d5f575438e92c4586 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 29 Sep 2017 14:24:50 +0200 Subject: [PATCH 03/65] Fix #498: switches implemented by the compiler as Dictionary lookup are not correctly decompiled --- .../TestCases/Correctness/Switch.cs | 7 ++ .../IL/Transforms/SwitchOnStringTransform.cs | 119 +++++++++++++++++- 2 files changed, 125 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Switch.cs b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Switch.cs index 7816d4697..cdecec92f 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Switch.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Switch.cs @@ -46,6 +46,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"; @@ -64,6 +65,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness public static string ShortSwitchOverString(string text) { + Console.WriteLine("ShortSwitchOverString: " + text); switch (text) { case "First case": return "Text"; @@ -74,6 +76,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness public static string SwitchOverString1(string text) { + Console.WriteLine("SwitchOverString1: " + text); switch (text) { case "First case": return "Text1"; @@ -97,6 +100,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness public static string SwitchOverString2() { + Console.WriteLine("SwitchOverString2:"); switch (Environment.UserName) { case "First case": return "Text1"; @@ -127,6 +131,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness public static string SwitchOverBool(bool b) { + Console.WriteLine("SwitchOverBool: " + b); switch (b) { case true: return bool.TrueString; @@ -139,6 +144,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness public static void SwitchInLoop(int i) { + Console.WriteLine("SwitchInLoop: " + i); while (true) { switch (i) { case 1: @@ -164,6 +170,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness public static void SwitchWithGoto(int i) { + Console.WriteLine("SwitchWithGoto: " + i); switch (i) { case 1: Console.WriteLine("one"); diff --git a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs index 7065ca5ac..ee6f2b6a7 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs @@ -20,6 +20,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Text; +using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL.Transforms { @@ -31,9 +32,16 @@ namespace ICSharpCode.Decompiler.IL.Transforms foreach (var block in function.Descendants.OfType()) { for (int i = block.Instructions.Count - 1; i >= 0; i--) { - if (!MatchRoslynSwitchOnString(block.Instructions, i, out var newSwitch)) + SwitchInstruction newSwitch; + Block blockAfterSwitch = null; + if (!MatchLegacySwitchOnString(block.Instructions, i, out newSwitch, out blockAfterSwitch) && + !MatchRoslynSwitchOnString(block.Instructions, i, out newSwitch)) continue; + if (i + 1 < block.Instructions.Count && block.Instructions[i + 1] is Branch b && blockAfterSwitch != null) { + block.Instructions[i + 1].ReplaceWith(new Branch(blockAfterSwitch)); + } + block.Instructions[i].ReplaceWith(newSwitch); block.Instructions.RemoveAt(i - 1); @@ -53,6 +61,115 @@ namespace ICSharpCode.Decompiler.IL.Transforms container.SortBlocks(deleteUnreachableBlocks: true); } + bool MatchLegacySwitchOnString(InstructionCollection instructions, int i, out SwitchInstruction inst, out Block blockAfterSwitch) + { + inst = null; + blockAfterSwitch = null; + if (i < 1) return false; + // match first block: checking switch-value for null + 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 (!exitBlockJump.MatchBranch(out var exitBlock)) + return false; + if (!(condition.MatchCompEquals(out var left, out var right) && right.MatchLdNull() && left.Match(switchValue).Success)) + 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 + 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, out var dictField, out var dictionaryType))) + return false; + // match third block: initialization of compiler-generated Dictionary + if (dictInitBlock.IncomingEdgeCount != 1 || dictInitBlock.Instructions.Count < 3) + return false; + if (!ExtractStringValuesFromDictionaryInitBlock(dictInitBlock, out var stringValues, tryGetValueBlock, dictionaryType, dictField)) + return false; + // match fourth block: TryGetValue on compiler-generated Dictionary + 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], 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 + if (switchBlock.IncomingEdgeCount != 1 || switchBlock.Instructions.Count != 2) + return false; + if (!(switchBlock.Instructions[0] is SwitchInstruction switchInst && switchInst.Value.MatchLdLoc(switchIndexVar))) + return false; + if (!switchBlock.Instructions[1].MatchBranch(defaultBlock)) + return false; + // switch contains case null: + var sections = new List(switchInst.Sections); + if (exitBlock != defaultBlock) { + stringValues.Add(null); + sections.Add(new SwitchSection() { Labels = new Util.LongSet(stringValues.Count - 1), Body = new Branch(exitBlock) }); + } + var stringToInt = new StringToInt(switchValue.Clone(), stringValues.ToArray()); + inst = new SwitchInstruction(stringToInt); + inst.DefaultBody = switchInst.DefaultBody; + inst.Sections.AddRange(sections); + blockAfterSwitch = defaultBlock; + return true; + } + + bool MatchDictionaryFieldLoad(ILInstruction inst, out IField dictField, out IType dictionaryType) + { + dictField = null; + dictionaryType = null; + return inst.MatchLdObj(out var dictionaryFieldLoad, out dictionaryType) && + IsStringToIntDictionary(dictionaryType) && + dictionaryFieldLoad.MatchLdsFlda(out dictField) && + dictField.IsCompilerGeneratedOrIsInCompilerGeneratedClass(); + } + + bool ExtractStringValuesFromDictionaryInitBlock(Block block, out List values, Block targetBlock, IType dictionaryType, IField dictionaryField) + { + values = null; + if (!(block.Instructions[0].MatchStLoc(out var dictVar, out var newObjDict) && + newObjDict is NewObj newObj && newObj.Arguments.Count == 1 && newObj.Arguments[0].MatchLdcI4(out var valuesLength))) + return false; + if (block.Instructions.Count != valuesLength + 3) + return false; + values = new List(valuesLength); + for (int i = 0; i < valuesLength; i++) { + if (!(block.Instructions[i + 1] is Call c && c.Method.Name == "Add" && c.Arguments.Count == 3 && + c.Arguments[0].MatchLdLoc(dictVar) && c.Arguments[1].MatchLdStr(out var value) && c.Arguments[2].MatchLdcI4(i))) + return false; + values.Add(value); + } + if (!(block.Instructions[valuesLength + 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[valuesLength + 2].MatchBranch(targetBlock); + } + + 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 MatchRoslynSwitchOnString(InstructionCollection instructions, int i, out SwitchInstruction inst) { inst = null; From e0df621e44fd11311d227ecc8d27df4b24c41d0d Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 29 Sep 2017 17:08:08 +0200 Subject: [PATCH 04/65] Implement translation of cascading if-statements with string comparisons to switch(string). --- .../TestCases/Correctness/Switch.cs | 31 +++++++ .../IL/Transforms/SwitchOnStringTransform.cs | 84 ++++++++++++++++++- 2 files changed, 111 insertions(+), 4 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Switch.cs b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Switch.cs index cdecec92f..f28e691cf 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Switch.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Switch.cs @@ -26,6 +26,8 @@ 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)); @@ -74,6 +76,35 @@ 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); diff --git a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs index ee6f2b6a7..b533e8d3b 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs @@ -21,6 +21,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.Transforms { @@ -34,7 +35,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms for (int i = block.Instructions.Count - 1; i >= 0; i--) { SwitchInstruction newSwitch; Block blockAfterSwitch = null; - if (!MatchLegacySwitchOnString(block.Instructions, i, out newSwitch, out blockAfterSwitch) && + if (!MatchCascadingIfStatements(block.Instructions, i, out newSwitch, out blockAfterSwitch) && + !MatchLegacySwitchOnString(block.Instructions, i, out newSwitch, out blockAfterSwitch) && !MatchRoslynSwitchOnString(block.Instructions, i, out newSwitch)) continue; @@ -43,9 +45,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms } block.Instructions[i].ReplaceWith(newSwitch); - block.Instructions.RemoveAt(i - 1); - - i--; + if (newSwitch.Value.MatchLdLoc(out var switchVar) && !block.Instructions[i - 1].MatchLdLoc(switchVar)) { + block.Instructions.RemoveAt(i - 1); + i--; + } // This happens in some cases: // Use correct index after transformation. @@ -61,6 +64,79 @@ namespace ICSharpCode.Decompiler.IL.Transforms container.SortBlocks(deleteUnreachableBlocks: true); } + bool MatchCascadingIfStatements(InstructionCollection instructions, int i, out SwitchInstruction inst, out Block blockAfterSwitch) + { + inst = null; + blockAfterSwitch = null; + if (i < 1) return false; + // match first block: checking switch-value for null or first value (Roslyn) + if (!(instructions[i].MatchIfInstruction(out var condition, out var firstBlockJump) && + instructions[i - 1].MatchStLoc(out var switchValueVar, out var switchValue) && switchValueVar.Type.IsKnownType(KnownTypeCode.String))) + return false; + if (!firstBlockJump.MatchBranch(out var firstBlock)) + return false; + bool isLegacy; + Block defaultBlock; + List<(string, Block)> values = new List<(string, Block)>(); + if (condition.MatchCompEquals(out var left, out var right) && right.MatchLdNull() && left.MatchLdLoc(switchValueVar)) { + isLegacy = true; + defaultBlock = firstBlock; + } else if (MatchStringEqualityComparison(condition, switchValueVar, out string value)) { + isLegacy = false; + defaultBlock = null; + values.Add((value, firstBlock)); + } else return false; + if (!(instructions.ElementAtOrDefault(i + 1) is Branch nextCaseJump)) + return false; + Block currentCaseBlock = nextCaseJump.TargetBlock; + Block nextCaseBlock; + while ((nextCaseBlock = MatchCaseBlock(currentCaseBlock, switchValueVar, out string value, out Block block)) != null) { + values.Add((value, block)); + currentCaseBlock = nextCaseBlock; + } + if (!ExtractLastJumpFromBlock(currentCaseBlock, out var exitBlock)) + return false; + if (values.Count == 0) + return false; + if (!values.All(b => ExtractLastJumpFromBlock(b.Item2, out var nextExit) && nextExit == exitBlock)) + return false; + if (currentCaseBlock.IncomingEdgeCount == (isLegacy ? 2 : 1)) { + var sections = new List(values.SelectWithIndex((index, b) => new SwitchSection { Labels = new LongSet(index), Body = new Branch(b.Item2) })); + var stringToInt = new StringToInt(new LdLoc(switchValueVar), values.SelectArray(item => item.Item1)); + inst = new SwitchInstruction(stringToInt); + inst.Sections.AddRange(sections); + blockAfterSwitch = currentCaseBlock; + return true; + } + return false; + } + + bool ExtractLastJumpFromBlock(Block block, out Block exitBlock) + { + exitBlock = null; + var lastInst = block.Instructions.LastOrDefault(); + if (lastInst == null || !lastInst.MatchBranch(out exitBlock)) + return false; + return true; + } + + 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; + if (!MatchStringEqualityComparison(condition, switchVariable, out value)) + return null; + if (!currentBlock.Instructions[1].MatchBranch(out var nextBlock)) + return null; + return nextBlock; + } + bool MatchLegacySwitchOnString(InstructionCollection instructions, int i, out SwitchInstruction inst, out Block blockAfterSwitch) { inst = null; From 2145543ada5774f48845956856f6cf26558f7f1b Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 29 Sep 2017 23:54:16 +0200 Subject: [PATCH 05/65] Add Switch pretty tests --- .../ICSharpCode.Decompiler.Tests.csproj | 1 + .../PrettyTestRunner.cs | 6 + .../TestCases/Pretty/Switch.cs | 226 +++++ .../TestCases/Pretty/Switch.il | 694 ++++++++++++++ .../TestCases/Pretty/Switch.opt.il | 558 +++++++++++ .../TestCases/Pretty/Switch.opt.roslyn.il | 700 ++++++++++++++ .../TestCases/Pretty/Switch.roslyn.il | 890 ++++++++++++++++++ 7 files changed, 3075 insertions(+) create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index 40842b724..683d63415 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -100,6 +100,7 @@ + diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs index 880297416..2b458282b 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 AnonymousTypes([Values(CompilerOptions.None, CompilerOptions.Optimize)] CompilerOptions cscOptions) { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs new file mode 100644 index 000000000..bfc8ea0c4 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs @@ -0,0 +1,226 @@ +// 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; + +namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty +{ + public static class Switch + { + 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 int.MaxValue: { + return "int.MaxValue"; + } + default: { + return "something else"; + } + } + } + + 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 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; + } + } + } +} \ 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..e04aa74b5 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il @@ -0,0 +1,694 @@ + +// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.17929 +// Copyright (c) Microsoft Corporation. All rights reserved. + + + +// 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 wau4yvxd +{ + .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. + .permissionset reqmin + = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +.module wau4yvxd.dll +// MVID: {102ACC94-685A-42C5-9229-AC386C0A78B1} +.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: 0x01440000 + + +// =============== CLASS MEMBERS DECLARATION =================== + +.class public abstract auto ansi sealed beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch + extends [mscorlib]System.Object +{ + .method public hidebysig static string + SparseIntegerSwitch(int32 i) cil managed + { + // Code size 224 (0xe0) + .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_004f + + IL_001d: ldloc.1 + IL_001e: ldc.i4 0xff676980 + IL_0023: beq.s IL_0072 + + IL_0025: ldloc.1 + IL_0026: ldc.i4.s -100 + IL_0028: beq.s IL_007b + + IL_002a: ldloc.1 + IL_002b: ldc.i4.m1 + IL_002c: sub + IL_002d: switch ( + IL_0084, + IL_008d, + IL_0096, + IL_009f, + IL_00d5, + IL_00a8) + IL_004a: br IL_00d5 + + IL_004f: ldloc.1 + IL_0050: ldc.i4.s 100 + IL_0052: beq.s IL_00b1 + + IL_0054: ldloc.1 + IL_0055: ldc.i4 0x2710 + IL_005a: sub + IL_005b: switch ( + IL_00ba, + IL_00c3) + IL_0068: ldloc.1 + IL_0069: ldc.i4 0x7fffffff + IL_006e: beq.s IL_00cc + + IL_0070: br.s IL_00d5 + + IL_0072: nop + IL_0073: ldstr "-10 mln" + IL_0078: stloc.0 + IL_0079: br.s IL_00de + + IL_007b: nop + IL_007c: ldstr "-hundred" + IL_0081: stloc.0 + IL_0082: br.s IL_00de + + IL_0084: nop + IL_0085: ldstr "-1" + IL_008a: stloc.0 + IL_008b: br.s IL_00de + + IL_008d: nop + IL_008e: ldstr "0" + IL_0093: stloc.0 + IL_0094: br.s IL_00de + + IL_0096: nop + IL_0097: ldstr "1" + IL_009c: stloc.0 + IL_009d: br.s IL_00de + + IL_009f: nop + IL_00a0: ldstr "2" + IL_00a5: stloc.0 + IL_00a6: br.s IL_00de + + IL_00a8: nop + IL_00a9: ldstr "4" + IL_00ae: stloc.0 + IL_00af: br.s IL_00de + + IL_00b1: nop + IL_00b2: ldstr "hundred" + IL_00b7: stloc.0 + IL_00b8: br.s IL_00de + + IL_00ba: nop + IL_00bb: ldstr "ten thousand" + IL_00c0: stloc.0 + IL_00c1: br.s IL_00de + + IL_00c3: nop + IL_00c4: ldstr "ten thousand and one" + IL_00c9: stloc.0 + IL_00ca: br.s IL_00de + + IL_00cc: nop + IL_00cd: ldstr "int.MaxValue" + IL_00d2: stloc.0 + IL_00d3: br.s IL_00de + + IL_00d5: nop + IL_00d6: ldstr "something else" + IL_00db: stloc.0 + IL_00dc: br.s IL_00de + + IL_00de: ldloc.0 + IL_00df: ret + } // end of method Switch::SparseIntegerSwitch + + .method public hidebysig static string + ShortSwitchOverString(string text) cil managed + { + // Code size 102 (0x66) + .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_005b + + 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_0049 + + 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_0052 + + IL_003e: br.s IL_005b + + IL_0040: nop + IL_0041: ldstr "Text1" + IL_0046: stloc.0 + IL_0047: br.s IL_0064 + + IL_0049: nop + IL_004a: ldstr "Text2" + IL_004f: stloc.0 + IL_0050: br.s IL_0064 + + IL_0052: nop + IL_0053: ldstr "Text3" + IL_0058: stloc.0 + IL_0059: br.s IL_0064 + + IL_005b: nop + IL_005c: ldstr "Default" + IL_0061: stloc.0 + IL_0062: br.s IL_0064 + + IL_0064: ldloc.0 + IL_0065: ret + } // end of method Switch::ShortSwitchOverString + + .method public hidebysig static string + SwitchOverString1(string text) cil managed + { + // Code size 255 (0xff) + .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_00ef + + IL_001a: volatile. + IL_001c: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{102ACC94-685A-42C5-9229-AC386C0A78B1}'::'$$method0x6000003-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 '{102ACC94-685A-42C5-9229-AC386C0A78B1}'::'$$method0x6000003-1' + IL_0084: volatile. + IL_0086: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{102ACC94-685A-42C5-9229-AC386C0A78B1}'::'$$method0x6000003-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_00f4 + + IL_0095: ldloc.2 + IL_0096: switch ( + IL_00b9, + IL_00c2, + IL_00c2, + IL_00cb, + IL_00d4, + IL_00dd, + IL_00e6) + IL_00b7: br.s IL_00f4 + + IL_00b9: nop + IL_00ba: ldstr "Text1" + IL_00bf: stloc.0 + IL_00c0: br.s IL_00fd + + IL_00c2: nop + IL_00c3: ldstr "Text2" + IL_00c8: stloc.0 + IL_00c9: br.s IL_00fd + + IL_00cb: nop + IL_00cc: ldstr "Text3" + IL_00d1: stloc.0 + IL_00d2: br.s IL_00fd + + IL_00d4: nop + IL_00d5: ldstr "Text4" + IL_00da: stloc.0 + IL_00db: br.s IL_00fd + + IL_00dd: nop + IL_00de: ldstr "Text5" + IL_00e3: stloc.0 + IL_00e4: br.s IL_00fd + + IL_00e6: nop + IL_00e7: ldstr "Text6" + IL_00ec: stloc.0 + IL_00ed: br.s IL_00fd + + IL_00ef: nop + IL_00f0: ldnull + IL_00f1: stloc.0 + IL_00f2: br.s IL_00fd + + IL_00f4: nop + IL_00f5: ldstr "Default" + IL_00fa: stloc.0 + IL_00fb: br.s IL_00fd + + IL_00fd: ldloc.0 + IL_00fe: ret + } // end of method Switch::SwitchOverString1 + + .method public hidebysig static string + SwitchOverString2() cil managed + { + // Code size 366 (0x16e) + .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_0163 + + IL_0018: volatile. + IL_001a: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{102ACC94-685A-42C5-9229-AC386C0A78B1}'::'$$method0x6000004-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 '{102ACC94-685A-42C5-9229-AC386C0A78B1}'::'$$method0x6000004-1' + IL_00b8: volatile. + IL_00ba: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{102ACC94-685A-42C5-9229-AC386C0A78B1}'::'$$method0x6000004-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_0163 + + IL_00cc: ldloc.2 + IL_00cd: switch ( + IL_0100, + IL_0109, + IL_0112, + IL_011b, + IL_0124, + IL_012d, + IL_0136, + IL_013f, + IL_0148, + IL_0151, + IL_015a) + IL_00fe: br.s IL_0163 + + IL_0100: nop + IL_0101: ldstr "Text1" + IL_0106: stloc.0 + IL_0107: br.s IL_016c + + IL_0109: nop + IL_010a: ldstr "Text2" + IL_010f: stloc.0 + IL_0110: br.s IL_016c + + IL_0112: nop + IL_0113: ldstr "Text3" + IL_0118: stloc.0 + IL_0119: br.s IL_016c + + IL_011b: nop + IL_011c: ldstr "Text4" + IL_0121: stloc.0 + IL_0122: br.s IL_016c + + IL_0124: nop + IL_0125: ldstr "Text5" + IL_012a: stloc.0 + IL_012b: br.s IL_016c + + IL_012d: nop + IL_012e: ldstr "Text6" + IL_0133: stloc.0 + IL_0134: br.s IL_016c + + IL_0136: nop + IL_0137: ldstr "Text7" + IL_013c: stloc.0 + IL_013d: br.s IL_016c + + IL_013f: nop + IL_0140: ldstr "Text8" + IL_0145: stloc.0 + IL_0146: br.s IL_016c + + IL_0148: nop + IL_0149: ldstr "Text9" + IL_014e: stloc.0 + IL_014f: br.s IL_016c + + IL_0151: nop + IL_0152: ldstr "Text10" + IL_0157: stloc.0 + IL_0158: br.s IL_016c + + IL_015a: nop + IL_015b: ldstr "Text11" + IL_0160: stloc.0 + IL_0161: br.s IL_016c + + IL_0163: nop + IL_0164: ldstr "Default" + IL_0169: stloc.0 + IL_016a: br.s IL_016c + + IL_016c: ldloc.0 + IL_016d: ret + } // end of method Switch::SwitchOverString2 + + .method public hidebysig static string + SwitchOverBool(bool b) cil managed + { + // Code size 67 (0x43) + .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_0033, + IL_002a) + IL_0028: br.s IL_003c + + IL_002a: nop + IL_002b: ldsfld string [mscorlib]System.Boolean::TrueString + IL_0030: stloc.0 + IL_0031: br.s IL_0041 + + IL_0033: nop + IL_0034: ldsfld string [mscorlib]System.Boolean::FalseString + IL_0039: stloc.0 + IL_003a: br.s IL_0041 + + IL_003c: nop + IL_003d: ldnull + IL_003e: stloc.0 + IL_003f: br.s IL_0041 + + IL_0041: ldloc.0 + IL_0042: ret + } // end of method Switch::SwitchOverBool + + .method public hidebysig static void SwitchInLoop(int32 i) cil managed + { + // Code size 141 (0x8d) + .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_0088 + + 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_0050, + IL_005d) + IL_0034: br.s IL_006a + + IL_0036: ldstr "one" + IL_003b: call void [mscorlib]System.Console::WriteLine(string) + IL_0040: nop + IL_0041: br.s IL_0082 + + IL_0043: ldstr "two" + IL_0048: call void [mscorlib]System.Console::WriteLine(string) + IL_004d: nop + IL_004e: br.s IL_0082 + + IL_0050: ldstr "three" + IL_0055: call void [mscorlib]System.Console::WriteLine(string) + IL_005a: nop + IL_005b: br.s IL_0088 + + IL_005d: ldstr "four" + IL_0062: call void [mscorlib]System.Console::WriteLine(string) + IL_0067: nop + IL_0068: br.s IL_008c + + IL_006a: ldstr "default" + IL_006f: call void [mscorlib]System.Console::WriteLine(string) + IL_0074: nop + IL_0075: ldstr "more code" + IL_007a: call void [mscorlib]System.Console::WriteLine(string) + IL_007f: nop + IL_0080: br.s IL_008c + + IL_0082: ldarg.0 + IL_0083: ldc.i4.1 + IL_0084: add + IL_0085: starg.s i + IL_0087: nop + IL_0088: ldc.i4.1 + IL_0089: stloc.1 + IL_008a: br.s IL_0019 + + IL_008c: ret + } // end of method Switch::SwitchInLoop + + .method public hidebysig static void SwitchWithGoto(int32 i) cil managed + { + // Code size 117 (0x75) + .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_0074 + + IL_0067: ldstr "default" + IL_006c: call void [mscorlib]System.Console::WriteLine(string) + IL_0071: nop + IL_0072: br.s IL_0074 + + IL_0074: ret + } // end of method Switch::SwitchWithGoto + +} // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch + +.class private auto ansi '{102ACC94-685A-42C5-9229-AC386C0A78B1}' + 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 '$$method0x6000003-1' + .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000004-1' +} // end of class '{102ACC94-685A-42C5-9229-AC386C0A78B1}' + + +// ============================================================= + +// *********** DISASSEMBLY COMPLETE *********************** +// WARNING: Created Win32 resource file ../../../TestCases/Pretty\Switch.res 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..4c6e7946e --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il @@ -0,0 +1,558 @@ + +// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.17929 +// Copyright (c) Microsoft Corporation. All rights reserved. + + + +// 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 gz2l4xfo +{ + .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. + .permissionset reqmin + = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +.module gz2l4xfo.dll +// MVID: {FFA858C4-FC28-4EB1-BDB5-C80B304AD168} +.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: 0x008F0000 + + +// =============== CLASS MEMBERS DECLARATION =================== + +.class public abstract auto ansi sealed beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch + extends [mscorlib]System.Object +{ + .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 + 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 + 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 '{FFA858C4-FC28-4EB1-BDB5-C80B304AD168}'::'$$method0x6000003-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 '{FFA858C4-FC28-4EB1-BDB5-C80B304AD168}'::'$$method0x6000003-1' + IL_0082: volatile. + IL_0084: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{FFA858C4-FC28-4EB1-BDB5-C80B304AD168}'::'$$method0x6000003-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 '{FFA858C4-FC28-4EB1-BDB5-C80B304AD168}'::'$$method0x6000004-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 '{FFA858C4-FC28-4EB1-BDB5-C80B304AD168}'::'$$method0x6000004-1' + IL_00b6: volatile. + IL_00b8: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{FFA858C4-FC28-4EB1-BDB5-C80B304AD168}'::'$$method0x6000004-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 124 (0x7c) + .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_0049, + IL_0055) + IL_002f: br.s IL_0060 + + IL_0031: ldstr "one" + IL_0036: call void [mscorlib]System.Console::WriteLine(string) + IL_003b: br.s IL_0075 + + IL_003d: ldstr "two" + IL_0042: call void [mscorlib]System.Console::WriteLine(string) + IL_0047: br.s IL_0075 + + IL_0049: ldstr "three" + IL_004e: call void [mscorlib]System.Console::WriteLine(string) + IL_0053: br.s IL_0015 + + IL_0055: ldstr "four" + IL_005a: call void [mscorlib]System.Console::WriteLine(string) + IL_005f: ret + + IL_0060: ldstr "default" + IL_0065: call void [mscorlib]System.Console::WriteLine(string) + IL_006a: ldstr "more code" + IL_006f: call void [mscorlib]System.Console::WriteLine(string) + IL_0074: ret + + IL_0075: ldarg.0 + IL_0076: ldc.i4.1 + IL_0077: add + IL_0078: starg.s i + IL_007a: br.s IL_0015 + } // end of method Switch::SwitchInLoop + + .method public hidebysig static void SwitchWithGoto(int32 i) cil managed + { + // Code size 104 (0x68) + .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_0052) + IL_002f: br.s IL_005d + + IL_0031: ldstr "one" + IL_0036: call void [mscorlib]System.Console::WriteLine(string) + IL_003b: br.s IL_005d + + 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: ret + + IL_0052: ldstr "four" + IL_0057: call void [mscorlib]System.Console::WriteLine(string) + IL_005c: ret + + IL_005d: ldstr "default" + IL_0062: call void [mscorlib]System.Console::WriteLine(string) + IL_0067: ret + } // end of method Switch::SwitchWithGoto + +} // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch + +.class private auto ansi '{FFA858C4-FC28-4EB1-BDB5-C80B304AD168}' + 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 '$$method0x6000003-1' + .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000004-1' +} // end of class '{FFA858C4-FC28-4EB1-BDB5-C80B304AD168}' + + +// ============================================================= + +// *********** DISASSEMBLY COMPLETE *********************** +// WARNING: Created Win32 resource file ../../../TestCases/Pretty\Switch.opt.res 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..a86c1b3cc --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il @@ -0,0 +1,700 @@ + +// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.17929 +// Copyright (c) Microsoft Corporation. All rights reserved. + + + +// 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: {3690F18D-C570-405A-8B33-B6E0A6696EFD} +.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: 0x015B0000 + + +// =============== CLASS MEMBERS DECLARATION =================== + +.class public abstract auto ansi sealed beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch + extends [mscorlib]System.Object +{ + .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 + 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 + 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 122 (0x7a) + .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_0047, + IL_0053) + IL_002d: br.s IL_005e + + IL_002f: ldstr "one" + IL_0034: call void [mscorlib]System.Console::WriteLine(string) + IL_0039: br.s IL_0073 + + IL_003b: ldstr "two" + IL_0040: call void [mscorlib]System.Console::WriteLine(string) + IL_0045: br.s IL_0073 + + IL_0047: ldstr "three" + IL_004c: call void [mscorlib]System.Console::WriteLine(string) + IL_0051: br.s IL_0015 + + 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 "more code" + IL_006d: call void [mscorlib]System.Console::WriteLine(string) + IL_0072: ret + + IL_0073: ldarg.0 + IL_0074: ldc.i4.1 + IL_0075: add + IL_0076: starg.s i + IL_0078: br.s IL_0015 + } // end of method Switch::SwitchInLoop + + .method public hidebysig static void SwitchWithGoto(int32 i) cil managed + { + // Code size 102 (0x66) + .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_0050) + IL_002d: br.s IL_005b + + IL_002f: ldstr "one" + IL_0034: call void [mscorlib]System.Console::WriteLine(string) + IL_0039: br.s IL_005b + + 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: ret + + IL_0050: ldstr "four" + IL_0055: call void [mscorlib]System.Console::WriteLine(string) + IL_005a: ret + + IL_005b: ldstr "default" + IL_0060: call void [mscorlib]System.Console::WriteLine(string) + IL_0065: ret + } // end of method Switch::SwitchWithGoto + +} // 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..0132ebf10 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il @@ -0,0 +1,890 @@ + +// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.17929 +// Copyright (c) Microsoft Corporation. All rights reserved. + + + +// 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: {08E636DF-9FF0-4BF4-9F8F-69859C40E218} +.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: 0x02E70000 + + +// =============== CLASS MEMBERS DECLARATION =================== + +.class public abstract auto ansi sealed beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch + extends [mscorlib]System.Object +{ + .method public hidebysig static string + SparseIntegerSwitch(int32 i) cil managed + { + // Code size 238 (0xee) + .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_0089 + + IL_002c: br.s IL_002e + + IL_002e: ldloc.0 + IL_002f: ldc.i4.m1 + IL_0030: sub + IL_0031: switch ( + IL_0092, + IL_009b, + IL_00a4, + IL_00ad, + IL_00e3, + IL_00b6) + IL_004e: br IL_00e3 + + 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_00bf + + IL_0060: br.s IL_0062 + + IL_0062: ldloc.0 + IL_0063: ldc.i4 0x2710 + IL_0068: beq.s IL_00c8 + + IL_006a: br.s IL_00e3 + + IL_006c: ldloc.0 + IL_006d: ldc.i4 0x2711 + IL_0072: beq.s IL_00d1 + + IL_0074: br.s IL_0076 + + IL_0076: ldloc.0 + IL_0077: ldc.i4 0x7fffffff + IL_007c: beq.s IL_00da + + IL_007e: br.s IL_00e3 + + IL_0080: nop + IL_0081: ldstr "-10 mln" + IL_0086: stloc.1 + IL_0087: br.s IL_00ec + + IL_0089: nop + IL_008a: ldstr "-hundred" + IL_008f: stloc.1 + IL_0090: br.s IL_00ec + + IL_0092: nop + IL_0093: ldstr "-1" + IL_0098: stloc.1 + IL_0099: br.s IL_00ec + + IL_009b: nop + IL_009c: ldstr "0" + IL_00a1: stloc.1 + IL_00a2: br.s IL_00ec + + IL_00a4: nop + IL_00a5: ldstr "1" + IL_00aa: stloc.1 + IL_00ab: br.s IL_00ec + + IL_00ad: nop + IL_00ae: ldstr "2" + IL_00b3: stloc.1 + IL_00b4: br.s IL_00ec + + IL_00b6: nop + IL_00b7: ldstr "4" + IL_00bc: stloc.1 + IL_00bd: br.s IL_00ec + + IL_00bf: nop + IL_00c0: ldstr "hundred" + IL_00c5: stloc.1 + IL_00c6: br.s IL_00ec + + IL_00c8: nop + IL_00c9: ldstr "ten thousand" + IL_00ce: stloc.1 + IL_00cf: br.s IL_00ec + + IL_00d1: nop + IL_00d2: ldstr "ten thousand and one" + IL_00d7: stloc.1 + IL_00d8: br.s IL_00ec + + IL_00da: nop + IL_00db: ldstr "int.MaxValue" + IL_00e0: stloc.1 + IL_00e1: br.s IL_00ec + + IL_00e3: nop + IL_00e4: ldstr "something else" + IL_00e9: stloc.1 + IL_00ea: br.s IL_00ec + + IL_00ec: ldloc.1 + IL_00ed: ret + } // end of method Switch::SparseIntegerSwitch + + .method public hidebysig static string + ShortSwitchOverString(string text) cil managed + { + // Code size 99 (0x63) + .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_0046 + + 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_004f + + IL_003b: br.s IL_0058 + + IL_003d: nop + IL_003e: ldstr "Text1" + IL_0043: stloc.1 + IL_0044: br.s IL_0061 + + IL_0046: nop + IL_0047: ldstr "Text2" + IL_004c: stloc.1 + IL_004d: br.s IL_0061 + + IL_004f: nop + IL_0050: ldstr "Text3" + IL_0055: stloc.1 + IL_0056: br.s IL_0061 + + IL_0058: nop + IL_0059: ldstr "Default" + IL_005e: stloc.1 + IL_005f: br.s IL_0061 + + IL_0061: ldloc.1 + IL_0062: ret + } // end of method Switch::ShortSwitchOverString + + .method public hidebysig static string + SwitchOverString1(string text) cil managed + { + // Code size 333 (0x14d) + .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_0142 + + 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_0142 + + 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_0142 + + 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_0142 + + 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_0142 + + 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_0110 + + IL_00b2: br IL_0142 + + 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_0110 + + IL_00c4: br.s IL_0142 + + 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_0119 + + IL_00d3: br.s IL_0142 + + 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_0122 + + IL_00e2: br.s IL_0142 + + 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_012b + + IL_00f1: br.s IL_0142 + + 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_0134 + + IL_0100: br.s IL_0142 + + IL_0102: ldloc.0 + IL_0103: brfalse.s IL_013d + + IL_0105: br.s IL_0142 + + IL_0107: nop + IL_0108: ldstr "Text1" + IL_010d: stloc.2 + IL_010e: br.s IL_014b + + IL_0110: nop + IL_0111: ldstr "Text2" + IL_0116: stloc.2 + IL_0117: br.s IL_014b + + IL_0119: nop + IL_011a: ldstr "Text3" + IL_011f: stloc.2 + IL_0120: br.s IL_014b + + IL_0122: nop + IL_0123: ldstr "Text4" + IL_0128: stloc.2 + IL_0129: br.s IL_014b + + IL_012b: nop + IL_012c: ldstr "Text5" + IL_0131: stloc.2 + IL_0132: br.s IL_014b + + IL_0134: nop + IL_0135: ldstr "Text6" + IL_013a: stloc.2 + IL_013b: br.s IL_014b + + IL_013d: nop + IL_013e: ldnull + IL_013f: stloc.2 + IL_0140: br.s IL_014b + + IL_0142: nop + IL_0143: ldstr "Default" + IL_0148: stloc.2 + IL_0149: br.s IL_014b + + IL_014b: ldloc.2 + IL_014c: ret + } // end of method Switch::SwitchOverString1 + + .method public hidebysig static string + SwitchOverString2() cil managed + { + // Code size 518 (0x206) + .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_01fb + + 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_0156 + + IL_005e: br.s IL_0060 + + IL_0060: ldloc.1 + IL_0061: ldc.i4 0x4c7c71f6 + IL_0066: beq IL_0168 + + IL_006b: br IL_01fb + + 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_0189 + + 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_0144 + + IL_009d: br IL_01fb + + 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_017a + + 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_01fb + + IL_00c6: ldloc.0 + IL_00c7: ldstr "First case" + IL_00cc: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_00d1: brtrue IL_0198 + + IL_00d6: br IL_01fb + + IL_00db: ldloc.0 + IL_00dc: ldstr "Second case" + IL_00e1: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_00e6: brtrue IL_01a1 + + IL_00eb: br IL_01fb + + IL_00f0: ldloc.0 + IL_00f1: ldstr "Third case" + IL_00f6: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_00fb: brtrue IL_01aa + + IL_0100: br IL_01fb + + IL_0105: ldloc.0 + IL_0106: ldstr "Fourth case" + IL_010b: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0110: brtrue IL_01b3 + + IL_0115: br IL_01fb + + IL_011a: ldloc.0 + IL_011b: ldstr "Fifth case" + IL_0120: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0125: brtrue IL_01bc + + IL_012a: br IL_01fb + + IL_012f: ldloc.0 + IL_0130: ldstr "Sixth case" + IL_0135: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_013a: brtrue IL_01c5 + + IL_013f: br IL_01fb + + IL_0144: ldloc.0 + IL_0145: ldstr "Seventh case" + IL_014a: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_014f: brtrue.s IL_01ce + + IL_0151: br IL_01fb + + IL_0156: ldloc.0 + IL_0157: ldstr "Eighth case" + IL_015c: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0161: brtrue.s IL_01d7 + + IL_0163: br IL_01fb + + IL_0168: ldloc.0 + IL_0169: ldstr "Ninth case" + IL_016e: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0173: brtrue.s IL_01e0 + + IL_0175: br IL_01fb + + IL_017a: ldloc.0 + IL_017b: ldstr "Tenth case" + IL_0180: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0185: brtrue.s IL_01e9 + + IL_0187: br.s IL_01fb + + IL_0189: ldloc.0 + IL_018a: ldstr "Eleventh case" + IL_018f: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0194: brtrue.s IL_01f2 + + IL_0196: br.s IL_01fb + + IL_0198: nop + IL_0199: ldstr "Text1" + IL_019e: stloc.2 + IL_019f: br.s IL_0204 + + IL_01a1: nop + IL_01a2: ldstr "Text2" + IL_01a7: stloc.2 + IL_01a8: br.s IL_0204 + + IL_01aa: nop + IL_01ab: ldstr "Text3" + IL_01b0: stloc.2 + IL_01b1: br.s IL_0204 + + IL_01b3: nop + IL_01b4: ldstr "Text4" + IL_01b9: stloc.2 + IL_01ba: br.s IL_0204 + + IL_01bc: nop + IL_01bd: ldstr "Text5" + IL_01c2: stloc.2 + IL_01c3: br.s IL_0204 + + IL_01c5: nop + IL_01c6: ldstr "Text6" + IL_01cb: stloc.2 + IL_01cc: br.s IL_0204 + + IL_01ce: nop + IL_01cf: ldstr "Text7" + IL_01d4: stloc.2 + IL_01d5: br.s IL_0204 + + IL_01d7: nop + IL_01d8: ldstr "Text8" + IL_01dd: stloc.2 + IL_01de: br.s IL_0204 + + IL_01e0: nop + IL_01e1: ldstr "Text9" + IL_01e6: stloc.2 + IL_01e7: br.s IL_0204 + + IL_01e9: nop + IL_01ea: ldstr "Text10" + IL_01ef: stloc.2 + IL_01f0: br.s IL_0204 + + IL_01f2: nop + IL_01f3: ldstr "Text11" + IL_01f8: stloc.2 + IL_01f9: br.s IL_0204 + + IL_01fb: nop + IL_01fc: ldstr "Default" + IL_0201: stloc.2 + IL_0202: br.s IL_0204 + + IL_0204: ldloc.2 + IL_0205: ret + } // end of method Switch::SwitchOverString2 + + .method public hidebysig static string + SwitchOverBool(bool b) cil managed + { + // Code size 62 (0x3e) + .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_002e + + 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_0037 + + IL_0025: nop + IL_0026: ldsfld string [mscorlib]System.Boolean::TrueString + IL_002b: stloc.1 + IL_002c: br.s IL_003c + + IL_002e: nop + IL_002f: ldsfld string [mscorlib]System.Boolean::FalseString + IL_0034: stloc.1 + IL_0035: br.s IL_003c + + IL_0037: nop + IL_0038: ldnull + IL_0039: stloc.1 + IL_003a: br.s IL_003c + + IL_003c: ldloc.1 + IL_003d: ret + } // end of method Switch::SwitchOverBool + + .method public hidebysig static void SwitchInLoop(int32 i) cil managed + { + // Code size 141 (0x8d) + .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_0088 + + 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_0050, + IL_005d) + IL_0034: br.s IL_006a + + IL_0036: ldstr "one" + IL_003b: call void [mscorlib]System.Console::WriteLine(string) + IL_0040: nop + IL_0041: br.s IL_0082 + + IL_0043: ldstr "two" + IL_0048: call void [mscorlib]System.Console::WriteLine(string) + IL_004d: nop + IL_004e: br.s IL_0082 + + IL_0050: ldstr "three" + IL_0055: call void [mscorlib]System.Console::WriteLine(string) + IL_005a: nop + IL_005b: br.s IL_0088 + + IL_005d: ldstr "four" + IL_0062: call void [mscorlib]System.Console::WriteLine(string) + IL_0067: nop + IL_0068: br.s IL_008c + + IL_006a: ldstr "default" + IL_006f: call void [mscorlib]System.Console::WriteLine(string) + IL_0074: nop + IL_0075: ldstr "more code" + IL_007a: call void [mscorlib]System.Console::WriteLine(string) + IL_007f: nop + IL_0080: br.s IL_008c + + IL_0082: ldarg.0 + IL_0083: ldc.i4.1 + IL_0084: add + IL_0085: starg.s i + IL_0087: nop + IL_0088: ldc.i4.1 + IL_0089: stloc.1 + IL_008a: br.s IL_0019 + + IL_008c: ret + } // end of method Switch::SwitchInLoop + + .method public hidebysig static void SwitchWithGoto(int32 i) cil managed + { + // Code size 117 (0x75) + .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_0074 + + IL_0067: ldstr "default" + IL_006c: call void [mscorlib]System.Console::WriteLine(string) + IL_0071: nop + IL_0072: br.s IL_0074 + + IL_0074: ret + } // end of method Switch::SwitchWithGoto + +} // 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 *********************** From 8ee222b37370c9c851c6e4b733a2da4dd072939b Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 29 Sep 2017 23:55:06 +0200 Subject: [PATCH 06/65] Remove old switch-on-string code from PatternStatementTransform --- .../CSharp/CSharpDecompiler.cs | 8 +- .../Transforms/PatternStatementTransform.cs | 172 ------------------ 2 files changed, 4 insertions(+), 176 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index b6ff80b98..b488578ba 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -201,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; } @@ -215,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/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()) }, From 4394250d712e6b539a5dbcf5a75b3cd96c78980d Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 29 Sep 2017 23:56:13 +0200 Subject: [PATCH 07/65] Fix Switch(string)-detection in various cases --- .../IL/Transforms/SwitchOnStringTransform.cs | 82 +++++++++++++++---- 1 file changed, 64 insertions(+), 18 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs index b533e8d3b..723bb6e02 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs @@ -29,23 +29,29 @@ namespace ICSharpCode.Decompiler.IL.Transforms { public void Run(ILFunction function, ILTransformContext context) { + if (!context.Settings.SwitchStatementOnString) + return; + HashSet changedContainers = new HashSet(); foreach (var block in function.Descendants.OfType()) { for (int i = block.Instructions.Count - 1; i >= 0; i--) { SwitchInstruction newSwitch; Block blockAfterSwitch = null; - if (!MatchCascadingIfStatements(block.Instructions, i, out newSwitch, out blockAfterSwitch) && - !MatchLegacySwitchOnString(block.Instructions, i, out newSwitch, out blockAfterSwitch) && - !MatchRoslynSwitchOnString(block.Instructions, i, out newSwitch)) - continue; + bool removeExtraStore = false; // the Roslyn switch pattern uses an extra store for hash calculation. + if (!MatchCascadingIfStatements(block.Instructions, i, out newSwitch, out blockAfterSwitch)) + if (!MatchLegacySwitchOnString(block.Instructions, i, out newSwitch, out blockAfterSwitch)) + if (MatchRoslynSwitchOnString(block.Instructions, i, out newSwitch)) + removeExtraStore = true; + else + continue; if (i + 1 < block.Instructions.Count && block.Instructions[i + 1] is Branch b && blockAfterSwitch != null) { block.Instructions[i + 1].ReplaceWith(new Branch(blockAfterSwitch)); } block.Instructions[i].ReplaceWith(newSwitch); - if (newSwitch.Value.MatchLdLoc(out var switchVar) && !block.Instructions[i - 1].MatchLdLoc(switchVar)) { + if (removeExtraStore) { block.Instructions.RemoveAt(i - 1); i--; } @@ -68,37 +74,39 @@ namespace ICSharpCode.Decompiler.IL.Transforms { inst = null; blockAfterSwitch = null; - if (i < 1) return false; // match first block: checking switch-value for null or first value (Roslyn) - if (!(instructions[i].MatchIfInstruction(out var condition, out var firstBlockJump) && - instructions[i - 1].MatchStLoc(out var switchValueVar, out var switchValue) && switchValueVar.Type.IsKnownType(KnownTypeCode.String))) + if (!(instructions[i].MatchIfInstruction(out var condition, out var firstBlockJump))) return false; if (!firstBlockJump.MatchBranch(out var firstBlock)) return false; bool isLegacy; Block defaultBlock; List<(string, Block)> values = new List<(string, Block)>(); - if (condition.MatchCompEquals(out var left, out var right) && right.MatchLdNull() && left.MatchLdLoc(switchValueVar)) { + if (condition.MatchCompEquals(out var left, out var right) && right.MatchLdNull() && left.MatchLdLoc(out var switchValueVar)) { isLegacy = true; defaultBlock = firstBlock; - } else if (MatchStringEqualityComparison(condition, switchValueVar, out string value)) { + } else if (MatchStringEqualityComparison(condition, out switchValueVar, out string value)) { isLegacy = false; defaultBlock = null; values.Add((value, firstBlock)); } else return false; + if (!switchValueVar.IsSingleDefinition) + return false; if (!(instructions.ElementAtOrDefault(i + 1) is Branch nextCaseJump)) return false; Block currentCaseBlock = nextCaseJump.TargetBlock; Block nextCaseBlock; - while ((nextCaseBlock = MatchCaseBlock(currentCaseBlock, switchValueVar, out string value, out Block block)) != null) { + while ((nextCaseBlock = MatchCaseBlock(currentCaseBlock, ref switchValueVar, out string value, out Block block)) != null) { values.Add((value, block)); currentCaseBlock = nextCaseBlock; } - if (!ExtractLastJumpFromBlock(currentCaseBlock, out var exitBlock)) + BlockContainer container = null; + if (!ExtractLastJumpFromBlock(currentCaseBlock, out var exitBlock) && !ExtractLastLeaveFromBlock(currentCaseBlock, out container)) return false; if (values.Count == 0) return false; - if (!values.All(b => ExtractLastJumpFromBlock(b.Item2, out var nextExit) && nextExit == exitBlock)) + if (!(values.All(b => ExtractLastJumpFromBlock(b.Item2, out var nextExit) && nextExit == exitBlock) || + (exitBlock == null && values.All(b => ExtractLastLeaveFromBlock(b.Item2, out var exitContainer) && exitContainer == container)))) return false; if (currentCaseBlock.IncomingEdgeCount == (isLegacy ? 2 : 1)) { var sections = new List(values.SelectWithIndex((index, b) => new SwitchSection { Labels = new LongSet(index), Body = new Branch(b.Item2) })); @@ -120,23 +128,55 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; } - Block MatchCaseBlock(Block currentBlock, ILVariable switchVariable, out string value, out Block caseBlock) + bool ExtractLastLeaveFromBlock(Block block, out BlockContainer exitBlock) + { + exitBlock = null; + var lastInst = block.Instructions.LastOrDefault(); + if (lastInst == null || !lastInst.MatchLeave(out exitBlock, out _)) + return false; + return true; + } + + Block MatchCaseBlock(Block currentBlock, ref 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; - if (!MatchStringEqualityComparison(condition, switchVariable, out value)) + 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, out var newSwitchVariable, out value)) + return null; + if (!newSwitchVariable.IsSingleDefinition) return null; - if (!currentBlock.Instructions[1].MatchBranch(out var nextBlock)) + if (switchVariable != newSwitchVariable && !(IsInitializedBy(switchVariable, newSwitchVariable) || IsInitializedBy(newSwitchVariable, switchVariable))) return null; + if (!newSwitchVariable.Type.IsKnownType(KnownTypeCode.String)) + return null; + switchVariable = newSwitchVariable; return nextBlock; } + bool IsInitializedBy(ILVariable switchVariable, ILVariable newSwitchVariable) + { + if (!switchVariable.IsSingleDefinition) + return false; + return switchVariable.StoreInstructions.OfType().Single().Value.MatchLdLoc(newSwitchVariable); + } + bool MatchLegacySwitchOnString(InstructionCollection instructions, int i, out SwitchInstruction inst, out Block blockAfterSwitch) { inst = null; @@ -148,7 +188,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!exitBlockJump.MatchBranch(out var exitBlock)) return false; - if (!(condition.MatchCompEquals(out var left, out var right) && right.MatchLdNull() && left.Match(switchValue).Success)) + if (!(condition.MatchCompEquals(out var left, out var right) && right.MatchLdNull() && (left.Match(switchValue).Success || left.MatchLdLoc(switchValueVar)))) return false; var nextBlockJump = instructions.ElementAtOrDefault(i + 1) as Branch; if (nextBlockJump == null || nextBlockJump.TargetBlock.IncomingEdgeCount != 1) @@ -291,8 +331,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms } bool MatchStringEqualityComparison(ILInstruction condition, ILVariable variable, out string stringValue) + { + return MatchStringEqualityComparison(condition, out var v, out stringValue) && v == variable; + } + + 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.Arguments.Count == 2) { left = c.Arguments[0]; @@ -301,7 +347,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; } else if (condition.MatchCompEquals(out left, out right) && right.MatchLdNull()) { } else return false; - return left.MatchLdLoc(variable); + return left.MatchLdLoc(out variable); } } } From b78ef2209bc7fb78b20c47ed097514ad47ee734e Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 1 Oct 2017 20:08:33 +0200 Subject: [PATCH 08/65] Add InlineReturnTransform --- .../CSharp/CSharpDecompiler.cs | 1 + .../ICSharpCode.Decompiler.csproj | 1 + .../IL/Transforms/InlineReturnTransform.cs | 83 +++++++++++++++++++ 3 files changed, 85 insertions(+) create mode 100644 ICSharpCode.Decompiler/IL/Transforms/InlineReturnTransform.cs diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index b488578ba..00717a4bd 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 diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 16409351a..35edd7dbc 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -273,6 +273,7 @@ + diff --git a/ICSharpCode.Decompiler/IL/Transforms/InlineReturnTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/InlineReturnTransform.cs new file mode 100644 index 000000000..ae5138358 --- /dev/null +++ b/ICSharpCode.Decompiler/IL/Transforms/InlineReturnTransform.cs @@ -0,0 +1,83 @@ +// 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 thie return. + /// + class InlineReturnTransform : IILTransform + { + public void Run(ILFunction function, ILTransformContext context) + { + var instructionsToModify = new List<(BlockContainer, Block, Branch)>(); + var possibleReturnVars = new Queue<(ILVariable, Block)>(); + var tempList = new List<(BlockContainer, Block, Branch)>(); + + foreach (var leave in function.Descendants.OfType()) { + if (!(leave.Parent is Block b && b.Instructions.Count == 1)) + continue; + if (!leave.Value.MatchLdLoc(out var returnVar) || returnVar.Kind != VariableKind.Local) + continue; + possibleReturnVars.Enqueue((returnVar, b)); + } + + while (possibleReturnVars.Count > 0) { + var (returnVar, leaveBlock) = possibleReturnVars.Dequeue(); + bool transform = true; + foreach (StLoc store in returnVar.StoreInstructions.OfType()) { + if (!(store.Parent is Block storeBlock)) { + transform = false; + break; + } + if (store.ChildIndex + 2 != storeBlock.Instructions.Count) { + transform = false; + break; + } + if (!(storeBlock.Instructions[store.ChildIndex + 1] is Branch br)) { + transform = false; + break; + } + if (br.TargetBlock != leaveBlock) { + transform = false; + break; + } + var targetBlockContainer = BlockContainer.FindClosestContainer(store); + if (targetBlockContainer == null) { + transform = false; + break; + } + tempList.Add((targetBlockContainer, leaveBlock, br)); + } + if (transform) + instructionsToModify.AddRange(tempList); + tempList.Clear(); + } + + foreach (var (container, block, br) in instructionsToModify) { + var newBlock = (Block)block.Clone(); + container.Blocks.Add(newBlock); + br.TargetBlock = newBlock; + } + } + } +} From 9719926b6bc1e43c9d43d811e5c27d9e797209b8 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 1 Oct 2017 22:01:31 +0200 Subject: [PATCH 09/65] Fix some more bugs in SwitchOnStringTransform --- .../TestCases/Pretty/Switch.cs | 6 +-- .../IL/Transforms/SwitchOnStringTransform.cs | 46 +++++++++++++------ 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs index bfc8ea0c4..c76d1ff85 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs @@ -56,7 +56,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty case 10001: { return "ten thousand and one"; } - case int.MaxValue: { + case 2147483647: { return "int.MaxValue"; } default: { @@ -107,8 +107,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty case "Sixth case": { return "Text6"; } - case null: { - return null; + case (string)null: { + return (string)null; } default: { return "Default"; diff --git a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs index 723bb6e02..a72cff6db 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs @@ -19,7 +19,7 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; +using ICSharpCode.Decompiler.IL.ControlFlow; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; @@ -56,6 +56,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms i--; } + // Combine cases with the same branch target: + SwitchDetection.SimplifySwitchInstruction(block); + // This happens in some cases: // Use correct index after transformation. if (i >= block.Instructions.Count) @@ -100,13 +103,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms values.Add((value, block)); currentCaseBlock = nextCaseBlock; } - BlockContainer container = null; - if (!ExtractLastJumpFromBlock(currentCaseBlock, out var exitBlock) && !ExtractLastLeaveFromBlock(currentCaseBlock, out container)) + var container = BlockContainer.FindClosestContainer(firstBlock); + if (!ExtractLastJumpFromBlock(currentCaseBlock, out var exitBlock) && !ExtractLastLeaveFromBlock(currentCaseBlock, container)) return false; if (values.Count == 0) return false; - if (!(values.All(b => ExtractLastJumpFromBlock(b.Item2, out var nextExit) && nextExit == exitBlock) || - (exitBlock == null && values.All(b => ExtractLastLeaveFromBlock(b.Item2, out var exitContainer) && exitContainer == container)))) + if (!(values.All(b => ExtractLastJumpFromBlock(b.Item2, out var nextExit) && IsExitBlock(nextExit, container)) || + (exitBlock == null && values.All(b => ExtractLastLeaveFromBlock(b.Item2, container))))) return false; if (currentCaseBlock.IncomingEdgeCount == (isLegacy ? 2 : 1)) { var sections = new List(values.SelectWithIndex((index, b) => new SwitchSection { Labels = new LongSet(index), Body = new Branch(b.Item2) })); @@ -119,6 +122,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; } + bool IsExitBlock(Block nextExit, BlockContainer container) + { + if (nextExit.Instructions.Count != 1) + return false; + return nextExit.Instructions[0].MatchLeave(container, out _); + } + bool ExtractLastJumpFromBlock(Block block, out Block exitBlock) { exitBlock = null; @@ -128,13 +138,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; } - bool ExtractLastLeaveFromBlock(Block block, out BlockContainer exitBlock) + bool ExtractLastLeaveFromBlock(Block block, BlockContainer container) { - exitBlock = null; var lastInst = block.Instructions.LastOrDefault(); - if (lastInst == null || !lastInst.MatchLeave(out exitBlock, out _)) + if (lastInst == null || !lastInst.MatchLeave(out var b, out _)) return false; - return true; + return b == container; } Block MatchCaseBlock(Block currentBlock, ref ILVariable switchVariable, out string value, out Block caseBlock) @@ -174,7 +183,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms { if (!switchVariable.IsSingleDefinition) return false; - return switchVariable.StoreInstructions.OfType().Single().Value.MatchLdLoc(newSwitchVariable); + var storeInst = switchVariable.StoreInstructions.OfType().SingleOrDefault(); + if (storeInst == null) + return false; + return storeInst.Value.MatchLdLoc(newSwitchVariable); } bool MatchLegacySwitchOnString(InstructionCollection instructions, int i, out SwitchInstruction inst, out Block blockAfterSwitch) @@ -236,7 +248,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms stringValues.Add(null); sections.Add(new SwitchSection() { Labels = new Util.LongSet(stringValues.Count - 1), Body = new Branch(exitBlock) }); } - var stringToInt = new StringToInt(switchValue.Clone(), stringValues.ToArray()); + var stringToInt = new StringToInt(new LdLoc(switchValueVar), stringValues.ToArray()); inst = new SwitchInstruction(stringToInt); inst.DefaultBody = switchInst.DefaultBody; inst.Sections.AddRange(sections); @@ -299,14 +311,22 @@ namespace ICSharpCode.Decompiler.IL.Transforms foreach (var section in switchInst.Sections) { if (!section.Body.MatchBranch(out Block target)) return false; - if (target.IncomingEdgeCount != 1 || target.Instructions.Count == 0) + if (target.IncomingEdgeCount != 1 || target.Instructions.Count != 2) return false; if (!target.Instructions[0].MatchIfInstruction(out var condition, out var bodyBranch)) return false; - if (!MatchStringEqualityComparison(condition, switchValue.Variable, out string stringValue)) + if (!target.Instructions[1].MatchBranch(out Block exit)) return false; if (!bodyBranch.MatchBranch(out Block body)) return false; + if (!MatchStringEqualityComparison(condition, switchValue.Variable, out string stringValue)) { + if (condition.MatchLogicNot(out condition) && MatchStringEqualityComparison(condition, switchValue.Variable, out stringValue)) { + var swap = body; + body = exit; + exit = swap; + } else + return false; + } stringValues.Add((index++, stringValue, body)); } From 66dc52c33c897e212202ab1a8e9ff5562aaa0347 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 1 Oct 2017 22:05:40 +0200 Subject: [PATCH 10/65] Fix handling of 'leave' instruction in SwitchAnalysis. This is necessary for detecting a sparse switch when the default case consists of "return;". --- .../IL/ControlFlow/SwitchAnalysis.cs | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/SwitchAnalysis.cs b/ICSharpCode.Decompiler/IL/ControlFlow/SwitchAnalysis.cs index 928b934c6..c280faa02 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; @@ -180,10 +191,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 +201,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)); } From 930d142a62baf39d7be892a2662e2880a505b4a2 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 2 Oct 2017 08:28:34 +0200 Subject: [PATCH 11/65] Fix bug in SwitchOnStringTransform introduced by 9719926b6bc1e43c9d43d811e5c27d9e797209b8: Ignore exit instruction if condition is not inverted. Otherwise we need a special case for br and leave. --- .../PrettyTestRunner.cs | 2 +- .../TestCases/Pretty/Switch.cs | 103 ++++++++++-------- .../IL/Transforms/SwitchOnStringTransform.cs | 6 +- 3 files changed, 60 insertions(+), 51 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs index 2b458282b..f8f0c8049 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -97,7 +97,7 @@ namespace ICSharpCode.Decompiler.Tests Run(cscOptions: cscOptions); } - [Test] + [Test, Ignore("unnecessary casts on null literals, control-flow issues with switch in loops, goto, goto case, etc.")] public void Switch([ValueSource("defaultOptions")] CompilerOptions cscOptions) { Run(cscOptions: cscOptions); diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs index c76d1ff85..858d91a79 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs @@ -27,41 +27,41 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty Console.WriteLine("SparseIntegerSwitch: " + i); switch (i) { case -10000000: { - return "-10 mln"; - } + return "-10 mln"; + } case -100: { - return "-hundred"; - } + return "-hundred"; + } case -1: { - return "-1"; - } + return "-1"; + } case 0: { - return "0"; - } - case 1: { - return "1"; - } + return "0"; + } + case 1: { + return "1"; + } case 2: { - return "2"; - } + return "2"; + } case 4: { - return "4"; - } + return "4"; + } case 100: { - return "hundred"; - } + return "hundred"; + } case 10000: { - return "ten thousand"; - } + return "ten thousand"; + } case 10001: { - return "ten thousand and one"; - } + return "ten thousand and one"; + } case 2147483647: { - return "int.MaxValue"; - } + return "int.MaxValue"; + } default: { - return "something else"; - } + return "something else"; + } } } @@ -70,17 +70,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty Console.WriteLine("ShortSwitchOverString: " + text); switch (text) { case "First case": { - return "Text1"; - } + return "Text1"; + } case "Second case": { - return "Text2"; - } + return "Text2"; + } case "Third case": { - return "Text3"; - } + return "Text3"; + } default: { - return "Default"; - } + return "Default"; + } } } @@ -107,8 +107,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty case "Sixth case": { return "Text6"; } - case (string)null: { - return (string)null; + case null: { + return null; } default: { return "Default"; @@ -119,7 +119,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static string SwitchOverString2() { Console.WriteLine("SwitchOverString2:"); - switch (Environment.UserName) { + string userName = Environment.UserName; + switch (userName) { case "First case": { return "Text1"; } @@ -180,22 +181,27 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty Console.WriteLine("SwitchInLoop: " + i); while (true) { switch (i) { - case 1: + case 1: { Console.WriteLine("one"); break; - case 2: + } + case 2: { Console.WriteLine("two"); break; - case 3: + } + case 3: { Console.WriteLine("three"); continue; - case 4: + } + case 4: { Console.WriteLine("four"); return; - default: + } + default: { Console.WriteLine("default"); Console.WriteLine("more code"); return; + } } i++; } @@ -205,21 +211,26 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { Console.WriteLine("SwitchWithGoto: " + i); switch (i) { - case 1: + case 1: { Console.WriteLine("one"); goto default; - case 2: + } + case 2: { Console.WriteLine("two"); goto case 3; - case 3: + } + case 3: { Console.WriteLine("three"); break; - case 4: + } + case 4: { Console.WriteLine("four"); return; - default: + } + default: { Console.WriteLine("default"); break; + } } } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs index a72cff6db..f0bbd13ae 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs @@ -315,15 +315,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!target.Instructions[0].MatchIfInstruction(out var condition, out var bodyBranch)) return false; - if (!target.Instructions[1].MatchBranch(out Block exit)) - return false; if (!bodyBranch.MatchBranch(out Block body)) return false; if (!MatchStringEqualityComparison(condition, switchValue.Variable, out string stringValue)) { if (condition.MatchLogicNot(out condition) && MatchStringEqualityComparison(condition, switchValue.Variable, out stringValue)) { - var swap = body; + if (!target.Instructions[1].MatchBranch(out Block exit)) + return false; body = exit; - exit = swap; } else return false; } From 1bff19a7ce8a103f8b992ede689cd5f6f57e5091 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 2 Oct 2017 22:17:13 +0200 Subject: [PATCH 12/65] Remove unnecessary casts in switch tests --- .../CSharp/ExpressionBuilder.cs | 24 ++++++++++--------- .../CSharp/StatementBuilder.cs | 2 +- .../CSharp/TranslatedExpression.cs | 2 +- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 1ccf44910..9a1120768 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 bdb59f436..d5923cb3e 100644 --- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs @@ -96,7 +96,7 @@ namespace ICSharpCode.Decompiler.CSharp } else { value = CSharpPrimitiveCast.Cast(ReflectionHelper.GetTypeCode(type), i, false); } - return new CaseLabel(exprBuilder.ConvertConstantValue(new ConstantResolveResult(type, value))); + return new CaseLabel(exprBuilder.ConvertConstantValue(new ConstantResolveResult(type, value), allowImplicitConversion: true)); } protected internal override Statement VisitSwitchInstruction(SwitchInstruction inst) diff --git a/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs b/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs index 8d49ce4cd..7c01dc7f0 100644 --- a/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs @@ -296,7 +296,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))) { From 945ebc770229edf9873189ab9caa6759731018f5 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 2 Oct 2017 22:20:54 +0200 Subject: [PATCH 13/65] Add test cases for switch on int? --- .../TestCases/Pretty/Switch.cs | 142 +++- .../TestCases/Pretty/Switch.il | 711 +++++++++++----- .../TestCases/Pretty/Switch.opt.il | 435 +++++++--- .../TestCases/Pretty/Switch.opt.roslyn.il | 212 ++++- .../TestCases/Pretty/Switch.roslyn.il | 785 ++++++++++++------ 5 files changed, 1662 insertions(+), 623 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs index 858d91a79..4b31f0a2a 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs @@ -27,41 +27,119 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty Console.WriteLine("SparseIntegerSwitch: " + i); switch (i) { case -10000000: { - return "-10 mln"; - } + return "-10 mln"; + } case -100: { - return "-hundred"; - } + return "-hundred"; + } case -1: { - return "-1"; - } + return "-1"; + } case 0: { - return "0"; - } + return "0"; + } case 1: { - return "1"; - } + return "1"; + } case 2: { - return "2"; - } + return "2"; + } case 4: { - return "4"; - } + return "4"; + } case 100: { - return "hundred"; - } + return "hundred"; + } case 10000: { - return "ten thousand"; - } + return "ten thousand"; + } case 10001: { - return "ten thousand and one"; - } + return "ten thousand and one"; + } case 2147483647: { - return "int.MaxValue"; - } + return "int.MaxValue"; + } default: { - return "something else"; - } + 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 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 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"; + } } } @@ -70,17 +148,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty Console.WriteLine("ShortSwitchOverString: " + text); switch (text) { case "First case": { - return "Text1"; - } + return "Text1"; + } case "Second case": { - return "Text2"; - } + return "Text2"; + } case "Third case": { - return "Text3"; - } + return "Text3"; + } default: { - return "Default"; - } + return "Default"; + } } } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il index e04aa74b5..c64e70f03 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il @@ -10,7 +10,7 @@ .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } -.assembly wau4yvxd +.assembly iuizqapl { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx @@ -20,15 +20,15 @@ .hash algorithm 0x00008004 .ver 0:0:0:0 } -.module wau4yvxd.dll -// MVID: {102ACC94-685A-42C5-9229-AC386C0A78B1} +.module iuizqapl.dll +// MVID: {207B14E2-2177-4CF2-8D8E-2CD85A17CF5C} .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: 0x01440000 +// Image base: 0x027D0000 // =============== CLASS MEMBERS DECLARATION =================== @@ -157,6 +157,250 @@ IL_00df: 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 + 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 + 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 string ShortSwitchOverString(string text) cil managed { @@ -241,7 +485,7 @@ IL_0015: brfalse IL_00ef IL_001a: volatile. - IL_001c: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{102ACC94-685A-42C5-9229-AC386C0A78B1}'::'$$method0x6000003-1' + IL_001c: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{207B14E2-2177-4CF2-8D8E-2CD85A17CF5C}'::'$$method0x6000007-1' IL_0021: brtrue.s IL_0084 IL_0023: ldc.i4.7 @@ -282,9 +526,9 @@ 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 '{102ACC94-685A-42C5-9229-AC386C0A78B1}'::'$$method0x6000003-1' + IL_007f: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{207B14E2-2177-4CF2-8D8E-2CD85A17CF5C}'::'$$method0x6000007-1' IL_0084: volatile. - IL_0086: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{102ACC94-685A-42C5-9229-AC386C0A78B1}'::'$$method0x6000003-1' + IL_0086: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{207B14E2-2177-4CF2-8D8E-2CD85A17CF5C}'::'$$method0x6000007-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, @@ -349,168 +593,171 @@ .method public hidebysig static string SwitchOverString2() cil managed { - // Code size 366 (0x16e) + // Code size 368 (0x170) .maxstack 4 .locals init (string V_0, string V_1, - int32 V_2) + string V_2, + int32 V_3) 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_0163 - - IL_0018: volatile. - IL_001a: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{102ACC94-685A-42C5-9229-AC386C0A78B1}'::'$$method0x6000004-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, + IL_0011: stloc.0 + IL_0012: ldloc.0 + IL_0013: stloc.2 + IL_0014: ldloc.2 + IL_0015: brfalse IL_0165 + + IL_001a: volatile. + IL_001c: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{207B14E2-2177-4CF2-8D8E-2CD85A17CF5C}'::'$$method0x6000008-1' + IL_0021: brtrue IL_00ba + + IL_0026: ldc.i4.s 11 + IL_0028: newobj instance void class [mscorlib]System.Collections.Generic.Dictionary`2::.ctor(int32) + IL_002d: dup + IL_002e: ldstr "First case" + IL_0033: ldc.i4.0 + IL_0034: 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, + IL_0039: dup + IL_003a: ldstr "Second case" + IL_003f: ldc.i4.1 + IL_0040: 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, + IL_0045: dup + IL_0046: ldstr "Third case" + IL_004b: ldc.i4.2 + IL_004c: 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, + IL_0051: dup + IL_0052: ldstr "Fourth case" + IL_0057: ldc.i4.3 + IL_0058: 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, + IL_005d: dup + IL_005e: ldstr "Fifth case" + IL_0063: ldc.i4.4 + IL_0064: 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, + IL_0069: dup + IL_006a: ldstr "Sixth case" + IL_006f: ldc.i4.5 + IL_0070: 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, + IL_0075: dup + IL_0076: ldstr "Seventh case" + IL_007b: ldc.i4.6 + IL_007c: 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, + IL_0081: dup + IL_0082: ldstr "Eighth case" + IL_0087: ldc.i4.7 + IL_0088: 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, + IL_008d: dup + IL_008e: ldstr "Ninth case" + IL_0093: ldc.i4.8 + IL_0094: 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, + IL_0099: dup + IL_009a: ldstr "Tenth case" + IL_009f: ldc.i4.s 9 + IL_00a1: 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, + IL_00a6: dup + IL_00a7: ldstr "Eleventh case" + IL_00ac: ldc.i4.s 10 + IL_00ae: 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 '{102ACC94-685A-42C5-9229-AC386C0A78B1}'::'$$method0x6000004-1' - IL_00b8: volatile. - IL_00ba: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{102ACC94-685A-42C5-9229-AC386C0A78B1}'::'$$method0x6000004-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, + IL_00b3: volatile. + IL_00b5: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{207B14E2-2177-4CF2-8D8E-2CD85A17CF5C}'::'$$method0x6000008-1' + IL_00ba: volatile. + IL_00bc: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{207B14E2-2177-4CF2-8D8E-2CD85A17CF5C}'::'$$method0x6000008-1' + IL_00c1: ldloc.2 + IL_00c2: ldloca.s V_3 + IL_00c4: call instance bool class [mscorlib]System.Collections.Generic.Dictionary`2::TryGetValue(!0, !1&) - IL_00c7: brfalse IL_0163 - - IL_00cc: ldloc.2 - IL_00cd: switch ( - IL_0100, - IL_0109, - IL_0112, - IL_011b, - IL_0124, - IL_012d, - IL_0136, - IL_013f, - IL_0148, - IL_0151, - IL_015a) - IL_00fe: br.s IL_0163 - - IL_0100: nop - IL_0101: ldstr "Text1" - IL_0106: stloc.0 - IL_0107: br.s IL_016c - - IL_0109: nop - IL_010a: ldstr "Text2" - IL_010f: stloc.0 - IL_0110: br.s IL_016c - - IL_0112: nop - IL_0113: ldstr "Text3" - IL_0118: stloc.0 - IL_0119: br.s IL_016c - - IL_011b: nop - IL_011c: ldstr "Text4" - IL_0121: stloc.0 - IL_0122: br.s IL_016c - - IL_0124: nop - IL_0125: ldstr "Text5" - IL_012a: stloc.0 - IL_012b: br.s IL_016c - - IL_012d: nop - IL_012e: ldstr "Text6" - IL_0133: stloc.0 - IL_0134: br.s IL_016c - - IL_0136: nop - IL_0137: ldstr "Text7" - IL_013c: stloc.0 - IL_013d: br.s IL_016c - - IL_013f: nop - IL_0140: ldstr "Text8" - IL_0145: stloc.0 - IL_0146: br.s IL_016c - - IL_0148: nop - IL_0149: ldstr "Text9" - IL_014e: stloc.0 - IL_014f: br.s IL_016c - - IL_0151: nop - IL_0152: ldstr "Text10" - IL_0157: stloc.0 - IL_0158: br.s IL_016c - - IL_015a: nop - IL_015b: ldstr "Text11" - IL_0160: stloc.0 - IL_0161: br.s IL_016c - - IL_0163: nop - IL_0164: ldstr "Default" - IL_0169: stloc.0 - IL_016a: br.s IL_016c - - IL_016c: ldloc.0 - IL_016d: ret + IL_00c9: brfalse IL_0165 + + IL_00ce: ldloc.3 + IL_00cf: switch ( + IL_0102, + IL_010b, + IL_0114, + IL_011d, + IL_0126, + IL_012f, + IL_0138, + IL_0141, + IL_014a, + IL_0153, + IL_015c) + IL_0100: br.s IL_0165 + + IL_0102: nop + IL_0103: ldstr "Text1" + IL_0108: stloc.1 + IL_0109: br.s IL_016e + + IL_010b: nop + IL_010c: ldstr "Text2" + IL_0111: stloc.1 + IL_0112: br.s IL_016e + + IL_0114: nop + IL_0115: ldstr "Text3" + IL_011a: stloc.1 + IL_011b: br.s IL_016e + + IL_011d: nop + IL_011e: ldstr "Text4" + IL_0123: stloc.1 + IL_0124: br.s IL_016e + + IL_0126: nop + IL_0127: ldstr "Text5" + IL_012c: stloc.1 + IL_012d: br.s IL_016e + + IL_012f: nop + IL_0130: ldstr "Text6" + IL_0135: stloc.1 + IL_0136: br.s IL_016e + + IL_0138: nop + IL_0139: ldstr "Text7" + IL_013e: stloc.1 + IL_013f: br.s IL_016e + + IL_0141: nop + IL_0142: ldstr "Text8" + IL_0147: stloc.1 + IL_0148: br.s IL_016e + + IL_014a: nop + IL_014b: ldstr "Text9" + IL_0150: stloc.1 + IL_0151: br.s IL_016e + + IL_0153: nop + IL_0154: ldstr "Text10" + IL_0159: stloc.1 + IL_015a: br.s IL_016e + + IL_015c: nop + IL_015d: ldstr "Text11" + IL_0162: stloc.1 + IL_0163: br.s IL_016e + + IL_0165: nop + IL_0166: ldstr "Default" + IL_016b: stloc.1 + IL_016c: br.s IL_016e + + IL_016e: ldloc.1 + IL_016f: ret } // end of method Switch::SwitchOverString2 .method public hidebysig static string @@ -557,7 +804,7 @@ .method public hidebysig static void SwitchInLoop(int32 i) cil managed { - // Code size 141 (0x8d) + // Code size 146 (0x92) .maxstack 2 .locals init (int32 V_0, bool V_1) @@ -569,7 +816,7 @@ object) IL_0011: call void [mscorlib]System.Console::WriteLine(string) IL_0016: nop - IL_0017: br.s IL_0088 + IL_0017: br.s IL_008d IL_0019: nop IL_001a: ldarg.0 @@ -579,54 +826,59 @@ IL_001e: sub IL_001f: switch ( IL_0036, - IL_0043, - IL_0050, - IL_005d) - IL_0034: br.s IL_006a + IL_0044, + IL_0052, + IL_0060) + IL_0034: br.s IL_006e + + IL_0036: nop + IL_0037: ldstr "one" + IL_003c: call void [mscorlib]System.Console::WriteLine(string) + IL_0041: nop + IL_0042: br.s IL_0087 + + IL_0044: nop + IL_0045: ldstr "two" + IL_004a: call void [mscorlib]System.Console::WriteLine(string) + IL_004f: nop + IL_0050: br.s IL_0087 - IL_0036: ldstr "one" - IL_003b: call void [mscorlib]System.Console::WriteLine(string) - IL_0040: nop - IL_0041: br.s IL_0082 - - IL_0043: ldstr "two" - IL_0048: call void [mscorlib]System.Console::WriteLine(string) - IL_004d: nop - IL_004e: br.s IL_0082 + IL_0052: nop + IL_0053: ldstr "three" + IL_0058: call void [mscorlib]System.Console::WriteLine(string) + IL_005d: nop + IL_005e: br.s IL_008d + + IL_0060: nop + IL_0061: ldstr "four" + IL_0066: call void [mscorlib]System.Console::WriteLine(string) + IL_006b: nop + IL_006c: br.s IL_0091 + + IL_006e: nop + IL_006f: ldstr "default" + IL_0074: call void [mscorlib]System.Console::WriteLine(string) + IL_0079: nop + IL_007a: ldstr "more code" + IL_007f: call void [mscorlib]System.Console::WriteLine(string) + IL_0084: nop + IL_0085: br.s IL_0091 - IL_0050: ldstr "three" - IL_0055: call void [mscorlib]System.Console::WriteLine(string) - IL_005a: nop - IL_005b: br.s IL_0088 - - IL_005d: ldstr "four" - IL_0062: call void [mscorlib]System.Console::WriteLine(string) - IL_0067: nop - IL_0068: br.s IL_008c - - IL_006a: ldstr "default" - IL_006f: call void [mscorlib]System.Console::WriteLine(string) - IL_0074: nop - IL_0075: ldstr "more code" - IL_007a: call void [mscorlib]System.Console::WriteLine(string) - IL_007f: nop - IL_0080: br.s IL_008c - - IL_0082: ldarg.0 - IL_0083: ldc.i4.1 - IL_0084: add - IL_0085: starg.s i - IL_0087: nop + IL_0087: ldarg.0 IL_0088: ldc.i4.1 - IL_0089: stloc.1 - IL_008a: br.s IL_0019 - - IL_008c: ret + IL_0089: add + IL_008a: starg.s i + IL_008c: nop + IL_008d: ldc.i4.1 + IL_008e: stloc.1 + IL_008f: br.s IL_0019 + + IL_0091: ret } // end of method Switch::SwitchInLoop .method public hidebysig static void SwitchWithGoto(int32 i) cil managed { - // Code size 117 (0x75) + // Code size 122 (0x7a) .maxstack 2 .locals init (int32 V_0) IL_0000: nop @@ -644,48 +896,53 @@ 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_0074 - - IL_0067: ldstr "default" - IL_006c: call void [mscorlib]System.Console::WriteLine(string) - IL_0071: nop - IL_0072: br.s IL_0074 - - IL_0074: ret + IL_0041, + IL_004f, + IL_005d) + IL_0031: br.s IL_006b + + IL_0033: nop + IL_0034: ldstr "one" + IL_0039: call void [mscorlib]System.Console::WriteLine(string) + IL_003e: nop + IL_003f: br.s IL_006b + + IL_0041: nop + IL_0042: ldstr "two" + IL_0047: call void [mscorlib]System.Console::WriteLine(string) + IL_004c: nop + IL_004d: br.s IL_004f + + IL_004f: nop + IL_0050: ldstr "three" + IL_0055: call void [mscorlib]System.Console::WriteLine(string) + IL_005a: nop + IL_005b: br.s IL_0079 + + IL_005d: nop + IL_005e: ldstr "four" + IL_0063: call void [mscorlib]System.Console::WriteLine(string) + IL_0068: nop + IL_0069: br.s IL_0079 + + IL_006b: nop + IL_006c: ldstr "default" + IL_0071: call void [mscorlib]System.Console::WriteLine(string) + IL_0076: nop + IL_0077: br.s IL_0079 + + IL_0079: ret } // end of method Switch::SwitchWithGoto } // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch -.class private auto ansi '{102ACC94-685A-42C5-9229-AC386C0A78B1}' +.class private auto ansi '{207B14E2-2177-4CF2-8D8E-2CD85A17CF5C}' 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 '$$method0x6000003-1' - .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000004-1' -} // end of class '{102ACC94-685A-42C5-9229-AC386C0A78B1}' + .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000007-1' + .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000008-1' +} // end of class '{207B14E2-2177-4CF2-8D8E-2CD85A17CF5C}' // ============================================================= diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il index 4c6e7946e..0765e0099 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il @@ -10,7 +10,7 @@ .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } -.assembly gz2l4xfo +.assembly utwwumxi { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx @@ -20,15 +20,15 @@ .hash algorithm 0x00008004 .ver 0:0:0:0 } -.module gz2l4xfo.dll -// MVID: {FFA858C4-FC28-4EB1-BDB5-C80B304AD168} +.module utwwumxi.dll +// MVID: {30E98C35-5F99-4742-941F-78E7F27D8BD5} .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: 0x008F0000 +// Image base: 0x02C70000 // =============== CLASS MEMBERS DECLARATION =================== @@ -127,6 +127,212 @@ 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 + 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 + 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 string ShortSwitchOverString(string text) cil managed { @@ -194,7 +400,7 @@ IL_0013: brfalse IL_00db IL_0018: volatile. - IL_001a: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{FFA858C4-FC28-4EB1-BDB5-C80B304AD168}'::'$$method0x6000003-1' + IL_001a: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{30E98C35-5F99-4742-941F-78E7F27D8BD5}'::'$$method0x6000007-1' IL_001f: brtrue.s IL_0082 IL_0021: ldc.i4.7 @@ -235,9 +441,9 @@ 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 '{FFA858C4-FC28-4EB1-BDB5-C80B304AD168}'::'$$method0x6000003-1' + IL_007d: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{30E98C35-5F99-4742-941F-78E7F27D8BD5}'::'$$method0x6000007-1' IL_0082: volatile. - IL_0084: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{FFA858C4-FC28-4EB1-BDB5-C80B304AD168}'::'$$method0x6000003-1' + IL_0084: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{30E98C35-5F99-4742-941F-78E7F27D8BD5}'::'$$method0x6000007-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, @@ -283,138 +489,141 @@ .method public hidebysig static string SwitchOverString2() cil managed { - // Code size 323 (0x143) + // Code size 325 (0x145) .maxstack 4 .locals init (string V_0, - int32 V_1) + string V_1, + int32 V_2) 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 '{FFA858C4-FC28-4EB1-BDB5-C80B304AD168}'::'$$method0x6000004-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, + IL_000f: stloc.0 + IL_0010: ldloc.0 + IL_0011: dup + IL_0012: stloc.1 + IL_0013: brfalse IL_013f + + IL_0018: volatile. + IL_001a: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{30E98C35-5F99-4742-941F-78E7F27D8BD5}'::'$$method0x6000008-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_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, + 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_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, + 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_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, + 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_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, + 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_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, + 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_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, + 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_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, + 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_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, + 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_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, + 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_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, + 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_00af: volatile. - IL_00b1: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{FFA858C4-FC28-4EB1-BDB5-C80B304AD168}'::'$$method0x6000004-1' - IL_00b6: volatile. - IL_00b8: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{FFA858C4-FC28-4EB1-BDB5-C80B304AD168}'::'$$method0x6000004-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, + IL_00b1: volatile. + IL_00b3: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{30E98C35-5F99-4742-941F-78E7F27D8BD5}'::'$$method0x6000008-1' + IL_00b8: volatile. + IL_00ba: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{30E98C35-5F99-4742-941F-78E7F27D8BD5}'::'$$method0x6000008-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_00c5: brfalse.s IL_013d + IL_00c7: brfalse.s IL_013f - 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_00c9: ldloc.2 + IL_00ca: switch ( + IL_00fd, + IL_0103, + IL_0109, + IL_010f, + IL_0115, + IL_011b, + IL_0121, + IL_0127, + IL_012d, + IL_0133, + IL_0139) + IL_00fb: br.s IL_013f - IL_00fb: ldstr "Text1" - IL_0100: ret + IL_00fd: ldstr "Text1" + IL_0102: ret - IL_0101: ldstr "Text2" - IL_0106: ret + IL_0103: ldstr "Text2" + IL_0108: ret - IL_0107: ldstr "Text3" - IL_010c: ret + IL_0109: ldstr "Text3" + IL_010e: ret - IL_010d: ldstr "Text4" - IL_0112: ret + IL_010f: ldstr "Text4" + IL_0114: ret - IL_0113: ldstr "Text5" - IL_0118: ret + IL_0115: ldstr "Text5" + IL_011a: ret - IL_0119: ldstr "Text6" - IL_011e: ret + IL_011b: ldstr "Text6" + IL_0120: ret - IL_011f: ldstr "Text7" - IL_0124: ret + IL_0121: ldstr "Text7" + IL_0126: ret - IL_0125: ldstr "Text8" - IL_012a: ret + IL_0127: ldstr "Text8" + IL_012c: ret - IL_012b: ldstr "Text9" - IL_0130: ret + IL_012d: ldstr "Text9" + IL_0132: ret - IL_0131: ldstr "Text10" - IL_0136: ret + IL_0133: ldstr "Text10" + IL_0138: ret - IL_0137: ldstr "Text11" - IL_013c: ret + IL_0139: ldstr "Text11" + IL_013e: ret - IL_013d: ldstr "Default" - IL_0142: ret + IL_013f: ldstr "Default" + IL_0144: ret } // end of method Switch::SwitchOverString2 .method public hidebysig static string @@ -543,13 +752,13 @@ } // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch -.class private auto ansi '{FFA858C4-FC28-4EB1-BDB5-C80B304AD168}' +.class private auto ansi '{30E98C35-5F99-4742-941F-78E7F27D8BD5}' 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 '$$method0x6000003-1' - .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000004-1' -} // end of class '{FFA858C4-FC28-4EB1-BDB5-C80B304AD168}' + .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000007-1' + .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000008-1' +} // end of class '{30E98C35-5F99-4742-941F-78E7F27D8BD5}' // ============================================================= diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il index a86c1b3cc..ef1606d38 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il @@ -25,14 +25,14 @@ .ver 0:0:0:0 } .module Switch.dll -// MVID: {3690F18D-C570-405A-8B33-B6E0A6696EFD} +// MVID: {E38DFC98-1601-4EC8-896E-FC0EA87D4437} .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: 0x015B0000 +// Image base: 0x02590000 // =============== CLASS MEMBERS DECLARATION =================== @@ -136,6 +136,214 @@ 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 + 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 + 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 string ShortSwitchOverString(string text) cil managed { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il index 0132ebf10..f94d258ba 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il @@ -25,14 +25,14 @@ .ver 0:0:0:0 } .module Switch.dll -// MVID: {08E636DF-9FF0-4BF4-9F8F-69859C40E218} +// MVID: {7C3CB7C3-DBBF-4EB8-ACC1-E7AFA899FD3B} .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: 0x02E70000 +// Image base: 0x019E0000 // =============== CLASS MEMBERS DECLARATION =================== @@ -177,6 +177,280 @@ IL_00ed: 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 + 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 + 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 string ShortSwitchOverString(string text) cil managed { @@ -424,11 +698,12 @@ .method public hidebysig static string SwitchOverString2() cil managed { - // Code size 518 (0x206) + // Code size 520 (0x208) .maxstack 2 .locals init (string V_0, - uint32 V_1, - string V_2) + string V_1, + uint32 V_2, + string V_3) IL_0000: nop IL_0001: ldstr "SwitchOverString2:" IL_0006: call void [mscorlib]System.Console::WriteLine(string) @@ -436,236 +711,238 @@ 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_0013: stloc.1 + IL_0014: ldloc.1 + IL_0015: call uint32 ''::ComputeStringHash(string) + IL_001a: stloc.2 + IL_001b: ldloc.2 + IL_001c: ldc.i4 0x4c7c71f6 + IL_0021: bgt.un.s IL_0072 - IL_0021: ldloc.1 - IL_0022: ldc.i4 0xc9a8f4f - IL_0027: bgt.un.s IL_0046 + IL_0023: ldloc.2 + IL_0024: ldc.i4 0xc9a8f4f + IL_0029: bgt.un.s IL_0048 - IL_0029: ldloc.1 - IL_002a: ldc.i4 0x8861b86 - IL_002f: beq IL_011a + IL_002b: ldloc.2 + IL_002c: ldc.i4 0x8861b86 + IL_0031: beq IL_011c - IL_0034: br.s IL_0036 + IL_0036: br.s IL_0038 - IL_0036: ldloc.1 - IL_0037: ldc.i4 0xc9a8f4f - IL_003c: beq IL_00c6 + IL_0038: ldloc.2 + IL_0039: ldc.i4 0xc9a8f4f + IL_003e: beq IL_00c8 - IL_0041: br IL_01fb + IL_0043: br IL_01fd - IL_0046: ldloc.1 - IL_0047: ldc.i4 0xf3d44a6 - IL_004c: beq IL_00f0 + IL_0048: ldloc.2 + IL_0049: ldc.i4 0xf3d44a6 + IL_004e: beq IL_00f2 - IL_0051: br.s IL_0053 + IL_0053: br.s IL_0055 - IL_0053: ldloc.1 - IL_0054: ldc.i4 0x20289804 - IL_0059: beq IL_0156 + IL_0055: ldloc.2 + IL_0056: ldc.i4 0x20289804 + IL_005b: beq IL_0158 - IL_005e: br.s IL_0060 + IL_0060: br.s IL_0062 - IL_0060: ldloc.1 - IL_0061: ldc.i4 0x4c7c71f6 - IL_0066: beq IL_0168 + IL_0062: ldloc.2 + IL_0063: ldc.i4 0x4c7c71f6 + IL_0068: beq IL_016a - IL_006b: br IL_01fb + IL_006d: br IL_01fd - IL_0070: ldloc.1 - IL_0071: ldc.i4 0xa151b28a - IL_0076: bgt.un.s IL_00a2 + IL_0072: ldloc.2 + IL_0073: ldc.i4 0xa151b28a + IL_0078: bgt.un.s IL_00a4 - IL_0078: ldloc.1 - IL_0079: ldc.i4 0x4d0cea48 - IL_007e: beq IL_0189 + IL_007a: ldloc.2 + IL_007b: ldc.i4 0x4d0cea48 + IL_0080: beq IL_018b - IL_0083: br.s IL_0085 + IL_0085: br.s IL_0087 - IL_0085: ldloc.1 - IL_0086: ldc.i4 0x51650fb9 - IL_008b: beq IL_012f + IL_0087: ldloc.2 + IL_0088: ldc.i4 0x51650fb9 + IL_008d: beq IL_0131 - IL_0090: br.s IL_0092 + IL_0092: br.s IL_0094 - IL_0092: ldloc.1 - IL_0093: ldc.i4 0xa151b28a - IL_0098: beq IL_0144 + IL_0094: ldloc.2 + IL_0095: ldc.i4 0xa151b28a + IL_009a: beq IL_0146 - IL_009d: br IL_01fb + IL_009f: br IL_01fd - IL_00a2: ldloc.1 - IL_00a3: ldc.i4 0xea3d096b - IL_00a8: beq.s IL_00db + IL_00a4: ldloc.2 + IL_00a5: ldc.i4 0xea3d096b + IL_00aa: beq.s IL_00dd - IL_00aa: br.s IL_00ac + IL_00ac: br.s IL_00ae - IL_00ac: ldloc.1 - IL_00ad: ldc.i4 0xed5134d4 - IL_00b2: beq IL_017a + IL_00ae: ldloc.2 + IL_00af: ldc.i4 0xed5134d4 + IL_00b4: beq IL_017c - IL_00b7: br.s IL_00b9 + IL_00b9: br.s IL_00bb - IL_00b9: ldloc.1 - IL_00ba: ldc.i4 0xf701cc7f - IL_00bf: beq.s IL_0105 + IL_00bb: ldloc.2 + IL_00bc: ldc.i4 0xf701cc7f + IL_00c1: beq.s IL_0107 - IL_00c1: br IL_01fb + IL_00c3: br IL_01fd - IL_00c6: ldloc.0 - IL_00c7: ldstr "First case" - IL_00cc: call bool [mscorlib]System.String::op_Equality(string, + IL_00c8: ldloc.1 + IL_00c9: ldstr "First case" + IL_00ce: call bool [mscorlib]System.String::op_Equality(string, string) - IL_00d1: brtrue IL_0198 + IL_00d3: brtrue IL_019a - IL_00d6: br IL_01fb + IL_00d8: br IL_01fd - IL_00db: ldloc.0 - IL_00dc: ldstr "Second case" - IL_00e1: call bool [mscorlib]System.String::op_Equality(string, + IL_00dd: ldloc.1 + IL_00de: ldstr "Second case" + IL_00e3: call bool [mscorlib]System.String::op_Equality(string, string) - IL_00e6: brtrue IL_01a1 + IL_00e8: brtrue IL_01a3 - IL_00eb: br IL_01fb + IL_00ed: br IL_01fd - IL_00f0: ldloc.0 - IL_00f1: ldstr "Third case" - IL_00f6: call bool [mscorlib]System.String::op_Equality(string, + IL_00f2: ldloc.1 + IL_00f3: ldstr "Third case" + IL_00f8: call bool [mscorlib]System.String::op_Equality(string, string) - IL_00fb: brtrue IL_01aa + IL_00fd: brtrue IL_01ac - IL_0100: br IL_01fb + IL_0102: br IL_01fd - IL_0105: ldloc.0 - IL_0106: ldstr "Fourth case" - IL_010b: call bool [mscorlib]System.String::op_Equality(string, + IL_0107: ldloc.1 + IL_0108: ldstr "Fourth case" + IL_010d: call bool [mscorlib]System.String::op_Equality(string, string) - IL_0110: brtrue IL_01b3 + IL_0112: brtrue IL_01b5 - IL_0115: br IL_01fb + IL_0117: br IL_01fd - IL_011a: ldloc.0 - IL_011b: ldstr "Fifth case" - IL_0120: call bool [mscorlib]System.String::op_Equality(string, + IL_011c: ldloc.1 + IL_011d: ldstr "Fifth case" + IL_0122: call bool [mscorlib]System.String::op_Equality(string, string) - IL_0125: brtrue IL_01bc + IL_0127: brtrue IL_01be - IL_012a: br IL_01fb + IL_012c: br IL_01fd - IL_012f: ldloc.0 - IL_0130: ldstr "Sixth case" - IL_0135: call bool [mscorlib]System.String::op_Equality(string, + IL_0131: ldloc.1 + IL_0132: ldstr "Sixth case" + IL_0137: call bool [mscorlib]System.String::op_Equality(string, string) - IL_013a: brtrue IL_01c5 + IL_013c: brtrue IL_01c7 - IL_013f: br IL_01fb + IL_0141: br IL_01fd - IL_0144: ldloc.0 - IL_0145: ldstr "Seventh case" - IL_014a: call bool [mscorlib]System.String::op_Equality(string, + IL_0146: ldloc.1 + IL_0147: ldstr "Seventh case" + IL_014c: call bool [mscorlib]System.String::op_Equality(string, string) - IL_014f: brtrue.s IL_01ce + IL_0151: brtrue.s IL_01d0 - IL_0151: br IL_01fb + IL_0153: br IL_01fd - IL_0156: ldloc.0 - IL_0157: ldstr "Eighth case" - IL_015c: call bool [mscorlib]System.String::op_Equality(string, + IL_0158: ldloc.1 + IL_0159: ldstr "Eighth case" + IL_015e: call bool [mscorlib]System.String::op_Equality(string, string) - IL_0161: brtrue.s IL_01d7 + IL_0163: brtrue.s IL_01d9 - IL_0163: br IL_01fb + IL_0165: br IL_01fd - IL_0168: ldloc.0 - IL_0169: ldstr "Ninth case" - IL_016e: call bool [mscorlib]System.String::op_Equality(string, + IL_016a: ldloc.1 + IL_016b: ldstr "Ninth case" + IL_0170: call bool [mscorlib]System.String::op_Equality(string, string) - IL_0173: brtrue.s IL_01e0 + IL_0175: brtrue.s IL_01e2 - IL_0175: br IL_01fb + IL_0177: br IL_01fd - IL_017a: ldloc.0 - IL_017b: ldstr "Tenth case" - IL_0180: call bool [mscorlib]System.String::op_Equality(string, + IL_017c: ldloc.1 + IL_017d: ldstr "Tenth case" + IL_0182: call bool [mscorlib]System.String::op_Equality(string, string) - IL_0185: brtrue.s IL_01e9 + IL_0187: brtrue.s IL_01eb - IL_0187: br.s IL_01fb + IL_0189: br.s IL_01fd - IL_0189: ldloc.0 - IL_018a: ldstr "Eleventh case" - IL_018f: call bool [mscorlib]System.String::op_Equality(string, + IL_018b: ldloc.1 + IL_018c: ldstr "Eleventh case" + IL_0191: call bool [mscorlib]System.String::op_Equality(string, string) - IL_0194: brtrue.s IL_01f2 - - IL_0196: br.s IL_01fb - - IL_0198: nop - IL_0199: ldstr "Text1" - IL_019e: stloc.2 - IL_019f: br.s IL_0204 - - IL_01a1: nop - IL_01a2: ldstr "Text2" - IL_01a7: stloc.2 - IL_01a8: br.s IL_0204 - - IL_01aa: nop - IL_01ab: ldstr "Text3" - IL_01b0: stloc.2 - IL_01b1: br.s IL_0204 - - IL_01b3: nop - IL_01b4: ldstr "Text4" - IL_01b9: stloc.2 - IL_01ba: br.s IL_0204 - - IL_01bc: nop - IL_01bd: ldstr "Text5" - IL_01c2: stloc.2 - IL_01c3: br.s IL_0204 - - IL_01c5: nop - IL_01c6: ldstr "Text6" - IL_01cb: stloc.2 - IL_01cc: br.s IL_0204 - - IL_01ce: nop - IL_01cf: ldstr "Text7" - IL_01d4: stloc.2 - IL_01d5: br.s IL_0204 - - IL_01d7: nop - IL_01d8: ldstr "Text8" - IL_01dd: stloc.2 - IL_01de: br.s IL_0204 - - IL_01e0: nop - IL_01e1: ldstr "Text9" - IL_01e6: stloc.2 - IL_01e7: br.s IL_0204 - - IL_01e9: nop - IL_01ea: ldstr "Text10" - IL_01ef: stloc.2 - IL_01f0: br.s IL_0204 - - IL_01f2: nop - IL_01f3: ldstr "Text11" - IL_01f8: stloc.2 - IL_01f9: br.s IL_0204 - - IL_01fb: nop - IL_01fc: ldstr "Default" - IL_0201: stloc.2 - IL_0202: br.s IL_0204 - - IL_0204: ldloc.2 - IL_0205: ret + IL_0196: brtrue.s IL_01f4 + + IL_0198: br.s IL_01fd + + IL_019a: nop + IL_019b: ldstr "Text1" + IL_01a0: stloc.3 + IL_01a1: br.s IL_0206 + + IL_01a3: nop + IL_01a4: ldstr "Text2" + IL_01a9: stloc.3 + IL_01aa: br.s IL_0206 + + IL_01ac: nop + IL_01ad: ldstr "Text3" + IL_01b2: stloc.3 + IL_01b3: br.s IL_0206 + + IL_01b5: nop + IL_01b6: ldstr "Text4" + IL_01bb: stloc.3 + IL_01bc: br.s IL_0206 + + IL_01be: nop + IL_01bf: ldstr "Text5" + IL_01c4: stloc.3 + IL_01c5: br.s IL_0206 + + IL_01c7: nop + IL_01c8: ldstr "Text6" + IL_01cd: stloc.3 + IL_01ce: br.s IL_0206 + + IL_01d0: nop + IL_01d1: ldstr "Text7" + IL_01d6: stloc.3 + IL_01d7: br.s IL_0206 + + IL_01d9: nop + IL_01da: ldstr "Text8" + IL_01df: stloc.3 + IL_01e0: br.s IL_0206 + + IL_01e2: nop + IL_01e3: ldstr "Text9" + IL_01e8: stloc.3 + IL_01e9: br.s IL_0206 + + IL_01eb: nop + IL_01ec: ldstr "Text10" + IL_01f1: stloc.3 + IL_01f2: br.s IL_0206 + + IL_01f4: nop + IL_01f5: ldstr "Text11" + IL_01fa: stloc.3 + IL_01fb: br.s IL_0206 + + IL_01fd: nop + IL_01fe: ldstr "Default" + IL_0203: stloc.3 + IL_0204: br.s IL_0206 + + IL_0206: ldloc.3 + IL_0207: ret } // end of method Switch::SwitchOverString2 .method public hidebysig static string @@ -717,7 +994,7 @@ .method public hidebysig static void SwitchInLoop(int32 i) cil managed { - // Code size 141 (0x8d) + // Code size 146 (0x92) .maxstack 2 .locals init (int32 V_0, bool V_1) @@ -729,7 +1006,7 @@ object) IL_0011: call void [mscorlib]System.Console::WriteLine(string) IL_0016: nop - IL_0017: br.s IL_0088 + IL_0017: br.s IL_008d IL_0019: nop IL_001a: ldarg.0 @@ -739,54 +1016,59 @@ IL_001e: sub IL_001f: switch ( IL_0036, - IL_0043, - IL_0050, - IL_005d) - IL_0034: br.s IL_006a - - IL_0036: ldstr "one" - IL_003b: call void [mscorlib]System.Console::WriteLine(string) - IL_0040: nop - IL_0041: br.s IL_0082 - - IL_0043: ldstr "two" - IL_0048: call void [mscorlib]System.Console::WriteLine(string) - IL_004d: nop - IL_004e: br.s IL_0082 - - IL_0050: ldstr "three" - IL_0055: call void [mscorlib]System.Console::WriteLine(string) - IL_005a: nop - IL_005b: br.s IL_0088 - - IL_005d: ldstr "four" - IL_0062: call void [mscorlib]System.Console::WriteLine(string) - IL_0067: nop - IL_0068: br.s IL_008c - - IL_006a: ldstr "default" - IL_006f: call void [mscorlib]System.Console::WriteLine(string) - IL_0074: nop - IL_0075: ldstr "more code" - IL_007a: call void [mscorlib]System.Console::WriteLine(string) - IL_007f: nop - IL_0080: br.s IL_008c - - IL_0082: ldarg.0 - IL_0083: ldc.i4.1 - IL_0084: add - IL_0085: starg.s i - IL_0087: nop + IL_0044, + IL_0052, + IL_0060) + IL_0034: br.s IL_006e + + IL_0036: nop + IL_0037: ldstr "one" + IL_003c: call void [mscorlib]System.Console::WriteLine(string) + IL_0041: nop + IL_0042: br.s IL_0087 + + IL_0044: nop + IL_0045: ldstr "two" + IL_004a: call void [mscorlib]System.Console::WriteLine(string) + IL_004f: nop + IL_0050: br.s IL_0087 + + IL_0052: nop + IL_0053: ldstr "three" + IL_0058: call void [mscorlib]System.Console::WriteLine(string) + IL_005d: nop + IL_005e: br.s IL_008d + + IL_0060: nop + IL_0061: ldstr "four" + IL_0066: call void [mscorlib]System.Console::WriteLine(string) + IL_006b: nop + IL_006c: br.s IL_0091 + + IL_006e: nop + IL_006f: ldstr "default" + IL_0074: call void [mscorlib]System.Console::WriteLine(string) + IL_0079: nop + IL_007a: ldstr "more code" + IL_007f: call void [mscorlib]System.Console::WriteLine(string) + IL_0084: nop + IL_0085: br.s IL_0091 + + IL_0087: ldarg.0 IL_0088: ldc.i4.1 - IL_0089: stloc.1 - IL_008a: br.s IL_0019 - - IL_008c: ret + IL_0089: add + IL_008a: starg.s i + IL_008c: nop + IL_008d: ldc.i4.1 + IL_008e: stloc.1 + IL_008f: br.s IL_0019 + + IL_0091: ret } // end of method Switch::SwitchInLoop .method public hidebysig static void SwitchWithGoto(int32 i) cil managed { - // Code size 117 (0x75) + // Code size 122 (0x7a) .maxstack 2 .locals init (int32 V_0) IL_0000: nop @@ -804,37 +1086,42 @@ IL_001b: sub IL_001c: switch ( IL_0033, - IL_0040, - IL_004d, - IL_005a) - IL_0031: br.s IL_0067 + IL_0041, + IL_004f, + IL_005d) + IL_0031: br.s IL_006b - IL_0033: ldstr "one" - IL_0038: call void [mscorlib]System.Console::WriteLine(string) - IL_003d: nop - IL_003e: br.s IL_0067 + IL_0033: nop + IL_0034: ldstr "one" + IL_0039: call void [mscorlib]System.Console::WriteLine(string) + IL_003e: nop + IL_003f: br.s IL_006b - IL_0040: ldstr "two" - IL_0045: call void [mscorlib]System.Console::WriteLine(string) - IL_004a: nop - IL_004b: br.s IL_004d + IL_0041: nop + IL_0042: ldstr "two" + IL_0047: call void [mscorlib]System.Console::WriteLine(string) + IL_004c: nop + IL_004d: br.s IL_004f - IL_004d: ldstr "three" - IL_0052: call void [mscorlib]System.Console::WriteLine(string) - IL_0057: nop - IL_0058: br.s IL_0074 + IL_004f: nop + IL_0050: ldstr "three" + IL_0055: call void [mscorlib]System.Console::WriteLine(string) + IL_005a: nop + IL_005b: br.s IL_0079 - IL_005a: ldstr "four" - IL_005f: call void [mscorlib]System.Console::WriteLine(string) - IL_0064: nop - IL_0065: br.s IL_0074 + IL_005d: nop + IL_005e: ldstr "four" + IL_0063: call void [mscorlib]System.Console::WriteLine(string) + IL_0068: nop + IL_0069: br.s IL_0079 - IL_0067: ldstr "default" - IL_006c: call void [mscorlib]System.Console::WriteLine(string) - IL_0071: nop - IL_0072: br.s IL_0074 + IL_006b: nop + IL_006c: ldstr "default" + IL_0071: call void [mscorlib]System.Console::WriteLine(string) + IL_0076: nop + IL_0077: br.s IL_0079 - IL_0074: ret + IL_0079: ret } // end of method Switch::SwitchWithGoto } // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch From ef4dd6431eca770dbfc5b0fbbd72ae2722c49cd9 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Wed, 4 Oct 2017 22:51:48 +0200 Subject: [PATCH 14/65] Add documentation to InlineReturnTransform and SwitchOnStringTransform --- .../IL/Transforms/InlineReturnTransform.cs | 88 +++++++++++-------- .../IL/Transforms/SwitchOnStringTransform.cs | 43 ++++++++- 2 files changed, 89 insertions(+), 42 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/InlineReturnTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/InlineReturnTransform.cs index ae5138358..7245316c4 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/InlineReturnTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/InlineReturnTransform.cs @@ -23,61 +23,73 @@ using System.Linq; namespace ICSharpCode.Decompiler.IL.Transforms { /// - /// This transform duplicates return blocks if they return a local variable that was assigned right before thie return. + /// 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)>(); - var possibleReturnVars = new Queue<(ILVariable, Block)>(); - var tempList = 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 b && b.Instructions.Count == 1)) + 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; - possibleReturnVars.Enqueue((returnVar, b)); + // If all instructions can be modified, add item to the global list. + if (CanModifyInstructions(returnVar, leaveBlock, out var list)); + instructionsToModify.AddRange(list); } - while (possibleReturnVars.Count > 0) { - var (returnVar, leaveBlock) = possibleReturnVars.Dequeue(); - bool transform = true; - foreach (StLoc store in returnVar.StoreInstructions.OfType()) { - if (!(store.Parent is Block storeBlock)) { - transform = false; - break; - } - if (store.ChildIndex + 2 != storeBlock.Instructions.Count) { - transform = false; - break; - } - if (!(storeBlock.Instructions[store.ChildIndex + 1] is Branch br)) { - transform = false; - break; - } - if (br.TargetBlock != leaveBlock) { - transform = false; - break; - } - var targetBlockContainer = BlockContainer.FindClosestContainer(store); - if (targetBlockContainer == null) { - transform = false; - break; - } - tempList.Add((targetBlockContainer, leaveBlock, br)); + 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(); } - if (transform) - instructionsToModify.AddRange(tempList); - tempList.Clear(); + container.Blocks.Add(block); + // adjust the target of the branch to the newly created block. + br.TargetBlock = block; } + } - foreach (var (container, block, br) in instructionsToModify) { - var newBlock = (Block)block.Clone(); - container.Blocks.Add(newBlock); - br.TargetBlock = newBlock; + /// + /// 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/SwitchOnStringTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs index f0bbd13ae..70f0903f0 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs @@ -25,6 +25,9 @@ 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) @@ -78,6 +81,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms inst = null; blockAfterSwitch = null; // 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)) @@ -85,32 +91,43 @@ namespace ICSharpCode.Decompiler.IL.Transforms bool isLegacy; Block defaultBlock; List<(string, Block)> values = new List<(string, Block)>(); + // match null check: this is used by the old C# compiler. if (condition.MatchCompEquals(out var left, out var right) && right.MatchLdNull() && left.MatchLdLoc(out var switchValueVar)) { isLegacy = true; defaultBlock = firstBlock; + // Roslyn: match call to operator ==(string, string) } else if (MatchStringEqualityComparison(condition, out switchValueVar, out string value)) { isLegacy = false; defaultBlock = null; values.Add((value, firstBlock)); } else return false; + // switchValueVar must be assigned only once. if (!switchValueVar.IsSingleDefinition) return false; + // 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, ref switchValueVar, out string value, out Block block)) != null) { values.Add((value, block)); currentCaseBlock = nextCaseBlock; } + // the last instruction of the last case/default block must be either + // a branch to the a block after the switch statement or a leave instruction. var container = BlockContainer.FindClosestContainer(firstBlock); if (!ExtractLastJumpFromBlock(currentCaseBlock, out var exitBlock) && !ExtractLastLeaveFromBlock(currentCaseBlock, container)) return false; + // We didn't find any cases, exit if (values.Count == 0) return false; + // All case blocks must either leave the current block container or branch to the same block after the switch statement. if (!(values.All(b => ExtractLastJumpFromBlock(b.Item2, out var nextExit) && IsExitBlock(nextExit, container)) || (exitBlock == null && values.All(b => ExtractLastLeaveFromBlock(b.Item2, container))))) return false; + // if the block after the switch has the correct number of branches, generate the switch statement + // and return it and the block. if (currentCaseBlock.IncomingEdgeCount == (isLegacy ? 2 : 1)) { var sections = new List(values.SelectWithIndex((index, b) => new SwitchSection { Labels = new LongSet(index), Body = new Branch(b.Item2) })); var stringToInt = new StringToInt(new LdLoc(switchValueVar), values.SelectArray(item => item.Item1)); @@ -146,6 +163,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms return b == container; } + /// + /// Each case consists of two blocks: + /// 1. block: + /// if (call op_Equality(ldloc switchVariable, ldstr value)) br caseBlock + /// br nextBlock + /// 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, ref ILVariable switchVariable, out string value, out Block caseBlock) { value = null; @@ -167,10 +195,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (!currentBlock.Instructions[1].MatchBranch(out nextBlock)) return null; } + // Sometimes the switch pattern uses one variable at the beginning for null checks + // and another variable for the if-else-if-else-pattern. + // both variables must be only assigned once and be of the type: System.String. if (!MatchStringEqualityComparison(condition, out var newSwitchVariable, out value)) return null; if (!newSwitchVariable.IsSingleDefinition) return null; + // if the used variable differs and both variables are not related, return null: if (switchVariable != newSwitchVariable && !(IsInitializedBy(switchVariable, newSwitchVariable) || IsInitializedBy(newSwitchVariable, switchVariable))) return null; if (!newSwitchVariable.Type.IsKnownType(KnownTypeCode.String)) @@ -179,14 +211,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms return nextBlock; } - bool IsInitializedBy(ILVariable switchVariable, ILVariable newSwitchVariable) + /// + /// Returns true if is only assigned once and the initialization is done by copying . + /// + bool IsInitializedBy(ILVariable left, ILVariable right) { - if (!switchVariable.IsSingleDefinition) + if (!left.IsSingleDefinition) return false; - var storeInst = switchVariable.StoreInstructions.OfType().SingleOrDefault(); + var storeInst = left.StoreInstructions.OfType().SingleOrDefault(); if (storeInst == null) return false; - return storeInst.Value.MatchLdLoc(newSwitchVariable); + return storeInst.Value.MatchLdLoc(right); } bool MatchLegacySwitchOnString(InstructionCollection instructions, int i, out SwitchInstruction inst, out Block blockAfterSwitch) From fd775ec0823466adbe7375363f8437cc298ea290 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 5 Oct 2017 08:11:23 +0200 Subject: [PATCH 15/65] Fix threading bug in LoadedAssembly. --- .../DotNetCore/DotNetCorePathFinderExtensions.cs | 1 - ILSpy/LoadedAssembly.cs | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) 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/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; From 0fa58c3d53493f8fece34629f299badca1ac5379 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 5 Oct 2017 14:33:38 +0200 Subject: [PATCH 16/65] Fix #567: switch statement not properly decompile --- .../TestCases/Pretty/Switch.cs | 256 +++++--- .../TestCases/Pretty/Switch.il | 563 ++++++++---------- .../TestCases/Pretty/Switch.opt.il | 474 +++++++-------- .../TestCases/Pretty/Switch.opt.roslyn.il | 419 ++++++------- .../TestCases/Pretty/Switch.roslyn.il | 533 +++++++---------- .../IL/Transforms/SwitchOnStringTransform.cs | 18 +- 6 files changed, 1018 insertions(+), 1245 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs index 4b31f0a2a..e5f42c278 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs @@ -17,11 +17,28 @@ // 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); @@ -65,83 +82,83 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } - 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 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 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 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 SwitchOverNullableIntNoNullCase(int? i) - { - switch (i) { - case 0: { - return "zero"; - } - case 5: { - return "five"; - } - case 10: { - return "ten"; - } - default: { - return "other"; - } - } - } + //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 string SwitchOverNullableIntNoNullCaseShifted(int? i) + //{ + // switch (i + 5) { + // case 0: { + // return "zero"; + // } + // case 5: { + // return "five"; + // } + // case 10: { + // return "ten"; + // } + // default: { + // return "other"; + // } + // } + //} public static string ShortSwitchOverString(string text) { @@ -285,29 +302,76 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } - public static void SwitchWithGoto(int 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; + // } + // } + //} + + private static SetProperty[] GetProperties() { - 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; + 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]; + string name = setProperty.Property.Name; + switch (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; + } } } } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il index c64e70f03..eed5597a0 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il @@ -10,25 +10,25 @@ .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } -.assembly iuizqapl +.assembly gn0oqkcb { - .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. + .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 iuizqapl.dll -// MVID: {207B14E2-2177-4CF2-8D8E-2CD85A17CF5C} +.module gn0oqkcb.dll +// MVID: {D3E1C722-15E3-49C8-B86B-96413DA7BEEE} .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: 0x027D0000 +// Image base: 0x00C80000 // =============== CLASS MEMBERS DECLARATION =================== @@ -36,6 +36,63 @@ .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 { @@ -157,250 +214,6 @@ IL_00df: 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 - 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 - 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 string ShortSwitchOverString(string text) cil managed { @@ -485,7 +298,7 @@ IL_0015: brfalse IL_00ef IL_001a: volatile. - IL_001c: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{207B14E2-2177-4CF2-8D8E-2CD85A17CF5C}'::'$$method0x6000007-1' + IL_001c: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{D3E1C722-15E3-49C8-B86B-96413DA7BEEE}'::'$$method0x6000003-1' IL_0021: brtrue.s IL_0084 IL_0023: ldc.i4.7 @@ -526,9 +339,9 @@ 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 '{207B14E2-2177-4CF2-8D8E-2CD85A17CF5C}'::'$$method0x6000007-1' + IL_007f: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{D3E1C722-15E3-49C8-B86B-96413DA7BEEE}'::'$$method0x6000003-1' IL_0084: volatile. - IL_0086: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{207B14E2-2177-4CF2-8D8E-2CD85A17CF5C}'::'$$method0x6000007-1' + IL_0086: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{D3E1C722-15E3-49C8-B86B-96413DA7BEEE}'::'$$method0x6000003-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, @@ -611,7 +424,7 @@ IL_0015: brfalse IL_0165 IL_001a: volatile. - IL_001c: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{207B14E2-2177-4CF2-8D8E-2CD85A17CF5C}'::'$$method0x6000008-1' + IL_001c: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{D3E1C722-15E3-49C8-B86B-96413DA7BEEE}'::'$$method0x6000004-1' IL_0021: brtrue IL_00ba IL_0026: ldc.i4.s 11 @@ -672,9 +485,9 @@ IL_00ae: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, !1) IL_00b3: volatile. - IL_00b5: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{207B14E2-2177-4CF2-8D8E-2CD85A17CF5C}'::'$$method0x6000008-1' + IL_00b5: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{D3E1C722-15E3-49C8-B86B-96413DA7BEEE}'::'$$method0x6000004-1' IL_00ba: volatile. - IL_00bc: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{207B14E2-2177-4CF2-8D8E-2CD85A17CF5C}'::'$$method0x6000008-1' + IL_00bc: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{D3E1C722-15E3-49C8-B86B-96413DA7BEEE}'::'$$method0x6000004-1' IL_00c1: ldloc.2 IL_00c2: ldloca.s V_3 IL_00c4: call instance bool class [mscorlib]System.Collections.Generic.Dictionary`2::TryGetValue(!0, @@ -876,73 +689,201 @@ IL_0091: ret } // end of method Switch::SwitchInLoop - .method public hidebysig static void SwitchWithGoto(int32 i) cil managed + .method private hidebysig static class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty[] + GetProperties() cil managed { - // Code size 122 (0x7a) - .maxstack 2 - .locals init (int32 V_0) + // Code size 12 (0xc) + .maxstack 1 + .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty[] 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_0041, - IL_004f, - IL_005d) - IL_0031: br.s IL_006b - - IL_0033: nop - IL_0034: ldstr "one" - IL_0039: call void [mscorlib]System.Console::WriteLine(string) - IL_003e: nop - IL_003f: br.s IL_006b - - IL_0041: nop - IL_0042: ldstr "two" - IL_0047: call void [mscorlib]System.Console::WriteLine(string) - IL_004c: nop - IL_004d: br.s IL_004f - - IL_004f: nop - IL_0050: ldstr "three" - IL_0055: call void [mscorlib]System.Console::WriteLine(string) - IL_005a: nop - IL_005b: br.s IL_0079 - - IL_005d: nop - IL_005e: ldstr "four" - IL_0063: call void [mscorlib]System.Console::WriteLine(string) - IL_0068: nop - IL_0069: br.s IL_0079 + 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_006b: nop - IL_006c: ldstr "default" - IL_0071: call void [mscorlib]System.Console::WriteLine(string) - IL_0076: nop - IL_0077: br.s IL_0079 + IL_000a: ldloc.0 + IL_000b: ret + } // end of method Switch::GetProperties - IL_0079: ret - } // end of method Switch::SwitchWithGoto + .method public hidebysig static void SwitchOnStringInForLoop() cil managed + { + // Code size 334 (0x14e) + .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, + string V_6, + int32 V_7, + bool V_8) + 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_013e + + 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: stloc.s V_6 + IL_0032: ldloc.s V_6 + IL_0034: brfalse IL_012d + + IL_0039: volatile. + IL_003b: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{D3E1C722-15E3-49C8-B86B-96413DA7BEEE}'::'$$method0x6000008-1' + IL_0040: brtrue.s IL_0097 + + IL_0042: ldc.i4.6 + IL_0043: newobj instance void class [mscorlib]System.Collections.Generic.Dictionary`2::.ctor(int32) + IL_0048: dup + IL_0049: ldstr "Name1" + IL_004e: ldc.i4.0 + IL_004f: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_0054: dup + IL_0055: ldstr "Name2" + IL_005a: ldc.i4.1 + IL_005b: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_0060: dup + IL_0061: ldstr "Name3" + IL_0066: ldc.i4.2 + IL_0067: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_006c: dup + IL_006d: ldstr "Name4" + IL_0072: ldc.i4.3 + IL_0073: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_0078: dup + IL_0079: ldstr "Name5" + IL_007e: ldc.i4.4 + IL_007f: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_0084: dup + IL_0085: ldstr "Name6" + IL_008a: ldc.i4.5 + IL_008b: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_0090: volatile. + IL_0092: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{D3E1C722-15E3-49C8-B86B-96413DA7BEEE}'::'$$method0x6000008-1' + IL_0097: volatile. + IL_0099: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{D3E1C722-15E3-49C8-B86B-96413DA7BEEE}'::'$$method0x6000008-1' + IL_009e: ldloc.s V_6 + IL_00a0: ldloca.s V_7 + IL_00a2: call instance bool class [mscorlib]System.Collections.Generic.Dictionary`2::TryGetValue(!0, + !1&) + IL_00a7: brfalse IL_012d + + IL_00ac: ldloc.s V_7 + IL_00ae: switch ( + IL_00cd, + IL_00e2, + IL_00f7, + IL_010c, + IL_0121, + IL_0121) + IL_00cb: br.s IL_012d + + IL_00cd: nop + IL_00ce: ldloc.s V_4 + IL_00d0: ldc.i4.1 + IL_00d1: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + IL_00d6: nop + IL_00d7: ldloc.0 + IL_00d8: ldloc.s V_4 + IL_00da: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_00df: nop + IL_00e0: br.s IL_0139 + + IL_00e2: nop + IL_00e3: ldloc.s V_4 + IL_00e5: ldc.i4.2 + IL_00e6: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + IL_00eb: nop + IL_00ec: ldloc.0 + IL_00ed: ldloc.s V_4 + IL_00ef: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_00f4: nop + IL_00f5: br.s IL_0139 + + IL_00f7: nop + IL_00f8: ldloc.s V_4 + IL_00fa: ldc.i4.3 + IL_00fb: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + IL_0100: nop + 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: nop + IL_010a: br.s IL_0139 + + IL_010c: nop + IL_010d: ldloc.s V_4 + IL_010f: ldc.i4.4 + IL_0110: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + IL_0115: nop + 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_0139 + + IL_0121: nop + IL_0122: ldloc.0 + IL_0123: ldloc.s V_4 + IL_0125: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_012a: nop + IL_012b: br.s IL_0139 + + IL_012d: nop + IL_012e: ldloc.1 + IL_012f: ldloc.s V_4 + IL_0131: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_0136: nop + IL_0137: br.s IL_0139 + + IL_0139: nop + IL_013a: ldloc.3 + IL_013b: ldc.i4.1 + IL_013c: add + IL_013d: stloc.3 + IL_013e: ldloc.3 + IL_013f: ldloc.2 + IL_0140: ldlen + IL_0141: conv.i4 + IL_0142: clt + IL_0144: stloc.s V_8 + IL_0146: ldloc.s V_8 + IL_0148: brtrue IL_001a + + IL_014d: ret + } // end of method Switch::SwitchOnStringInForLoop } // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch -.class private auto ansi '{207B14E2-2177-4CF2-8D8E-2CD85A17CF5C}' +.class private auto ansi '{D3E1C722-15E3-49C8-B86B-96413DA7BEEE}' 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 '$$method0x6000007-1' + .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000003-1' + .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000004-1' .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000008-1' -} // end of class '{207B14E2-2177-4CF2-8D8E-2CD85A17CF5C}' +} // end of class '{D3E1C722-15E3-49C8-B86B-96413DA7BEEE}' // ============================================================= diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il index 0765e0099..becdf49a9 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il @@ -10,25 +10,25 @@ .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } -.assembly utwwumxi +.assembly zlaei1fn { - .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. + .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 utwwumxi.dll -// MVID: {30E98C35-5F99-4742-941F-78E7F27D8BD5} +.module zlaei1fn.dll +// MVID: {64CCBA80-944A-4F77-9230-24B174DEE22A} .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: 0x02C70000 +// Image base: 0x00680000 // =============== CLASS MEMBERS DECLARATION =================== @@ -36,6 +36,55 @@ .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 { @@ -127,212 +176,6 @@ 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 - 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 - 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 string ShortSwitchOverString(string text) cil managed { @@ -400,7 +243,7 @@ IL_0013: brfalse IL_00db IL_0018: volatile. - IL_001a: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{30E98C35-5F99-4742-941F-78E7F27D8BD5}'::'$$method0x6000007-1' + IL_001a: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{64CCBA80-944A-4F77-9230-24B174DEE22A}'::'$$method0x6000003-1' IL_001f: brtrue.s IL_0082 IL_0021: ldc.i4.7 @@ -441,9 +284,9 @@ 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 '{30E98C35-5F99-4742-941F-78E7F27D8BD5}'::'$$method0x6000007-1' + IL_007d: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{64CCBA80-944A-4F77-9230-24B174DEE22A}'::'$$method0x6000003-1' IL_0082: volatile. - IL_0084: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{30E98C35-5F99-4742-941F-78E7F27D8BD5}'::'$$method0x6000007-1' + IL_0084: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{64CCBA80-944A-4F77-9230-24B174DEE22A}'::'$$method0x6000003-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, @@ -504,7 +347,7 @@ IL_0013: brfalse IL_013f IL_0018: volatile. - IL_001a: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{30E98C35-5F99-4742-941F-78E7F27D8BD5}'::'$$method0x6000008-1' + IL_001a: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{64CCBA80-944A-4F77-9230-24B174DEE22A}'::'$$method0x6000004-1' IL_001f: brtrue IL_00b8 IL_0024: ldc.i4.s 11 @@ -565,9 +408,9 @@ 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 '{30E98C35-5F99-4742-941F-78E7F27D8BD5}'::'$$method0x6000008-1' + IL_00b3: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{64CCBA80-944A-4F77-9230-24B174DEE22A}'::'$$method0x6000004-1' IL_00b8: volatile. - IL_00ba: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{30E98C35-5F99-4742-941F-78E7F27D8BD5}'::'$$method0x6000008-1' + IL_00ba: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{64CCBA80-944A-4F77-9230-24B174DEE22A}'::'$$method0x6000004-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, @@ -708,57 +551,170 @@ IL_007a: br.s IL_0015 } // end of method Switch::SwitchInLoop - .method public hidebysig static void SwitchWithGoto(int32 i) cil managed + .method private hidebysig static class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty[] + GetProperties() cil managed { - // Code size 104 (0x68) - .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_0052) - IL_002f: br.s IL_005d - - IL_0031: ldstr "one" - IL_0036: call void [mscorlib]System.Console::WriteLine(string) - IL_003b: br.s IL_005d - - 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: ret - - IL_0052: ldstr "four" - IL_0057: call void [mscorlib]System.Console::WriteLine(string) - IL_005c: ret + // 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 303 (0x12f) + .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, + string V_6, + int32 V_7) + 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_0125 + + 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: dup + IL_002f: stloc.s V_6 + IL_0031: brfalse IL_0119 + + IL_0036: volatile. + IL_0038: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{64CCBA80-944A-4F77-9230-24B174DEE22A}'::'$$method0x6000008-1' + IL_003d: brtrue.s IL_0094 + + IL_003f: ldc.i4.6 + IL_0040: newobj instance void class [mscorlib]System.Collections.Generic.Dictionary`2::.ctor(int32) + IL_0045: dup + IL_0046: ldstr "Name1" + IL_004b: ldc.i4.0 + IL_004c: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_0051: dup + IL_0052: ldstr "Name2" + IL_0057: ldc.i4.1 + IL_0058: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_005d: dup + IL_005e: ldstr "Name3" + IL_0063: ldc.i4.2 + IL_0064: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_0069: dup + IL_006a: ldstr "Name4" + IL_006f: ldc.i4.3 + IL_0070: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_0075: dup + IL_0076: ldstr "Name5" + IL_007b: ldc.i4.4 + IL_007c: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_0081: dup + IL_0082: ldstr "Name6" + IL_0087: ldc.i4.5 + IL_0088: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + !1) + IL_008d: volatile. + IL_008f: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{64CCBA80-944A-4F77-9230-24B174DEE22A}'::'$$method0x6000008-1' + IL_0094: volatile. + IL_0096: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{64CCBA80-944A-4F77-9230-24B174DEE22A}'::'$$method0x6000008-1' + IL_009b: ldloc.s V_6 + IL_009d: ldloca.s V_7 + IL_009f: call instance bool class [mscorlib]System.Collections.Generic.Dictionary`2::TryGetValue(!0, + !1&) + IL_00a4: brfalse.s IL_0119 - IL_005d: ldstr "default" - IL_0062: call void [mscorlib]System.Console::WriteLine(string) - IL_0067: ret - } // end of method Switch::SwitchWithGoto + IL_00a6: ldloc.s V_7 + IL_00a8: switch ( + IL_00c7, + IL_00d9, + IL_00eb, + IL_00fd, + IL_010f, + IL_010f) + IL_00c5: br.s IL_0119 + + IL_00c7: ldloc.s V_4 + IL_00c9: ldc.i4.1 + IL_00ca: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + 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: br.s IL_0121 + + IL_00d9: ldloc.s V_4 + IL_00db: ldc.i4.2 + IL_00dc: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + IL_00e1: ldloc.0 + IL_00e2: ldloc.s V_4 + IL_00e4: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_00e9: br.s IL_0121 + + IL_00eb: ldloc.s V_4 + IL_00ed: ldc.i4.3 + IL_00ee: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + IL_00f3: ldloc.0 + IL_00f4: ldloc.s V_4 + IL_00f6: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_00fb: br.s IL_0121 + + IL_00fd: ldloc.s V_4 + IL_00ff: ldc.i4.4 + IL_0100: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + IL_0105: ldloc.0 + IL_0106: ldloc.s V_4 + IL_0108: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_010d: br.s IL_0121 + + IL_010f: ldloc.0 + IL_0110: ldloc.s V_4 + IL_0112: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_0117: br.s IL_0121 + + IL_0119: ldloc.1 + IL_011a: ldloc.s V_4 + IL_011c: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_0121: ldloc.3 + IL_0122: ldc.i4.1 + IL_0123: add + IL_0124: stloc.3 + IL_0125: ldloc.3 + IL_0126: ldloc.2 + IL_0127: ldlen + IL_0128: conv.i4 + IL_0129: blt IL_0019 + + IL_012e: ret + } // end of method Switch::SwitchOnStringInForLoop } // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch -.class private auto ansi '{30E98C35-5F99-4742-941F-78E7F27D8BD5}' +.class private auto ansi '{64CCBA80-944A-4F77-9230-24B174DEE22A}' 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 '$$method0x6000007-1' + .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000003-1' + .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000004-1' .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000008-1' -} // end of class '{30E98C35-5F99-4742-941F-78E7F27D8BD5}' +} // end of class '{64CCBA80-944A-4F77-9230-24B174DEE22A}' // ============================================================= diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il index ef1606d38..48993c1fe 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il @@ -25,14 +25,14 @@ .ver 0:0:0:0 } .module Switch.dll -// MVID: {E38DFC98-1601-4EC8-896E-FC0EA87D4437} +// MVID: {25FC064E-F764-4556-A3C7-F6570E457CDD} .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: 0x02590000 +// Image base: 0x00B30000 // =============== CLASS MEMBERS DECLARATION =================== @@ -40,6 +40,55 @@ .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 { @@ -136,214 +185,6 @@ 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 - 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 - 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 string ShortSwitchOverString(string text) cil managed { @@ -818,44 +659,134 @@ IL_0078: br.s IL_0015 } // end of method Switch::SwitchInLoop - .method public hidebysig static void SwitchWithGoto(int32 i) cil managed + .method private hidebysig static class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty[] + GetProperties() cil managed { - // Code size 102 (0x66) + // 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 - 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_0050) - IL_002d: br.s IL_005b + .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_002f: ldstr "one" - IL_0034: call void [mscorlib]System.Console::WriteLine(string) - IL_0039: br.s IL_005b + 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_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: ret - - IL_0050: ldstr "four" - IL_0055: call void [mscorlib]System.Console::WriteLine(string) - IL_005a: ret - - IL_005b: ldstr "default" - IL_0060: call void [mscorlib]System.Console::WriteLine(string) - IL_0065: ret - } // end of method Switch::SwitchWithGoto + 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 } // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il index f94d258ba..4da071b46 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il @@ -25,14 +25,14 @@ .ver 0:0:0:0 } .module Switch.dll -// MVID: {7C3CB7C3-DBBF-4EB8-ACC1-E7AFA899FD3B} +// MVID: {134EA2E4-FA5A-4D44-A0FD-C4E5A18E39B1} .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: 0x019E0000 +// Image base: 0x00C40000 // =============== CLASS MEMBERS DECLARATION =================== @@ -40,6 +40,58 @@ .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 { @@ -177,280 +229,6 @@ IL_00ed: 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 - 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 - 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 string ShortSwitchOverString(string text) cil managed { @@ -1066,63 +844,168 @@ IL_0091: ret } // end of method Switch::SwitchInLoop - .method public hidebysig static void SwitchWithGoto(int32 i) cil managed + .method private hidebysig static class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty[] + GetProperties() cil managed { - // Code size 122 (0x7a) + // 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 265 (0x109) .maxstack 2 - .locals init (int32 V_0) + .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, + string V_6, + bool V_7) 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_0041, - IL_004f, - IL_005d) - IL_0031: br.s IL_006b - - IL_0033: nop - IL_0034: ldstr "one" - IL_0039: call void [mscorlib]System.Console::WriteLine(string) - IL_003e: nop - IL_003f: br.s IL_006b + 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_00f9 + + 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: stloc.s V_6 + IL_0032: ldloc.s V_6 + IL_0034: ldstr "Name1" + IL_0039: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_003e: brtrue.s IL_0088 - IL_0041: nop - IL_0042: ldstr "two" - IL_0047: call void [mscorlib]System.Console::WriteLine(string) - IL_004c: nop - IL_004d: br.s IL_004f + IL_0040: ldloc.s V_6 + IL_0042: ldstr "Name2" + IL_0047: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_004c: brtrue.s IL_009d - IL_004f: nop - IL_0050: ldstr "three" - IL_0055: call void [mscorlib]System.Console::WriteLine(string) - IL_005a: nop - IL_005b: br.s IL_0079 + IL_004e: ldloc.s V_6 + IL_0050: ldstr "Name3" + IL_0055: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_005a: brtrue.s IL_00b2 - IL_005d: nop - IL_005e: ldstr "four" - IL_0063: call void [mscorlib]System.Console::WriteLine(string) - IL_0068: nop - IL_0069: br.s IL_0079 + IL_005c: ldloc.s V_6 + IL_005e: ldstr "Name4" + IL_0063: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0068: brtrue.s IL_00c7 - IL_006b: nop - IL_006c: ldstr "default" - IL_0071: call void [mscorlib]System.Console::WriteLine(string) - IL_0076: nop - IL_0077: br.s IL_0079 + IL_006a: ldloc.s V_6 + IL_006c: ldstr "Name5" + IL_0071: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0076: brtrue.s IL_00dc - IL_0079: ret - } // end of method Switch::SwitchWithGoto + IL_0078: ldloc.s V_6 + IL_007a: ldstr "Name6" + IL_007f: call bool [mscorlib]System.String::op_Equality(string, + string) + IL_0084: brtrue.s IL_00dc + + IL_0086: br.s IL_00e8 + + IL_0088: nop + IL_0089: ldloc.s V_4 + IL_008b: ldc.i4.1 + IL_008c: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + IL_0091: nop + IL_0092: ldloc.0 + IL_0093: ldloc.s V_4 + IL_0095: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_009a: nop + IL_009b: br.s IL_00f4 + + IL_009d: nop + IL_009e: ldloc.s V_4 + IL_00a0: ldc.i4.2 + IL_00a1: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + IL_00a6: nop + IL_00a7: ldloc.0 + IL_00a8: ldloc.s V_4 + IL_00aa: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_00af: nop + IL_00b0: br.s IL_00f4 + + IL_00b2: nop + IL_00b3: ldloc.s V_4 + IL_00b5: ldc.i4.3 + IL_00b6: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + IL_00bb: nop + IL_00bc: ldloc.0 + IL_00bd: ldloc.s V_4 + IL_00bf: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_00c4: nop + IL_00c5: br.s IL_00f4 + + IL_00c7: nop + IL_00c8: ldloc.s V_4 + IL_00ca: ldc.i4.4 + IL_00cb: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + IL_00d0: nop + IL_00d1: ldloc.0 + IL_00d2: ldloc.s V_4 + IL_00d4: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_00d9: nop + IL_00da: br.s IL_00f4 + + IL_00dc: nop + 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: nop + IL_00e6: br.s IL_00f4 + + IL_00e8: nop + IL_00e9: ldloc.1 + IL_00ea: ldloc.s V_4 + IL_00ec: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_00f1: nop + IL_00f2: br.s IL_00f4 + + IL_00f4: nop + IL_00f5: ldloc.3 + IL_00f6: ldc.i4.1 + IL_00f7: add + IL_00f8: stloc.3 + IL_00f9: ldloc.3 + IL_00fa: ldloc.2 + IL_00fb: ldlen + IL_00fc: conv.i4 + IL_00fd: clt + IL_00ff: stloc.s V_7 + IL_0101: ldloc.s V_7 + IL_0103: brtrue IL_001a + + IL_0108: ret + } // end of method Switch::SwitchOnStringInForLoop } // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch diff --git a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs index 70f0903f0..dc35b6158 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs @@ -123,9 +123,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (values.Count == 0) return false; // All case blocks must either leave the current block container or branch to the same block after the switch statement. - if (!(values.All(b => ExtractLastJumpFromBlock(b.Item2, out var nextExit) && IsExitBlock(nextExit, container)) || - (exitBlock == null && values.All(b => ExtractLastLeaveFromBlock(b.Item2, container))))) - return false; + if (exitBlock == null) { + if (!values.All(b => ExtractLastLeaveFromBlock(b.Item2, container))) + return false; + } else { + // Compare blocks by label as duplicated blocks should have the same label. + if (!(values.All(b => ExtractLastJumpFromBlock(b.Item2, out var nextExitBlock) && nextExitBlock.Label == exitBlock.Label))) + return false; + } // if the block after the switch has the correct number of branches, generate the switch statement // and return it and the block. if (currentCaseBlock.IncomingEdgeCount == (isLegacy ? 2 : 1)) { @@ -139,13 +144,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; } - bool IsExitBlock(Block nextExit, BlockContainer container) - { - if (nextExit.Instructions.Count != 1) - return false; - return nextExit.Instructions[0].MatchLeave(container, out _); - } - bool ExtractLastJumpFromBlock(Block block, out Block exitBlock) { exitBlock = null; From 38482888a132503b5cc5c71bffc610d60fd45454 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 5 Oct 2017 17:47:03 +0200 Subject: [PATCH 17/65] Fix Using pretty tests and remove semicolon on single-line if. --- ICSharpCode.Decompiler.Tests/TestCases/Pretty/Using.cs | 2 +- ICSharpCode.Decompiler/IL/Transforms/InlineReturnTransform.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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/IL/Transforms/InlineReturnTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/InlineReturnTransform.cs index 7245316c4..e9c53f8ca 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/InlineReturnTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/InlineReturnTransform.cs @@ -39,7 +39,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms 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)); + if (CanModifyInstructions(returnVar, leaveBlock, out var list)) instructionsToModify.AddRange(list); } From 8a68a94d355302d4db0f144a02791aa80b6ab24e Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 7 Oct 2017 00:13:14 +0200 Subject: [PATCH 18/65] Simplify use of SwitchInstruction in ILAst * the default case is now handled as a normal case * when dealing with basic blocks, SwitchInstruction will be the last instruction in the block * introduced ILAst instruction for 'goto case' --- .../CSharp/StatementBuilder.cs | 26 +++-- .../FlowAnalysis/DataFlowVisitor.cs | 16 ++- ICSharpCode.Decompiler/IL/BlockBuilder.cs | 11 +- .../IL/ControlFlow/ConditionDetection.cs | 39 +------ .../IL/ControlFlow/StateRangeAnalysis.cs | 4 - .../IL/ControlFlow/SwitchAnalysis.cs | 23 ++-- .../IL/ControlFlow/SwitchDetection.cs | 37 +++--- ICSharpCode.Decompiler/IL/ILReader.cs | 5 +- ICSharpCode.Decompiler/IL/Instructions.cs | 62 +++++++++- ICSharpCode.Decompiler/IL/Instructions.tt | 9 +- .../IL/Instructions/Branch.cs | 15 +-- .../IL/Instructions/SwitchInstruction.cs | 109 +++++++++++++----- .../IL/Transforms/SwitchOnStringTransform.cs | 1 - 13 files changed, 223 insertions(+), 134 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs index d5923cb3e..3ddb00fe9 100644 --- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs @@ -112,21 +112,29 @@ namespace ICSharpCode.Decompiler.CSharp value = exprBuilder.Translate(inst.Value); } - var stmt = new SwitchStatement() { Expression = value }; + // Pick the section with the most labels as default section. + IL.SwitchSection defaultSection = inst.Sections.First(); + foreach (var section in inst.Sections) { + if (section.Labels.Count() > defaultSection.Labels.Count()) { + defaultSection = section; + } + } + + var stmt = new SwitchStatement() { Expression = value }; foreach (var section in inst.Sections) { var astSection = new Syntax.SwitchSection(); - astSection.CaseLabels.AddRange(section.Labels.Values.Select(i => CreateTypedCaseLabel(i, value.Type, strToInt?.Map))); + if (section == defaultSection) { + astSection.CaseLabels.Add(new CaseLabel()); + } else { + if (section.HasNullLabel) { + astSection.CaseLabels.Add(new CaseLabel(new NullReferenceExpression())); + } + astSection.CaseLabels.AddRange(section.Labels.Values.Select(i => CreateTypedCaseLabel(i, value.Type, strToInt?.Map))); + } ConvertSwitchSectionBody(astSection, section.Body); stmt.SwitchSections.Add(astSection); } - if (inst.DefaultBody.OpCode != OpCode.Nop) { - var astSection = new Syntax.SwitchSection(); - astSection.CaseLabels.Add(new CaseLabel()); - ConvertSwitchSectionBody(astSection, inst.DefaultBody); - stmt.SwitchSections.Add(astSection); - } - breakTarget = oldBreakTarget; return stmt; } diff --git a/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs b/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs index 2ac18d313..0632edc79 100644 --- a/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs +++ b/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs @@ -614,17 +614,27 @@ 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 VisitGotoCase(GotoCase inst) + { + // Handling goto case here is tricky: + // We'll need a fixed-point iteration for SwitchInstruction similar to BlockContainer, + // and we'll need to handle GotoCase like Branch, including stuff like ProcessBranchesLeavingTryFinally(). + // Hopefully we won't need a data-flow analysis in the decompiler pipeline after 'goto case' instructions + // are already introduced. + throw new NotImplementedException(); + } + protected internal override void VisitYieldReturn(YieldReturn inst) { DebugStartPoint(inst); diff --git a/ICSharpCode.Decompiler/IL/BlockBuilder.cs b/ICSharpCode.Decompiler/IL/BlockBuilder.cs index 2ba957cb4..46e51c794 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 48ecdf218..21da91022 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs @@ -63,18 +63,17 @@ 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(); + if (exitInst is SwitchInstruction switchInst) { + HandleSwitchInstruction(cfgNode, block, switchInst); + } // 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;" @@ -289,9 +288,8 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow && cfgNode.Dominates(context.ControlFlowGraph.GetNode(targetBlock)); } - private void HandleSwitchInstruction(ControlFlowNode cfgNode, Block block, SwitchInstruction sw, ref ILInstruction exitInst) + private void HandleSwitchInstruction(ControlFlowNode cfgNode, Block block, SwitchInstruction sw) { - Debug.Assert(sw.DefaultBody is Nop); // First, move blocks into the switch section foreach (var section in sw.Sections) { if (IsUsableBranchToChild(cfgNode, section.Body)) { @@ -301,32 +299,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow 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); - } - } + // TODO: find a common exit point among the cases, and if possible inline that into this block. sw.Sections.ReplaceList(sw.Sections.OrderBy(s => s.Body.ILRange.Start)); } 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 c280faa02..962c76f3f 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/SwitchAnalysis.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/SwitchAnalysis.cs @@ -94,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)) { @@ -122,8 +123,10 @@ 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, out trueValues)) { ContainsILSwitch = true; // OK } else { // switch analysis failed (e.g. switchVar mismatch) return false; @@ -147,7 +150,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow private bool AnalyzeSwitch(SwitchInstruction inst, LongSet inputValues, out LongSet anyMatchValues) { - Debug.Assert(inst.DefaultBody is Nop); + Debug.Assert(!inst.IsLifted); anyMatchValues = LongSet.Empty; long offset; if (MatchSwitchVar(inst.Value)) { diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs index 897d41e7e..5c31baa9a 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs @@ -60,16 +60,19 @@ 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 Branch) { + Debug.Assert(block.Instructions.SecondToLastOrDefault() is IfInstruction); + block.Instructions.RemoveAt(block.Instructions.Count - 1); + } else { + Debug.Assert(block.Instructions.Last() is SwitchInstruction); } - 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); @@ -87,8 +90,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 +99,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); diff --git a/ICSharpCode.Decompiler/IL/ILReader.cs b/ICSharpCode.Decompiler/IL/ILReader.cs index b401f5c5d..9ed9cb9ad 100644 --- a/ICSharpCode.Decompiler/IL/ILReader.cs +++ b/ICSharpCode.Decompiler/IL/ILReader.cs @@ -1232,7 +1232,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 c6f361279..ccb7bdedf 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions.cs @@ -63,6 +63,8 @@ namespace ICSharpCode.Decompiler.IL SwitchInstruction, /// Switch section within a switch statement SwitchSection, + /// Unconditional branch to switch section. goto case target; + GotoCase, /// Try-catch statement. TryCatch, /// Catch handler within a try-catch statement. @@ -1014,6 +1016,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); @@ -1319,7 +1330,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); } } } @@ -1391,7 +1402,41 @@ 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; + } + } +} +namespace ICSharpCode.Decompiler.IL +{ + /// Unconditional branch to switch section. goto case target; + public sealed partial class GotoCase : 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.VisitGotoCase(this); + } + public override T AcceptVisitor(ILVisitor visitor) + { + return visitor.VisitGotoCase(this); + } + public override T AcceptVisitor(ILVisitor visitor, C context) + { + return visitor.VisitGotoCase(this, context); + } + protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match) + { + var o = other as GotoCase; + return o != null && this.TargetSection == o.TargetSection; } } } @@ -4528,6 +4573,10 @@ namespace ICSharpCode.Decompiler.IL { Default(inst); } + protected internal virtual void VisitGotoCase(GotoCase inst) + { + Default(inst); + } protected internal virtual void VisitTryCatch(TryCatch inst) { Default(inst); @@ -4818,6 +4867,10 @@ namespace ICSharpCode.Decompiler.IL { return Default(inst); } + protected internal virtual T VisitGotoCase(GotoCase inst) + { + return Default(inst); + } protected internal virtual T VisitTryCatch(TryCatch inst) { return Default(inst); @@ -5108,6 +5161,10 @@ namespace ICSharpCode.Decompiler.IL { return Default(inst, context); } + protected internal virtual T VisitGotoCase(GotoCase inst, C context) + { + return Default(inst, context); + } protected internal virtual T VisitTryCatch(TryCatch inst, C context) { return Default(inst, context); @@ -5342,6 +5399,7 @@ namespace ICSharpCode.Decompiler.IL "if.notnull", "switch", "switch.section", + "goto.case", "try.catch", "try.catch.handler", "try.finally", diff --git a/ICSharpCode.Decompiler/IL/Instructions.tt b/ICSharpCode.Decompiler/IL/Instructions.tt index f25e3fa0d..c75172ddf 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,14 @@ }), 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("goto.case", "Unconditional branch to switch section. goto case target;", + CustomClassName("GotoCase"), NoArguments, CustomConstructor, UnconditionalBranch, MayBranch, + MatchCondition("this.TargetSection == o.TargetSection")), new OpCode("try.catch", "Try-catch statement.", BaseClass("TryInstruction"), CustomConstructor, CustomComputeFlags, CustomWriteTo, MatchCondition("TryBlock.PerformMatch(o.TryBlock, ref match)"), 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/SwitchInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs index 8e22ffe20..5de07f592 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,11 +180,53 @@ 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); } } + + /// + /// Unconditional branch to switch section. goto case target;/goto default; + /// + /// + /// Like normal branches, can trigger finally blocks. + /// + partial class GotoCase // : IBranchOrLeaveInstruction + { + public SwitchSection TargetSection { get; } + + public GotoCase(SwitchSection target) : base(OpCode.GotoCase) + { + this.TargetSection = target ?? throw new ArgumentNullException(nameof(target)); + } + + internal override void CheckInvariant(ILPhase phase) + { + base.CheckInvariant(phase); + var parentSwitch = this.Ancestors.OfType().First(); + Debug.Assert(parentSwitch == TargetSection.Parent); + } + + public override void WriteTo(ITextOutput output, ILAstWritingOptions options) + { + output.Write("goto.case "); + if (TargetSection.HasNullLabel) { + output.WriteReference("null", TargetSection, isLocal: true); + } else { + output.WriteReference(TargetSection.Labels.Values.First().ToString(), TargetSection, isLocal: true); + } + } + } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs index dc35b6158..0b7d27359 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs @@ -283,7 +283,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms } var stringToInt = new StringToInt(new LdLoc(switchValueVar), stringValues.ToArray()); inst = new SwitchInstruction(stringToInt); - inst.DefaultBody = switchInst.DefaultBody; inst.Sections.AddRange(sections); blockAfterSwitch = defaultBlock; return true; From 5399b93df76abf1a5530b4f0969947218838fd12 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 7 Oct 2017 12:42:52 +0200 Subject: [PATCH 19/65] ConnectMethodDecompiler: Adapt to new switch transformation. --- ILSpy.BamlDecompiler/BamlResourceEntryNode.cs | 10 +++++----- ILSpy.BamlDecompiler/ConnectMethodDecompiler.cs | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) 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..a28f63d7b 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) @@ -70,8 +71,7 @@ namespace ILSpy.BamlDecompiler 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; From 621e355f364b09f4f272d5d7a7f5c7a665748a23 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 7 Oct 2017 12:46:03 +0200 Subject: [PATCH 20/65] Adapt SwitchOnStringTransform to switch instruction changes. --- ICSharpCode.Decompiler/IL/DetectedLoop.cs | 2 +- .../IL/Transforms/SwitchOnStringTransform.cs | 135 +++++++----------- 2 files changed, 52 insertions(+), 85 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/DetectedLoop.cs b/ICSharpCode.Decompiler/IL/DetectedLoop.cs index 276ca2657..9b935fc00 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/Transforms/SwitchOnStringTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs index 0b7d27359..ef199199a 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs @@ -38,36 +38,31 @@ namespace ICSharpCode.Decompiler.IL.Transforms 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; - Block blockAfterSwitch = null; - bool removeExtraStore = false; // the Roslyn switch pattern uses an extra store for hash calculation. - if (!MatchCascadingIfStatements(block.Instructions, i, out newSwitch, out blockAfterSwitch)) - if (!MatchLegacySwitchOnString(block.Instructions, i, out newSwitch, out blockAfterSwitch)) - if (MatchRoslynSwitchOnString(block.Instructions, i, out newSwitch)) - removeExtraStore = true; - else - continue; - - if (i + 1 < block.Instructions.Count && block.Instructions[i + 1] is Branch b && blockAfterSwitch != null) { - block.Instructions[i + 1].ReplaceWith(new Branch(blockAfterSwitch)); + if (SimplifyCascadingIfStatements(block.Instructions, i, out newSwitch)) { + block.Instructions[i + 1].ReplaceWith(newSwitch); + block.Instructions.RemoveAt(i); + changed = true; + continue; } - - block.Instructions[i].ReplaceWith(newSwitch); - if (removeExtraStore) { - block.Instructions.RemoveAt(i - 1); + if (MatchLegacySwitchOnString(block.Instructions, i, out newSwitch)) { + block.Instructions[i + 1].ReplaceWith(newSwitch); + block.Instructions.RemoveAt(i); + changed = true; + continue; + } + if (MatchRoslynSwitchOnString(block.Instructions, i, out newSwitch)) { + block.Instructions[i - 1].ReplaceWith(newSwitch); + block.Instructions.RemoveAt(i); + changed = true; i--; + continue; } - - // Combine cases with the same branch target: - SwitchDetection.SimplifySwitchInstruction(block); - - // This happens in some cases: - // Use correct index after transformation. - if (i >= block.Instructions.Count) - i = block.Instructions.Count; } - + if (!changed) continue; + SwitchDetection.SimplifySwitchInstruction(block); if (block.Parent is BlockContainer container) changedContainers.Add(container); } @@ -76,10 +71,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms container.SortBlocks(deleteUnreachableBlocks: true); } - bool MatchCascadingIfStatements(InstructionCollection instructions, int i, out SwitchInstruction inst, out Block blockAfterSwitch) + bool SimplifyCascadingIfStatements(InstructionCollection instructions, int i, out SwitchInstruction inst) { inst = null; - blockAfterSwitch = null; // match first block: checking switch-value for null or first value (Roslyn) // if (call op_Equality(ldloc switchValueVar, ldstr value)) br firstBlock // -or- @@ -89,16 +83,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (!firstBlockJump.MatchBranch(out var firstBlock)) return false; bool isLegacy; - Block defaultBlock; List<(string, Block)> values = new List<(string, Block)>(); // match null check: this is used by the old C# compiler. if (condition.MatchCompEquals(out var left, out var right) && right.MatchLdNull() && left.MatchLdLoc(out var switchValueVar)) { isLegacy = true; - defaultBlock = firstBlock; - // Roslyn: match call to operator ==(string, string) + values.Add((null, firstBlock)); + // Roslyn: match call to operator ==(string, string) } else if (MatchStringEqualityComparison(condition, out switchValueVar, out string value)) { isLegacy = false; - defaultBlock = null; values.Add((value, firstBlock)); } else return false; // switchValueVar must be assigned only once. @@ -114,53 +106,20 @@ namespace ICSharpCode.Decompiler.IL.Transforms values.Add((value, block)); currentCaseBlock = nextCaseBlock; } - // the last instruction of the last case/default block must be either - // a branch to the a block after the switch statement or a leave instruction. - var container = BlockContainer.FindClosestContainer(firstBlock); - if (!ExtractLastJumpFromBlock(currentCaseBlock, out var exitBlock) && !ExtractLastLeaveFromBlock(currentCaseBlock, container)) + // We didn't find enough cases, exit + if (values.Count < 3) return false; - // We didn't find any cases, exit - if (values.Count == 0) - return false; - // All case blocks must either leave the current block container or branch to the same block after the switch statement. - if (exitBlock == null) { - if (!values.All(b => ExtractLastLeaveFromBlock(b.Item2, container))) - return false; - } else { - // Compare blocks by label as duplicated blocks should have the same label. - if (!(values.All(b => ExtractLastJumpFromBlock(b.Item2, out var nextExitBlock) && nextExitBlock.Label == exitBlock.Label))) - return false; - } - // if the block after the switch has the correct number of branches, generate the switch statement - // and return it and the block. - if (currentCaseBlock.IncomingEdgeCount == (isLegacy ? 2 : 1)) { - var sections = new List(values.SelectWithIndex((index, b) => new SwitchSection { Labels = new LongSet(index), Body = new Branch(b.Item2) })); - var stringToInt = new StringToInt(new LdLoc(switchValueVar), values.SelectArray(item => item.Item1)); - inst = new SwitchInstruction(stringToInt); - inst.Sections.AddRange(sections); - blockAfterSwitch = currentCaseBlock; - return true; - } - return false; - } - - bool ExtractLastJumpFromBlock(Block block, out Block exitBlock) - { - exitBlock = null; - var lastInst = block.Instructions.LastOrDefault(); - if (lastInst == null || !lastInst.MatchBranch(out exitBlock)) + // The block after all cases should only be reachable from the previous block and the null-check (in legacy code). + if (currentCaseBlock.IncomingEdgeCount != (isLegacy ? 2 : 1)) return false; + 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(new LdLoc(switchValueVar), values.SelectArray(item => item.Item1)); + inst = new SwitchInstruction(stringToInt); + inst.Sections.AddRange(sections); return true; } - bool ExtractLastLeaveFromBlock(Block block, BlockContainer container) - { - var lastInst = block.Instructions.LastOrDefault(); - if (lastInst == null || !lastInst.MatchLeave(out var b, out _)) - return false; - return b == container; - } - /// /// Each case consists of two blocks: /// 1. block: @@ -222,16 +181,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms return storeInst.Value.MatchLdLoc(right); } - bool MatchLegacySwitchOnString(InstructionCollection instructions, int i, out SwitchInstruction inst, out Block blockAfterSwitch) + bool MatchLegacySwitchOnString(InstructionCollection instructions, int i, out SwitchInstruction inst) { inst = null; - blockAfterSwitch = null; if (i < 1) return false; // match first block: checking switch-value for null 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 (!exitBlockJump.MatchBranch(out var exitBlock)) + if (!exitBlockJump.MatchBranch(out var nullValueCaseBlock)) return false; if (!(condition.MatchCompEquals(out var left, out var right) && right.MatchLdNull() && (left.Match(switchValue).Success || left.MatchLdLoc(switchValueVar)))) return false; @@ -269,22 +227,25 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (!tryGetValueBlock.Instructions[1].MatchBranch(out var switchBlock)) return false; // match fifth block: switch-instruction block - if (switchBlock.IncomingEdgeCount != 1 || switchBlock.Instructions.Count != 2) + if (switchBlock.IncomingEdgeCount != 1 || switchBlock.Instructions.Count != 1) return false; if (!(switchBlock.Instructions[0] is SwitchInstruction switchInst && switchInst.Value.MatchLdLoc(switchIndexVar))) return false; - if (!switchBlock.Instructions[1].MatchBranch(defaultBlock)) - return false; - // switch contains case null: var sections = new List(switchInst.Sections); - if (exitBlock != defaultBlock) { + // switch contains case null: + if (nullValueCaseBlock != defaultBlock) { + var label = new Util.LongSet(switchInst.Sections.Count); + var possibleConflicts = switchInst.Sections.Where(sec => sec.Labels.Overlaps(label)).ToArray(); + if (possibleConflicts.Length > 1) + return false; + else if (possibleConflicts.Length == 1) + possibleConflicts[0].Labels = possibleConflicts[0].Labels.ExceptWith(label); stringValues.Add(null); - sections.Add(new SwitchSection() { Labels = new Util.LongSet(stringValues.Count - 1), Body = new Branch(exitBlock) }); + sections.Add(new SwitchSection() { Labels = label, Body = new Branch(nullValueCaseBlock) }); } var stringToInt = new StringToInt(new LdLoc(switchValueVar), stringValues.ToArray()); inst = new SwitchInstruction(stringToInt); inst.Sections.AddRange(sections); - blockAfterSwitch = defaultBlock; return true; } @@ -340,10 +301,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms var stringValues = new List<(int, string, Block)>(); int index = 0; + Block defaultBlock = null; foreach (var section in switchInst.Sections) { if (!section.Body.MatchBranch(out Block target)) return false; - if (target.IncomingEdgeCount != 1 || target.Instructions.Count != 2) + if (target.IncomingEdgeCount > 1) { + defaultBlock = target; + continue; + } + if (target.Instructions.Count != 2) return false; if (!target.Instructions[0].MatchIfInstruction(out var condition, out var bodyBranch)) return false; @@ -359,10 +325,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms } stringValues.Add((index++, stringValue, body)); } - + var defaultLabel = new LongSet(new LongInterval(0, index)).Invert(); var value = new StringToInt(switchValue.Clone(), stringValues.Select(item => item.Item2).ToArray()); inst = new SwitchInstruction(value); inst.Sections.AddRange(stringValues.Select(section => new SwitchSection { Labels = new Util.LongSet(section.Item1), Body = new Branch(section.Item3) })); + inst.Sections.Add(new SwitchSection { Labels = defaultLabel, Body = new Branch(defaultBlock) }); return true; } From f42d1a4b34c7bf6b5e33682a68b8b3e8e19375f6 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 7 Oct 2017 13:54:49 +0200 Subject: [PATCH 21/65] Fix crash in SwitchDetection. --- ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs index 5c31baa9a..6e2fa3e67 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs @@ -65,11 +65,12 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow Body = section.Value }); } - if (block.Instructions.Last() is Branch) { + 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); - } else { - Debug.Assert(block.Instructions.Last() is SwitchInstruction); } block.Instructions[block.Instructions.Count - 1] = sw; From 43c2f891a0f183009630bc188e34e6632fd589f1 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 7 Oct 2017 13:26:31 +0200 Subject: [PATCH 22/65] SwitchOnStringTransform: make sure switch variable is of type string in Roslyn case. --- .../IL/Transforms/SwitchOnStringTransform.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs index ef199199a..774857f80 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs @@ -93,8 +93,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms isLegacy = false; values.Add((value, firstBlock)); } else return false; - // switchValueVar must be assigned only once. - if (!switchValueVar.IsSingleDefinition) + // switchValueVar must be assigned only once and must be of type string. + if (!switchValueVar.IsSingleDefinition || !switchValueVar.Type.IsKnownType(KnownTypeCode.String)) return false; // if instruction must be followed by a branch to the next case if (!(instructions.ElementAtOrDefault(i + 1) is Branch nextCaseJump)) From bd165642cb3db8fc0abf3c7133a1933285fd1576 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 7 Oct 2017 22:15:53 +0200 Subject: [PATCH 23/65] Fix #489: Add support for switch statement pattern using Hashtable --- .../IL/Transforms/SwitchOnStringTransform.cs | 152 +++++++++++++++--- 1 file changed, 134 insertions(+), 18 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs index 774857f80..96836b3db 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs @@ -47,7 +47,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms changed = true; continue; } - if (MatchLegacySwitchOnString(block.Instructions, i, out newSwitch)) { + if (MatchLegacySwitchOnStringWithHashtable(block.Instructions, i, out newSwitch)) { + block.Instructions[i + 1].ReplaceWith(newSwitch); + block.Instructions.RemoveAt(i); + changed = true; + continue; + } + if (MatchLegacySwitchOnStringWithDict(block.Instructions, i, out newSwitch)) { block.Instructions[i + 1].ReplaceWith(newSwitch); block.Instructions.RemoveAt(i); changed = true; @@ -181,7 +187,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms return storeInst.Value.MatchLdLoc(right); } - bool MatchLegacySwitchOnString(InstructionCollection instructions, int i, out SwitchInstruction inst) + /// + /// Matches the C# 2.0 switch-on-string pattern, which uses Dictionary<string, int>. + /// + bool MatchLegacySwitchOnStringWithDict(InstructionCollection instructions, int i, out SwitchInstruction inst) { inst = null; if (i < 1) return false; @@ -205,12 +214,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (!nextBlock.Instructions[1].MatchBranch(out var dictInitBlock) || dictInitBlock.IncomingEdgeCount != 1) return false; if (!(condition.MatchCompNotEquals(out left, out right) && right.MatchLdNull() && - MatchDictionaryFieldLoad(left, out var dictField, out var dictionaryType))) + MatchDictionaryFieldLoad(left, IsStringToIntDictionary, out var dictField, out var dictionaryType))) return false; // match third block: initialization of compiler-generated Dictionary if (dictInitBlock.IncomingEdgeCount != 1 || dictInitBlock.Instructions.Count < 3) return false; - if (!ExtractStringValuesFromDictionaryInitBlock(dictInitBlock, out var stringValues, tryGetValueBlock, dictionaryType, dictField)) + if (!ExtractStringValuesFromInitBlock(dictInitBlock, out var stringValues, tryGetValueBlock, dictionaryType, dictField)) return false; // match fourth block: TryGetValue on compiler-generated Dictionary if (tryGetValueBlock.IncomingEdgeCount != 2 || tryGetValueBlock.Instructions.Count != 2) @@ -220,7 +229,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms 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], out var dictField2, out _) && dictField2.Equals(dictField))) + 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; @@ -249,36 +258,41 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; } - bool MatchDictionaryFieldLoad(ILInstruction inst, out IField dictField, out IType dictionaryType) + bool MatchDictionaryFieldLoad(ILInstruction inst, Func typeMatcher, out IField dictField, out IType dictionaryType) { dictField = null; dictionaryType = null; return inst.MatchLdObj(out var dictionaryFieldLoad, out dictionaryType) && - IsStringToIntDictionary(dictionaryType) && + typeMatcher(dictionaryType) && dictionaryFieldLoad.MatchLdsFlda(out dictField) && - dictField.IsCompilerGeneratedOrIsInCompilerGeneratedClass(); + (dictField.IsCompilerGeneratedOrIsInCompilerGeneratedClass() || dictField.Name.StartsWith("$$method", StringComparison.Ordinal)); } - bool ExtractStringValuesFromDictionaryInitBlock(Block block, out List values, Block targetBlock, IType dictionaryType, IField dictionaryField) + bool ExtractStringValuesFromInitBlock(Block block, out List values, Block targetBlock, IType dictionaryType, IField dictionaryField) { values = null; if (!(block.Instructions[0].MatchStLoc(out var dictVar, out var newObjDict) && - newObjDict is NewObj newObj && newObj.Arguments.Count == 1 && newObj.Arguments[0].MatchLdcI4(out var valuesLength))) - return false; - if (block.Instructions.Count != valuesLength + 3) + newObjDict is NewObj newObj && newObj.Arguments.Count >= 1 && newObj.Arguments[0].MatchLdcI4(out var valuesLength))) return false; values = new List(valuesLength); - for (int i = 0; i < valuesLength; i++) { - if (!(block.Instructions[i + 1] is Call c && c.Method.Name == "Add" && c.Arguments.Count == 3 && - c.Arguments[0].MatchLdLoc(dictVar) && c.Arguments[1].MatchLdStr(out var value) && c.Arguments[2].MatchLdcI4(i))) - return false; + int i = 0; + while (i + 1 < block.Instructions.Count - 2 && MatchAddCall(block.Instructions[i + 1], dictVar, i, out var value)) { values.Add(value); + i++; } - if (!(block.Instructions[valuesLength + 1].MatchStObj(out var loadField, out var dictVarLoad, out var dictType) && + 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[valuesLength + 2].MatchBranch(targetBlock); + return block.Instructions[i + 2].MatchBranch(targetBlock); + } + + bool MatchAddCall(ILInstruction inst, ILVariable dictVar, int i, out string value) + { + value = null; + return inst is Call c && c.Method.Name == "Add" && c.Arguments.Count == 3 && + c.Arguments[0].MatchLdLoc(dictVar) && c.Arguments[1].MatchLdStr(out value) && + (c.Arguments[2].MatchLdcI4(i) || (c.Arguments[2].MatchBox(out var arg, out _) && arg.MatchLdcI4(i))); } bool IsStringToIntDictionary(IType dictionaryType) @@ -291,6 +305,108 @@ namespace ICSharpCode.Decompiler.IL.Transforms 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, int i, out SwitchInstruction inst) + { + inst = null; + // 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) { + var label = new Util.LongSet(switchInst.Sections.Count); + var possibleConflicts = switchInst.Sections.Where(sec => sec.Labels.Overlaps(label)).ToArray(); + if (possibleConflicts.Length > 1) + return false; + else if (possibleConflicts.Length == 1) + possibleConflicts[0].Labels = possibleConflicts[0].Labels.ExceptWith(label); + stringValues.Add(null); + sections.Add(new SwitchSection() { Labels = label, Body = new Branch(nullCaseBlock) }); + } + var stringToInt = new StringToInt(switchValue, stringValues.ToArray()); + inst = new SwitchInstruction(stringToInt); + inst.Sections.AddRange(sections); + return true; + } + bool MatchRoslynSwitchOnString(InstructionCollection instructions, int i, out SwitchInstruction inst) { inst = null; From 102729cfde299da70bfc278bbaad1f4d992da72f Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 8 Oct 2017 23:06:38 +0200 Subject: [PATCH 24/65] Put switch instructions into their own BlockContainer. This removes the need for "goto case" in the ILAst as we can just branch to the appropriate block. It also means we can support "break;" within switch using the "leave" instruction (otherwise we'd have to introduce yet another special kind of jump). --- .../CSharp/StatementBuilder.cs | 35 ++++++++- .../FlowAnalysis/DataFlowVisitor.cs | 12 +--- .../IL/ControlFlow/ConditionDetection.cs | 22 +----- .../IL/ControlFlow/LoopDetection.cs | 72 ++++++++++++++++--- ICSharpCode.Decompiler/IL/Instructions.cs | 49 ------------- ICSharpCode.Decompiler/IL/Instructions.tt | 3 - .../IL/Instructions/Block.cs | 3 + .../IL/Instructions/BlockContainer.cs | 2 +- .../IL/Instructions/SwitchInstruction.cs | 33 --------- 9 files changed, 102 insertions(+), 129 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs index 3ddb00fe9..12ba474aa 100644 --- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs @@ -98,11 +98,17 @@ namespace ICSharpCode.Decompiler.CSharp } return new CaseLabel(exprBuilder.ConvertConstantValue(new ConstantResolveResult(type, value), allowImplicitConversion: true)); } - + 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 + breakTarget = switchContainer; // 'break' within a switch would only leave the switch TranslatedExpression value; var strToInt = inst.Value as StringToInt; @@ -134,7 +140,28 @@ namespace ICSharpCode.Decompiler.CSharp ConvertSwitchSectionBody(astSection, section.Body); stmt.SwitchSections.Add(astSection); } - + if (switchContainer != null) { + // Translate any remaining blocks: + var lastSectionStatements = stmt.SwitchSections.Last().Statements; + foreach (var block in switchContainer.Blocks.Skip(1)) { + 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; return stmt; } @@ -471,6 +498,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 { return ConvertBlockContainer(container, false); } diff --git a/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs b/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs index 0632edc79..034e33798 100644 --- a/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs +++ b/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs @@ -624,17 +624,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis state = afterSections; DebugEndPoint(inst); } - - protected internal override void VisitGotoCase(GotoCase inst) - { - // Handling goto case here is tricky: - // We'll need a fixed-point iteration for SwitchInstruction similar to BlockContainer, - // and we'll need to handle GotoCase like Branch, including stuff like ProcessBranchesLeavingTryFinally(). - // Hopefully we won't need a data-flow analysis in the decompiler pipeline after 'goto case' instructions - // are already introduced. - throw new NotImplementedException(); - } - + protected internal override void VisitYieldReturn(YieldReturn inst) { DebugStartPoint(inst); diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs index 21da91022..954120bbf 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs @@ -65,10 +65,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow Debug.Assert(block.Instructions.Last().HasFlag(InstructionFlags.EndPointUnreachable)); ILInstruction exitInst = block.Instructions.Last(); - if (exitInst is SwitchInstruction switchInst) { - HandleSwitchInstruction(cfgNode, block, switchInst); - } - + // Previous-to-last instruction might have conditional control flow, // usually an IfInstruction with a branch: IfInstruction ifInst = block.Instructions.SecondToLastOrDefault() as IfInstruction; @@ -287,22 +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) - { - // 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; - } - } - // TODO: find a common exit point among the cases, and if possible inline that into this block. - 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/LoopDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs index b8086bee0..322543b05 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. /// @@ -412,13 +418,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 +458,48 @@ 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 potential loop 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); + + 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/Instructions.cs b/ICSharpCode.Decompiler/IL/Instructions.cs index ccb7bdedf..0ae8a6815 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions.cs @@ -63,8 +63,6 @@ namespace ICSharpCode.Decompiler.IL SwitchInstruction, /// Switch section within a switch statement SwitchSection, - /// Unconditional branch to switch section. goto case target; - GotoCase, /// Try-catch statement. TryCatch, /// Catch handler within a try-catch statement. @@ -1407,40 +1405,6 @@ namespace ICSharpCode.Decompiler.IL } } namespace ICSharpCode.Decompiler.IL -{ - /// Unconditional branch to switch section. goto case target; - public sealed partial class GotoCase : 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.VisitGotoCase(this); - } - public override T AcceptVisitor(ILVisitor visitor) - { - return visitor.VisitGotoCase(this); - } - public override T AcceptVisitor(ILVisitor visitor, C context) - { - return visitor.VisitGotoCase(this, context); - } - protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match) - { - var o = other as GotoCase; - return o != null && this.TargetSection == o.TargetSection; - } - } -} -namespace ICSharpCode.Decompiler.IL { /// Try-catch statement. public sealed partial class TryCatch : TryInstruction @@ -4573,10 +4537,6 @@ namespace ICSharpCode.Decompiler.IL { Default(inst); } - protected internal virtual void VisitGotoCase(GotoCase inst) - { - Default(inst); - } protected internal virtual void VisitTryCatch(TryCatch inst) { Default(inst); @@ -4867,10 +4827,6 @@ namespace ICSharpCode.Decompiler.IL { return Default(inst); } - protected internal virtual T VisitGotoCase(GotoCase inst) - { - return Default(inst); - } protected internal virtual T VisitTryCatch(TryCatch inst) { return Default(inst); @@ -5161,10 +5117,6 @@ namespace ICSharpCode.Decompiler.IL { return Default(inst, context); } - protected internal virtual T VisitGotoCase(GotoCase inst, C context) - { - return Default(inst, context); - } protected internal virtual T VisitTryCatch(TryCatch inst, C context) { return Default(inst, context); @@ -5399,7 +5351,6 @@ namespace ICSharpCode.Decompiler.IL "if.notnull", "switch", "switch.section", - "goto.case", "try.catch", "try.catch.handler", "try.finally", diff --git a/ICSharpCode.Decompiler/IL/Instructions.tt b/ICSharpCode.Decompiler/IL/Instructions.tt index c75172ddf..ca7590811 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.tt +++ b/ICSharpCode.Decompiler/IL/Instructions.tt @@ -98,9 +98,6 @@ CustomClassName("SwitchSection"), CustomChildren(new [] { new ChildInfo("body") }), CustomConstructor, CustomComputeFlags, CustomWriteTo, ResultType("Void"), MatchCondition("this.Labels.SetEquals(o.Labels) && this.HasNullLabel == o.HasNullLabel")), - new OpCode("goto.case", "Unconditional branch to switch section. goto case target;", - CustomClassName("GotoCase"), NoArguments, CustomConstructor, UnconditionalBranch, MayBranch, - MatchCondition("this.TargetSection == o.TargetSection")), new OpCode("try.catch", "Try-catch statement.", BaseClass("TryInstruction"), CustomConstructor, CustomComputeFlags, CustomWriteTo, MatchCondition("TryBlock.PerformMatch(o.TryBlock, ref match)"), 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..c48021ac7 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() diff --git a/ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs index 5de07f592..ae828983f 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs @@ -196,37 +196,4 @@ namespace ICSharpCode.Decompiler.IL body.WriteTo(output, options); } } - - /// - /// Unconditional branch to switch section. goto case target;/goto default; - /// - /// - /// Like normal branches, can trigger finally blocks. - /// - partial class GotoCase // : IBranchOrLeaveInstruction - { - public SwitchSection TargetSection { get; } - - public GotoCase(SwitchSection target) : base(OpCode.GotoCase) - { - this.TargetSection = target ?? throw new ArgumentNullException(nameof(target)); - } - - internal override void CheckInvariant(ILPhase phase) - { - base.CheckInvariant(phase); - var parentSwitch = this.Ancestors.OfType().First(); - Debug.Assert(parentSwitch == TargetSection.Parent); - } - - public override void WriteTo(ITextOutput output, ILAstWritingOptions options) - { - output.Write("goto.case "); - if (TargetSection.HasNullLabel) { - output.WriteReference("null", TargetSection, isLocal: true); - } else { - output.WriteReference(TargetSection.Labels.Values.First().ToString(), TargetSection, isLocal: true); - } - } - } } From 105ff744b336111c6214552c4451619b2e6e6d35 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 8 Oct 2017 23:55:08 +0200 Subject: [PATCH 25/65] Fix bug that could cause nodes reachable from the exit point to be moved into the loop/switch. --- .../IL/ControlFlow/LoopDetection.cs | 36 +++++++++++++++---- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs index 322543b05..2e49db446 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs @@ -101,7 +101,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow loop.Sort((a, b) => b.PostOrderNumber.CompareTo(a.PostOrderNumber)); Debug.Assert(loop[0] == h); foreach (var node in loop) { - node.Visited = false; // reset visited flag so that we can find outer loops Debug.Assert(h.Dominates(node) || !node.IsReachable, "The loop body must be dominated by the loop head"); } ConstructLoop(loop, exitPoint); @@ -207,6 +206,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow /// used by the post-dominance analysis. In this case, we fall back to the old heuristic algorithm. /// /// Precondition: Requires that a node is marked as visited iff it is contained in the loop. + /// Postcondition: loop is subset of nodes that are only reachable from each other, + /// exit-point (if non-null) is not in this set, + /// nodes are no longer marked as visited in the CFG /// void ExtendLoop(ControlFlowNode loopHead, List loop, out ControlFlowNode exitPoint) { @@ -215,9 +217,16 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow if (exitPoint != null) { // Either we are in case 1 and just picked an exit that maximizes the amount of code // outside the loop, or we are in case 2 and found an exit point via post-dominance. - var ep = exitPoint; - foreach (var node in TreeTraversal.PreOrder(loopHead, n => (n != ep) ? n.DominatorTreeChildren : null)) { - if (node != exitPoint && !node.Visited) { + + // We'll move all blocks dominated by the loop head into the loop, except for those that + // are reachable from the exit point. + MarkReachableWithinBlocksDominatedByLoop(exitPoint, loopHead); + foreach (var node in TreeTraversal.PreOrder(loopHead, n => n.DominatorTreeChildren)) { + if (node.Visited) { + // node.Visited means that the node is already part of the loop, + // or that it is reachable from the exit point. + node.Visited = false; + } else { loop.Add(node); } } @@ -226,9 +235,25 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow // Heuristically try to minimize the number of exit points // (but we'll always end up with more than 1 exit and will require goto statements). ExtendLoopHeuristic(loopHead, loop, loopHead); + // Reset Visited flag: + foreach (var node in loop) { + node.Visited = false; + } } } - + + void MarkReachableWithinBlocksDominatedByLoop(ControlFlowNode node, ControlFlowNode loopHead) + { + if (node.Visited) + return; // already visited + if (!loopHead.Dominates(node)) + return; + node.Visited = true; + foreach (var successor in node.Successors) { + MarkReachableWithinBlocksDominatedByLoop(successor, loopHead); + } + } + /// /// Finds a suitable single exit point for the specified loop. /// @@ -478,7 +503,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow 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"); } From 180f5341784b7455c2598561ade9952740395f5b Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 7 Oct 2017 23:20:16 +0200 Subject: [PATCH 26/65] Fix #778: Errors about decompiling local variables and default for switch-cases --- .../IL/Transforms/DelegateConstruction.cs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs index 130349971..f90a57aff 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs @@ -228,6 +228,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms public TransformDisplayClassUsages(IInstructionWithVariableOperand targetLoad, BlockContainer captureScope, List orphanedVariableInits) { + this.currentFunction = captureScope.Ancestors.OfType().First(); this.targetLoad = targetLoad; this.captureScope = captureScope; this.orphanedVariableInits = orphanedVariableInits; @@ -241,17 +242,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } - protected internal override void VisitILFunction(ILFunction function) - { - var old = currentFunction; - currentFunction = function; - try { - base.VisitILFunction(function); - } finally { - currentFunction = old; - } - } - protected internal override void VisitStLoc(StLoc inst) { base.VisitStLoc(inst); From 155b97b32e5d046421ae9bd3458f938e793d1298 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 8 Oct 2017 23:26:36 +0200 Subject: [PATCH 27/65] SwitchOnStringTransform: fix Roslyn transform --- .../IL/Transforms/SwitchOnStringTransform.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs index 96836b3db..0c8e6ef45 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs @@ -417,12 +417,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms var stringValues = new List<(int, string, Block)>(); int index = 0; - Block defaultBlock = null; + ILInstruction defaultBranch = null; foreach (var section in switchInst.Sections) { - if (!section.Body.MatchBranch(out Block target)) + if (!section.Body.MatchBranch(out Block target)) { + if (section.Body is Leave leave) { + defaultBranch = leave; + continue; + } return false; + } if (target.IncomingEdgeCount > 1) { - defaultBlock = target; + defaultBranch = new Branch(target); continue; } if (target.Instructions.Count != 2) @@ -445,8 +450,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms var value = new StringToInt(switchValue.Clone(), stringValues.Select(item => item.Item2).ToArray()); inst = new SwitchInstruction(value); inst.Sections.AddRange(stringValues.Select(section => new SwitchSection { Labels = new Util.LongSet(section.Item1), Body = new Branch(section.Item3) })); - inst.Sections.Add(new SwitchSection { Labels = defaultLabel, Body = new Branch(defaultBlock) }); - + inst.Sections.Add(new SwitchSection { Labels = defaultLabel, Body = defaultBranch }); return true; } From 01c6f414f91d524d129b36f43aa6f4b18900a265 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 9 Oct 2017 14:43:29 +0200 Subject: [PATCH 28/65] Implement goto default in StatementBuilder --- .../CSharp/StatementBuilder.cs | 48 +++++++++++++++++-- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs index 12ba474aa..37705e265 100644 --- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs @@ -83,7 +83,7 @@ namespace ICSharpCode.Decompiler.CSharp return new IfElseStatement(condition, trueStatement, falseStatement); } - CaseLabel CreateTypedCaseLabel(long i, IType type, string[] map = null) + ConstantResolveResult CreateTypedCaseLabel(long i, IType type, string[] map = null) { object value; if (type.IsKnownType(KnownTypeCode.Boolean)) { @@ -96,7 +96,7 @@ namespace ICSharpCode.Decompiler.CSharp } else { value = CSharpPrimitiveCast.Cast(ReflectionHelper.GetTypeCode(type), i, false); } - return new CaseLabel(exprBuilder.ConvertConstantValue(new ConstantResolveResult(type, value), allowImplicitConversion: true)); + return new ConstantResolveResult(type, value); } protected internal override Statement VisitSwitchInstruction(SwitchInstruction inst) @@ -109,6 +109,8 @@ namespace ICSharpCode.Decompiler.CSharp Debug.Assert(switchContainer.EntryPoint.IncomingEdgeCount == 1); var oldBreakTarget = breakTarget; 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; @@ -127,23 +129,53 @@ namespace ICSharpCode.Decompiler.CSharp } 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(); + // 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(section.Labels.Values.Select(i => CreateTypedCaseLabel(i, value.Type, strToInt?.Map))); + astSection.CaseLabels.AddRange(values.Select(label => new CaseLabel(exprBuilder.ConvertConstantValue(label, allowImplicitConversion: true)))); + } + switch (section.Body) { + case Branch br: + caseLabelMapping.Add(br.TargetBlock, firstValueResolveResult); + break; + default: + break; } - ConvertSwitchSectionBody(astSection, section.Body); + translationDictionary.Add(section, astSection); stmt.SwitchSections.Add(astSection); } + foreach (var section in inst.Sections) { + var astSection = translationDictionary[section]; + switch (section.Body) { + case Branch br: + ConvertSwitchSectionBody(astSection, br.TargetBlock); + break; + 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); @@ -163,6 +195,7 @@ namespace ICSharpCode.Decompiler.CSharp } breakTarget = oldBreakTarget; + caseLabelMapping = oldCaseLabelMapping; return stmt; } @@ -185,6 +218,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) { @@ -192,6 +227,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); } From 4c44f05af169c1e1304c37b3bc53508d5da8b65a Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Tue, 10 Oct 2017 13:06:13 +0200 Subject: [PATCH 29/65] Fix bug in StatementBuilder: do not inline blocks into switch case that are outside of the switchContainer. --- ICSharpCode.Decompiler/CSharp/StatementBuilder.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs index cc5dd1d43..8cf20cfb8 100644 --- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs @@ -152,7 +152,9 @@ namespace ICSharpCode.Decompiler.CSharp } switch (section.Body) { case Branch br: - caseLabelMapping.Add(br.TargetBlock, firstValueResolveResult); + // we can only inline the block, if all branches are in the switchContainer. + if (br.TargetBlock.IsDescendantOf(switchContainer)) + caseLabelMapping.Add(br.TargetBlock, firstValueResolveResult); break; default: break; @@ -164,7 +166,11 @@ namespace ICSharpCode.Decompiler.CSharp var astSection = translationDictionary[section]; switch (section.Body) { case Branch br: - ConvertSwitchSectionBody(astSection, br.TargetBlock); + // we can only inline the block, if all branches are in the switchContainer. + if (br.TargetBlock.IsDescendantOf(switchContainer)) + ConvertSwitchSectionBody(astSection, br.TargetBlock); + else + ConvertSwitchSectionBody(astSection, section.Body); break; default: ConvertSwitchSectionBody(astSection, section.Body); From 596fca2b373b60a59d4fb12b638037bb3eeb490b Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Tue, 10 Oct 2017 16:35:06 +0200 Subject: [PATCH 30/65] Fix unit tests --- .../CSharp/StatementBuilder.cs | 4 ++-- .../ConnectMethodDecompiler.cs | 21 ++++++++++++------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs index 8cf20cfb8..56fdfb6e9 100644 --- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs @@ -153,7 +153,7 @@ namespace ICSharpCode.Decompiler.CSharp switch (section.Body) { case Branch br: // we can only inline the block, if all branches are in the switchContainer. - if (br.TargetBlock.IsDescendantOf(switchContainer)) + if (br.TargetBlock.Parent == switchContainer && switchContainer.Descendants.OfType().Where(b => b.TargetBlock == br.TargetBlock).All(b => BlockContainer.FindClosestContainer(b) == switchContainer)) caseLabelMapping.Add(br.TargetBlock, firstValueResolveResult); break; default: @@ -167,7 +167,7 @@ namespace ICSharpCode.Decompiler.CSharp switch (section.Body) { case Branch br: // we can only inline the block, if all branches are in the switchContainer. - if (br.TargetBlock.IsDescendantOf(switchContainer)) + if (br.TargetBlock.Parent == switchContainer && switchContainer.Descendants.OfType().Where(b => b.TargetBlock == br.TargetBlock).All(b => BlockContainer.FindClosestContainer(b) == switchContainer)) ConvertSwitchSectionBody(astSection, br.TargetBlock); else ConvertSwitchSectionBody(astSection, section.Body); diff --git a/ILSpy.BamlDecompiler/ConnectMethodDecompiler.cs b/ILSpy.BamlDecompiler/ConnectMethodDecompiler.cs index a28f63d7b..39ddba883 100644 --- a/ILSpy.BamlDecompiler/ConnectMethodDecompiler.cs +++ b/ILSpy.BamlDecompiler/ConnectMethodDecompiler.cs @@ -66,7 +66,7 @@ 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) { @@ -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(); } From 403a79099a579c9092bcdfcf48c2db187b8d56c5 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Tue, 10 Oct 2017 22:39:57 +0200 Subject: [PATCH 31/65] Introduce BlockContainer.FindClosestSwitchContainer --- ICSharpCode.Decompiler/CSharp/StatementBuilder.cs | 4 ++-- .../IL/Instructions/BlockContainer.cs | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs index 56fdfb6e9..91ca8d309 100644 --- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs @@ -153,7 +153,7 @@ namespace ICSharpCode.Decompiler.CSharp 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.FindClosestContainer(b) == 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: @@ -167,7 +167,7 @@ namespace ICSharpCode.Decompiler.CSharp 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.FindClosestContainer(b) == 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); diff --git a/ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs b/ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs index c48021ac7..b70056841 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs @@ -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; + } } } From fcbfada2e0eb9e57c1a78bfd06bbc8462ffa0e46 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Wed, 11 Oct 2017 11:31:06 +0200 Subject: [PATCH 32/65] Add legacy switch-on-nullable transform --- .../CSharp/CSharpDecompiler.cs | 1 + .../ICSharpCode.Decompiler.csproj | 1 + .../Transforms/SwitchOnNullableTransform.cs | 117 ++++++++++++++++++ 3 files changed, 119 insertions(+) create mode 100644 ICSharpCode.Decompiler/IL/Transforms/SwitchOnNullableTransform.cs diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 647dcdaf5..355464e64 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -83,6 +83,7 @@ namespace ICSharpCode.Decompiler.CSharp new SplitVariables(), // split variables once again, because the stobj(ldloca V, ...) may open up new replacements new SwitchDetection(), new SwitchOnStringTransform(), + new SwitchOnNullableTransform(), new BlockILTransform { // per-block transforms PostOrderTransforms = { // Even though it's a post-order block-transform as most other transforms, diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 534af2cbe..7fee5ff0d 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -277,6 +277,7 @@ + diff --git a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnNullableTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnNullableTransform.cs new file mode 100644 index 000000000..30005e06b --- /dev/null +++ b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnNullableTransform.cs @@ -0,0 +1,117 @@ +// 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 (!changed) continue; + SwitchDetection.SimplifySwitchInstruction(block); + if (block.Parent is BlockContainer container) + changedContainers.Add(container); + } + + foreach (var container in changedContainers) + container.SortBlocks(deleteUnreachableBlocks: true); + } + + 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 (!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 (!(getValueOrDefault is Call getValueOrDefaultCall) || getValueOrDefaultCall.Method.FullName != "System.Nullable.GetValueOrDefault" || + getValueOrDefaultCall.Method.DeclaringType.TypeParameterCount != 1) + return false; + if (!(getHasValue is Call getHasValueCall) || !getHasValueCall.Method.IsAccessor || getHasValueCall.Method.FullName != "System.Nullable.get_HasValue" || + getHasValueCall.Method.DeclaringType.TypeParameterCount != 1) + return false; + if (getHasValueCall.Arguments.Count != 1 || getValueOrDefaultCall.Arguments.Count != 1) + return false; + if (!getHasValueCall.Arguments[0].MatchLdLoc(tmp) || !getValueOrDefaultCall.Arguments[0].MatchLdLoc(tmp)) + return false; + // match second block: switchBlock + // switch (ldloc swtichVariable) { + // 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 = new SwitchInstruction(new LdLoc(switchValueVar)); + newSwitch.IsLifted = true; + SwitchSection defaultSection = null; + foreach (var section in switchInst.Sections) { + if (defaultSection == null || section.Labels.Count() >= defaultSection.Labels.Count()) + defaultSection = section; + newSwitch.Sections.Add(section); + } + if (defaultSection.Body.MatchBranch(out var defaultBlock) && defaultBlock == nullCaseBlock) + defaultSection.HasNullLabel = true; + else { + newSwitch.Sections.Add(new SwitchSection { Body = new Branch(nullCaseBlock), HasNullLabel = true }); + } + return true; + } + } +} From b3c7f53c8614a455cdc417690c85ccd0d7815830 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Wed, 11 Oct 2017 11:40:54 +0200 Subject: [PATCH 33/65] Add nullable handling to StatementBuilder.CreateTypedCaseLabel --- ICSharpCode.Decompiler/CSharp/StatementBuilder.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs index 91ca8d309..8be341e76 100644 --- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs @@ -93,6 +93,9 @@ namespace ICSharpCode.Decompiler.CSharp } 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); } From 51d13bfefedeeddf53cdcb00c97e0c6780ec045f Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Wed, 11 Oct 2017 12:28:35 +0200 Subject: [PATCH 34/65] Add transform for Roslyn Switch-On-Nullable --- .../TestCases/Pretty/Switch.cs | 200 +++++----- .../TestCases/Pretty/Switch.il | 360 +++++++++++++++++- .../TestCases/Pretty/Switch.opt.il | 288 +++++++++++++- .../TestCases/Pretty/Switch.opt.roslyn.il | 255 ++++++++++++- .../TestCases/Pretty/Switch.roslyn.il | 358 ++++++++++++++++- .../Transforms/SwitchOnNullableTransform.cs | 60 +++ 6 files changed, 1373 insertions(+), 148 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs index e5f42c278..fcf01f6d9 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs @@ -82,83 +82,83 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } - //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 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 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 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 SwitchOverNullableIntNoNullCase(int? i) - //{ - // switch (i) { - // case 0: { - // return "zero"; - // } - // case 5: { - // return "five"; - // } - // case 10: { - // return "ten"; - // } - // default: { - // return "other"; - // } - // } - //} + 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 string SwitchOverNullableIntNoNullCaseShifted(int? i) + { + switch (i + 5) { + case 0: { + return "zero"; + } + case 5: { + return "five"; + } + case 10: { + return "ten"; + } + default: { + return "other"; + } + } + } public static string ShortSwitchOverString(string text) { @@ -302,32 +302,32 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } - //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; - // } - // } - //} + 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; + } + } + } private static SetProperty[] GetProperties() { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il index eed5597a0..c6af28ac4 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il @@ -1,6 +1,6 @@ -// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.17929 -// Copyright (c) Microsoft Corporation. All rights reserved. +// Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0 +// Copyright (c) Microsoft Corporation. Alle Rechte vorbehalten. @@ -10,7 +10,7 @@ .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } -.assembly gn0oqkcb +.assembly nyniamwr { .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. @@ -20,15 +20,15 @@ .hash algorithm 0x00008004 .ver 0:0:0:0 } -.module gn0oqkcb.dll -// MVID: {D3E1C722-15E3-49C8-B86B-96413DA7BEEE} +.module nyniamwr.dll +// MVID: {95C99B41-CBA3-42E4-A4DE-27E535671AB2} .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: 0x00C80000 +// Image base: 0x03080000 // =============== CLASS MEMBERS DECLARATION =================== @@ -214,6 +214,268 @@ IL_00df: ret } // end of method Switch::SparseIntegerSwitch + .method public hidebysig static string + SwitchOverNullableInt(valuetype [mscorlib]System.Nullable`1 i) cil managed + { + // Code size 79 (0x4f) + .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_0029 + + IL_0015: ldloc.1 + IL_0016: ldc.i4.5 + IL_0017: beq.s IL_0032 + + IL_0019: ldloc.1 + IL_001a: ldc.i4.s 10 + IL_001c: beq.s IL_003b + + IL_001e: br.s IL_0044 + + IL_0020: nop + IL_0021: ldstr "null" + IL_0026: stloc.0 + IL_0027: br.s IL_004d + + IL_0029: nop + IL_002a: ldstr "zero" + IL_002f: stloc.0 + IL_0030: br.s IL_004d + + IL_0032: nop + IL_0033: ldstr "five" + IL_0038: stloc.0 + IL_0039: br.s IL_004d + + IL_003b: nop + IL_003c: ldstr "ten" + IL_0041: stloc.0 + IL_0042: br.s IL_004d + + IL_0044: nop + IL_0045: ldstr "large" + IL_004a: stloc.0 + IL_004b: br.s IL_004d + + IL_004d: ldloc.0 + IL_004e: ret + } // end of method Switch::SwitchOverNullableInt + + .method public hidebysig static string + SwitchOverNullableIntShifted(valuetype [mscorlib]System.Nullable`1 i) cil managed + { + // Code size 117 (0x75) + .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_004f + + IL_003b: ldloc.3 + IL_003c: ldc.i4.5 + IL_003d: beq.s IL_0058 + + IL_003f: ldloc.3 + IL_0040: ldc.i4.s 10 + IL_0042: beq.s IL_0061 + + IL_0044: br.s IL_006a + + IL_0046: nop + IL_0047: ldstr "null" + IL_004c: stloc.0 + IL_004d: br.s IL_0073 + + IL_004f: nop + IL_0050: ldstr "zero" + IL_0055: stloc.0 + IL_0056: br.s IL_0073 + + IL_0058: nop + IL_0059: ldstr "five" + IL_005e: stloc.0 + IL_005f: br.s IL_0073 + + IL_0061: nop + IL_0062: ldstr "ten" + IL_0067: stloc.0 + IL_0068: br.s IL_0073 + + IL_006a: nop + IL_006b: ldstr "large" + IL_0070: stloc.0 + IL_0071: br.s IL_0073 + + IL_0073: ldloc.0 + IL_0074: ret + } // end of method Switch::SwitchOverNullableIntShifted + + .method public hidebysig static string + SwitchOverNullableIntNoNullCase(valuetype [mscorlib]System.Nullable`1 i) cil managed + { + // Code size 70 (0x46) + .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_003b + + 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_0029 + + IL_0019: ldloc.1 + IL_001a: ldc.i4.s 10 + IL_001c: beq.s IL_0032 + + IL_001e: br.s IL_003b + + IL_0020: nop + IL_0021: ldstr "zero" + IL_0026: stloc.0 + IL_0027: br.s IL_0044 + + IL_0029: nop + IL_002a: ldstr "five" + IL_002f: stloc.0 + IL_0030: br.s IL_0044 + + IL_0032: nop + IL_0033: ldstr "ten" + IL_0038: stloc.0 + IL_0039: br.s IL_0044 + + IL_003b: nop + IL_003c: ldstr "other" + IL_0041: stloc.0 + IL_0042: br.s IL_0044 + + IL_0044: ldloc.0 + IL_0045: ret + } // end of method Switch::SwitchOverNullableIntNoNullCase + + .method public hidebysig static string + SwitchOverNullableIntNoNullCaseShifted(valuetype [mscorlib]System.Nullable`1 i) cil managed + { + // Code size 108 (0x6c) + .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_0061 + + 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_004f + + IL_003f: ldloc.3 + IL_0040: ldc.i4.s 10 + IL_0042: beq.s IL_0058 + + IL_0044: br.s IL_0061 + + IL_0046: nop + IL_0047: ldstr "zero" + IL_004c: stloc.0 + IL_004d: br.s IL_006a + + IL_004f: nop + IL_0050: ldstr "five" + IL_0055: stloc.0 + IL_0056: br.s IL_006a + + IL_0058: nop + IL_0059: ldstr "ten" + IL_005e: stloc.0 + IL_005f: br.s IL_006a + + IL_0061: nop + IL_0062: ldstr "other" + IL_0067: stloc.0 + IL_0068: br.s IL_006a + + IL_006a: ldloc.0 + IL_006b: ret + } // end of method Switch::SwitchOverNullableIntNoNullCaseShifted + .method public hidebysig static string ShortSwitchOverString(string text) cil managed { @@ -298,7 +560,7 @@ IL_0015: brfalse IL_00ef IL_001a: volatile. - IL_001c: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{D3E1C722-15E3-49C8-B86B-96413DA7BEEE}'::'$$method0x6000003-1' + IL_001c: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{95C99B41-CBA3-42E4-A4DE-27E535671AB2}'::'$$method0x6000007-1' IL_0021: brtrue.s IL_0084 IL_0023: ldc.i4.7 @@ -339,9 +601,9 @@ 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 '{D3E1C722-15E3-49C8-B86B-96413DA7BEEE}'::'$$method0x6000003-1' + IL_007f: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{95C99B41-CBA3-42E4-A4DE-27E535671AB2}'::'$$method0x6000007-1' IL_0084: volatile. - IL_0086: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{D3E1C722-15E3-49C8-B86B-96413DA7BEEE}'::'$$method0x6000003-1' + IL_0086: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{95C99B41-CBA3-42E4-A4DE-27E535671AB2}'::'$$method0x6000007-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, @@ -424,7 +686,7 @@ IL_0015: brfalse IL_0165 IL_001a: volatile. - IL_001c: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{D3E1C722-15E3-49C8-B86B-96413DA7BEEE}'::'$$method0x6000004-1' + IL_001c: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{95C99B41-CBA3-42E4-A4DE-27E535671AB2}'::'$$method0x6000008-1' IL_0021: brtrue IL_00ba IL_0026: ldc.i4.s 11 @@ -485,9 +747,9 @@ IL_00ae: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, !1) IL_00b3: volatile. - IL_00b5: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{D3E1C722-15E3-49C8-B86B-96413DA7BEEE}'::'$$method0x6000004-1' + IL_00b5: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{95C99B41-CBA3-42E4-A4DE-27E535671AB2}'::'$$method0x6000008-1' IL_00ba: volatile. - IL_00bc: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{D3E1C722-15E3-49C8-B86B-96413DA7BEEE}'::'$$method0x6000004-1' + IL_00bc: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{95C99B41-CBA3-42E4-A4DE-27E535671AB2}'::'$$method0x6000008-1' IL_00c1: ldloc.2 IL_00c2: ldloca.s V_3 IL_00c4: call instance bool class [mscorlib]System.Collections.Generic.Dictionary`2::TryGetValue(!0, @@ -689,6 +951,64 @@ IL_0091: ret } // end of method Switch::SwitchInLoop + .method public hidebysig static void SwitchWithGoto(int32 i) cil managed + { + // Code size 122 (0x7a) + .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_0041, + IL_004f, + IL_005d) + IL_0031: br.s IL_006b + + IL_0033: nop + IL_0034: ldstr "one" + IL_0039: call void [mscorlib]System.Console::WriteLine(string) + IL_003e: nop + IL_003f: br.s IL_006b + + IL_0041: nop + IL_0042: ldstr "two" + IL_0047: call void [mscorlib]System.Console::WriteLine(string) + IL_004c: nop + IL_004d: br.s IL_004f + + IL_004f: nop + IL_0050: ldstr "three" + IL_0055: call void [mscorlib]System.Console::WriteLine(string) + IL_005a: nop + IL_005b: br.s IL_0079 + + IL_005d: nop + IL_005e: ldstr "four" + IL_0063: call void [mscorlib]System.Console::WriteLine(string) + IL_0068: nop + IL_0069: br.s IL_0079 + + IL_006b: nop + IL_006c: ldstr "default" + IL_0071: call void [mscorlib]System.Console::WriteLine(string) + IL_0076: nop + IL_0077: br.s IL_0079 + + IL_0079: ret + } // end of method Switch::SwitchWithGoto + .method private hidebysig static class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty[] GetProperties() cil managed { @@ -744,7 +1064,7 @@ IL_0034: brfalse IL_012d IL_0039: volatile. - IL_003b: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{D3E1C722-15E3-49C8-B86B-96413DA7BEEE}'::'$$method0x6000008-1' + IL_003b: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{95C99B41-CBA3-42E4-A4DE-27E535671AB2}'::'$$method0x600000d-1' IL_0040: brtrue.s IL_0097 IL_0042: ldc.i4.6 @@ -780,9 +1100,9 @@ IL_008b: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, !1) IL_0090: volatile. - IL_0092: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{D3E1C722-15E3-49C8-B86B-96413DA7BEEE}'::'$$method0x6000008-1' + IL_0092: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{95C99B41-CBA3-42E4-A4DE-27E535671AB2}'::'$$method0x600000d-1' IL_0097: volatile. - IL_0099: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{D3E1C722-15E3-49C8-B86B-96413DA7BEEE}'::'$$method0x6000008-1' + IL_0099: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{95C99B41-CBA3-42E4-A4DE-27E535671AB2}'::'$$method0x600000d-1' IL_009e: ldloc.s V_6 IL_00a0: ldloca.s V_7 IL_00a2: call instance bool class [mscorlib]System.Collections.Generic.Dictionary`2::TryGetValue(!0, @@ -876,17 +1196,17 @@ } // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch -.class private auto ansi '{D3E1C722-15E3-49C8-B86B-96413DA7BEEE}' +.class private auto ansi '{95C99B41-CBA3-42E4-A4DE-27E535671AB2}' 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 '$$method0x6000003-1' - .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000004-1' + .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000007-1' .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000008-1' -} // end of class '{D3E1C722-15E3-49C8-B86B-96413DA7BEEE}' + .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x600000d-1' +} // end of class '{95C99B41-CBA3-42E4-A4DE-27E535671AB2}' // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** -// WARNING: Created Win32 resource file ../../../TestCases/Pretty\Switch.res +// 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 index becdf49a9..e09dd767c 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il @@ -1,6 +1,6 @@ -// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.17929 -// Copyright (c) Microsoft Corporation. All rights reserved. +// Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0 +// Copyright (c) Microsoft Corporation. Alle Rechte vorbehalten. @@ -10,7 +10,7 @@ .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } -.assembly zlaei1fn +.assembly f3gworj3 { .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. @@ -20,15 +20,15 @@ .hash algorithm 0x00008004 .ver 0:0:0:0 } -.module zlaei1fn.dll -// MVID: {64CCBA80-944A-4F77-9230-24B174DEE22A} +.module f3gworj3.dll +// MVID: {6837A40E-7A00-4F01-B2D7-DE0001F70EDF} .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: 0x00680000 +// Image base: 0x00780000 // =============== CLASS MEMBERS DECLARATION =================== @@ -176,6 +176,212 @@ 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 + 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 + 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 string ShortSwitchOverString(string text) cil managed { @@ -243,7 +449,7 @@ IL_0013: brfalse IL_00db IL_0018: volatile. - IL_001a: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{64CCBA80-944A-4F77-9230-24B174DEE22A}'::'$$method0x6000003-1' + IL_001a: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{6837A40E-7A00-4F01-B2D7-DE0001F70EDF}'::'$$method0x6000007-1' IL_001f: brtrue.s IL_0082 IL_0021: ldc.i4.7 @@ -284,9 +490,9 @@ 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 '{64CCBA80-944A-4F77-9230-24B174DEE22A}'::'$$method0x6000003-1' + IL_007d: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{6837A40E-7A00-4F01-B2D7-DE0001F70EDF}'::'$$method0x6000007-1' IL_0082: volatile. - IL_0084: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{64CCBA80-944A-4F77-9230-24B174DEE22A}'::'$$method0x6000003-1' + IL_0084: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{6837A40E-7A00-4F01-B2D7-DE0001F70EDF}'::'$$method0x6000007-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, @@ -347,7 +553,7 @@ IL_0013: brfalse IL_013f IL_0018: volatile. - IL_001a: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{64CCBA80-944A-4F77-9230-24B174DEE22A}'::'$$method0x6000004-1' + IL_001a: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{6837A40E-7A00-4F01-B2D7-DE0001F70EDF}'::'$$method0x6000008-1' IL_001f: brtrue IL_00b8 IL_0024: ldc.i4.s 11 @@ -408,9 +614,9 @@ 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 '{64CCBA80-944A-4F77-9230-24B174DEE22A}'::'$$method0x6000004-1' + IL_00b3: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{6837A40E-7A00-4F01-B2D7-DE0001F70EDF}'::'$$method0x6000008-1' IL_00b8: volatile. - IL_00ba: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{64CCBA80-944A-4F77-9230-24B174DEE22A}'::'$$method0x6000004-1' + IL_00ba: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{6837A40E-7A00-4F01-B2D7-DE0001F70EDF}'::'$$method0x6000008-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, @@ -551,6 +757,48 @@ IL_007a: br.s IL_0015 } // end of method Switch::SwitchInLoop + .method public hidebysig static void SwitchWithGoto(int32 i) cil managed + { + // Code size 104 (0x68) + .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_0052) + IL_002f: br.s IL_005d + + IL_0031: ldstr "one" + IL_0036: call void [mscorlib]System.Console::WriteLine(string) + IL_003b: br.s IL_005d + + 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: ret + + IL_0052: ldstr "four" + IL_0057: call void [mscorlib]System.Console::WriteLine(string) + IL_005c: ret + + IL_005d: ldstr "default" + IL_0062: call void [mscorlib]System.Console::WriteLine(string) + IL_0067: ret + } // end of method Switch::SwitchWithGoto + .method private hidebysig static class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty[] GetProperties() cil managed { @@ -597,7 +845,7 @@ IL_0031: brfalse IL_0119 IL_0036: volatile. - IL_0038: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{64CCBA80-944A-4F77-9230-24B174DEE22A}'::'$$method0x6000008-1' + IL_0038: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{6837A40E-7A00-4F01-B2D7-DE0001F70EDF}'::'$$method0x600000d-1' IL_003d: brtrue.s IL_0094 IL_003f: ldc.i4.6 @@ -633,9 +881,9 @@ IL_0088: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, !1) IL_008d: volatile. - IL_008f: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{64CCBA80-944A-4F77-9230-24B174DEE22A}'::'$$method0x6000008-1' + IL_008f: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{6837A40E-7A00-4F01-B2D7-DE0001F70EDF}'::'$$method0x600000d-1' IL_0094: volatile. - IL_0096: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{64CCBA80-944A-4F77-9230-24B174DEE22A}'::'$$method0x6000008-1' + IL_0096: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{6837A40E-7A00-4F01-B2D7-DE0001F70EDF}'::'$$method0x600000d-1' IL_009b: ldloc.s V_6 IL_009d: ldloca.s V_7 IL_009f: call instance bool class [mscorlib]System.Collections.Generic.Dictionary`2::TryGetValue(!0, @@ -707,17 +955,17 @@ } // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch -.class private auto ansi '{64CCBA80-944A-4F77-9230-24B174DEE22A}' +.class private auto ansi '{6837A40E-7A00-4F01-B2D7-DE0001F70EDF}' 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 '$$method0x6000003-1' - .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000004-1' + .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000007-1' .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000008-1' -} // end of class '{64CCBA80-944A-4F77-9230-24B174DEE22A}' + .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x600000d-1' +} // end of class '{6837A40E-7A00-4F01-B2D7-DE0001F70EDF}' // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** -// WARNING: Created Win32 resource file ../../../TestCases/Pretty\Switch.opt.res +// 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 index 48993c1fe..bd0488bb3 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il @@ -1,6 +1,6 @@ -// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.17929 -// Copyright (c) Microsoft Corporation. All rights reserved. +// Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0 +// Copyright (c) Microsoft Corporation. Alle Rechte vorbehalten. @@ -25,14 +25,14 @@ .ver 0:0:0:0 } .module Switch.dll -// MVID: {25FC064E-F764-4556-A3C7-F6570E457CDD} +// MVID: {04DBCBA6-8175-41CD-8917-9428C9765986} .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: 0x00B30000 +// Image base: 0x00830000 // =============== CLASS MEMBERS DECLARATION =================== @@ -185,6 +185,214 @@ 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 + 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 + 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 string ShortSwitchOverString(string text) cil managed { @@ -659,6 +867,45 @@ IL_0078: br.s IL_0015 } // end of method Switch::SwitchInLoop + .method public hidebysig static void SwitchWithGoto(int32 i) cil managed + { + // Code size 102 (0x66) + .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_0050) + IL_002d: br.s IL_005b + + IL_002f: ldstr "one" + IL_0034: call void [mscorlib]System.Console::WriteLine(string) + IL_0039: br.s IL_005b + + 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: ret + + IL_0050: ldstr "four" + IL_0055: call void [mscorlib]System.Console::WriteLine(string) + IL_005a: ret + + IL_005b: ldstr "default" + IL_0060: call void [mscorlib]System.Console::WriteLine(string) + IL_0065: ret + } // end of method Switch::SwitchWithGoto + .method private hidebysig static class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty[] GetProperties() cil managed { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il index 4da071b46..38331d470 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il @@ -1,6 +1,6 @@ -// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.17929 -// Copyright (c) Microsoft Corporation. All rights reserved. +// Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0 +// Copyright (c) Microsoft Corporation. Alle Rechte vorbehalten. @@ -25,14 +25,14 @@ .ver 0:0:0:0 } .module Switch.dll -// MVID: {134EA2E4-FA5A-4D44-A0FD-C4E5A18E39B1} +// MVID: {87DEBC09-DFAD-437D-9221-109E2117A07A} .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: 0x00C40000 +// Image base: 0x02960000 // =============== CLASS MEMBERS DECLARATION =================== @@ -229,6 +229,298 @@ IL_00ed: ret } // end of method Switch::SparseIntegerSwitch + .method public hidebysig static string + SwitchOverNullableInt(valuetype [mscorlib]System.Nullable`1 i) cil managed + { + // Code size 87 (0x57) + .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_0031 + + IL_0019: br.s IL_001b + + IL_001b: ldloc.2 + IL_001c: ldc.i4.5 + IL_001d: beq.s IL_003a + + IL_001f: br.s IL_0021 + + IL_0021: ldloc.2 + IL_0022: ldc.i4.s 10 + IL_0024: beq.s IL_0043 + + IL_0026: br.s IL_004c + + IL_0028: nop + IL_0029: ldstr "null" + IL_002e: stloc.3 + IL_002f: br.s IL_0055 + + IL_0031: nop + IL_0032: ldstr "zero" + IL_0037: stloc.3 + IL_0038: br.s IL_0055 + + IL_003a: nop + IL_003b: ldstr "five" + IL_0040: stloc.3 + IL_0041: br.s IL_0055 + + IL_0043: nop + IL_0044: ldstr "ten" + IL_0049: stloc.3 + IL_004a: br.s IL_0055 + + IL_004c: nop + IL_004d: ldstr "large" + IL_0052: stloc.3 + IL_0053: br.s IL_0055 + + IL_0055: ldloc.3 + IL_0056: ret + } // end of method Switch::SwitchOverNullableInt + + .method public hidebysig static string + SwitchOverNullableIntShifted(valuetype [mscorlib]System.Nullable`1 i) cil managed + { + // Code size 132 (0x84) + .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_0059 + + IL_003e: br.s IL_0040 + + IL_0040: ldloc.s V_4 + IL_0042: ldc.i4.5 + IL_0043: beq.s IL_0063 + + IL_0045: br.s IL_0047 + + IL_0047: ldloc.s V_4 + IL_0049: ldc.i4.s 10 + IL_004b: beq.s IL_006d + + IL_004d: br.s IL_0077 + + IL_004f: nop + IL_0050: ldstr "null" + IL_0055: stloc.s V_5 + IL_0057: br.s IL_0081 + + IL_0059: nop + IL_005a: ldstr "zero" + IL_005f: stloc.s V_5 + IL_0061: br.s IL_0081 + + IL_0063: nop + IL_0064: ldstr "five" + IL_0069: stloc.s V_5 + IL_006b: br.s IL_0081 + + IL_006d: nop + IL_006e: ldstr "ten" + IL_0073: stloc.s V_5 + IL_0075: br.s IL_0081 + + IL_0077: nop + IL_0078: ldstr "large" + IL_007d: stloc.s V_5 + IL_007f: br.s IL_0081 + + IL_0081: ldloc.s V_5 + IL_0083: ret + } // end of method Switch::SwitchOverNullableIntShifted + + .method public hidebysig static string + SwitchOverNullableIntNoNullCase(valuetype [mscorlib]System.Nullable`1 i) cil managed + { + // Code size 78 (0x4e) + .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_0043 + + 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_0031 + + IL_001f: br.s IL_0021 + + IL_0021: ldloc.2 + IL_0022: ldc.i4.s 10 + IL_0024: beq.s IL_003a + + IL_0026: br.s IL_0043 + + IL_0028: nop + IL_0029: ldstr "zero" + IL_002e: stloc.3 + IL_002f: br.s IL_004c + + IL_0031: nop + IL_0032: ldstr "five" + IL_0037: stloc.3 + IL_0038: br.s IL_004c + + IL_003a: nop + IL_003b: ldstr "ten" + IL_0040: stloc.3 + IL_0041: br.s IL_004c + + IL_0043: nop + IL_0044: ldstr "other" + IL_0049: stloc.3 + IL_004a: br.s IL_004c + + IL_004c: ldloc.3 + IL_004d: ret + } // end of method Switch::SwitchOverNullableIntNoNullCase + + .method public hidebysig static string + SwitchOverNullableIntNoNullCaseShifted(valuetype [mscorlib]System.Nullable`1 i) cil managed + { + // Code size 122 (0x7a) + .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_006d + + 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_0059 + + IL_0045: br.s IL_0047 + + IL_0047: ldloc.s V_4 + IL_0049: ldc.i4.s 10 + IL_004b: beq.s IL_0063 + + IL_004d: br.s IL_006d + + IL_004f: nop + IL_0050: ldstr "zero" + IL_0055: stloc.s V_5 + IL_0057: br.s IL_0077 + + IL_0059: nop + IL_005a: ldstr "five" + IL_005f: stloc.s V_5 + IL_0061: br.s IL_0077 + + IL_0063: nop + IL_0064: ldstr "ten" + IL_0069: stloc.s V_5 + IL_006b: br.s IL_0077 + + IL_006d: nop + IL_006e: ldstr "other" + IL_0073: stloc.s V_5 + IL_0075: br.s IL_0077 + + IL_0077: ldloc.s V_5 + IL_0079: ret + } // end of method Switch::SwitchOverNullableIntNoNullCaseShifted + .method public hidebysig static string ShortSwitchOverString(string text) cil managed { @@ -844,6 +1136,64 @@ IL_0091: ret } // end of method Switch::SwitchInLoop + .method public hidebysig static void SwitchWithGoto(int32 i) cil managed + { + // Code size 122 (0x7a) + .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_0041, + IL_004f, + IL_005d) + IL_0031: br.s IL_006b + + IL_0033: nop + IL_0034: ldstr "one" + IL_0039: call void [mscorlib]System.Console::WriteLine(string) + IL_003e: nop + IL_003f: br.s IL_006b + + IL_0041: nop + IL_0042: ldstr "two" + IL_0047: call void [mscorlib]System.Console::WriteLine(string) + IL_004c: nop + IL_004d: br.s IL_004f + + IL_004f: nop + IL_0050: ldstr "three" + IL_0055: call void [mscorlib]System.Console::WriteLine(string) + IL_005a: nop + IL_005b: br.s IL_0079 + + IL_005d: nop + IL_005e: ldstr "four" + IL_0063: call void [mscorlib]System.Console::WriteLine(string) + IL_0068: nop + IL_0069: br.s IL_0079 + + IL_006b: nop + IL_006c: ldstr "default" + IL_0071: call void [mscorlib]System.Console::WriteLine(string) + IL_0076: nop + IL_0077: br.s IL_0079 + + IL_0079: ret + } // end of method Switch::SwitchWithGoto + .method private hidebysig static class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty[] GetProperties() cil managed { diff --git a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnNullableTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnNullableTransform.cs index 30005e06b..f2e3107b2 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnNullableTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnNullableTransform.cs @@ -48,6 +48,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms 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); @@ -59,6 +66,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms container.SortBlocks(deleteUnreachableBlocks: true); } + /// + /// Matches legacy C# switch on nullable. + /// bool MatchSwitchOnNullable(InstructionCollection instructions, int i, out SwitchInstruction newSwitch) { newSwitch = null; @@ -113,5 +123,55 @@ namespace ICSharpCode.Decompiler.IL.Transforms } return true; } + + /// + /// 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 (!instructions[i + 1].MatchBranch(out var switchBlock) || !trueInst.MatchBranch(out var nullCaseBlock)) + return false; + if (!condition.MatchLogicNot(out var getHasValue) || !NullableLiftingTransform.MatchHasValueCall(getHasValue, out var 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 (!NullableLiftingTransform.MatchGetValueOrDefault(getValueOrDefault, out var target2) || target2 != tmp) + return false; + if (!(switchBlock.Instructions[1] is SwitchInstruction switchInst)) + return false; + newSwitch = new SwitchInstruction(switchValue); + newSwitch.IsLifted = true; + SwitchSection defaultSection = null; + foreach (var section in switchInst.Sections) { + if (defaultSection == null || section.Labels.Count() >= defaultSection.Labels.Count()) + defaultSection = section; + newSwitch.Sections.Add(section); + } + if (defaultSection.Body.MatchBranch(out var defaultBlock) && defaultBlock == nullCaseBlock) + defaultSection.HasNullLabel = true; + else { + newSwitch.Sections.Add(new SwitchSection { Body = new Branch(nullCaseBlock), HasNullLabel = true }); + } + return true; + } + } } From 71c0e8e0000e69fdfc40b76a5d21d407b2096524 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Wed, 11 Oct 2017 12:42:45 +0200 Subject: [PATCH 35/65] SwitchOnNullableTransform: add extra checks for temporary variables --- .../IL/Transforms/SwitchOnNullableTransform.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnNullableTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnNullableTransform.cs index f2e3107b2..6e01731fc 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnNullableTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnNullableTransform.cs @@ -82,6 +82,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms !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)) @@ -99,7 +103,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (!getHasValueCall.Arguments[0].MatchLdLoc(tmp) || !getValueOrDefaultCall.Arguments[0].MatchLdLoc(tmp)) return false; // match second block: switchBlock - // switch (ldloc swtichVariable) { + // switch (ldloc switchVariable) { // case [0..1): br caseBlock1 // ... more cases ... // case [long.MinValue..0),[1..5),[6..10),[11..long.MaxValue]: br defaultBlock @@ -138,6 +142,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms 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) + 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 var target1) || target1 != tmp) @@ -153,6 +159,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms 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, out var target2) || target2 != tmp) return false; if (!(switchBlock.Instructions[1] is SwitchInstruction switchInst)) @@ -172,6 +180,5 @@ namespace ICSharpCode.Decompiler.IL.Transforms } return true; } - } } From 9ef97703cd2e4a235c0e67a2a19607247e317f6f Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Wed, 11 Oct 2017 19:43:14 +0200 Subject: [PATCH 36/65] [switch] consider loop back-edges to be exit points when looking for the switch body --- .../FlowAnalysis/ControlFlowNode.cs | 63 ++--- .../ICSharpCode.Decompiler.csproj | 1 + .../IL/ControlFlow/LoopDetection.cs | 51 +++- ICSharpCode.Decompiler/Util/GraphVizGraph.cs | 221 ++++++++++++++++++ 4 files changed, 296 insertions(+), 40 deletions(-) create mode 100644 ICSharpCode.Decompiler/Util/GraphVizGraph.cs 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/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 7fee5ff0d..48313b70c 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -300,6 +300,7 @@ + diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs index 2e49db446..ad95af4c0 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs @@ -210,9 +210,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow /// exit-point (if non-null) is not in this set, /// nodes are no longer marked as visited in the CFG /// - 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 @@ -258,9 +258,16 @@ 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)) { + treatBackEdgesAsExits = false; + 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. @@ -275,7 +282,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]; @@ -297,7 +304,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow return exitPoint; } } - // least common dominator is the artificial exit node + // least common post-dominator is the artificial exit node return null; } } @@ -339,7 +346,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]; @@ -353,7 +360,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]); @@ -488,14 +495,22 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow private void DetectSwitchBody(Block block, SwitchInstruction switchInst) { Debug.Assert(block.Instructions.Last() == switchInst); - ControlFlowNode h = context.ControlFlowNode; // CFG node for our potential loop head + 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); + ExtendLoop(h, nodesInSwitch, out var exitPoint, isSwitch: true); + if (IsSimpleSwitchExit(exitPoint)) { + // Also move the exit point into the switch if it's simple enough. + exitPoint.TraversePreOrder(p => p.Successors, nodesInSwitch.Add); + foreach (var node in nodesInSwitch) { + node.Visited = false; + } + 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. @@ -527,5 +542,21 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow } } } + + private bool IsSimpleSwitchExit(ControlFlowNode exitPoint) + { + int totalInstructionCount = 0; + while (exitPoint?.UserData is Block block && block.IncomingEdgeCount == 1) { + totalInstructionCount += block.Instructions.Count; + if (exitPoint.Successors.Count == 1 && block.Instructions.Last() is Branch) { + exitPoint = exitPoint.Successors[0]; + } else if (exitPoint.Successors.Count == 0 && block.Instructions.Last() is Leave leave && leave.IsLeavingFunction) { + return totalInstructionCount < 10; + } else { + return false; + } + } + return false; + } } } 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 +} From cd993ad0741d767fb6694b1976ad753318ff0fdb Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Wed, 11 Oct 2017 20:53:00 +0200 Subject: [PATCH 37/65] Property validate exit points determined using post-dominance. --- .../IL/ControlFlow/LoopDetection.cs | 49 +++++++++++++++++-- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs index ad95af4c0..e01a84129 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs @@ -293,22 +293,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 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.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. /// From de2c647114e5d1659179ee16b172154b342b4e13 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Wed, 11 Oct 2017 19:50:04 +0200 Subject: [PATCH 38/65] Revert "Fix bug that could cause nodes reachable from the exit point to be moved into the loop/switch." This reverts commit 105ff744b336111c6214552c4451619b2e6e6d35. A correctly chosen single exit point should dominate all other code that is dominated by the loop but not included in the body -- otherwise there would be multiple exit points. So this "bugfix" was only covering up for us not properly validating the exit points from post-dominance. --- .../IL/ControlFlow/LoopDetection.cs | 36 ++++--------------- 1 file changed, 6 insertions(+), 30 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs index e01a84129..b73b7e2f5 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs @@ -101,6 +101,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow loop.Sort((a, b) => b.PostOrderNumber.CompareTo(a.PostOrderNumber)); Debug.Assert(loop[0] == h); foreach (var node in loop) { + node.Visited = false; // reset visited flag so that we can find outer loops Debug.Assert(h.Dominates(node) || !node.IsReachable, "The loop body must be dominated by the loop head"); } ConstructLoop(loop, exitPoint); @@ -206,9 +207,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow /// used by the post-dominance analysis. In this case, we fall back to the old heuristic algorithm. /// /// Precondition: Requires that a node is marked as visited iff it is contained in the loop. - /// Postcondition: loop is subset of nodes that are only reachable from each other, - /// exit-point (if non-null) is not in this set, - /// nodes are no longer marked as visited in the CFG /// void ExtendLoop(ControlFlowNode loopHead, List loop, out ControlFlowNode exitPoint, bool isSwitch=false) { @@ -217,16 +215,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow if (exitPoint != null) { // Either we are in case 1 and just picked an exit that maximizes the amount of code // outside the loop, or we are in case 2 and found an exit point via post-dominance. - - // We'll move all blocks dominated by the loop head into the loop, except for those that - // are reachable from the exit point. - MarkReachableWithinBlocksDominatedByLoop(exitPoint, loopHead); - foreach (var node in TreeTraversal.PreOrder(loopHead, n => n.DominatorTreeChildren)) { - if (node.Visited) { - // node.Visited means that the node is already part of the loop, - // or that it is reachable from the exit point. - node.Visited = false; - } else { + var ep = exitPoint; + foreach (var node in TreeTraversal.PreOrder(loopHead, n => (n != ep) ? n.DominatorTreeChildren : null)) { + if (node != exitPoint && !node.Visited) { loop.Add(node); } } @@ -235,25 +226,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow // Heuristically try to minimize the number of exit points // (but we'll always end up with more than 1 exit and will require goto statements). ExtendLoopHeuristic(loopHead, loop, loopHead); - // Reset Visited flag: - foreach (var node in loop) { - node.Visited = false; - } - } - } - - void MarkReachableWithinBlocksDominatedByLoop(ControlFlowNode node, ControlFlowNode loopHead) - { - if (node.Visited) - return; // already visited - if (!loopHead.Dominates(node)) - return; - node.Visited = true; - foreach (var successor in node.Successors) { - MarkReachableWithinBlocksDominatedByLoop(successor, loopHead); } } - + /// /// Finds a suitable single exit point for the specified loop. /// @@ -559,6 +534,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow 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"); } From 51c895d5a5fb5158451f3c14dc9d0dfcf29fdc3d Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Wed, 11 Oct 2017 21:15:41 +0200 Subject: [PATCH 39/65] Fix inlining into switch instructions. --- ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs index d5dd01d6e..c78072dac 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs @@ -292,6 +292,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: @@ -333,6 +340,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) From c7490ff2fe1a7a0934261d341eb445b4ed8088da Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Wed, 11 Oct 2017 21:30:15 +0200 Subject: [PATCH 40/65] [switch] Sort switch sections --- ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs index 6e2fa3e67..7f72c6806 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs @@ -115,6 +115,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow return false; }); AdjustLabels(sw); + sw.Sections.ReplaceList(sw.Sections.OrderBy(s => (s.Body as Branch)?.TargetILOffset).ThenBy(s => s.Labels.Values.FirstOrDefault())); } static void AdjustLabels(SwitchInstruction sw) From 953c6a092977254863762a1e585e7de9fa8a0abb Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Wed, 11 Oct 2017 21:41:11 +0200 Subject: [PATCH 41/65] Keep code dominated by a single switch section within the switch. --- .../IL/ControlFlow/LoopDetection.cs | 28 ++++--------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs index b73b7e2f5..06dabbfcc 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs @@ -519,12 +519,12 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow nodesInSwitch.Add(h); h.Visited = true; ExtendLoop(h, nodesInSwitch, out var exitPoint, isSwitch: true); - if (IsSimpleSwitchExit(exitPoint)) { - // Also move the exit point into the switch if it's simple enough. - exitPoint.TraversePreOrder(p => p.Successors, nodesInSwitch.Add); - foreach (var node in nodesInSwitch) { - node.Visited = false; - } + 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; } @@ -559,21 +559,5 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow } } } - - private bool IsSimpleSwitchExit(ControlFlowNode exitPoint) - { - int totalInstructionCount = 0; - while (exitPoint?.UserData is Block block && block.IncomingEdgeCount == 1) { - totalInstructionCount += block.Instructions.Count; - if (exitPoint.Successors.Count == 1 && block.Instructions.Last() is Branch) { - exitPoint = exitPoint.Successors[0]; - } else if (exitPoint.Successors.Count == 0 && block.Instructions.Last() is Leave leave && leave.IsLeavingFunction) { - return totalInstructionCount < 10; - } else { - return false; - } - } - return false; - } } } From f2ee1c55f95d30938800e71959db1399aeaabfa6 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Wed, 11 Oct 2017 22:11:59 +0200 Subject: [PATCH 42/65] Add test case for switch without default + remove empty default sections in StatementBuilder. --- .../TestCases/Pretty/Switch.cs | 34 +++++ .../TestCases/Pretty/Switch.il | 129 +++++++++++++++--- .../TestCases/Pretty/Switch.opt.il | 112 ++++++++++++--- .../TestCases/Pretty/Switch.opt.roslyn.il | 78 ++++++++++- .../TestCases/Pretty/Switch.roslyn.il | 106 +++++++++++++- .../CSharp/StatementBuilder.cs | 6 + 6 files changed, 427 insertions(+), 38 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs index fcf01f6d9..47cae5066 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs @@ -160,6 +160,40 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } + 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); diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il index c6af28ac4..f5e28683c 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il @@ -10,7 +10,7 @@ .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } -.assembly nyniamwr +.assembly cgwywrw0 { .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. @@ -20,15 +20,15 @@ .hash algorithm 0x00008004 .ver 0:0:0:0 } -.module nyniamwr.dll -// MVID: {95C99B41-CBA3-42E4-A4DE-27E535671AB2} +.module cgwywrw0.dll +// MVID: {12C66A56-696F-4026-B79B-EFB40F4CD81E} .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: 0x03080000 +// Image base: 0x00B50000 // =============== CLASS MEMBERS DECLARATION =================== @@ -476,6 +476,101 @@ IL_006b: 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_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_0042 + + IL_0010: ldloc.0 + IL_0011: ldc.i4.s 10 + IL_0013: beq.s IL_0050 + + IL_0015: br.s IL_0096 + + 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_005e + + IL_0021: ldloc.0 + IL_0022: ldc.i4.s 20 + IL_0024: beq.s IL_006c + + IL_0026: br.s IL_0096 + + IL_0028: ldloc.0 + IL_0029: ldc.i4.s 25 + IL_002b: beq.s IL_007a + + IL_002d: ldloc.0 + IL_002e: ldc.i4.s 30 + IL_0030: beq.s IL_0088 + + IL_0032: br.s IL_0096 + + IL_0034: nop + IL_0035: ldstr "zero" + IL_003a: call void [mscorlib]System.Console::WriteLine(string) + IL_003f: nop + IL_0040: br.s IL_0096 + + IL_0042: nop + IL_0043: ldstr "five" + IL_0048: call void [mscorlib]System.Console::WriteLine(string) + IL_004d: nop + IL_004e: br.s IL_0096 + + IL_0050: nop + IL_0051: ldstr "ten" + IL_0056: call void [mscorlib]System.Console::WriteLine(string) + IL_005b: nop + IL_005c: br.s IL_0096 + + IL_005e: nop + IL_005f: ldstr "fifteen" + IL_0064: call void [mscorlib]System.Console::WriteLine(string) + IL_0069: nop + IL_006a: br.s IL_0096 + + IL_006c: nop + IL_006d: ldstr "twenty" + IL_0072: call void [mscorlib]System.Console::WriteLine(string) + IL_0077: nop + IL_0078: br.s IL_0096 + + IL_007a: nop + IL_007b: ldstr "twenty-five" + IL_0080: call void [mscorlib]System.Console::WriteLine(string) + IL_0085: nop + IL_0086: br.s IL_0096 + + IL_0088: nop + 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 { @@ -560,7 +655,7 @@ IL_0015: brfalse IL_00ef IL_001a: volatile. - IL_001c: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{95C99B41-CBA3-42E4-A4DE-27E535671AB2}'::'$$method0x6000007-1' + IL_001c: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{12C66A56-696F-4026-B79B-EFB40F4CD81E}'::'$$method0x6000008-1' IL_0021: brtrue.s IL_0084 IL_0023: ldc.i4.7 @@ -601,9 +696,9 @@ 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 '{95C99B41-CBA3-42E4-A4DE-27E535671AB2}'::'$$method0x6000007-1' + IL_007f: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{12C66A56-696F-4026-B79B-EFB40F4CD81E}'::'$$method0x6000008-1' IL_0084: volatile. - IL_0086: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{95C99B41-CBA3-42E4-A4DE-27E535671AB2}'::'$$method0x6000007-1' + IL_0086: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{12C66A56-696F-4026-B79B-EFB40F4CD81E}'::'$$method0x6000008-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, @@ -686,7 +781,7 @@ IL_0015: brfalse IL_0165 IL_001a: volatile. - IL_001c: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{95C99B41-CBA3-42E4-A4DE-27E535671AB2}'::'$$method0x6000008-1' + IL_001c: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{12C66A56-696F-4026-B79B-EFB40F4CD81E}'::'$$method0x6000009-1' IL_0021: brtrue IL_00ba IL_0026: ldc.i4.s 11 @@ -747,9 +842,9 @@ IL_00ae: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, !1) IL_00b3: volatile. - IL_00b5: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{95C99B41-CBA3-42E4-A4DE-27E535671AB2}'::'$$method0x6000008-1' + IL_00b5: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{12C66A56-696F-4026-B79B-EFB40F4CD81E}'::'$$method0x6000009-1' IL_00ba: volatile. - IL_00bc: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{95C99B41-CBA3-42E4-A4DE-27E535671AB2}'::'$$method0x6000008-1' + IL_00bc: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{12C66A56-696F-4026-B79B-EFB40F4CD81E}'::'$$method0x6000009-1' IL_00c1: ldloc.2 IL_00c2: ldloca.s V_3 IL_00c4: call instance bool class [mscorlib]System.Collections.Generic.Dictionary`2::TryGetValue(!0, @@ -1064,7 +1159,7 @@ IL_0034: brfalse IL_012d IL_0039: volatile. - IL_003b: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{95C99B41-CBA3-42E4-A4DE-27E535671AB2}'::'$$method0x600000d-1' + IL_003b: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{12C66A56-696F-4026-B79B-EFB40F4CD81E}'::'$$method0x600000e-1' IL_0040: brtrue.s IL_0097 IL_0042: ldc.i4.6 @@ -1100,9 +1195,9 @@ IL_008b: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, !1) IL_0090: volatile. - IL_0092: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{95C99B41-CBA3-42E4-A4DE-27E535671AB2}'::'$$method0x600000d-1' + IL_0092: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{12C66A56-696F-4026-B79B-EFB40F4CD81E}'::'$$method0x600000e-1' IL_0097: volatile. - IL_0099: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{95C99B41-CBA3-42E4-A4DE-27E535671AB2}'::'$$method0x600000d-1' + IL_0099: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{12C66A56-696F-4026-B79B-EFB40F4CD81E}'::'$$method0x600000e-1' IL_009e: ldloc.s V_6 IL_00a0: ldloca.s V_7 IL_00a2: call instance bool class [mscorlib]System.Collections.Generic.Dictionary`2::TryGetValue(!0, @@ -1196,14 +1291,14 @@ } // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch -.class private auto ansi '{95C99B41-CBA3-42E4-A4DE-27E535671AB2}' +.class private auto ansi '{12C66A56-696F-4026-B79B-EFB40F4CD81E}' 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 '$$method0x6000007-1' .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000008-1' - .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x600000d-1' -} // end of class '{95C99B41-CBA3-42E4-A4DE-27E535671AB2}' + .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000009-1' + .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x600000e-1' +} // end of class '{12C66A56-696F-4026-B79B-EFB40F4CD81E}' // ============================================================= diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il index e09dd767c..bff2ea6b4 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il @@ -10,7 +10,7 @@ .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } -.assembly f3gworj3 +.assembly '2bmtzwrf' { .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. @@ -20,15 +20,15 @@ .hash algorithm 0x00008004 .ver 0:0:0:0 } -.module f3gworj3.dll -// MVID: {6837A40E-7A00-4F01-B2D7-DE0001F70EDF} +.module '2bmtzwrf.dll' +// MVID: {F90E3F03-F070-45F0-AC05-0914EF70B327} .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: 0x00780000 +// Image base: 0x02E10000 // =============== CLASS MEMBERS DECLARATION =================== @@ -382,6 +382,84 @@ 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 { @@ -449,7 +527,7 @@ IL_0013: brfalse IL_00db IL_0018: volatile. - IL_001a: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{6837A40E-7A00-4F01-B2D7-DE0001F70EDF}'::'$$method0x6000007-1' + IL_001a: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{F90E3F03-F070-45F0-AC05-0914EF70B327}'::'$$method0x6000008-1' IL_001f: brtrue.s IL_0082 IL_0021: ldc.i4.7 @@ -490,9 +568,9 @@ 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 '{6837A40E-7A00-4F01-B2D7-DE0001F70EDF}'::'$$method0x6000007-1' + IL_007d: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{F90E3F03-F070-45F0-AC05-0914EF70B327}'::'$$method0x6000008-1' IL_0082: volatile. - IL_0084: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{6837A40E-7A00-4F01-B2D7-DE0001F70EDF}'::'$$method0x6000007-1' + IL_0084: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{F90E3F03-F070-45F0-AC05-0914EF70B327}'::'$$method0x6000008-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, @@ -553,7 +631,7 @@ IL_0013: brfalse IL_013f IL_0018: volatile. - IL_001a: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{6837A40E-7A00-4F01-B2D7-DE0001F70EDF}'::'$$method0x6000008-1' + IL_001a: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{F90E3F03-F070-45F0-AC05-0914EF70B327}'::'$$method0x6000009-1' IL_001f: brtrue IL_00b8 IL_0024: ldc.i4.s 11 @@ -614,9 +692,9 @@ 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 '{6837A40E-7A00-4F01-B2D7-DE0001F70EDF}'::'$$method0x6000008-1' + IL_00b3: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{F90E3F03-F070-45F0-AC05-0914EF70B327}'::'$$method0x6000009-1' IL_00b8: volatile. - IL_00ba: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{6837A40E-7A00-4F01-B2D7-DE0001F70EDF}'::'$$method0x6000008-1' + IL_00ba: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{F90E3F03-F070-45F0-AC05-0914EF70B327}'::'$$method0x6000009-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, @@ -845,7 +923,7 @@ IL_0031: brfalse IL_0119 IL_0036: volatile. - IL_0038: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{6837A40E-7A00-4F01-B2D7-DE0001F70EDF}'::'$$method0x600000d-1' + IL_0038: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{F90E3F03-F070-45F0-AC05-0914EF70B327}'::'$$method0x600000e-1' IL_003d: brtrue.s IL_0094 IL_003f: ldc.i4.6 @@ -881,9 +959,9 @@ IL_0088: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, !1) IL_008d: volatile. - IL_008f: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{6837A40E-7A00-4F01-B2D7-DE0001F70EDF}'::'$$method0x600000d-1' + IL_008f: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{F90E3F03-F070-45F0-AC05-0914EF70B327}'::'$$method0x600000e-1' IL_0094: volatile. - IL_0096: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{6837A40E-7A00-4F01-B2D7-DE0001F70EDF}'::'$$method0x600000d-1' + IL_0096: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{F90E3F03-F070-45F0-AC05-0914EF70B327}'::'$$method0x600000e-1' IL_009b: ldloc.s V_6 IL_009d: ldloca.s V_7 IL_009f: call instance bool class [mscorlib]System.Collections.Generic.Dictionary`2::TryGetValue(!0, @@ -955,14 +1033,14 @@ } // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch -.class private auto ansi '{6837A40E-7A00-4F01-B2D7-DE0001F70EDF}' +.class private auto ansi '{F90E3F03-F070-45F0-AC05-0914EF70B327}' 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 '$$method0x6000007-1' .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000008-1' - .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x600000d-1' -} // end of class '{6837A40E-7A00-4F01-B2D7-DE0001F70EDF}' + .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000009-1' + .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x600000e-1' +} // end of class '{F90E3F03-F070-45F0-AC05-0914EF70B327}' // ============================================================= diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il index bd0488bb3..50e230ad0 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il @@ -25,14 +25,14 @@ .ver 0:0:0:0 } .module Switch.dll -// MVID: {04DBCBA6-8175-41CD-8917-9428C9765986} +// MVID: {C83C2FCD-FA35-4E74-9418-9B91F2C0748A} .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: 0x00830000 +// Image base: 0x01020000 // =============== CLASS MEMBERS DECLARATION =================== @@ -393,6 +393,80 @@ 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 { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il index 38331d470..eb57faa5d 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il @@ -25,14 +25,14 @@ .ver 0:0:0:0 } .module Switch.dll -// MVID: {87DEBC09-DFAD-437D-9221-109E2117A07A} +// MVID: {B5801C4A-C979-4EC9-B5B2-295587FEEF30} .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: 0x02960000 +// Image base: 0x005D0000 // =============== CLASS MEMBERS DECLARATION =================== @@ -521,6 +521,108 @@ IL_0079: ret } // end of method Switch::SwitchOverNullableIntNoNullCaseShifted + .method public hidebysig static void SwitchOverInt(int32 i) cil managed + { + // Code size 161 (0xa1) + .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_001d + + IL_0008: ldloc.0 + IL_0009: brfalse.s IL_003e + + IL_000b: br.s IL_000d + + IL_000d: ldloc.0 + IL_000e: ldc.i4.5 + IL_000f: beq.s IL_004c + + IL_0011: br.s IL_0013 + + IL_0013: ldloc.0 + IL_0014: ldc.i4.s 10 + IL_0016: beq.s IL_005a + + IL_0018: br IL_00a0 + + IL_001d: ldloc.0 + IL_001e: ldc.i4.s 20 + IL_0020: bgt.s IL_0030 + + IL_0022: ldloc.0 + IL_0023: ldc.i4.s 15 + IL_0025: beq.s IL_0068 + + IL_0027: br.s IL_0029 + + IL_0029: ldloc.0 + IL_002a: ldc.i4.s 20 + IL_002c: beq.s IL_0076 + + IL_002e: br.s IL_00a0 + + IL_0030: ldloc.0 + IL_0031: ldc.i4.s 25 + IL_0033: beq.s IL_0084 + + IL_0035: br.s IL_0037 + + IL_0037: ldloc.0 + IL_0038: ldc.i4.s 30 + IL_003a: beq.s IL_0092 + + IL_003c: br.s IL_00a0 + + IL_003e: nop + IL_003f: ldstr "zero" + IL_0044: call void [mscorlib]System.Console::WriteLine(string) + IL_0049: nop + IL_004a: br.s IL_00a0 + + IL_004c: nop + IL_004d: ldstr "five" + IL_0052: call void [mscorlib]System.Console::WriteLine(string) + IL_0057: nop + IL_0058: br.s IL_00a0 + + IL_005a: nop + IL_005b: ldstr "ten" + IL_0060: call void [mscorlib]System.Console::WriteLine(string) + IL_0065: nop + IL_0066: br.s IL_00a0 + + IL_0068: nop + IL_0069: ldstr "fifteen" + IL_006e: call void [mscorlib]System.Console::WriteLine(string) + IL_0073: nop + IL_0074: br.s IL_00a0 + + IL_0076: nop + IL_0077: ldstr "twenty" + IL_007c: call void [mscorlib]System.Console::WriteLine(string) + IL_0081: nop + IL_0082: br.s IL_00a0 + + IL_0084: nop + IL_0085: ldstr "twenty-five" + IL_008a: call void [mscorlib]System.Console::WriteLine(string) + IL_008f: nop + IL_0090: br.s IL_00a0 + + IL_0092: nop + IL_0093: ldstr "thirty" + IL_0098: call void [mscorlib]System.Console::WriteLine(string) + IL_009d: nop + IL_009e: br.s IL_00a0 + + IL_00a0: ret + } // end of method Switch::SwitchOverInt + .method public hidebysig static string ShortSwitchOverString(string text) cil managed { diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs index 8be341e76..909339d6d 100644 --- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs @@ -175,6 +175,12 @@ namespace ICSharpCode.Decompiler.CSharp 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; From 3bf8d0da91ea0d4525884c7bb050f1b9d235eca9 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Wed, 11 Oct 2017 23:20:39 +0200 Subject: [PATCH 43/65] Reduce size of LINQRaytracer test case --- .../TestCases/Correctness/LINQRaytracer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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); From 4927de647ba34deb4713f76498894a32c3d625c9 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Wed, 11 Oct 2017 23:21:17 +0200 Subject: [PATCH 44/65] Add more switch pretty test cases --- .../TestCases/Pretty/Switch.cs | 337 ++++++++++-------- .../TestCases/Pretty/Switch.il | 178 ++++++++- .../TestCases/Pretty/Switch.opt.il | 176 +++++++-- .../TestCases/Pretty/Switch.opt.roslyn.il | 143 +++++++- .../TestCases/Pretty/Switch.roslyn.il | 144 +++++++- 5 files changed, 771 insertions(+), 207 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs index 47cae5066..056229bff 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs @@ -44,41 +44,41 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty Console.WriteLine("SparseIntegerSwitch: " + i); switch (i) { case -10000000: { - return "-10 mln"; - } + return "-10 mln"; + } case -100: { - return "-hundred"; - } + return "-hundred"; + } case -1: { - return "-1"; - } + return "-1"; + } case 0: { - return "0"; - } + return "0"; + } case 1: { - return "1"; - } + return "1"; + } case 2: { - return "2"; - } + return "2"; + } case 4: { - return "4"; - } + return "4"; + } case 100: { - return "hundred"; - } + return "hundred"; + } case 10000: { - return "ten thousand"; - } + return "ten thousand"; + } case 10001: { - return "ten thousand and one"; - } + return "ten thousand and one"; + } case 2147483647: { - return "int.MaxValue"; - } + return "int.MaxValue"; + } default: { - return "something else"; - } + return "something else"; + } } } @@ -86,20 +86,20 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { switch (i) { case null: { - return "null"; - } + return "null"; + } case 0: { - return "zero"; - } + return "zero"; + } case 5: { - return "five"; - } + return "five"; + } case 10: { - return "ten"; - } + return "ten"; + } default: { - return "large"; - } + return "large"; + } } } @@ -107,20 +107,20 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { switch (i + 5) { case null: { - return "null"; - } + return "null"; + } case 0: { - return "zero"; - } + return "zero"; + } case 5: { - return "five"; - } + return "five"; + } case 10: { - return "ten"; - } + return "ten"; + } default: { - return "large"; - } + return "large"; + } } } @@ -128,17 +128,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { switch (i) { case 0: { - return "zero"; - } + return "zero"; + } case 5: { - return "five"; - } + return "five"; + } case 10: { - return "ten"; - } + return "ten"; + } default: { - return "other"; - } + return "other"; + } } } @@ -146,17 +146,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { switch (i + 5) { case 0: { - return "zero"; - } + return "zero"; + } case 5: { - return "five"; - } + return "five"; + } case 10: { - return "ten"; - } + return "ten"; + } default: { - return "other"; - } + return "other"; + } } } @@ -199,17 +199,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty Console.WriteLine("ShortSwitchOverString: " + text); switch (text) { case "First case": { - return "Text1"; - } + return "Text1"; + } case "Second case": { - return "Text2"; - } + return "Text2"; + } case "Third case": { - return "Text3"; - } + return "Text3"; + } default: { - return "Default"; - } + return "Default"; + } } } @@ -218,30 +218,30 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty Console.WriteLine("SwitchOverString1: " + text); switch (text) { case "First case": { - return "Text1"; - } + return "Text1"; + } case "Second case": case "2nd case": { - return "Text2"; - } + return "Text2"; + } case "Third case": { - return "Text3"; - } + return "Text3"; + } case "Fourth case": { - return "Text4"; - } + return "Text4"; + } case "Fifth case": { - return "Text5"; - } + return "Text5"; + } case "Sixth case": { - return "Text6"; - } + return "Text6"; + } case null: { - return null; - } + return null; + } default: { - return "Default"; - } + return "Default"; + } } } @@ -251,41 +251,41 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty string userName = Environment.UserName; switch (userName) { case "First case": { - return "Text1"; - } + return "Text1"; + } case "Second case": { - return "Text2"; - } + return "Text2"; + } case "Third case": { - return "Text3"; - } + return "Text3"; + } case "Fourth case": { - return "Text4"; - } + return "Text4"; + } case "Fifth case": { - return "Text5"; - } + return "Text5"; + } case "Sixth case": { - return "Text6"; - } + return "Text6"; + } case "Seventh case": { - return "Text7"; - } + return "Text7"; + } case "Eighth case": { - return "Text8"; - } + return "Text8"; + } case "Ninth case": { - return "Text9"; - } + return "Text9"; + } case "Tenth case": { - return "Text10"; - } + return "Text10"; + } case "Eleventh case": { - return "Text11"; - } + return "Text11"; + } default: { - return "Default"; - } + return "Default"; + } } } @@ -294,14 +294,14 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty Console.WriteLine("SwitchOverBool: " + b.ToString()); switch (b) { case true: { - return bool.TrueString; - } + return bool.TrueString; + } case false: { - return bool.FalseString; - } + return bool.FalseString; + } default: { - return null; - } + return null; + } } } @@ -311,26 +311,26 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty while (true) { switch (i) { case 1: { - Console.WriteLine("one"); - break; - } + Console.WriteLine("one"); + break; + } case 2: { - Console.WriteLine("two"); - break; - } + Console.WriteLine("two"); + break; + } case 3: { - Console.WriteLine("three"); - continue; - } + Console.WriteLine("three"); + continue; + } case 4: { - Console.WriteLine("four"); - return; - } + Console.WriteLine("four"); + return; + } default: { - Console.WriteLine("default"); - Console.WriteLine("more code"); - return; - } + Console.WriteLine("default"); + Console.WriteLine("more code"); + return; + } } i++; } @@ -341,26 +341,27 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty Console.WriteLine("SwitchWithGoto: " + i); switch (i) { case 1: { - Console.WriteLine("one"); - goto default; - } + Console.WriteLine("one"); + goto default; + } case 2: { - Console.WriteLine("two"); - goto case 3; - } + Console.WriteLine("two"); + goto case 3; + } case 3: { - Console.WriteLine("three"); - break; - } + Console.WriteLine("three"); + break; + } case 4: { - Console.WriteLine("four"); - return; - } + Console.WriteLine("four"); + return; + } default: { - Console.WriteLine("default"); - break; - } + Console.WriteLine("default"); + break; + } } + Console.WriteLine("End of method"); } private static SetProperty[] GetProperties() @@ -409,5 +410,51 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } } + + 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 index f5e28683c..91f9e4ff5 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il @@ -10,7 +10,7 @@ .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } -.assembly cgwywrw0 +.assembly y0cd2lcb { .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. @@ -20,15 +20,15 @@ .hash algorithm 0x00008004 .ver 0:0:0:0 } -.module cgwywrw0.dll -// MVID: {12C66A56-696F-4026-B79B-EFB40F4CD81E} +.module y0cd2lcb.dll +// MVID: {85E7C039-2097-47F9-A636-4F4E3015541A} .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: 0x00B50000 +// Image base: 0x02630000 // =============== CLASS MEMBERS DECLARATION =================== @@ -655,7 +655,7 @@ IL_0015: brfalse IL_00ef IL_001a: volatile. - IL_001c: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{12C66A56-696F-4026-B79B-EFB40F4CD81E}'::'$$method0x6000008-1' + IL_001c: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{85E7C039-2097-47F9-A636-4F4E3015541A}'::'$$method0x6000008-1' IL_0021: brtrue.s IL_0084 IL_0023: ldc.i4.7 @@ -696,9 +696,9 @@ 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 '{12C66A56-696F-4026-B79B-EFB40F4CD81E}'::'$$method0x6000008-1' + IL_007f: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{85E7C039-2097-47F9-A636-4F4E3015541A}'::'$$method0x6000008-1' IL_0084: volatile. - IL_0086: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{12C66A56-696F-4026-B79B-EFB40F4CD81E}'::'$$method0x6000008-1' + IL_0086: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{85E7C039-2097-47F9-A636-4F4E3015541A}'::'$$method0x6000008-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, @@ -781,7 +781,7 @@ IL_0015: brfalse IL_0165 IL_001a: volatile. - IL_001c: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{12C66A56-696F-4026-B79B-EFB40F4CD81E}'::'$$method0x6000009-1' + IL_001c: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{85E7C039-2097-47F9-A636-4F4E3015541A}'::'$$method0x6000009-1' IL_0021: brtrue IL_00ba IL_0026: ldc.i4.s 11 @@ -842,9 +842,9 @@ IL_00ae: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, !1) IL_00b3: volatile. - IL_00b5: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{12C66A56-696F-4026-B79B-EFB40F4CD81E}'::'$$method0x6000009-1' + IL_00b5: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{85E7C039-2097-47F9-A636-4F4E3015541A}'::'$$method0x6000009-1' IL_00ba: volatile. - IL_00bc: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{12C66A56-696F-4026-B79B-EFB40F4CD81E}'::'$$method0x6000009-1' + IL_00bc: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{85E7C039-2097-47F9-A636-4F4E3015541A}'::'$$method0x6000009-1' IL_00c1: ldloc.2 IL_00c2: ldloca.s V_3 IL_00c4: call instance bool class [mscorlib]System.Collections.Generic.Dictionary`2::TryGetValue(!0, @@ -1048,7 +1048,7 @@ .method public hidebysig static void SwitchWithGoto(int32 i) cil managed { - // Code size 122 (0x7a) + // Code size 133 (0x85) .maxstack 2 .locals init (int32 V_0) IL_0000: nop @@ -1093,7 +1093,7 @@ IL_005e: ldstr "four" IL_0063: call void [mscorlib]System.Console::WriteLine(string) IL_0068: nop - IL_0069: br.s IL_0079 + IL_0069: br.s IL_0084 IL_006b: nop IL_006c: ldstr "default" @@ -1101,7 +1101,10 @@ IL_0076: nop IL_0077: br.s IL_0079 - IL_0079: ret + IL_0079: ldstr "End of method" + IL_007e: call void [mscorlib]System.Console::WriteLine(string) + IL_0083: nop + IL_0084: ret } // end of method Switch::SwitchWithGoto .method private hidebysig static class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty[] @@ -1159,7 +1162,7 @@ IL_0034: brfalse IL_012d IL_0039: volatile. - IL_003b: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{12C66A56-696F-4026-B79B-EFB40F4CD81E}'::'$$method0x600000e-1' + IL_003b: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{85E7C039-2097-47F9-A636-4F4E3015541A}'::'$$method0x600000e-1' IL_0040: brtrue.s IL_0097 IL_0042: ldc.i4.6 @@ -1195,9 +1198,9 @@ IL_008b: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, !1) IL_0090: volatile. - IL_0092: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{12C66A56-696F-4026-B79B-EFB40F4CD81E}'::'$$method0x600000e-1' + IL_0092: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{85E7C039-2097-47F9-A636-4F4E3015541A}'::'$$method0x600000e-1' IL_0097: volatile. - IL_0099: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{12C66A56-696F-4026-B79B-EFB40F4CD81E}'::'$$method0x600000e-1' + IL_0099: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{85E7C039-2097-47F9-A636-4F4E3015541A}'::'$$method0x600000e-1' IL_009e: ldloc.s V_6 IL_00a0: ldloca.s V_7 IL_00a2: call instance bool class [mscorlib]System.Collections.Generic.Dictionary`2::TryGetValue(!0, @@ -1289,16 +1292,155 @@ IL_014d: 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 '{12C66A56-696F-4026-B79B-EFB40F4CD81E}' +.class private auto ansi '{85E7C039-2097-47F9-A636-4F4E3015541A}' 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 '$$method0x6000008-1' .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000009-1' .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x600000e-1' -} // end of class '{12C66A56-696F-4026-B79B-EFB40F4CD81E}' +} // end of class '{85E7C039-2097-47F9-A636-4F4E3015541A}' // ============================================================= diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il index bff2ea6b4..85eea31f3 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il @@ -10,7 +10,7 @@ .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } -.assembly '2bmtzwrf' +.assembly nwkzngd2 { .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. @@ -20,15 +20,15 @@ .hash algorithm 0x00008004 .ver 0:0:0:0 } -.module '2bmtzwrf.dll' -// MVID: {F90E3F03-F070-45F0-AC05-0914EF70B327} +.module nwkzngd2.dll +// MVID: {2FE2A6DB-2DD1-4526-BE2D-8029A91D5DA5} .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: 0x02E10000 +// Image base: 0x008E0000 // =============== CLASS MEMBERS DECLARATION =================== @@ -527,7 +527,7 @@ IL_0013: brfalse IL_00db IL_0018: volatile. - IL_001a: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{F90E3F03-F070-45F0-AC05-0914EF70B327}'::'$$method0x6000008-1' + IL_001a: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{2FE2A6DB-2DD1-4526-BE2D-8029A91D5DA5}'::'$$method0x6000008-1' IL_001f: brtrue.s IL_0082 IL_0021: ldc.i4.7 @@ -568,9 +568,9 @@ 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 '{F90E3F03-F070-45F0-AC05-0914EF70B327}'::'$$method0x6000008-1' + IL_007d: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{2FE2A6DB-2DD1-4526-BE2D-8029A91D5DA5}'::'$$method0x6000008-1' IL_0082: volatile. - IL_0084: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{F90E3F03-F070-45F0-AC05-0914EF70B327}'::'$$method0x6000008-1' + IL_0084: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{2FE2A6DB-2DD1-4526-BE2D-8029A91D5DA5}'::'$$method0x6000008-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, @@ -631,7 +631,7 @@ IL_0013: brfalse IL_013f IL_0018: volatile. - IL_001a: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{F90E3F03-F070-45F0-AC05-0914EF70B327}'::'$$method0x6000009-1' + IL_001a: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{2FE2A6DB-2DD1-4526-BE2D-8029A91D5DA5}'::'$$method0x6000009-1' IL_001f: brtrue IL_00b8 IL_0024: ldc.i4.s 11 @@ -692,9 +692,9 @@ 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 '{F90E3F03-F070-45F0-AC05-0914EF70B327}'::'$$method0x6000009-1' + IL_00b3: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{2FE2A6DB-2DD1-4526-BE2D-8029A91D5DA5}'::'$$method0x6000009-1' IL_00b8: volatile. - IL_00ba: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{F90E3F03-F070-45F0-AC05-0914EF70B327}'::'$$method0x6000009-1' + IL_00ba: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{2FE2A6DB-2DD1-4526-BE2D-8029A91D5DA5}'::'$$method0x6000009-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, @@ -837,7 +837,7 @@ .method public hidebysig static void SwitchWithGoto(int32 i) cil managed { - // Code size 104 (0x68) + // Code size 115 (0x73) .maxstack 2 .locals init (int32 V_0) IL_0000: ldstr "SwitchWithGoto: " @@ -855,26 +855,28 @@ IL_0031, IL_003d, IL_0047, - IL_0052) - IL_002f: br.s IL_005d + 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_005d + 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: ret + IL_0051: br.s IL_0068 - IL_0052: ldstr "four" - IL_0057: call void [mscorlib]System.Console::WriteLine(string) - IL_005c: ret + IL_0053: ldstr "four" + IL_0058: call void [mscorlib]System.Console::WriteLine(string) + IL_005d: ret - IL_005d: ldstr "default" - IL_0062: call void [mscorlib]System.Console::WriteLine(string) - IL_0067: 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[] @@ -923,7 +925,7 @@ IL_0031: brfalse IL_0119 IL_0036: volatile. - IL_0038: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{F90E3F03-F070-45F0-AC05-0914EF70B327}'::'$$method0x600000e-1' + IL_0038: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{2FE2A6DB-2DD1-4526-BE2D-8029A91D5DA5}'::'$$method0x600000e-1' IL_003d: brtrue.s IL_0094 IL_003f: ldc.i4.6 @@ -959,9 +961,9 @@ IL_0088: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, !1) IL_008d: volatile. - IL_008f: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{F90E3F03-F070-45F0-AC05-0914EF70B327}'::'$$method0x600000e-1' + IL_008f: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{2FE2A6DB-2DD1-4526-BE2D-8029A91D5DA5}'::'$$method0x600000e-1' IL_0094: volatile. - IL_0096: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{F90E3F03-F070-45F0-AC05-0914EF70B327}'::'$$method0x600000e-1' + IL_0096: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{2FE2A6DB-2DD1-4526-BE2D-8029A91D5DA5}'::'$$method0x600000e-1' IL_009b: ldloc.s V_6 IL_009d: ldloca.s V_7 IL_009f: call instance bool class [mscorlib]System.Collections.Generic.Dictionary`2::TryGetValue(!0, @@ -1031,16 +1033,138 @@ IL_012e: 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 '{F90E3F03-F070-45F0-AC05-0914EF70B327}' +.class private auto ansi '{2FE2A6DB-2DD1-4526-BE2D-8029A91D5DA5}' 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 '$$method0x6000008-1' .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000009-1' .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x600000e-1' -} // end of class '{F90E3F03-F070-45F0-AC05-0914EF70B327}' +} // end of class '{2FE2A6DB-2DD1-4526-BE2D-8029A91D5DA5}' // ============================================================= diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il index 50e230ad0..c28f0e721 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il @@ -25,14 +25,14 @@ .ver 0:0:0:0 } .module Switch.dll -// MVID: {C83C2FCD-FA35-4E74-9418-9B91F2C0748A} +// MVID: {94058166-F81D-4A82-ABCC-FB400C785214} .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: 0x01020000 +// Image base: 0x00EF0000 // =============== CLASS MEMBERS DECLARATION =================== @@ -943,7 +943,7 @@ .method public hidebysig static void SwitchWithGoto(int32 i) cil managed { - // Code size 102 (0x66) + // Code size 113 (0x71) .maxstack 2 IL_0000: ldstr "SwitchWithGoto: " IL_0005: ldarg.0 @@ -958,26 +958,28 @@ IL_002f, IL_003b, IL_0045, - IL_0050) - IL_002d: br.s IL_005b + 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_005b + 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: ret + IL_004f: br.s IL_0066 - IL_0050: ldstr "four" - IL_0055: call void [mscorlib]System.Console::WriteLine(string) - IL_005a: ret + IL_0051: ldstr "four" + IL_0056: call void [mscorlib]System.Console::WriteLine(string) + IL_005b: ret - IL_005b: ldstr "default" - IL_0060: call void [mscorlib]System.Console::WriteLine(string) - IL_0065: 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[] @@ -1109,6 +1111,121 @@ 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 '' diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il index eb57faa5d..b74cb403c 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il @@ -25,14 +25,14 @@ .ver 0:0:0:0 } .module Switch.dll -// MVID: {B5801C4A-C979-4EC9-B5B2-295587FEEF30} +// MVID: {1E0E6114-7796-44C6-B88B-AA126D09F561} .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: 0x005D0000 +// Image base: 0x02FE0000 // =============== CLASS MEMBERS DECLARATION =================== @@ -1240,7 +1240,7 @@ .method public hidebysig static void SwitchWithGoto(int32 i) cil managed { - // Code size 122 (0x7a) + // Code size 133 (0x85) .maxstack 2 .locals init (int32 V_0) IL_0000: nop @@ -1285,7 +1285,7 @@ IL_005e: ldstr "four" IL_0063: call void [mscorlib]System.Console::WriteLine(string) IL_0068: nop - IL_0069: br.s IL_0079 + IL_0069: br.s IL_0084 IL_006b: nop IL_006c: ldstr "default" @@ -1293,7 +1293,10 @@ IL_0076: nop IL_0077: br.s IL_0079 - IL_0079: ret + IL_0079: ldstr "End of method" + IL_007e: call void [mscorlib]System.Console::WriteLine(string) + IL_0083: nop + IL_0084: ret } // end of method Switch::SwitchWithGoto .method private hidebysig static class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty[] @@ -1459,6 +1462,137 @@ IL_0108: 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 '' From 800a635663224d183d4238f3f337c8cf2d310d61 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Wed, 11 Oct 2017 23:21:36 +0200 Subject: [PATCH 45/65] Fix bug in SimplifyCascadingIfStatements --- .../IL/Transforms/SwitchOnStringTransform.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs index 0c8e6ef45..cd360d722 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs @@ -88,15 +88,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!firstBlockJump.MatchBranch(out var firstBlock)) return false; - bool isLegacy; List<(string, Block)> values = new List<(string, Block)>(); // match null check: this is used by the old C# compiler. if (condition.MatchCompEquals(out var left, out var right) && right.MatchLdNull() && left.MatchLdLoc(out var switchValueVar)) { - isLegacy = true; values.Add((null, firstBlock)); // Roslyn: match call to operator ==(string, string) } else if (MatchStringEqualityComparison(condition, out switchValueVar, out string value)) { - isLegacy = false; values.Add((value, firstBlock)); } else return false; // switchValueVar must be assigned only once and must be of type string. @@ -115,9 +112,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms // We didn't find enough cases, exit if (values.Count < 3) return false; - // The block after all cases should only be reachable from the previous block and the null-check (in legacy code). - if (currentCaseBlock.IncomingEdgeCount != (isLegacy ? 2 : 1)) - return false; 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(new LdLoc(switchValueVar), values.SelectArray(item => item.Item1)); From 87b350e4eb1b976b74fe032362ff9d1c722804eb Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Wed, 11 Oct 2017 23:36:41 +0200 Subject: [PATCH 46/65] Fix SwitchAnalysis not recursing into the default-block of IL switches. --- .../IL/ControlFlow/SwitchAnalysis.cs | 13 ++++++++----- ILSpy/Languages/ILAstLanguage.cs | 4 ++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/SwitchAnalysis.cs b/ICSharpCode.Decompiler/IL/ControlFlow/SwitchAnalysis.cs index 962c76f3f..20767b136 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/SwitchAnalysis.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/SwitchAnalysis.cs @@ -126,8 +126,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow } else if (block.Instructions.Last() is SwitchInstruction switchInst) { if (!(tailOnly || block.Instructions.Count == 1)) return false; - if (AnalyzeSwitch(switchInst, inputValues, out trueValues)) { + if (AnalyzeSwitch(switchInst, inputValues)) { ContainsILSwitch = true; // OK + return true; } else { // switch analysis failed (e.g. switchVar mismatch) return false; } @@ -148,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.IsLifted); - anyMatchValues = LongSet.Empty; long offset; if (MatchSwitchVar(inst.Value)) { offset = 0; @@ -177,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; } 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) { From f7f583056a79424c76fde9102d7d78e631745f9a Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 12 Oct 2017 00:12:20 +0200 Subject: [PATCH 47/65] Fix order of switch blocks. --- ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs index 7f72c6806..8473c17bf 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs @@ -81,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) @@ -115,6 +116,11 @@ 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())); } From d39a21afd78027a5e1b6647cc8b146755d266cb3 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 12 Oct 2017 15:06:23 +0200 Subject: [PATCH 48/65] SimplifyCascadingIfStatements: Remove extra variable declarations before switch, if possible. --- .../IL/Transforms/SwitchOnStringTransform.cs | 43 ++++++++++++++++--- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs index cd360d722..458445f32 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs @@ -41,9 +41,21 @@ namespace ICSharpCode.Decompiler.IL.Transforms bool changed = false; for (int i = block.Instructions.Count - 1; i >= 0; i--) { SwitchInstruction newSwitch; - if (SimplifyCascadingIfStatements(block.Instructions, i, out newSwitch)) { - block.Instructions[i + 1].ReplaceWith(newSwitch); - block.Instructions.RemoveAt(i); + if (SimplifyCascadingIfStatements(block.Instructions, i, out newSwitch, out var extraLoad, out var keepAssignmentBefore)) { + if (extraLoad) { + block.Instructions[i - 2].ReplaceWith(newSwitch); + block.Instructions.RemoveRange(i - 1, 3); + i -= 2; + } else { + if (keepAssignmentBefore) { + block.Instructions[i].ReplaceWith(newSwitch); + block.Instructions.RemoveAt(i + 1); + } else { + block.Instructions[i - 1].ReplaceWith(newSwitch); + block.Instructions.RemoveRange(i, 2); + i--; + } + } changed = true; continue; } @@ -77,9 +89,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms container.SortBlocks(deleteUnreachableBlocks: true); } - bool SimplifyCascadingIfStatements(InstructionCollection instructions, int i, out SwitchInstruction inst) + bool SimplifyCascadingIfStatements(InstructionCollection instructions, int i, out SwitchInstruction inst, out bool extraLoad, out bool keepAssignmentBefore) { inst = null; + extraLoad = false; + keepAssignmentBefore = false; + 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- @@ -99,6 +114,19 @@ namespace ICSharpCode.Decompiler.IL.Transforms // switchValueVar must be assigned only once and must be of type string. if (!switchValueVar.IsSingleDefinition || !switchValueVar.Type.IsKnownType(KnownTypeCode.String)) return false; + // if instruction must be preceeded by a stloc to the switchValueVar + if (instructions[i - 1].MatchStLoc(switchValueVar, out var switchValue)) { } + // in case of legacy code there are two stlocs: + // stloc switchValueVar(ldloc switchValue) + // stloc otherSwitchValueVar(ldloc switchValueVar) + // if (comp(ldloc switchValueVar == ldnull)) br nullCase + else if (i > 1 && instructions[i - 2].MatchStLoc(switchValueVar, out switchValue) && + instructions[i - 1].MatchStLoc(out var otherSwitchValueVar, out var switchValueCopyInst) && + switchValueCopyInst.MatchLdLoc(switchValueVar) && otherSwitchValueVar.IsSingleDefinition && switchValueVar.LoadCount == 2) + { + extraLoad = true; + } + else return false; // if instruction must be followed by a branch to the next case if (!(instructions.ElementAtOrDefault(i + 1) is Branch nextCaseJump)) return false; @@ -112,9 +140,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms // 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. + 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(new LdLoc(switchValueVar), values.SelectArray(item => item.Item1)); + var stringToInt = new StringToInt(switchValue, values.SelectArray(item => item.Item1)); inst = new SwitchInstruction(stringToInt); inst.Sections.AddRange(sections); return true; From 67272e58efc5b548054f1d5dab90410b31ed7d9d Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 12 Oct 2017 16:42:25 +0200 Subject: [PATCH 49/65] SwitchOnStringTransform: Remove extra variables introduced by switch pattern. --- .../TestCases/Pretty/Switch.cs | 2 +- .../IL/Transforms/SwitchOnStringTransform.cs | 46 +++++++++++++++---- 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs index 056229bff..53ebea954 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs @@ -413,7 +413,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void SwitchWithComplexCondition(string[] args) { - switch (args.Length == 0 ? "dummy" : args[0]) { + switch ((args.Length == 0) ? "dummy" : args[0]) { case "a": { Console.WriteLine("a"); break; diff --git a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs index 458445f32..d8eac46fe 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs @@ -65,17 +65,28 @@ namespace ICSharpCode.Decompiler.IL.Transforms changed = true; continue; } - if (MatchLegacySwitchOnStringWithDict(block.Instructions, i, out newSwitch)) { + if (MatchLegacySwitchOnStringWithDict(block.Instructions, i, out newSwitch, out keepAssignmentBefore)) { block.Instructions[i + 1].ReplaceWith(newSwitch); - block.Instructions.RemoveAt(i); + if (keepAssignmentBefore) { + block.Instructions.RemoveAt(i); + i--; + } else { + block.Instructions.RemoveRange(i - 1, 2); + i -= 2; + } changed = true; continue; } - if (MatchRoslynSwitchOnString(block.Instructions, i, out newSwitch)) { - block.Instructions[i - 1].ReplaceWith(newSwitch); - block.Instructions.RemoveAt(i); + if (MatchRoslynSwitchOnString(block.Instructions, i, out newSwitch, out keepAssignmentBefore)) { + block.Instructions[i].ReplaceWith(newSwitch); + if (keepAssignmentBefore) { + block.Instructions.RemoveAt(i - 1); + i--; + } else { + block.Instructions.RemoveRange(i - 2, 2); + i -= 2; + } changed = true; - i--; continue; } } @@ -217,14 +228,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// /// Matches the C# 2.0 switch-on-string pattern, which uses Dictionary<string, int>. /// - bool MatchLegacySwitchOnStringWithDict(InstructionCollection instructions, int i, out SwitchInstruction inst) + bool MatchLegacySwitchOnStringWithDict(InstructionCollection instructions, int i, out SwitchInstruction inst, out bool keepAssignmentBefore) { inst = null; + keepAssignmentBefore = false; if (i < 1) return false; // match first block: checking switch-value for null 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() && (left.Match(switchValue).Success || left.MatchLdLoc(switchValueVar)))) @@ -279,7 +293,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms stringValues.Add(null); sections.Add(new SwitchSection() { Labels = label, Body = new Branch(nullValueCaseBlock) }); } - var stringToInt = new StringToInt(new LdLoc(switchValueVar), stringValues.ToArray()); + if (switchValueVar.LoadCount > 2) { + switchValue = new LdLoc(switchValueVar); + keepAssignmentBefore = true; + } + var stringToInt = new StringToInt(switchValue, stringValues.ToArray()); inst = new SwitchInstruction(stringToInt); inst.Sections.AddRange(sections); return true; @@ -434,9 +452,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; } - bool MatchRoslynSwitchOnString(InstructionCollection instructions, int i, out SwitchInstruction inst) + bool MatchRoslynSwitchOnString(InstructionCollection instructions, int i, out SwitchInstruction inst, out bool keepAssignmentBefore) { inst = null; + keepAssignmentBefore = false; if (i < 1) return false; if (!(instructions[i] is SwitchInstruction switchInst && switchInst.Value.MatchLdLoc(out var targetVar) && MatchComputeStringHashCall(instructions[i - 1], targetVar, out var switchValue))) @@ -473,8 +492,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms } stringValues.Add((index++, stringValue, body)); } + ILInstruction switchValueInst = switchValue; + if (i > 1 && instructions[i - 2].MatchStLoc(switchValue.Variable, out var switchValueTmp) && + switchValue.Variable.IsSingleDefinition && switchValue.Variable.LoadCount == switchInst.Sections.Count) { + switchValueInst = switchValueTmp; + } else { + keepAssignmentBefore = true; + } var defaultLabel = new LongSet(new LongInterval(0, index)).Invert(); - var value = new StringToInt(switchValue.Clone(), stringValues.Select(item => item.Item2).ToArray()); + var value = new StringToInt(switchValueInst, stringValues.Select(item => item.Item2).ToArray()); inst = new SwitchInstruction(value); inst.Sections.AddRange(stringValues.Select(section => new SwitchSection { Labels = new Util.LongSet(section.Item1), Body = new Branch(section.Item3) })); inst.Sections.Add(new SwitchSection { Labels = defaultLabel, Body = defaultBranch }); From 9e5d4c10f76ca587cdbbc7532590fb03422e585d Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 12 Oct 2017 23:13:03 +0200 Subject: [PATCH 50/65] Fix array.Length == 0 in Roslyn. --- .../IL/Transforms/ExpressionTransforms.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) 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) { From 12e39cf63c5754b16d982c2cd29d6de003d239b2 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 12 Oct 2017 23:14:01 +0200 Subject: [PATCH 51/65] Fix switch exit points. --- ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs index 06dabbfcc..ce3d0f43d 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs @@ -235,7 +235,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow /// This method must not write to the Visited flags on the CFG. ControlFlowNode FindExitPoint(ControlFlowNode loopHead, IReadOnlyList naturalLoop, bool treatBackEdgesAsExits) { - treatBackEdgesAsExits = false; 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 @@ -314,7 +313,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow return true; } foreach (var succ in node.Successors) { - if (loopHead.Dominates(succ) && !exitPoint.Dominates(succ)) + if (loopHead != succ && loopHead.Dominates(succ) && !exitPoint.Dominates(succ)) return false; } foreach (var child in node.DominatorTreeChildren) { From 6ffec75c247fcace3e6d06d31152dd3a6d9b5861 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 12 Oct 2017 23:16:59 +0200 Subject: [PATCH 52/65] Run another round of SplitVariables so that the NullableLiftingTransform can work in the switch expression. --- ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 355464e64..dff75e4c8 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -84,6 +84,7 @@ namespace ICSharpCode.Decompiler.CSharp 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, From e33a010cc75494c296147dca8c4ea9f4fc9df8b6 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 12 Oct 2017 23:17:37 +0200 Subject: [PATCH 53/65] Remove compiler-generated variable in while (true) loops. --- .../ControlFlow/ControlFlowSimplification.cs | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) 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); + } } } From 63b626716f8b7ee221ca556be3dd1aee554bd484 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 13 Oct 2017 13:26:57 +0200 Subject: [PATCH 54/65] Refactoring and cleanup of SimplifyCascadingIfStatements and other switch transforms. --- .../IL/Transforms/SwitchOnStringTransform.cs | 103 +++++++++++------- 1 file changed, 62 insertions(+), 41 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs index d8eac46fe..2ff02e41f 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs @@ -115,36 +115,42 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (!firstBlockJump.MatchBranch(out var firstBlock)) return false; List<(string, Block)> values = new List<(string, Block)>(); - // match null check: this is used by the old C# compiler. - if (condition.MatchCompEquals(out var left, out var right) && right.MatchLdNull() && left.MatchLdLoc(out var switchValueVar)) { - values.Add((null, firstBlock)); - // Roslyn: match call to operator ==(string, string) - } else if (MatchStringEqualityComparison(condition, out switchValueVar, out string value)) { + ILInstruction switchValue = null; + // Roslyn: match call to operator ==(string, string) + if (MatchStringEqualityComparison(condition, out var switchValueVar, out string value)) { values.Add((value, firstBlock)); - } else return false; - // switchValueVar must be assigned only once and must be of type string. - if (!switchValueVar.IsSingleDefinition || !switchValueVar.Type.IsKnownType(KnownTypeCode.String)) - return false; - // if instruction must be preceeded by a stloc to the switchValueVar - if (instructions[i - 1].MatchStLoc(switchValueVar, out var switchValue)) { } - // in case of legacy code there are two stlocs: - // stloc switchValueVar(ldloc switchValue) - // stloc otherSwitchValueVar(ldloc switchValueVar) - // if (comp(ldloc switchValueVar == ldnull)) br nullCase - else if (i > 1 && instructions[i - 2].MatchStLoc(switchValueVar, out switchValue) && - instructions[i - 1].MatchStLoc(out var otherSwitchValueVar, out var switchValueCopyInst) && - switchValueCopyInst.MatchLdLoc(switchValueVar) && otherSwitchValueVar.IsSingleDefinition && switchValueVar.LoadCount == 2) - { - extraLoad = true; + if(!instructions[i - 1].MatchStLoc(switchValueVar, out switchValue)) { + switchValue = new LdLoc(switchValueVar); + } + } else { + // match null check with different variable: + // this is used by the old C# compiler. + if (MatchCompEqualsNull(condition, out var otherSwitchValueVar)) { + values.Add((null, firstBlock)); + } else { + return false; + } + + // in case of optimized legacy code there are two stlocs: + // stloc otherSwitchValueVar(ldloc switchValue) + // stloc switchValueVar(ldloc otherSwitchValueVar) + // if (comp(ldloc otherSwitchValueVar == ldnull)) br nullCase + if (i > 1 && otherSwitchValueVar != null && instructions[i - 2].MatchStLoc(otherSwitchValueVar, out switchValue) + && instructions[i - 1].MatchStLoc(out switchValueVar, out var switchValueCopyInst) + && switchValueCopyInst.MatchLdLoc(otherSwitchValueVar) && otherSwitchValueVar.IsSingleDefinition && otherSwitchValueVar.LoadCount == 2) { + extraLoad = true; + } else if (instructions[i - 1].MatchStLoc(out switchValueVar, out switchValue)) { + // unoptimized legacy switch + } } - else return false; + // 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, ref switchValueVar, out string value, out Block block)) != null) { + while ((nextCaseBlock = MatchCaseBlock(currentCaseBlock, switchValueVar, out value, out Block block)) != null) { values.Add((value, block)); currentCaseBlock = nextCaseBlock; } @@ -169,13 +175,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// 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, ref ILVariable switchVariable, out string value, out Block caseBlock) + Block MatchCaseBlock(Block currentBlock, ILVariable switchVariable, out string value, out Block caseBlock) { value = null; caseBlock = null; @@ -196,19 +206,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (!currentBlock.Instructions[1].MatchBranch(out nextBlock)) return null; } - // Sometimes the switch pattern uses one variable at the beginning for null checks - // and another variable for the if-else-if-else-pattern. - // both variables must be only assigned once and be of the type: System.String. - if (!MatchStringEqualityComparison(condition, out var newSwitchVariable, out value)) - return null; - if (!newSwitchVariable.IsSingleDefinition) - return null; - // if the used variable differs and both variables are not related, return null: - if (switchVariable != newSwitchVariable && !(IsInitializedBy(switchVariable, newSwitchVariable) || IsInitializedBy(newSwitchVariable, switchVariable))) - return null; - if (!newSwitchVariable.Type.IsKnownType(KnownTypeCode.String)) + if (!MatchStringEqualityComparison(condition, out var v, out value)) { + if (MatchCompEqualsNull(condition, out v)) { + value = null; + } else { + return null; + } + } + if (v != switchVariable) return null; - switchVariable = newSwitchVariable; return nextBlock; } @@ -482,8 +488,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!bodyBranch.MatchBranch(out Block body)) return false; - if (!MatchStringEqualityComparison(condition, switchValue.Variable, out string stringValue)) { - if (condition.MatchLogicNot(out condition) && MatchStringEqualityComparison(condition, switchValue.Variable, out stringValue)) { + if (!MatchStringEqualityOrNullComparison(condition, switchValue.Variable, out string stringValue)) { + if (condition.MatchLogicNot(out condition) && MatchStringEqualityOrNullComparison(condition, switchValue.Variable, out stringValue)) { if (!target.Instructions[1].MatchBranch(out Block exit)) return false; body = exit; @@ -520,9 +526,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; } - bool MatchStringEqualityComparison(ILInstruction condition, ILVariable variable, out string stringValue) + bool MatchStringEqualityOrNullComparison(ILInstruction condition, ILVariable variable, out string stringValue) { - return MatchStringEqualityComparison(condition, out var v, out stringValue) && v == variable; + if (!MatchStringEqualityComparison(condition, out var v, out stringValue)) { + if (!MatchCompEqualsNull(condition, out v)) + return false; + stringValue = null; + } + + return v == variable; } bool MatchStringEqualityComparison(ILInstruction condition, out ILVariable variable, out string stringValue) @@ -535,9 +547,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms right = c.Arguments[1]; if (!right.MatchLdStr(out stringValue)) return false; - } else if (condition.MatchCompEquals(out left, out right) && right.MatchLdNull()) { - } else return false; + } else { + return false; + } return left.MatchLdLoc(out variable); } + + bool MatchCompEqualsNull(ILInstruction condition, out ILVariable variable) + { + variable = null; + if (!condition.MatchCompEquals(out var left, out var right)) + return false; + return right.MatchLdNull() && left.MatchLdLoc(out variable); + } } } From c2761b0e0246aeeb4074d93174a218a59c80faa8 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 13 Oct 2017 13:28:11 +0200 Subject: [PATCH 55/65] Update test cases for switch. --- .../PrettyTestRunner.cs | 2 +- .../TestCases/Pretty/Switch.cs | 33 +- .../TestCases/Pretty/Switch.il | 807 ++++++++++-------- .../TestCases/Pretty/Switch.opt.il | 529 ++++++------ .../TestCases/Pretty/Switch.opt.roslyn.il | 84 +- .../TestCases/Pretty/Switch.roslyn.il | 785 +++++++++-------- 6 files changed, 1216 insertions(+), 1024 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs index 1acbd0023..6e340a2d3 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -97,7 +97,7 @@ namespace ICSharpCode.Decompiler.Tests Run(cscOptions: cscOptions); } - [Test, Ignore("unnecessary casts on null literals, control-flow issues with switch in loops, goto, goto case, etc.")] + [Test] public void Switch([ValueSource("defaultOptions")] CompilerOptions cscOptions) { Run(cscOptions: cscOptions); diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs index 53ebea954..45c1c8274 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs @@ -213,6 +213,25 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } + 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); @@ -248,8 +267,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static string SwitchOverString2() { Console.WriteLine("SwitchOverString2:"); - string userName = Environment.UserName; - switch (userName) { + switch (Environment.UserName) { case "First case": { return "Text1"; } @@ -318,10 +336,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty Console.WriteLine("two"); break; } - case 3: { - Console.WriteLine("three"); - continue; - } + //case 3: { + // Console.WriteLine("three"); + // continue; + // } case 4: { Console.WriteLine("four"); return; @@ -376,8 +394,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty SetProperty[] properties = Switch.GetProperties(); for (int i = 0; i < properties.Length; i++) { SetProperty setProperty = properties[i]; - string name = setProperty.Property.Name; - switch (name) { + switch (setProperty.Property.Name) { case "Name1": { setProperty.Set = 1; list.Add(setProperty); diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il index 91f9e4ff5..e4f06b1f6 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il @@ -10,7 +10,7 @@ .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } -.assembly y0cd2lcb +.assembly yojdxjhu { .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. @@ -20,15 +20,15 @@ .hash algorithm 0x00008004 .ver 0:0:0:0 } -.module y0cd2lcb.dll -// MVID: {85E7C039-2097-47F9-A636-4F4E3015541A} +.module yojdxjhu.dll +// MVID: {7A918135-BE28-4F14-9240-86E34BA33540} .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: 0x02630000 +// Image base: 0x032F0000 // =============== CLASS MEMBERS DECLARATION =================== @@ -634,6 +634,63 @@ IL_0065: ret } // end of method Switch::ShortSwitchOverString + .method public hidebysig static string + ShortSwitchOverStringWithNullCase(string text) cil managed + { + // Code size 89 (0x59) + .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_0045 + + 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_003c + + IL_0031: br.s IL_004e + + IL_0033: nop + IL_0034: ldstr "Text1" + IL_0039: stloc.0 + IL_003a: br.s IL_0057 + + IL_003c: nop + IL_003d: ldstr "Text2" + IL_0042: stloc.0 + IL_0043: br.s IL_0057 + + IL_0045: nop + IL_0046: ldstr "null" + IL_004b: stloc.0 + IL_004c: br.s IL_0057 + + IL_004e: nop + IL_004f: ldstr "Default" + IL_0054: stloc.0 + IL_0055: br.s IL_0057 + + IL_0057: ldloc.0 + IL_0058: ret + } // end of method Switch::ShortSwitchOverStringWithNullCase + .method public hidebysig static string SwitchOverString1(string text) cil managed { @@ -655,7 +712,7 @@ IL_0015: brfalse IL_00ef IL_001a: volatile. - IL_001c: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{85E7C039-2097-47F9-A636-4F4E3015541A}'::'$$method0x6000008-1' + IL_001c: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{7A918135-BE28-4F14-9240-86E34BA33540}'::'$$method0x6000009-1' IL_0021: brtrue.s IL_0084 IL_0023: ldc.i4.7 @@ -696,9 +753,9 @@ 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 '{85E7C039-2097-47F9-A636-4F4E3015541A}'::'$$method0x6000008-1' + IL_007f: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{7A918135-BE28-4F14-9240-86E34BA33540}'::'$$method0x6000009-1' IL_0084: volatile. - IL_0086: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{85E7C039-2097-47F9-A636-4F4E3015541A}'::'$$method0x6000008-1' + IL_0086: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{7A918135-BE28-4F14-9240-86E34BA33540}'::'$$method0x6000009-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, @@ -763,171 +820,168 @@ .method public hidebysig static string SwitchOverString2() cil managed { - // Code size 368 (0x170) + // Code size 366 (0x16e) .maxstack 4 .locals init (string V_0, string V_1, - string V_2, - int32 V_3) + 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.0 - IL_0012: ldloc.0 - IL_0013: stloc.2 - IL_0014: ldloc.2 - IL_0015: brfalse IL_0165 - - IL_001a: volatile. - IL_001c: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{85E7C039-2097-47F9-A636-4F4E3015541A}'::'$$method0x6000009-1' - IL_0021: brtrue IL_00ba - - IL_0026: ldc.i4.s 11 - IL_0028: newobj instance void class [mscorlib]System.Collections.Generic.Dictionary`2::.ctor(int32) - IL_002d: dup - IL_002e: ldstr "First case" - IL_0033: ldc.i4.0 - IL_0034: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + IL_0011: stloc.1 + IL_0012: ldloc.1 + IL_0013: brfalse IL_0163 + + IL_0018: volatile. + IL_001a: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{7A918135-BE28-4F14-9240-86E34BA33540}'::'$$method0x600000a-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_0039: dup - IL_003a: ldstr "Second case" - IL_003f: ldc.i4.1 - IL_0040: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + 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_0045: dup - IL_0046: ldstr "Third case" - IL_004b: ldc.i4.2 - IL_004c: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + 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_0051: dup - IL_0052: ldstr "Fourth case" - IL_0057: ldc.i4.3 - IL_0058: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + 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_005d: dup - IL_005e: ldstr "Fifth case" - IL_0063: ldc.i4.4 - IL_0064: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + 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_0069: dup - IL_006a: ldstr "Sixth case" - IL_006f: ldc.i4.5 - IL_0070: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + 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_0075: dup - IL_0076: ldstr "Seventh case" - IL_007b: ldc.i4.6 - IL_007c: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + 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_0081: dup - IL_0082: ldstr "Eighth case" - IL_0087: ldc.i4.7 - IL_0088: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + 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_008d: dup - IL_008e: ldstr "Ninth case" - IL_0093: ldc.i4.8 - IL_0094: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + 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_0099: dup - IL_009a: ldstr "Tenth case" - IL_009f: ldc.i4.s 9 - IL_00a1: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + 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_00a6: dup - IL_00a7: ldstr "Eleventh case" - IL_00ac: ldc.i4.s 10 - IL_00ae: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + 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_00b3: volatile. - IL_00b5: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{85E7C039-2097-47F9-A636-4F4E3015541A}'::'$$method0x6000009-1' - IL_00ba: volatile. - IL_00bc: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{85E7C039-2097-47F9-A636-4F4E3015541A}'::'$$method0x6000009-1' - IL_00c1: ldloc.2 - IL_00c2: ldloca.s V_3 - IL_00c4: call instance bool class [mscorlib]System.Collections.Generic.Dictionary`2::TryGetValue(!0, + IL_00b1: volatile. + IL_00b3: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{7A918135-BE28-4F14-9240-86E34BA33540}'::'$$method0x600000a-1' + IL_00b8: volatile. + IL_00ba: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{7A918135-BE28-4F14-9240-86E34BA33540}'::'$$method0x600000a-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_00c9: brfalse IL_0165 + IL_00c7: brfalse IL_0163 + + IL_00cc: ldloc.2 + IL_00cd: switch ( + IL_0100, + IL_0109, + IL_0112, + IL_011b, + IL_0124, + IL_012d, + IL_0136, + IL_013f, + IL_0148, + IL_0151, + IL_015a) + IL_00fe: br.s IL_0163 - IL_00ce: ldloc.3 - IL_00cf: switch ( - IL_0102, - IL_010b, - IL_0114, - IL_011d, - IL_0126, - IL_012f, - IL_0138, - IL_0141, - IL_014a, - IL_0153, - IL_015c) - IL_0100: br.s IL_0165 - - IL_0102: nop - IL_0103: ldstr "Text1" - IL_0108: stloc.1 - IL_0109: br.s IL_016e - - IL_010b: nop - IL_010c: ldstr "Text2" - IL_0111: stloc.1 - IL_0112: br.s IL_016e - - IL_0114: nop - IL_0115: ldstr "Text3" - IL_011a: stloc.1 - IL_011b: br.s IL_016e + IL_0100: nop + IL_0101: ldstr "Text1" + IL_0106: stloc.0 + IL_0107: br.s IL_016c - IL_011d: nop - IL_011e: ldstr "Text4" - IL_0123: stloc.1 - IL_0124: br.s IL_016e + IL_0109: nop + IL_010a: ldstr "Text2" + IL_010f: stloc.0 + IL_0110: br.s IL_016c - IL_0126: nop - IL_0127: ldstr "Text5" - IL_012c: stloc.1 - IL_012d: br.s IL_016e - - IL_012f: nop - IL_0130: ldstr "Text6" - IL_0135: stloc.1 - IL_0136: br.s IL_016e - - IL_0138: nop - IL_0139: ldstr "Text7" - IL_013e: stloc.1 - IL_013f: br.s IL_016e - - IL_0141: nop - IL_0142: ldstr "Text8" - IL_0147: stloc.1 - IL_0148: br.s IL_016e - - IL_014a: nop - IL_014b: ldstr "Text9" - IL_0150: stloc.1 - IL_0151: br.s IL_016e - - IL_0153: nop - IL_0154: ldstr "Text10" - IL_0159: stloc.1 - IL_015a: br.s IL_016e - - IL_015c: nop - IL_015d: ldstr "Text11" - IL_0162: stloc.1 - IL_0163: br.s IL_016e - - IL_0165: nop - IL_0166: ldstr "Default" - IL_016b: stloc.1 - IL_016c: br.s IL_016e - - IL_016e: ldloc.1 - IL_016f: ret + IL_0112: nop + IL_0113: ldstr "Text3" + IL_0118: stloc.0 + IL_0119: br.s IL_016c + + IL_011b: nop + IL_011c: ldstr "Text4" + IL_0121: stloc.0 + IL_0122: br.s IL_016c + + IL_0124: nop + IL_0125: ldstr "Text5" + IL_012a: stloc.0 + IL_012b: br.s IL_016c + + IL_012d: nop + IL_012e: ldstr "Text6" + IL_0133: stloc.0 + IL_0134: br.s IL_016c + + IL_0136: nop + IL_0137: ldstr "Text7" + IL_013c: stloc.0 + IL_013d: br.s IL_016c + + IL_013f: nop + IL_0140: ldstr "Text8" + IL_0145: stloc.0 + IL_0146: br.s IL_016c + + IL_0148: nop + IL_0149: ldstr "Text9" + IL_014e: stloc.0 + IL_014f: br.s IL_016c + + IL_0151: nop + IL_0152: ldstr "Text10" + IL_0157: stloc.0 + IL_0158: br.s IL_016c + + IL_015a: nop + IL_015b: ldstr "Text11" + IL_0160: stloc.0 + IL_0161: br.s IL_016c + + IL_0163: nop + IL_0164: ldstr "Default" + IL_0169: stloc.0 + IL_016a: br.s IL_016c + + IL_016c: ldloc.0 + IL_016d: ret } // end of method Switch::SwitchOverString2 .method public hidebysig static string @@ -974,7 +1028,7 @@ .method public hidebysig static void SwitchInLoop(int32 i) cil managed { - // Code size 146 (0x92) + // Code size 132 (0x84) .maxstack 2 .locals init (int32 V_0, bool V_1) @@ -986,7 +1040,7 @@ object) IL_0011: call void [mscorlib]System.Console::WriteLine(string) IL_0016: nop - IL_0017: br.s IL_008d + IL_0017: br.s IL_007f IL_0019: nop IL_001a: ldarg.0 @@ -997,53 +1051,47 @@ IL_001f: switch ( IL_0036, IL_0044, - IL_0052, - IL_0060) - IL_0034: br.s IL_006e + IL_0060, + IL_0052) + IL_0034: br.s IL_0060 IL_0036: nop IL_0037: ldstr "one" IL_003c: call void [mscorlib]System.Console::WriteLine(string) IL_0041: nop - IL_0042: br.s IL_0087 + IL_0042: br.s IL_0079 IL_0044: nop IL_0045: ldstr "two" IL_004a: call void [mscorlib]System.Console::WriteLine(string) IL_004f: nop - IL_0050: br.s IL_0087 + IL_0050: br.s IL_0079 IL_0052: nop - IL_0053: ldstr "three" + IL_0053: ldstr "four" IL_0058: call void [mscorlib]System.Console::WriteLine(string) IL_005d: nop - IL_005e: br.s IL_008d + IL_005e: br.s IL_0083 IL_0060: nop - IL_0061: ldstr "four" + IL_0061: ldstr "default" IL_0066: call void [mscorlib]System.Console::WriteLine(string) IL_006b: nop - IL_006c: br.s IL_0091 - - IL_006e: nop - IL_006f: ldstr "default" - IL_0074: call void [mscorlib]System.Console::WriteLine(string) - IL_0079: nop - IL_007a: ldstr "more code" - IL_007f: call void [mscorlib]System.Console::WriteLine(string) - IL_0084: nop - IL_0085: br.s IL_0091 - - IL_0087: ldarg.0 - IL_0088: ldc.i4.1 - IL_0089: add - IL_008a: starg.s i - IL_008c: nop - IL_008d: ldc.i4.1 - IL_008e: stloc.1 - IL_008f: br.s IL_0019 - - IL_0091: ret + IL_006c: ldstr "more code" + IL_0071: call void [mscorlib]System.Console::WriteLine(string) + IL_0076: nop + IL_0077: br.s IL_0083 + + IL_0079: ldarg.0 + IL_007a: ldc.i4.1 + IL_007b: add + IL_007c: starg.s i + IL_007e: nop + IL_007f: ldc.i4.1 + IL_0080: stloc.1 + IL_0081: br.s IL_0019 + + IL_0083: ret } // end of method Switch::SwitchInLoop .method public hidebysig static void SwitchWithGoto(int32 i) cil managed @@ -1125,7 +1173,7 @@ .method public hidebysig static void SwitchOnStringInForLoop() cil managed { - // Code size 334 (0x14e) + // Code size 330 (0x14a) .maxstack 4 .locals init (class [mscorlib]System.Collections.Generic.List`1 V_0, class [mscorlib]System.Collections.Generic.List`1 V_1, @@ -1133,9 +1181,8 @@ int32 V_3, class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty V_4, string V_5, - string V_6, - int32 V_7, - bool V_8) + 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 @@ -1145,7 +1192,7 @@ IL_0012: stloc.2 IL_0013: ldc.i4.0 IL_0014: stloc.3 - IL_0015: br IL_013e + IL_0015: br IL_013a IL_001a: nop IL_001b: ldloc.2 @@ -1157,144 +1204,142 @@ 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: stloc.s V_6 - IL_0032: ldloc.s V_6 - IL_0034: brfalse IL_012d - - IL_0039: volatile. - IL_003b: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{85E7C039-2097-47F9-A636-4F4E3015541A}'::'$$method0x600000e-1' - IL_0040: brtrue.s IL_0097 - - IL_0042: ldc.i4.6 - IL_0043: newobj instance void class [mscorlib]System.Collections.Generic.Dictionary`2::.ctor(int32) - IL_0048: dup - IL_0049: ldstr "Name1" - IL_004e: ldc.i4.0 - IL_004f: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + IL_0030: brfalse IL_0129 + + IL_0035: volatile. + IL_0037: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{7A918135-BE28-4F14-9240-86E34BA33540}'::'$$method0x600000f-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_0054: dup - IL_0055: ldstr "Name2" - IL_005a: ldc.i4.1 - IL_005b: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + 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_0060: dup - IL_0061: ldstr "Name3" - IL_0066: ldc.i4.2 - IL_0067: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + 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_006c: dup - IL_006d: ldstr "Name4" - IL_0072: ldc.i4.3 - IL_0073: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + 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_0078: dup - IL_0079: ldstr "Name5" - IL_007e: ldc.i4.4 - IL_007f: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + 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_0084: dup - IL_0085: ldstr "Name6" - IL_008a: ldc.i4.5 - IL_008b: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + 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_0090: volatile. - IL_0092: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{85E7C039-2097-47F9-A636-4F4E3015541A}'::'$$method0x600000e-1' - IL_0097: volatile. - IL_0099: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{85E7C039-2097-47F9-A636-4F4E3015541A}'::'$$method0x600000e-1' - IL_009e: ldloc.s V_6 - IL_00a0: ldloca.s V_7 - IL_00a2: call instance bool class [mscorlib]System.Collections.Generic.Dictionary`2::TryGetValue(!0, + IL_008c: volatile. + IL_008e: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{7A918135-BE28-4F14-9240-86E34BA33540}'::'$$method0x600000f-1' + IL_0093: volatile. + IL_0095: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{7A918135-BE28-4F14-9240-86E34BA33540}'::'$$method0x600000f-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_00a7: brfalse IL_012d - - IL_00ac: ldloc.s V_7 - IL_00ae: switch ( - IL_00cd, - IL_00e2, - IL_00f7, - IL_010c, - IL_0121, - IL_0121) - IL_00cb: br.s IL_012d - - IL_00cd: nop - IL_00ce: ldloc.s V_4 - IL_00d0: ldc.i4.1 - IL_00d1: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) - IL_00d6: nop - IL_00d7: ldloc.0 - IL_00d8: ldloc.s V_4 - IL_00da: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) - IL_00df: nop - IL_00e0: br.s IL_0139 - - IL_00e2: nop - IL_00e3: ldloc.s V_4 - IL_00e5: ldc.i4.2 - IL_00e6: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) - IL_00eb: nop - IL_00ec: ldloc.0 - IL_00ed: ldloc.s V_4 - IL_00ef: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) - IL_00f4: nop - IL_00f5: br.s IL_0139 - - IL_00f7: nop - IL_00f8: ldloc.s V_4 - IL_00fa: ldc.i4.3 - IL_00fb: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) - IL_0100: nop - 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: nop - IL_010a: br.s IL_0139 - - IL_010c: nop - IL_010d: ldloc.s V_4 - IL_010f: ldc.i4.4 - IL_0110: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) - IL_0115: nop - 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_0139 - - IL_0121: nop - IL_0122: ldloc.0 - IL_0123: ldloc.s V_4 - IL_0125: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) - IL_012a: nop - IL_012b: br.s IL_0139 - - IL_012d: nop - IL_012e: ldloc.1 - IL_012f: ldloc.s V_4 - IL_0131: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) - IL_0136: nop - IL_0137: br.s IL_0139 + IL_00a3: brfalse IL_0129 + + IL_00a8: ldloc.s V_6 + IL_00aa: switch ( + IL_00c9, + IL_00de, + IL_00f3, + IL_0108, + IL_011d, + IL_011d) + IL_00c7: br.s IL_0129 + + IL_00c9: nop + IL_00ca: ldloc.s V_4 + IL_00cc: ldc.i4.1 + IL_00cd: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + IL_00d2: nop + IL_00d3: ldloc.0 + IL_00d4: ldloc.s V_4 + IL_00d6: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_00db: nop + IL_00dc: br.s IL_0135 + + IL_00de: nop + IL_00df: ldloc.s V_4 + IL_00e1: ldc.i4.2 + IL_00e2: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + IL_00e7: nop + IL_00e8: ldloc.0 + IL_00e9: ldloc.s V_4 + IL_00eb: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_00f0: nop + IL_00f1: br.s IL_0135 + + IL_00f3: nop + IL_00f4: ldloc.s V_4 + IL_00f6: ldc.i4.3 + IL_00f7: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + IL_00fc: nop + IL_00fd: ldloc.0 + IL_00fe: ldloc.s V_4 + IL_0100: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_0105: nop + IL_0106: br.s IL_0135 + + IL_0108: nop + IL_0109: ldloc.s V_4 + IL_010b: ldc.i4.4 + IL_010c: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + IL_0111: nop + IL_0112: ldloc.0 + IL_0113: ldloc.s V_4 + IL_0115: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_011a: nop + IL_011b: br.s IL_0135 - IL_0139: nop + IL_011d: nop + IL_011e: ldloc.0 + IL_011f: ldloc.s V_4 + IL_0121: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_0126: nop + IL_0127: br.s IL_0135 + + IL_0129: nop + IL_012a: ldloc.1 + IL_012b: ldloc.s V_4 + IL_012d: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_0132: nop + IL_0133: br.s IL_0135 + + IL_0135: nop + IL_0136: ldloc.3 + IL_0137: ldc.i4.1 + IL_0138: add + IL_0139: stloc.3 IL_013a: ldloc.3 - IL_013b: ldc.i4.1 - IL_013c: add - IL_013d: stloc.3 - IL_013e: ldloc.3 - IL_013f: ldloc.2 - IL_0140: ldlen - IL_0141: conv.i4 - IL_0142: clt - IL_0144: stloc.s V_8 - IL_0146: ldloc.s V_8 - IL_0148: brtrue IL_001a - - IL_014d: ret + IL_013b: ldloc.2 + IL_013c: ldlen + IL_013d: conv.i4 + IL_013e: clt + IL_0140: stloc.s V_7 + IL_0142: ldloc.s V_7 + IL_0144: brtrue IL_001a + + IL_0149: ret } // end of method Switch::SwitchOnStringInForLoop .method public hidebysig static void SwitchWithComplexCondition(string[] args) cil managed { - // Code size 139 (0x8b) + // Code size 143 (0x8f) .maxstack 2 .locals init (string V_0) IL_0000: nop @@ -1312,7 +1357,7 @@ IL_0010: nop IL_0011: stloc.0 IL_0012: ldloc.0 - IL_0013: brfalse.s IL_007f + IL_0013: brfalse.s IL_0083 IL_0015: ldloc.0 IL_0016: ldstr "a" @@ -1324,51 +1369,55 @@ IL_0023: ldstr "b" IL_0028: call bool [mscorlib]System.String::op_Equality(string, string) - IL_002d: brtrue.s IL_0058 + IL_002d: brtrue.s IL_0059 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_003a: brtrue.s IL_0067 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 + IL_0047: brtrue.s IL_0075 + + IL_0049: br.s IL_0083 + + IL_004b: nop + IL_004c: ldstr "a" + IL_0051: call void [mscorlib]System.Console::WriteLine(string) + IL_0056: nop + IL_0057: br.s IL_0083 + + IL_0059: nop + IL_005a: ldstr "b" + IL_005f: call void [mscorlib]System.Console::WriteLine(string) + IL_0064: nop + IL_0065: br.s IL_0083 + + IL_0067: nop + IL_0068: ldstr "c" + IL_006d: call void [mscorlib]System.Console::WriteLine(string) + IL_0072: nop + IL_0073: br.s IL_0083 + + IL_0075: nop + IL_0076: ldstr "d" + IL_007b: call void [mscorlib]System.Console::WriteLine(string) + IL_0080: nop + IL_0081: br.s IL_0083 + + IL_0083: ldstr "end" + IL_0088: call void [mscorlib]System.Console::WriteLine(string) + IL_008d: nop + IL_008e: ret } // end of method Switch::SwitchWithComplexCondition .method public hidebysig static void SwitchWithArray(string[] args) cil managed { - // Code size 126 (0x7e) + // Code size 130 (0x82) .maxstack 2 .locals init (string V_0) IL_0000: nop @@ -1377,7 +1426,7 @@ IL_0003: ldelem.ref IL_0004: stloc.0 IL_0005: ldloc.0 - IL_0006: brfalse.s IL_0072 + IL_0006: brfalse.s IL_0076 IL_0008: ldloc.0 IL_0009: ldstr "a" @@ -1389,58 +1438,62 @@ IL_0016: ldstr "b" IL_001b: call bool [mscorlib]System.String::op_Equality(string, string) - IL_0020: brtrue.s IL_004b + IL_0020: brtrue.s IL_004c 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_002d: brtrue.s IL_005a 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 + IL_003a: brtrue.s IL_0068 + + IL_003c: br.s IL_0076 + + IL_003e: nop + IL_003f: ldstr "a" + IL_0044: call void [mscorlib]System.Console::WriteLine(string) + IL_0049: nop + IL_004a: br.s IL_0076 + + IL_004c: nop + IL_004d: ldstr "b" + IL_0052: call void [mscorlib]System.Console::WriteLine(string) + IL_0057: nop + IL_0058: br.s IL_0076 + + IL_005a: nop + IL_005b: ldstr "c" + IL_0060: call void [mscorlib]System.Console::WriteLine(string) + IL_0065: nop + IL_0066: br.s IL_0076 + + IL_0068: nop + IL_0069: ldstr "d" + IL_006e: call void [mscorlib]System.Console::WriteLine(string) + IL_0073: nop + IL_0074: br.s IL_0076 + + IL_0076: ldstr "end" + IL_007b: call void [mscorlib]System.Console::WriteLine(string) + IL_0080: nop + IL_0081: ret } // end of method Switch::SwitchWithArray } // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch -.class private auto ansi '{85E7C039-2097-47F9-A636-4F4E3015541A}' +.class private auto ansi '{7A918135-BE28-4F14-9240-86E34BA33540}' 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 '$$method0x6000008-1' .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000009-1' - .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x600000e-1' -} // end of class '{85E7C039-2097-47F9-A636-4F4E3015541A}' + .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x600000a-1' + .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x600000f-1' +} // end of class '{7A918135-BE28-4F14-9240-86E34BA33540}' // ============================================================= diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il index 85eea31f3..c951dbce9 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il @@ -10,7 +10,7 @@ .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } -.assembly nwkzngd2 +.assembly '4wc22bae' { .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. @@ -20,15 +20,15 @@ .hash algorithm 0x00008004 .ver 0:0:0:0 } -.module nwkzngd2.dll -// MVID: {2FE2A6DB-2DD1-4526-BE2D-8029A91D5DA5} +.module '4wc22bae.dll' +// MVID: {B84EA70D-C67F-455B-9708-0E39585F7DA1} .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: 0x008E0000 +// Image base: 0x01140000 // =============== CLASS MEMBERS DECLARATION =================== @@ -509,6 +509,49 @@ 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 { @@ -527,7 +570,7 @@ IL_0013: brfalse IL_00db IL_0018: volatile. - IL_001a: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{2FE2A6DB-2DD1-4526-BE2D-8029A91D5DA5}'::'$$method0x6000008-1' + IL_001a: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{B84EA70D-C67F-455B-9708-0E39585F7DA1}'::'$$method0x6000009-1' IL_001f: brtrue.s IL_0082 IL_0021: ldc.i4.7 @@ -568,9 +611,9 @@ 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 '{2FE2A6DB-2DD1-4526-BE2D-8029A91D5DA5}'::'$$method0x6000008-1' + IL_007d: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{B84EA70D-C67F-455B-9708-0E39585F7DA1}'::'$$method0x6000009-1' IL_0082: volatile. - IL_0084: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{2FE2A6DB-2DD1-4526-BE2D-8029A91D5DA5}'::'$$method0x6000008-1' + IL_0084: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{B84EA70D-C67F-455B-9708-0E39585F7DA1}'::'$$method0x6000009-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, @@ -616,141 +659,138 @@ .method public hidebysig static string SwitchOverString2() cil managed { - // Code size 325 (0x145) + // Code size 323 (0x143) .maxstack 4 .locals init (string V_0, - string V_1, - int32 V_2) + 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: stloc.0 - IL_0010: ldloc.0 - IL_0011: dup - IL_0012: stloc.1 - IL_0013: brfalse IL_013f - - IL_0018: volatile. - IL_001a: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{2FE2A6DB-2DD1-4526-BE2D-8029A91D5DA5}'::'$$method0x6000009-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, + 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 '{B84EA70D-C67F-455B-9708-0E39585F7DA1}'::'$$method0x600000a-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_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, + 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_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, + 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_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, + 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_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, + 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_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, + 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_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, + 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_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, + 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_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, + 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_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, + 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_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, + 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_00b1: volatile. - IL_00b3: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{2FE2A6DB-2DD1-4526-BE2D-8029A91D5DA5}'::'$$method0x6000009-1' - IL_00b8: volatile. - IL_00ba: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{2FE2A6DB-2DD1-4526-BE2D-8029A91D5DA5}'::'$$method0x6000009-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, + IL_00af: volatile. + IL_00b1: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{B84EA70D-C67F-455B-9708-0E39585F7DA1}'::'$$method0x600000a-1' + IL_00b6: volatile. + IL_00b8: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{B84EA70D-C67F-455B-9708-0E39585F7DA1}'::'$$method0x600000a-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_00c7: brfalse.s IL_013f + IL_00c5: brfalse.s IL_013d - IL_00c9: ldloc.2 - IL_00ca: switch ( - IL_00fd, - IL_0103, - IL_0109, - IL_010f, - IL_0115, - IL_011b, - IL_0121, - IL_0127, - IL_012d, - IL_0133, - IL_0139) - IL_00fb: br.s IL_013f + 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_00fd: ldstr "Text1" - IL_0102: ret + IL_00fb: ldstr "Text1" + IL_0100: ret - IL_0103: ldstr "Text2" - IL_0108: ret + IL_0101: ldstr "Text2" + IL_0106: ret - IL_0109: ldstr "Text3" - IL_010e: ret + IL_0107: ldstr "Text3" + IL_010c: ret - IL_010f: ldstr "Text4" - IL_0114: ret + IL_010d: ldstr "Text4" + IL_0112: ret - IL_0115: ldstr "Text5" - IL_011a: ret + IL_0113: ldstr "Text5" + IL_0118: ret - IL_011b: ldstr "Text6" - IL_0120: ret + IL_0119: ldstr "Text6" + IL_011e: ret - IL_0121: ldstr "Text7" - IL_0126: ret + IL_011f: ldstr "Text7" + IL_0124: ret - IL_0127: ldstr "Text8" - IL_012c: ret + IL_0125: ldstr "Text8" + IL_012a: ret - IL_012d: ldstr "Text9" - IL_0132: ret + IL_012b: ldstr "Text9" + IL_0130: ret - IL_0133: ldstr "Text10" - IL_0138: ret + IL_0131: ldstr "Text10" + IL_0136: ret - IL_0139: ldstr "Text11" - IL_013e: ret + IL_0137: ldstr "Text11" + IL_013c: ret - IL_013f: ldstr "Default" - IL_0144: ret + IL_013d: ldstr "Default" + IL_0142: ret } // end of method Switch::SwitchOverString2 .method public hidebysig static string @@ -785,7 +825,7 @@ .method public hidebysig static void SwitchInLoop(int32 i) cil managed { - // Code size 124 (0x7c) + // Code size 112 (0x70) .maxstack 2 .locals init (int32 V_0) IL_0000: ldstr "SwitchInLoop: " @@ -802,37 +842,33 @@ IL_001a: switch ( IL_0031, IL_003d, - IL_0049, - IL_0055) - IL_002f: br.s IL_0060 + 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_0075 + IL_003b: br.s IL_0069 IL_003d: ldstr "two" IL_0042: call void [mscorlib]System.Console::WriteLine(string) - IL_0047: br.s IL_0075 + IL_0047: br.s IL_0069 - IL_0049: ldstr "three" + IL_0049: ldstr "four" IL_004e: call void [mscorlib]System.Console::WriteLine(string) - IL_0053: br.s IL_0015 + IL_0053: ret - IL_0055: ldstr "four" - IL_005a: call void [mscorlib]System.Console::WriteLine(string) - IL_005f: ret - - IL_0060: ldstr "default" - IL_0065: call void [mscorlib]System.Console::WriteLine(string) - IL_006a: ldstr "more code" - IL_006f: call void [mscorlib]System.Console::WriteLine(string) - IL_0074: ret - - IL_0075: ldarg.0 - IL_0076: ldc.i4.1 - IL_0077: add - IL_0078: starg.s i - IL_007a: br.s IL_0015 + 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 @@ -891,7 +927,7 @@ .method public hidebysig static void SwitchOnStringInForLoop() cil managed { - // Code size 303 (0x12f) + // 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, @@ -899,8 +935,7 @@ int32 V_3, class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty V_4, string V_5, - string V_6, - int32 V_7) + 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() @@ -909,7 +944,7 @@ IL_0011: stloc.2 IL_0012: ldc.i4.0 IL_0013: stloc.3 - IL_0014: br IL_0125 + IL_0014: br IL_0121 IL_0019: ldloc.2 IL_001a: ldloc.3 @@ -918,119 +953,117 @@ 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: dup - IL_002f: stloc.s V_6 - IL_0031: brfalse IL_0119 - - IL_0036: volatile. - IL_0038: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{2FE2A6DB-2DD1-4526-BE2D-8029A91D5DA5}'::'$$method0x600000e-1' - IL_003d: brtrue.s IL_0094 - - IL_003f: ldc.i4.6 - IL_0040: newobj instance void class [mscorlib]System.Collections.Generic.Dictionary`2::.ctor(int32) - IL_0045: dup - IL_0046: ldstr "Name1" - IL_004b: ldc.i4.0 - IL_004c: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + 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 '{B84EA70D-C67F-455B-9708-0E39585F7DA1}'::'$$method0x600000f-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_0051: dup - IL_0052: ldstr "Name2" - IL_0057: ldc.i4.1 - IL_0058: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + 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_005d: dup - IL_005e: ldstr "Name3" - IL_0063: ldc.i4.2 - IL_0064: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + 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_0069: dup - IL_006a: ldstr "Name4" - IL_006f: ldc.i4.3 - IL_0070: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + 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_0075: dup - IL_0076: ldstr "Name5" - IL_007b: ldc.i4.4 - IL_007c: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + 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_0081: dup - IL_0082: ldstr "Name6" - IL_0087: ldc.i4.5 - IL_0088: call instance void class [mscorlib]System.Collections.Generic.Dictionary`2::Add(!0, + 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_008d: volatile. - IL_008f: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{2FE2A6DB-2DD1-4526-BE2D-8029A91D5DA5}'::'$$method0x600000e-1' - IL_0094: volatile. - IL_0096: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{2FE2A6DB-2DD1-4526-BE2D-8029A91D5DA5}'::'$$method0x600000e-1' - IL_009b: ldloc.s V_6 - IL_009d: ldloca.s V_7 - IL_009f: call instance bool class [mscorlib]System.Collections.Generic.Dictionary`2::TryGetValue(!0, + IL_0089: volatile. + IL_008b: stsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{B84EA70D-C67F-455B-9708-0E39585F7DA1}'::'$$method0x600000f-1' + IL_0090: volatile. + IL_0092: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{B84EA70D-C67F-455B-9708-0E39585F7DA1}'::'$$method0x600000f-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_00a4: brfalse.s IL_0119 - - IL_00a6: ldloc.s V_7 - IL_00a8: switch ( - IL_00c7, - IL_00d9, - IL_00eb, - IL_00fd, - IL_010f, - IL_010f) - IL_00c5: br.s IL_0119 - - IL_00c7: ldloc.s V_4 - IL_00c9: ldc.i4.1 - IL_00ca: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) - 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: br.s IL_0121 - - IL_00d9: ldloc.s V_4 - IL_00db: ldc.i4.2 - IL_00dc: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) - IL_00e1: ldloc.0 - IL_00e2: ldloc.s V_4 - IL_00e4: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) - IL_00e9: br.s IL_0121 - - IL_00eb: ldloc.s V_4 - IL_00ed: ldc.i4.3 - IL_00ee: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) - IL_00f3: ldloc.0 - IL_00f4: ldloc.s V_4 - IL_00f6: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) - IL_00fb: br.s IL_0121 - - IL_00fd: ldloc.s V_4 - IL_00ff: ldc.i4.4 - IL_0100: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) - IL_0105: ldloc.0 - IL_0106: ldloc.s V_4 - IL_0108: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) - IL_010d: br.s IL_0121 - - IL_010f: ldloc.0 - IL_0110: ldloc.s V_4 - IL_0112: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) - IL_0117: br.s IL_0121 - - IL_0119: ldloc.1 - IL_011a: ldloc.s V_4 - IL_011c: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + 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: ldc.i4.1 - IL_0123: add - IL_0124: stloc.3 - IL_0125: ldloc.3 - IL_0126: ldloc.2 - IL_0127: ldlen - IL_0128: conv.i4 - IL_0129: blt IL_0019 - - IL_012e: ret + 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 @@ -1157,14 +1190,14 @@ } // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch -.class private auto ansi '{2FE2A6DB-2DD1-4526-BE2D-8029A91D5DA5}' +.class private auto ansi '{B84EA70D-C67F-455B-9708-0E39585F7DA1}' 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 '$$method0x6000008-1' .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x6000009-1' - .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x600000e-1' -} // end of class '{2FE2A6DB-2DD1-4526-BE2D-8029A91D5DA5}' + .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x600000a-1' + .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x600000f-1' +} // end of class '{B84EA70D-C67F-455B-9708-0E39585F7DA1}' // ============================================================= diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il index c28f0e721..00d6cacd1 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il @@ -25,14 +25,14 @@ .ver 0:0:0:0 } .module Switch.dll -// MVID: {94058166-F81D-4A82-ABCC-FB400C785214} +// MVID: {F38BF1C4-C0E5-4BAD-8838-849DFFFA97DF} .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: 0x00EF0000 +// Image base: 0x00300000 // =============== CLASS MEMBERS DECLARATION =================== @@ -510,6 +510,46 @@ 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 { @@ -894,7 +934,7 @@ .method public hidebysig static void SwitchInLoop(int32 i) cil managed { - // Code size 122 (0x7a) + // Code size 110 (0x6e) .maxstack 2 IL_0000: ldstr "SwitchInLoop: " IL_0005: ldarg.0 @@ -908,37 +948,33 @@ IL_0018: switch ( IL_002f, IL_003b, - IL_0047, - IL_0053) - IL_002d: br.s IL_005e + 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_0073 + IL_0039: br.s IL_0067 IL_003b: ldstr "two" IL_0040: call void [mscorlib]System.Console::WriteLine(string) - IL_0045: br.s IL_0073 + IL_0045: br.s IL_0067 - IL_0047: ldstr "three" + IL_0047: ldstr "four" IL_004c: call void [mscorlib]System.Console::WriteLine(string) - IL_0051: br.s IL_0015 - - IL_0053: ldstr "four" - IL_0058: call void [mscorlib]System.Console::WriteLine(string) - IL_005d: ret + IL_0051: ret - IL_005e: ldstr "default" - IL_0063: call void [mscorlib]System.Console::WriteLine(string) - IL_0068: ldstr "more code" - IL_006d: call void [mscorlib]System.Console::WriteLine(string) - IL_0072: 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_0073: ldarg.0 - IL_0074: ldc.i4.1 - IL_0075: add - IL_0076: starg.s i - IL_0078: br.s IL_0015 + 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 diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il index b74cb403c..0ce401e0a 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il @@ -25,14 +25,14 @@ .ver 0:0:0:0 } .module Switch.dll -// MVID: {1E0E6114-7796-44C6-B88B-AA126D09F561} +// MVID: {25920C54-28DD-4B8C-9EBF-16E716D0EC15} .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: 0x02FE0000 +// Image base: 0x01040000 // =============== CLASS MEMBERS DECLARATION =================== @@ -683,6 +683,63 @@ IL_0062: ret } // end of method Switch::ShortSwitchOverString + .method public hidebysig static string + ShortSwitchOverStringWithNullCase(string text) cil managed + { + // Code size 89 (0x59) + .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_003c + + IL_002e: ldloc.0 + IL_002f: brfalse.s IL_0045 + + IL_0031: br.s IL_004e + + IL_0033: nop + IL_0034: ldstr "Text1" + IL_0039: stloc.1 + IL_003a: br.s IL_0057 + + IL_003c: nop + IL_003d: ldstr "Text2" + IL_0042: stloc.1 + IL_0043: br.s IL_0057 + + IL_0045: nop + IL_0046: ldstr "null" + IL_004b: stloc.1 + IL_004c: br.s IL_0057 + + IL_004e: nop + IL_004f: ldstr "Default" + IL_0054: stloc.1 + IL_0055: br.s IL_0057 + + IL_0057: ldloc.1 + IL_0058: ret + } // end of method Switch::ShortSwitchOverStringWithNullCase + .method public hidebysig static string SwitchOverString1(string text) cil managed { @@ -870,12 +927,11 @@ .method public hidebysig static string SwitchOverString2() cil managed { - // Code size 520 (0x208) + // Code size 518 (0x206) .maxstack 2 .locals init (string V_0, - string V_1, - uint32 V_2, - string V_3) + uint32 V_1, + string V_2) IL_0000: nop IL_0001: ldstr "SwitchOverString2:" IL_0006: call void [mscorlib]System.Console::WriteLine(string) @@ -883,238 +939,236 @@ IL_000c: call string [mscorlib]System.Environment::get_UserName() IL_0011: stloc.0 IL_0012: ldloc.0 - IL_0013: stloc.1 - IL_0014: ldloc.1 - IL_0015: call uint32 ''::ComputeStringHash(string) - IL_001a: stloc.2 - IL_001b: ldloc.2 - IL_001c: ldc.i4 0x4c7c71f6 - IL_0021: bgt.un.s IL_0072 + 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_0023: ldloc.2 - IL_0024: ldc.i4 0xc9a8f4f - IL_0029: bgt.un.s IL_0048 + IL_0021: ldloc.1 + IL_0022: ldc.i4 0xc9a8f4f + IL_0027: bgt.un.s IL_0046 - IL_002b: ldloc.2 - IL_002c: ldc.i4 0x8861b86 - IL_0031: beq IL_011c + IL_0029: ldloc.1 + IL_002a: ldc.i4 0x8861b86 + IL_002f: beq IL_011a - IL_0036: br.s IL_0038 + IL_0034: br.s IL_0036 - IL_0038: ldloc.2 - IL_0039: ldc.i4 0xc9a8f4f - IL_003e: beq IL_00c8 + IL_0036: ldloc.1 + IL_0037: ldc.i4 0xc9a8f4f + IL_003c: beq IL_00c6 - IL_0043: br IL_01fd + IL_0041: br IL_01fb - IL_0048: ldloc.2 - IL_0049: ldc.i4 0xf3d44a6 - IL_004e: beq IL_00f2 + IL_0046: ldloc.1 + IL_0047: ldc.i4 0xf3d44a6 + IL_004c: beq IL_00f0 - IL_0053: br.s IL_0055 + IL_0051: br.s IL_0053 - IL_0055: ldloc.2 - IL_0056: ldc.i4 0x20289804 - IL_005b: beq IL_0158 + IL_0053: ldloc.1 + IL_0054: ldc.i4 0x20289804 + IL_0059: beq IL_0156 - IL_0060: br.s IL_0062 + IL_005e: br.s IL_0060 - IL_0062: ldloc.2 - IL_0063: ldc.i4 0x4c7c71f6 - IL_0068: beq IL_016a + IL_0060: ldloc.1 + IL_0061: ldc.i4 0x4c7c71f6 + IL_0066: beq IL_0168 - IL_006d: br IL_01fd + IL_006b: br IL_01fb - IL_0072: ldloc.2 - IL_0073: ldc.i4 0xa151b28a - IL_0078: bgt.un.s IL_00a4 + IL_0070: ldloc.1 + IL_0071: ldc.i4 0xa151b28a + IL_0076: bgt.un.s IL_00a2 - IL_007a: ldloc.2 - IL_007b: ldc.i4 0x4d0cea48 - IL_0080: beq IL_018b + IL_0078: ldloc.1 + IL_0079: ldc.i4 0x4d0cea48 + IL_007e: beq IL_0189 - IL_0085: br.s IL_0087 + IL_0083: br.s IL_0085 - IL_0087: ldloc.2 - IL_0088: ldc.i4 0x51650fb9 - IL_008d: beq IL_0131 + IL_0085: ldloc.1 + IL_0086: ldc.i4 0x51650fb9 + IL_008b: beq IL_012f - IL_0092: br.s IL_0094 + IL_0090: br.s IL_0092 - IL_0094: ldloc.2 - IL_0095: ldc.i4 0xa151b28a - IL_009a: beq IL_0146 + IL_0092: ldloc.1 + IL_0093: ldc.i4 0xa151b28a + IL_0098: beq IL_0144 - IL_009f: br IL_01fd + IL_009d: br IL_01fb - IL_00a4: ldloc.2 - IL_00a5: ldc.i4 0xea3d096b - IL_00aa: beq.s IL_00dd + IL_00a2: ldloc.1 + IL_00a3: ldc.i4 0xea3d096b + IL_00a8: beq.s IL_00db - IL_00ac: br.s IL_00ae + IL_00aa: br.s IL_00ac - IL_00ae: ldloc.2 - IL_00af: ldc.i4 0xed5134d4 - IL_00b4: beq IL_017c + IL_00ac: ldloc.1 + IL_00ad: ldc.i4 0xed5134d4 + IL_00b2: beq IL_017a - IL_00b9: br.s IL_00bb + IL_00b7: br.s IL_00b9 - IL_00bb: ldloc.2 - IL_00bc: ldc.i4 0xf701cc7f - IL_00c1: beq.s IL_0107 + IL_00b9: ldloc.1 + IL_00ba: ldc.i4 0xf701cc7f + IL_00bf: beq.s IL_0105 - IL_00c3: br IL_01fd + IL_00c1: br IL_01fb - IL_00c8: ldloc.1 - IL_00c9: ldstr "First case" - IL_00ce: call bool [mscorlib]System.String::op_Equality(string, + IL_00c6: ldloc.0 + IL_00c7: ldstr "First case" + IL_00cc: call bool [mscorlib]System.String::op_Equality(string, string) - IL_00d3: brtrue IL_019a + IL_00d1: brtrue IL_0198 - IL_00d8: br IL_01fd + IL_00d6: br IL_01fb - IL_00dd: ldloc.1 - IL_00de: ldstr "Second case" - IL_00e3: call bool [mscorlib]System.String::op_Equality(string, + IL_00db: ldloc.0 + IL_00dc: ldstr "Second case" + IL_00e1: call bool [mscorlib]System.String::op_Equality(string, string) - IL_00e8: brtrue IL_01a3 + IL_00e6: brtrue IL_01a1 - IL_00ed: br IL_01fd + IL_00eb: br IL_01fb - IL_00f2: ldloc.1 - IL_00f3: ldstr "Third case" - IL_00f8: call bool [mscorlib]System.String::op_Equality(string, + IL_00f0: ldloc.0 + IL_00f1: ldstr "Third case" + IL_00f6: call bool [mscorlib]System.String::op_Equality(string, string) - IL_00fd: brtrue IL_01ac + IL_00fb: brtrue IL_01aa - IL_0102: br IL_01fd + IL_0100: br IL_01fb - IL_0107: ldloc.1 - IL_0108: ldstr "Fourth case" - IL_010d: call bool [mscorlib]System.String::op_Equality(string, + IL_0105: ldloc.0 + IL_0106: ldstr "Fourth case" + IL_010b: call bool [mscorlib]System.String::op_Equality(string, string) - IL_0112: brtrue IL_01b5 + IL_0110: brtrue IL_01b3 - IL_0117: br IL_01fd + IL_0115: br IL_01fb - IL_011c: ldloc.1 - IL_011d: ldstr "Fifth case" - IL_0122: call bool [mscorlib]System.String::op_Equality(string, + IL_011a: ldloc.0 + IL_011b: ldstr "Fifth case" + IL_0120: call bool [mscorlib]System.String::op_Equality(string, string) - IL_0127: brtrue IL_01be + IL_0125: brtrue IL_01bc - IL_012c: br IL_01fd + IL_012a: br IL_01fb - IL_0131: ldloc.1 - IL_0132: ldstr "Sixth case" - IL_0137: call bool [mscorlib]System.String::op_Equality(string, + IL_012f: ldloc.0 + IL_0130: ldstr "Sixth case" + IL_0135: call bool [mscorlib]System.String::op_Equality(string, string) - IL_013c: brtrue IL_01c7 + IL_013a: brtrue IL_01c5 - IL_0141: br IL_01fd + IL_013f: br IL_01fb - IL_0146: ldloc.1 - IL_0147: ldstr "Seventh case" - IL_014c: call bool [mscorlib]System.String::op_Equality(string, + IL_0144: ldloc.0 + IL_0145: ldstr "Seventh case" + IL_014a: call bool [mscorlib]System.String::op_Equality(string, string) - IL_0151: brtrue.s IL_01d0 + IL_014f: brtrue.s IL_01ce - IL_0153: br IL_01fd + IL_0151: br IL_01fb - IL_0158: ldloc.1 - IL_0159: ldstr "Eighth case" - IL_015e: call bool [mscorlib]System.String::op_Equality(string, + IL_0156: ldloc.0 + IL_0157: ldstr "Eighth case" + IL_015c: call bool [mscorlib]System.String::op_Equality(string, string) - IL_0163: brtrue.s IL_01d9 + IL_0161: brtrue.s IL_01d7 - IL_0165: br IL_01fd + IL_0163: br IL_01fb - IL_016a: ldloc.1 - IL_016b: ldstr "Ninth case" - IL_0170: call bool [mscorlib]System.String::op_Equality(string, + IL_0168: ldloc.0 + IL_0169: ldstr "Ninth case" + IL_016e: call bool [mscorlib]System.String::op_Equality(string, string) - IL_0175: brtrue.s IL_01e2 + IL_0173: brtrue.s IL_01e0 - IL_0177: br IL_01fd + IL_0175: br IL_01fb - IL_017c: ldloc.1 - IL_017d: ldstr "Tenth case" - IL_0182: call bool [mscorlib]System.String::op_Equality(string, + IL_017a: ldloc.0 + IL_017b: ldstr "Tenth case" + IL_0180: call bool [mscorlib]System.String::op_Equality(string, string) - IL_0187: brtrue.s IL_01eb + IL_0185: brtrue.s IL_01e9 - IL_0189: br.s IL_01fd + IL_0187: br.s IL_01fb - IL_018b: ldloc.1 - IL_018c: ldstr "Eleventh case" - IL_0191: call bool [mscorlib]System.String::op_Equality(string, + IL_0189: ldloc.0 + IL_018a: ldstr "Eleventh case" + IL_018f: call bool [mscorlib]System.String::op_Equality(string, string) - IL_0196: brtrue.s IL_01f4 - - IL_0198: br.s IL_01fd - - IL_019a: nop - IL_019b: ldstr "Text1" - IL_01a0: stloc.3 - IL_01a1: br.s IL_0206 - - IL_01a3: nop - IL_01a4: ldstr "Text2" - IL_01a9: stloc.3 - IL_01aa: br.s IL_0206 - - IL_01ac: nop - IL_01ad: ldstr "Text3" - IL_01b2: stloc.3 - IL_01b3: br.s IL_0206 - - IL_01b5: nop - IL_01b6: ldstr "Text4" - IL_01bb: stloc.3 - IL_01bc: br.s IL_0206 - - IL_01be: nop - IL_01bf: ldstr "Text5" - IL_01c4: stloc.3 - IL_01c5: br.s IL_0206 - - IL_01c7: nop - IL_01c8: ldstr "Text6" - IL_01cd: stloc.3 - IL_01ce: br.s IL_0206 - - IL_01d0: nop - IL_01d1: ldstr "Text7" - IL_01d6: stloc.3 - IL_01d7: br.s IL_0206 - - IL_01d9: nop - IL_01da: ldstr "Text8" - IL_01df: stloc.3 - IL_01e0: br.s IL_0206 - - IL_01e2: nop - IL_01e3: ldstr "Text9" - IL_01e8: stloc.3 - IL_01e9: br.s IL_0206 - - IL_01eb: nop - IL_01ec: ldstr "Text10" - IL_01f1: stloc.3 - IL_01f2: br.s IL_0206 - - IL_01f4: nop - IL_01f5: ldstr "Text11" - IL_01fa: stloc.3 - IL_01fb: br.s IL_0206 - - IL_01fd: nop - IL_01fe: ldstr "Default" - IL_0203: stloc.3 - IL_0204: br.s IL_0206 - - IL_0206: ldloc.3 - IL_0207: ret + IL_0194: brtrue.s IL_01f2 + + IL_0196: br.s IL_01fb + + IL_0198: nop + IL_0199: ldstr "Text1" + IL_019e: stloc.2 + IL_019f: br.s IL_0204 + + IL_01a1: nop + IL_01a2: ldstr "Text2" + IL_01a7: stloc.2 + IL_01a8: br.s IL_0204 + + IL_01aa: nop + IL_01ab: ldstr "Text3" + IL_01b0: stloc.2 + IL_01b1: br.s IL_0204 + + IL_01b3: nop + IL_01b4: ldstr "Text4" + IL_01b9: stloc.2 + IL_01ba: br.s IL_0204 + + IL_01bc: nop + IL_01bd: ldstr "Text5" + IL_01c2: stloc.2 + IL_01c3: br.s IL_0204 + + IL_01c5: nop + IL_01c6: ldstr "Text6" + IL_01cb: stloc.2 + IL_01cc: br.s IL_0204 + + IL_01ce: nop + IL_01cf: ldstr "Text7" + IL_01d4: stloc.2 + IL_01d5: br.s IL_0204 + + IL_01d7: nop + IL_01d8: ldstr "Text8" + IL_01dd: stloc.2 + IL_01de: br.s IL_0204 + + IL_01e0: nop + IL_01e1: ldstr "Text9" + IL_01e6: stloc.2 + IL_01e7: br.s IL_0204 + + IL_01e9: nop + IL_01ea: ldstr "Text10" + IL_01ef: stloc.2 + IL_01f0: br.s IL_0204 + + IL_01f2: nop + IL_01f3: ldstr "Text11" + IL_01f8: stloc.2 + IL_01f9: br.s IL_0204 + + IL_01fb: nop + IL_01fc: ldstr "Default" + IL_0201: stloc.2 + IL_0202: br.s IL_0204 + + IL_0204: ldloc.2 + IL_0205: ret } // end of method Switch::SwitchOverString2 .method public hidebysig static string @@ -1166,7 +1220,7 @@ .method public hidebysig static void SwitchInLoop(int32 i) cil managed { - // Code size 146 (0x92) + // Code size 132 (0x84) .maxstack 2 .locals init (int32 V_0, bool V_1) @@ -1178,7 +1232,7 @@ object) IL_0011: call void [mscorlib]System.Console::WriteLine(string) IL_0016: nop - IL_0017: br.s IL_008d + IL_0017: br.s IL_007f IL_0019: nop IL_001a: ldarg.0 @@ -1189,53 +1243,47 @@ IL_001f: switch ( IL_0036, IL_0044, - IL_0052, - IL_0060) - IL_0034: br.s IL_006e + IL_0060, + IL_0052) + IL_0034: br.s IL_0060 IL_0036: nop IL_0037: ldstr "one" IL_003c: call void [mscorlib]System.Console::WriteLine(string) IL_0041: nop - IL_0042: br.s IL_0087 + IL_0042: br.s IL_0079 IL_0044: nop IL_0045: ldstr "two" IL_004a: call void [mscorlib]System.Console::WriteLine(string) IL_004f: nop - IL_0050: br.s IL_0087 + IL_0050: br.s IL_0079 IL_0052: nop - IL_0053: ldstr "three" + IL_0053: ldstr "four" IL_0058: call void [mscorlib]System.Console::WriteLine(string) IL_005d: nop - IL_005e: br.s IL_008d + IL_005e: br.s IL_0083 IL_0060: nop - IL_0061: ldstr "four" + IL_0061: ldstr "default" IL_0066: call void [mscorlib]System.Console::WriteLine(string) IL_006b: nop - IL_006c: br.s IL_0091 - - IL_006e: nop - IL_006f: ldstr "default" - IL_0074: call void [mscorlib]System.Console::WriteLine(string) - IL_0079: nop - IL_007a: ldstr "more code" - IL_007f: call void [mscorlib]System.Console::WriteLine(string) - IL_0084: nop - IL_0085: br.s IL_0091 - - IL_0087: ldarg.0 - IL_0088: ldc.i4.1 - IL_0089: add - IL_008a: starg.s i - IL_008c: nop - IL_008d: ldc.i4.1 - IL_008e: stloc.1 - IL_008f: br.s IL_0019 - - IL_0091: ret + IL_006c: ldstr "more code" + IL_0071: call void [mscorlib]System.Console::WriteLine(string) + IL_0076: nop + IL_0077: br.s IL_0083 + + IL_0079: ldarg.0 + IL_007a: ldc.i4.1 + IL_007b: add + IL_007c: starg.s i + IL_007e: nop + IL_007f: ldc.i4.1 + IL_0080: stloc.1 + IL_0081: br.s IL_0019 + + IL_0083: ret } // end of method Switch::SwitchInLoop .method public hidebysig static void SwitchWithGoto(int32 i) cil managed @@ -1317,7 +1365,7 @@ .method public hidebysig static void SwitchOnStringInForLoop() cil managed { - // Code size 265 (0x109) + // Code size 261 (0x105) .maxstack 2 .locals init (class [mscorlib]System.Collections.Generic.List`1 V_0, class [mscorlib]System.Collections.Generic.List`1 V_1, @@ -1325,8 +1373,7 @@ int32 V_3, class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty V_4, string V_5, - string V_6, - bool V_7) + bool V_6) IL_0000: nop IL_0001: newobj instance void class [mscorlib]System.Collections.Generic.List`1::.ctor() IL_0006: stloc.0 @@ -1336,7 +1383,7 @@ IL_0012: stloc.2 IL_0013: ldc.i4.0 IL_0014: stloc.3 - IL_0015: br IL_00f9 + IL_0015: br IL_00f5 IL_001a: nop IL_001b: ldloc.2 @@ -1348,123 +1395,121 @@ 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: stloc.s V_6 - IL_0032: ldloc.s V_6 - IL_0034: ldstr "Name1" - IL_0039: call bool [mscorlib]System.String::op_Equality(string, + IL_0030: ldstr "Name1" + IL_0035: call bool [mscorlib]System.String::op_Equality(string, string) - IL_003e: brtrue.s IL_0088 + IL_003a: brtrue.s IL_0084 - IL_0040: ldloc.s V_6 - IL_0042: ldstr "Name2" - IL_0047: call bool [mscorlib]System.String::op_Equality(string, + IL_003c: ldloc.s V_5 + IL_003e: ldstr "Name2" + IL_0043: call bool [mscorlib]System.String::op_Equality(string, string) - IL_004c: brtrue.s IL_009d + IL_0048: brtrue.s IL_0099 - IL_004e: ldloc.s V_6 - IL_0050: ldstr "Name3" - IL_0055: call bool [mscorlib]System.String::op_Equality(string, + IL_004a: ldloc.s V_5 + IL_004c: ldstr "Name3" + IL_0051: call bool [mscorlib]System.String::op_Equality(string, string) - IL_005a: brtrue.s IL_00b2 + IL_0056: brtrue.s IL_00ae - IL_005c: ldloc.s V_6 - IL_005e: ldstr "Name4" - IL_0063: call bool [mscorlib]System.String::op_Equality(string, + IL_0058: ldloc.s V_5 + IL_005a: ldstr "Name4" + IL_005f: call bool [mscorlib]System.String::op_Equality(string, string) - IL_0068: brtrue.s IL_00c7 + IL_0064: brtrue.s IL_00c3 - IL_006a: ldloc.s V_6 - IL_006c: ldstr "Name5" - IL_0071: call bool [mscorlib]System.String::op_Equality(string, + IL_0066: ldloc.s V_5 + IL_0068: ldstr "Name5" + IL_006d: call bool [mscorlib]System.String::op_Equality(string, string) - IL_0076: brtrue.s IL_00dc + IL_0072: brtrue.s IL_00d8 - IL_0078: ldloc.s V_6 - IL_007a: ldstr "Name6" - IL_007f: call bool [mscorlib]System.String::op_Equality(string, + IL_0074: ldloc.s V_5 + IL_0076: ldstr "Name6" + IL_007b: call bool [mscorlib]System.String::op_Equality(string, string) - IL_0084: brtrue.s IL_00dc + IL_0080: brtrue.s IL_00d8 - IL_0086: br.s IL_00e8 + IL_0082: br.s IL_00e4 - IL_0088: nop - IL_0089: ldloc.s V_4 - IL_008b: ldc.i4.1 - IL_008c: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) - IL_0091: nop - IL_0092: ldloc.0 - IL_0093: ldloc.s V_4 - IL_0095: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) - IL_009a: nop - IL_009b: br.s IL_00f4 - - IL_009d: nop - IL_009e: ldloc.s V_4 - IL_00a0: ldc.i4.2 - IL_00a1: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) - IL_00a6: nop - IL_00a7: ldloc.0 - IL_00a8: ldloc.s V_4 - IL_00aa: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) - IL_00af: nop - IL_00b0: br.s IL_00f4 - - IL_00b2: nop - IL_00b3: ldloc.s V_4 - IL_00b5: ldc.i4.3 - IL_00b6: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) - IL_00bb: nop - IL_00bc: ldloc.0 - IL_00bd: ldloc.s V_4 - IL_00bf: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) - IL_00c4: nop - IL_00c5: br.s IL_00f4 - - IL_00c7: nop - IL_00c8: ldloc.s V_4 - IL_00ca: ldc.i4.4 - IL_00cb: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) - IL_00d0: nop - IL_00d1: ldloc.0 - IL_00d2: ldloc.s V_4 - IL_00d4: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) - IL_00d9: nop - IL_00da: br.s IL_00f4 - - IL_00dc: nop - 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: nop - IL_00e6: br.s IL_00f4 - - IL_00e8: nop - IL_00e9: ldloc.1 - IL_00ea: ldloc.s V_4 - IL_00ec: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) - IL_00f1: nop - IL_00f2: br.s IL_00f4 - - IL_00f4: nop + IL_0084: nop + IL_0085: ldloc.s V_4 + IL_0087: ldc.i4.1 + IL_0088: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + IL_008d: nop + IL_008e: ldloc.0 + IL_008f: ldloc.s V_4 + IL_0091: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_0096: nop + IL_0097: br.s IL_00f0 + + IL_0099: nop + IL_009a: ldloc.s V_4 + IL_009c: ldc.i4.2 + IL_009d: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + IL_00a2: nop + IL_00a3: ldloc.0 + IL_00a4: ldloc.s V_4 + IL_00a6: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_00ab: nop + IL_00ac: br.s IL_00f0 + + IL_00ae: nop + IL_00af: ldloc.s V_4 + IL_00b1: ldc.i4.3 + IL_00b2: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + IL_00b7: nop + IL_00b8: ldloc.0 + IL_00b9: ldloc.s V_4 + IL_00bb: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_00c0: nop + IL_00c1: br.s IL_00f0 + + IL_00c3: nop + IL_00c4: ldloc.s V_4 + IL_00c6: ldc.i4.4 + IL_00c7: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) + IL_00cc: nop + IL_00cd: ldloc.0 + IL_00ce: ldloc.s V_4 + IL_00d0: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_00d5: nop + IL_00d6: br.s IL_00f0 + + IL_00d8: nop + IL_00d9: ldloc.0 + IL_00da: ldloc.s V_4 + IL_00dc: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_00e1: nop + IL_00e2: br.s IL_00f0 + + IL_00e4: nop + IL_00e5: ldloc.1 + IL_00e6: ldloc.s V_4 + IL_00e8: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_00ed: nop + IL_00ee: br.s IL_00f0 + + IL_00f0: nop + IL_00f1: ldloc.3 + IL_00f2: ldc.i4.1 + IL_00f3: add + IL_00f4: stloc.3 IL_00f5: ldloc.3 - IL_00f6: ldc.i4.1 - IL_00f7: add - IL_00f8: stloc.3 - IL_00f9: ldloc.3 - IL_00fa: ldloc.2 - IL_00fb: ldlen - IL_00fc: conv.i4 - IL_00fd: clt - IL_00ff: stloc.s V_7 - IL_0101: ldloc.s V_7 - IL_0103: brtrue IL_001a - - IL_0108: ret + IL_00f6: ldloc.2 + IL_00f7: ldlen + IL_00f8: conv.i4 + IL_00f9: clt + IL_00fb: stloc.s V_6 + IL_00fd: ldloc.s V_6 + IL_00ff: brtrue IL_001a + + IL_0104: ret } // end of method Switch::SwitchOnStringInForLoop .method public hidebysig static void SwitchWithComplexCondition(string[] args) cil managed { - // Code size 134 (0x86) + // Code size 138 (0x8a) .maxstack 2 .locals init (string V_0) IL_0000: nop @@ -1489,51 +1534,55 @@ IL_001e: ldstr "b" IL_0023: call bool [mscorlib]System.String::op_Equality(string, string) - IL_0028: brtrue.s IL_0053 + IL_0028: brtrue.s IL_0054 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_0035: brtrue.s IL_0062 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_0042: brtrue.s IL_0070 - IL_0044: br.s IL_007a + IL_0044: br.s IL_007e - IL_0046: ldstr "a" - IL_004b: call void [mscorlib]System.Console::WriteLine(string) - IL_0050: nop - IL_0051: br.s IL_007a + IL_0046: nop + IL_0047: ldstr "a" + IL_004c: call void [mscorlib]System.Console::WriteLine(string) + IL_0051: nop + IL_0052: br.s IL_007e - IL_0053: ldstr "b" - IL_0058: call void [mscorlib]System.Console::WriteLine(string) - IL_005d: nop - IL_005e: br.s IL_007a + IL_0054: nop + IL_0055: ldstr "b" + IL_005a: call void [mscorlib]System.Console::WriteLine(string) + IL_005f: nop + IL_0060: br.s IL_007e - IL_0060: ldstr "c" - IL_0065: call void [mscorlib]System.Console::WriteLine(string) - IL_006a: nop - IL_006b: br.s IL_007a + IL_0062: nop + IL_0063: ldstr "c" + IL_0068: call void [mscorlib]System.Console::WriteLine(string) + IL_006d: nop + IL_006e: br.s IL_007e - IL_006d: ldstr "d" - IL_0072: call void [mscorlib]System.Console::WriteLine(string) - IL_0077: nop - IL_0078: br.s IL_007a + IL_0070: nop + IL_0071: ldstr "d" + IL_0076: call void [mscorlib]System.Console::WriteLine(string) + IL_007b: nop + IL_007c: br.s IL_007e - IL_007a: ldstr "end" - IL_007f: call void [mscorlib]System.Console::WriteLine(string) - IL_0084: nop - IL_0085: ret + IL_007e: ldstr "end" + IL_0083: call void [mscorlib]System.Console::WriteLine(string) + IL_0088: nop + IL_0089: ret } // end of method Switch::SwitchWithComplexCondition .method public hidebysig static void SwitchWithArray(string[] args) cil managed { - // Code size 123 (0x7b) + // Code size 127 (0x7f) .maxstack 2 .locals init (string V_0) IL_0000: nop @@ -1551,46 +1600,50 @@ IL_0013: ldstr "b" IL_0018: call bool [mscorlib]System.String::op_Equality(string, string) - IL_001d: brtrue.s IL_0048 + IL_001d: brtrue.s IL_0049 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_002a: brtrue.s IL_0057 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_0037: brtrue.s IL_0065 - IL_003b: ldstr "a" - IL_0040: call void [mscorlib]System.Console::WriteLine(string) - IL_0045: nop - IL_0046: br.s IL_006f + IL_0039: br.s IL_0073 - IL_0048: ldstr "b" - IL_004d: call void [mscorlib]System.Console::WriteLine(string) - IL_0052: nop - IL_0053: br.s IL_006f + IL_003b: nop + IL_003c: ldstr "a" + IL_0041: call void [mscorlib]System.Console::WriteLine(string) + IL_0046: nop + IL_0047: br.s IL_0073 - IL_0055: ldstr "c" - IL_005a: call void [mscorlib]System.Console::WriteLine(string) - IL_005f: nop - IL_0060: br.s IL_006f + IL_0049: nop + IL_004a: ldstr "b" + IL_004f: call void [mscorlib]System.Console::WriteLine(string) + IL_0054: nop + IL_0055: br.s IL_0073 - IL_0062: ldstr "d" - IL_0067: call void [mscorlib]System.Console::WriteLine(string) - IL_006c: nop - IL_006d: br.s IL_006f + IL_0057: nop + IL_0058: ldstr "c" + IL_005d: call void [mscorlib]System.Console::WriteLine(string) + IL_0062: nop + IL_0063: br.s IL_0073 - IL_006f: ldstr "end" - IL_0074: call void [mscorlib]System.Console::WriteLine(string) - IL_0079: nop - IL_007a: ret + IL_0065: nop + IL_0066: ldstr "d" + IL_006b: call void [mscorlib]System.Console::WriteLine(string) + IL_0070: nop + IL_0071: br.s IL_0073 + + IL_0073: ldstr "end" + IL_0078: call void [mscorlib]System.Console::WriteLine(string) + IL_007d: nop + IL_007e: ret } // end of method Switch::SwitchWithArray } // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch From 7ae44fcdc6559f051448dfc19c102fc6857cd8bf Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 13 Oct 2017 13:50:12 +0200 Subject: [PATCH 56/65] Add FlattenSwitchBlocks transform + update test cases. --- .../TestCases/Pretty/Switch.cs | 512 +++++++----------- .../CSharp/CSharpDecompiler.cs | 2 +- .../CSharp/Transforms/FlattenSwitchBlocks.cs | 6 +- .../ICSharpCode.Decompiler.csproj | 1 + 4 files changed, 215 insertions(+), 306 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs index 45c1c8274..462330e92 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs @@ -43,154 +43,117 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { 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"; - } + 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"; - } + case null: + return "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"; - } + case null: + return "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"; - } + 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"; - } + 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; - } + 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; } } @@ -198,18 +161,14 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { Console.WriteLine("ShortSwitchOverString: " + text); switch (text) { - case "First case": { - return "Text1"; - } - case "Second case": { - return "Text2"; - } - case "Third case": { - return "Text3"; - } - default: { - return "Default"; - } + case "First case": + return "Text1"; + case "Second case": + return "Text2"; + case "Third case": + return "Text3"; + default: + return "Default"; } } @@ -217,18 +176,14 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { Console.WriteLine("ShortSwitchOverStringWithNullCase: " + text); switch (text) { - case "First case": { - return "Text1"; - } - case "Second case": { - return "Text2"; - } - case null: { - return "null"; - } - default: { - return "Default"; - } + case "First case": + return "Text1"; + case "Second case": + return "Text2"; + case null: + return "null"; + default: + return "Default"; } } @@ -236,31 +191,23 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { Console.WriteLine("SwitchOverString1: " + text); switch (text) { - case "First case": { - return "Text1"; - } + 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"; - } + 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"; } } @@ -268,42 +215,30 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { 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"; - } + 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"; } } @@ -311,15 +246,12 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { Console.WriteLine("SwitchOverBool: " + b.ToString()); switch (b) { - case true: { - return bool.TrueString; - } - case false: { - return bool.FalseString; - } - default: { - return null; - } + case true: + return bool.TrueString; + case false: + return bool.FalseString; + default: + return null; } } @@ -328,27 +260,22 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty Console.WriteLine("SwitchInLoop: " + i); while (true) { switch (i) { - case 1: { - Console.WriteLine("one"); - break; - } - case 2: { - Console.WriteLine("two"); - break; - } - //case 3: { + 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; - } + case 4: + Console.WriteLine("four"); + return; + default: + Console.WriteLine("default"); + Console.WriteLine("more code"); + return; } i++; } @@ -358,26 +285,21 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { 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; - } + 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"); } @@ -395,35 +317,29 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty 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 "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; - } + case "Name6": + list.Add(setProperty); + break; + default: + list2.Add(setProperty); + break; } } } @@ -431,22 +347,18 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty 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; - } + 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"); } @@ -454,22 +366,18 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void SwitchWithArray(string[] args) { switch (args[0]) { - case "a": { + case "a": Console.WriteLine("a"); break; - } - case "b": { + case "b": Console.WriteLine("b"); break; - } - case "c": { + case "c": Console.WriteLine("c"); break; - } - case "d": { + case "d": Console.WriteLine("d"); break; - } } Console.WriteLine("end"); } diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index dff75e4c8..801b95b7c 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -144,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(), }; 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/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 48313b70c..a1e17edfd 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -224,6 +224,7 @@ + From d6d702089309681f74cd3de97136dbdd45d84153 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 13 Oct 2017 14:57:03 +0200 Subject: [PATCH 57/65] Add test cases for nullable switch --- .../TestCases/Pretty/Switch.cs | 30 + .../TestCases/Pretty/Switch.il | 1415 ++++++++-------- .../TestCases/Pretty/Switch.opt.il | 136 +- .../TestCases/Pretty/Switch.opt.roslyn.il | 105 +- .../TestCases/Pretty/Switch.roslyn.il | 1450 +++++++++-------- 5 files changed, 1718 insertions(+), 1418 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs index 462330e92..1f6ec6f17 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs @@ -86,6 +86,21 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } + 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) { @@ -102,6 +117,21 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } + 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) { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il index e4f06b1f6..40a21bc93 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.il @@ -10,7 +10,7 @@ .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } -.assembly yojdxjhu +.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. @@ -20,15 +20,15 @@ .hash algorithm 0x00008004 .ver 0:0:0:0 } -.module yojdxjhu.dll -// MVID: {7A918135-BE28-4F14-9240-86E34BA33540} +.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: 0x032F0000 +// Image base: 0x03320000 // =============== CLASS MEMBERS DECLARATION =================== @@ -96,7 +96,7 @@ .method public hidebysig static string SparseIntegerSwitch(int32 i) cil managed { - // Code size 224 (0xe0) + // Code size 209 (0xd1) .maxstack 2 .locals init (string V_0, int32 V_1) @@ -112,112 +112,100 @@ IL_0018: stloc.1 IL_0019: ldloc.1 IL_001a: ldc.i4.4 - IL_001b: bgt.s IL_004f + IL_001b: bgt.s IL_004c IL_001d: ldloc.1 IL_001e: ldc.i4 0xff676980 - IL_0023: beq.s IL_0072 + IL_0023: beq.s IL_006f IL_0025: ldloc.1 IL_0026: ldc.i4.s -100 - IL_0028: beq.s IL_007b + IL_0028: beq.s IL_0077 IL_002a: ldloc.1 IL_002b: ldc.i4.m1 IL_002c: sub IL_002d: switch ( - IL_0084, - IL_008d, - IL_0096, - IL_009f, - IL_00d5, - IL_00a8) - IL_004a: br IL_00d5 - - IL_004f: ldloc.1 - IL_0050: ldc.i4.s 100 - IL_0052: beq.s IL_00b1 - - IL_0054: ldloc.1 - IL_0055: ldc.i4 0x2710 - IL_005a: sub - IL_005b: switch ( - IL_00ba, - IL_00c3) - IL_0068: ldloc.1 - IL_0069: ldc.i4 0x7fffffff - IL_006e: beq.s IL_00cc - - IL_0070: br.s IL_00d5 - - IL_0072: nop - IL_0073: ldstr "-10 mln" - IL_0078: stloc.0 - IL_0079: br.s IL_00de - - IL_007b: nop - IL_007c: ldstr "-hundred" - IL_0081: stloc.0 - IL_0082: br.s IL_00de - - IL_0084: nop - IL_0085: ldstr "-1" - IL_008a: stloc.0 - IL_008b: br.s IL_00de - - IL_008d: nop - IL_008e: ldstr "0" - IL_0093: stloc.0 - IL_0094: br.s IL_00de - - IL_0096: nop - IL_0097: ldstr "1" + 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_00de - - IL_009f: nop - IL_00a0: ldstr "2" - IL_00a5: stloc.0 - IL_00a6: br.s IL_00de - - IL_00a8: nop - IL_00a9: ldstr "4" - IL_00ae: stloc.0 - IL_00af: br.s IL_00de - - IL_00b1: nop - IL_00b2: ldstr "hundred" - IL_00b7: stloc.0 - IL_00b8: br.s IL_00de - - IL_00ba: nop - IL_00bb: ldstr "ten thousand" - IL_00c0: stloc.0 - IL_00c1: br.s IL_00de - - IL_00c3: nop - IL_00c4: ldstr "ten thousand and one" - IL_00c9: stloc.0 - IL_00ca: br.s IL_00de - - IL_00cc: nop - IL_00cd: ldstr "int.MaxValue" - IL_00d2: stloc.0 - IL_00d3: br.s IL_00de - - IL_00d5: nop - IL_00d6: ldstr "something else" - IL_00db: stloc.0 - IL_00dc: br.s IL_00de - - IL_00de: ldloc.0 - IL_00df: ret + 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 79 (0x4f) + // Code size 74 (0x4a) .maxstack 2 .locals init (string V_0, int32 V_1) @@ -231,51 +219,95 @@ IL_0011: ldloc.1 IL_0012: ldc.i4.0 - IL_0013: beq.s IL_0029 + IL_0013: beq.s IL_0028 IL_0015: ldloc.1 IL_0016: ldc.i4.5 - IL_0017: beq.s IL_0032 + IL_0017: beq.s IL_0030 IL_0019: ldloc.1 IL_001a: ldc.i4.s 10 - IL_001c: beq.s IL_003b + IL_001c: beq.s IL_0038 - IL_001e: br.s IL_0044 + IL_001e: br.s IL_0040 - IL_0020: nop - IL_0021: ldstr "null" - IL_0026: stloc.0 - IL_0027: br.s IL_004d + IL_0020: ldstr "null" + IL_0025: stloc.0 + IL_0026: br.s IL_0048 - IL_0029: nop - IL_002a: ldstr "zero" - IL_002f: stloc.0 - IL_0030: br.s IL_004d + IL_0028: ldstr "zero" + IL_002d: stloc.0 + IL_002e: br.s IL_0048 - IL_0032: nop - IL_0033: ldstr "five" - IL_0038: stloc.0 - IL_0039: br.s IL_004d + IL_0030: ldstr "five" + IL_0035: stloc.0 + IL_0036: br.s IL_0048 - IL_003b: nop - IL_003c: ldstr "ten" - IL_0041: stloc.0 - IL_0042: br.s IL_004d + IL_0038: ldstr "ten" + IL_003d: stloc.0 + IL_003e: br.s IL_0048 - IL_0044: nop - IL_0045: ldstr "large" - IL_004a: stloc.0 - IL_004b: br.s IL_004d + IL_0040: ldstr "large" + IL_0045: stloc.0 + IL_0046: br.s IL_0048 - IL_004d: ldloc.0 - IL_004e: ret + 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 117 (0x75) + // Code size 112 (0x70) .maxstack 2 .locals init (string V_0, valuetype [mscorlib]System.Nullable`1 V_1, @@ -309,51 +341,115 @@ IL_0037: ldloc.3 IL_0038: ldc.i4.0 - IL_0039: beq.s IL_004f + IL_0039: beq.s IL_004e IL_003b: ldloc.3 IL_003c: ldc.i4.5 - IL_003d: beq.s IL_0058 + IL_003d: beq.s IL_0056 IL_003f: ldloc.3 IL_0040: ldc.i4.s 10 - IL_0042: beq.s IL_0061 + IL_0042: beq.s IL_005e - IL_0044: br.s IL_006a + IL_0044: br.s IL_0066 - IL_0046: nop - IL_0047: ldstr "null" - IL_004c: stloc.0 - IL_004d: br.s IL_0073 + IL_0046: ldstr "null" + IL_004b: stloc.0 + IL_004c: br.s IL_006e - IL_004f: nop - IL_0050: ldstr "zero" - IL_0055: stloc.0 - IL_0056: br.s IL_0073 + IL_004e: ldstr "zero" + IL_0053: stloc.0 + IL_0054: br.s IL_006e - IL_0058: nop - IL_0059: ldstr "five" - IL_005e: stloc.0 - IL_005f: br.s IL_0073 - - IL_0061: nop - IL_0062: ldstr "ten" - IL_0067: stloc.0 - IL_0068: br.s IL_0073 - - IL_006a: nop - IL_006b: ldstr "large" - IL_0070: stloc.0 - IL_0071: br.s IL_0073 - - IL_0073: ldloc.0 - IL_0074: ret + 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 70 (0x46) + // Code size 66 (0x42) .maxstack 2 .locals init (string V_0, int32 V_1) @@ -363,7 +459,7 @@ 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_003b + IL_000f: brfalse.s IL_0038 IL_0011: ldloc.1 IL_0012: ldc.i4.0 @@ -371,42 +467,38 @@ IL_0015: ldloc.1 IL_0016: ldc.i4.5 - IL_0017: beq.s IL_0029 + IL_0017: beq.s IL_0028 IL_0019: ldloc.1 IL_001a: ldc.i4.s 10 - IL_001c: beq.s IL_0032 + IL_001c: beq.s IL_0030 - IL_001e: br.s IL_003b + IL_001e: br.s IL_0038 - IL_0020: nop - IL_0021: ldstr "zero" - IL_0026: stloc.0 - IL_0027: br.s IL_0044 + IL_0020: ldstr "zero" + IL_0025: stloc.0 + IL_0026: br.s IL_0040 - IL_0029: nop - IL_002a: ldstr "five" - IL_002f: stloc.0 - IL_0030: br.s IL_0044 + IL_0028: ldstr "five" + IL_002d: stloc.0 + IL_002e: br.s IL_0040 - IL_0032: nop - IL_0033: ldstr "ten" - IL_0038: stloc.0 - IL_0039: br.s IL_0044 + IL_0030: ldstr "ten" + IL_0035: stloc.0 + IL_0036: br.s IL_0040 - IL_003b: nop - IL_003c: ldstr "other" - IL_0041: stloc.0 - IL_0042: br.s IL_0044 + IL_0038: ldstr "other" + IL_003d: stloc.0 + IL_003e: br.s IL_0040 - IL_0044: ldloc.0 - IL_0045: ret + 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 108 (0x6c) + // Code size 104 (0x68) .maxstack 2 .locals init (string V_0, valuetype [mscorlib]System.Nullable`1 V_1, @@ -436,7 +528,7 @@ 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_0061 + IL_0035: brfalse.s IL_005e IL_0037: ldloc.3 IL_0038: ldc.i4.0 @@ -444,41 +536,37 @@ IL_003b: ldloc.3 IL_003c: ldc.i4.5 - IL_003d: beq.s IL_004f + IL_003d: beq.s IL_004e IL_003f: ldloc.3 IL_0040: ldc.i4.s 10 - IL_0042: beq.s IL_0058 + IL_0042: beq.s IL_0056 - IL_0044: br.s IL_0061 + IL_0044: br.s IL_005e - IL_0046: nop - IL_0047: ldstr "zero" - IL_004c: stloc.0 - IL_004d: br.s IL_006a + IL_0046: ldstr "zero" + IL_004b: stloc.0 + IL_004c: br.s IL_0066 - IL_004f: nop - IL_0050: ldstr "five" - IL_0055: stloc.0 - IL_0056: br.s IL_006a + IL_004e: ldstr "five" + IL_0053: stloc.0 + IL_0054: br.s IL_0066 - IL_0058: nop - IL_0059: ldstr "ten" - IL_005e: stloc.0 - IL_005f: br.s IL_006a + IL_0056: ldstr "ten" + IL_005b: stloc.0 + IL_005c: br.s IL_0066 - IL_0061: nop - IL_0062: ldstr "other" - IL_0067: stloc.0 - IL_0068: br.s IL_006a + IL_005e: ldstr "other" + IL_0063: stloc.0 + IL_0064: br.s IL_0066 - IL_006a: ldloc.0 - IL_006b: ret + IL_0066: ldloc.0 + IL_0067: ret } // end of method Switch::SwitchOverNullableIntNoNullCaseShifted .method public hidebysig static void SwitchOverInt(int32 i) cil managed { - // Code size 151 (0x97) + // Code size 144 (0x90) .maxstack 2 .locals init (int32 V_0) IL_0000: nop @@ -494,13 +582,13 @@ IL_000c: ldloc.0 IL_000d: ldc.i4.5 - IL_000e: beq.s IL_0042 + IL_000e: beq.s IL_0041 IL_0010: ldloc.0 IL_0011: ldc.i4.s 10 - IL_0013: beq.s IL_0050 + IL_0013: beq.s IL_004e - IL_0015: br.s IL_0096 + IL_0015: br.s IL_008f IL_0017: ldloc.0 IL_0018: ldc.i4.s 20 @@ -508,73 +596,66 @@ IL_001c: ldloc.0 IL_001d: ldc.i4.s 15 - IL_001f: beq.s IL_005e + IL_001f: beq.s IL_005b IL_0021: ldloc.0 IL_0022: ldc.i4.s 20 - IL_0024: beq.s IL_006c + IL_0024: beq.s IL_0068 - IL_0026: br.s IL_0096 + IL_0026: br.s IL_008f IL_0028: ldloc.0 IL_0029: ldc.i4.s 25 - IL_002b: beq.s IL_007a + IL_002b: beq.s IL_0075 IL_002d: ldloc.0 IL_002e: ldc.i4.s 30 - IL_0030: beq.s IL_0088 + IL_0030: beq.s IL_0082 - IL_0032: br.s IL_0096 + IL_0032: br.s IL_008f - IL_0034: nop - IL_0035: ldstr "zero" - IL_003a: call void [mscorlib]System.Console::WriteLine(string) - IL_003f: nop - IL_0040: br.s IL_0096 + IL_0034: ldstr "zero" + IL_0039: call void [mscorlib]System.Console::WriteLine(string) + IL_003e: nop + IL_003f: br.s IL_008f - IL_0042: nop - IL_0043: ldstr "five" - IL_0048: call void [mscorlib]System.Console::WriteLine(string) - IL_004d: nop - IL_004e: br.s IL_0096 - - IL_0050: nop - IL_0051: ldstr "ten" - IL_0056: call void [mscorlib]System.Console::WriteLine(string) - IL_005b: nop - IL_005c: br.s IL_0096 - - IL_005e: nop - IL_005f: ldstr "fifteen" - IL_0064: call void [mscorlib]System.Console::WriteLine(string) - IL_0069: nop - IL_006a: br.s IL_0096 - - IL_006c: nop - IL_006d: ldstr "twenty" - IL_0072: call void [mscorlib]System.Console::WriteLine(string) - IL_0077: nop - IL_0078: br.s IL_0096 + IL_0041: ldstr "five" + IL_0046: call void [mscorlib]System.Console::WriteLine(string) + IL_004b: nop + IL_004c: br.s IL_008f - IL_007a: nop - IL_007b: ldstr "twenty-five" - IL_0080: call void [mscorlib]System.Console::WriteLine(string) - IL_0085: nop - IL_0086: br.s IL_0096 - - IL_0088: nop - 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 + 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 102 (0x66) + // Code size 98 (0x62) .maxstack 2 .locals init (string V_0, string V_1) @@ -588,7 +669,7 @@ IL_0012: ldarg.0 IL_0013: stloc.1 IL_0014: ldloc.1 - IL_0015: brfalse.s IL_005b + IL_0015: brfalse.s IL_0058 IL_0017: ldloc.1 IL_0018: ldstr "First case" @@ -600,44 +681,40 @@ IL_0025: ldstr "Second case" IL_002a: call bool [mscorlib]System.String::op_Equality(string, string) - IL_002f: brtrue.s IL_0049 + 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_0052 + IL_003c: brtrue.s IL_0050 - IL_003e: br.s IL_005b + IL_003e: br.s IL_0058 - IL_0040: nop - IL_0041: ldstr "Text1" - IL_0046: stloc.0 - IL_0047: br.s IL_0064 - - IL_0049: nop - IL_004a: ldstr "Text2" - IL_004f: stloc.0 - IL_0050: br.s IL_0064 - - IL_0052: nop - IL_0053: ldstr "Text3" - IL_0058: stloc.0 - IL_0059: br.s IL_0064 - - IL_005b: nop - IL_005c: ldstr "Default" - IL_0061: stloc.0 - IL_0062: br.s IL_0064 - - IL_0064: ldloc.0 - IL_0065: ret + 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 89 (0x59) + // Code size 85 (0x55) .maxstack 2 .locals init (string V_0, string V_1) @@ -651,7 +728,7 @@ IL_0012: ldarg.0 IL_0013: stloc.1 IL_0014: ldloc.1 - IL_0015: brfalse.s IL_0045 + IL_0015: brfalse.s IL_0043 IL_0017: ldloc.1 IL_0018: ldstr "First case" @@ -663,38 +740,34 @@ IL_0025: ldstr "Second case" IL_002a: call bool [mscorlib]System.String::op_Equality(string, string) - IL_002f: brtrue.s IL_003c + IL_002f: brtrue.s IL_003b - IL_0031: br.s IL_004e + IL_0031: br.s IL_004b - IL_0033: nop - IL_0034: ldstr "Text1" - IL_0039: stloc.0 - IL_003a: br.s IL_0057 + IL_0033: ldstr "Text1" + IL_0038: stloc.0 + IL_0039: br.s IL_0053 - IL_003c: nop - IL_003d: ldstr "Text2" - IL_0042: stloc.0 - IL_0043: br.s IL_0057 + IL_003b: ldstr "Text2" + IL_0040: stloc.0 + IL_0041: br.s IL_0053 - IL_0045: nop - IL_0046: ldstr "null" - IL_004b: stloc.0 - IL_004c: br.s IL_0057 + IL_0043: ldstr "null" + IL_0048: stloc.0 + IL_0049: br.s IL_0053 - IL_004e: nop - IL_004f: ldstr "Default" - IL_0054: stloc.0 - IL_0055: br.s IL_0057 + IL_004b: ldstr "Default" + IL_0050: stloc.0 + IL_0051: br.s IL_0053 - IL_0057: ldloc.0 - IL_0058: ret + IL_0053: ldloc.0 + IL_0054: ret } // end of method Switch::ShortSwitchOverStringWithNullCase .method public hidebysig static string SwitchOverString1(string text) cil managed { - // Code size 255 (0xff) + // Code size 247 (0xf7) .maxstack 4 .locals init (string V_0, string V_1, @@ -709,10 +782,10 @@ IL_0012: ldarg.0 IL_0013: stloc.1 IL_0014: ldloc.1 - IL_0015: brfalse IL_00ef + IL_0015: brfalse IL_00e9 IL_001a: volatile. - IL_001c: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{7A918135-BE28-4F14-9240-86E34BA33540}'::'$$method0x6000009-1' + 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 @@ -753,74 +826,66 @@ 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 '{7A918135-BE28-4F14-9240-86E34BA33540}'::'$$method0x6000009-1' + 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 '{7A918135-BE28-4F14-9240-86E34BA33540}'::'$$method0x6000009-1' + 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_00f4 + IL_0093: brfalse.s IL_00ed IL_0095: ldloc.2 IL_0096: switch ( IL_00b9, - IL_00c2, - IL_00c2, - IL_00cb, - IL_00d4, - IL_00dd, - IL_00e6) - IL_00b7: br.s IL_00f4 - - IL_00b9: nop - IL_00ba: ldstr "Text1" - IL_00bf: stloc.0 - IL_00c0: br.s IL_00fd - - IL_00c2: nop - IL_00c3: ldstr "Text2" - IL_00c8: stloc.0 - IL_00c9: br.s IL_00fd - - IL_00cb: nop - IL_00cc: ldstr "Text3" - IL_00d1: stloc.0 - IL_00d2: br.s IL_00fd - - IL_00d4: nop - IL_00d5: ldstr "Text4" - IL_00da: stloc.0 - IL_00db: br.s IL_00fd - - IL_00dd: nop - IL_00de: ldstr "Text5" - IL_00e3: stloc.0 - IL_00e4: br.s IL_00fd - - IL_00e6: nop - IL_00e7: ldstr "Text6" - IL_00ec: stloc.0 - IL_00ed: br.s IL_00fd - - IL_00ef: nop - IL_00f0: ldnull - IL_00f1: stloc.0 - IL_00f2: br.s IL_00fd - - IL_00f4: nop - IL_00f5: ldstr "Default" - IL_00fa: stloc.0 - IL_00fb: br.s IL_00fd - - IL_00fd: ldloc.0 - IL_00fe: ret + 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 366 (0x16e) + // Code size 354 (0x162) .maxstack 4 .locals init (string V_0, string V_1, @@ -832,10 +897,10 @@ IL_000c: call string [mscorlib]System.Environment::get_UserName() IL_0011: stloc.1 IL_0012: ldloc.1 - IL_0013: brfalse IL_0163 + IL_0013: brfalse IL_0158 IL_0018: volatile. - IL_001a: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{7A918135-BE28-4F14-9240-86E34BA33540}'::'$$method0x600000a-1' + 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 @@ -896,98 +961,86 @@ 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 '{7A918135-BE28-4F14-9240-86E34BA33540}'::'$$method0x600000a-1' + 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 '{7A918135-BE28-4F14-9240-86E34BA33540}'::'$$method0x600000a-1' + 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_0163 + IL_00c7: brfalse IL_0158 IL_00cc: ldloc.2 IL_00cd: switch ( IL_0100, - IL_0109, - IL_0112, - IL_011b, - IL_0124, - IL_012d, - IL_0136, - IL_013f, + IL_0108, + IL_0110, + IL_0118, + IL_0120, + IL_0128, + IL_0130, + IL_0138, + IL_0140, IL_0148, - IL_0151, - IL_015a) - IL_00fe: br.s IL_0163 - - IL_0100: nop - IL_0101: ldstr "Text1" - IL_0106: stloc.0 - IL_0107: br.s IL_016c - - IL_0109: nop - IL_010a: ldstr "Text2" - IL_010f: stloc.0 - IL_0110: br.s IL_016c - - IL_0112: nop - IL_0113: ldstr "Text3" - IL_0118: stloc.0 - IL_0119: br.s IL_016c - - IL_011b: nop - IL_011c: ldstr "Text4" - IL_0121: stloc.0 - IL_0122: br.s IL_016c - - IL_0124: nop - IL_0125: ldstr "Text5" - IL_012a: stloc.0 - IL_012b: br.s IL_016c - - IL_012d: nop - IL_012e: ldstr "Text6" - IL_0133: stloc.0 - IL_0134: br.s IL_016c - - IL_0136: nop - IL_0137: ldstr "Text7" - IL_013c: stloc.0 - IL_013d: br.s IL_016c - - IL_013f: nop - IL_0140: ldstr "Text8" + 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_016c - - IL_0148: nop - IL_0149: ldstr "Text9" - IL_014e: stloc.0 - IL_014f: br.s IL_016c - - IL_0151: nop - IL_0152: ldstr "Text10" - IL_0157: stloc.0 - IL_0158: br.s IL_016c - - IL_015a: nop - IL_015b: ldstr "Text11" - IL_0160: stloc.0 - IL_0161: br.s IL_016c - - IL_0163: nop - IL_0164: ldstr "Default" - IL_0169: stloc.0 - IL_016a: br.s IL_016c - - IL_016c: ldloc.0 - IL_016d: ret + 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 67 (0x43) + // Code size 64 (0x40) .maxstack 2 .locals init (string V_0, bool V_1) @@ -1003,32 +1056,29 @@ IL_0019: stloc.1 IL_001a: ldloc.1 IL_001b: switch ( - IL_0033, + IL_0032, IL_002a) - IL_0028: br.s IL_003c + IL_0028: br.s IL_003a - IL_002a: nop - IL_002b: ldsfld string [mscorlib]System.Boolean::TrueString - IL_0030: stloc.0 - IL_0031: br.s IL_0041 + IL_002a: ldsfld string [mscorlib]System.Boolean::TrueString + IL_002f: stloc.0 + IL_0030: br.s IL_003e - IL_0033: nop - IL_0034: ldsfld string [mscorlib]System.Boolean::FalseString - IL_0039: stloc.0 - IL_003a: br.s IL_0041 + IL_0032: ldsfld string [mscorlib]System.Boolean::FalseString + IL_0037: stloc.0 + IL_0038: br.s IL_003e - IL_003c: nop - IL_003d: ldnull - IL_003e: stloc.0 - IL_003f: br.s IL_0041 + IL_003a: ldnull + IL_003b: stloc.0 + IL_003c: br.s IL_003e - IL_0041: ldloc.0 - IL_0042: ret + IL_003e: ldloc.0 + IL_003f: ret } // end of method Switch::SwitchOverBool .method public hidebysig static void SwitchInLoop(int32 i) cil managed { - // Code size 132 (0x84) + // Code size 128 (0x80) .maxstack 2 .locals init (int32 V_0, bool V_1) @@ -1040,7 +1090,7 @@ object) IL_0011: call void [mscorlib]System.Console::WriteLine(string) IL_0016: nop - IL_0017: br.s IL_007f + IL_0017: br.s IL_007b IL_0019: nop IL_001a: ldarg.0 @@ -1050,53 +1100,49 @@ IL_001e: sub IL_001f: switch ( IL_0036, - IL_0044, - IL_0060, - IL_0052) - IL_0034: br.s IL_0060 - - IL_0036: nop - IL_0037: ldstr "one" - IL_003c: call void [mscorlib]System.Console::WriteLine(string) - IL_0041: nop - IL_0042: br.s IL_0079 - - IL_0044: nop - IL_0045: ldstr "two" - IL_004a: call void [mscorlib]System.Console::WriteLine(string) - IL_004f: nop - IL_0050: br.s IL_0079 - - IL_0052: nop - IL_0053: ldstr "four" - IL_0058: call void [mscorlib]System.Console::WriteLine(string) - IL_005d: nop - IL_005e: br.s IL_0083 - - IL_0060: nop - IL_0061: ldstr "default" - IL_0066: call void [mscorlib]System.Console::WriteLine(string) - IL_006b: nop - IL_006c: ldstr "more code" - IL_0071: call void [mscorlib]System.Console::WriteLine(string) - IL_0076: nop - IL_0077: br.s IL_0083 - - IL_0079: ldarg.0 - IL_007a: ldc.i4.1 - IL_007b: add - IL_007c: starg.s i - IL_007e: nop - IL_007f: ldc.i4.1 - IL_0080: stloc.1 - IL_0081: br.s IL_0019 + 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_0083: ret + 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 133 (0x85) + // Code size 128 (0x80) .maxstack 2 .locals init (int32 V_0) IL_0000: nop @@ -1114,45 +1160,40 @@ IL_001b: sub IL_001c: switch ( IL_0033, - IL_0041, - IL_004f, - IL_005d) - IL_0031: br.s IL_006b + 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_0033: nop - IL_0034: ldstr "one" - IL_0039: call void [mscorlib]System.Console::WriteLine(string) - IL_003e: nop - IL_003f: br.s IL_006b + IL_004d: ldstr "three" + IL_0052: call void [mscorlib]System.Console::WriteLine(string) + IL_0057: nop + IL_0058: br.s IL_0074 - IL_0041: nop - IL_0042: ldstr "two" - IL_0047: call void [mscorlib]System.Console::WriteLine(string) - IL_004c: nop - IL_004d: br.s IL_004f + IL_005a: ldstr "four" + IL_005f: call void [mscorlib]System.Console::WriteLine(string) + IL_0064: nop + IL_0065: br.s IL_007f - IL_004f: nop - IL_0050: ldstr "three" - IL_0055: call void [mscorlib]System.Console::WriteLine(string) - IL_005a: nop - IL_005b: br.s IL_0079 - - IL_005d: nop - IL_005e: ldstr "four" - IL_0063: call void [mscorlib]System.Console::WriteLine(string) - IL_0068: nop - IL_0069: br.s IL_0084 - - IL_006b: nop - IL_006c: ldstr "default" - IL_0071: call void [mscorlib]System.Console::WriteLine(string) - IL_0076: nop - IL_0077: br.s IL_0079 - - IL_0079: ldstr "End of method" - IL_007e: call void [mscorlib]System.Console::WriteLine(string) - IL_0083: nop - IL_0084: ret + 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[] @@ -1173,7 +1214,7 @@ .method public hidebysig static void SwitchOnStringInForLoop() cil managed { - // Code size 330 (0x14a) + // 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, @@ -1192,7 +1233,7 @@ IL_0012: stloc.2 IL_0013: ldc.i4.0 IL_0014: stloc.3 - IL_0015: br IL_013a + IL_0015: br IL_0131 IL_001a: nop IL_001b: ldloc.2 @@ -1204,10 +1245,10 @@ 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_0129 + IL_0030: brfalse IL_0121 IL_0035: volatile. - IL_0037: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{7A918135-BE28-4F14-9240-86E34BA33540}'::'$$method0x600000f-1' + 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 @@ -1243,103 +1284,97 @@ 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 '{7A918135-BE28-4F14-9240-86E34BA33540}'::'$$method0x600000f-1' + 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 '{7A918135-BE28-4F14-9240-86E34BA33540}'::'$$method0x600000f-1' + 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 IL_0129 - - IL_00a8: ldloc.s V_6 - IL_00aa: switch ( - IL_00c9, - IL_00de, - IL_00f3, - IL_0108, - IL_011d, - IL_011d) - IL_00c7: br.s IL_0129 - - IL_00c9: nop - IL_00ca: ldloc.s V_4 - IL_00cc: ldc.i4.1 - IL_00cd: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) - IL_00d2: nop - IL_00d3: ldloc.0 - IL_00d4: ldloc.s V_4 - IL_00d6: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) - IL_00db: nop - IL_00dc: br.s IL_0135 - - IL_00de: nop - IL_00df: ldloc.s V_4 - IL_00e1: ldc.i4.2 - IL_00e2: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) - IL_00e7: nop - IL_00e8: ldloc.0 - IL_00e9: ldloc.s V_4 - IL_00eb: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) - IL_00f0: nop - IL_00f1: br.s IL_0135 - - IL_00f3: nop - IL_00f4: ldloc.s V_4 - IL_00f6: ldc.i4.3 - IL_00f7: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) - IL_00fc: nop - IL_00fd: ldloc.0 - IL_00fe: ldloc.s V_4 - IL_0100: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) - IL_0105: nop - IL_0106: br.s IL_0135 - - IL_0108: nop - IL_0109: ldloc.s V_4 - IL_010b: ldc.i4.4 - IL_010c: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) - IL_0111: nop - IL_0112: ldloc.0 - IL_0113: ldloc.s V_4 - IL_0115: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) - IL_011a: nop - IL_011b: br.s IL_0135 - - IL_011d: nop - IL_011e: ldloc.0 - IL_011f: ldloc.s V_4 - IL_0121: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) - IL_0126: nop - IL_0127: br.s IL_0135 - + 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: ldloc.1 - IL_012b: ldloc.s V_4 - IL_012d: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) - IL_0132: nop - IL_0133: br.s IL_0135 - - IL_0135: nop - IL_0136: ldloc.3 - IL_0137: ldc.i4.1 - IL_0138: add - IL_0139: stloc.3 - IL_013a: ldloc.3 - IL_013b: ldloc.2 - IL_013c: ldlen - IL_013d: conv.i4 - IL_013e: clt - IL_0140: stloc.s V_7 - IL_0142: ldloc.s V_7 - IL_0144: brtrue IL_001a - - IL_0149: ret + 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 143 (0x8f) + // Code size 139 (0x8b) .maxstack 2 .locals init (string V_0) IL_0000: nop @@ -1357,7 +1392,7 @@ IL_0010: nop IL_0011: stloc.0 IL_0012: ldloc.0 - IL_0013: brfalse.s IL_0083 + IL_0013: brfalse.s IL_007f IL_0015: ldloc.0 IL_0016: ldstr "a" @@ -1369,55 +1404,51 @@ IL_0023: ldstr "b" IL_0028: call bool [mscorlib]System.String::op_Equality(string, string) - IL_002d: brtrue.s IL_0059 + 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_0067 + 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_0075 - - IL_0049: br.s IL_0083 - - IL_004b: nop - IL_004c: ldstr "a" - IL_0051: call void [mscorlib]System.Console::WriteLine(string) - IL_0056: nop - IL_0057: br.s IL_0083 - - IL_0059: nop - IL_005a: ldstr "b" - IL_005f: call void [mscorlib]System.Console::WriteLine(string) - IL_0064: nop - IL_0065: br.s IL_0083 - - IL_0067: nop - IL_0068: ldstr "c" - IL_006d: call void [mscorlib]System.Console::WriteLine(string) - IL_0072: nop - IL_0073: br.s IL_0083 - - IL_0075: nop - IL_0076: ldstr "d" - IL_007b: call void [mscorlib]System.Console::WriteLine(string) - IL_0080: nop - IL_0081: br.s IL_0083 - - IL_0083: ldstr "end" - IL_0088: call void [mscorlib]System.Console::WriteLine(string) - IL_008d: nop - IL_008e: ret + 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 130 (0x82) + // Code size 126 (0x7e) .maxstack 2 .locals init (string V_0) IL_0000: nop @@ -1426,7 +1457,7 @@ IL_0003: ldelem.ref IL_0004: stloc.0 IL_0005: ldloc.0 - IL_0006: brfalse.s IL_0076 + IL_0006: brfalse.s IL_0072 IL_0008: ldloc.0 IL_0009: ldstr "a" @@ -1438,62 +1469,58 @@ IL_0016: ldstr "b" IL_001b: call bool [mscorlib]System.String::op_Equality(string, string) - IL_0020: brtrue.s IL_004c + 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_005a + 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_0068 - - IL_003c: br.s IL_0076 - - IL_003e: nop - IL_003f: ldstr "a" - IL_0044: call void [mscorlib]System.Console::WriteLine(string) - IL_0049: nop - IL_004a: br.s IL_0076 - - IL_004c: nop - IL_004d: ldstr "b" - IL_0052: call void [mscorlib]System.Console::WriteLine(string) - IL_0057: nop - IL_0058: br.s IL_0076 - - IL_005a: nop - IL_005b: ldstr "c" - IL_0060: call void [mscorlib]System.Console::WriteLine(string) - IL_0065: nop - IL_0066: br.s IL_0076 - - IL_0068: nop - IL_0069: ldstr "d" - IL_006e: call void [mscorlib]System.Console::WriteLine(string) - IL_0073: nop - IL_0074: br.s IL_0076 - - IL_0076: ldstr "end" - IL_007b: call void [mscorlib]System.Console::WriteLine(string) - IL_0080: nop - IL_0081: ret + 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 '{7A918135-BE28-4F14-9240-86E34BA33540}' +.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 '$$method0x6000009-1' - .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x600000a-1' - .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x600000f-1' -} // end of class '{7A918135-BE28-4F14-9240-86E34BA33540}' + .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}' // ============================================================= diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il index c951dbce9..44acd173c 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.il @@ -10,7 +10,7 @@ .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } -.assembly '4wc22bae' +.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. @@ -20,15 +20,15 @@ .hash algorithm 0x00008004 .ver 0:0:0:0 } -.module '4wc22bae.dll' -// MVID: {B84EA70D-C67F-455B-9708-0E39585F7DA1} +.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: 0x01140000 +// Image base: 0x01900000 // =============== CLASS MEMBERS DECLARATION =================== @@ -219,6 +219,46 @@ 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 { @@ -282,6 +322,66 @@ 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 { @@ -570,7 +670,7 @@ IL_0013: brfalse IL_00db IL_0018: volatile. - IL_001a: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{B84EA70D-C67F-455B-9708-0E39585F7DA1}'::'$$method0x6000009-1' + 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 @@ -611,9 +711,9 @@ 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 '{B84EA70D-C67F-455B-9708-0E39585F7DA1}'::'$$method0x6000009-1' + 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 '{B84EA70D-C67F-455B-9708-0E39585F7DA1}'::'$$method0x6000009-1' + 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, @@ -671,7 +771,7 @@ IL_0011: brfalse IL_013d IL_0016: volatile. - IL_0018: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{B84EA70D-C67F-455B-9708-0E39585F7DA1}'::'$$method0x600000a-1' + 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 @@ -732,9 +832,9 @@ 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 '{B84EA70D-C67F-455B-9708-0E39585F7DA1}'::'$$method0x600000a-1' + 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 '{B84EA70D-C67F-455B-9708-0E39585F7DA1}'::'$$method0x600000a-1' + 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, @@ -958,7 +1058,7 @@ IL_002d: brfalse IL_0115 IL_0032: volatile. - IL_0034: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2 '{B84EA70D-C67F-455B-9708-0E39585F7DA1}'::'$$method0x600000f-1' + 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 @@ -994,9 +1094,9 @@ 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 '{B84EA70D-C67F-455B-9708-0E39585F7DA1}'::'$$method0x600000f-1' + 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 '{B84EA70D-C67F-455B-9708-0E39585F7DA1}'::'$$method0x600000f-1' + 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, @@ -1190,14 +1290,14 @@ } // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch -.class private auto ansi '{B84EA70D-C67F-455B-9708-0E39585F7DA1}' +.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 '$$method0x6000009-1' - .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x600000a-1' - .field static assembly class [mscorlib]System.Collections.Generic.Dictionary`2 '$$method0x600000f-1' -} // end of class '{B84EA70D-C67F-455B-9708-0E39585F7DA1}' + .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}' // ============================================================= diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il index 00d6cacd1..3c4b7ec5b 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.opt.roslyn.il @@ -25,14 +25,14 @@ .ver 0:0:0:0 } .module Switch.dll -// MVID: {F38BF1C4-C0E5-4BAD-8838-849DFFFA97DF} +// 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: 0x00300000 +// Image base: 0x00690000 // =============== CLASS MEMBERS DECLARATION =================== @@ -230,6 +230,48 @@ 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 { @@ -292,6 +334,65 @@ 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 { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il index 0ce401e0a..47d88ae31 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.roslyn.il @@ -25,14 +25,14 @@ .ver 0:0:0:0 } .module Switch.dll -// MVID: {25920C54-28DD-4B8C-9EBF-16E716D0EC15} +// 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: 0x01040000 +// Image base: 0x00690000 // =============== CLASS MEMBERS DECLARATION =================== @@ -95,7 +95,7 @@ .method public hidebysig static string SparseIntegerSwitch(int32 i) cil managed { - // Code size 238 (0xee) + // Code size 226 (0xe2) .maxstack 2 .locals init (int32 V_0, string V_1) @@ -121,7 +121,7 @@ IL_0027: ldloc.0 IL_0028: ldc.i4.s -100 - IL_002a: beq.s IL_0089 + IL_002a: beq.s IL_0088 IL_002c: br.s IL_002e @@ -129,13 +129,13 @@ IL_002f: ldc.i4.m1 IL_0030: sub IL_0031: switch ( - IL_0092, - IL_009b, - IL_00a4, - IL_00ad, - IL_00e3, - IL_00b6) - IL_004e: br IL_00e3 + 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 @@ -143,96 +143,84 @@ IL_005b: ldloc.0 IL_005c: ldc.i4.s 100 - IL_005e: beq.s IL_00bf + 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_00c8 + IL_0068: beq.s IL_00c0 - IL_006a: br.s IL_00e3 + IL_006a: br.s IL_00d8 IL_006c: ldloc.0 IL_006d: ldc.i4 0x2711 - IL_0072: beq.s IL_00d1 + 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_00da - - IL_007e: br.s IL_00e3 - - IL_0080: nop - IL_0081: ldstr "-10 mln" - IL_0086: stloc.1 - IL_0087: br.s IL_00ec - - IL_0089: nop - IL_008a: ldstr "-hundred" - IL_008f: stloc.1 - IL_0090: br.s IL_00ec - - IL_0092: nop - IL_0093: ldstr "-1" - IL_0098: stloc.1 - IL_0099: br.s IL_00ec - - IL_009b: nop - IL_009c: ldstr "0" - IL_00a1: stloc.1 - IL_00a2: br.s IL_00ec - - IL_00a4: nop - IL_00a5: ldstr "1" - IL_00aa: stloc.1 - IL_00ab: br.s IL_00ec - - IL_00ad: nop - IL_00ae: ldstr "2" - IL_00b3: stloc.1 - IL_00b4: br.s IL_00ec - - IL_00b6: nop - IL_00b7: ldstr "4" - IL_00bc: stloc.1 - IL_00bd: br.s IL_00ec - - IL_00bf: nop - IL_00c0: ldstr "hundred" + 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_00ec + IL_00c6: br.s IL_00e0 - IL_00c8: nop - IL_00c9: ldstr "ten thousand" - IL_00ce: stloc.1 - IL_00cf: br.s IL_00ec + IL_00c8: ldstr "ten thousand and one" + IL_00cd: stloc.1 + IL_00ce: br.s IL_00e0 - IL_00d1: nop - IL_00d2: ldstr "ten thousand and one" - IL_00d7: stloc.1 - IL_00d8: br.s IL_00ec - - IL_00da: nop - IL_00db: ldstr "int.MaxValue" - IL_00e0: stloc.1 - IL_00e1: br.s IL_00ec - - IL_00e3: nop - IL_00e4: ldstr "something else" - IL_00e9: stloc.1 - IL_00ea: br.s IL_00ec - - IL_00ec: ldloc.1 - IL_00ed: ret + 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 87 (0x57) + // Code size 82 (0x52) .maxstack 2 .locals init (valuetype [mscorlib]System.Nullable`1 V_0, valuetype [mscorlib]System.Nullable`1 V_1, @@ -251,55 +239,108 @@ IL_0010: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() IL_0015: stloc.2 IL_0016: ldloc.2 - IL_0017: brfalse.s IL_0031 + 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_003a + 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_0043 + IL_0024: beq.s IL_0040 - IL_0026: br.s IL_004c + IL_0026: br.s IL_0048 - IL_0028: nop - IL_0029: ldstr "null" - IL_002e: stloc.3 - IL_002f: br.s IL_0055 + IL_0028: ldstr "null" + IL_002d: stloc.3 + IL_002e: br.s IL_0050 - IL_0031: nop - IL_0032: ldstr "zero" - IL_0037: stloc.3 - IL_0038: br.s IL_0055 + IL_0030: ldstr "zero" + IL_0035: stloc.3 + IL_0036: br.s IL_0050 - IL_003a: nop - IL_003b: ldstr "five" - IL_0040: stloc.3 - IL_0041: br.s IL_0055 + IL_0038: ldstr "five" + IL_003d: stloc.3 + IL_003e: br.s IL_0050 - IL_0043: nop - IL_0044: ldstr "ten" - IL_0049: stloc.3 - IL_004a: br.s IL_0055 + IL_0040: ldstr "ten" + IL_0045: stloc.3 + IL_0046: br.s IL_0050 - IL_004c: nop - IL_004d: ldstr "large" - IL_0052: stloc.3 - IL_0053: br.s IL_0055 + IL_0048: ldstr "large" + IL_004d: stloc.3 + IL_004e: br.s IL_0050 - IL_0055: ldloc.3 - IL_0056: ret + 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 132 (0x84) + // Code size 127 (0x7f) .maxstack 2 .locals init (valuetype [mscorlib]System.Nullable`1 V_0, valuetype [mscorlib]System.Nullable`1 V_1, @@ -335,55 +376,125 @@ 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_0059 + 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_0063 + 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_006d + IL_004b: beq.s IL_006a - IL_004d: br.s IL_0077 + IL_004d: br.s IL_0073 - IL_004f: nop - IL_0050: ldstr "null" - IL_0055: stloc.s V_5 - IL_0057: br.s IL_0081 + IL_004f: ldstr "null" + IL_0054: stloc.s V_5 + IL_0056: br.s IL_007c - IL_0059: nop - IL_005a: ldstr "zero" - IL_005f: stloc.s V_5 - IL_0061: br.s IL_0081 + IL_0058: ldstr "zero" + IL_005d: stloc.s V_5 + IL_005f: br.s IL_007c - IL_0063: nop - IL_0064: ldstr "five" - IL_0069: stloc.s V_5 - IL_006b: br.s IL_0081 + IL_0061: ldstr "five" + IL_0066: stloc.s V_5 + IL_0068: br.s IL_007c - IL_006d: nop - IL_006e: ldstr "ten" - IL_0073: stloc.s V_5 - IL_0075: br.s IL_0081 + IL_006a: ldstr "ten" + IL_006f: stloc.s V_5 + IL_0071: br.s IL_007c - IL_0077: nop - IL_0078: ldstr "large" - IL_007d: stloc.s V_5 - IL_007f: br.s IL_0081 + IL_0073: ldstr "large" + IL_0078: stloc.s V_5 + IL_007a: br.s IL_007c - IL_0081: ldloc.s V_5 - IL_0083: ret + 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 78 (0x4e) + // Code size 74 (0x4a) .maxstack 2 .locals init (valuetype [mscorlib]System.Nullable`1 V_0, valuetype [mscorlib]System.Nullable`1 V_1, @@ -396,7 +507,7 @@ 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_0043 + IL_000c: brfalse.s IL_0040 IL_000e: ldloca.s V_0 IL_0010: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() @@ -408,44 +519,40 @@ IL_001b: ldloc.2 IL_001c: ldc.i4.5 - IL_001d: beq.s IL_0031 + 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_003a + IL_0024: beq.s IL_0038 - IL_0026: br.s IL_0043 + IL_0026: br.s IL_0040 - IL_0028: nop - IL_0029: ldstr "zero" - IL_002e: stloc.3 - IL_002f: br.s IL_004c + IL_0028: ldstr "zero" + IL_002d: stloc.3 + IL_002e: br.s IL_0048 - IL_0031: nop - IL_0032: ldstr "five" - IL_0037: stloc.3 - IL_0038: br.s IL_004c + IL_0030: ldstr "five" + IL_0035: stloc.3 + IL_0036: br.s IL_0048 - IL_003a: nop - IL_003b: ldstr "ten" - IL_0040: stloc.3 - IL_0041: br.s IL_004c + IL_0038: ldstr "ten" + IL_003d: stloc.3 + IL_003e: br.s IL_0048 - IL_0043: nop - IL_0044: ldstr "other" - IL_0049: stloc.3 - IL_004a: br.s IL_004c + IL_0040: ldstr "other" + IL_0045: stloc.3 + IL_0046: br.s IL_0048 - IL_004c: ldloc.3 - IL_004d: ret + 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 122 (0x7a) + // Code size 118 (0x76) .maxstack 2 .locals init (valuetype [mscorlib]System.Nullable`1 V_0, valuetype [mscorlib]System.Nullable`1 V_1, @@ -475,7 +582,7 @@ 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_006d + IL_002f: brfalse.s IL_006a IL_0031: ldloca.s V_0 IL_0033: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() @@ -487,43 +594,39 @@ IL_0040: ldloc.s V_4 IL_0042: ldc.i4.5 - IL_0043: beq.s IL_0059 + 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_0063 + IL_004b: beq.s IL_0061 - IL_004d: br.s IL_006d + IL_004d: br.s IL_006a - IL_004f: nop - IL_0050: ldstr "zero" - IL_0055: stloc.s V_5 - IL_0057: br.s IL_0077 + IL_004f: ldstr "zero" + IL_0054: stloc.s V_5 + IL_0056: br.s IL_0073 - IL_0059: nop - IL_005a: ldstr "five" - IL_005f: stloc.s V_5 - IL_0061: br.s IL_0077 + IL_0058: ldstr "five" + IL_005d: stloc.s V_5 + IL_005f: br.s IL_0073 - IL_0063: nop - IL_0064: ldstr "ten" - IL_0069: stloc.s V_5 - IL_006b: br.s IL_0077 + IL_0061: ldstr "ten" + IL_0066: stloc.s V_5 + IL_0068: br.s IL_0073 - IL_006d: nop - IL_006e: ldstr "other" - IL_0073: stloc.s V_5 - IL_0075: br.s IL_0077 + IL_006a: ldstr "other" + IL_006f: stloc.s V_5 + IL_0071: br.s IL_0073 - IL_0077: ldloc.s V_5 - IL_0079: ret + 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 161 (0xa1) + // Code size 151 (0x97) .maxstack 2 .locals init (int32 V_0) IL_0000: nop @@ -531,102 +634,95 @@ IL_0002: stloc.0 IL_0003: ldloc.0 IL_0004: ldc.i4.s 10 - IL_0006: bgt.s IL_001d + IL_0006: bgt.s IL_001a IL_0008: ldloc.0 - IL_0009: brfalse.s IL_003e + 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_004c + 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_005a + IL_0016: beq.s IL_0055 - IL_0018: br IL_00a0 + IL_0018: br.s IL_0096 - IL_001d: ldloc.0 - IL_001e: ldc.i4.s 20 - IL_0020: bgt.s IL_0030 + 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_0022: ldloc.0 - IL_0023: ldc.i4.s 15 - IL_0025: beq.s IL_0068 + IL_0024: br.s IL_0026 - IL_0027: br.s IL_0029 + IL_0026: ldloc.0 + IL_0027: ldc.i4.s 20 + IL_0029: beq.s IL_006f - IL_0029: ldloc.0 - IL_002a: ldc.i4.s 20 - IL_002c: beq.s IL_0076 + IL_002b: br.s IL_0096 - IL_002e: br.s IL_00a0 + IL_002d: ldloc.0 + IL_002e: ldc.i4.s 25 + IL_0030: beq.s IL_007c - IL_0030: ldloc.0 - IL_0031: ldc.i4.s 25 - IL_0033: beq.s IL_0084 + IL_0032: br.s IL_0034 - IL_0035: br.s IL_0037 + IL_0034: ldloc.0 + IL_0035: ldc.i4.s 30 + IL_0037: beq.s IL_0089 - IL_0037: ldloc.0 - IL_0038: ldc.i4.s 30 - IL_003a: beq.s IL_0092 + IL_0039: br.s IL_0096 - IL_003c: br.s IL_00a0 + IL_003b: ldstr "zero" + IL_0040: call void [mscorlib]System.Console::WriteLine(string) + IL_0045: nop + IL_0046: br.s IL_0096 - IL_003e: nop - IL_003f: ldstr "zero" - IL_0044: call void [mscorlib]System.Console::WriteLine(string) - IL_0049: nop - IL_004a: br.s IL_00a0 + IL_0048: ldstr "five" + IL_004d: call void [mscorlib]System.Console::WriteLine(string) + IL_0052: nop + IL_0053: br.s IL_0096 - IL_004c: nop - IL_004d: ldstr "five" - IL_0052: call void [mscorlib]System.Console::WriteLine(string) - IL_0057: nop - IL_0058: br.s IL_00a0 + IL_0055: ldstr "ten" + IL_005a: call void [mscorlib]System.Console::WriteLine(string) + IL_005f: nop + IL_0060: br.s IL_0096 - IL_005a: nop - IL_005b: ldstr "ten" - IL_0060: call void [mscorlib]System.Console::WriteLine(string) - IL_0065: nop - IL_0066: br.s IL_00a0 - - IL_0068: nop - IL_0069: ldstr "fifteen" - IL_006e: call void [mscorlib]System.Console::WriteLine(string) - IL_0073: nop - IL_0074: br.s IL_00a0 - - IL_0076: nop - IL_0077: ldstr "twenty" - IL_007c: call void [mscorlib]System.Console::WriteLine(string) - IL_0081: nop - IL_0082: br.s IL_00a0 + IL_0062: ldstr "fifteen" + IL_0067: call void [mscorlib]System.Console::WriteLine(string) + IL_006c: nop + IL_006d: br.s IL_0096 - IL_0084: nop - IL_0085: ldstr "twenty-five" - IL_008a: call void [mscorlib]System.Console::WriteLine(string) - IL_008f: nop - IL_0090: br.s IL_00a0 - - IL_0092: nop - IL_0093: ldstr "thirty" - IL_0098: call void [mscorlib]System.Console::WriteLine(string) - IL_009d: nop - IL_009e: br.s IL_00a0 - - IL_00a0: ret + 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 99 (0x63) + // Code size 95 (0x5f) .maxstack 2 .locals init (string V_0, string V_1) @@ -649,44 +745,40 @@ IL_0022: ldstr "Second case" IL_0027: call bool [mscorlib]System.String::op_Equality(string, string) - IL_002c: brtrue.s IL_0046 + 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_004f + IL_0039: brtrue.s IL_004d - IL_003b: br.s IL_0058 + IL_003b: br.s IL_0055 - IL_003d: nop - IL_003e: ldstr "Text1" - IL_0043: stloc.1 - IL_0044: br.s IL_0061 - - IL_0046: nop - IL_0047: ldstr "Text2" - IL_004c: stloc.1 - IL_004d: br.s IL_0061 - - IL_004f: nop - IL_0050: ldstr "Text3" - IL_0055: stloc.1 - IL_0056: br.s IL_0061 - - IL_0058: nop - IL_0059: ldstr "Default" - IL_005e: stloc.1 - IL_005f: br.s IL_0061 - - IL_0061: ldloc.1 - IL_0062: ret + 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 89 (0x59) + // Code size 85 (0x55) .maxstack 2 .locals init (string V_0, string V_1) @@ -709,41 +801,37 @@ IL_0022: ldstr "Second case" IL_0027: call bool [mscorlib]System.String::op_Equality(string, string) - IL_002c: brtrue.s IL_003c + IL_002c: brtrue.s IL_003b IL_002e: ldloc.0 - IL_002f: brfalse.s IL_0045 + IL_002f: brfalse.s IL_0043 - IL_0031: br.s IL_004e + IL_0031: br.s IL_004b - IL_0033: nop - IL_0034: ldstr "Text1" - IL_0039: stloc.1 - IL_003a: br.s IL_0057 + IL_0033: ldstr "Text1" + IL_0038: stloc.1 + IL_0039: br.s IL_0053 - IL_003c: nop - IL_003d: ldstr "Text2" - IL_0042: stloc.1 - IL_0043: br.s IL_0057 + IL_003b: ldstr "Text2" + IL_0040: stloc.1 + IL_0041: br.s IL_0053 - IL_0045: nop - IL_0046: ldstr "null" - IL_004b: stloc.1 - IL_004c: br.s IL_0057 + IL_0043: ldstr "null" + IL_0048: stloc.1 + IL_0049: br.s IL_0053 - IL_004e: nop - IL_004f: ldstr "Default" - IL_0054: stloc.1 - IL_0055: br.s IL_0057 + IL_004b: ldstr "Default" + IL_0050: stloc.1 + IL_0051: br.s IL_0053 - IL_0057: ldloc.1 - IL_0058: ret + IL_0053: ldloc.1 + IL_0054: ret } // end of method Switch::ShortSwitchOverStringWithNullCase .method public hidebysig static string SwitchOverString1(string text) cil managed { - // Code size 333 (0x14d) + // Code size 325 (0x145) .maxstack 2 .locals init (string V_0, uint32 V_1, @@ -777,7 +865,7 @@ IL_0034: ldc.i4 0x8861b86 IL_0039: beq IL_00e4 - IL_003e: br IL_0142 + IL_003e: br IL_013b IL_0043: ldloc.1 IL_0044: ldc.i4 0xc9a8f4f @@ -789,7 +877,7 @@ IL_004e: ldc.i4 0xf3d44a6 IL_0053: beq.s IL_00c6 - IL_0055: br IL_0142 + IL_0055: br IL_013b IL_005a: ldloc.1 IL_005b: ldc.i4 0x652a1179 @@ -805,7 +893,7 @@ IL_0070: ldc.i4 0x652a1179 IL_0075: beq.s IL_00b7 - IL_0077: br IL_0142 + IL_0077: br IL_013b IL_007c: ldloc.1 IL_007d: ldc.i4 0xea3d096b @@ -817,7 +905,7 @@ IL_0087: ldc.i4 0xf701cc7f IL_008c: beq.s IL_00d5 - IL_008e: br IL_0142 + IL_008e: br IL_013b IL_0093: ldloc.0 IL_0094: ldstr "First case" @@ -825,109 +913,101 @@ string) IL_009e: brtrue.s IL_0107 - IL_00a0: br IL_0142 + 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_0110 + IL_00b0: brtrue.s IL_010f - IL_00b2: br IL_0142 + 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_0110 + IL_00c2: brtrue.s IL_010f - IL_00c4: br.s IL_0142 + 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_0119 + IL_00d1: brtrue.s IL_0117 - IL_00d3: br.s IL_0142 + 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_0122 + IL_00e0: brtrue.s IL_011f - IL_00e2: br.s IL_0142 + 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_012b + IL_00ef: brtrue.s IL_0127 - IL_00f1: br.s IL_0142 + 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_0134 + IL_00fe: brtrue.s IL_012f - IL_0100: br.s IL_0142 + IL_0100: br.s IL_013b IL_0102: ldloc.0 - IL_0103: brfalse.s IL_013d - - IL_0105: br.s IL_0142 - - IL_0107: nop - IL_0108: ldstr "Text1" - IL_010d: stloc.2 - IL_010e: br.s IL_014b - - IL_0110: nop - IL_0111: ldstr "Text2" - IL_0116: stloc.2 - IL_0117: br.s IL_014b - - IL_0119: nop - IL_011a: ldstr "Text3" - IL_011f: stloc.2 - IL_0120: br.s IL_014b - - IL_0122: nop - IL_0123: ldstr "Text4" - IL_0128: stloc.2 - IL_0129: br.s IL_014b - - IL_012b: nop - IL_012c: ldstr "Text5" - IL_0131: stloc.2 - IL_0132: br.s IL_014b - - IL_0134: nop - IL_0135: ldstr "Text6" - IL_013a: stloc.2 - IL_013b: br.s IL_014b - - IL_013d: nop - IL_013e: ldnull - IL_013f: stloc.2 - IL_0140: br.s IL_014b - - IL_0142: nop - IL_0143: ldstr "Default" - IL_0148: stloc.2 - IL_0149: br.s IL_014b - - IL_014b: ldloc.2 - IL_014c: ret + 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 518 (0x206) + // Code size 500 (0x1f4) .maxstack 2 .locals init (string V_0, uint32 V_1, @@ -959,7 +1039,7 @@ IL_0037: ldc.i4 0xc9a8f4f IL_003c: beq IL_00c6 - IL_0041: br IL_01fb + IL_0041: br IL_01ea IL_0046: ldloc.1 IL_0047: ldc.i4 0xf3d44a6 @@ -969,15 +1049,15 @@ IL_0053: ldloc.1 IL_0054: ldc.i4 0x20289804 - IL_0059: beq IL_0156 + IL_0059: beq IL_0153 IL_005e: br.s IL_0060 IL_0060: ldloc.1 IL_0061: ldc.i4 0x4c7c71f6 - IL_0066: beq IL_0168 + IL_0066: beq IL_0165 - IL_006b: br IL_01fb + IL_006b: br IL_01ea IL_0070: ldloc.1 IL_0071: ldc.i4 0xa151b28a @@ -985,7 +1065,7 @@ IL_0078: ldloc.1 IL_0079: ldc.i4 0x4d0cea48 - IL_007e: beq IL_0189 + IL_007e: beq IL_0183 IL_0083: br.s IL_0085 @@ -997,9 +1077,9 @@ IL_0092: ldloc.1 IL_0093: ldc.i4 0xa151b28a - IL_0098: beq IL_0144 + IL_0098: beq IL_0141 - IL_009d: br IL_01fb + IL_009d: br IL_01ea IL_00a2: ldloc.1 IL_00a3: ldc.i4 0xea3d096b @@ -1009,7 +1089,7 @@ IL_00ac: ldloc.1 IL_00ad: ldc.i4 0xed5134d4 - IL_00b2: beq IL_017a + IL_00b2: beq IL_0174 IL_00b7: br.s IL_00b9 @@ -1017,164 +1097,152 @@ IL_00ba: ldc.i4 0xf701cc7f IL_00bf: beq.s IL_0105 - IL_00c1: br IL_01fb + 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_0198 + IL_00d1: brtrue IL_0192 - IL_00d6: br IL_01fb + 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_01a1 + IL_00e6: brtrue IL_019a - IL_00eb: br IL_01fb + 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_01aa + IL_00fb: brtrue IL_01a2 - IL_0100: br IL_01fb + 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_01b3 + IL_0110: brtrue IL_01aa - IL_0115: br IL_01fb + 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_01bc + IL_0125: brtrue IL_01b2 - IL_012a: br IL_01fb + 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 IL_01c5 + IL_013a: brtrue.s IL_01ba - IL_013f: br IL_01fb + IL_013c: br IL_01ea - IL_0144: ldloc.0 - IL_0145: ldstr "Seventh case" - IL_014a: call bool [mscorlib]System.String::op_Equality(string, + IL_0141: ldloc.0 + IL_0142: ldstr "Seventh case" + IL_0147: call bool [mscorlib]System.String::op_Equality(string, string) - IL_014f: brtrue.s IL_01ce + IL_014c: brtrue.s IL_01c2 - IL_0151: br IL_01fb + IL_014e: br IL_01ea - IL_0156: ldloc.0 - IL_0157: ldstr "Eighth case" - IL_015c: call bool [mscorlib]System.String::op_Equality(string, + IL_0153: ldloc.0 + IL_0154: ldstr "Eighth case" + IL_0159: call bool [mscorlib]System.String::op_Equality(string, string) - IL_0161: brtrue.s IL_01d7 + IL_015e: brtrue.s IL_01ca - IL_0163: br IL_01fb + IL_0160: br IL_01ea - IL_0168: ldloc.0 - IL_0169: ldstr "Ninth case" - IL_016e: call bool [mscorlib]System.String::op_Equality(string, + IL_0165: ldloc.0 + IL_0166: ldstr "Ninth case" + IL_016b: call bool [mscorlib]System.String::op_Equality(string, string) - IL_0173: brtrue.s IL_01e0 + IL_0170: brtrue.s IL_01d2 - IL_0175: br IL_01fb + IL_0172: br.s IL_01ea - IL_017a: ldloc.0 - IL_017b: ldstr "Tenth case" - IL_0180: call bool [mscorlib]System.String::op_Equality(string, + IL_0174: ldloc.0 + IL_0175: ldstr "Tenth case" + IL_017a: call bool [mscorlib]System.String::op_Equality(string, string) - IL_0185: brtrue.s IL_01e9 + IL_017f: brtrue.s IL_01da - IL_0187: br.s IL_01fb + IL_0181: br.s IL_01ea - IL_0189: ldloc.0 - IL_018a: ldstr "Eleventh case" - IL_018f: call bool [mscorlib]System.String::op_Equality(string, + IL_0183: ldloc.0 + IL_0184: ldstr "Eleventh case" + IL_0189: call bool [mscorlib]System.String::op_Equality(string, string) - IL_0194: brtrue.s IL_01f2 + IL_018e: brtrue.s IL_01e2 + + IL_0190: br.s IL_01ea - IL_0196: br.s IL_01fb + IL_0192: ldstr "Text1" + IL_0197: stloc.2 + IL_0198: br.s IL_01f2 - IL_0198: nop - IL_0199: ldstr "Text1" - IL_019e: stloc.2 - IL_019f: br.s IL_0204 + IL_019a: ldstr "Text2" + IL_019f: stloc.2 + IL_01a0: br.s IL_01f2 - IL_01a1: nop - IL_01a2: ldstr "Text2" + IL_01a2: ldstr "Text3" IL_01a7: stloc.2 - IL_01a8: br.s IL_0204 - - IL_01aa: nop - IL_01ab: ldstr "Text3" - IL_01b0: stloc.2 - IL_01b1: br.s IL_0204 - - IL_01b3: nop - IL_01b4: ldstr "Text4" - IL_01b9: stloc.2 - IL_01ba: br.s IL_0204 - - IL_01bc: nop - IL_01bd: ldstr "Text5" - IL_01c2: stloc.2 - IL_01c3: br.s IL_0204 - - IL_01c5: nop - IL_01c6: ldstr "Text6" - IL_01cb: stloc.2 - IL_01cc: br.s IL_0204 - - IL_01ce: nop - IL_01cf: ldstr "Text7" - IL_01d4: stloc.2 - IL_01d5: br.s IL_0204 - - IL_01d7: nop - IL_01d8: ldstr "Text8" - IL_01dd: stloc.2 - IL_01de: br.s IL_0204 - - IL_01e0: nop - IL_01e1: ldstr "Text9" - IL_01e6: stloc.2 - IL_01e7: br.s IL_0204 - - IL_01e9: nop - IL_01ea: ldstr "Text10" - IL_01ef: stloc.2 - IL_01f0: br.s IL_0204 + 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_01f2: nop - IL_01f3: ldstr "Text11" - IL_01f8: stloc.2 - IL_01f9: br.s IL_0204 + IL_01c2: ldstr "Text7" + IL_01c7: stloc.2 + IL_01c8: br.s IL_01f2 - IL_01fb: nop - IL_01fc: ldstr "Default" - IL_0201: stloc.2 - IL_0202: br.s IL_0204 + IL_01ca: ldstr "Text8" + IL_01cf: stloc.2 + IL_01d0: br.s IL_01f2 - IL_0204: ldloc.2 - IL_0205: ret + 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 62 (0x3e) + // Code size 59 (0x3b) .maxstack 2 .locals init (bool V_0, string V_1) @@ -1189,7 +1257,7 @@ IL_0018: ldarg.0 IL_0019: stloc.0 IL_001a: ldloc.0 - IL_001b: brfalse.s IL_002e + IL_001b: brfalse.s IL_002d IL_001d: br.s IL_001f @@ -1197,30 +1265,27 @@ IL_0020: ldc.i4.1 IL_0021: beq.s IL_0025 - IL_0023: br.s IL_0037 + IL_0023: br.s IL_0035 - IL_0025: nop - IL_0026: ldsfld string [mscorlib]System.Boolean::TrueString - IL_002b: stloc.1 - IL_002c: br.s IL_003c + IL_0025: ldsfld string [mscorlib]System.Boolean::TrueString + IL_002a: stloc.1 + IL_002b: br.s IL_0039 - IL_002e: nop - IL_002f: ldsfld string [mscorlib]System.Boolean::FalseString - IL_0034: stloc.1 - IL_0035: br.s IL_003c + IL_002d: ldsfld string [mscorlib]System.Boolean::FalseString + IL_0032: stloc.1 + IL_0033: br.s IL_0039 - IL_0037: nop - IL_0038: ldnull - IL_0039: stloc.1 - IL_003a: br.s IL_003c + IL_0035: ldnull + IL_0036: stloc.1 + IL_0037: br.s IL_0039 - IL_003c: ldloc.1 - IL_003d: ret + IL_0039: ldloc.1 + IL_003a: ret } // end of method Switch::SwitchOverBool .method public hidebysig static void SwitchInLoop(int32 i) cil managed { - // Code size 132 (0x84) + // Code size 128 (0x80) .maxstack 2 .locals init (int32 V_0, bool V_1) @@ -1232,7 +1297,7 @@ object) IL_0011: call void [mscorlib]System.Console::WriteLine(string) IL_0016: nop - IL_0017: br.s IL_007f + IL_0017: br.s IL_007b IL_0019: nop IL_001a: ldarg.0 @@ -1242,53 +1307,49 @@ IL_001e: sub IL_001f: switch ( IL_0036, - IL_0044, - IL_0060, - IL_0052) - IL_0034: br.s IL_0060 - - IL_0036: nop - IL_0037: ldstr "one" - IL_003c: call void [mscorlib]System.Console::WriteLine(string) - IL_0041: nop - IL_0042: br.s IL_0079 - - IL_0044: nop - IL_0045: ldstr "two" - IL_004a: call void [mscorlib]System.Console::WriteLine(string) - IL_004f: nop - IL_0050: br.s IL_0079 - - IL_0052: nop - IL_0053: ldstr "four" - IL_0058: call void [mscorlib]System.Console::WriteLine(string) - IL_005d: nop - IL_005e: br.s IL_0083 - - IL_0060: nop - IL_0061: ldstr "default" - IL_0066: call void [mscorlib]System.Console::WriteLine(string) - IL_006b: nop - IL_006c: ldstr "more code" - IL_0071: call void [mscorlib]System.Console::WriteLine(string) - IL_0076: nop - IL_0077: br.s IL_0083 - - IL_0079: ldarg.0 - IL_007a: ldc.i4.1 - IL_007b: add - IL_007c: starg.s i - IL_007e: nop - IL_007f: ldc.i4.1 - IL_0080: stloc.1 - IL_0081: br.s IL_0019 - - IL_0083: ret + 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 133 (0x85) + // Code size 128 (0x80) .maxstack 2 .locals init (int32 V_0) IL_0000: nop @@ -1306,45 +1367,40 @@ IL_001b: sub IL_001c: switch ( IL_0033, - IL_0041, - IL_004f, - IL_005d) - IL_0031: br.s IL_006b - - IL_0033: nop - IL_0034: ldstr "one" - IL_0039: call void [mscorlib]System.Console::WriteLine(string) - IL_003e: nop - IL_003f: br.s IL_006b - - IL_0041: nop - IL_0042: ldstr "two" - IL_0047: call void [mscorlib]System.Console::WriteLine(string) - IL_004c: nop - IL_004d: br.s IL_004f - - IL_004f: nop - IL_0050: ldstr "three" - IL_0055: call void [mscorlib]System.Console::WriteLine(string) - IL_005a: nop - IL_005b: br.s IL_0079 + IL_0040, + IL_004d, + IL_005a) + IL_0031: br.s IL_0067 - IL_005d: nop - IL_005e: ldstr "four" - IL_0063: call void [mscorlib]System.Console::WriteLine(string) - IL_0068: nop - IL_0069: br.s IL_0084 - - IL_006b: nop - IL_006c: ldstr "default" - IL_0071: call void [mscorlib]System.Console::WriteLine(string) - IL_0076: nop - IL_0077: br.s IL_0079 - - IL_0079: ldstr "End of method" - IL_007e: call void [mscorlib]System.Console::WriteLine(string) - IL_0083: nop - IL_0084: ret + 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[] @@ -1365,7 +1421,7 @@ .method public hidebysig static void SwitchOnStringInForLoop() cil managed { - // Code size 261 (0x105) + // 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, @@ -1383,7 +1439,7 @@ IL_0012: stloc.2 IL_0013: ldc.i4.0 IL_0014: stloc.3 - IL_0015: br IL_00f5 + IL_0015: br IL_00ef IL_001a: nop IL_001b: ldloc.2 @@ -1404,112 +1460,106 @@ IL_003e: ldstr "Name2" IL_0043: call bool [mscorlib]System.String::op_Equality(string, string) - IL_0048: brtrue.s IL_0099 + 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_00ae + 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_00c3 + 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_00d8 + 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_00d8 - - IL_0082: br.s IL_00e4 - - IL_0084: nop - IL_0085: ldloc.s V_4 - IL_0087: ldc.i4.1 - IL_0088: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) - IL_008d: nop - IL_008e: ldloc.0 - IL_008f: ldloc.s V_4 - IL_0091: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) - IL_0096: nop - IL_0097: br.s IL_00f0 - - IL_0099: nop - IL_009a: ldloc.s V_4 - IL_009c: ldc.i4.2 - IL_009d: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) - IL_00a2: nop - IL_00a3: ldloc.0 - IL_00a4: ldloc.s V_4 - IL_00a6: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) - IL_00ab: nop - IL_00ac: br.s IL_00f0 - - IL_00ae: nop - IL_00af: ldloc.s V_4 - IL_00b1: ldc.i4.3 - IL_00b2: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) - IL_00b7: nop - IL_00b8: ldloc.0 - IL_00b9: ldloc.s V_4 - IL_00bb: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) - IL_00c0: nop - IL_00c1: br.s IL_00f0 - - IL_00c3: nop - IL_00c4: ldloc.s V_4 - IL_00c6: ldc.i4.4 - IL_00c7: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.Switch/SetProperty::set_Set(int32) - IL_00cc: nop - IL_00cd: ldloc.0 - IL_00ce: ldloc.s V_4 - IL_00d0: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) - IL_00d5: nop - IL_00d6: br.s IL_00f0 - - IL_00d8: nop - IL_00d9: ldloc.0 - IL_00da: ldloc.s V_4 - IL_00dc: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) - IL_00e1: nop - IL_00e2: br.s IL_00f0 - - IL_00e4: nop - IL_00e5: ldloc.1 - IL_00e6: ldloc.s V_4 - IL_00e8: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) - IL_00ed: nop - IL_00ee: br.s IL_00f0 - - IL_00f0: nop - IL_00f1: ldloc.3 - IL_00f2: ldc.i4.1 - IL_00f3: add - IL_00f4: stloc.3 - IL_00f5: ldloc.3 - IL_00f6: ldloc.2 - IL_00f7: ldlen - IL_00f8: conv.i4 - IL_00f9: clt - IL_00fb: stloc.s V_6 - IL_00fd: ldloc.s V_6 - IL_00ff: brtrue IL_001a - - IL_0104: ret + 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 138 (0x8a) + // Code size 134 (0x86) .maxstack 2 .locals init (string V_0) IL_0000: nop @@ -1534,55 +1584,51 @@ IL_001e: ldstr "b" IL_0023: call bool [mscorlib]System.String::op_Equality(string, string) - IL_0028: brtrue.s IL_0054 + 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_0062 + 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_0070 + IL_0042: brtrue.s IL_006d - IL_0044: br.s IL_007e + IL_0044: br.s IL_007a - IL_0046: nop - IL_0047: ldstr "a" - IL_004c: call void [mscorlib]System.Console::WriteLine(string) - IL_0051: nop - IL_0052: br.s IL_007e + IL_0046: ldstr "a" + IL_004b: call void [mscorlib]System.Console::WriteLine(string) + IL_0050: nop + IL_0051: br.s IL_007a - IL_0054: nop - IL_0055: ldstr "b" - IL_005a: call void [mscorlib]System.Console::WriteLine(string) - IL_005f: nop - IL_0060: br.s IL_007e - - IL_0062: nop - IL_0063: ldstr "c" - IL_0068: call void [mscorlib]System.Console::WriteLine(string) - IL_006d: nop - IL_006e: br.s IL_007e - - IL_0070: nop - IL_0071: ldstr "d" - IL_0076: call void [mscorlib]System.Console::WriteLine(string) - IL_007b: nop - IL_007c: br.s IL_007e - - IL_007e: ldstr "end" - IL_0083: call void [mscorlib]System.Console::WriteLine(string) - IL_0088: nop - IL_0089: ret + 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 127 (0x7f) + // Code size 123 (0x7b) .maxstack 2 .locals init (string V_0) IL_0000: nop @@ -1600,50 +1646,46 @@ IL_0013: ldstr "b" IL_0018: call bool [mscorlib]System.String::op_Equality(string, string) - IL_001d: brtrue.s IL_0049 + 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_0057 + 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_0065 + IL_0037: brtrue.s IL_0062 - IL_0039: br.s IL_0073 + IL_0039: br.s IL_006f - IL_003b: nop - IL_003c: ldstr "a" - IL_0041: call void [mscorlib]System.Console::WriteLine(string) - IL_0046: nop - IL_0047: br.s IL_0073 + IL_003b: ldstr "a" + IL_0040: call void [mscorlib]System.Console::WriteLine(string) + IL_0045: nop + IL_0046: br.s IL_006f - IL_0049: nop - IL_004a: ldstr "b" - IL_004f: call void [mscorlib]System.Console::WriteLine(string) - IL_0054: nop - IL_0055: br.s IL_0073 + IL_0048: ldstr "b" + IL_004d: call void [mscorlib]System.Console::WriteLine(string) + IL_0052: nop + IL_0053: br.s IL_006f - IL_0057: nop - IL_0058: ldstr "c" - IL_005d: call void [mscorlib]System.Console::WriteLine(string) - IL_0062: nop - IL_0063: br.s IL_0073 - - IL_0065: nop - IL_0066: ldstr "d" - IL_006b: call void [mscorlib]System.Console::WriteLine(string) - IL_0070: nop - IL_0071: br.s IL_0073 + IL_0055: ldstr "c" + IL_005a: call void [mscorlib]System.Console::WriteLine(string) + IL_005f: nop + IL_0060: br.s IL_006f - IL_0073: ldstr "end" - IL_0078: call void [mscorlib]System.Console::WriteLine(string) - IL_007d: nop - IL_007e: ret + 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 From 6272c21ece4c61233644adbe09a98673dc3b481b Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 13 Oct 2017 14:57:43 +0200 Subject: [PATCH 58/65] Simplify SwitchOnNullableTransform. --- .../IL/Transforms/NullableLiftingTransform.cs | 41 +++++++++----- .../Transforms/SwitchOnNullableTransform.cs | 53 ++++++------------- .../IL/Transforms/UsingTransform.cs | 2 +- 3 files changed, 45 insertions(+), 51 deletions(-) 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 index 6e01731fc..ed538ca8b 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnNullableTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnNullableTransform.cs @@ -92,15 +92,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!condition.MatchLogicNot(out var getHasValue)) return false; - if (!(getValueOrDefault is Call getValueOrDefaultCall) || getValueOrDefaultCall.Method.FullName != "System.Nullable.GetValueOrDefault" || - getValueOrDefaultCall.Method.DeclaringType.TypeParameterCount != 1) + if (!NullableLiftingTransform.MatchGetValueOrDefault(getValueOrDefault, out ILInstruction getValueOrDefaultArg)) return false; - if (!(getHasValue is Call getHasValueCall) || !getHasValueCall.Method.IsAccessor || getHasValueCall.Method.FullName != "System.Nullable.get_HasValue" || - getHasValueCall.Method.DeclaringType.TypeParameterCount != 1) + if (!NullableLiftingTransform.MatchHasValueCall(getHasValue, out ILInstruction getHasValueArg)) return false; - if (getHasValueCall.Arguments.Count != 1 || getValueOrDefaultCall.Arguments.Count != 1) - return false; - if (!getHasValueCall.Arguments[0].MatchLdLoc(tmp) || !getValueOrDefaultCall.Arguments[0].MatchLdLoc(tmp)) + if (!(getHasValueArg.MatchLdLoc(tmp) && getValueOrDefaultArg.MatchLdLoc(tmp))) return false; // match second block: switchBlock // switch (ldloc switchVariable) { @@ -112,22 +108,19 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!(switchBlock.Instructions[0] is SwitchInstruction switchInst)) return false; - newSwitch = new SwitchInstruction(new LdLoc(switchValueVar)); - newSwitch.IsLifted = true; - SwitchSection defaultSection = null; - foreach (var section in switchInst.Sections) { - if (defaultSection == null || section.Labels.Count() >= defaultSection.Labels.Count()) - defaultSection = section; - newSwitch.Sections.Add(section); - } - if (defaultSection.Body.MatchBranch(out var defaultBlock) && defaultBlock == nullCaseBlock) - defaultSection.HasNullLabel = true; - else { - newSwitch.Sections.Add(new SwitchSection { Body = new Branch(nullCaseBlock), HasNullLabel = true }); - } + 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. /// @@ -142,11 +135,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms 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) + 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 var target1) || target1 != tmp) + 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)) @@ -161,23 +154,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!switchVar.IsSingleDefinition || switchVar.LoadCount != 1) return false; - if (!NullableLiftingTransform.MatchGetValueOrDefault(getValueOrDefault, out var target2) || target2 != tmp) + if (!NullableLiftingTransform.MatchGetValueOrDefault(getValueOrDefault, tmp)) return false; if (!(switchBlock.Instructions[1] is SwitchInstruction switchInst)) return false; - newSwitch = new SwitchInstruction(switchValue); - newSwitch.IsLifted = true; - SwitchSection defaultSection = null; - foreach (var section in switchInst.Sections) { - if (defaultSection == null || section.Labels.Count() >= defaultSection.Labels.Count()) - defaultSection = section; - newSwitch.Sections.Add(section); - } - if (defaultSection.Body.MatchBranch(out var defaultBlock) && defaultBlock == nullCaseBlock) - defaultSection.HasNullLabel = true; - else { - newSwitch.Sections.Add(new SwitchSection { Body = new Branch(nullCaseBlock), HasNullLabel = true }); - } + newSwitch = BuildLiftedSwitch(nullCaseBlock, switchInst, switchValue); return true; } } 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; From f988c112298ff9999e31784f09f5f6ee91579144 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 13 Oct 2017 15:35:29 +0200 Subject: [PATCH 59/65] Refactor SwitchOnStringTransform.Run --- .../IL/Transforms/SwitchOnStringTransform.cs | 107 +++++++++--------- 1 file changed, 52 insertions(+), 55 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs index 2ff02e41f..ddc9ab01d 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs @@ -40,52 +40,19 @@ namespace ICSharpCode.Decompiler.IL.Transforms foreach (var block in function.Descendants.OfType()) { bool changed = false; for (int i = block.Instructions.Count - 1; i >= 0; i--) { - SwitchInstruction newSwitch; - if (SimplifyCascadingIfStatements(block.Instructions, i, out newSwitch, out var extraLoad, out var keepAssignmentBefore)) { - if (extraLoad) { - block.Instructions[i - 2].ReplaceWith(newSwitch); - block.Instructions.RemoveRange(i - 1, 3); - i -= 2; - } else { - if (keepAssignmentBefore) { - block.Instructions[i].ReplaceWith(newSwitch); - block.Instructions.RemoveAt(i + 1); - } else { - block.Instructions[i - 1].ReplaceWith(newSwitch); - block.Instructions.RemoveRange(i, 2); - i--; - } - } + if (SimplifyCascadingIfStatements(block.Instructions, ref i)) { changed = true; continue; } - if (MatchLegacySwitchOnStringWithHashtable(block.Instructions, i, out newSwitch)) { - block.Instructions[i + 1].ReplaceWith(newSwitch); - block.Instructions.RemoveAt(i); + if (MatchLegacySwitchOnStringWithHashtable(block.Instructions, ref i)) { changed = true; continue; } - if (MatchLegacySwitchOnStringWithDict(block.Instructions, i, out newSwitch, out keepAssignmentBefore)) { - block.Instructions[i + 1].ReplaceWith(newSwitch); - if (keepAssignmentBefore) { - block.Instructions.RemoveAt(i); - i--; - } else { - block.Instructions.RemoveRange(i - 1, 2); - i -= 2; - } + if (MatchLegacySwitchOnStringWithDict(block.Instructions, ref i)) { changed = true; continue; } - if (MatchRoslynSwitchOnString(block.Instructions, i, out newSwitch, out keepAssignmentBefore)) { - block.Instructions[i].ReplaceWith(newSwitch); - if (keepAssignmentBefore) { - block.Instructions.RemoveAt(i - 1); - i--; - } else { - block.Instructions.RemoveRange(i - 2, 2); - i -= 2; - } + if (MatchRoslynSwitchOnString(block.Instructions, ref i)) { changed = true; continue; } @@ -100,11 +67,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms container.SortBlocks(deleteUnreachableBlocks: true); } - bool SimplifyCascadingIfStatements(InstructionCollection instructions, int i, out SwitchInstruction inst, out bool extraLoad, out bool keepAssignmentBefore) + bool SimplifyCascadingIfStatements(InstructionCollection instructions, ref int i) { - inst = null; - extraLoad = false; - keepAssignmentBefore = false; 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 @@ -116,6 +80,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; List<(string, Block)> values = new List<(string, Block)>(); ILInstruction switchValue = null; + bool extraLoad = false; + bool keepAssignmentBefore = false; + // Roslyn: match call to operator ==(string, string) if (MatchStringEqualityComparison(condition, out var switchValueVar, out string value)) { values.Add((value, firstBlock)); @@ -165,8 +132,22 @@ namespace ICSharpCode.Decompiler.IL.Transforms 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)); - inst = new SwitchInstruction(stringToInt); + 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; } @@ -234,10 +215,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// /// Matches the C# 2.0 switch-on-string pattern, which uses Dictionary<string, int>. /// - bool MatchLegacySwitchOnStringWithDict(InstructionCollection instructions, int i, out SwitchInstruction inst, out bool keepAssignmentBefore) + bool MatchLegacySwitchOnStringWithDict(InstructionCollection instructions, ref int i) { - inst = null; - keepAssignmentBefore = false; if (i < 1) return false; // match first block: checking switch-value for null if (!(instructions[i].MatchIfInstruction(out var condition, out var exitBlockJump) && @@ -299,13 +278,22 @@ namespace ICSharpCode.Decompiler.IL.Transforms stringValues.Add(null); sections.Add(new SwitchSection() { Labels = label, Body = new Branch(nullValueCaseBlock) }); } + bool keepAssignmentBefore = false; if (switchValueVar.LoadCount > 2) { switchValue = new LdLoc(switchValueVar); keepAssignmentBefore = true; } var stringToInt = new StringToInt(switchValue, stringValues.ToArray()); - inst = new SwitchInstruction(stringToInt); + var inst = new SwitchInstruction(stringToInt); inst.Sections.AddRange(sections); + instructions[i + 1].ReplaceWith(inst); + if (keepAssignmentBefore) { + instructions.RemoveAt(i); + i--; + } else { + instructions.RemoveRange(i - 1, 2); + i -= 2; + } return true; } @@ -365,9 +353,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; } - bool MatchLegacySwitchOnStringWithHashtable(InstructionCollection instructions, int i, out SwitchInstruction inst) + bool MatchLegacySwitchOnStringWithHashtable(InstructionCollection instructions, ref int i) { - inst = null; // match first block: checking compiler-generated Hashtable for null // if (comp(volatile.ldobj System.Collections.Hashtable(ldsflda $$method0x600003f-1) != ldnull)) br switchHeadBlock // br tableInitBlock @@ -453,15 +440,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms sections.Add(new SwitchSection() { Labels = label, Body = new Branch(nullCaseBlock) }); } var stringToInt = new StringToInt(switchValue, stringValues.ToArray()); - inst = new SwitchInstruction(stringToInt); + var inst = new SwitchInstruction(stringToInt); inst.Sections.AddRange(sections); + instructions[i + 1].ReplaceWith(inst); + instructions.RemoveAt(i); return true; } - bool MatchRoslynSwitchOnString(InstructionCollection instructions, int i, out SwitchInstruction inst, out bool keepAssignmentBefore) + bool MatchRoslynSwitchOnString(InstructionCollection instructions, ref int i) { - inst = null; - keepAssignmentBefore = false; if (i < 1) return false; if (!(instructions[i] is SwitchInstruction switchInst && switchInst.Value.MatchLdLoc(out var targetVar) && MatchComputeStringHashCall(instructions[i - 1], targetVar, out var switchValue))) @@ -499,17 +486,27 @@ namespace ICSharpCode.Decompiler.IL.Transforms stringValues.Add((index++, stringValue, body)); } ILInstruction switchValueInst = switchValue; + bool keepAssignmentBefore; if (i > 1 && instructions[i - 2].MatchStLoc(switchValue.Variable, out var switchValueTmp) && switchValue.Variable.IsSingleDefinition && switchValue.Variable.LoadCount == switchInst.Sections.Count) { switchValueInst = switchValueTmp; + keepAssignmentBefore = false; } else { keepAssignmentBefore = true; } var defaultLabel = new LongSet(new LongInterval(0, index)).Invert(); var value = new StringToInt(switchValueInst, stringValues.Select(item => item.Item2).ToArray()); - inst = new SwitchInstruction(value); - inst.Sections.AddRange(stringValues.Select(section => new SwitchSection { Labels = new Util.LongSet(section.Item1), Body = new Branch(section.Item3) })); - inst.Sections.Add(new SwitchSection { Labels = defaultLabel, Body = defaultBranch }); + var newSwitch = new SwitchInstruction(value); + 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 = defaultBranch }); + instructions[i].ReplaceWith(newSwitch); + if (keepAssignmentBefore) { + instructions.RemoveAt(i - 1); + i--; + } else { + instructions.RemoveRange(i - 2, 2); + i -= 2; + } return true; } From 9fcee831d25af86e36bf2f741415ef884d50d428 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 13 Oct 2017 16:04:24 +0200 Subject: [PATCH 60/65] Simplify SimplifyCascadingIfStatements --- .../IL/Instructions/PatternMatching.cs | 21 ++++ .../IL/Transforms/SwitchOnStringTransform.cs | 109 +++++++----------- 2 files changed, 63 insertions(+), 67 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs b/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs index 26b33c0f6..0510b8c87 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs @@ -321,6 +321,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/Transforms/SwitchOnStringTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs index ddc9ab01d..c37b8fe15 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs @@ -80,44 +80,40 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; List<(string, Block)> values = new List<(string, Block)>(); ILInstruction switchValue = null; - bool extraLoad = false; - bool keepAssignmentBefore = false; - // Roslyn: match call to operator ==(string, string) - if (MatchStringEqualityComparison(condition, out var switchValueVar, out string value)) { - values.Add((value, firstBlock)); - if(!instructions[i - 1].MatchStLoc(switchValueVar, out switchValue)) { - switchValue = new LdLoc(switchValueVar); - } - } else { - // match null check with different variable: - // this is used by the old C# compiler. - if (MatchCompEqualsNull(condition, out var otherSwitchValueVar)) { - values.Add((null, firstBlock)); - } else { - return false; - } + // 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 (comp(ldloc otherSwitchValueVar == ldnull)) br nullCase - if (i > 1 && otherSwitchValueVar != null && instructions[i - 2].MatchStLoc(otherSwitchValueVar, out switchValue) - && instructions[i - 1].MatchStLoc(out switchValueVar, out var switchValueCopyInst) - && switchValueCopyInst.MatchLdLoc(otherSwitchValueVar) && otherSwitchValueVar.IsSingleDefinition && otherSwitchValueVar.LoadCount == 2) { + // 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 if (instructions[i - 1].MatchStLoc(out switchValueVar, out switchValue)) { - // unoptimized legacy switch + } 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 value, out Block block)) != null) { + while ((nextCaseBlock = MatchCaseBlock(currentCaseBlock, switchValueVar, out string value, out Block block)) != null) { values.Add((value, block)); currentCaseBlock = nextCaseBlock; } @@ -125,6 +121,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms 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); @@ -187,31 +184,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (!currentBlock.Instructions[1].MatchBranch(out nextBlock)) return null; } - if (!MatchStringEqualityComparison(condition, out var v, out value)) { - if (MatchCompEqualsNull(condition, out v)) { - value = null; - } else { + if (!MatchStringEqualityComparison(condition, switchVariable, out value)) { return null; } - } - if (v != switchVariable) - return null; return nextBlock; } - - /// - /// Returns true if is only assigned once and the initialization is done by copying . - /// - bool IsInitializedBy(ILVariable left, ILVariable right) - { - if (!left.IsSingleDefinition) - return false; - var storeInst = left.StoreInstructions.OfType().SingleOrDefault(); - if (storeInst == null) - return false; - return storeInst.Value.MatchLdLoc(right); - } - + /// /// Matches the C# 2.0 switch-on-string pattern, which uses Dictionary<string, int>. /// @@ -475,8 +453,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!bodyBranch.MatchBranch(out Block body)) return false; - if (!MatchStringEqualityOrNullComparison(condition, switchValue.Variable, out string stringValue)) { - if (condition.MatchLogicNot(out condition) && MatchStringEqualityOrNullComparison(condition, switchValue.Variable, out stringValue)) { + if (!MatchStringEqualityComparison(condition, switchValue.Variable, out string stringValue)) { + if (condition.MatchLogicNot(out condition) && MatchStringEqualityComparison(condition, switchValue.Variable, out stringValue)) { if (!target.Instructions[1].MatchBranch(out Block exit)) return false; body = exit; @@ -523,39 +501,36 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; } - bool MatchStringEqualityOrNullComparison(ILInstruction condition, ILVariable variable, out string stringValue) + /// + /// Matches 'call string.op_Equality(ldloc(variable), ldstr(stringValue))' + /// or 'comp(ldloc(variable) == ldnull)' + /// + bool MatchStringEqualityComparison(ILInstruction condition, ILVariable variable, out string stringValue) { - if (!MatchStringEqualityComparison(condition, out var v, out stringValue)) { - if (!MatchCompEqualsNull(condition, out v)) - return false; - stringValue = null; + return MatchStringEqualityComparison(condition, out var v, out stringValue) && v == variable; } - return 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.Arguments.Count == 2) { + 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]; - if (!right.MatchLdStr(out stringValue)) - return false; + 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; } - return left.MatchLdLoc(out variable); } - - bool MatchCompEqualsNull(ILInstruction condition, out ILVariable variable) - { - variable = null; - if (!condition.MatchCompEquals(out var left, out var right)) - return false; - return right.MatchLdNull() && left.MatchLdLoc(out variable); } } -} From b94088fbc92c78ad32233a709b8bafe9215f73c0 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 13 Oct 2017 17:12:34 +0200 Subject: [PATCH 61/65] Add MaxBy, MaxOrDefault, MinBy extension methods --- .../Util/CollectionExtensions.cs | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) 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; + } + } } } From e6608832a4b4fdec6c5886727aa7c2fbc943b485 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 13 Oct 2017 18:03:53 +0200 Subject: [PATCH 62/65] Add documentation to SwitchOnStringTransform --- .../IL/Transforms/SwitchOnStringTransform.cs | 127 ++++++++++++------ 1 file changed, 87 insertions(+), 40 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs index c37b8fe15..1f7d0f232 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs @@ -185,11 +185,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms return null; } if (!MatchStringEqualityComparison(condition, switchVariable, out value)) { - return null; - } + return null; + } return nextBlock; } - + /// /// Matches the C# 2.0 switch-on-string pattern, which uses Dictionary<string, int>. /// @@ -197,6 +197,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms { 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; @@ -210,6 +213,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms 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; @@ -221,11 +226,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms 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)) @@ -240,6 +252,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms 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))) @@ -275,6 +292,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; } + /// + /// Matches 'volatile.ldobj dictionaryType(ldsflda dictField)' + /// bool MatchDictionaryFieldLoad(ILInstruction inst, Func typeMatcher, out IField dictField, out IType dictionaryType) { dictField = null; @@ -285,6 +305,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms (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; @@ -304,12 +327,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms return block.Instructions[i + 2].MatchBranch(targetBlock); } - bool MatchAddCall(ILInstruction inst, ILVariable dictVar, int i, out string value) + /// + /// call Add(ldloc dictVar, ldstr value, ldc.i4 index) + /// -or- + /// call Add(ldloc dictVar, ldstr value, box System.Int32(ldc.i4 index)) + /// + bool MatchAddCall(ILInstruction inst, ILVariable dictVar, int index, out string value) { value = null; return inst is Call c && c.Method.Name == "Add" && c.Arguments.Count == 3 && c.Arguments[0].MatchLdLoc(dictVar) && c.Arguments[1].MatchLdStr(out value) && - (c.Arguments[2].MatchLdcI4(i) || (c.Arguments[2].MatchBox(out var arg, out _) && arg.MatchLdcI4(i))); + (c.Arguments[2].MatchLdcI4(index) || (c.Arguments[2].MatchBox(out var arg, out _) && arg.MatchLdcI4(index))); } bool IsStringToIntDictionary(IType dictionaryType) @@ -428,55 +456,45 @@ namespace ICSharpCode.Decompiler.IL.Transforms bool MatchRoslynSwitchOnString(InstructionCollection instructions, ref int i) { if (i < 1) return false; - if (!(instructions[i] is SwitchInstruction switchInst && switchInst.Value.MatchLdLoc(out var targetVar) && - MatchComputeStringHashCall(instructions[i - 1], targetVar, out var switchValue))) + // 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; - ILInstruction defaultBranch = null; + SwitchSection defaultSection = switchInst.Sections.MaxBy(s => s.Labels.Count()); foreach (var section in switchInst.Sections) { - if (!section.Body.MatchBranch(out Block target)) { - if (section.Body is Leave leave) { - defaultBranch = leave; - continue; - } + if (section == defaultSection) continue; + // extract target block + if (!section.Body.MatchBranch(out Block target)) return false; - } - if (target.IncomingEdgeCount > 1) { - defaultBranch = new Branch(target); - continue; - } - if (target.Instructions.Count != 2) - return false; - if (!target.Instructions[0].MatchIfInstruction(out var condition, out var bodyBranch)) - return false; - if (!bodyBranch.MatchBranch(out Block body)) + if (!MatchRoslynCaseBlockHead(target, switchValueLoad.Variable, out Block body, out string stringValue)) return false; - if (!MatchStringEqualityComparison(condition, switchValue.Variable, out string stringValue)) { - if (condition.MatchLogicNot(out condition) && MatchStringEqualityComparison(condition, switchValue.Variable, out stringValue)) { - if (!target.Instructions[1].MatchBranch(out Block exit)) - return false; - body = exit; - } else - return false; - } stringValues.Add((index++, stringValue, body)); } - ILInstruction switchValueInst = switchValue; + ILInstruction switchValueInst = switchValueLoad; + // stloc switchValueLoadVariable(switchValue) + // stloc switchValueVar(call ComputeStringHash(ldloc V_0)) + // switch (ldloc switchValueVar) { bool keepAssignmentBefore; - if (i > 1 && instructions[i - 2].MatchStLoc(switchValue.Variable, out var switchValueTmp) && - switchValue.Variable.IsSingleDefinition && switchValue.Variable.LoadCount == switchInst.Sections.Count) { + // 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 value = new StringToInt(switchValueInst, stringValues.Select(item => item.Item2).ToArray()); - var newSwitch = new SwitchInstruction(value); + 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 = defaultBranch }); + newSwitch.Sections.Add(new SwitchSection { Labels = defaultLabel, Body = defaultSection.Body }); instructions[i].ReplaceWith(newSwitch); if (keepAssignmentBefore) { instructions.RemoveAt(i - 1); @@ -488,6 +506,35 @@ namespace ICSharpCode.Decompiler.IL.Transforms 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)) { + if (condition.MatchLogicNot(out condition) && MatchStringEqualityComparison(condition, switchValueVar, out stringValue)) { + if (!target.Instructions[1].MatchBranch(out Block exit)) + return false; + body = exit; + } else + return false; + } + return body != null; + } + + /// + /// Matches 'stloc(targetVar, call ComputeStringHash(ldloc(switchValue)))' + /// bool MatchComputeStringHashCall(ILInstruction inst, ILVariable targetVar, out LdLoc switchValue) { switchValue = null; @@ -508,8 +555,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms 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)' @@ -532,5 +579,5 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; } } - } } +} From a423e7da9ccd2b513dfc1c8eedd3cc343e05301b Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 13 Oct 2017 19:41:31 +0200 Subject: [PATCH 63/65] Expand checks in ExtractStringValuesFromInitBlock and MatchAddCall --- .../IL/Instructions/PatternMatching.cs | 5 +++ .../IL/Transforms/SwitchOnStringTransform.cs | 37 +++++++++++++++---- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs b/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs index 0510b8c87..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. /// diff --git a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs index 1f7d0f232..670e870c8 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs @@ -207,7 +207,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!exitBlockJump.MatchBranch(out var nullValueCaseBlock)) return false; - if (!(condition.MatchCompEquals(out var left, out var right) && right.MatchLdNull() && (left.Match(switchValue).Success || left.MatchLdLoc(switchValueVar)))) + 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) @@ -311,15 +312,31 @@ namespace ICSharpCode.Decompiler.IL.Transforms bool ExtractStringValuesFromInitBlock(Block block, out List values, Block targetBlock, IType dictionaryType, IField dictionaryField) { values = null; - if (!(block.Instructions[0].MatchStLoc(out var dictVar, out var newObjDict) && - newObjDict is NewObj newObj && newObj.Arguments.Count >= 1 && newObj.Arguments[0].MatchLdcI4(out var valuesLength))) + // 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 (i + 1 < block.Instructions.Count - 2 && MatchAddCall(block.Instructions[i + 1], dictVar, i, out var value)) { + 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)) @@ -332,12 +349,16 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// -or- /// call Add(ldloc dictVar, ldstr value, box System.Int32(ldc.i4 index)) /// - bool MatchAddCall(ILInstruction inst, ILVariable dictVar, int index, out string value) + bool MatchAddCall(IType dictionaryType, ILInstruction inst, ILVariable dictVar, int index, out string value) { value = null; - return inst is Call c && c.Method.Name == "Add" && c.Arguments.Count == 3 && - c.Arguments[0].MatchLdLoc(dictVar) && c.Arguments[1].MatchLdStr(out value) && - (c.Arguments[2].MatchLdcI4(index) || (c.Arguments[2].MatchBox(out var arg, out _) && arg.MatchLdcI4(index))); + 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) From 1ac631b3f1442ccca667da228466d84b4d2cb228 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 13 Oct 2017 20:04:20 +0200 Subject: [PATCH 64/65] SwitchOnStringTransform: fix bugs in ExtractStringValuesFromInitBlock and MatchAddCall --- .../IL/Transforms/SwitchOnStringTransform.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs index 670e870c8..278520689 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs @@ -338,8 +338,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms // 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)) + dictType.Equals(dictionaryType) && loadField.MatchLdsFlda(out var dictField) && dictField.Equals(dictionaryField) && + dictVarLoad.MatchLdLoc(dictVar))) return false; return block.Instructions[i + 2].MatchBranch(targetBlock); } @@ -356,7 +356,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms 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)) + 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))); } From 2824906f8fb6f74f918a87ba9d70ebae981daf6a Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 13 Oct 2017 20:07:13 +0200 Subject: [PATCH 65/65] Code cleanup SwitchOnStringTransform. --- .../IL/Transforms/SwitchOnStringTransform.cs | 58 +++++++++++-------- 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs index 278520689..7dffaf2b4 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs @@ -265,14 +265,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms var sections = new List(switchInst.Sections); // switch contains case null: if (nullValueCaseBlock != defaultBlock) { - var label = new Util.LongSet(switchInst.Sections.Count); - var possibleConflicts = switchInst.Sections.Where(sec => sec.Labels.Overlaps(label)).ToArray(); - if (possibleConflicts.Length > 1) + if (!AddNullSection(sections, stringValues, nullValueCaseBlock)) { return false; - else if (possibleConflicts.Length == 1) - possibleConflicts[0].Labels = possibleConflicts[0].Labels.ExceptWith(label); - stringValues.Add(null); - sections.Add(new SwitchSection() { Labels = label, Body = new Branch(nullValueCaseBlock) }); + } } bool keepAssignmentBefore = false; if (switchValueVar.LoadCount > 2) { @@ -284,15 +279,33 @@ namespace ICSharpCode.Decompiler.IL.Transforms 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)' /// @@ -457,14 +470,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms var sections = new List(switchInst.Sections); // switch contains case null: if (nullCaseBlock != defaultBlock) { - var label = new Util.LongSet(switchInst.Sections.Count); - var possibleConflicts = switchInst.Sections.Where(sec => sec.Labels.Overlaps(label)).ToArray(); - if (possibleConflicts.Length > 1) + if (!AddNullSection(sections, stringValues, nullCaseBlock)) { return false; - else if (possibleConflicts.Length == 1) - possibleConflicts[0].Labels = possibleConflicts[0].Labels.ExceptWith(label); - stringValues.Add(null); - sections.Add(new SwitchSection() { Labels = label, Body = new Branch(nullCaseBlock) }); + } } var stringToInt = new StringToInt(switchValue, stringValues.ToArray()); var inst = new SwitchInstruction(stringToInt); @@ -501,12 +509,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms } ILInstruction switchValueInst = switchValueLoad; // stloc switchValueLoadVariable(switchValue) - // stloc switchValueVar(call ComputeStringHash(ldloc V_0)) + // 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) { + switchValueLoad.Variable.IsSingleDefinition && switchValueLoad.Variable.LoadCount == switchInst.Sections.Count) + { switchValueInst = switchValueTmp; keepAssignmentBefore = false; } else { @@ -542,19 +551,20 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!bodyBranch.MatchBranch(out body)) return false; - if (!MatchStringEqualityComparison(condition, switchValueVar, out stringValue)) { - if (condition.MatchLogicNot(out condition) && MatchStringEqualityComparison(condition, switchValueVar, out stringValue)) { - if (!target.Instructions[1].MatchBranch(out Block exit)) - return false; - body = exit; - } else + 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; } - return body != null; } /// - /// Matches 'stloc(targetVar, call ComputeStringHash(ldloc(switchValue)))' + /// Matches 'stloc(targetVar, call ComputeStringHash(ldloc switchValue))' /// bool MatchComputeStringHashCall(ILInstruction inst, ILVariable targetVar, out LdLoc switchValue) {