summaryrefslogtreecommitdiff
path: root/ICSharpCode.Decompiler/FlowAnalysis/ControlStructureDetector.cs
diff options
context:
space:
mode:
Diffstat (limited to 'ICSharpCode.Decompiler/FlowAnalysis/ControlStructureDetector.cs')
-rw-r--r--ICSharpCode.Decompiler/FlowAnalysis/ControlStructureDetector.cs241
1 files changed, 241 insertions, 0 deletions
diff --git a/ICSharpCode.Decompiler/FlowAnalysis/ControlStructureDetector.cs b/ICSharpCode.Decompiler/FlowAnalysis/ControlStructureDetector.cs
new file mode 100644
index 00000000..b7b77e07
--- /dev/null
+++ b/ICSharpCode.Decompiler/FlowAnalysis/ControlStructureDetector.cs
@@ -0,0 +1,241 @@
+// 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.Diagnostics;
+using System.Linq;
+using System.Threading;
+using Mono.Cecil.Cil;
+
+namespace ICSharpCode.Decompiler.FlowAnalysis
+{
+ /// <summary>
+ /// Detects the structure of the control flow (exception blocks and loops).
+ /// </summary>
+ public class ControlStructureDetector
+ {
+ public static ControlStructure DetectStructure(ControlFlowGraph g, IEnumerable<ExceptionHandler> exceptionHandlers, CancellationToken cancellationToken)
+ {
+ ControlStructure root = new ControlStructure(new HashSet<ControlFlowNode>(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<ExceptionHandler> 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) {
+ throw new NotSupportedException();
+ }
+
+ 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, handlerNode, ControlStructureType.Handler);
+ handlerBlock.ExceptionHandler = eh;
+ MoveControlStructures(current, handlerBlock, eh.HandlerStart, eh.HandlerEnd);
+ current.Children.Add(handlerBlock);
+ }
+ }
+
+ /// <summary>
+ /// Removes all nodes from start to end (exclusive) from this ControlStructure and moves them to the target structure.
+ /// </summary>
+ static HashSet<ControlFlowNode> FindNodes(ControlStructure current, Instruction startInst, Instruction endInst)
+ {
+ HashSet<ControlFlowNode> result = new HashSet<ControlFlowNode>();
+ int start = startInst.Offset;
+ int end = endInst.Offset;
+ foreach (var node in current.Nodes.ToArray()) {
+ if (node.Start != null && start <= node.Start.Offset && 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 (startInst.Offset <= child.EntryPoint.Offset && child.EntryPoint.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)
+
+ // But maybe we should get rid of this complex stuff and instead treat every backward jump as a loop?
+ // That should still work with the IL produced by compilers, and has the advantage that the detected loop bodies are consecutive IL regions.
+
+ static void DetectLoops(ControlFlowGraph g, ControlStructure current, CancellationToken cancellationToken)
+ {
+ if (!current.EntryPoint.IsReachable)
+ return;
+ 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<ControlFlowNode> loopContents = new HashSet<ControlFlowNode>();
+ FindLoopContents(current, loopContents, node, node);
+ List<ControlStructure> containedChildStructures = new List<ControlStructure>();
+ 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<ControlFlowNode> 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
+ {
+ /// <summary>
+ /// The root block of the method
+ /// </summary>
+ Root,
+ /// <summary>
+ /// A nested control structure representing a loop.
+ /// </summary>
+ Loop,
+ /// <summary>
+ /// A nested control structure representing a try block.
+ /// </summary>
+ Try,
+ /// <summary>
+ /// A nested control structure representing a catch, finally, or fault block.
+ /// </summary>
+ Handler,
+ /// <summary>
+ /// A nested control structure representing an exception filter block.
+ /// </summary>
+ Filter
+ }
+
+ /// <summary>
+ /// Represents the structure detected by the <see cref="ControlStructureDetector"/>.
+ ///
+ /// 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.
+ /// </summary>
+ public class ControlStructure
+ {
+ public readonly ControlStructureType Type;
+ public readonly List<ControlStructure> Children = new List<ControlStructure>();
+
+ /// <summary>
+ /// The nodes in this control structure.
+ /// </summary>
+ public readonly HashSet<ControlFlowNode> Nodes;
+
+ /// <summary>
+ /// The nodes in this control structure and in all child control structures.
+ /// </summary>
+ public readonly HashSet<ControlFlowNode> AllNodes;
+
+ /// <summary>
+ /// The entry point of this control structure.
+ /// </summary>
+ public readonly ControlFlowNode EntryPoint;
+
+ /// <summary>
+ /// The exception handler associated with this Try,Handler or Finally structure.
+ /// </summary>
+ public ExceptionHandler ExceptionHandler;
+
+ public ControlStructure(HashSet<ControlFlowNode> nodes, ControlFlowNode entryPoint, ControlStructureType type)
+ {
+ if (nodes == null)
+ throw new ArgumentNullException("nodes");
+ Nodes = nodes;
+ EntryPoint = entryPoint;
+ Type = type;
+ AllNodes = new HashSet<ControlFlowNode>(nodes);
+ }
+ }
+}