Browse Source

Generate switch default case. Closes #26

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

4
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -123,7 +123,11 @@ namespace ICSharpCode.Decompiler.Ast @@ -123,7 +123,11 @@ namespace ICSharpCode.Decompiler.Ast
SwitchStatement switchStmt = new SwitchStatement() { Expression = (Expression)TransformExpression(ilSwitch.Condition) };
foreach (var caseBlock in ilSwitch.CaseBlocks) {
SwitchSection section = new SwitchSection();
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));
switchStmt.SwitchSections.Add(section);
}

7
ICSharpCode.Decompiler/ILAst/GotoRemoval.cs

@ -60,6 +60,8 @@ namespace ICSharpCode.Decompiler.ILAst @@ -60,6 +60,8 @@ namespace ICSharpCode.Decompiler.ILAst
// Remove redundant case blocks altogether
foreach(ILSwitch ilSwitch in method.GetSelfAndChildrenRecursive<ILSwitch>()) {
foreach(ILBlock ilCase in ilSwitch.CaseBlocks) {
Debug.Assert(ilCase.EntryGoto == null);
int count = ilCase.Body.Count;
if (count >= 2) {
if (!ilCase.Body[count - 2].CanFallthough() &&
@ -68,8 +70,13 @@ namespace ICSharpCode.Decompiler.ILAst @@ -68,8 +70,13 @@ namespace ICSharpCode.Decompiler.ILAst
}
}
}
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
if (method.Body.Count > 0 && method.Body.Last().Match(ILCode.Ret) && ((ILExpression)method.Body.Last()).Arguments.Count == 0) {

28
ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

@ -584,18 +584,20 @@ namespace ICSharpCode.Decompiler.ILAst @@ -584,18 +584,20 @@ namespace ICSharpCode.Decompiler.ILAst
if (condBranch.Match(ILCode.Switch, out caseLabels)) {
ILSwitch ilSwitch = new ILSwitch() { Condition = condBranch.Arguments.Single() };
result.Add(new ILBasicBlock() {
ILBasicBlock newBB = new ILBasicBlock() {
EntryLabel = block.EntryLabel, // Keep the entry label
Body = { ilSwitch },
FallthoughGoto = block.FallthoughGoto
});
};
result.Add(newBB);
// Remove the item so that it is not picked up as content
scope.RemoveOrThrow(node);
// Pull in code of cases
ILLabel fallLabel = (ILLabel)block.FallthoughGoto.Operand;
ControlFlowNode fallTarget = null;
labelToCfNode.TryGetValue((ILLabel)block.FallthoughGoto.Operand, out fallTarget);
labelToCfNode.TryGetValue(fallLabel, out fallTarget);
HashSet<ControlFlowNode> frontiers = new HashSet<ControlFlowNode>();
if (fallTarget != null)
@ -614,7 +616,10 @@ namespace ICSharpCode.Decompiler.ILAst @@ -614,7 +616,10 @@ namespace ICSharpCode.Decompiler.ILAst
// Find or create new case block
ILSwitch.CaseBlock caseBlock = ilSwitch.CaseBlocks.Where(b => b.EntryGoto.Operand == condLabel).FirstOrDefault();
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);
ControlFlowNode condTarget = null;
@ -629,6 +634,21 @@ namespace ICSharpCode.Decompiler.ILAst @@ -629,6 +634,21 @@ namespace ICSharpCode.Decompiler.ILAst
}
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

2
ICSharpCode.Decompiler/ILAst/ILAstTypes.cs

@ -444,7 +444,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -444,7 +444,7 @@ namespace ICSharpCode.Decompiler.ILAst
{
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)
{

12
NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs

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

Loading…
Cancel
Save