Browse Source

Completely rewritten the Node tree structure. It became increasingly difficult to maintain the node links (Predecessors, Successors) consistent during transformations. So rather then keeping them consistent I have implemented an algorithm that can calculate the Predecessors or Successors for any given node. There are a few caches on the way so that the calculation does not calculate everything again every time. Affected caches are automatically flushed when a tree structure changes. This is implemented using 'collection with events'.

pull/1/head^2
David Srbecký 18 years ago
parent
commit
356c0b0d2f
  1. 5
      Decompiler.csproj
  2. 2
      src/AstMetodBodyBuilder.cs
  3. 73
      src/ControlFlow/Node-Optimize.cs
  4. 227
      src/ControlFlow/Node-Structure.cs
  5. 63
      src/ControlFlow/Node-Transforms.cs
  6. 366
      src/ControlFlow/Node.cs
  7. 113
      src/ControlFlow/NodeCollection.cs
  8. 39
      src/ControlFlow/Nodes.cs

5
Decompiler.csproj

@ -45,8 +45,11 @@ @@ -45,8 +45,11 @@
<Compile Include="src\ByteCode.Type.cs" />
<Compile Include="src\ByteCodeCollection.cs" />
<Compile Include="src\CilStack.cs" />
<Compile Include="src\ControlFlow\Node-Optimize.cs" />
<Compile Include="src\ControlFlow\Node-Transforms.cs" />
<Compile Include="src\ControlFlow\NodeCollection.cs" />
<Compile Include="src\ControlFlow\Nodes.cs" />
<Compile Include="src\ControlFlow\Node.cs" />
<Compile Include="src\ControlFlow\Node-Structure.cs" />
<Compile Include="src\MainForm.cs" />
<Compile Include="src\MainForm.Designer.cs" />
<Compile Include="src\Options.cs" />

2
src/AstMetodBodyBuilder.cs

