Browse Source

Generate switch default case. Closes #26

pull/70/head
David Srbecký 15 years ago
parent
commit
84d45645bd
  1. 6
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  2. 9
      ICSharpCode.Decompiler/ILAst/GotoRemoval.cs
  3. 28
      ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
  4. 2
      ICSharpCode.Decompiler/ILAst/ILAstTypes.cs
  5. 18
      NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs

6
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -123,7 +123,11 @@ namespace ICSharpCode.Decompiler.Ast
SwitchStatement switchStmt = new SwitchStatement() { Expression = (Expression)TransformExpression(ilSwitch.Condition) }; SwitchStatement switchStmt = new SwitchStatement() { Expression = (Expression)TransformExpression(ilSwitch.Condition) };
foreach (var caseBlock in ilSwitch.CaseBlocks) { foreach (var caseBlock in ilSwitch.CaseBlocks) {
SwitchSection section = new SwitchSection(); SwitchSection section = new SwitchSection();
section.CaseLabels.AddRange(caseBlock.Values.Select(i => new CaseLabel() { Expression = AstBuilder.MakePrimitive(i, ilSwitch.Condition.InferredType) })); if (caseBlock.Values != null) {
section.CaseLabels.AddRange(caseBlock.Values.Select(i => new CaseLabel() { Expression = AstBuilder.MakePrimitive(i, ilSwitch.Condition.InferredType) }));
} else {
section.CaseLabels.Add(new CaseLabel());
}
section.Statements.Add(TransformBlock(caseBlock)); section.Statements.Add(TransformBlock(caseBlock));
switchStmt.SwitchSections.Add(section); switchStmt.SwitchSections.Add(section);
} }

9
ICSharpCode.Decompiler/ILAst/GotoRemoval.cs

@ -60,6 +60,8 @@ namespace ICSharpCode.Decompiler.ILAst
// Remove redundant case blocks altogether // Remove redundant case blocks altogether
foreach(ILSwitch ilSwitch in method.GetSelfAndChildrenRecursive<ILSwitch>()) { foreach(ILSwitch ilSwitch in method.GetSelfAndChildrenRecursive<ILSwitch>()) {
foreach(ILBlock ilCase in ilSwitch.CaseBlocks) { foreach(ILBlock ilCase in ilSwitch.CaseBlocks) {
Debug.Assert(ilCase.EntryGoto == null);
int count = ilCase.Body.Count; int count = ilCase.Body.Count;
if (count >= 2) { if (count >= 2) {
if (!ilCase.Body[count - 2].CanFallthough() && if (!ilCase.Body[count - 2].CanFallthough() &&
@ -68,7 +70,12 @@ namespace ICSharpCode.Decompiler.ILAst
} }
} }
} }
ilSwitch.CaseBlocks.RemoveAll(b => b.Body.Count == 1 && b.Body.Single().Match(ILCode.LoopOrSwitchBreak));
var defaultCase = ilSwitch.CaseBlocks.Where(cb => cb.Values == null).SingleOrDefault();
// If there is no default block, remove empty case blocks
if (defaultCase == null || (defaultCase.Body.Count == 1 && defaultCase.Body.Single().Match(ILCode.LoopOrSwitchBreak))) {
ilSwitch.CaseBlocks.RemoveAll(b => b.Body.Count == 1 && b.Body.Single().Match(ILCode.LoopOrSwitchBreak));
}
} }
// Remove redundant return // Remove redundant return

28
ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

