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.
245 lines
9.6 KiB
245 lines
9.6 KiB
// 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.IO; |
|
using System.Linq; |
|
using System.Threading; |
|
using ICSharpCode.Decompiler.FlowAnalysis; |
|
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) { |
|
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); |
|
} |
|
} |
|
|
|
/// <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 && 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<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) |
|
{ |
|
this.Nodes = nodes; |
|
this.EntryPoint = entryPoint; |
|
this.Type = type; |
|
this.AllNodes = new HashSet<ControlFlowNode>(nodes); |
|
} |
|
} |
|
}
|
|
|