/* * 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 edu.umd.cs.findbugs.annotations.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 */ private 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(); } }