PageRenderTime 30ms CodeModel.GetById 15ms app.highlight 12ms RepoModel.GetById 1ms app.codeStats 0ms

/ICSharpCode.Decompiler/FlowAnalysis/ControlStructureDetector.cs

http://github.com/icsharpcode/ILSpy
C# | 241 lines | 153 code | 20 blank | 68 comment | 30 complexity | d4b7e26b4f2127d3e9fc428f9788354b MD5 | raw file
  1// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
  2// 
  3// Permission is hereby granted, free of charge, to any person obtaining a copy of this
  4// software and associated documentation files (the "Software"), to deal in the Software
  5// without restriction, including without limitation the rights to use, copy, modify, merge,
  6// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
  7// to whom the Software is furnished to do so, subject to the following conditions:
  8// 
  9// The above copyright notice and this permission notice shall be included in all copies or
 10// substantial portions of the Software.
 11// 
 12// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 13// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 14// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
 15// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 16// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 17// DEALINGS IN THE SOFTWARE.
 18
 19using System;
 20using System.Collections.Generic;
 21using System.Diagnostics;
 22using System.Linq;
 23using System.Threading;
 24using Mono.Cecil.Cil;
 25
 26namespace ICSharpCode.Decompiler.FlowAnalysis
 27{
 28	/// <summary>
 29	/// Detects the structure of the control flow (exception blocks and loops).
 30	/// </summary>
 31	public class ControlStructureDetector
 32	{
 33		public static ControlStructure DetectStructure(ControlFlowGraph g, IEnumerable<ExceptionHandler> exceptionHandlers, CancellationToken cancellationToken)
 34		{
 35			ControlStructure root = new ControlStructure(new HashSet<ControlFlowNode>(g.Nodes), g.EntryPoint, ControlStructureType.Root);
 36			// First build a structure tree out of the exception table
 37			DetectExceptionHandling(root, g, exceptionHandlers);
 38			// Then run the loop detection.
 39			DetectLoops(g, root, cancellationToken);
 40			return root;
 41		}
 42		
 43		#region Exception Handling
 44		static void DetectExceptionHandling(ControlStructure current, ControlFlowGraph g, IEnumerable<ExceptionHandler> exceptionHandlers)
 45		{
 46			// We rely on the fact that the exception handlers are sorted so that the innermost come first.
 47			// For each exception handler, we determine the nodes and substructures inside that handler, and move them into a new substructure.
 48			// This is always possible because exception handlers are guaranteed (by the CLR spec) to be properly nested and non-overlapping;
 49			// so they directly form the tree that we need.
 50			foreach (ExceptionHandler eh in exceptionHandlers) {
 51				var tryNodes = FindNodes(current, eh.TryStart, eh.TryEnd);
 52				current.Nodes.ExceptWith(tryNodes);
 53				ControlStructure tryBlock = new ControlStructure(
 54					tryNodes,
 55					g.Nodes.Single(n => n.Start == eh.TryStart),
 56					ControlStructureType.Try);
 57				tryBlock.ExceptionHandler = eh;
 58				MoveControlStructures(current, tryBlock, eh.TryStart, eh.TryEnd);
 59				current.Children.Add(tryBlock);
 60				
 61				if (eh.FilterStart != null) {
 62					throw new NotSupportedException();
 63				}
 64				
 65				var handlerNodes = FindNodes(current, eh.HandlerStart, eh.HandlerEnd);
 66				var handlerNode = current.Nodes.Single(n => n.ExceptionHandler == eh);
 67				handlerNodes.Add(handlerNode);
 68				if (handlerNode.EndFinallyOrFaultNode != null)
 69					handlerNodes.Add(handlerNode.EndFinallyOrFaultNode);
 70				current.Nodes.ExceptWith(handlerNodes);
 71				ControlStructure handlerBlock = new ControlStructure(
 72					handlerNodes, handlerNode, ControlStructureType.Handler);
 73				handlerBlock.ExceptionHandler = eh;
 74				MoveControlStructures(current, handlerBlock, eh.HandlerStart, eh.HandlerEnd);
 75				current.Children.Add(handlerBlock);
 76			}
 77		}
 78		
 79		/// <summary>
 80		/// Removes all nodes from start to end (exclusive) from this ControlStructure and moves them to the target structure.
 81		/// </summary>
 82		static HashSet<ControlFlowNode> FindNodes(ControlStructure current, Instruction startInst, Instruction endInst)
 83		{
 84			HashSet<ControlFlowNode> result = new HashSet<ControlFlowNode>();
 85			int start = startInst.Offset;
 86			int end = endInst.Offset;
 87			foreach (var node in current.Nodes.ToArray()) {
 88				if (node.Start != null && start <= node.Start.Offset && node.Start.Offset < end) {
 89					result.Add(node);
 90				}
 91			}
 92			return result;
 93		}
 94		
 95		static void MoveControlStructures(ControlStructure current, ControlStructure target, Instruction startInst, Instruction endInst)
 96		{
 97			for (int i = 0; i < current.Children.Count; i++) {
 98				var child = current.Children[i];
 99				if (startInst.Offset <= child.EntryPoint.Offset && child.EntryPoint.Offset < endInst.Offset) {
100					current.Children.RemoveAt(i--);
101					target.Children.Add(child);
102					target.AllNodes.UnionWith(child.AllNodes);
103				}
104			}
105		}
106		#endregion
107		
108		#region Loop Detection
109		// Loop detection works like this:
110		// We find a top-level loop by looking for its entry point, which is characterized by a node dominating its own predecessor.
111		// 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).
112		// Finally, we check whether our result conforms with potential existing exception structures, and create the substructure for the loop if successful.
113		
114		// This algorithm is applied recursively for any substructures (both detected loops and exception blocks)
115		
116		// But maybe we should get rid of this complex stuff and instead treat every backward jump as a loop?
117		// That should still work with the IL produced by compilers, and has the advantage that the detected loop bodies are consecutive IL regions.
118		
119		static void DetectLoops(ControlFlowGraph g, ControlStructure current, CancellationToken cancellationToken)
120		{
121			if (!current.EntryPoint.IsReachable)
122				return;
123			g.ResetVisited();
124			cancellationToken.ThrowIfCancellationRequested();
125			FindLoops(current, current.EntryPoint);
126			foreach (ControlStructure loop in current.Children)
127				DetectLoops(g, loop, cancellationToken);
128		}
129		
130		static void FindLoops(ControlStructure current, ControlFlowNode node)
131		{
132			if (node.Visited)
133				return;
134			node.Visited = true;
135			if (current.Nodes.Contains(node)
136			    && node.DominanceFrontier.Contains(node)
137			    && !(node == current.EntryPoint && current.Type == ControlStructureType.Loop))
138			{
139				HashSet<ControlFlowNode> loopContents = new HashSet<ControlFlowNode>();
140				FindLoopContents(current, loopContents, node, node);
141				List<ControlStructure> containedChildStructures = new List<ControlStructure>();
142				bool invalidNesting = false;
143				foreach (ControlStructure childStructure in current.Children) {
144					if (childStructure.AllNodes.IsSubsetOf(loopContents)) {
145						containedChildStructures.Add(childStructure);
146					} else if (childStructure.AllNodes.Intersect(loopContents).Any()) {
147						invalidNesting = true;
148					}
149				}
150				if (!invalidNesting) {
151					current.Nodes.ExceptWith(loopContents);
152					ControlStructure ctl = new ControlStructure(loopContents, node, ControlStructureType.Loop);
153					foreach (ControlStructure childStructure in containedChildStructures) {
154						ctl.Children.Add(childStructure);
155						current.Children.Remove(childStructure);
156						ctl.Nodes.ExceptWith(childStructure.AllNodes);
157					}
158					current.Children.Add(ctl);
159				}
160			}
161			foreach (var edge in node.Outgoing) {
162				FindLoops(current, edge.Target);
163			}
164		}
165		
166		static void FindLoopContents(ControlStructure current, HashSet<ControlFlowNode> loopContents, ControlFlowNode loopHead, ControlFlowNode node)
167		{
168			if (current.AllNodes.Contains(node) && loopHead.Dominates(node) && loopContents.Add(node)) {
169				foreach (var edge in node.Incoming) {
170					FindLoopContents(current, loopContents, loopHead, edge.Source);
171				}
172			}
173		}
174		#endregion
175	}
176	
177	public enum ControlStructureType
178	{
179		/// <summary>
180		/// The root block of the method
181		/// </summary>
182		Root,
183		/// <summary>
184		/// A nested control structure representing a loop.
185		/// </summary>
186		Loop,
187		/// <summary>
188		/// A nested control structure representing a try block.
189		/// </summary>
190		Try,
191		/// <summary>
192		/// A nested control structure representing a catch, finally, or fault block.
193		/// </summary>
194		Handler,
195		/// <summary>
196		/// A nested control structure representing an exception filter block.
197		/// </summary>
198		Filter
199	}
200	
201	/// <summary>
202	/// Represents the structure detected by the <see cref="ControlStructureDetector"/>.
203	/// 
204	/// 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.
205	/// </summary>
206	public class ControlStructure
207	{
208		public readonly ControlStructureType Type;
209		public readonly List<ControlStructure> Children = new List<ControlStructure>();
210		
211		/// <summary>
212		/// The nodes in this control structure.
213		/// </summary>
214		public readonly HashSet<ControlFlowNode> Nodes;
215		
216		/// <summary>
217		/// The nodes in this control structure and in all child control structures.
218		/// </summary>
219		public readonly HashSet<ControlFlowNode> AllNodes;
220		
221		/// <summary>
222		/// The entry point of this control structure.
223		/// </summary>
224		public readonly ControlFlowNode EntryPoint;
225		
226		/// <summary>
227		/// The exception handler associated with this Try,Handler or Finally structure.
228		/// </summary>
229		public ExceptionHandler ExceptionHandler;
230		
231		public ControlStructure(HashSet<ControlFlowNode> nodes, ControlFlowNode entryPoint, ControlStructureType type)
232		{
233			if (nodes == null)
234				throw new ArgumentNullException("nodes");
235			this.Nodes = nodes;
236			this.EntryPoint = entryPoint;
237			this.Type = type;
238			this.AllNodes = new HashSet<ControlFlowNode>(nodes);
239		}
240	}
241}