Browse Source

Fix #1602: Recognize Roslyn empty string case block.

pull/1652/head
Siegfried Pammer 6 years ago
parent
commit
bbb40ecb32
  1. 24
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs
  2. 75
      ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs

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

@ -1168,5 +1168,29 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -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();
}
}
}
}

75
ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs

@ -820,8 +820,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -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 @@ -902,7 +907,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// <summary>
/// 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
/// </summary>
bool MatchRoslynCaseBlockHead(Block target, ILVariable switchValueVar, out ILInstruction bodyOrLeave, out Block defaultOrExitBlock, out string stringValue)
@ -938,6 +943,72 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -938,6 +943,72 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false;
}
/// <summary>
/// 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
/// }
/// </summary>
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;
}
/// <summary>
/// call get_Length(ldloc switchValueVar)
/// </summary>
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);
}
/// <summary>
/// Matches 'stloc(targetVar, call ComputeStringHash(ldloc switchValue))'
/// </summary>

Loading…
Cancel
Save