@ -65,7 +65,7 @@ namespace Decompiler @@ -65,7 +65,7 @@ namespace Decompiler
IEnumerable<Ast.INode> TransformNode(Node node)
{
if (Options.NodeComments) {
yield return MakeComment("// " + node.ToString());
yield return MakeComment("// " + node.Description);
}
yield return new Ast.LabelStatement(node.Label);

73
src/ControlFlow/Node-Optimize.cs

@ -0,0 +1,73 @@ @@ -0,0 +1,73 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace Decompiler.ControlFlow
{
public abstract partial class Node
{
public void Optimize()
{
OptimizeLoops();
}
public void OptimizeLoops()
{
Reset:
foreach(Node child in this.Childs) {
if (child.Predecessors.Count == 1) {
if (Options.ReduceGraph <= 0) return;
Node predecessor = child.Predecessors[0];
Node mergedNode;
if (child.Successors.Contains(predecessor)) {
mergedNode = MergeChilds<Loop>(predecessor, child);
} else {
mergedNode = MergeChilds<AcyclicGraph>(predecessor, child);
}
mergedNode.FalttenAcyclicChilds();
goto Reset;
}
}
// If the result is single acyclic node, eliminate it
if (this.Childs.Count == 1 && this.HeadChild is AcyclicGraph) {
if (Options.ReduceGraph-- <= 0) return;
Node headChild = this.HeadChild;
this.Childs.Remove(this.HeadChild);
headChild.Childs.MoveTo(this);
}
}
NodeCollection GetReachableNodes()
{
NodeCollection accumulator = new NodeCollection();
AddReachableNode(accumulator, this);
return accumulator;
}
static void AddReachableNode(NodeCollection accumulator, Node node)
{
if (!accumulator.Contains(node)) {
accumulator.Add(node);
foreach(Node successor in node.Successors) {
AddReachableNode(accumulator, successor);
}
}
}
public void OptimizeIf()
{
Node conditionNode = this.HeadChild;
// Find conditionNode (the start)
while(true) {
if (conditionNode is BasicBlock && conditionNode.Successors.Count == 2) {
break; // Found
} else if (conditionNode.Successors.Count == 1) {
conditionNode = conditionNode.Successors[0];
continue; // Next
} else {
return;
}
}
}
}
}

227
src/ControlFlow/Node-Structure.cs

@ -0,0 +1,227 @@ @@ -0,0 +1,227 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace Decompiler.ControlFlow
{
public abstract partial class Node
{
public static int NextNodeID = 1;
int id;
Node parent;
NodeCollection childs = new NodeCollection();
// Structural and linking cache
NodeCollection basicBlocks_cache = null;
NodeCollection predecessors_cache = null;
NodeCollection successors_cache = null;
public int ID {
get { return id; }
}
public Node Parent {
get { return parent; }
}
public Node HeadChild {
get {
if (this.Childs.Count > 0) {
return this.Childs[0];
} else {
return null;
}
}
}
public NodeCollection Childs {
get {
return childs;
}
}
/// <summary> All basic blocks within the scope of this node (inclusive) </summary>
public NodeCollection BasicBlocks {
get {
if (basicBlocks_cache == null) {
NodeCollection basicBlocks = new NodeCollection();
if (this is BasicBlock) {
basicBlocks.Add(this);
}
foreach(Node child in this.Childs) {
basicBlocks.AddRange(child.BasicBlocks);
}
basicBlocks_cache = basicBlocks;
}
return basicBlocks_cache;
}
}
NodeCollection FloatUpToNeighbours(IEnumerable<BasicBlock> basickBlocks)
{
NodeCollection neighbours = new NodeCollection();
if (this.Parent != null) {
foreach(BasicBlock basicBlock in basickBlocks) {
// Find neighbour coresponding to the target
Node targetNode = basicBlock;
while(targetNode != null && targetNode.Parent != this.Parent) {
targetNode = targetNode.Parent;
}
// The target is outside the scope of the parent node
if (targetNode == null) continue;
// This child is a loop
if (targetNode == this) continue;
// We found a target in our scope
neighbours.Add(targetNode);
}
}
return neighbours;
}
public NodeCollection Predecessors {
get {
if (predecessors_cache == null) {
List<BasicBlock> basicBlockPredecessors = new List<BasicBlock>();
foreach(BasicBlock basicBlock in this.BasicBlocks) {
foreach(BasicBlock basicBlockPredecessor in basicBlock.BasicBlockPredecessors) {
basicBlockPredecessors.Add(basicBlockPredecessor);
}
}
predecessors_cache = FloatUpToNeighbours(basicBlockPredecessors);
}
return predecessors_cache;
}
}
public NodeCollection Successors {
get {
if (successors_cache == null) {
List<BasicBlock> basicBlockSuccessors = new List<BasicBlock>();
foreach(BasicBlock basicBlock in this.BasicBlocks) {
foreach(BasicBlock basicBlockSuccessor in basicBlock.BasicBlockSuccessors) {
basicBlockSuccessors.Add(basicBlockSuccessor);
}
}
successors_cache = FloatUpToNeighbours(basicBlockSuccessors);
}
return successors_cache;
}
}
int Index {
get {
if (this.Parent == null) throw new Exception("Does not have a parent");
return this.Parent.Childs.IndexOf(this);
}
}
public Node NextNode {
get {
int index = this.Index + 1;
if (0 <= index && index < this.Parent.Childs.Count) {
return this.Parent.Childs[index];
} else {
return null;
}
}
}
public string Label {
get {
return this.GetType().Name + "_" + ID;
}
}
public string Description {
get {
return ToString();
}
}
protected Node()
{
this.id = NextNodeID++;
this.Childs.Added += delegate(object sender, NodeEventArgs e) {
if (e.Node.Parent != null) {
throw new Exception("Node is already assigned to other parent");
}
e.Node.parent = this;
NotifyChildsChanged();
};
this.Childs.Removed += delegate(object sender, NodeEventArgs e) {
e.Node.parent = null;
NotifyChildsChanged();
};
}
void NotifyChildsChanged()
{
this.basicBlocks_cache = null;
foreach(Node child in this.Childs) {
child.predecessors_cache = null;
child.successors_cache = null;
}
if (this.Parent != null) {
this.Parent.NotifyChildsChanged();
}
}
public override string ToString()
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.Append(this.GetType().Name);
sb.Append(" ");
sb.Append(ID);
sb.Append(" ");
sb.Append("(");
if (this.Predecessors.Count > 0) {
sb.Append("Predecessors:");
bool isFirst = true;
foreach(Node predecessor in this.Predecessors) {
if (isFirst) {
isFirst = false;
} else {
sb.Append(",");
}
sb.Append(predecessor.ID);
}
sb.Append(" ");
}
if (this.Successors.Count > 0) {
sb.Append("Successors:");
bool isFirst = true;
foreach(Node successor in this.Successors) {
if (isFirst) {
isFirst = false;
} else {
sb.Append(",");
}
sb.Append(successor.ID);
}
sb.Append(" ");
}
if (this.Parent != null) {
sb.Append("Parent:");
sb.Append(this.Parent.ID);
sb.Append(" ");
}
if (sb[sb.Length - 1] == '(') {
sb.Length -= 1;
} else if (sb[sb.Length - 1] == ' ') {
sb.Length -= 1;
sb.Append(")");
}
return sb.ToString();
}
}
}

