// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team // // Permission is hereby granted, free of charge, to any person obtaining a copy of this // software and associated documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons // to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or // substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. using System; using System.Collections.Generic; using System.Linq; using System.Threading; using Mono.Cecil.Cil; namespace ICSharpCode.Decompiler.FlowAnalysis { /// /// Detects the structure of the control flow (exception blocks and loops). /// public class ControlStructureDetector { public static ControlStructure DetectStructure(ControlFlowGraph g, IEnumerable exceptionHandlers, CancellationToken cancellationToken) { ControlStructure root = new ControlStructure(new HashSet(g.Nodes), g.EntryPoint, ControlStructureType.Root); // First build a structure tree out of the exception table DetectExceptionHandling(root, g, exceptionHandlers); // Then run the loop detection. DetectLoops(g, root, cancellationToken); return root; } #region Exception Handling static void DetectExceptionHandling(ControlStructure current, ControlFlowGraph g, IEnumerable exceptionHandlers) { // We rely on the fact that the exception handlers are sorted so that the innermost come first. // For each exception handler, we determine the nodes and substructures inside that handler, and move them into a new substructure. // This is always possible because exception handlers are guaranteed (by the CLR spec) to be properly nested and non-overlapping; // so they directly form the tree that we need. foreach (ExceptionHandler eh in exceptionHandlers) { var tryNodes = FindNodes(current, eh.TryStart, eh.TryEnd); current.Nodes.ExceptWith(tryNodes); ControlStructure tryBlock = new ControlStructure( tryNodes, 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) { var filterNodes = FindNodes(current, eh.FilterStart, eh.FilterEnd); current.Nodes.ExceptWith(filterNodes); ControlStructure filterBlock = new ControlStructure( filterNodes, g.Nodes.Single(n => n.Start == eh.HandlerStart), ControlStructureType.Filter); filterBlock.ExceptionHandler = eh; MoveControlStructures(current, filterBlock, eh.FilterStart, eh.FilterEnd); current.Children.Add(filterBlock); } var handlerNodes = FindNodes(current, eh.HandlerStart, eh.HandlerEnd); var handlerNode = current.Nodes.Single(n => n.ExceptionHandler == eh); handlerNodes.Add(handlerNode); if (handlerNode.EndFinallyOrFaultNode != null) handlerNodes.Add(handlerNode.EndFinallyOrFaultNode); current.Nodes.ExceptWith(handlerNodes); ControlStructure handlerBlock = new ControlStructure( handlerNodes, 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 FindNodes(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) { 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); target.AllNodes.UnionWith(child.AllNodes); } } } #endregion #region Loop Detection // Loop detection works like this: // We find a top-level loop by looking for its entry point, which is characterized by a node dominating its own predecessor. // Then we determine all other nodes that belong to such a loop (all nodes which lead to the entry point, and are dominated by it). // Finally, we check whether our result conforms with potential existing exception structures, and create the substructure for the loop if successful. // This algorithm is applied recursively for any substructures (both detected loops and exception blocks) static void DetectLoops(ControlFlowGraph g, ControlStructure current, CancellationToken cancellationToken) { g.ResetVisited(); cancellationToken.ThrowIfCancellationRequested(); FindLoops(current, current.EntryPoint); foreach (ControlStructure loop in current.Children) DetectLoops(g, loop, cancellationToken); } 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); List containedChildStructures = new List(); bool invalidNesting = false; foreach (ControlStructure childStructure in current.Children) { if (childStructure.AllNodes.IsSubsetOf(loopContents)) { containedChildStructures.Add(childStructure); } else if (childStructure.AllNodes.Intersect(loopContents).Any()) { invalidNesting = true; } } if (!invalidNesting) { current.Nodes.ExceptWith(loopContents); ControlStructure ctl = new ControlStructure(loopContents, node, ControlStructureType.Loop); foreach (ControlStructure childStructure in containedChildStructures) { ctl.Children.Add(childStructure); current.Children.Remove(childStructure); ctl.Nodes.ExceptWith(childStructure.AllNodes); } current.Children.Add(ctl); } } foreach (var edge in node.Outgoing) { FindLoops(current, edge.Target); } } static void FindLoopContents(ControlStructure current, HashSet loopContents, ControlFlowNode loopHead, ControlFlowNode node) { if (current.AllNodes.Contains(node) && loopHead.Dominates(node) && loopContents.Add(node)) { foreach (var edge in node.Incoming) { FindLoopContents(current, loopContents, loopHead, edge.Source); } } } #endregion } public enum ControlStructureType { /// /// The root block of the method /// Root, /// /// A nested control structure representing a loop. /// Loop, /// /// A nested control structure representing a try block. /// Try, /// /// A nested control structure representing a catch, finally, or fault block. /// Handler, /// /// A nested control structure representing an exception filter block. /// Filter } /// /// Represents the structure detected by the . /// /// This is a tree of ControlStructure nodes. Each node contains a set of CFG nodes, and every CFG node is contained in exactly one ControlStructure node. /// public class ControlStructure { public readonly ControlStructureType Type; public readonly List Children = new List(); /// /// The nodes in this control structure. /// public readonly HashSet Nodes; /// /// The nodes in this control structure and in all child control structures. /// public readonly HashSet AllNodes; /// /// The entry point of this control structure. /// public readonly ControlFlowNode EntryPoint; /// /// The exception handler associated with this Try,Handler or Finally structure. /// public ExceptionHandler ExceptionHandler; public ControlStructure(HashSet nodes, ControlFlowNode entryPoint, ControlStructureType type) { this.Nodes = nodes; this.EntryPoint = entryPoint; this.Type = type; this.AllNodes = new HashSet(nodes); } } }