/*
* FindBugs - Find Bugs in Java programs
* Copyright (C) 2006, 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.deref;
import java.util.BitSet;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import javax.annotation.CheckForNull;
import edu.umd.cs.findbugs.ba.Location;
import edu.umd.cs.findbugs.ba.vna.ValueNumber;
import edu.umd.cs.findbugs.ba.vna.ValueNumberFactory;
import edu.umd.cs.findbugs.ba.vna.ValueNumberFrame;
import edu.umd.cs.findbugs.util.Util;
/**
* A set of values unconditionally dereferenced in the future.
*
* @author David Hovemeyer
*/
public class UnconditionalValueDerefSet {
/** Number of distinct value numbers in method */
private int numValueNumbersInMethod;
/** Set of value numbers unconditionally dereferenced */
private BitSet valueNumbersUnconditionallyDereferenced;
/** Map of value numbers to locations */
private Map<ValueNumber, Set<Location>> derefLocationSetMap;
boolean resultsFromBackEdge = false;
int backEdgeUpdateCount = 0;
private int lastUpdateTimestamp;
/**
* Constructor.
*
* @param numValueNumbersInMethod
* number of distinct value numbers in method
*/
public UnconditionalValueDerefSet(int numValueNumbersInMethod) {
this.numValueNumbersInMethod = numValueNumbersInMethod;
this.valueNumbersUnconditionallyDereferenced = new BitSet();
this.derefLocationSetMap = new HashMap<ValueNumber, Set<Location>>(3);
}
/**
* Is this the bottom value?
*
* @return true if this is the bottom value, false otherwise
*/
public boolean isBottom() {
return valueNumbersUnconditionallyDereferenced.get(numValueNumbersInMethod);
}
/**
* Make this dataflow fact the bottom value.
*/
public void setIsBottom() {
clear();
valueNumbersUnconditionallyDereferenced.set(numValueNumbersInMethod);
}
/**
* Is this the top value?
*
* @return true if this is the top value, false otherwise
*/
public boolean isTop() {
return valueNumbersUnconditionallyDereferenced.get(numValueNumbersInMethod + 1);
}
/**
* Make this dataflow fact the top value.
*/
public void setIsTop() {
clear();
valueNumbersUnconditionallyDereferenced.set(numValueNumbersInMethod + 1);
lastUpdateTimestamp = 0;
}
/**
* Clear the deref set. This sets the fact so it is valid as the dataflow
* entry fact: no future dereferences are guaranteed.
*/
void clear() {
valueNumbersUnconditionallyDereferenced.clear();
derefLocationSetMap.clear();
}
/**
* Make this dataflow fact the same as the given one.
*
* @param source
* another dataflow fact
*/
public void makeSameAs(UnconditionalValueDerefSet source) {
// Copy value numbers
valueNumbersUnconditionallyDereferenced.clear();
valueNumbersUnconditionallyDereferenced.or(source.valueNumbersUnconditionallyDereferenced);
lastUpdateTimestamp = source.lastUpdateTimestamp;
// Copy dereference locations for each value number
derefLocationSetMap.clear();
if (source.derefLocationSetMap.size() > 0)
for (Map.Entry<ValueNumber, Set<Location>> sourceEntry : source.derefLocationSetMap.entrySet()) {
Set<Location> derefLocationSet = Util.makeSmallHashSet(sourceEntry.getValue());
derefLocationSetMap.put(sourceEntry.getKey(), derefLocationSet);
}
}
/**
* Return whether or not this dataflow fact is identical to the one given.
*
* @param otherFact
* another dataflow fact
* @return true if the other dataflow fact is identical to this one, false
* otherwise
*/
public boolean isSameAs(UnconditionalValueDerefSet otherFact) {
return valueNumbersUnconditionallyDereferenced.equals(otherFact.valueNumbersUnconditionallyDereferenced)
&& derefLocationSetMap.equals(otherFact.derefLocationSetMap);
}
/**
* Merge given dataflow fact into this one. We take the intersection of the
* unconditional deref value number set, and union the deref locations.
*
* @param fact
* another dataflow fact
* @param skipMe
* TODO
*/
public void mergeWith(UnconditionalValueDerefSet fact, @CheckForNull ValueNumber skipMe, ValueNumberFactory valueNumberFactory) {
if (UnconditionalValueDerefAnalysis.DEBUG) {
System.out.println("merge update of # " + System.identityHashCode(this) + " from " + System.identityHashCode(fact));
System.out.println("update " + this);
System.out.println("with " + fact);
}
boolean resultForSkippedValue = false;
if (skipMe != null) {
resultForSkippedValue = valueNumbersUnconditionallyDereferenced.get(skipMe.getNumber());
}
// Compute the intersection of the unconditionally dereferenced value
// sets
valueNumbersUnconditionallyDereferenced.and(fact.valueNumbersUnconditionallyDereferenced);
if (skipMe != null) {
valueNumbersUnconditionallyDereferenced.set(skipMe.getNumber(), resultForSkippedValue);
}
// For each unconditionally dereferenced value...
for (int i = 0; i < numValueNumbersInMethod; i++) {
ValueNumber vn = valueNumberFactory.forNumber(i);
if (vn.equals(skipMe))
continue;
Set<Location> factDerefLocationSet = fact.derefLocationSetMap.get(vn);
if (valueNumbersUnconditionallyDereferenced.get(i)) {
if (factDerefLocationSet != null && !factDerefLocationSet.isEmpty()) {
// Compute the union of the dereference locations for
// this value number.
Set<Location> derefLocationSet = derefLocationSetMap.get(vn);
if (derefLocationSet == null) {
derefLocationSet = new HashSet<Location>();
derefLocationSetMap.put(vn, derefLocationSet);
}
derefLocationSet.addAll(fact.derefLocationSetMap.get(vn));
}
} else {
Set<Location> removed = derefLocationSetMap.remove(vn);
// The value number is not in the fact:
// remove its location set
if (removed != null) {
if (UnconditionalValueDerefAnalysis.DEBUG)
System.out.println("Goodbye: " + removed);
}
}
}
}
public void unionWith(UnconditionalValueDerefSet fact, ValueNumberFactory valueNumberFactory) {
if (UnconditionalValueDerefAnalysis.DEBUG) {
System.out.println("union update of # " + System.identityHashCode(this) + " from " + System.identityHashCode(fact));
}
// Compute the union of the unconditionally dereferenced value sets
valueNumbersUnconditionallyDereferenced.or(fact.valueNumbersUnconditionallyDereferenced);
// For each unconditionally dereferenced value...
for (int i = 0; i < numValueNumbersInMethod; i++) {
ValueNumber vn = valueNumberFactory.forNumber(i);
if (fact.valueNumbersUnconditionallyDereferenced.get(i)) {
// Compute the union of the dereference locations for
// this value number.
Set<Location> derefLocationSet = derefLocationSetMap.get(vn);
if (derefLocationSet == null) {
derefLocationSet = new HashSet<Location>();
derefLocationSetMap.put(vn, derefLocationSet);
}
derefLocationSet.addAll(fact.derefLocationSetMap.get(vn));
} else {
derefLocationSetMap.put(vn, new HashSet<Location>(fact.getDerefLocationSet(vn)));
}
}
}
/**
* Mark a value as being dereferenced at given Location.
*
* @param vn
* the value
* @param location
* the Location
*/
public void addDeref(ValueNumber vn, Location location) {
if (UnconditionalValueDerefAnalysis.DEBUG) {
System.out.println("Adding dereference of " + vn + " to # " + System.identityHashCode(this) + " @ " + location);
}
valueNumbersUnconditionallyDereferenced.set(vn.getNumber());
Set<Location> derefLocationSet = getDerefLocationSet(vn);
derefLocationSet.add(location);
}
/**
* Set a value as being unconditionally dereferenced at the given set of
* locations.
*
* @param vn
* the value
* @param derefSet
* the Set of dereference Locations
*/
public void setDerefSet(ValueNumber vn, Set<Location> derefSet) {
if (UnconditionalValueDerefAnalysis.DEBUG) {
System.out.println("Adding dereference of " + vn + " for # " + System.identityHashCode(this) + " to " + derefSet);
}
valueNumbersUnconditionallyDereferenced.set(vn.getNumber());
Set<Location> derefLocationSet = getDerefLocationSet(vn);
derefLocationSet.clear();
derefLocationSet.addAll(derefSet);
}
/**
* Clear the set of dereferences for given ValueNumber
*
* @param value
* the ValueNumber
*/
public void clearDerefSet(ValueNumber value) {
if (UnconditionalValueDerefAnalysis.DEBUG) {
System.out.println("Clearing dereference of " + value + " for # " + System.identityHashCode(this));
}
valueNumbersUnconditionallyDereferenced.clear(value.getNumber());
derefLocationSetMap.remove(value);
}
/**
* Get the set of dereference Locations for given value number.
*
* @param vn
* the value number
* @return the set of dereference Locations
*/
public Set<Location> getDerefLocationSet(ValueNumber vn) {
Set<Location> derefLocationSet = derefLocationSetMap.get(vn);
if (derefLocationSet == null) {
derefLocationSet = new HashSet<Location>();
derefLocationSetMap.put(vn, derefLocationSet);
}
return derefLocationSet;
}
/**
* Return whether or not the given value number is unconditionally
* dereferenced.
*
* @param vn
* the value number
* @return true if the value is unconditionally dereferenced, false
* otherwise
*/
public boolean isUnconditionallyDereferenced(ValueNumber vn) {
return valueNumbersUnconditionallyDereferenced.get(vn.getNumber());
}
public Set<ValueNumber> getValueNumbersThatAreUnconditionallyDereferenced() {
HashSet<ValueNumber> result = new HashSet<ValueNumber>();
for (Map.Entry<ValueNumber, Set<Location>> e : derefLocationSetMap.entrySet()) {
if (!e.getValue().isEmpty())
result.add(e.getKey());
}
return result;
}
public void retainOnlyTheseValueNumbers(Collection<ValueNumber> valueNumbers) {
for (Iterator<ValueNumber> i = derefLocationSetMap.keySet().iterator(); i.hasNext();) {
ValueNumber v = i.next();
if (!valueNumbers.contains(v)) {
i.remove();
valueNumbersUnconditionallyDereferenced.clear(v.getNumber());
}
}
}
/**
* Get the set of Locations where given value is guaranteed to be
* dereferenced. (I.e., if non-implicit-exception control paths are
* followed, one of these locations will be reached).
*
* @param vn
* the value
* @return set of Locations, one of which will definitely be reached if
* non-implicit-exception control paths are followed
*/
public Set<Location> getUnconditionalDerefLocationSet(ValueNumber vn) {
Set<Location> derefLocationSet = derefLocationSetMap.get(vn);
if (derefLocationSet == null) {
derefLocationSet = Collections.<Location> emptySet();
}
return derefLocationSet;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
if (isTop()) {
return "[TOP]";
}
if (isBottom()) {
return "[BOTTOM]";
}
StringBuilder buf = new StringBuilder();
buf.append('[');
boolean firstVN = true;
for (int i = 0; i < numValueNumbersInMethod; i++) {
if (!(valueNumbersUnconditionallyDereferenced.get(i))) {
continue;
}
if (firstVN) {
firstVN = false;
} else {
buf.append(',');
}
buf.append('{');
buf.append(i);
if (valueNumbersUnconditionallyDereferenced.get(i))
buf.append(':');
else
buf.append('?');
TreeSet<Location> derefLocationSet = new TreeSet<Location>();
derefLocationSet.addAll(getDerefLocationSet(i));
boolean firstLoc = true;
for (Location location : derefLocationSet) {
if (firstLoc) {
firstLoc = false;
} else {
buf.append(',');
}
buf.append("(" + location.getBasicBlock().getLabel() + ":" + location.getHandle().getPosition() + ")");
}
buf.append('}');
}
buf.append(']');
return buf.toString();
}
private Set<Location> getDerefLocationSet(int vn) {
for (Map.Entry<ValueNumber, Set<Location>> entry : derefLocationSetMap.entrySet()) {
if (entry.getKey().getNumber() == vn) {
return Collections.<Location> unmodifiableSet(entry.getValue());
}
}
return new HashSet<Location>();
}
/**
* @param location
* @param vnaFrame
*/
public void cleanDerefSet(@CheckForNull Location location, ValueNumberFrame vnaFrame) {
Set<ValueNumber> valueNumbers = new HashSet<ValueNumber>(vnaFrame.allSlots());
valueNumbers.addAll(vnaFrame.valueNumbersForLoads());
if (UnconditionalValueDerefAnalysis.DEBUG) {
for (ValueNumber v : getValueNumbersThatAreUnconditionallyDereferenced())
if (!valueNumbers.contains(v)) {
System.out.println("\nWhy is " + v + " unconditionally dereferenced in #" + System.identityHashCode(this));
System.out.println("VN: " + vnaFrame);
System.out.println("UD: " + this);
System.out.println("Location: " + location);
System.out.println();
}
}
retainOnlyTheseValueNumbers(valueNumbers);
}
/**
* @param lastUpdateTimestamp
* The lastUpdateTimestamp to set.
*/
public void setLastUpdateTimestamp(int lastUpdateTimestamp) {
this.lastUpdateTimestamp = lastUpdateTimestamp;
}
/**
* @return Returns the lastUpdateTimestamp.
*/
public int getLastUpdateTimestamp() {
return lastUpdateTimestamp;
}
/**
* @return {@code true} if the set is empty
*/
public boolean isEmpty() {
return valueNumbersUnconditionallyDereferenced.isEmpty();
}
}