diff --git a/ICSharpCode.Decompiler/DominanceLoopDetector.cs b/ICSharpCode.Decompiler/DominanceLoopDetector.cs
new file mode 100644
index 000000000..cda145b21
--- /dev/null
+++ b/ICSharpCode.Decompiler/DominanceLoopDetector.cs
@@ -0,0 +1,80 @@
+//
+//
+//
+//
+// $Revision$
+//
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using ICSharpCode.Decompiler.FlowAnalysis;
+
+namespace ICSharpCode.Decompiler
+{
+ ///
+ /// Description of DominanceLoopDetector.
+ ///
+ public class DominanceLoopDetector
+ {
+ public static ControlStructure DetectLoops(ControlFlowGraph g)
+ {
+ ControlStructure root = new ControlStructure(
+ new HashSet(g.Nodes),
+ g.EntryPoint
+ );
+ DetectLoops(g, root);
+ g.ResetVisited();
+ return root;
+ }
+
+ static void DetectLoops(ControlFlowGraph g, ControlStructure current)
+ {
+ g.ResetVisited();
+ FindLoops(current, current.EntryPoint);
+ foreach (ControlStructure loop in current.Children)
+ DetectLoops(g, loop);
+ }
+
+ static void FindLoops(ControlStructure current, ControlFlowNode node)
+ {
+ if (node.Visited)
+ return;
+ node.Visited = true;
+ if (current.Nodes.Contains(node)
+ && node.DominanceFrontier.Contains(node)
+ && node != current.EntryPoint)
+ {
+ HashSet loopContents = new HashSet();
+ FindLoopContents(current, loopContents, node, node);
+ current.Nodes.ExceptWith(loopContents);
+ current.Children.Add(new ControlStructure(loopContents, node));
+ }
+ foreach (var edge in node.Outgoing) {
+ FindLoops(current, edge.Target);
+ }
+ }
+
+ static void FindLoopContents(ControlStructure current, HashSet loopContents, ControlFlowNode loopHead, ControlFlowNode node)
+ {
+ if (current.Nodes.Contains(node) && loopHead.Dominates(node) && loopContents.Add(node)) {
+ foreach (var edge in node.Incoming) {
+ FindLoopContents(current, loopContents, loopHead, edge.Source);
+ }
+ }
+ }
+ }
+
+ public class ControlStructure
+ {
+ public List Children = new List();
+ public HashSet Nodes;
+ public ControlFlowNode EntryPoint;
+
+ public ControlStructure(HashSet nodes, ControlFlowNode entryPoint)
+ {
+ this.Nodes = nodes;
+ this.EntryPoint = entryPoint;
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowEdge.cs b/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowEdge.cs
index 288bff70a..e01dbf1bc 100644
--- a/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowEdge.cs
+++ b/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowEdge.cs
@@ -23,9 +23,23 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
public enum JumpType
{
Normal,
+ ///
+ /// Jump to exception handler (an exception occurred)
+ ///
JumpToExceptionHandler,
+ ///
+ /// Jump from try block to leave target:
+ /// This is not a real jump, as the finally handler is executed first!
+ ///
LeaveTry,
- MutualProtection
+ ///
+ /// Jump from one catch block to its sibling
+ ///
+ MutualProtection,
+ ///
+ /// non-determistic jump at end finally (to any of the potential leave targets)
+ ///
+ EndFinally
}
public sealed class ControlFlowEdge
diff --git a/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraphBuilder.cs b/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraphBuilder.cs
index d3fffb1cf..ba2e8dadb 100644
--- a/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraphBuilder.cs
+++ b/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraphBuilder.cs
@@ -32,14 +32,14 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
return new ControlFlowGraphBuilder(methodBody).Build();
}
+ bool copyFinallyBlocks = false;
MethodBody methodBody;
int[] offsets; // array index = instruction index; value = IL offset
bool[] hasIncomingJumps; // array index = instruction index
List nodes = new List();
ControlFlowNode entryPoint;
- ControlFlowNode regularExit ;
+ ControlFlowNode regularExit;
ControlFlowNode exceptionalExit;
- //Stack<> activeExceptionHandlers = new Stack();
private ControlFlowGraphBuilder(MethodBody methodBody)
{
@@ -69,7 +69,10 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
CreateNodes();
CreateRegularControlFlow();
CreateExceptionalControlFlow();
- CopyFinallyBlocksIntoLeaveEdges();
+ if (copyFinallyBlocks)
+ CopyFinallyBlocksIntoLeaveEdges();
+ else
+ TransformLeaveEdges();
return new ControlFlowGraph(nodes.ToArray());
}
@@ -242,6 +245,28 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
}
}
+
+ void TransformLeaveEdges()
+ {
+ for (int i = nodes.Count - 1; i >= 0; i--) {
+ ControlFlowNode node = nodes[i];
+ if (node.End != null && node.Outgoing.Count == 1 && node.Outgoing[0].Type == JumpType.LeaveTry) {
+ Debug.Assert(node.End.OpCode == OpCodes.Leave);
+
+ ControlFlowNode target = node.Outgoing[0].Target;
+ // remove the edge
+ target.Incoming.Remove(node.Outgoing[0]);
+ node.Outgoing.Clear();
+
+ ControlFlowNode handler = FindInnermostExceptionHandler(node.End.Offset);
+ Debug.Assert(handler.NodeType == ControlFlowNodeType.FinallyOrFaultHandler);
+
+ CreateEdge(node, handler, JumpType.Normal);
+ CreateEdge(handler.EndFinallyOrFaultNode, target, JumpType.EndFinally);
+ }
+ }
+ }
+
///
/// Creates a copy of all nodes pointing to 'end' and replaces those references with references to 'newEnd'.
/// Nodes pointing to the copied node are copied recursively to update those references, too.
diff --git a/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowNode.cs b/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowNode.cs
index 329733c8a..855f8b4ec 100644
--- a/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowNode.cs
+++ b/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowNode.cs
@@ -174,11 +174,26 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
// writer.WriteLine();
// writer.Write("ImmediateDominator: #{0}", ImmediateDominator.BlockIndex);
// }
+ if (DominanceFrontier != null && DominanceFrontier.Any()) {
+ writer.WriteLine();
+ writer.Write("DominanceFrontier: " + string.Join(",", DominanceFrontier.OrderBy(d => d.BlockIndex).Select(d => d.BlockIndex.ToString())));
+ }
foreach (Instruction inst in this.Instructions) {
writer.WriteLine();
inst.WriteTo(writer);
}
return writer.ToString();
}
+
+ public bool Dominates(ControlFlowNode node)
+ {
+ ControlFlowNode tmp = node;
+ while (tmp != null) {
+ if (tmp == this)
+ return true;
+ tmp = tmp.ImmediateDominator;
+ }
+ return false;
+ }
}
}
diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
index e326ffbea..3f688ed02 100644
--- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
+++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
@@ -50,6 +50,7 @@
+
diff --git a/ILSpy/Decompiler/CSharpLanguage.cs b/ILSpy/Decompiler/CSharpLanguage.cs
index db67dfda8..ec2b887c9 100644
--- a/ILSpy/Decompiler/CSharpLanguage.cs
+++ b/ILSpy/Decompiler/CSharpLanguage.cs
@@ -17,7 +17,11 @@
// DEALINGS IN THE SOFTWARE.
using System;
+using System.IO;
+using ICSharpCode.Decompiler;
+using ICSharpCode.Decompiler.FlowAnalysis;
using Mono.Cecil;
+using Mono.Cecil.Rocks;
namespace ICSharpCode.ILSpy.Decompiler
{
@@ -30,8 +34,9 @@ namespace ICSharpCode.ILSpy.Decompiler
get { return "C#"; }
}
- public override void Decompile(MethodDefinition methodDefinition, ITextOutput output)
+ public override void Decompile(MethodDefinition method, ITextOutput output)
{
+ throw new NotImplementedException();
}
}
}
diff --git a/ILSpy/Disassembler/ILLanguage.cs b/ILSpy/Disassembler/ILLanguage.cs
index 28d9b6d71..4c22f2c0a 100644
--- a/ILSpy/Disassembler/ILLanguage.cs
+++ b/ILSpy/Disassembler/ILLanguage.cs
@@ -17,7 +17,11 @@
// DEALINGS IN THE SOFTWARE.
using System;
+using System.Linq;
+using ICSharpCode.Decompiler;
+using ICSharpCode.Decompiler.FlowAnalysis;
using Mono.Cecil;
+using Mono.Cecil.Rocks;
namespace ICSharpCode.ILSpy.Disassembler
{
@@ -27,6 +31,8 @@ namespace ICSharpCode.ILSpy.Disassembler
get { return "IL"; }
}
+ bool detectControlStructure = true;
+
public override void Decompile(MethodDefinition method, ITextOutput output)
{
output.WriteComment("// Method begins at RVA 0x{0:x4}", method.RVA);
@@ -53,15 +59,44 @@ namespace ICSharpCode.ILSpy.Disassembler
output.WriteLine(")");
}
- foreach (var inst in method.Body.Instructions) {
- inst.WriteTo(output);
+ if (detectControlStructure) {
+ method.Body.SimplifyMacros();
+ var cfg = ControlFlowGraphBuilder.Build(method.Body);
+ cfg.ComputeDominance();
+ cfg.ComputeDominanceFrontier();
+ var s = DominanceLoopDetector.DetectLoops(cfg);
+ WriteStructure(output, s);
+ } else {
+ foreach (var inst in method.Body.Instructions) {
+ inst.WriteTo(output);
+ output.WriteLine();
+ }
output.WriteLine();
+ foreach (var eh in method.Body.ExceptionHandlers) {
+ eh.WriteTo(output);
+ output.WriteLine();
+ }
}
+ }
+
+ void WriteStructure(ITextOutput output, ControlStructure s)
+ {
+ output.WriteComment("// loop start");
output.WriteLine();
- foreach (var eh in method.Body.ExceptionHandlers) {
- eh.WriteTo(output);
- output.WriteLine();
+ output.Indent();
+ foreach (var node in s.Nodes.Concat(s.Children.Select(c => c.EntryPoint)).OrderBy(n => n.BlockIndex)) {
+ if (s.Nodes.Contains(node)) {
+ foreach (var inst in node.Instructions) {
+ inst.WriteTo(output);
+ output.WriteLine();
+ }
+ } else {
+ WriteStructure(output, s.Children.Single(c => c.EntryPoint == node));
+ }
}
+ output.Unindent();
+ output.WriteComment("// loop end");
+ output.WriteLine();
}
}
}
diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs
index a7dd55e7c..a6ac04641 100644
--- a/ILSpy/MainWindow.xaml.cs
+++ b/ILSpy/MainWindow.xaml.cs
@@ -125,7 +125,10 @@ namespace ICSharpCode.ILSpy
MethodTreeNode node = treeView.SelectedItem as MethodTreeNode;
if (node != null && node.MethodDefinition.HasBody) {
node.MethodDefinition.Body.SimplifyMacros();
- ShowGraph(node.MethodDefinition.Name + "-cfg", ControlFlowGraphBuilder.Build(node.MethodDefinition.Body).ExportGraph());
+ var cfg = ControlFlowGraphBuilder.Build(node.MethodDefinition.Body);
+ cfg.ComputeDominance();
+ cfg.ComputeDominanceFrontier();
+ ShowGraph(node.MethodDefinition.Name + "-cfg", cfg.ExportGraph());
}
}