63
src/ControlFlow/Node-Transforms.cs

@ -0,0 +1,63 @@ @@ -0,0 +1,63 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace Decompiler.ControlFlow
{
public abstract partial class Node
{
public void Remove()
{
if (this.Parent != null) {
this.Parent.Childs.Remove(this);
}
}
public void MoveTo(Node newNode)
{
MoveTo(newNode, newNode.Childs.Count);
}
public void MoveTo(Node newNode, int index)
{
this.Remove();
newNode.Childs.Insert(index, this);
}
Node MergeChilds<T>(params Node[] nodes) where T: Node, new()
{
foreach(Node node in nodes) {
if (node == null) throw new ArgumentNullException("nodes");
if (node.Parent != this) throw new ArgumentException("The node is not my child");
}
if (nodes.Length == 0) throw new ArgumentException("At least one node must be specified");
Node mergedNode = new T();
// Add the merged node
if (Options.ReduceGraph-- <= 0) return mergedNode;
int headIndex = this.Childs.IndexOf(nodes[0]);
this.Childs.Insert(headIndex, mergedNode);
foreach(Node node in nodes) {
if (Options.ReduceGraph-- <= 0) return mergedNode;
node.MoveTo(mergedNode);
}
return mergedNode;
}
public void FalttenAcyclicChilds()
{
Reset:
foreach(Node child in this.Childs) {
if (child is AcyclicGraph) {
if (Options.ReduceGraph-- <= 0) return;
child.Childs.MoveTo(this, child.Index);
child.Remove();
goto Reset;
}
}
}
}
}

366
src/ControlFlow/Node.cs