@ -584,18 +584,20 @@ namespace ICSharpCode.Decompiler.ILAst
if (condBranch.Match(ILCode.Switch, out caseLabels)) { if (condBranch.Match(ILCode.Switch, out caseLabels)) {
ILSwitch ilSwitch = new ILSwitch() { Condition = condBranch.Arguments.Single() }; ILSwitch ilSwitch = new ILSwitch() { Condition = condBranch.Arguments.Single() };
result.Add(new ILBasicBlock() { ILBasicBlock newBB = new ILBasicBlock() {
EntryLabel = block.EntryLabel, // Keep the entry label EntryLabel = block.EntryLabel, // Keep the entry label
Body = { ilSwitch }, Body = { ilSwitch },
FallthoughGoto = block.FallthoughGoto FallthoughGoto = block.FallthoughGoto
}); };
result.Add(newBB);
// Remove the item so that it is not picked up as content // Remove the item so that it is not picked up as content
scope.RemoveOrThrow(node); scope.RemoveOrThrow(node);
// Pull in code of cases // Pull in code of cases
ILLabel fallLabel = (ILLabel)block.FallthoughGoto.Operand;
ControlFlowNode fallTarget = null; ControlFlowNode fallTarget = null;
labelToCfNode.TryGetValue((ILLabel)block.FallthoughGoto.Operand, out fallTarget); labelToCfNode.TryGetValue(fallLabel, out fallTarget);
HashSet<ControlFlowNode> frontiers = new HashSet<ControlFlowNode>(); HashSet<ControlFlowNode> frontiers = new HashSet<ControlFlowNode>();
if (fallTarget != null) if (fallTarget != null)
@ -614,7 +616,10 @@ namespace ICSharpCode.Decompiler.ILAst
// Find or create new case block // Find or create new case block
ILSwitch.CaseBlock caseBlock = ilSwitch.CaseBlocks.Where(b => b.EntryGoto.Operand == condLabel).FirstOrDefault(); ILSwitch.CaseBlock caseBlock = ilSwitch.CaseBlocks.Where(b => b.EntryGoto.Operand == condLabel).FirstOrDefault();
if (caseBlock == null) { if (caseBlock == null) {
caseBlock = new ILSwitch.CaseBlock() { EntryGoto = new ILExpression(ILCode.Br, condLabel) }; caseBlock = new ILSwitch.CaseBlock() {
Values = new List<int>(),
EntryGoto = new ILExpression(ILCode.Br, condLabel)
};
ilSwitch.CaseBlocks.Add(caseBlock); ilSwitch.CaseBlocks.Add(caseBlock);
ControlFlowNode condTarget = null; ControlFlowNode condTarget = null;
@ -629,6 +634,21 @@ namespace ICSharpCode.Decompiler.ILAst
} }
caseBlock.Values.Add(i); caseBlock.Values.Add(i);
} }
// Heuristis to determine if we want to use fallthough as default case
if (fallTarget != null && !frontiers.Contains(fallTarget)) {
HashSet<ControlFlowNode> content = FindDominatedNodes(scope, fallTarget);
if (content.Any()) {
var caseBlock = new ILSwitch.CaseBlock() { EntryGoto = new ILExpression(ILCode.Br, fallLabel) };
ilSwitch.CaseBlocks.Add(caseBlock);
newBB.FallthoughGoto = null;
scope.ExceptWith(content);
caseBlock.Body.AddRange(FindConditions(content, fallTarget));
// Add explicit break which should not be used by default, but the goto removal might decide to use it
caseBlock.Body.Add(new ILBasicBlock() { Body = { new ILExpression(ILCode.LoopOrSwitchBreak, null) } });
}
}
} }
// Two-way branch // Two-way branch

2
ICSharpCode.Decompiler/ILAst/ILAstTypes.cs

@ -444,7 +444,7 @@ namespace ICSharpCode.Decompiler.ILAst
{ {
public class CaseBlock: ILBlock public class CaseBlock: ILBlock
{ {
public List<int> Values = new List<int>(); public List<int> Values; // null for the default case
public override void WriteTo(ITextOutput output) public override void WriteTo(ITextOutput output)
{ {

18
NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs

@ -1529,8 +1529,13 @@ namespace ICSharpCode.NRefactory.CSharp
public object VisitSwitchSection(SwitchSection switchSection, object data) public object VisitSwitchSection(SwitchSection switchSection, object data)
{ {
StartNode(switchSection); StartNode(switchSection);
foreach (var label in switchSection.CaseLabels) bool first = true;
foreach (var label in switchSection.CaseLabels) {
if (!first)
NewLine();
label.AcceptVisitor(this, data); label.AcceptVisitor(this, data);
first = false;
}
foreach (var statement in switchSection.Statements) foreach (var statement in switchSection.Statements)
statement.AcceptVisitor(this, data); statement.AcceptVisitor(this, data);
return EndNode(switchSection); return EndNode(switchSection);
@ -1539,11 +1544,14 @@ namespace ICSharpCode.NRefactory.CSharp
public object VisitCaseLabel(CaseLabel caseLabel, object data) public object VisitCaseLabel(CaseLabel caseLabel, object data)
{ {
StartNode(caseLabel); StartNode(caseLabel);
WriteKeyword("case"); if (caseLabel.Expression.IsNull) {
Space(); WriteKeyword("default");
caseLabel.Expression.AcceptVisitor(this, data); } else {
WriteKeyword("case");
Space();
caseLabel.Expression.AcceptVisitor(this, data);
}
WriteToken(":", CaseLabel.Roles.Colon); WriteToken(":", CaseLabel.Roles.Colon);
NewLine();
return EndNode(caseLabel); return EndNode(caseLabel);
} }

Loading…
Cancel
Save