Пример #1
0
 /// <summary>A debugging oriented method, that helps assessing the consistency
 /// of the given array. Throws an exception if inconsistency is found.</summary>
 /// <param name="blocksPerOffset"></param>
 private static void AssertConsistency(BlockNode[] blocksPerOffset,
     List<CfgNode> knownNodes = null)
 {
     int arrayLength = blocksPerOffset.Length;
     for(int index = 0; index < arrayLength; index++) {
         BlockNode targetBlock = blocksPerOffset[index];
         if (null == targetBlock) { continue; }
         if (   targetBlock.IsCovering((uint)index, 1)
             && (object.ReferenceEquals(targetBlock, blocksPerOffset[index])))
         {
             continue;
         }
         StringBuilder builder = new StringBuilder();
         builder.AppendFormat(
             "Target block #{0} fails to cover code offset 0x{1:X4}. Offset mapped to block #{2}\r\n",
             targetBlock.NodeId, index, targetBlock.NodeId);
         if (null != knownNodes) {
             builder.Append("Known nodes\r\n");
             foreach (CfgNode scannedNode in knownNodes) {
                 builder.AppendFormat("#{0} : {1}\r\n",
                     scannedNode.NodeId, scannedNode.AdditionalInfo);
             }
         }
         string exceptionMessage = builder.ToString();
         throw new AssertionException(exceptionMessage);
     }
     return;
 }
Пример #2
0
        internal bool IsSuccessor(BlockNode from)
        {
            if (null == from) { throw new ArgumentNullException(); }
            bool result = (null != _predecessors) && _predecessors.Contains(from);
#if DBGCFG
            if (   (   result
                    && (   (null == from._successors)
                        || !from._successors.Contains(this)))
                || (   !result
                    && (null != from._successors)
                    && from._successors.Contains(this)))
            {
                throw new AssertionException(string.Format(
                    "Non matching predecessor/successor between nodes #{0} and #{1}.",
                    this.NodeId, from.NodeId));
            }
#endif
            return result;
        }
Пример #3
0
        /// <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;
        }
Пример #4
0
        /// <summary>Split the current node in two separate nodes reltively to the given
        /// method relative offset. Existing block is left with its orginal predecessors,
        /// newly create block inherit the successors of the original block and both blocks
        /// (old and new) are linked together.</summary>
        /// <param name="methodOffset">The offset within the owning method of the instruction
        /// that must be migrated to the new node.</param>
        /// <returns>The newly created block. This block is already linked as a successor of
        /// the block that has been split.</returns>
        internal BlockNode Split(uint methodOffset)
        {
            int splitIndex;
#if DBGCFG
            if (DebugEnabled) {
                DalvikInstruction lastInstruction = _instructions[_instructions.Count - 1];
                Console.WriteLine("[{0}] ({1:X4}|{2:X4})/{3:X4}", NodeId,
                    _instructions[0].MethodRelativeOffset,
                    lastInstruction.MethodRelativeOffset + lastInstruction.InstructionSize - 1,
                    methodOffset);
            }
#endif
            // Find index of instruction with method relative offset matching the
            // given method offset.
            for (splitIndex = 0; splitIndex < _instructions.Count; splitIndex++) {
                if (_instructions[splitIndex].MethodRelativeOffset == methodOffset) { break; }
            }
            if (splitIndex >= _instructions.Count) { throw new ArgumentException(); }
            int movedRangeSize = _instructions.Count - splitIndex;
            DalvikInstruction[] movedRange = new DalvikInstruction[movedRangeSize];
            this._instructions.CopyTo(splitIndex, movedRange, 0, movedRange.Length);
            BlockNode result = new BlockNode(this.DebugEnabled);
            result._instructions = new List<DalvikInstruction>(movedRange);
            this._instructions.RemoveRange(splitIndex, movedRangeSize);
            // Also adjust block size for both blocks.
            uint sizeDelta = 0;
            foreach (DalvikInstruction movedIndstruction in result._instructions) {
                sizeDelta += movedIndstruction.InstructionSize;
            }
            this._size -= sizeDelta;
            result._size = sizeDelta;
            // We must also transfer existing successors from the splited block to the
            // newly created one.
            base.TransferSuccessors(result);
            // This link MUST occur after successors transfer.
            CfgNode.Link(this, result);
            return result;
        }