@ -1,366 +0,0 @@ @@ -1,366 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace Decompiler.ControlFlow
{
public class NodeCollection: System.Collections.ObjectModel.Collection<Node>
{
public void AddRange(IEnumerable<Node> items)
{
foreach(Node item in items) {
this.Add(item);
}
}
public void RemoveRange(IEnumerable<Node> items)
{
foreach(Node item in items) {
this.Remove(item);
}
}
public static NodeCollection Intersection(NodeCollection collectionA, NodeCollection collectionB)
{
NodeCollection result = new NodeCollection();
foreach(Node a in collectionA) {
if (collectionB.Contains(a)) {
result.Add(a);
}
}
return result;
}
protected override void InsertItem(int index, Node item)
{
if (!this.Contains(item)) {
base.InsertItem(index, item);
}
}
public bool ContainsRecursive(Node node)
{
if (this.Contains(node)) {
return true;
}
foreach(Node item in this.Items) {
if (item.Childs.ContainsRecursive(node)) {
return true;
}
}
return false;
}
public Node FindContainer(Node node)
{
foreach(Node item in this.Items) {
if (item == node || item.Childs.ContainsRecursive(node)) {
return item;
}
}
return null;
}
}
public abstract class Node
{
public static int NextNodeID = 1;
int id;
Node parent;
Node headChild;
NodeCollection childs = new NodeCollection();
NodeCollection predecessors = new NodeCollection();
NodeCollection successors = new NodeCollection();
public int ID {
get { return id; }
}
public Node Parent {
get { return parent; }
set { parent = value; }
}
public Node HeadChild {
get { return headChild; }
protected set { headChild = value; }
}
public NodeCollection Childs {
get { return childs; }
}
public NodeCollection Predecessors {
get { return predecessors; }
}
public NodeCollection Successors {
get { return successors; }
}
public Node NextNode {
get {
if (this.Parent == null) throw new Exception("Does not have a parent");
int myIndex = this.Parent.Childs.IndexOf(this);
int index = myIndex + 1;
if (0 <= index && index < this.Parent.Childs.Count) {
return this.Parent.Childs[index];
} else {
return null;
}
}
}
public string Label {
get {
return this.GetType().Name + "_" + ID;
}
}
public Node(Node parent)
{
this.parent = parent;
this.id = NextNodeID++;
}
NodeCollection GetReachableNodes()
{
NodeCollection accumulator = new NodeCollection();
AddReachableNode(accumulator, this);
return accumulator;
}
static void AddReachableNode(NodeCollection accumulator, Node node)
{
if (!accumulator.Contains(node)) {
accumulator.Add(node);
foreach(Node successor in node.Successors) {
AddReachableNode(successor, accumulator);
}
}
}
void GetBasicBlockSuccessors(NodeCollection accumulator)
{
BasicBlock me = this as BasicBlock;
if (me != null) {
if (me.FallThroughBasicBlock != null) {
accumulator.Add(me.FallThroughBasicBlock);
}
if (me.BranchBasicBlock != null) {
accumulator.Add(me.BranchBasicBlock);
}
} else {
foreach(Node child in this.Childs) {
child.GetBasicBlockSuccessors(accumulator);
}
}
}
public void RebuildNodeLinks()
{
foreach(Node child in this.Childs) {
NodeCollection successorBasicBlocks = new NodeCollection();
child.GetBasicBlockSuccessors(successorBasicBlocks);
NodeCollection successorNodes = new NodeCollection();
foreach(Node successorBasicBlock in successorBasicBlocks) {
Node container = this.Childs.FindContainer(successorBasicBlock);
if (container != null) {
successorNodes.Add(container);
}
}
// Remove self link
if (successorNodes.Contains(child)) {
successorNodes.Remove(child);
}
foreach(Node target in successorNodes) {
child.Successors.Add(target);
target.Predecessors.Add(child);
}
}
}
public void Optimize()
{
for(int i = 0; i < this.Childs.Count;) {
Node child = childs[i];
if (child.Predecessors.Count == 1) {
if (Options.ReduceGraph-- <= 0) return;
MergeChilds(child.Predecessors[0], child);
i = 0; // Restart
} else {
i++; // Next
}
}
// If it result is single acyclic node, eliminate it
if (this.Childs.Count == 1 && this.Childs[0] is AcyclicGraph) {
this.headChild = this.Childs[0].HeadChild;
this.childs = this.Childs[0].Childs;
this.UpdateParentOfChilds();
}
OptimizeIf();
}
public void OptimizeIf()
{
Node conditionNode = this.HeadChild;
// Find conditionNode (the start)
while(true) {
if (conditionNode is BasicBlock && conditionNode.Successors.Count == 2) {
break; // Found
} else if (conditionNode.Successors == 1) {
conditionNode = conditionNode.Successors[0];
continue; // Next
} else {
return;
}
}
}
void UpdateParentOfChilds()
{
foreach(Node child in this.Childs) {
child.Parent = this;
}
}
static void MergeChilds(Node head, Node tail)
{
if (head == null) throw new ArgumentNullException("head");
if (tail == null) throw new ArgumentNullException("tail");
if (head.Parent != tail.Parent) throw new ArgumentException("different parents");
Node container = head.Parent;
Node mergedNode;
// Get type
if (tail.Successors.Contains(head)) {
mergedNode = new Loop(container);
} else {
mergedNode = new AcyclicGraph(container);
}
// Add head
if (head is BasicBlock) {
mergedNode.HeadChild = head;
mergedNode.Childs.Add(head);
} else if (head is AcyclicGraph) {
mergedNode.HeadChild = ((AcyclicGraph)head).HeadChild;
mergedNode.Childs.AddRange(((AcyclicGraph)head).Childs);
} else if (head is Loop) {
mergedNode.HeadChild = head;
mergedNode.Childs.Add(head);
} else {
throw new Exception("Invalid head type");
}
// Add tail
if (tail is BasicBlock) {
mergedNode.Childs.Add(tail);
} else if (tail is AcyclicGraph) {
mergedNode.Childs.AddRange(((AcyclicGraph)tail).Childs);
} else if (tail is Loop) {
mergedNode.Childs.Add(tail);
} else {
throw new Exception("Invalid tail type");
}
mergedNode.UpdateParentOfChilds();
// Remove links between the head and tail
if (head.Successors.Contains(tail)) {
head.Successors.Remove(tail);
tail.Predecessors.Remove(head);
}
if (tail.Successors.Contains(head)) {
tail.Successors.Remove(head);
head.Predecessors.Remove(tail);
}
Relink(head, mergedNode);
Relink(tail, mergedNode);
mergedNode.RebuildNodeLinks();
// Remove the old nodes and add the merged node - replace head with the merged node
container.Childs.Remove(tail);
int headIndex = container.Childs.IndexOf(head);
container.Childs.Remove(head);
container.Childs.Insert(headIndex, mergedNode);
}
static void Relink(Node node, Node target)
{
// Relink all neighbours to the target node
foreach(Node predecessor in node.Predecessors) {
predecessor.Successors.Remove(node);
predecessor.Successors.Add(target);
}
foreach(Node successor in node.Successors) {
successor.Predecessors.Remove(node);
successor.Predecessors.Add(target);
}
// Move our pointers to the target node
target.Predecessors.AddRange(node.Predecessors);
target.Successors.AddRange(node.Successors);
node.Predecessors.Clear();
node.Successors.Clear();
}
public override string ToString()
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.Append(this.GetType().Name);
sb.Append(" ");
sb.Append(ID);
sb.Append(" ");
sb.Append("(");
if (this.Predecessors.Count > 0) {
sb.Append("Predecessors:");
bool isFirst = true;
foreach(Node predecessor in this.Predecessors) {
if (isFirst) {
isFirst = false;
} else {
sb.Append(",");
}
sb.Append(predecessor.ID);
}
sb.Append(" ");
}
if (this.Successors.Count > 0) {
sb.Append("Successors:");
bool isFirst = true;
foreach(Node successor in this.Successors) {
if (isFirst) {
isFirst = false;
} else {
sb.Append(",");
}
sb.Append(successor.ID);
}
sb.Append(" ");
}
if (this.Parent != null) {
sb.Append("Parent:");
sb.Append(this.Parent.ID);
}
sb.Append(" ");
if (this.HeadChild != null) {
sb.Append("Head:");
sb.Append(this.HeadChild.ID);
}
sb.Append(" ");
sb.Length = sb.Length - 1;
sb.Append(")");
return sb.ToString();
}
}
}

