Browse Source

Fix some more bugs in SwitchOnStringTransform

pull/887/head
Siegfried Pammer 8 years ago
parent
commit
9719926b6b
  1. 6
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs
  2. 46
      ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs

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

@ -56,7 +56,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
case 10001: { case 10001: {
return "ten thousand and one"; return "ten thousand and one";
} }
case int.MaxValue: { case 2147483647: {
return "int.MaxValue"; return "int.MaxValue";
} }
default: { default: {
@ -107,8 +107,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
case "Sixth case": { case "Sixth case": {
return "Text6"; return "Text6";
} }
case null: { case (string)null: {
return null; return (string)null;
} }
default: { default: {
return "Default"; return "Default";

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

@ -19,7 +19,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using ICSharpCode.Decompiler.IL.ControlFlow;
using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util; using ICSharpCode.Decompiler.Util;
@ -56,6 +56,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
i--; i--;
} }
// Combine cases with the same branch target:
SwitchDetection.SimplifySwitchInstruction(block);
// This happens in some cases: // This happens in some cases:
// Use correct index after transformation. // Use correct index after transformation.
if (i >= block.Instructions.Count) if (i >= block.Instructions.Count)
@ -100,13 +103,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms
values.Add((value, block)); values.Add((value, block));
currentCaseBlock = nextCaseBlock; currentCaseBlock = nextCaseBlock;
} }
BlockContainer container = null; var container = BlockContainer.FindClosestContainer(firstBlock);
if (!ExtractLastJumpFromBlock(currentCaseBlock, out var exitBlock) && !ExtractLastLeaveFromBlock(currentCaseBlock, out container)) if (!ExtractLastJumpFromBlock(currentCaseBlock, out var exitBlock) && !ExtractLastLeaveFromBlock(currentCaseBlock, container))
return false; return false;
if (values.Count == 0) if (values.Count == 0)
return false; return false;
if (!(values.All(b => ExtractLastJumpFromBlock(b.Item2, out var nextExit) && nextExit == exitBlock) || if (!(values.All(b => ExtractLastJumpFromBlock(b.Item2, out var nextExit) && IsExitBlock(nextExit, container)) ||
(exitBlock == null && values.All(b => ExtractLastLeaveFromBlock(b.Item2, out var exitContainer) && exitContainer == container)))) (exitBlock == null && values.All(b => ExtractLastLeaveFromBlock(b.Item2, container)))))
return false; return false;
if (currentCaseBlock.IncomingEdgeCount == (isLegacy ? 2 : 1)) { if (currentCaseBlock.IncomingEdgeCount == (isLegacy ? 2 : 1)) {
var sections = new List<SwitchSection>(values.SelectWithIndex((index, b) => new SwitchSection { Labels = new LongSet(index), Body = new Branch(b.Item2) })); var sections = new List<SwitchSection>(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; 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) bool ExtractLastJumpFromBlock(Block block, out Block exitBlock)
{ {
exitBlock = null; exitBlock = null;
@ -128,13 +138,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true; return true;
} }
bool ExtractLastLeaveFromBlock(Block block, out BlockContainer exitBlock) bool ExtractLastLeaveFromBlock(Block block, BlockContainer container)
{ {
exitBlock = null;
var lastInst = block.Instructions.LastOrDefault(); 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 false;
return true; return b == container;
} }
Block MatchCaseBlock(Block currentBlock, ref ILVariable switchVariable, out string value, out Block caseBlock) 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) if (!switchVariable.IsSingleDefinition)
return false; return false;
return switchVariable.StoreInstructions.OfType<StLoc>().Single().Value.MatchLdLoc(newSwitchVariable); var storeInst = switchVariable.StoreInstructions.OfType<StLoc>().SingleOrDefault();
if (storeInst == null)
return false;
return storeInst.Value.MatchLdLoc(newSwitchVariable);
} }
bool MatchLegacySwitchOnString(InstructionCollection<ILInstruction> instructions, int i, out SwitchInstruction inst, out Block blockAfterSwitch) bool MatchLegacySwitchOnString(InstructionCollection<ILInstruction> instructions, int i, out SwitchInstruction inst, out Block blockAfterSwitch)
@ -236,7 +248,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
stringValues.Add(null); stringValues.Add(null);
sections.Add(new SwitchSection() { Labels = new Util.LongSet(stringValues.Count - 1), Body = new Branch(exitBlock) }); 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 = new SwitchInstruction(stringToInt);
inst.DefaultBody = switchInst.DefaultBody; inst.DefaultBody = switchInst.DefaultBody;
inst.Sections.AddRange(sections); inst.Sections.AddRange(sections);
@ -299,14 +311,22 @@ namespace ICSharpCode.Decompiler.IL.Transforms
foreach (var section in switchInst.Sections) { foreach (var section in switchInst.Sections) {
if (!section.Body.MatchBranch(out Block target)) if (!section.Body.MatchBranch(out Block target))
return false; return false;
if (target.IncomingEdgeCount != 1 || target.Instructions.Count == 0) if (target.IncomingEdgeCount != 1 || target.Instructions.Count != 2)
return false; return false;
if (!target.Instructions[0].MatchIfInstruction(out var condition, out var bodyBranch)) if (!target.Instructions[0].MatchIfInstruction(out var condition, out var bodyBranch))
return false; return false;
if (!MatchStringEqualityComparison(condition, switchValue.Variable, out string stringValue)) if (!target.Instructions[1].MatchBranch(out Block exit))
return false; return false;
if (!bodyBranch.MatchBranch(out Block body)) if (!bodyBranch.MatchBranch(out Block body))
return false; 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)); stringValues.Add((index++, stringValue, body));
} }

Loading…
Cancel
Save