mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Constant boxing via SPECIAL_FIELD_VALUE; do not store boxed constants in memory state
This commit is contained in:
+36
-59
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+31
-18
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user