113
src/ControlFlow/NodeCollection.cs

@ -0,0 +1,113 @@ @@ -0,0 +1,113 @@
using System;
using System.Collections.Generic;
namespace Decompiler.ControlFlow
{
public class NodeEventArgs: EventArgs
{
Node node;
public Node Node {
get { return node; }
}
public NodeEventArgs(Node node)
{
this.node = node;
}
}
public class NodeCollection: System.Collections.ObjectModel.Collection<Node>
{
public event EventHandler<NodeEventArgs> Added;
public event EventHandler<NodeEventArgs> Removed;
protected virtual void OnAdded(Node node)
{
if (Added != null) {
Added(this, new NodeEventArgs(node));
}
}
protected virtual void OnRemoved(Node node)
{
if (Removed != null) {
Removed(this, new NodeEventArgs(node));
}
}
protected override void ClearItems()
{
while(this.Count > 0) {
this.RemoveAt(this.Count - 1);
}
}
protected override void InsertItem(int index, Node item)
{
if (!this.Contains(item)) {
base.InsertItem(index, item);
}
OnAdded(item);
}
protected override void RemoveItem(int index)
{
Node node = this[index];
base.RemoveItem(index);
OnRemoved(node);
}
protected override void SetItem(int index, Node item)
{
this.RemoveAt(index);
this.Insert(index, item);
}
public void AddRange(IEnumerable<Node> items)
{
foreach(Node item in items) {
this.Add(item);
}
}
public void RemoveRange(IEnumerable<Node> items)
{
foreach(Node item in items) {
this.Remove(item);
}
}
public void MoveTo(Node newNode)
{
while(this.Count > 0) {
this[0].MoveTo(newNode);
}
}
public void MoveTo(Node newNode, int index)
{
while(this.Count > 0) {
this[0].MoveTo(newNode, index);
index++;
}
}
public static NodeCollection Intersection(NodeCollection collectionA, NodeCollection collectionB)
{
NodeCollection result = new NodeCollection();
foreach(Node a in collectionA) {
if (collectionB.Contains(a)) {
result.Add(a);
}
}
return result;
}
public override string ToString()
{
return string.Format("{0} Count = {1}", typeof(NodeCollection).Name, this.Count);
}
}
}

