From 807a345d08bfd95cdc5e472c7d66398029fafa57 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 5 Feb 2011 13:37:52 +0100 Subject: [PATCH] Show exception handlers in IL assembly. --- .../DominanceLoopDetector.cs | 80 --------- .../FlowAnalysis/ControlFlowGraphBuilder.cs | 32 +++- .../FlowAnalysis/ControlStructureDetector.cs | 154 ++++++++++++++++++ .../ICSharpCode.Decompiler.csproj | 2 +- ILSpy/Disassembler/ILLanguage.cs | 72 ++++++-- ILSpy/ExtensionMethods.cs | 4 +- ILSpy/ITextOutput.cs | 7 +- 7 files changed, 246 insertions(+), 105 deletions(-) delete mode 100644 ICSharpCode.Decompiler/DominanceLoopDetector.cs create mode 100644 ICSharpCode.Decompiler/FlowAnalysis/ControlStructureDetector.cs diff --git a/ICSharpCode.Decompiler/DominanceLoopDetector.cs b/ICSharpCode.Decompiler/DominanceLoopDetector.cs deleted file mode 100644 index cda145b21..000000000 --- a/ICSharpCode.Decompiler/DominanceLoopDetector.cs +++ /dev/null @@ -1,80 +0,0 @@ -// -// -// -// -// $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/ControlFlowGraphBuilder.cs b/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraphBuilder.cs index ba2e8dadb..0e2c9ee0b 100644 --- a/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraphBuilder.cs +++ b/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraphBuilder.cs @@ -98,6 +98,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis { for (int i = 0; i < methodBody.Instructions.Count; i++) { Instruction blockStart = methodBody.Instructions[i]; + ExceptionHandler blockStartEH = FindInnermostExceptionHandler(blockStart.Offset); // try and see how big we can make that block: for (; i + 1 < methodBody.Instructions.Count; i++) { Instruction inst = methodBody.Instructions[i]; @@ -105,6 +106,12 @@ namespace ICSharpCode.Decompiler.FlowAnalysis break; if (hasIncomingJumps[i + 1]) break; + if (inst.Next != null) { + // ensure that blocks never contain instructions from different try blocks + ExceptionHandler instEH = FindInnermostExceptionHandler(inst.Next.Offset); + if (instEH != blockStartEH) + break; + } } nodes.Add(new ControlFlowNode(nodes.Count, blockStart, methodBody.Instructions[i])); @@ -170,17 +177,17 @@ namespace ICSharpCode.Decompiler.FlowAnalysis { foreach (ControlFlowNode node in nodes) { if (node.End != null && CanThrowException(node.End.OpCode)) { - CreateEdge(node, FindInnermostExceptionHandler(node.End.Offset), JumpType.JumpToExceptionHandler); + CreateEdge(node, FindInnermostExceptionHandlerNode(node.End.Offset), JumpType.JumpToExceptionHandler); } if (node.ExceptionHandler != null) { if (node.EndFinallyOrFaultNode != null) { // For Fault and Finally blocks, create edge from "EndFinally" to next exception handler. // This represents the exception bubbling up after finally block was executed. - CreateEdge(node.EndFinallyOrFaultNode, FindInnermostExceptionHandler(node.ExceptionHandler.HandlerEnd.Offset), JumpType.JumpToExceptionHandler); + CreateEdge(node.EndFinallyOrFaultNode, FindInnermostExceptionHandlerNode(node.ExceptionHandler.HandlerEnd.Offset), JumpType.JumpToExceptionHandler); } else { // For Catch blocks, create edge from "CatchHandler" block (at beginning) to next exception handler. // This represents the exception bubbling up because it did not match the type of the catch block. - CreateEdge(node, FindInnermostExceptionHandler(node.ExceptionHandler.HandlerStart.Offset), JumpType.JumpToExceptionHandler); + CreateEdge(node, FindInnermostExceptionHandlerNode(node.ExceptionHandler.HandlerStart.Offset), JumpType.JumpToExceptionHandler); } CreateEdge(node, node.ExceptionHandler.HandlerStart, JumpType.Normal); } @@ -200,14 +207,23 @@ namespace ICSharpCode.Decompiler.FlowAnalysis } } - ControlFlowNode FindInnermostExceptionHandler(int instructionOffsetInTryBlock) + ExceptionHandler FindInnermostExceptionHandler(int instructionOffsetInTryBlock) { foreach (ExceptionHandler h in methodBody.ExceptionHandlers) { if (h.TryStart.Offset <= instructionOffsetInTryBlock && instructionOffsetInTryBlock < h.TryEnd.Offset) { - return nodes.Single(n => n.ExceptionHandler == h && n.CopyFrom == null); + return h; } } - return exceptionalExit; + return null; + } + + ControlFlowNode FindInnermostExceptionHandlerNode(int instructionOffsetInTryBlock) + { + ExceptionHandler h = FindInnermostExceptionHandler(instructionOffsetInTryBlock); + if (h != null) + return nodes.Single(n => n.ExceptionHandler == h && n.CopyFrom == null); + else + return exceptionalExit; } ControlFlowNode FindInnermostHandlerBlock(int instructionOffset) @@ -236,7 +252,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis target.Incoming.Remove(node.Outgoing[0]); node.Outgoing.Clear(); - ControlFlowNode handler = FindInnermostExceptionHandler(node.End.Offset); + ControlFlowNode handler = FindInnermostExceptionHandlerNode(node.End.Offset); Debug.Assert(handler.NodeType == ControlFlowNodeType.FinallyOrFaultHandler); ControlFlowNode copy = CopyFinallySubGraph(handler, handler.EndFinallyOrFaultNode, target); @@ -258,7 +274,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis target.Incoming.Remove(node.Outgoing[0]); node.Outgoing.Clear(); - ControlFlowNode handler = FindInnermostExceptionHandler(node.End.Offset); + ControlFlowNode handler = FindInnermostExceptionHandlerNode(node.End.Offset); Debug.Assert(handler.NodeType == ControlFlowNodeType.FinallyOrFaultHandler); CreateEdge(node, handler, JumpType.Normal); diff --git a/ICSharpCode.Decompiler/FlowAnalysis/ControlStructureDetector.cs b/ICSharpCode.Decompiler/FlowAnalysis/ControlStructureDetector.cs new file mode 100644 index 000000000..f5b51850a --- /dev/null +++ b/ICSharpCode.Decompiler/FlowAnalysis/ControlStructureDetector.cs @@ -0,0 +1,154 @@ +// +// +// +// +// $Revision$ +// +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using ICSharpCode.Decompiler.FlowAnalysis; +using Mono.Cecil.Cil; + +namespace ICSharpCode.Decompiler.FlowAnalysis +{ + /// + /// Description of DominanceLoopDetector. + /// + public class ControlStructureDetector + { + public static ControlStructure DetectStructure(ControlFlowGraph g, IEnumerable exceptionHandlers) + { + ControlStructure root = new ControlStructure(new HashSet(g.Nodes), g.EntryPoint, ControlStructureType.Root); + DetectExceptionHandling(root, g, exceptionHandlers); + DetectLoops(g, root); + g.ResetVisited(); + return root; + } + + #region Exception Handling + static void DetectExceptionHandling(ControlStructure current, ControlFlowGraph g, IEnumerable exceptionHandlers) + { + foreach (ExceptionHandler eh in exceptionHandlers) { + ControlStructure tryBlock = new ControlStructure( + FindAndRemoveNodes(current, eh.TryStart, eh.TryEnd), + g.Nodes.Single(n => n.Start == eh.TryStart), + ControlStructureType.Try); + tryBlock.ExceptionHandler = eh; + MoveControlStructures(current, tryBlock, eh.TryStart, eh.TryEnd); + current.Children.Add(tryBlock); + + if (eh.FilterStart != null) { + ControlStructure filterBlock = new ControlStructure( + FindAndRemoveNodes(current, eh.HandlerStart, eh.HandlerEnd), + g.Nodes.Single(n => n.Start == eh.HandlerStart), + ControlStructureType.Filter); + filterBlock.ExceptionHandler = eh; + MoveControlStructures(current, filterBlock, eh.FilterStart, eh.FilterEnd); + current.Children.Add(filterBlock); + } + + ControlStructure handlerBlock = new ControlStructure( + FindAndRemoveNodes(current, eh.HandlerStart, eh.HandlerEnd), + g.Nodes.Single(n => n.Start == eh.HandlerStart), + ControlStructureType.Handler); + handlerBlock.ExceptionHandler = eh; + MoveControlStructures(current, handlerBlock, eh.HandlerStart, eh.HandlerEnd); + current.Children.Add(handlerBlock); + } + } + + /// + /// Removes all nodes from start to end (exclusive) from this ControlStructure and moves them to the target structure. + /// + static HashSet FindAndRemoveNodes(ControlStructure current, Instruction startInst, Instruction endInst) + { + HashSet result = new HashSet(); + int start = startInst.Offset; + int end = endInst.Offset; + foreach (var node in current.Nodes.ToArray()) { + if (node.Start != null && node.Start.Offset >= start && node.Start.Offset < end) { + current.Nodes.Remove(node); + result.Add(node); + } + } + return result; + } + + static void MoveControlStructures(ControlStructure current, ControlStructure target, Instruction startInst, Instruction endInst) + { + for (int i = 0; i < current.Children.Count; i++) { + var child = current.Children[i]; + if (child.EntryPoint.Start.Offset >= startInst.Offset && child.EntryPoint.Start.Offset <= endInst.Offset) { + current.Children.RemoveAt(i--); + target.Children.Add(child); + } + } + } + #endregion + + #region Loop Detection + 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 && current.Type == ControlStructureType.Loop)) + { + HashSet loopContents = new HashSet(); + FindLoopContents(current, loopContents, node, node); + current.Nodes.ExceptWith(loopContents); + current.Children.Add(new ControlStructure(loopContents, node, ControlStructureType.Loop)); + } + 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); + } + } + } + #endregion + } + + public enum ControlStructureType + { + Root, + Loop, + Try, + Handler, + Filter + } + + public class ControlStructure + { + public readonly ControlStructureType Type; + public readonly List Children = new List(); + public readonly HashSet Nodes; + public readonly ControlFlowNode EntryPoint; + public ExceptionHandler ExceptionHandler; + + public ControlStructure(HashSet nodes, ControlFlowNode entryPoint, ControlStructureType type) + { + this.Nodes = nodes; + this.EntryPoint = entryPoint; + this.Type = type; + } + } +} diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 3f688ed02..bac2a5dcb 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -50,11 +50,11 @@ - + diff --git a/ILSpy/Disassembler/ILLanguage.cs b/ILSpy/Disassembler/ILLanguage.cs index 4c22f2c0a..5027a4886 100644 --- a/ILSpy/Disassembler/ILLanguage.cs +++ b/ILSpy/Disassembler/ILLanguage.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.ComponentModel; using System.Linq; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.FlowAnalysis; @@ -35,10 +36,8 @@ namespace ICSharpCode.ILSpy.Disassembler public override void Decompile(MethodDefinition method, ITextOutput output) { - output.WriteComment("// Method begins at RVA 0x{0:x4}", method.RVA); - output.WriteLine(); - output.WriteComment("// Code size {0} (0x{0:x})", method.Body.CodeSize); - output.WriteLine(); + output.WriteCommentLine("// Method begins at RVA 0x{0:x4}", method.RVA); + output.WriteCommentLine("// Code size {0} (0x{0:x})", method.Body.CodeSize); output.WriteLine(".maxstack {0}", method.Body.MaxStackSize); if (method.DeclaringType.Module.Assembly.EntryPoint == method) output.WriteLine (".entrypoint"); @@ -58,13 +57,14 @@ namespace ICSharpCode.ILSpy.Disassembler output.Unindent(); output.WriteLine(")"); } + output.WriteLine(); if (detectControlStructure) { method.Body.SimplifyMacros(); var cfg = ControlFlowGraphBuilder.Build(method.Body); cfg.ComputeDominance(); cfg.ComputeDominanceFrontier(); - var s = DominanceLoopDetector.DetectLoops(cfg); + var s = ControlStructureDetector.DetectStructure(cfg, method.Body.ExceptionHandlers); WriteStructure(output, s); } else { foreach (var inst in method.Body.Instructions) { @@ -81,9 +81,43 @@ namespace ICSharpCode.ILSpy.Disassembler void WriteStructure(ITextOutput output, ControlStructure s) { - output.WriteComment("// loop start"); - output.WriteLine(); - output.Indent(); + if (s.Type != ControlStructureType.Root) { + switch (s.Type) { + case ControlStructureType.Loop: + output.WriteCommentLine("// loop start"); + break; + case ControlStructureType.Try: + output.WriteLine(".try {"); + break; + case ControlStructureType.Handler: + switch (s.ExceptionHandler.HandlerType) { + case Mono.Cecil.Cil.ExceptionHandlerType.Catch: + case Mono.Cecil.Cil.ExceptionHandlerType.Filter: + output.Write("catch"); + if (s.ExceptionHandler.CatchType != null) { + output.Write(' '); + s.ExceptionHandler.CatchType.WriteTo(output); + } + output.WriteLine(" {"); + break; + case Mono.Cecil.Cil.ExceptionHandlerType.Finally: + output.WriteLine("finally {"); + break; + case Mono.Cecil.Cil.ExceptionHandlerType.Fault: + output.WriteLine("fault {"); + break; + default: + throw new NotSupportedException(); + } + break; + case ControlStructureType.Filter: + output.WriteLine("filter {"); + break; + default: + throw new NotSupportedException(); + } + 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) { @@ -94,9 +128,25 @@ namespace ICSharpCode.ILSpy.Disassembler WriteStructure(output, s.Children.Single(c => c.EntryPoint == node)); } } - output.Unindent(); - output.WriteComment("// loop end"); - output.WriteLine(); + if (s.Type != ControlStructureType.Root) { + output.Unindent(); + switch (s.Type) { + case ControlStructureType.Loop: + output.WriteCommentLine("// end loop"); + break; + case ControlStructureType.Try: + output.WriteLine("} // end .try"); + break; + case ControlStructureType.Handler: + output.WriteLine("} // end handler"); + break; + case ControlStructureType.Filter: + output.WriteLine("} // end filter"); + break; + default: + throw new NotSupportedException(); + } + } } } } diff --git a/ILSpy/ExtensionMethods.cs b/ILSpy/ExtensionMethods.cs index d871713f5..bcaa23ac5 100644 --- a/ILSpy/ExtensionMethods.cs +++ b/ILSpy/ExtensionMethods.cs @@ -50,9 +50,9 @@ namespace ICSharpCode.ILSpy output.WriteLine(string.Format(format, args)); } - public static void WriteComment(this ITextOutput output, string format, params object[] args) + public static void WriteCommentLine(this ITextOutput output, string format, params object[] args) { - output.WriteComment(string.Format(format, args)); + output.WriteCommentLine(string.Format(format, args)); } /// diff --git a/ILSpy/ITextOutput.cs b/ILSpy/ITextOutput.cs index 18e0eb402..1d1da660d 100644 --- a/ILSpy/ITextOutput.cs +++ b/ILSpy/ITextOutput.cs @@ -29,7 +29,7 @@ namespace ICSharpCode.ILSpy void Unindent(); void Write(char ch); void Write(string text); - void WriteComment(string comment); + void WriteCommentLine(string comment); void WriteLine(); void WriteDefinition(string text, object definition); void WriteReference(string text, object reference); @@ -101,10 +101,11 @@ namespace ICSharpCode.ILSpy b.Append(text); } - public void WriteComment(string comment) + public void WriteCommentLine(string comment) { WriteIndent(); - b.Append(comment); + b.AppendLine(comment); + needsIndent = true; } public void WriteLine()