/* * FindBugs - Find Bugs in Java programs * Copyright (C) 2003-2007 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.jsr305; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.TreeSet; import edu.umd.cs.findbugs.ba.DataflowAnalysisException; import edu.umd.cs.findbugs.ba.vna.ValueNumber; /** * Set of ValueNumbers and their corresponding FlowValues. * * @author David Hovemeyer */ public class TypeQualifierValueSet { // States enum State { VALID, TOP, BOTTOM } private static final FlowValue DEFAULT_FLOW_VALUE = FlowValue.UNKNOWN; private Map<ValueNumber, FlowValue> valueMap; private Map<ValueNumber, Set<SourceSinkInfo>> whereAlways; private Map<ValueNumber, Set<SourceSinkInfo>> whereNever; private State state = State.VALID; public TypeQualifierValueSet() { this.valueMap = new HashMap<ValueNumber, FlowValue>(); this.whereAlways = new HashMap<ValueNumber, Set<SourceSinkInfo>>(); this.whereNever = new HashMap<ValueNumber, Set<SourceSinkInfo>>(); this.state = State.TOP; } public void modelSourceSink(SourceSinkInfo sourceSinkInfo) { assert sourceSinkInfo != null; ValueNumber vn = sourceSinkInfo.getValueNumber(); FlowValue flowValue = FlowValue.flowValueFromWhen(sourceSinkInfo.getWhen()); setValue(vn, flowValue); if (flowValue.isYes()) { addSourceSinkInfo(whereAlways, vn, sourceSinkInfo); } if (flowValue.isNo()) { addSourceSinkInfo(whereNever, vn, sourceSinkInfo); } } private void setValue(ValueNumber vn, FlowValue flowValue) { valueMap.put(vn, flowValue); } private static void addSourceSinkInfo(Map<ValueNumber, Set<SourceSinkInfo>> sourceSinkInfoSetMap, ValueNumber vn, SourceSinkInfo sourceSinkInfo) { Set<SourceSinkInfo> sourceSinkInfoSet = getOrCreateSourceSinkInfoSet(sourceSinkInfoSetMap, vn); sourceSinkInfoSet.add(sourceSinkInfo); } public void pruneValue(ValueNumber vn) { assert isValid(); valueMap.remove(vn); whereAlways.remove(vn); whereNever.remove(vn); } public Set<SourceSinkInfo> getWhereAlways(ValueNumber vn) { return getOrCreateSourceSinkInfoSet(whereAlways, vn); } public Set<SourceSinkInfo> getWhereNever(ValueNumber vn) { return getOrCreateSourceSinkInfoSet(whereNever, vn); } private static Set<SourceSinkInfo> getOrCreateSourceSinkInfoSet(Map<ValueNumber, Set<SourceSinkInfo>> sourceSinkInfoSetMap, ValueNumber vn) { Set<SourceSinkInfo> sourceSinkInfoSet = sourceSinkInfoSetMap.get(vn); if (sourceSinkInfoSet == null) { sourceSinkInfoSet = new HashSet<SourceSinkInfo>(); sourceSinkInfoSetMap.put(vn, sourceSinkInfoSet); } return sourceSinkInfoSet; } public FlowValue getValue(ValueNumber vn) { FlowValue result = valueMap.get(vn); return result != null ? result : FlowValue.TOP; } public Collection<? extends ValueNumber> getValueNumbers() { return valueMap.keySet(); } public boolean isValid() { return state == State.VALID; } public void makeValid() { /* this.state = State.VALID; this.valueMap.clear(); this.whereAlways.clear(); this.whereNever.clear(); */ reset(State.VALID); } public void makeSameAs(TypeQualifierValueSet source) { /* this.state = source.state; this.valueMap.clear(); */ reset(source.state); this.valueMap.putAll(source.valueMap); copySourceSinkInfoSetMap(this.whereAlways, source.whereAlways); copySourceSinkInfoSetMap(this.whereNever, source.whereNever); } private void copySourceSinkInfoSetMap(Map<ValueNumber, Set<SourceSinkInfo>> dest, Map<ValueNumber, Set<SourceSinkInfo>> source) { dest.keySet().retainAll(source.keySet()); for (Map.Entry<ValueNumber, Set<SourceSinkInfo>> entry : source.entrySet()) { Set<SourceSinkInfo> locSet = getOrCreateSourceSinkInfoSet(dest, entry.getKey()); locSet.clear(); locSet.addAll(entry.getValue()); } } public boolean isTop() { return state == State.TOP; } public void setTop() { /* this.valueMap.clear(); this.state = State.TOP; */ reset(State.TOP); } public boolean isBottom() { return state == State.BOTTOM; } public void setBottom() { /* this.valueMap.clear(); this.state = State.BOTTOM; */ reset(State.BOTTOM); } private void reset(State state) { valueMap.clear(); whereAlways.clear(); whereNever.clear(); this.state = state; } public void propagateAcrossPhiNode(ValueNumber fromVN, ValueNumber toVN) { assert isValid(); setValue(toVN, getValue(fromVN)); // Propagate source/sink information transferSourceSinkInfoSet(whereAlways, fromVN, toVN); transferSourceSinkInfoSet(whereNever, fromVN, toVN); // Remove all information about the "from" value valueMap.remove(fromVN); whereAlways.remove(fromVN); whereNever.remove(fromVN); } private static void transferSourceSinkInfoSet( Map<ValueNumber, Set<SourceSinkInfo>> sourceSinkInfoSetMap, ValueNumber fromVN, ValueNumber toVN) { Set<SourceSinkInfo> locSet = getOrCreateSourceSinkInfoSet(sourceSinkInfoSetMap, fromVN); for (SourceSinkInfo loc : locSet) { addSourceSinkInfo(sourceSinkInfoSetMap, toVN, loc); } } public void mergeWith(TypeQualifierValueSet fact) throws DataflowAnalysisException { if (!isValid() || !fact.isValid()) { throw new DataflowAnalysisException("merging an invalid TypeQualifierValueSet"); } Set<ValueNumber> interesting = new HashSet<ValueNumber>(); interesting.addAll(this.valueMap.keySet()); interesting.addAll(fact.valueMap.keySet()); for (ValueNumber vn : interesting) { setValue(vn, FlowValue.meet(this.getValue(vn), fact.getValue(vn))); mergeSourceSinkInfoSets(this.whereAlways, fact.whereAlways, vn); mergeSourceSinkInfoSets(this.whereNever, fact.whereNever, vn); } } private void mergeSourceSinkInfoSets( Map<ValueNumber, Set<SourceSinkInfo>> sourceSinkInfoSetMapToUpdate, Map<ValueNumber, Set<SourceSinkInfo>> otherSourceSinkInfoSetMap, ValueNumber vn) { if (!otherSourceSinkInfoSetMap.containsKey(vn)) { return; } Set<SourceSinkInfo> sourceSinkInfoSetToUpdate = getOrCreateSourceSinkInfoSet(sourceSinkInfoSetMapToUpdate, vn); sourceSinkInfoSetToUpdate.addAll(getOrCreateSourceSinkInfoSet(otherSourceSinkInfoSetMap, vn)); } /* (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (obj == null || obj.getClass() != this.getClass()) { return false; } TypeQualifierValueSet other = (TypeQualifierValueSet) obj; if (this.isValid() && other.isValid()) { return this.valueMap.equals(other.valueMap); } else { return this.state == other.state; } } /* (non-Javadoc) * @see java.lang.Object#hashCode() */ @Override public int hashCode() { throw new UnsupportedOperationException(); } @Override public String toString() { if (state != State.VALID) { return state.toString(); } TreeSet<ValueNumber> interesting = new TreeSet<ValueNumber>(); interesting.addAll(valueMap.keySet()); StringBuilder buf = new StringBuilder(); buf.append("{"); for (ValueNumber vn : interesting) { if (buf.length() > 1) { buf.append(", "); } buf.append(valueNumberToString(vn)); } buf.append("}"); return buf.toString(); } public String valueNumberToString(ValueNumber vn) { StringBuilder buf = new StringBuilder(); buf.append(vn.getNumber()); buf.append("->"); buf.append(getValue(vn).toString()); buf.append("["); appendSourceSinkInfos(buf, "YES=", getOrCreateSourceSinkInfoSet(whereAlways, vn)); buf.append(","); appendSourceSinkInfos(buf, "NO=", getOrCreateSourceSinkInfoSet(whereNever, vn)); buf.append("]"); return buf.toString(); } private static void appendSourceSinkInfos(StringBuilder buf, String key, Set<SourceSinkInfo> sourceSinkInfoSet) { TreeSet<SourceSinkInfo> sortedLocSet = new TreeSet<SourceSinkInfo>(); sortedLocSet.addAll(sourceSinkInfoSet); boolean first = true; buf.append(key); buf.append("("); for (SourceSinkInfo loc : sortedLocSet) { if (first) { first = false; } else { buf.append(","); } buf.append(loc.getLocation().toCompactString()); } buf.append(")"); } }