39
src/ControlFlow/Nodes.cs

@ -7,7 +7,13 @@ namespace Decompiler.ControlFlow @@ -7,7 +7,13 @@ namespace Decompiler.ControlFlow
{
public class MethodBodyGraph: Node
{
public MethodBodyGraph(StackExpressionCollection exprs): base(null)
BasicBlock methodEntry;
public BasicBlock MethodEntry {
get { return methodEntry; }
}
public MethodBodyGraph(StackExpressionCollection exprs)
{
if (exprs.Count == 0) throw new ArgumentException("Count == 0", "exprs");
@ -18,7 +24,7 @@ namespace Decompiler.ControlFlow @@ -18,7 +24,7 @@ namespace Decompiler.ControlFlow
// - last expression was branch
// - this expression is branch target
if (i == 0 || exprs[i - 1].BranchTarget != null || exprs[i].BranchesHere.Count > 0){
basicBlock = new BasicBlock(this);
basicBlock = new BasicBlock();
this.Childs.Add(basicBlock);
}
basicBlock.Body.Add(exprs[i]);
@ -37,6 +43,7 @@ namespace Decompiler.ControlFlow @@ -37,6 +43,7 @@ namespace Decompiler.ControlFlow
if (exprs[i].LastByteCode.OpCode.Code == Code.Br) continue;
node.FallThroughBasicBlock = target;
target.BasicBlockPredecessors.Add(node);
}
// Add branch links to BasicBlocks
@ -46,33 +53,26 @@ namespace Decompiler.ControlFlow @@ -46,33 +53,26 @@ namespace Decompiler.ControlFlow
BasicBlock target = exprs[i].BranchTarget.BasicBlock;
node.BranchBasicBlock = target;
target.BasicBlockPredecessors.Add(node);
}
}
// Link all nodes
RebuildNodeLinks();
this.HeadChild = this.Childs[0];
this.methodEntry = (BasicBlock)this.HeadChild;
}
}
public class AcyclicGraph: Node
{
public AcyclicGraph(Node parent): base(parent){
}
}
public class Loop: Node
{
public Loop(Node parent): base(parent){
}
}
public class BasicBlock: Node
{
List<StackExpression> body = new List<StackExpression>();
List<BasicBlock> basicBlockPredecessors = new List<BasicBlock>();
BasicBlock fallThroughBasicBlock;
BasicBlock branchBasicBlock;
@ -80,6 +80,10 @@ namespace Decompiler.ControlFlow @@ -80,6 +80,10 @@ namespace Decompiler.ControlFlow
get { return body; }
}
public List<BasicBlock> BasicBlockPredecessors {
get { return basicBlockPredecessors; }
}
public BasicBlock FallThroughBasicBlock {
get { return fallThroughBasicBlock; }
set { fallThroughBasicBlock = value; }
@ -90,8 +94,15 @@ namespace Decompiler.ControlFlow @@ -90,8 +94,15 @@ namespace Decompiler.ControlFlow
set { branchBasicBlock = value; }
}
public BasicBlock(Node parent): base(parent)
{
public IEnumerable<BasicBlock> BasicBlockSuccessors {
get {
if (this.FallThroughBasicBlock != null) {
yield return this.FallThroughBasicBlock;
}
if (this.BranchBasicBlock != null) {
yield return this.BranchBasicBlock;
}
}
}
}
}

Loading…
Cancel
Save