From bbb40ecb32cdaea8eb7dbe5eb6e05e456114fc9d Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 18 Aug 2019 18:38:55 +0200 Subject: [PATCH] Fix #1602: Recognize Roslyn empty string case block. --- .../TestCases/Pretty/Switch.cs | 24 ++++++ .../IL/Transforms/SwitchOnStringTransform.cs | 75 ++++++++++++++++++- 2 files changed, 97 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs index a6f551686..874769b9b 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs @@ -1168,5 +1168,29 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty throw new Exception(); } } + + public static int Issue1602(string x) + { + switch (x) { + case null: + return 0; + case "": + return -1; + case "A": + return 65; + case "B": + return 66; + case "C": + return 67; + case "D": + return 68; + case "E": + return 69; + case "F": + return 70; + default: + throw new ArgumentOutOfRangeException(); + } + } } } \ No newline at end of file diff --git a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs index f1d1f126c..7c8575c20 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs @@ -820,8 +820,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms // extract target block if (!section.Body.MatchBranch(out Block target)) return false; - if (!MatchRoslynCaseBlockHead(target, switchValueLoad.Variable, out ILInstruction targetOrLeave, out Block currentExitBlock, out string stringValue)) + string stringValue; + if (MatchRoslynEmptyStringCaseBlockHead(target, switchValueLoad.Variable, out ILInstruction targetOrLeave, out Block currentExitBlock)) { + stringValue = ""; + } else if (!MatchRoslynCaseBlockHead(target, switchValueLoad.Variable, out targetOrLeave, out currentExitBlock, out stringValue)) { return false; + } + if (exitOrDefaultBlock != null && exitOrDefaultBlock != currentExitBlock) return false; exitOrDefaultBlock = currentExitBlock; @@ -902,7 +907,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// /// Matches (and the negated version): - /// if (call op_Equality(ldloc V_0, ldstr "Fifth case")) br body + /// if (call op_Equality(ldloc switchValueVar, stringValue)) br body /// br exit /// bool MatchRoslynCaseBlockHead(Block target, ILVariable switchValueVar, out ILInstruction bodyOrLeave, out Block defaultOrExitBlock, out string stringValue) @@ -938,6 +943,72 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; } + /// + /// Block target(incoming: 1) { + /// if (comp.o(ldloc switchValueVar == ldnull)) br exit + /// br lengthCheckBlock + /// } + /// + /// Block lengthCheckBlock(incoming: 1) { + /// if (logic.not(call get_Length(ldloc switchValueVar))) br body + /// br exit + /// } + /// + bool MatchRoslynEmptyStringCaseBlockHead(Block target, ILVariable switchValueVar, out ILInstruction bodyOrLeave, out Block defaultOrExitBlock) + { + bodyOrLeave = null; + defaultOrExitBlock = null; + if (target.Instructions.Count != 2 || target.IncomingEdgeCount != 1) + return false; + if (!target.Instructions[0].MatchIfInstruction(out var nullComparisonCondition, out var exitBranch)) + return false; + if (!nullComparisonCondition.MatchCompEqualsNull(out var arg) || !arg.MatchLdLoc(switchValueVar)) + return false; + if (!target.Instructions[1].MatchBranch(out Block lengthCheckBlock)) + return false; + if (lengthCheckBlock.Instructions.Count != 2 || lengthCheckBlock.IncomingEdgeCount != 1) + return false; + if (!lengthCheckBlock.Instructions[0].MatchIfInstruction(out var lengthCheckCondition, out var exitBranch2)) + return false; + ILInstruction bodyBranch; + if (lengthCheckCondition.MatchLogicNot(out arg)) { + bodyBranch = exitBranch2; + exitBranch2 = lengthCheckBlock.Instructions[1]; + lengthCheckCondition = arg; + } else { + bodyBranch = lengthCheckBlock.Instructions[1]; + } + if (!(exitBranch2.MatchBranch(out defaultOrExitBlock) || exitBranch2.MatchLeave(out _))) + return false; + if (!MatchStringLengthCall(lengthCheckCondition, switchValueVar)) + return false; + if (!(exitBranch.MatchBranch(out defaultOrExitBlock) && exitBranch2.MatchBranch(defaultOrExitBlock))) + return false; + if (bodyBranch.MatchLeave(out _)) { + bodyOrLeave = bodyBranch; + return true; + } + if (bodyBranch.MatchBranch(out var bodyBlock)) { + bodyOrLeave = bodyBlock; + return true; + } + return false; + } + + /// + /// call get_Length(ldloc switchValueVar) + /// + bool MatchStringLengthCall(ILInstruction inst, ILVariable switchValueVar) + { + return inst is Call call + && call.Method.DeclaringType.IsKnownType(KnownTypeCode.String) + && call.Method.IsAccessor + && call.Method.AccessorKind == System.Reflection.MethodSemanticsAttributes.Getter + && call.Method.AccessorOwner.Name == "Length" + && call.Arguments.Count == 1 + && call.Arguments[0].MatchLdLoc(switchValueVar); + } + /// /// Matches 'stloc(targetVar, call ComputeStringHash(ldloc switchValue))' ///