Constant boxing via SPECIAL_FIELD_VALUE; do not store boxed constants in memory state

This commit is contained in:
Tagir Valeev
2018-11-15 15:26:41 +07:00
parent 0679b6e208
commit bde1e018fe
6 changed files with 82 additions and 106 deletions
@@ -45,7 +45,6 @@ import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* Invariant: qualifiers of the variables used in myEqClasses or myVariableStates must be canonical variables
@@ -402,14 +401,14 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
return false;
}
EqClass eqClass = getEqClass(dfaValue);
DfaValue constant = eqClass == null ? null : eqClass.findConstant(false);
DfaConstValue constant = eqClass == null ? null : eqClass.findConstant();
return constant == null || isNaN(constant);
}
private boolean isEffectivelyNaN(@NotNull DfaValue dfaValue) {
EqClass eqClass = getEqClass(dfaValue);
return eqClass != null && isNaN(eqClass.findConstant(false));
return eqClass != null && isNaN(eqClass.findConstant());
}
List<EqClass> getEqClasses() {
@@ -465,7 +464,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
EqClass c1 = myEqClasses.get(c1Index);
EqClass c2 = myEqClasses.get(c2Index);
if (c1.findConstant(true) != null && c2.findConstant(true) != null) return false;
if (c1.findConstant() != null && c2.findConstant() != null) return false;
EqClass newClass = new EqClass(c1);
@@ -578,14 +577,14 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
*/
public Set<Object> getNonEqualConstants(DfaVariableValue value) {
int index = getEqClassIndex(value);
if (index == -1 || myEqClasses.get(index).findConstant(true) != null) return Collections.emptySet();
return getDistinctClassPairs().stream()
if (index == -1 || myEqClasses.get(index).findConstant() != null) return Collections.emptySet();
return StreamEx.of(getDistinctClassPairs())
.map(pair -> pair.getOtherClass(index))
.filter(Objects::nonNull)
.map(otherClass -> otherClass.findConstant(true))
.filter(Objects::nonNull)
.map(constant -> ((DfaConstValue)unwrap(constant)).getValue())
.collect(Collectors.toSet());
.nonNull()
.map(EqClass::findConstant)
.nonNull()
.map(DfaConstValue::getValue)
.toSet();
}
@Override
@@ -596,8 +595,12 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
return (DfaConstValue)value;
}
if (value instanceof DfaVariableValue) {
PsiPrimitiveType unboxedType = PsiPrimitiveType.getUnboxedType(value.getType());
if (unboxedType != null) {
value = myFactory.getBoxedFactory().createUnboxed(value, unboxedType);
}
EqClass ec = getEqClass(value);
return ec == null ? null : (DfaConstValue)unwrap(ec.findConstant(true));
return ec == null ? null : ec.findConstant();
}
return null;
}
@@ -619,7 +622,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
@Override
public boolean castTopOfStack(@NotNull DfaPsiType type) {
DfaValue value = unwrap(peek());
DfaValue value = peek();
DfaFactMap facts = null;
if (value instanceof DfaVariableValue) {
@@ -673,7 +676,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
for (Iterator<DistinctPairSet.DistinctPair> iterator = myDistinctClasses.iterator(); iterator.hasNext(); ) {
DistinctPairSet.DistinctPair pair = iterator.next();
EqClass otherClass = pair.getOtherClass(index);
if (otherClass != null && otherClass.findConstant(false) != getFactory().getConstFactory().getNull()) {
if (otherClass != null && otherClass.findConstant() != getFactory().getConstFactory().getNull()) {
iterator.remove();
}
}
@@ -717,13 +720,6 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
return true;
}
static DfaValue unwrap(DfaValue value) {
if (value instanceof DfaBoxedValue) {
return ((DfaBoxedValue)value).getWrappedValue();
}
return value;
}
@Override
public boolean applyContractCondition(DfaValue condition) {
if (condition instanceof DfaRelationValue) {
@@ -768,13 +764,8 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
public boolean applyCondition(DfaValue dfaCond) {
if (dfaCond instanceof DfaUnknownValue) return true;
if (dfaCond instanceof DfaVariableValue) {
DfaVariableValue dfaVar = (DfaVariableValue)dfaCond;
DfaValue dfaTrue = myFactory.getConstFactory().getTrue();
if (dfaVar.getSource() == SpecialField.UNBOX) {
dfaVar = dfaVar.getQualifier();
dfaTrue = myFactory.getBoxedFactory().createBoxed(dfaTrue, null);
}
return applyRelationCondition(myFactory.getRelationFactory().createRelation(dfaVar, RelationType.EQ, dfaTrue));
return applyRelationCondition(myFactory.getRelationFactory().createRelation(dfaCond, RelationType.EQ, dfaTrue));
}
if (dfaCond instanceof DfaConstValue) {
@@ -815,6 +806,10 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
!applyRelation(dfaLeft, getFactory().getConstFactory().getNull(), true)) {
return false;
}
if ((relationType == RelationType.EQ || relationType.isInequality()) &&
!applyUnboxedRelation(dfaLeft, dfaRight, relationType.isInequality())) {
return false;
}
if (dfaLeft instanceof DfaVariableValue) {
DfaVariableValue dfaVar = (DfaVariableValue)dfaLeft;
@@ -962,14 +957,19 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
}
private boolean applyUnboxedRelation(@NotNull DfaValue dfaLeft, DfaValue dfaRight, boolean negated) {
if (!(dfaLeft instanceof DfaBoxedValue) && !TypeConversionUtil.isPrimitiveWrapper(dfaLeft.getType()) ||
!(dfaRight instanceof DfaBoxedValue) && !TypeConversionUtil.isPrimitiveWrapper(dfaRight.getType())) {
if (dfaLeft instanceof DfaVariableValue && !TypeConversionUtil.isPrimitiveWrapper(dfaLeft.getType()) ||
dfaRight instanceof DfaVariableValue && !TypeConversionUtil.isPrimitiveWrapper(dfaRight.getType())) {
return true;
}
DfaBoxedValue.Factory boxedFactory = myFactory.getBoxedFactory();
DfaValue unboxedLeft = boxedFactory.createUnboxed(dfaLeft, null);
DfaValue unboxedRight = boxedFactory.createUnboxed(dfaRight, null);
DfaConstValue leftConst = getConstantValue(unboxedLeft);
DfaConstValue rightConst = getConstantValue(unboxedRight);
if (leftConst != null && rightConst != null) {
return leftConst.getValue().equals(rightConst.getValue()) != negated;
}
if (negated && (PsiType.FLOAT.equals(unboxedLeft.getType()) || PsiType.DOUBLE.equals(unboxedLeft.getType()))) {
// If floating point wrappers are not equal, unboxed versions could still be equal if they are 0.0 and -0.0
return true;
@@ -1009,8 +1009,8 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
for (Iterator<DistinctPairSet.DistinctPair> iterator = myDistinctClasses.iterator(); iterator.hasNext(); ) {
DistinctPairSet.DistinctPair pair = iterator.next();
DfaConstValue const1 = (DfaConstValue)pair.getFirst().findConstant(false);
DfaConstValue const2 = (DfaConstValue)pair.getSecond().findConstant(false);
DfaConstValue const1 = pair.getFirst().findConstant();
DfaConstValue const2 = pair.getSecond().findConstant();
if (const1 != null && const2 != null && !preserveConstantDistinction(const1.getValue(), const2.getValue())) {
iterator.remove();
}
@@ -1089,24 +1089,9 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
EqClass ec1 = myEqClasses.get(i1);
EqClass ec2 = myEqClasses.get(i2);
if (ec1 == null || ec2 == null) return ThreeState.UNSURE;
DfaValue constOrBox1 = ec1.findConstant(true);
DfaValue constOrBox2 = ec2.findConstant(true);
if (constOrBox1 == null || constOrBox2 == null) return ThreeState.UNSURE;
if (constOrBox1 instanceof DfaConstValue && constOrBox2 instanceof DfaConstValue) {
return areConstantsEqual((DfaConstValue)constOrBox1, (DfaConstValue)constOrBox2);
}
if (constOrBox1 instanceof DfaBoxedValue && constOrBox2 instanceof DfaBoxedValue) {
DfaValue wrapped1 = ((DfaBoxedValue)constOrBox1).getWrappedValue();
DfaValue wrapped2 = ((DfaBoxedValue)constOrBox2).getWrappedValue();
if (wrapped1 instanceof DfaConstValue && wrapped2 instanceof DfaConstValue &&
areConstantsEqual((DfaConstValue)wrapped1, (DfaConstValue)wrapped2) == ThreeState.NO) {
return ThreeState.NO;
}
}
return ThreeState.UNSURE;
}
private static ThreeState areConstantsEqual(DfaConstValue const1, DfaConstValue const2) {
DfaConstValue const1 = ec1.findConstant();
DfaConstValue const2 = ec2.findConstant();
if (const1 == null || const2 == null) return ThreeState.UNSURE;
Number value1 = ObjectUtils.tryCast(const1.getValue(), Number.class);
Number value2 = ObjectUtils.tryCast(const2.getValue(), Number.class);
if (value1 == null || value2 == null) return ThreeState.UNSURE;
@@ -1207,16 +1192,8 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
}
if (value instanceof DfaBoxedValue) {
DfaBoxedValue boxedValue = (DfaBoxedValue)value;
if (boxedValue.getWrappedValue() instanceof DfaVariableValue) {
DfaValue canonicalized = canonicalize(boxedValue.getWrappedValue());
return Objects.requireNonNull(myFactory.getBoxedFactory().createBoxed(canonicalized, boxedValue.getType()));
}
}
if (value instanceof DfaConstValue) {
Object constant = ((DfaConstValue)value).getValue();
if (Double.valueOf(-0.0).equals(constant)) {
return myFactory.getConstFactory().createFromValue(0.0, PsiType.DOUBLE);
}
DfaValue canonicalized = canonicalize(boxedValue.getWrappedValue());
return Objects.requireNonNull(myFactory.getBoxedFactory().createBoxed(canonicalized, boxedValue.getType()));
}
return value;
}
@@ -15,10 +15,7 @@
*/
package com.intellij.codeInspection.dataFlow;
import com.intellij.codeInspection.dataFlow.value.DfaConstValue;
import com.intellij.codeInspection.dataFlow.value.DfaValue;
import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
import com.intellij.codeInspection.dataFlow.value.*;
import com.intellij.openapi.util.Ref;
import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.ContainerUtil;
@@ -74,12 +71,12 @@ class EqClass extends SortedIntSet {
List<DfaVariableValue> vars = ContainerUtil.newArrayList();
forEach(id -> {
DfaValue value = myFactory.getValue(id);
if (unwrap) {
value = DfaMemoryStateImpl.unwrap(value);
}
if (value instanceof DfaVariableValue) {
vars.add((DfaVariableValue)value);
}
else if (unwrap && value instanceof DfaBoxedValue) {
vars.add(((DfaBoxedValue)value).getWrappedValue());
}
return true;
});
return vars;
@@ -109,12 +106,12 @@ class EqClass extends SortedIntSet {
}
@Nullable
DfaValue findConstant(boolean wrapped) {
Ref<DfaValue> result = new Ref<>();
DfaConstValue findConstant() {
Ref<DfaConstValue> result = new Ref<>();
forEach(id -> {
DfaValue value = myFactory.getValue(id);
if (value instanceof DfaConstValue || wrapped && DfaMemoryStateImpl.unwrap(value) instanceof DfaConstValue) {
result.set(value);
if (value instanceof DfaConstValue) {
result.set((DfaConstValue)value);
return false;
}
return true;
@@ -122,20 +119,9 @@ class EqClass extends SortedIntSet {
return result.get();
}
@Nullable
private static DfaConstValue asConstantValue(DfaValue value) {
value = DfaMemoryStateImpl.unwrap(value);
return value instanceof DfaConstValue ? (DfaConstValue)value : null;
}
boolean containsConstantsOnly() {
for (int i = 0; i < size(); i++) {
if (asConstantValue(myFactory.getValue(get(i))) == null) {
return false;
}
}
return true;
int size = size();
return size <= 1 && (size == 0 || myFactory.getValue(get(0)) instanceof DfaConstValue);
}
}
@@ -907,7 +907,7 @@ public class StandardInstructionVisitor extends InstructionVisitor {
long bLong = b.longValue();
if (aLong != bLong) return aLong > bLong ? 1 : -1;
return Double.compare(a.doubleValue(), b.doubleValue());
return a.doubleValue() == 0.0 && b.doubleValue() == 0.0 ? 0 : Double.compare(a.doubleValue(), b.doubleValue());
}
private DfaInstructionState[] makeBooleanResultArray(BinopInstruction instruction,
@@ -606,11 +606,11 @@ class StateMerger {
static final class EqClassInfo {
final List<DfaVariableValue> vars;
final DfaValue constant;
final DfaConstValue constant;
EqClassInfo(EqClass eqClass) {
vars = eqClass.getVariables(false);
constant = eqClass.findConstant(true);
constant = eqClass.findConstant();
}
}
}
@@ -15,22 +15,20 @@
*/
package com.intellij.codeInspection.dataFlow.value;
import com.intellij.codeInspection.dataFlow.SpecialField;
import com.intellij.codeInspection.dataFlow.*;
import com.intellij.psi.PsiPrimitiveType;
import com.intellij.psi.PsiType;
import com.intellij.psi.util.TypeConversionUtil;
import gnu.trove.TIntObjectHashMap;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
public class DfaBoxedValue extends DfaValue {
private final DfaValue myWrappedValue;
private final @NotNull DfaVariableValue myWrappedValue;
private final @Nullable PsiType myType;
private DfaBoxedValue(DfaValue valueToWrap, DfaValueFactory factory, @Nullable PsiType type) {
private DfaBoxedValue(@NotNull DfaVariableValue valueToWrap, DfaValueFactory factory, @Nullable PsiType type) {
super(factory);
myWrappedValue = valueToWrap;
myType = type;
@@ -41,7 +39,8 @@ public class DfaBoxedValue extends DfaValue {
return "Boxed "+myWrappedValue.toString();
}
public DfaValue getWrappedValue() {
@NotNull
public DfaVariableValue getWrappedValue() {
return myWrappedValue;
}
@@ -52,7 +51,7 @@ public class DfaBoxedValue extends DfaValue {
}
public static class Factory {
private final Map<Object, DfaBoxedValue> cachedValues = new HashMap<>();
private final TIntObjectHashMap<DfaBoxedValue> cachedValues = new TIntObjectHashMap<>();
private final DfaValueFactory myFactory;
@@ -60,8 +59,8 @@ public class DfaBoxedValue extends DfaValue {
myFactory = factory;
}
public DfaValue getBoxedIfExists(DfaVariableValue variable) {
return cachedValues.get(variable);
public DfaBoxedValue getBoxedIfExists(DfaVariableValue variable) {
return cachedValues.get(variable.getID());
}
@Nullable
@@ -69,15 +68,23 @@ public class DfaBoxedValue extends DfaValue {
if (valueToWrap instanceof DfaVariableValue && ((DfaVariableValue)valueToWrap).getSource() == SpecialField.UNBOX) {
return ((DfaVariableValue)valueToWrap).getQualifier();
}
Object o = valueToWrap instanceof DfaConstValue
? ((DfaConstValue)valueToWrap).getValue()
: valueToWrap instanceof DfaVariableValue ? valueToWrap : null;
if (o == null) return null;
DfaBoxedValue boxedValue = cachedValues.get(o);
if (boxedValue == null) {
cachedValues.put(o, boxedValue = new DfaBoxedValue(valueToWrap, myFactory, type));
if (valueToWrap instanceof DfaConstValue) {
DfaConstValue constValue = (DfaConstValue)valueToWrap;
DfaFactMap facts = DfaFactMap.EMPTY
.with(DfaFactType.TYPE_CONSTRAINT, type == null ? null : TypeConstraint.exact(myFactory.createDfaType(type)))
.with(DfaFactType.NULLABILITY, DfaNullability.NOT_NULL)
.with(DfaFactType.SPECIAL_FIELD_VALUE, SpecialField.UNBOX.withValue(constValue.getValue(), constValue.getType()));
return myFactory.getFactFactory().createValue(facts);
}
return boxedValue;
if (valueToWrap instanceof DfaVariableValue) {
int id = valueToWrap.getID();
DfaBoxedValue boxedValue = cachedValues.get(id);
if (boxedValue == null) {
cachedValues.put(id, boxedValue = new DfaBoxedValue((DfaVariableValue)valueToWrap, myFactory, type));
}
return boxedValue;
}
return null;
}
@NotNull
@@ -85,6 +92,12 @@ public class DfaBoxedValue extends DfaValue {
if (value instanceof DfaBoxedValue) {
return ((DfaBoxedValue)value).getWrappedValue();
}
if (value instanceof DfaFactMapValue) {
SpecialFieldValue sfValue = ((DfaFactMapValue)value).get(DfaFactType.SPECIAL_FIELD_VALUE);
if (sfValue != null && sfValue.getField() == SpecialField.UNBOX) {
return sfValue.toConstant(myFactory);
}
}
if (value instanceof DfaConstValue) {
return TypeConversionUtil.isPrimitiveAndNotNull(((DfaConstValue)value).getType()) ? value : DfaUnknownValue.getInstance();
}
@@ -4,12 +4,12 @@ import org.jetbrains.annotations.NotNull;
class ArrayStoreProblems {
void test(String[] args, Integer[] args2) {
Object[] arr = args;
arr[0] <warning descr="Storing element of type 'int' to array of 'java.lang.String' elements may produce 'ArrayStoreException'">=</warning> 123;
arr[0] <warning descr="Storing element of type 'java.lang.Integer' to array of 'java.lang.String' elements may produce 'ArrayStoreException'">=</warning> 123;
arr = args2;
arr[1] = 124;
arr[2] <warning descr="Storing element of type 'java.lang.String' to array of 'java.lang.Integer' elements may produce 'ArrayStoreException'">=</warning> "foo";
arr = args;
arr[3] = "bar";
arr[4] <warning descr="Storing element of type 'int' to array of 'java.lang.String' elements may produce 'ArrayStoreException'">=</warning> 125;
arr[4] <warning descr="Storing element of type 'java.lang.Integer' to array of 'java.lang.String' elements may produce 'ArrayStoreException'">=</warning> 125;
}
}