/* * Bytecode Analysis Framework * Copyright (C) 2004,2008 University of Maryland * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package edu.umd.cs.findbugs.ba; import java.util.Iterator; import org.apache.bcel.generic.InstructionHandle; /** * A Path is a sequence of basic blocks. * * @author David Hovemeyer */ public class Path { private static final int DEFAULT_CAPACITY = 8; private static final int INVALID_HASH_CODE = -1; private int[] blockIdList; private int length; private int cachedHashCode; /** * Constructor. Creates an empty Path. */ public Path() { this.blockIdList = new int[DEFAULT_CAPACITY]; this.length = 0; invalidate(); } /** * Append given BasicBlock id to the path. * * @param id * a BasicBlock id (label) */ public void append(int id) { grow(length); blockIdList[length] = id; ++length; invalidate(); } /** * Determine whether or not the id of the given BasicBlock appears anywhere * in the path. * * @param blockId * the id (label) of a BasicBlock * @return true if the BasicBlock's id appears in the path, false if not */ public boolean hasComponent(int blockId) { for (int i = 0; i < length; i++) { if (blockIdList[i] == blockId) { return true; } } return false; } /** * Get the BasicBlock id at the given index in the path. * * @param index * an index in the Path (0 is the first component) * @return the id of the BasicBlock at the given index */ public int getBlockIdAt(int index) { assert index < length; return blockIdList[index]; } /** * Get the number of components (BasicBlock ids) in the Path. * * @return number of components in the Path */ public int getLength() { return length; } /** * Return an exact copy of this Path. * * @return an exact copy of this Path */ public Path duplicate() { Path dup = new Path(); dup.copyFrom(this); return dup; } /** * Make this Path identical to the given one. * * @param other * a Path to which this object should be made identical */ public void copyFrom(Path other) { grow(other.length - 1); System.arraycopy(other.blockIdList, 0, this.blockIdList, 0, other.length); this.length = other.length; this.cachedHashCode = other.cachedHashCode; } /** * Accept a PathVisitor. * * @param cfg * the control flow graph * @param visitor * a PathVisitor */ public void acceptVisitor(CFG cfg, PathVisitor visitor) { if (getLength() > 0) { BasicBlock startBlock = cfg.lookupBlockByLabel(getBlockIdAt(0)); acceptVisitorStartingFromLocation(cfg, visitor, startBlock, startBlock.getFirstInstruction()); } } /** * Accept a PathVisitor, starting from a given BasicBlock and * InstructionHandle. * * @param cfg * the control flow graph * @param visitor * a PathVisitor * @param startBlock * BasicBlock where traversal should start * @param startHandle * InstructionHandle within the start block where traversal * should start */ public void acceptVisitorStartingFromLocation(CFG cfg, PathVisitor visitor, BasicBlock startBlock, InstructionHandle startHandle) { // Find the start block in the path int index; for (index = 0; index < getLength(); index++) { if (getBlockIdAt(index) == startBlock.getLabel()) { break; } } assert index < getLength(); Iterator<InstructionHandle> i = startBlock.instructionIterator(); // Position iterator at start instruction handle if (startHandle != startBlock.getFirstInstruction()) { while (i.hasNext()) { InstructionHandle handle = i.next(); if (handle.getNext() == startHandle) { break; } } } BasicBlock basicBlock = startBlock; while (true) { // visit block visitor.visitBasicBlock(basicBlock); // visit instructions in block while (i.hasNext()) { visitor.visitInstructionHandle(i.next()); } // end of path? index++; if (index >= getLength()) { break; } // visit edge BasicBlock next = cfg.lookupBlockByLabel(getBlockIdAt(index)); Edge edge = cfg.lookupEdge(basicBlock, next); assert edge != null; visitor.visitEdge(edge); // continue to next block basicBlock = next; i = basicBlock.instructionIterator(); } } /** * Determine whether or not given Path is a prefix of this one. * * @param path * another Path * @return true if this Path is a prefix of the other Path, false otherwise */ public boolean isPrefixOf(Path path) { if (this.getLength() > path.getLength()) { return false; } for (int i = 0; i < getLength(); i++) { if (this.getBlockIdAt(i) != path.getBlockIdAt(i)) { return false; } } return true; } private void invalidate() { this.cachedHashCode = INVALID_HASH_CODE; } @Override public int hashCode() { if (cachedHashCode == INVALID_HASH_CODE) { int value = 0; for (int i = 0; i < this.length; ++i) { value += (i * 1009 * blockIdList[i]); } cachedHashCode = value; } return cachedHashCode; } @Override public boolean equals(Object o) { if (o == null || o.getClass() != this.getClass()) return false; Path other = (Path) o; if (this.length != other.length) return false; for (int i = 0; i < this.length; ++i) { if (this.blockIdList[i] != other.blockIdList[i]) return false; } return true; } private static final String SYMBOLS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!@#$%^&*()"; @Override public String toString() { StringBuilder buf = new StringBuilder(); for (int i = 0; i < length; ++i) { int block = blockIdList[i]; if (block < SYMBOLS.length()) buf.append(SYMBOLS.charAt(block)); else buf.append("'" + block + "'"); } return buf.toString(); } private void grow(int index) { if (index >= blockIdList.length) { int newLen = blockIdList.length; do { newLen *= 2; } while (index >= newLen); int[] arr = new int[newLen]; System.arraycopy(this.blockIdList, 0, arr, 0, length); this.blockIdList = arr; } } } // vim:ts=4