/*
* 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.npe;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import edu.umd.cs.findbugs.ba.Frame;
import edu.umd.cs.findbugs.ba.vna.ValueNumber;
import edu.umd.cs.findbugs.ba.vna.ValueNumberAnalysisFeatures;
import edu.umd.cs.findbugs.ba.vna.ValueNumberFrame;
import edu.umd.cs.findbugs.util.Strings;
import edu.umd.cs.findbugs.util.Util;
public class IsNullValueFrame extends Frame<IsNullValue> {
static class PointerEqualityInfo {
final ValueNumber addr1, addr2;
final boolean areEqual;
public PointerEqualityInfo(ValueNumber addr1, ValueNumber addr2, boolean areEqual) {
if (addr1.getNumber() > addr2.getNumber()) {
ValueNumber tmp = addr1;
addr1 = addr2;
addr2 = tmp;
}
this.addr1 = addr1;
this.addr2 = addr2;
this.areEqual = areEqual;
}
@Override
public int hashCode() {
throw new UnsupportedOperationException();
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof PointerEqualityInfo))
return false;
PointerEqualityInfo other = (PointerEqualityInfo) obj;
return this.addr1.equals(other.addr1) && this.addr2.equals(other.addr2) && this.areEqual == other.areEqual;
}
}
private IsNullConditionDecision decision;
private final boolean trackValueNumbers;
public boolean isTrackValueNumbers() {
return trackValueNumbers;
}
private Map<ValueNumber, IsNullValue> knownValueMap;
public IsNullValueFrame(int numLocals, boolean trackValueNumbers) {
super(numLocals);
this.trackValueNumbers = trackValueNumbers;
if (trackValueNumbers) {
this.knownValueMap = new HashMap<ValueNumber, IsNullValue>(3);
}
}
public void cleanStaleKnowledge(ValueNumberFrame vnaFrameAfter) {
if (vnaFrameAfter.isTop() && !isTop())
throw new IllegalArgumentException("VNA frame is top");
if (!trackValueNumbers)
return;
if (!ValueNumberAnalysisFeatures.REDUNDANT_LOAD_ELIMINATION)
return;
for (Iterator<ValueNumber> i = knownValueMap.keySet().iterator(); i.hasNext();) {
ValueNumber v = i.next();
if (vnaFrameAfter.getLoad(v) == null) {
if (IsNullValueAnalysis.DEBUG)
System.out.println("PURGING " + v);
i.remove();
}
}
}
@Override
public void setTop() {
super.setTop();
if (trackValueNumbers) {
knownValueMap.clear();
}
decision = null;
}
public void toExceptionValues() {
for (int i = 0; i < getNumSlots(); ++i)
setValue(i, getValue(i).toExceptionValue());
if (trackValueNumbers) {
Map<ValueNumber, IsNullValue> replaceMap = new HashMap<ValueNumber, IsNullValue>();
for (Map.Entry<ValueNumber, IsNullValue> entry : knownValueMap.entrySet()) {
replaceMap.put(entry.getKey(), entry.getValue().toExceptionValue());
}
this.knownValueMap = replaceMap;
}
}
public void setDecision(@CheckForNull IsNullConditionDecision decision) {
this.decision = decision;
}
public @CheckForNull
IsNullConditionDecision getDecision() {
return decision;
}
public void setKnownValue(@Nonnull ValueNumber valueNumber, @Nonnull IsNullValue knownValue) {
assert trackValueNumbers;
if (valueNumber == null || knownValue == null)
throw new NullPointerException();
knownValueMap.put(valueNumber, knownValue);
if (IsNullValueAnalysis.DEBUG) {
System.out.println("Updated information for " + valueNumber);
System.out.println(" now " + this);
}
}
public void useNewValueNumberForLoad(ValueNumber oldValueNumber, ValueNumber newValueNumber) {
if (oldValueNumber == null || newValueNumber == null)
throw new NullPointerException();
if (newValueNumber.equals(oldValueNumber) || !trackValueNumbers)
return;
IsNullValue isNullValue = knownValueMap.get(oldValueNumber);
if (isNullValue != null) {
knownValueMap.put(newValueNumber, isNullValue);
knownValueMap.remove(oldValueNumber);
}
}
public @CheckForNull
IsNullValue getKnownValue(ValueNumber valueNumber) {
assert trackValueNumbers;
return knownValueMap.get(valueNumber);
}
public Collection<ValueNumber> getKnownValues() {
if (trackValueNumbers) {
return knownValueMap.keySet();
} else {
return Collections.<ValueNumber> emptySet();
}
}
public Collection<Map.Entry<ValueNumber, IsNullValue>> getKnownValueMapEntrySet() {
if (trackValueNumbers) {
return knownValueMap.entrySet();
} else {
return Collections.<Map.Entry<ValueNumber, IsNullValue>> emptySet();
}
}
public void mergeKnownValuesWith(IsNullValueFrame otherFrame) {
assert trackValueNumbers;
if (IsNullValueAnalysis.DEBUG) {
System.out.println("merge");
System.out.println(" " + this);
System.out.println(" with" + otherFrame);
}
Map<ValueNumber, IsNullValue> replaceMap = new HashMap<ValueNumber, IsNullValue>();
for (Map.Entry<ValueNumber, IsNullValue> entry : knownValueMap.entrySet()) {
IsNullValue otherKnownValue = otherFrame.knownValueMap.get(entry.getKey());
if (otherKnownValue == null) {
if (IsNullValueAnalysis.DEBUG) {
System.out.println("No match for " + entry.getKey());
}
continue;
}
IsNullValue mergedValue = IsNullValue.merge(entry.getValue(), otherKnownValue);
replaceMap.put(entry.getKey(), mergedValue);
if (IsNullValueAnalysis.DEBUG && !mergedValue.equals(entry.getValue())) {
System.out.println("Updated information for " + entry.getKey());
System.out.println(" was " + entry.getValue());
System.out.println(" merged value " + mergedValue);
}
}
knownValueMap.clear();
knownValueMap.putAll(replaceMap);
if (IsNullValueAnalysis.DEBUG) {
System.out.println("resulting in " + this);
}
}
/*
* (non-Javadoc)
*
* @see edu.umd.cs.findbugs.ba.Frame#copyFrom(edu.umd.cs.findbugs.ba.Frame)
*/
@Override
public void copyFrom(Frame<IsNullValue> other) {
super.copyFrom(other);
decision = ((IsNullValueFrame) other).decision;
if (trackValueNumbers) {
knownValueMap = Util.makeSmallHashMap(((IsNullValueFrame) other).knownValueMap);
}
}
@Override
public boolean sameAs(Frame<IsNullValue> other) {
if (!(other instanceof IsNullValueFrame))
return false;
if (!super.sameAs(other))
return false;
IsNullValueFrame o2 = (IsNullValueFrame) other;
if (!Util.nullSafeEquals(decision, o2.decision))
return false;
if (trackValueNumbers && !Util.nullSafeEquals(knownValueMap, o2.knownValueMap))
return false;
return true;
}
@Override
public String toString() {
String result = super.toString();
if (decision != null) {
result = result + ", [decision=" + decision.toString() + "]";
}
if (knownValueMap != null) {
// result = result + ", [known=" + knownValueMap.toString() + "]";
StringBuilder buf = new StringBuilder();
buf.append("{");
boolean first = true;
for (Map.Entry<ValueNumber, IsNullValue> entry : knownValueMap.entrySet()) {
if (!first) {
buf.append(", ");
} else {
first = false;
}
buf.append(Strings.trimComma(entry.getKey().toString()));
buf.append("->");
buf.append(Strings.trimComma(entry.getValue().toString()));
}
buf.append("}");
result += ", [known=" + buf.toString() + "]";
}
return result;
}
/**
* Downgrade all NSP values in frame. Should be called when a non-exception
* control split occurs.
*/
public void downgradeOnControlSplit() {
final int numSlots = getNumSlots();
for (int i = 0; i < numSlots; ++i) {
IsNullValue value = getValue(i);
value = value.downgradeOnControlSplit();
setValue(i, value);
}
if (knownValueMap != null) {
for (Map.Entry<ValueNumber, IsNullValue> entry : knownValueMap.entrySet()) {
entry.setValue(entry.getValue().downgradeOnControlSplit());
}
}
}
}
// vim:ts=4