Browse Source

Initial support for switch statements

pull/10/head
David Srbecký 15 years ago
parent
commit
9937d991e8
  1. 32
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  2. 69
      ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
  3. 35
      ICSharpCode.Decompiler/ILAst/ILAstTypes.cs

32
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -1,14 +1,14 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using Ast = ICSharpCode.NRefactory.CSharp; using Ast = ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp;
using Cecil = Mono.Cecil; using Cecil = Mono.Cecil;
using Mono.Cecil; using Mono.Cecil;
using Mono.Cecil.Cil; using Mono.Cecil.Cil;
using Mono.Cecil.Rocks; using Mono.Cecil.Rocks;
using Decompiler.ControlFlow; using Decompiler.ControlFlow;
namespace Decompiler namespace Decompiler
{ {
@ -167,6 +167,16 @@ namespace Decompiler
FalseStatement = TransformBlock(conditionalNode.FalseBlock) FalseStatement = TransformBlock(conditionalNode.FalseBlock)
}; };
} }
} else if (node is ILSwitch) {
ILSwitch ilSwitch = (ILSwitch)node;
SwitchStatement switchStmt = new SwitchStatement() { Expression = (Expression)TransformExpression(ilSwitch.Condition.Arguments[0]) };
for (int i = 0; i < ilSwitch.CaseBlocks.Count; i++) {
switchStmt.AddChild(new SwitchSection() {
CaseLabels = new CaseLabel[] { new CaseLabel() { Expression = new PrimitiveExpression(i) } },
Statements = new Statement[] { TransformBlock(ilSwitch.CaseBlocks[i]) }
}, SwitchStatement.SwitchSectionRole);
}
yield return switchStmt;
} else if (node is ILTryCatchBlock) { } else if (node is ILTryCatchBlock) {
ILTryCatchBlock tryCatchNode = ((ILTryCatchBlock)node); ILTryCatchBlock tryCatchNode = ((ILTryCatchBlock)node);
List<Ast.CatchClause> catchClauses = new List<CatchClause>(); List<Ast.CatchClause> catchClauses = new List<CatchClause>();

69
ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

@ -217,18 +217,69 @@ namespace Decompiler.ControlFlow
ILMoveableBlock block = node.UserData as ILMoveableBlock; ILMoveableBlock block = node.UserData as ILMoveableBlock;
if (block != null && block.Body.Count == 3) { if (block != null && block.Body.Count == 3) {
ILLabel label = block.Body[0] as ILLabel; ILLabel label = block.Body[0] as ILLabel;
ILExpression condBranch = block.Body[1] as ILExpression; ILExpression condBranch = block.Body[1] as ILExpression;
ILExpression statBranch = block.Body[2] as ILExpression; ILExpression statBranch = block.Body[2] as ILExpression;
// Switch
if (label != null &&
condBranch != null && condBranch.Operand is ILLabel[] && condBranch.Arguments.Count > 0 &&
statBranch != null && statBranch.Operand is ILLabel && statBranch.Arguments.Count == 0)
{
ILSwitch ilSwitch = new ILSwitch() { Condition = condBranch };
// Replace the two branches with a conditional structure - this preserves the node label
block.Body.Remove(condBranch);
block.Body.Remove(statBranch);
block.Body.Add(ilSwitch);
ControlFlowNode statTarget = null;
labelToCfNode.TryGetValue((ILLabel)statBranch.Operand, out statTarget);
// Pull in the conditional code
HashSet<ControlFlowNode> frontiers = new HashSet<ControlFlowNode>();
if (statTarget != null)
frontiers.UnionWith(statTarget.DominanceFrontier);
foreach(ILLabel condLabel in (ILLabel[])condBranch.Operand) {
ControlFlowNode condTarget = null;
labelToCfNode.TryGetValue(condLabel, out condTarget);
if (condTarget != null)
frontiers.UnionWith(condTarget.DominanceFrontier);
}
foreach(ILLabel condLabel in (ILLabel[])condBranch.Operand) {
ControlFlowNode condTarget = null;
labelToCfNode.TryGetValue(condLabel, out condTarget);
ILBlock caseBlock = new ILBlock() { EntryPoint = condLabel };
if (condTarget != null && !frontiers.Contains(condTarget)) {
HashSet<ControlFlowNode> content = FindDominatedNodes(nodes, condTarget);
nodes.ExceptWith(content);
caseBlock.Body.AddRange(FindConditions(content, condTarget));
}
ilSwitch.CaseBlocks.Add(caseBlock);
}
// The labels will not be used - kill them
condBranch.Operand = null;
result.Add(block);
nodes.Remove(node);
}
// Two-way branch
if (label != null && if (label != null &&
condBranch != null && condBranch.Operand is ILLabel && condBranch.Arguments.Count > 0 && condBranch != null && condBranch.Operand is ILLabel && condBranch.Arguments.Count > 0 &&
statBranch != null && statBranch.Operand is ILLabel && statBranch.Arguments.Count == 0) statBranch != null && statBranch.Operand is ILLabel && statBranch.Arguments.Count == 0)
{ {
ControlFlowNode condTarget = null;
ControlFlowNode statTarget = null; ControlFlowNode statTarget = null;
labelToCfNode.TryGetValue((ILLabel)condBranch.Operand, out condTarget);
labelToCfNode.TryGetValue((ILLabel)statBranch.Operand, out statTarget); labelToCfNode.TryGetValue((ILLabel)statBranch.Operand, out statTarget);
ControlFlowNode condTarget = null;
labelToCfNode.TryGetValue((ILLabel)condBranch.Operand, out condTarget);
ILCondition condition = new ILCondition() { ILCondition condition = new ILCondition() {
Condition = condBranch, Condition = condBranch,
@ -236,21 +287,17 @@ namespace Decompiler.ControlFlow
FalseBlock = new ILBlock() { EntryPoint = (ILLabel)statBranch.Operand } FalseBlock = new ILBlock() { EntryPoint = (ILLabel)statBranch.Operand }
}; };
// The label will not be used - kill it // Replace the two branches with a conditional structure - this preserves the node label
condBranch.Operand = null;
// Replace the two branches with a conditional structure
block.Body.Remove(condBranch); block.Body.Remove(condBranch);
block.Body.Remove(statBranch); block.Body.Remove(statBranch);
block.Body.Add(condition); block.Body.Add(condition);
result.Add(block);
// Pull in the conditional code // Pull in the conditional code
HashSet<ControlFlowNode> frontiers = new HashSet<ControlFlowNode>(); HashSet<ControlFlowNode> frontiers = new HashSet<ControlFlowNode>();
if (condTarget != null)
frontiers.UnionWith(condTarget.DominanceFrontier);
if (statTarget != null) if (statTarget != null)
frontiers.UnionWith(statTarget.DominanceFrontier); frontiers.UnionWith(statTarget.DominanceFrontier);
if (condTarget != null)
frontiers.UnionWith(condTarget.DominanceFrontier);
if (condTarget != null && !frontiers.Contains(condTarget)) { if (condTarget != null && !frontiers.Contains(condTarget)) {
HashSet<ControlFlowNode> content = FindDominatedNodes(nodes, condTarget); HashSet<ControlFlowNode> content = FindDominatedNodes(nodes, condTarget);
@ -263,6 +310,10 @@ namespace Decompiler.ControlFlow
condition.FalseBlock.Body.AddRange(FindConditions(content, statTarget)); condition.FalseBlock.Body.AddRange(FindConditions(content, statTarget));
} }
// The label will not be used - kill it
condBranch.Operand = null;
result.Add(block);
nodes.Remove(node); nodes.Remove(node);
} }
} }

35
ICSharpCode.Decompiler/ILAst/ILAstTypes.cs

@ -1,11 +1,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using Decompiler.ControlFlow; using Decompiler.ControlFlow;
using Mono.Cecil; using Mono.Cecil;
using Mono.Cecil.Cil; using Mono.Cecil.Cil;
using Cecil = Mono.Cecil; using Cecil = Mono.Cecil;
namespace Decompiler namespace Decompiler
{ {
@ -48,6 +48,7 @@ namespace Decompiler
public class ILBlock: ILNode public class ILBlock: ILNode
{ {
// TODO: This should really be a goto, not a label
public ILLabel EntryPoint; public ILLabel EntryPoint;
public List<ILNode> Body; public List<ILNode> Body;
@ -124,8 +125,8 @@ namespace Decompiler
public int From; public int From;
public int To; // Exlusive public int To; // Exlusive
public override string ToString() public override string ToString()
{ {
return string.Format("{0}-{1}", From, To); return string.Format("{0}-{1}", From, To);
} }
} }
@ -226,4 +227,18 @@ namespace Decompiler
yield return FalseBlock; yield return FalseBlock;
} }
} }
public class ILSwitch: ILNode
{
public ILExpression Condition;
public List<ILBlock> CaseBlocks = new List<ILBlock>();
public override IEnumerable<ILNode> GetChildren()
{
yield return Condition;
foreach (ILBlock caseBlock in this.CaseBlocks) {
yield return caseBlock;
}
}
}
} }

Loading…
Cancel
Save