/// <summary>Make sure there is a block boundary at the given method offset. If no /// split the block hosting the instruction at this offset in two separate blocks. /// </summary> /// <param name="startNode">Start node for the tree to be considered.</param> /// <param name="methodOffset">Offset to be considered.</param> internal static void EnsureBlockBoundary(CfgNode startNode, uint methodOffset) { BlockNode hostingNode = startNode.FindHostingNode(methodOffset, 1); if (null == hostingNode) { throw new REException(); } if (hostingNode.FirstCoveredOffset == methodOffset) { return; } hostingNode.Split(methodOffset); return; }
/// <summary></summary> /// <param name="method"></param> /// <param name="rootAstNode"></param> /// <returns></returns> internal static CfgNode BuildBasicTree(IMethod method, DalvikInstruction[] instructions, #if DBGCFG bool debugMethodCfg = false #endif ) { CfgNode result = #if DBGCFG new CfgNode(debugMethodCfg); #else new CfgNode(); #endif CreateBasicBlocks(result, method, instructions); EnsureExitNodeUniqueness(result); #if DBGCFG if (debugMethodCfg) { result.DumpGraph(); } #endif return result; }
/// <summary>Make sure the given parameter is not a null reference and /// at the same time is an entry node, otherwise throw an exception.</summary> /// <param name="entryNode">Candidate entry node.</param> private static void AssertEntryNodeParameter(CfgNode entryNode) { if (null == entryNode) { throw new ArgumentNullException(); } if (!entryNode.IsEntryNode) { throw new ArgumentException(); } return; }
/// <summary>Enumerate all node starting from the given entryNode in no /// particular order.</summary> /// <param name="entryNode">The entry node from the graph to be walked.</param> /// <returns>A node enumerable object.</returns> private static IEnumerable<CfgNode> EnumerateAllNodes(CfgNode entryNode) { AssertEntryNodeParameter(entryNode); List<CfgNode> pendingNodes = new List<CfgNode>(); List<CfgNode> alreadyWalked = new List<CfgNode>(); pendingNodes.Add(entryNode); while (0 < pendingNodes.Count) { CfgNode candidate = pendingNodes[0]; pendingNodes.RemoveAt(0); if (alreadyWalked.Contains(candidate)) { continue; } yield return candidate; alreadyWalked.Add(candidate); if (null == candidate.Successors) { continue; } foreach (CfgNode targetNode in candidate.Successors) { if (alreadyWalked.Contains(targetNode)) { continue; } if (pendingNodes.Contains(targetNode)) { continue; } pendingNodes.Add(targetNode); } } }
/// <summary>Make sure there is a single exit node in the graph whose start /// node is provided. This may lead to the creation of a new exit node. /// </summary> /// <param name="entryNode">The graph entry node.</param> private static void EnsureExitNodeUniqueness(CfgNode entryNode) { AssertEntryNodeParameter(entryNode); CfgNode addedExitNode = null; CfgNode exitNodeCandidate = null; foreach(CfgNode candidate in EnumerateAllNodes(entryNode)) { if (!candidate.IsExitNode) { continue; } if ((null != addedExitNode) && !object.ReferenceEquals(addedExitNode, candidate)) { CfgNode.Link(candidate, addedExitNode); continue; } if (null == exitNodeCandidate) { exitNodeCandidate = candidate; continue; } addedExitNode = new CfgNode(); CfgNode.Link(candidate, addedExitNode); CfgNode.Link(exitNodeCandidate, addedExitNode); exitNodeCandidate = null; } return; }
/// <summary>Create basic blocks.</summary> /// <param name="rootNode">The root node of the graph.</param> /// <param name="method">The method which body is involved.</param> /// <param name="sparseInstructions">A sparse array of instructions to be analyzed. /// Some of the entries in this array are expected to be null references /// denoting no instruction starts at that particular offset within the method /// bytecode.</param> private static void CreateBasicBlocks(CfgNode rootNode, IMethod method, DalvikInstruction[] sparseInstructions) { #if DBGCFG List<CfgNode> methodNodes = new List<CfgNode>(); #endif List<BlockNode> unreachableBlocks = new List<BlockNode>(); BlockNode currentBlock = null; bool firstBlock = true; // An array that maps each bytecode byte to it's owning block if any. BlockNode[] blocksPerOffset = new BlockNode[method.ByteCodeSize]; blocksPerOffset[0] = currentBlock; bool cfgDebuggingEnabled = rootNode.DebugEnabled; // Create basic blocks. These are set of consecutive instructions with // each instruction having a single successor that is the next instruction. foreach (DalvikInstruction scannedInstruction in sparseInstructions) { if (null == scannedInstruction) { continue; } #if DBGCFG if (cfgDebuggingEnabled) { Console.WriteLine("@{0:X4} {1}", scannedInstruction.MethodRelativeOffset, scannedInstruction.GetType().Name); } #endif if (null == currentBlock) { // The previous instruction didn't fail in sequence. Either there is already a // block defined for the current offset or we must create a new one. currentBlock = blocksPerOffset[scannedInstruction.MethodRelativeOffset]; if (null == currentBlock) { currentBlock = new BlockNode(cfgDebuggingEnabled); #if DBGCFG methodNodes.Add(currentBlock); #endif blocksPerOffset[scannedInstruction.MethodRelativeOffset] = currentBlock; if (firstBlock) { CfgNode.Link(rootNode, currentBlock); #if DBGCFG Console.WriteLine("Created first block #{0}", currentBlock.NodeId); #endif firstBlock = false; } else { unreachableBlocks.Add(currentBlock); #if DBGCFG AssertConsistency(blocksPerOffset, methodNodes); if (cfgDebuggingEnabled) { Console.WriteLine("Created unreachable block #{0}", currentBlock.NodeId); } #endif } } #if DBGCFG else { if (cfgDebuggingEnabled) { Console.WriteLine("Reusing block #{0}", currentBlock.NodeId); } } #endif } else { // Last instruction failed in sequence. However we may have already defined a // block for the current offset. BlockNode alreadyDefined = blocksPerOffset[scannedInstruction.MethodRelativeOffset]; if ( (null != alreadyDefined) && !object.ReferenceEquals(currentBlock, alreadyDefined)) { #if DBGCFG if (cfgDebuggingEnabled) { Console.WriteLine( "Linking block #{0} to block #{1} and switching to the later", currentBlock.NodeId, alreadyDefined.NodeId); } #endif // make the already defined the current block. CfgNode.Link(currentBlock, alreadyDefined); currentBlock = alreadyDefined; } #if DBGCFG else { if (cfgDebuggingEnabled) { Console.WriteLine("Continuing with block #{0}", currentBlock.NodeId); } } #endif } currentBlock.Bind(scannedInstruction); for(uint sizeIndex = 0; sizeIndex < scannedInstruction.InstructionSize; sizeIndex++) { int offsetIndex = (int)(scannedInstruction.MethodRelativeOffset + sizeIndex); if ( (null != blocksPerOffset[offsetIndex]) && !object.ReferenceEquals(blocksPerOffset[offsetIndex], currentBlock)) { throw new ApplicationException(); } blocksPerOffset[offsetIndex] = currentBlock; #if DBGCFG AssertConsistency(blocksPerOffset, methodNodes); #endif } // Scan other targets if any. uint[] otherOffsets = scannedInstruction.AdditionalTargetMethodOffsets; if (null == otherOffsets) { if (!scannedInstruction.ContinueInSequence) { // Must switch to another block. currentBlock = null; } continue; } // Must create a block for each possible target and link current // block to each of those blocks. for (int index = 0; index < otherOffsets.Length; index++) { uint targetOffset = otherOffsets[index]; BlockNode targetBlock = blocksPerOffset[targetOffset]; if (null == targetBlock) { // Block doesn't exists yet. Create and register it. targetBlock = new BlockNode(currentBlock.DebugEnabled); #if DBGCFG methodNodes.Add(targetBlock); #endif blocksPerOffset[targetOffset] = targetBlock; #if DBGCFG AssertConsistency(blocksPerOffset, methodNodes); if (targetBlock.DebugEnabled) { Console.WriteLine("Pre-registering block #{0} @{1:X4}", targetBlock.NodeId, targetOffset); Console.WriteLine("Linking block #{0} to block #{1}", currentBlock.NodeId, targetBlock.NodeId); } #endif // Link current node and next one. CfgNode.Link(currentBlock, targetBlock); continue; } // The target block already exists albeit it may deserve a split. // if (0 == targetOffset) { continue; } BlockNode splitCandidate = targetBlock; bool splitCandidateIsCurrentBlock = object.ReferenceEquals(splitCandidate, currentBlock); bool splitCandidateAlreadyAligned = !object.ReferenceEquals(blocksPerOffset[targetOffset - 1], splitCandidate); bool linkCurrentToSplitted = true; try { if (splitCandidateAlreadyAligned && !splitCandidateIsCurrentBlock) { // The split candidate actually starts at target address // and is not the current block. No split required. continue; } // Need a split. uint splitAt; bool makeSplitResultCurrent; if (!splitCandidateAlreadyAligned) { splitAt = targetOffset; makeSplitResultCurrent = false; } else { // The target is the first instruction of current block. We // split the last instruction from the current block. if (!splitCandidateIsCurrentBlock) { throw new AssertionException(); } splitAt = scannedInstruction.MethodRelativeOffset; linkCurrentToSplitted = false; // From now on consider the newly created block to be the // current one. makeSplitResultCurrent = true; } #if DBGCFG if (targetBlock.DebugEnabled) { Console.WriteLine("Spliting block #{0} @{1:X4}.", splitCandidate.NodeId, splitAt); } #endif targetBlock = splitCandidate.Split(splitAt); #if DBGCFG methodNodes.Add(targetBlock); if (targetBlock.DebugEnabled) { Console.WriteLine("New block #{0} created while spliting block #{1}.", targetBlock.NodeId, splitCandidate.NodeId); } #endif if (makeSplitResultCurrent) { currentBlock = targetBlock; } // Update offset to block mapping for new splited block. Also // transfer instructions from existing block to split result. for (int scannedOffset = (int)splitAt; scannedOffset < blocksPerOffset.Length; scannedOffset++) { if (!object.ReferenceEquals(blocksPerOffset[scannedOffset], splitCandidate)) { break; } blocksPerOffset[scannedOffset] = targetBlock; } #if DBGCFG AssertConsistency(blocksPerOffset, methodNodes); #endif } finally { if (linkCurrentToSplitted) { #if DBGCFG if ((null != currentBlock) && currentBlock.DebugEnabled) { Console.WriteLine("Linking block #{0} to block #{1}", currentBlock.NodeId, targetBlock.NodeId); } #endif // Link current node and next one. CfgNode.Link(currentBlock, targetBlock); } if (unreachableBlocks.Contains(targetBlock)) { unreachableBlocks.Remove(targetBlock); #if DBGCFG if (targetBlock.DebugEnabled) { Console.WriteLine("Previously unreachable block #{0} now reachable.", targetBlock.NodeId); } #endif } } } // Having other targets force a block reset AND the next instruction to be in // a separate block than the current one, provided the current instruction fails // in sequence. if (scannedInstruction.ContinueInSequence) { uint nextInstructionOffset = scannedInstruction.MethodRelativeOffset + scannedInstruction.InstructionSize; BlockNode nextBlock = blocksPerOffset[nextInstructionOffset]; if (null == nextBlock) { nextBlock = new BlockNode(currentBlock.DebugEnabled); #if DBGCFG methodNodes.Add(nextBlock); #endif blocksPerOffset[nextInstructionOffset] = nextBlock; #if DBGCFG AssertConsistency(blocksPerOffset, methodNodes); if (nextBlock.DebugEnabled) { Console.WriteLine("Created next block #{0} @{1:X4}", nextBlock.NodeId, nextInstructionOffset); Console.WriteLine("Linking block #{0} to block #{1}", currentBlock.NodeId, nextBlock.NodeId); } #endif } // Link current node and next one. CfgNode.Link(currentBlock, nextBlock); } // Next block will always be different from the current one. currentBlock = null; } #if DBGCFG if (cfgDebuggingEnabled) { Console.WriteLine("Basic blocks creation done."); } #endif // Link allunreachable blocks to the root node. foreach (BlockNode scannedBlock in unreachableBlocks) { CfgNode.Link(rootNode, scannedBlock); } return; }
private void AddLink(CfgNode linked, ref List<CfgNode> collection) { if (null == collection) { collection = new List<CfgNode>(); } if (collection.Contains(linked)) { return; } collection.Add(linked); // TODO : Prevent loops. return; }
/// <summary>Transfer the successors of this node to the given one. This is for /// use during node splits. The receiving node must not have any successor yet.</summary> /// <param name="to">The receiving node.</param> protected void TransferSuccessors(CfgNode to) { if (null != to._successors) { throw new InvalidOperationException(); } if (null == this._successors) { return; } to._successors = this._successors; this._successors = null; // Must also adjust predecessors in successors. foreach (CfgNode target in to._successors) { List<CfgNode> targetPredecessors = target._predecessors; int targetPredecessorsCount = targetPredecessors.Count; bool replacementFound = false; for (int index = 0; index < targetPredecessorsCount; index++) { if (!object.ReferenceEquals(this, targetPredecessors[index])) { continue; } replacementFound = true; targetPredecessors[index] = to; break; } if (!replacementFound) { throw new ApplicationException(); } } return; }
internal static void Link(CfgNode predecessor, CfgNode successor) { if (object.ReferenceEquals(predecessor, successor)) { throw new InvalidOperationException(); } #if DBGCFG if ( (null != predecessor) && (null != successor) && (predecessor.DebugEnabled || successor.DebugEnabled)) { Console.WriteLine("[{0}] -> [{1}]", predecessor.NodeId, successor.NodeId); } #endif predecessor.AddLink(successor, ref predecessor._successors); successor.AddLink(predecessor, ref successor._predecessors); return; }