/* * Bytecode Analysis Framework * Copyright (C) 2003,2004 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.Collection; import java.util.HashSet; import edu.umd.cs.findbugs.ba.vna.ValueNumber; import edu.umd.cs.findbugs.ba.vna.ValueNumberFactory; import edu.umd.cs.findbugs.ba.vna.ValueNumberFrame; /** * Lock counts for values (as produced by ValueNumberAnalysis). * A LockSet tells us the lock counts for all values in a method, * insofar as we can accurately determine them. * * @author David Hovemeyer * @see edu.umd.cs.findbugs.ba.vna.ValueNumberAnalysis */ public final class LockSet { /** * An uninitialized lock value. */ public static final int TOP = -1; /** * An invalid lock count resulting from the meet of two * different (inconsistent) lock counts. */ public static final int BOTTOM = -2; private static final int INVALID = -1; private static final int DEFAULT_CAPACITY = 8; /** * Lock counts are stored in an array. * Even indices <i>i</i> are value numbers of lock objects. * Odd indices <i>i+1</i> are lock counts. * This representation is fairly compact in memory. */ private int[] array; /** * The lock count value to return for nonexistent lock entries. */ private int defaultLockCount; /** * Constructor. * Creates an empty lock set which returns TOP for * nonexistent lock entries. */ public LockSet() { this.array = new int[DEFAULT_CAPACITY]; this.defaultLockCount = TOP; clear(); } /** * Get the lock count for given lock object. * * @param valueNumber value number of the lock object * @return the lock count for the lock object */ public int getLockCount(int valueNumber) { int index = findIndex(valueNumber); if (index < 0) return defaultLockCount; else return array[index + 1]; } public boolean isTop() { return defaultLockCount == TOP; } /** * Set the lock count for a lock object. * * @param valueNumber value number of the lock object * @param lockCount the lock count for the lock */ public void setLockCount(int valueNumber, int lockCount) { int index = findIndex(valueNumber); if (index < 0) { addEntry(index, valueNumber, lockCount); } else { array[index + 1] = lockCount; } } /** * Set the default lock count to return for nonexistent lock entries. * * @param defaultLockCount the default lock count value */ public void setDefaultLockCount(int defaultLockCount) { this.defaultLockCount = defaultLockCount; } /** * Get the number of distinct lock values with positive lock counts. */ public int getNumLockedObjects() { int result = 0; for (int i = 0; i < array.length; i += 2) { if (array[i] == INVALID) break; if (array[i + 1] > 0) ++result; } return result; } /** * Make this LockSet the same as the given one. * * @param other the LockSet to copy */ public void copyFrom(LockSet other) { if (other.array.length != array.length) { array = new int[other.array.length]; } System.arraycopy(other.array, 0, array, 0, array.length); this.defaultLockCount = other.defaultLockCount; } /** * Clear all entries out of this LockSet. */ public void clear() { for (int i = 0; i < array.length; i += 2) { array[i] = INVALID; } } /** * Meet this LockSet with another LockSet, * storing the result in this object. * * @param other the other LockSet */ public void meetWith(LockSet other) { for (int i = 0; i < array.length; i += 2) { int valueNumber = array[i]; if (valueNumber < 0) break; int mine = array[i + 1]; int his = other.getLockCount(valueNumber); array[i + 1] = mergeValues(mine, his); } for (int i = 0; i < other.array.length; i += 2) { int valueNumber = other.array[i]; if (valueNumber < 0) break; int mine = getLockCount(valueNumber); int his = other.array[i + 1]; setLockCount(valueNumber, mergeValues(mine, his)); } setDefaultLockCount(0); } /** * Return whether or not this LockSet is the same as the one given. * * @param other the other LockSet */ public boolean sameAs(LockSet other) { return this.identicalSubset(other) && other.identicalSubset(this); } /** * Determine whether or not this lock set contains any * locked values which are method return values. * * @param factory the ValueNumberFactory that produced the lock values */ public boolean containsReturnValue(ValueNumberFactory factory) { for (int i = 0; i < array.length; i += 2) { int valueNumber = array[i]; if (valueNumber < 0) break; int lockCount = array[i + 1]; if (lockCount > 0 && factory.forNumber(valueNumber).hasFlag(ValueNumber.RETURN_VALUE)) return true; } return false; } /** * Destructively intersect this lock set with another. * Note that this is <em>not</em> a dataflow merge: * we are interested in finding out which locks are held * in both sets, not in the exact lock counts. * * @param other the other LockSet */ public void intersectWith(LockSet other) { for (int i = 0; i < array.length; i += 2) { int valueNumber = array[i]; if (valueNumber < 0) break; int myLockCount = array[i + 1]; if (myLockCount <= 0) continue; int otherLockCount = other.getLockCount(valueNumber); if (otherLockCount <= 0) { /* This set holds the lock, but the other one doesn't. */ array[i + 1] = 0; } } } /** * Return whether or not this lock set is empty, * meaning that no locks have a positive lock count. * * @return true if no locks are held, false if at least * one lock is held */ public boolean isEmpty() { for (int i = 0; i < array.length; i += 2) { int valueNumber = array[i]; if (valueNumber < 0) return true; int myLockCount = array[i + 1]; if (myLockCount > 0) return false; } return true; } private boolean identicalSubset(LockSet other) { for (int i = 0; i < array.length; i += 2) { int valueNumber = array[i]; if (valueNumber < 0) break; int mine = array[i + 1]; int his = other.getLockCount(valueNumber); if (mine != his) return false; //System.out.println("For value " + valueNumber + ", " + mine + "==" + his); } return true; } private static int mergeValues(int a, int b) { if (a == TOP) return b; else if (b == TOP) return a; else if (a == BOTTOM || b == BOTTOM) return BOTTOM; else if (a == b) return a; else return BOTTOM; } private int findIndex(int valueNumber) { for (int i = 0; i < array.length; i += 2) { int value = array[i]; if (value < 0) return -(i + 1); // didn't find requested valueNumber - return first available slot else if (value == valueNumber) return i; // found requested valueNumber } return -(array.length + 1); // didn't find requested valueNumber, and array is full } private void addEntry(int negatedIndex, int valueNumber, int lockCount) { int index = -(negatedIndex + 1); int origCapacity = array.length; if (index == origCapacity) { // Grow the array. // Allocate twice the space of the old array int[] data = new int[origCapacity * 2]; // Copy existing data System.arraycopy(array, 0, data, 0, origCapacity); // Clear entries in the new part of the array // that won't be used to store the entry that's // being added for (int i = index + 2; i < data.length; i += 2) { data[i] = INVALID; } // Now we're ready to use the new array array = data; } array[index] = valueNumber; array[index + 1] = lockCount; } @Override public String toString() { StringBuilder buf = new StringBuilder(); buf.append('['); boolean first = true; if (defaultLockCount == 0) { buf.append("default=0"); first = false; } for (int i = 0; i < array.length; i += 2) { int valueNumber = array[i]; if (valueNumber < 0) continue; int lockCount = array[i + 1]; if (lockCount == 0) continue; if (first) first = false; else buf.append(','); buf.append(valueNumber); buf.append('='); if (lockCount == TOP) buf.append("TOP"); else if (lockCount == BOTTOM) buf.append("BOTTOM"); else buf.append(lockCount); } buf.append(']'); return buf.toString(); } /** * @param frame * @return a set of the locked value numbers */ public Collection<ValueNumber> getLockedValueNumbers(ValueNumberFrame frame) { if (frame == null) throw new IllegalArgumentException("Null Frame"); HashSet<ValueNumber> result = new HashSet<ValueNumber>(); for(ValueNumber v : frame.allSlots()) if (v != null && getLockCount(v.getNumber()) > 0) result.add(v); return result; } /* public static void main(String[] argv) { LockSet l = new LockSet(); l.setLockCount(0, 1); System.out.println(l); l.setLockCount(0, 2); System.out.println(l); l.setLockCount(15, 1); System.out.println(l); LockSet ll = new LockSet(); ll.setLockCount(0, 1); ll.setLockCount(15, 1); ll.setLockCount(69, 3); LockSet tmp = new LockSet(); tmp.copyFrom(ll); ll.meetWith(l); System.out.println(l + " merge with " + tmp + " ==> " + ll); LockSet dup = new LockSet(); dup.copyFrom(ll); System.out.println(ll + " == " + dup + " ==> " + ll.sameAs(dup)); System.out.println(ll + " == " + l + " ==> " + ll.sameAs(l)); } */ } // vim:ts=4