// 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 offsetbool[] hasIncomingJumps; // array index = instruction indexList<ControlFlowNode> nodes = new List<ControlFlowNode>();ControlFlowNode entryPoint;ControlFlowNode regularExit;ControlFlowNode exceptionalExit;private 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();elseTransformLeaveEdges();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 nodesvoid CreateNodes(){// Step 2a: find basic blocks and create nodes for themfor (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 blocksExceptionHandler 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 constructsforeach (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 nextif (!OpCodeInfo.IsUnconditionalBranch(node.End.OpCode))CreateEdge(node, node.End.Next, JumpType.Normal);// create edges for branch instructionsif (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);elseCreateEdge(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 instructionsif (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);elsereturn 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 otherwisevoid 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 edgetarget.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 edgesvoid CopyFinallyBlocksIntoLeaveEdges(){// We need to process try-finally blocks inside-out.// We'll do that by going through all instructions in reverse orderfor (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 edgetarget.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 blocksthrow 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 methodsvoid 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 infostatic 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}}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。