summaryrefslogtreecommitdiff
path: root/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraphBuilder.cs
diff options
context:
space:
mode:
authorStephane Delcroix <stephane@delcroix.org>2016-12-05 13:31:31 +0100
committerGitHub <noreply@github.com>2016-12-05 13:31:31 +0100
commit1a5bead2f2e24cc16da23753eaf0882d38d54ea1 (patch)
tree2fb4bf607ca11d9ed5163ed329796ca651054a6a /ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraphBuilder.cs
parent3692786c3a0f9ba01ffe9516caa624a018ac885a (diff)
downloadxamarin-forms-1a5bead2f2e24cc16da23753eaf0882d38d54ea1.tar.gz
xamarin-forms-1a5bead2f2e24cc16da23753eaf0882d38d54ea1.tar.bz2
xamarin-forms-1a5bead2f2e24cc16da23753eaf0882d38d54ea1.zip
[XamlC] drop ICSharpCode.Decompiler (#586)
* [XamlC] drop ICSharpCode.Decompiler * update nuspec * fix typo
Diffstat (limited to 'ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraphBuilder.cs')
-rw-r--r--ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraphBuilder.cs439
1 files changed, 0 insertions, 439 deletions
diff --git a/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraphBuilder.cs b/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraphBuilder.cs
deleted file mode 100644
index 7570884a..00000000
--- a/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraphBuilder.cs
+++ /dev/null
@@ -1,439 +0,0 @@
-// 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 Mono.Cecil.Cil;
-
-namespace ICSharpCode.Decompiler.FlowAnalysis
-{
- /// <summary>
- /// Constructs the Control Flow Graph from a Cecil method body.
- /// </summary>
- public sealed class ControlFlowGraphBuilder
- {
- public static ControlFlowGraph Build(MethodBody methodBody)
- {
- return new ControlFlowGraphBuilder(methodBody).Build();
- }
-
- // This option controls how finally blocks are handled:
- // false means that the endfinally instruction will jump to any of the leave targets (EndFinally edge type).
- // true means that a copy of the whole finally block is created for each leave target. In this case, each endfinally node will be connected with the leave
- // target using a normal edge.
- bool copyFinallyBlocks = false;
-
- MethodBody methodBody;
- int[] offsets; // array index = instruction index; value = IL offset
- bool[] hasIncomingJumps; // array index = instruction index
- List<ControlFlowNode> nodes = new List<ControlFlowNode>();
- ControlFlowNode entryPoint;
- ControlFlowNode regularExit;
- ControlFlowNode exceptionalExit;
-
- ControlFlowGraphBuilder(MethodBody methodBody)
- {
- this.methodBody = methodBody;
- offsets = methodBody.Instructions.Select(i => i.Offset).ToArray();
- hasIncomingJumps = new bool[methodBody.Instructions.Count];
-
- entryPoint = new ControlFlowNode(0, 0, ControlFlowNodeType.EntryPoint);
- nodes.Add(entryPoint);
- regularExit = new ControlFlowNode(1, -1, ControlFlowNodeType.RegularExit);
- nodes.Add(regularExit);
- exceptionalExit = new ControlFlowNode(2, -1, ControlFlowNodeType.ExceptionalExit);
- nodes.Add(exceptionalExit);
- Debug.Assert(nodes.Count == 3);
- }
-
- /// <summary>
- /// Determines the index of the instruction (for use with the hasIncomingJumps array)
- /// </summary>
- int GetInstructionIndex(Instruction inst)
- {
- int index = Array.BinarySearch(offsets, inst.Offset);
- Debug.Assert(index >= 0);
- return index;
- }
-
- /// <summary>
- /// Builds the ControlFlowGraph.
- /// </summary>
- public ControlFlowGraph Build()
- {
- CalculateHasIncomingJumps();
- CreateNodes();
- CreateRegularControlFlow();
- CreateExceptionalControlFlow();
- if (copyFinallyBlocks)
- CopyFinallyBlocksIntoLeaveEdges();
- else
- TransformLeaveEdges();
- return new ControlFlowGraph(nodes.ToArray());
- }
-
- #region Step 1: calculate which instructions are the targets of jump instructions.
- void CalculateHasIncomingJumps()
- {
- foreach (Instruction inst in methodBody.Instructions) {
- if (inst.OpCode.OperandType == OperandType.InlineBrTarget || inst.OpCode.OperandType == OperandType.ShortInlineBrTarget) {
- hasIncomingJumps[GetInstructionIndex((Instruction)inst.Operand)] = true;
- } else if (inst.OpCode.OperandType == OperandType.InlineSwitch) {
- foreach (Instruction i in (Instruction[])inst.Operand)
- hasIncomingJumps[GetInstructionIndex(i)] = true;
- }
- }
- foreach (ExceptionHandler eh in methodBody.ExceptionHandlers) {
- if (eh.FilterStart != null) {
- hasIncomingJumps[GetInstructionIndex(eh.FilterStart)] = true;
- }
- hasIncomingJumps[GetInstructionIndex(eh.HandlerStart)] = true;
- }
- }
- #endregion
-
- #region Step 2: create nodes
- void CreateNodes()
- {
- // Step 2a: find basic blocks and create nodes for them
- for (int i = 0; i < methodBody.Instructions.Count; i++) {
- Instruction blockStart = methodBody.Instructions[i];
- ExceptionHandler blockStartEH = FindInnermostExceptionHandler(blockStart.Offset);
- // try and see how big we can make that block:
- for (; i + 1 < methodBody.Instructions.Count; i++) {
- Instruction inst = methodBody.Instructions[i];
- if (IsBranch(inst.OpCode) || CanThrowException(inst.OpCode))
- break;
- if (hasIncomingJumps[i + 1])
- break;
- if (inst.Next != null) {
- // ensure that blocks never contain instructions from different try blocks
- ExceptionHandler instEH = FindInnermostExceptionHandler(inst.Next.Offset);
- if (instEH != blockStartEH)
- break;
- }
- }
-
- nodes.Add(new ControlFlowNode(nodes.Count, blockStart, methodBody.Instructions[i]));
- }
- // Step 2b: Create special nodes for the exception handling constructs
- foreach (ExceptionHandler handler in methodBody.ExceptionHandlers) {
- if (handler.HandlerType == ExceptionHandlerType.Filter)
- throw new NotSupportedException();
- ControlFlowNode endFinallyOrFaultNode = null;
- if (handler.HandlerType == ExceptionHandlerType.Finally || handler.HandlerType == ExceptionHandlerType.Fault) {
- endFinallyOrFaultNode = new ControlFlowNode(nodes.Count, handler.HandlerEnd.Offset, ControlFlowNodeType.EndFinallyOrFault);
- nodes.Add(endFinallyOrFaultNode);
- }
- nodes.Add(new ControlFlowNode(nodes.Count, handler, endFinallyOrFaultNode));
- }
- }
- #endregion
-
- #region Step 3: create edges for the normal flow of control (assuming no exceptions thrown)
- void CreateRegularControlFlow()
- {
- CreateEdge(entryPoint, methodBody.Instructions[0], JumpType.Normal);
- foreach (ControlFlowNode node in nodes) {
- if (node.End != null) {
- // create normal edges from one instruction to the next
- if (!OpCodeInfo.IsUnconditionalBranch(node.End.OpCode))
- CreateEdge(node, node.End.Next, JumpType.Normal);
-
- // create edges for branch instructions
- if (node.End.OpCode.OperandType == OperandType.InlineBrTarget || node.End.OpCode.OperandType == OperandType.ShortInlineBrTarget) {
- if (node.End.OpCode == OpCodes.Leave || node.End.OpCode == OpCodes.Leave_S) {
- var handlerBlock = FindInnermostHandlerBlock(node.End.Offset);
- if (handlerBlock.NodeType == ControlFlowNodeType.FinallyOrFaultHandler)
- CreateEdge(node, (Instruction)node.End.Operand, JumpType.LeaveTry);
- else
- CreateEdge(node, (Instruction)node.End.Operand, JumpType.Normal);
- } else {
- CreateEdge(node, (Instruction)node.End.Operand, JumpType.Normal);
- }
- } else if (node.End.OpCode.OperandType == OperandType.InlineSwitch) {
- foreach (Instruction i in (Instruction[])node.End.Operand)
- CreateEdge(node, i, JumpType.Normal);
- }
-
- // create edges for return instructions
- if (node.End.OpCode.FlowControl == FlowControl.Return) {
- switch (node.End.OpCode.Code) {
- case Code.Ret:
- CreateEdge(node, regularExit, JumpType.Normal);
- break;
- case Code.Endfinally:
- ControlFlowNode handlerBlock = FindInnermostHandlerBlock(node.End.Offset);
- if (handlerBlock.EndFinallyOrFaultNode == null)
- throw new InvalidProgramException("Found endfinally in block " + handlerBlock);
- CreateEdge(node, handlerBlock.EndFinallyOrFaultNode, JumpType.Normal);
- break;
- default:
- throw new NotSupportedException(node.End.OpCode.ToString());
- }
- }
- }
- }
- }
- #endregion
-
- #region Step 4: create edges for the exceptional control flow (from instructions that might throw, to the innermost containing exception handler)
- void CreateExceptionalControlFlow()
- {
- foreach (ControlFlowNode node in nodes) {
- if (node.End != null && CanThrowException(node.End.OpCode)) {
- CreateEdge(node, FindInnermostExceptionHandlerNode(node.End.Offset), JumpType.JumpToExceptionHandler);
- }
- if (node.ExceptionHandler != null) {
- if (node.EndFinallyOrFaultNode != null) {
- // For Fault and Finally blocks, create edge from "EndFinally" to next exception handler.
- // This represents the exception bubbling up after finally block was executed.
- CreateEdge(node.EndFinallyOrFaultNode, FindParentExceptionHandlerNode(node), JumpType.JumpToExceptionHandler);
- } else {
- // For Catch blocks, create edge from "CatchHandler" block (at beginning) to next exception handler.
- // This represents the exception bubbling up because it did not match the type of the catch block.
- CreateEdge(node, FindParentExceptionHandlerNode(node), JumpType.JumpToExceptionHandler);
- }
- CreateEdge(node, node.ExceptionHandler.HandlerStart, JumpType.Normal);
- }
- }
- }
-
- ExceptionHandler FindInnermostExceptionHandler(int instructionOffsetInTryBlock)
- {
- foreach (ExceptionHandler h in methodBody.ExceptionHandlers) {
- if (h.TryStart.Offset <= instructionOffsetInTryBlock && instructionOffsetInTryBlock < h.TryEnd.Offset) {
- return h;
- }
- }
- return null;
- }
-
- ControlFlowNode FindInnermostExceptionHandlerNode(int instructionOffsetInTryBlock)
- {
- ExceptionHandler h = FindInnermostExceptionHandler(instructionOffsetInTryBlock);
- if (h != null)
- return nodes.Single(n => n.ExceptionHandler == h && n.CopyFrom == null);
- else
- return exceptionalExit;
- }
-
- ControlFlowNode FindInnermostHandlerBlock(int instructionOffset)
- {
- foreach (ExceptionHandler h in methodBody.ExceptionHandlers) {
- if (h.TryStart.Offset <= instructionOffset && instructionOffset < h.TryEnd.Offset
- || h.HandlerStart.Offset <= instructionOffset && instructionOffset < h.HandlerEnd.Offset)
- {
- return nodes.Single(n => n.ExceptionHandler == h && n.CopyFrom == null);
- }
- }
- return exceptionalExit;
- }
-
- ControlFlowNode FindParentExceptionHandlerNode(ControlFlowNode exceptionHandler)
- {
- Debug.Assert(exceptionHandler.NodeType == ControlFlowNodeType.CatchHandler
- || exceptionHandler.NodeType == ControlFlowNodeType.FinallyOrFaultHandler);
- int offset = exceptionHandler.ExceptionHandler.TryStart.Offset;
- for (int i = exceptionHandler.BlockIndex + 1; i < nodes.Count; i++) {
- ExceptionHandler h = nodes[i].ExceptionHandler;
- if (h != null && h.TryStart.Offset <= offset && offset < h.TryEnd.Offset)
- return nodes[i];
- }
- return exceptionalExit;
- }
- #endregion
-
- #region Step 5a: replace LeaveTry edges with EndFinally edges
- // this is used only for copyFinallyBlocks==false; see Step 5b otherwise
- void TransformLeaveEdges()
- {
- for (int i = nodes.Count - 1; i >= 0; i--) {
- ControlFlowNode node = nodes[i];
- if (node.End != null && node.Outgoing.Count == 1 && node.Outgoing[0].Type == JumpType.LeaveTry) {
- Debug.Assert(node.End.OpCode == OpCodes.Leave || node.End.OpCode == OpCodes.Leave_S);
-
- ControlFlowNode target = node.Outgoing[0].Target;
- // remove the edge
- target.Incoming.Remove(node.Outgoing[0]);
- node.Outgoing.Clear();
-
- ControlFlowNode handler = FindInnermostExceptionHandlerNode(node.End.Offset);
- Debug.Assert(handler.NodeType == ControlFlowNodeType.FinallyOrFaultHandler);
-
- CreateEdge(node, handler, JumpType.Normal);
- CreateEdge(handler.EndFinallyOrFaultNode, target, JumpType.EndFinally);
- }
- }
- }
- #endregion
-
- #region Step 5b: copy finally blocks into the LeaveTry edges
- void CopyFinallyBlocksIntoLeaveEdges()
- {
- // We need to process try-finally blocks inside-out.
- // We'll do that by going through all instructions in reverse order
- for (int i = nodes.Count - 1; i >= 0; i--) {
- ControlFlowNode node = nodes[i];
- if (node.End != null && node.Outgoing.Count == 1 && node.Outgoing[0].Type == JumpType.LeaveTry) {
- Debug.Assert(node.End.OpCode == OpCodes.Leave || node.End.OpCode == OpCodes.Leave_S);
-
- ControlFlowNode target = node.Outgoing[0].Target;
- // remove the edge
- target.Incoming.Remove(node.Outgoing[0]);
- node.Outgoing.Clear();
-
- ControlFlowNode handler = FindInnermostExceptionHandlerNode(node.End.Offset);
- Debug.Assert(handler.NodeType == ControlFlowNodeType.FinallyOrFaultHandler);
-
- ControlFlowNode copy = CopyFinallySubGraph(handler, handler.EndFinallyOrFaultNode, target);
- CreateEdge(node, copy, JumpType.Normal);
- }
- }
- }
-
- /// <summary>
- /// Creates a copy of all nodes pointing to 'end' and replaces those references with references to 'newEnd'.
- /// Nodes pointing to the copied node are copied recursively to update those references, too.
- /// This recursion stops at 'start'. The modified version of start is returned.
- /// </summary>
- ControlFlowNode CopyFinallySubGraph(ControlFlowNode start, ControlFlowNode end, ControlFlowNode newEnd)
- {
- return new CopyFinallySubGraphLogic(this, start, end, newEnd).CopyFinallySubGraph();
- }
-
- class CopyFinallySubGraphLogic
- {
- readonly ControlFlowGraphBuilder builder;
- readonly Dictionary<ControlFlowNode, ControlFlowNode> oldToNew = new Dictionary<ControlFlowNode, ControlFlowNode>();
- readonly ControlFlowNode start;
- readonly ControlFlowNode end;
- readonly ControlFlowNode newEnd;
-
- public CopyFinallySubGraphLogic(ControlFlowGraphBuilder builder, ControlFlowNode start, ControlFlowNode end, ControlFlowNode newEnd)
- {
- this.builder = builder;
- this.start = start;
- this.end = end;
- this.newEnd = newEnd;
- }
-
- internal ControlFlowNode CopyFinallySubGraph()
- {
- foreach (ControlFlowNode n in end.Predecessors) {
- CollectNodes(n);
- }
- foreach (var pair in oldToNew)
- ReconstructEdges(pair.Key, pair.Value);
- return GetNew(start);
- }
-
- void CollectNodes(ControlFlowNode node)
- {
- if (node == end || node == newEnd)
- throw new InvalidOperationException("unexpected cycle involving finally construct");
- if (!oldToNew.ContainsKey(node)) {
- int newBlockIndex = builder.nodes.Count;
- ControlFlowNode copy;
- switch (node.NodeType) {
- case ControlFlowNodeType.Normal:
- copy = new ControlFlowNode(newBlockIndex, node.Start, node.End);
- break;
- case ControlFlowNodeType.FinallyOrFaultHandler:
- copy = new ControlFlowNode(newBlockIndex, node.ExceptionHandler, node.EndFinallyOrFaultNode);
- break;
- default:
- // other nodes shouldn't occur when copying finally blocks
- throw new NotSupportedException(node.NodeType.ToString());
- }
- copy.CopyFrom = node;
- builder.nodes.Add(copy);
- oldToNew.Add(node, copy);
-
- if (node != start) {
- foreach (ControlFlowNode n in node.Predecessors) {
- CollectNodes(n);
- }
- }
- }
- }
-
- void ReconstructEdges(ControlFlowNode oldNode, ControlFlowNode newNode)
- {
- foreach (ControlFlowEdge oldEdge in oldNode.Outgoing) {
- builder.CreateEdge(newNode, GetNew(oldEdge.Target), oldEdge.Type);
- }
- }
-
- ControlFlowNode GetNew(ControlFlowNode oldNode)
- {
- if (oldNode == end)
- return newEnd;
- ControlFlowNode newNode;
- if (oldToNew.TryGetValue(oldNode, out newNode))
- return newNode;
- return oldNode;
- }
- }
- #endregion
-
- #region CreateEdge methods
- void CreateEdge(ControlFlowNode fromNode, Instruction toInstruction, JumpType type)
- {
- CreateEdge(fromNode, nodes.Single(n => n.Start == toInstruction), type);
- }
-
- void CreateEdge(ControlFlowNode fromNode, ControlFlowNode toNode, JumpType type)
- {
- ControlFlowEdge edge = new ControlFlowEdge(fromNode, toNode, type);
- fromNode.Outgoing.Add(edge);
- toNode.Incoming.Add(edge);
- }
- #endregion
-
- #region OpCode info
- static bool CanThrowException(OpCode opcode)
- {
- if (opcode.OpCodeType == OpCodeType.Prefix)
- return false;
- return OpCodeInfo.Get(opcode).CanThrow;
- }
-
- static bool IsBranch(OpCode opcode)
- {
- if (opcode.OpCodeType == OpCodeType.Prefix)
- return false;
- switch (opcode.FlowControl) {
- case FlowControl.Cond_Branch:
- case FlowControl.Branch:
- case FlowControl.Throw:
- case FlowControl.Return:
- return true;
- case FlowControl.Next:
- case FlowControl.Call:
- return false;
- default:
- throw new NotSupportedException(opcode.FlowControl.ToString());
- }
- }
- #endregion
- }
-}