immutable DfaVariableState, for faster copying and less memory usage

This commit is contained in:
peter
2013-09-23 16:30:25 +02:00
parent 666ed529c0
commit e8dbc2135e
7 changed files with 147 additions and 111 deletions
@@ -45,12 +45,14 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
private final Stack<DfaValue> myStack = new Stack<DfaValue>();
private TIntStack myOffsetStack = new TIntStack(1);
private final TLongHashSet myDistinctClasses = new TLongHashSet();
private final THashMap<DfaVariableValue,DfaVariableState> myVariableStates = new THashMap<DfaVariableValue, DfaVariableState>();
private final THashMap<DfaVariableValue,DfaVariableState> myVariableStates = ContainerUtil.newTroveMap();
private THashMap<DfaVariableValue,DfaVariableState> myDefaultVariableStates; // shared between all states
private final THashSet<DfaVariableValue> myUnknownVariables = new THashSet<DfaVariableValue>();
private boolean myEphemeral;
public DfaMemoryStateImpl(final DfaValueFactory factory) {
myFactory = factory;
myDefaultVariableStates = ContainerUtil.newTroveMap();
}
public DfaValueFactory getFactory() {
@@ -65,6 +67,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
public DfaMemoryStateImpl createCopy() {
DfaMemoryStateImpl newState = createNew();
newState.myDefaultVariableStates = myDefaultVariableStates;
newState.myEphemeral = myEphemeral;
newState.myStack.addAll(myStack);
newState.myDistinctClasses.addAll(myDistinctClasses.toArray());
@@ -76,9 +79,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
newState.myEqClasses.add(aClass != null ? new EqClass(aClass) : null);
}
for (DfaVariableValue dfaVariableValue : myVariableStates.keySet()) {
newState.myVariableStates.put(dfaVariableValue, myVariableStates.get(dfaVariableValue).clone());
}
newState.myVariableStates.putAll(myVariableStates);
return newState;
}
@@ -98,19 +99,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
if (myDistinctClasses.size() != that.myDistinctClasses.size()) return false;
if (!getNonTrivialEqClasses().equals(that.getNonTrivialEqClasses())) return false;
if (!getDistinctClassPairs().equals(that.getDistinctClassPairs())) return false;
for (DfaVariableValue var : myVariableStates.keySet()) {
DfaVariableState thatState = that.myVariableStates.get(var);
if (!myVariableStates.get(var).equals(thatState != null ? thatState : createVariableState(var))) {
return false;
}
}
for (DfaVariableValue var : that.myVariableStates.keySet()) {
if (!myVariableStates.containsKey(var) && !that.myVariableStates.get(var).equals(createVariableState(var))) {
return false;
}
}
if (!myVariableStates.equals(that.myVariableStates)) return false;
return true;
}
@@ -142,7 +131,11 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
}
public int hashCode() {
return ((getNonTrivialEqClasses().hashCode() * 31 + getDistinctClassPairs().hashCode()) * 31 + myStack.hashCode()) * 31 + myUnknownVariables.hashCode();
return (((getNonTrivialEqClasses().hashCode() * 31 +
getDistinctClassPairs().hashCode()) * 31 +
myStack.hashCode()) * 31 +
myUnknownVariables.hashCode()) * 31 +
myVariableStates.hashCode();
}
@SuppressWarnings({"HardCodedStringLiteral"})
@@ -218,15 +211,14 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
if (var == value) return;
flushVariable(var);
DfaVariableState varState = getVariableState(var);
if (value instanceof DfaUnknownValue) {
varState.setNullable(false);
setVariableState(var, getVariableState(var).withNullable(false));
return;
}
varState.setValue(value);
setVariableState(var, getVariableState(var).withValue(value));
if (value instanceof DfaTypeValue) {
varState.setNullable(((DfaTypeValue)value).isNullable());
setVariableState(var, getVariableState(var).withNullable(((DfaTypeValue)value).isNullable()));
DfaRelationValue dfaInstanceof = myFactory.getRelationFactory().createRelation(var, value, JavaTokenType.INSTANCEOF_KEYWORD, false);
if (((DfaTypeValue)value).isNotNull()) {
applyCondition(dfaInstanceof);
@@ -240,15 +232,15 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
applyCondition(dfaEqual);
if (value instanceof DfaVariableValue) {
myVariableStates.put(var, varState = getVariableState((DfaVariableValue)value).clone());
setVariableState(var, getVariableState((DfaVariableValue)value));
}
else if (value instanceof DfaBoxedValue) {
varState.setNullable(false);
setVariableState(var, getVariableState(var).withNullable(false));
applyCondition(compareToNull(var, true));
}
}
if (varState.isNotNull()) {
if (getVariableState(var).isNotNull()) {
applyCondition(compareToNull(var, true));
}
}
@@ -448,7 +440,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
public boolean isNull(DfaValue dfaValue) {
if (dfaValue instanceof DfaTypeValue && ((DfaTypeValue)dfaValue).isNotNull()) return false;
if (dfaValue instanceof DfaConstValue) return ((DfaConstValue)dfaValue).getConstant() == null;
if (dfaValue instanceof DfaConstValue) return ((DfaConstValue)dfaValue).getValue() == null;
if (dfaValue instanceof DfaVariableValue) {
int c1Index = getEqClassIndex(dfaValue);
@@ -509,7 +501,13 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
DfaVariableValue dfaVar = (DfaVariableValue)left;
DfaTypeValue dfaType = (DfaTypeValue)dfaCond.getRightOperand();
return isNull(dfaVar) || getVariableState(dfaVar).setInstanceofValue(dfaType);
if (isUnknownState(dfaVar) || isNull(dfaVar)) return true;
DfaVariableState newState = getVariableState(dfaVar).withInstanceofValue(dfaType);
if (newState != null) {
setVariableState(dfaVar, newState);
return true;
}
return false;
}
static DfaValue unwrap(DfaValue value) {
@@ -561,12 +559,25 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
if (dfaRight instanceof DfaTypeValue) {
if (dfaLeft instanceof DfaVariableValue) {
DfaVariableState varState = getVariableState((DfaVariableValue)dfaLeft);
DfaVariableValue dfaVar = (DfaVariableValue)dfaLeft;
if (isUnknownState(dfaVar)) return true;
if (isNegated) {
return varState.addNotInstanceofValue((DfaTypeValue)dfaRight) || applyCondition(compareToNull(dfaVar, false));
DfaVariableState newState = getVariableState(dfaVar).withNotInstanceofValue((DfaTypeValue)dfaRight);
if (newState != null) {
setVariableState(dfaVar, newState);
return true;
}
return applyCondition(compareToNull(dfaVar, false));
}
return applyCondition(compareToNull(dfaVar, true)) && varState.setInstanceofValue((DfaTypeValue)dfaRight);
if (applyCondition(compareToNull(dfaVar, true))) {
DfaVariableState newState = getVariableState(dfaVar).withInstanceofValue((DfaTypeValue)dfaRight);
if (newState != null) {
setVariableState(dfaVar, newState);
return true;
}
}
return false;
}
return true;
}
@@ -593,7 +604,9 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
if (isNotNull(dfaVar)) {
return true;
}
getVariableState(dfaVar).setNullable(true);
if (!isUnknownState(dfaVar)) {
setVariableState(dfaVar, getVariableState(dfaVar).withNullability(Nullness.NULLABLE));
}
}
return false;
}
@@ -734,17 +747,33 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
return myFactory.getRelationFactory().createRelation(dfaVar, dfaNull, JavaTokenType.EQEQ, negated);
}
void setVariableState(DfaVariableValue dfaVar, DfaVariableState state) {
assert !myUnknownVariables.contains(dfaVar);
if (state.equals(myDefaultVariableStates.get(dfaVar))) {
myVariableStates.remove(dfaVar);
} else {
myVariableStates.put(dfaVar, state);
}
}
public DfaVariableState getVariableState(DfaVariableValue dfaVar) {
DfaVariableState state = myVariableStates.get(dfaVar);
if (state == null) {
state = createVariableState(dfaVar);
state = myDefaultVariableStates.get(dfaVar);
if (state == null) {
state = createVariableState(dfaVar);
DfaTypeValue initialType = dfaVar.getTypeValue();
if (initialType != null) {
state = state.withInstanceofValue(initialType);
assert state != null;
}
myDefaultVariableStates.put(dfaVar, state);
}
if (isUnknownState(dfaVar)) {
state.setNullable(false);
return state;
return state.withNullable(false);
}
myVariableStates.put(dfaVar, state);
}
return state;
@@ -760,16 +789,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
@Override
public void flushFields(DfaVariableValue[] fields) {
Set<DfaVariableValue> allVars = new HashSet<DfaVariableValue>(myVariableStates.keySet());
Collections.addAll(allVars, fields);
Set<DfaVariableValue> dependencies = new HashSet<DfaVariableValue>();
for (DfaVariableValue variableValue : allVars) {
dependencies.addAll(myFactory.getVarFactory().getAllQualifiedBy(variableValue));
}
allVars.addAll(dependencies);
for (DfaVariableValue value : allVars) {
for (DfaVariableValue value : myFactory.getVarFactory().getAllVariables()) {
if (myVariableStates.containsKey(value) || getEqClassIndex(value) >= 0) {
if (value.isFlushableByCalls()) {
doFlush(value, true);
@@ -163,9 +163,10 @@ public class DfaUtil {
}
@Override
public DfaInstructionState[] visitAssign(AssignInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
public DfaInstructionState[] visitAssign(AssignInstruction instruction, DataFlowRunner runner, DfaMemoryState _memState) {
final Instruction nextInstruction = runner.getInstruction(instruction.getIndex() + 1);
ValuableDataFlowRunner.MyDfaMemoryState memState = (ValuableDataFlowRunner.MyDfaMemoryState)_memState;
final DfaValue dfaSource = memState.pop();
final DfaValue dfaDest = memState.pop();
@@ -176,10 +177,10 @@ public class DfaUtil {
final IElementType type = parent instanceof PsiAssignmentExpression
? ((PsiAssignmentExpression)parent).getOperationTokenType() : JavaTokenType.EQ;
// store current value - to use in case of '+='
final PsiExpression prevValue = ((ValuableDataFlowRunner.ValuableDfaVariableState)((ValuableDataFlowRunner.MyDfaMemoryState)memState).getVariableState(var)).myExpression;
final PsiExpression prevValue = ((ValuableDataFlowRunner.ValuableDfaVariableState)memState.getVariableState(var)).myExpression;
memState.setVarValue(var, dfaSource);
// state may have been changed so re-retrieve it
final ValuableDataFlowRunner.ValuableDfaVariableState curState = (ValuableDataFlowRunner.ValuableDfaVariableState)((ValuableDataFlowRunner.MyDfaMemoryState)memState).getVariableState(var);
final ValuableDataFlowRunner.ValuableDfaVariableState curState = (ValuableDataFlowRunner.ValuableDfaVariableState)memState.getVariableState(var);
final PsiExpression curValue = curState.myExpression;
final PsiExpression nextValue;
if (type == JavaTokenType.PLUSEQ && prevValue != null) {
@@ -196,7 +197,7 @@ public class DfaUtil {
else {
nextValue = curValue == null ? rightValue : curValue;
}
curState.myExpression = nextValue;
memState.setVariableState(var, curState.withExpression(nextValue));
}
memState.push(dfaDest);
return new DfaInstructionState[]{new DfaInstructionState(nextInstruction, memState)};
@@ -35,28 +35,24 @@ import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
public class DfaVariableState implements Cloneable {
private final Set<DfaPsiType> myInstanceofValues;
private final Set<DfaPsiType> myNotInstanceofValues;
private Nullness myNullability;
public class DfaVariableState {
protected final Set<DfaPsiType> myInstanceofValues;
protected final Set<DfaPsiType> myNotInstanceofValues;
protected final Nullness myNullability;
public DfaVariableState(@NotNull DfaVariableValue dfaVar) {
myInstanceofValues = ContainerUtil.newTroveSet();
myNotInstanceofValues = ContainerUtil.newTroveSet();
myNullability = dfaVar.getInherentNullability();
DfaTypeValue initialType = dfaVar.getTypeValue();
if (initialType != null) {
setInstanceofValue(initialType);
}
this(Collections.<DfaPsiType>emptySet(), Collections.<DfaPsiType>emptySet(), dfaVar.getInherentNullability());
}
protected DfaVariableState(final DfaVariableState toClone) {
myInstanceofValues = ContainerUtil.newTroveSet(toClone.myInstanceofValues);
myNotInstanceofValues = ContainerUtil.newTroveSet(toClone.myNotInstanceofValues);
myNullability = toClone.myNullability;
protected DfaVariableState(Set<DfaPsiType> instanceofValues,
Set<DfaPsiType> notInstanceofValues, Nullness nullability) {
myInstanceofValues = instanceofValues;
myNotInstanceofValues = notInstanceofValues;
myNullability = nullability;
}
public boolean isNullable() {
@@ -77,48 +73,51 @@ public class DfaVariableState implements Cloneable {
return true;
}
public boolean setInstanceofValue(DfaTypeValue dfaType) {
if (dfaType.isNullable()) {
myNullability = Nullness.NULLABLE;
}
if (dfaType.getDfaType().getPsiType() instanceof PsiPrimitiveType) return true;
@Nullable
public DfaVariableState withInstanceofValue(DfaTypeValue dfaType) {
if (dfaType.getDfaType().getPsiType() instanceof PsiPrimitiveType) return this;
if (checkInstanceofValue(dfaType.getDfaType())) {
myInstanceofValues.add(dfaType.getDfaType());
return true;
DfaVariableState result = dfaType.isNullable() ? withNullability(Nullness.NULLABLE) : this;
if (!myInstanceofValues.contains(dfaType.getDfaType())) {
HashSet<DfaPsiType> newInstanceof = ContainerUtil.newHashSet(myInstanceofValues);
newInstanceof.add(dfaType.getDfaType());
result = createCopy(newInstanceof, myNotInstanceofValues, result.myNullability);
}
return result;
}
return false;
return null;
}
public boolean addNotInstanceofValue(DfaTypeValue dfaType) {
if (myNotInstanceofValues.contains(dfaType.getDfaType())) return true;
@Nullable
public DfaVariableState withNotInstanceofValue(DfaTypeValue dfaType) {
if (myNotInstanceofValues.contains(dfaType.getDfaType())) return this;
for (DfaPsiType dfaTypeValue : myInstanceofValues) {
if (dfaType.getDfaType().isAssignableFrom(dfaTypeValue)) return false;
if (dfaType.getDfaType().isAssignableFrom(dfaTypeValue)) return null;
}
myNotInstanceofValues.add(dfaType.getDfaType());
return true;
HashSet<DfaPsiType> newNotInstanceof = ContainerUtil.newHashSet(myNotInstanceofValues);
newNotInstanceof.add(dfaType.getDfaType());
return createCopy(myInstanceofValues, newNotInstanceof, myNullability);
}
public int hashCode() {
return myInstanceofValues.hashCode() + myNotInstanceofValues.hashCode();
return (myInstanceofValues.hashCode() * 31 + myNotInstanceofValues.hashCode()) * 31 + myNullability.hashCode();
}
public boolean equals(Object obj) {
if (obj == this) return true;
if (!(obj instanceof DfaVariableState)) return false;
DfaVariableState aState = (DfaVariableState) obj;
return myInstanceofValues.equals(aState.myInstanceofValues) &&
myNotInstanceofValues.equals(aState.myNotInstanceofValues) &&
myNullability == aState.myNullability;
return myNullability == aState.myNullability &&
myInstanceofValues.equals(aState.myInstanceofValues) &&
myNotInstanceofValues.equals(aState.myNotInstanceofValues);
}
@Override
protected DfaVariableState clone() {
return new DfaVariableState(this);
protected DfaVariableState createCopy(Set<DfaPsiType> instanceofValues, Set<DfaPsiType> notInstanceofValues, Nullness nullability) {
return new DfaVariableState(instanceofValues, notInstanceofValues, nullability);
}
public String toString() {
@@ -143,13 +142,16 @@ public class DfaVariableState implements Cloneable {
return myNullability == Nullness.NOT_NULL;
}
public void setNullable(final boolean nullable) {
if (myNullability != Nullness.NOT_NULL) {
myNullability = nullable ? Nullness.NULLABLE : Nullness.UNKNOWN;
}
DfaVariableState withNullability(@NotNull Nullness nullness) {
return myNullability == nullness ? this : createCopy(myInstanceofValues, myNotInstanceofValues, nullness);
}
public void setValue(DfaValue value) {
public DfaVariableState withNullable(final boolean nullable) {
return myNullability != Nullness.NOT_NULL ? withNullability(nullable ? Nullness.NULLABLE : Nullness.UNKNOWN) : this;
}
public DfaVariableState withValue(DfaValue value) {
return this;
}
@Nullable
@@ -80,7 +80,7 @@ class StateMerger {
copy.doFlush(unknownVar, true);
}
if (removedState.isNull(var)) {
copy.getVariableState(var).setNullable(true);
copy.setVariableState(var, copy.getVariableState(var).withNullability(Nullness.NULLABLE));
}
}
@@ -16,10 +16,14 @@
package com.intellij.codeInspection.dataFlow;
import com.intellij.codeInspection.dataFlow.value.DfaPsiType;
import com.intellij.codeInspection.dataFlow.value.DfaValue;
import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
import com.intellij.psi.PsiExpression;
import org.jetbrains.annotations.Nullable;
import java.util.Set;
/**
* @author Gregory.Shrago
@@ -49,21 +53,37 @@ public class ValuableDataFlowRunner extends DataFlowRunner {
}
static class ValuableDfaVariableState extends DfaVariableState {
DfaValue myValue;
PsiExpression myExpression;
final DfaValue myValue;
final PsiExpression myExpression;
private ValuableDfaVariableState(final DfaVariableValue psiVariable) {
super(psiVariable);
myValue = null;
myExpression = null;
}
protected ValuableDfaVariableState(final ValuableDfaVariableState state) {
super(state);
myExpression = state.myExpression;
private ValuableDfaVariableState(Set<DfaPsiType> instanceofValues,
Set<DfaPsiType> notInstanceofValues,
Nullness nullability, DfaValue value, PsiExpression expression) {
super(instanceofValues, notInstanceofValues, nullability);
myValue = value;
myExpression = expression;
}
@Override
public void setValue(final DfaValue value) {
myValue = value;
protected DfaVariableState createCopy(Set<DfaPsiType> instanceofValues, Set<DfaPsiType> notInstanceofValues, Nullness nullability) {
return new ValuableDfaVariableState(instanceofValues, notInstanceofValues, nullability, myValue, myExpression);
}
@Override
public DfaVariableState withValue(@Nullable final DfaValue value) {
if (value == myValue) return this;
return new ValuableDfaVariableState(myInstanceofValues, myNotInstanceofValues, myNullability, value, myExpression);
}
public ValuableDfaVariableState withExpression(@Nullable final PsiExpression expression) {
if (expression == myExpression) return this;
return new ValuableDfaVariableState(myInstanceofValues, myNotInstanceofValues, myNullability, myValue, expression);
}
@Override
@@ -71,9 +91,5 @@ public class ValuableDataFlowRunner extends DataFlowRunner {
return myValue;
}
@Override
protected ValuableDfaVariableState clone() {
return new ValuableDfaVariableState(this);
}
}
}
@@ -74,6 +74,9 @@ public class DfaVariableValue extends DfaValue {
return value.myDependents;
}
public Iterable<? extends DfaVariableValue> getAllVariables() {
return myExistingVars.values();
}
}
private final PsiModifierListOwner myVariable;
@@ -384,10 +384,4 @@
<description>Condition &lt;code&gt;i&lt;/code&gt; is always &lt;code&gt;true&lt;/code&gt; when reached</description>
</problem>
<problem>
<file>Test.java</file>
<line>62</line>
<module>testUnboxingNPE_7454908424878253728</module>
<description>Switch label&lt;code&gt;case 0:&lt;/code&gt; is unreachable</description>
</problem>
</problems>