mirror of https://github.com/icsharpcode/ILSpy.git
				
				
			
			You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							169 lines
						
					
					
						
							6.1 KiB
						
					
					
				
			
		
		
	
	
							169 lines
						
					
					
						
							6.1 KiB
						
					
					
				using System; | 
						|
using System.Collections.Generic; | 
						|
using System.Diagnostics; | 
						|
using System.Linq; | 
						|
using System.Text; | 
						|
using System.Threading; | 
						|
using System.Threading.Tasks; | 
						|
using ICSharpCode.Decompiler.FlowAnalysis; | 
						|
using ICSharpCode.Decompiler.Util; | 
						|
 | 
						|
namespace ICSharpCode.Decompiler.IL.ControlFlow | 
						|
{ | 
						|
	/// <summary> | 
						|
	/// Holds the control flow graph. | 
						|
	/// A separate graph is computed for each BlockContainer at the start of the block transforms | 
						|
	/// (before loop detection). | 
						|
	/// </summary> | 
						|
	public class ControlFlowGraph | 
						|
	{ | 
						|
		readonly BlockContainer container; | 
						|
 | 
						|
		/// <summary> | 
						|
		/// The container for which the ControlFlowGraph was created. | 
						|
		///  | 
						|
		/// This may differ from the container currently holding a block, | 
						|
		/// because a transform could have moved the block since the CFG was created. | 
						|
		/// </summary> | 
						|
		public BlockContainer Container { get { return container; } } | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Nodes array, indexed by original block index. | 
						|
		///  | 
						|
		/// Originally <c>cfg[i].UserData == container.Blocks[i]</c>, | 
						|
		/// but the ILAst blocks may be moved/reordered by transforms. | 
						|
		/// </summary> | 
						|
		internal readonly ControlFlowNode[] cfg; | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Dictionary from Block to ControlFlowNode. | 
						|
		/// Unlike the cfg array, this can be used to discover control flow nodes even after | 
						|
		/// blocks were moved/reordered by transforms. | 
						|
		/// </summary> | 
						|
		readonly Dictionary<Block, ControlFlowNode> dict = new Dictionary<Block, ControlFlowNode>(); | 
						|
 | 
						|
		/// <summary> | 
						|
		/// nodeHasDirectExitOutOfContainer[i] == true iff cfg[i] directly contains a | 
						|
		/// branch/leave instruction leaving the <c>container</c>. | 
						|
		/// </summary> | 
						|
		readonly BitSet nodeHasDirectExitOutOfContainer; | 
						|
 | 
						|
		/// <summary> | 
						|
		/// nodeHasReachableExit[i] == true iff there is a path from cfg[i] to a node not dominated by cfg[i], | 
						|
		/// or if there is a path from cfg[i] to a branch/leave instruction leaving the <c>container</c>. | 
						|
		/// </summary> | 
						|
		readonly BitSet nodeHasReachableExit; | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Constructs a control flow graph for the blocks in the given block container. | 
						|
		///  | 
						|
		/// Return statements, exceptions, or branches leaving the block container are not | 
						|
		/// modeled by the control flow graph. | 
						|
		/// </summary> | 
						|
		public ControlFlowGraph(BlockContainer container, CancellationToken cancellationToken = default(CancellationToken)) | 
						|
		{ | 
						|
			this.container = container; | 
						|
			this.cfg = new ControlFlowNode[container.Blocks.Count]; | 
						|
			this.nodeHasDirectExitOutOfContainer = new BitSet(cfg.Length); | 
						|
			for (int i = 0; i < cfg.Length; i++) { | 
						|
				Block block = container.Blocks[i]; | 
						|
				cfg[i] = new ControlFlowNode { UserIndex = i, UserData = block }; | 
						|
				dict.Add(block, cfg[i]); | 
						|
			} | 
						|
 | 
						|
			CreateEdges(cancellationToken); | 
						|
			Dominance.ComputeDominance(cfg[0], cancellationToken); | 
						|
			this.nodeHasReachableExit = Dominance.MarkNodesWithReachableExits(cfg); | 
						|
			this.nodeHasReachableExit.UnionWith(FindNodesWithExitsOutOfContainer()); | 
						|
		} | 
						|
 | 
						|
		void CreateEdges(CancellationToken cancellationToken) | 
						|
		{ | 
						|
			for (int i = 0; i < container.Blocks.Count; i++) { | 
						|
				cancellationToken.ThrowIfCancellationRequested(); | 
						|
				var block = container.Blocks[i]; | 
						|
				var sourceNode = cfg[i]; | 
						|
				foreach (var node in block.Descendants) { | 
						|
					if (node is Branch branch) { | 
						|
						if (branch.TargetBlock.Parent == container) { | 
						|
							sourceNode.AddEdgeTo(cfg[container.Blocks.IndexOf(branch.TargetBlock)]); | 
						|
						} else if (branch.TargetBlock.IsDescendantOf(container)) { | 
						|
							// Internal control flow within a nested container. | 
						|
						} else { | 
						|
							// Branch out of this container into a parent container. | 
						|
							// Like return statements and exceptional exits, | 
						|
							// we ignore this for the CFG and the dominance calculation. | 
						|
							// However, it's relevant for HasReachableExit(). | 
						|
							nodeHasDirectExitOutOfContainer.Set(i); | 
						|
						} | 
						|
					} else if (node is Leave leave && !leave.TargetContainer.IsDescendantOf(block)) { | 
						|
						// Leave instructions (like other exits out of the container) | 
						|
						// are ignored for the CFG and dominance, | 
						|
						// but is relevant for HasReachableExit(). | 
						|
						// However, a 'leave' that exits the whole function represents a return, | 
						|
						// and is not considered a reachable exit. | 
						|
						if (!leave.IsLeavingFunction) { | 
						|
							nodeHasDirectExitOutOfContainer.Set(i); | 
						|
						} | 
						|
					} | 
						|
				} | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		BitSet FindNodesWithExitsOutOfContainer() | 
						|
		{ | 
						|
			// Also mark the nodes that exit the block container altogether. | 
						|
			// Invariant: leaving[n.UserIndex] == true implies leaving[n.ImmediateDominator.UserIndex] == true | 
						|
			var leaving = new BitSet(cfg.Length); | 
						|
			foreach (var node in cfg) { | 
						|
				if (leaving[node.UserIndex]) | 
						|
					continue; | 
						|
				if (nodeHasDirectExitOutOfContainer[node.UserIndex]) { | 
						|
					for (ControlFlowNode p = node; p != null; p = p.ImmediateDominator) { | 
						|
						if (leaving[p.UserIndex]) { | 
						|
							// we can stop marking when we've reached an already-marked node | 
						|
							break; | 
						|
						} | 
						|
						leaving.Set(p.UserIndex); | 
						|
					} | 
						|
				} | 
						|
			} | 
						|
			return leaving; | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Gets the ControlFlowNode for the block. | 
						|
		///  | 
						|
		/// Precondition: the block belonged to the <c>container</c> at the start of the block transforms | 
						|
		/// (when the control flow graph was created). | 
						|
		/// </summary> | 
						|
		public ControlFlowNode GetNode(Block block) | 
						|
		{ | 
						|
			return dict[block]; | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Returns true iff there is a control flow path from <c>node</c> to one of the following: | 
						|
		///  * branch or leave instruction leaving <c>this.Container</c> | 
						|
		///  * branch instruction within this container to another node that is not dominated by <c>node</c>. | 
						|
		///  | 
						|
		/// If this function returns false, the only way control flow can leave the set of nodes | 
						|
		/// dominated by <c>node</c> is by executing a <c>return</c> or <c>throw</c> instruction. | 
						|
		/// </summary> | 
						|
		public bool HasReachableExit(ControlFlowNode node) | 
						|
		{ | 
						|
			Debug.Assert(cfg[node.UserIndex] == node); | 
						|
			return nodeHasReachableExit[node.UserIndex]; | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Gets whether the control flow node directly contains a branch/leave instruction | 
						|
		/// exiting the container. | 
						|
		/// </summary> | 
						|
		public bool HasDirectExitOutOfContainer(ControlFlowNode node) | 
						|
		{ | 
						|
			Debug.Assert(cfg[node.UserIndex] == node); | 
						|
			return nodeHasDirectExitOutOfContainer[node.UserIndex]; | 
						|
		} | 
						|
	} | 
						|
}
 | 
						|
 |