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()); } }