@Nullable private DfaRelationValue compareToNull(DfaValue dfaVar, boolean negated) { DfaConstValue dfaNull = myFactory.getConstFactory().getNull(); return myFactory .getRelationFactory() .createRelation(dfaVar, dfaNull, JavaTokenType.EQEQ, negated); }
private DfaValue handleFlush(DfaVariableValue flushed, DfaValue value) { if (value instanceof DfaVariableValue && (value == flushed || myFactory.getVarFactory().getAllQualifiedBy(flushed).contains(value))) { Nullness nullability = isNotNull(value) ? Nullness.NOT_NULL : ((DfaVariableValue) value).getInherentNullability(); return myFactory.createTypeValue(((DfaVariableValue) value).getVariableType(), nullability); } return value; }
protected boolean checkNotNullable( DfaMemoryState state, DfaValue value, NullabilityProblem problem, PsiElement anchor) { boolean notNullable = state.checkNotNullable(value); if (notNullable && problem != NullabilityProblem.passingNullableArgumentToNonAnnotatedParameter) { DfaValueFactory factory = ((DfaMemoryStateImpl) state).getFactory(); state.applyCondition( factory .getRelationFactory() .createRelation(value, factory.getConstFactory().getNull(), NE, false)); } return notNullable; }
@Override public boolean isNotNull(DfaValue dfaVar) { if (dfaVar instanceof DfaConstValue) return ((DfaConstValue) dfaVar).getValue() != null; if (dfaVar instanceof DfaBoxedValue) return true; if (dfaVar instanceof DfaTypeValue) return ((DfaTypeValue) dfaVar).isNotNull(); if (dfaVar instanceof DfaVariableValue) { if (getVariableState((DfaVariableValue) dfaVar).isNotNull()) return true; DfaConstValue constantValue = getConstantValue((DfaVariableValue) dfaVar); if (constantValue != null && constantValue.getValue() != null) return true; } DfaConstValue dfaNull = myFactory.getConstFactory().getNull(); int c1Index = getEqClassIndex(dfaVar); int c2Index = getEqClassIndex(dfaNull); if (c1Index < 0 || c2Index < 0) { return false; } long[] pairs = myDistinctClasses.toArray(); for (long pair : pairs) { if (c1Index == low(pair) && c2Index == high(pair) || c1Index == high(pair) && c2Index == low(pair)) { return true; } } return false; }
private DfaValue getDfaContractReturnValue( MethodContract contract, MethodCallInstruction instruction, DfaValueFactory factory) { switch (contract.returnValue) { case NULL_VALUE: return factory.getConstFactory().getNull(); case NOT_NULL_VALUE: return factory.createTypeValue(instruction.getResultType(), Nullness.NOT_NULL); case TRUE_VALUE: return factory.getConstFactory().getTrue(); case FALSE_VALUE: return factory.getConstFactory().getFalse(); case THROW_EXCEPTION: return factory.getConstFactory().getContractFail(); default: return getMethodResultValue(instruction, null, factory); } }
private boolean applyBoxedRelation( @NotNull DfaVariableValue dfaLeft, DfaValue dfaRight, boolean negated) { if (!TypeConversionUtil.isPrimitiveAndNotNull(dfaLeft.getVariableType())) return true; DfaBoxedValue.Factory boxedFactory = myFactory.getBoxedFactory(); DfaValue boxedLeft = boxedFactory.createBoxed(dfaLeft); DfaValue boxedRight = boxedFactory.createBoxed(dfaRight); return boxedLeft == null || boxedRight == null || applyRelation(boxedLeft, boxedRight, negated); }
private void updateVarStateOnComparison(@NotNull DfaVariableValue dfaVar, DfaValue value) { if (!isUnknownState(dfaVar)) { if (value instanceof DfaConstValue && ((DfaConstValue) value).getValue() == null) { setVariableState(dfaVar, getVariableState(dfaVar).withNullability(Nullness.NULLABLE)); } else if (isNotNull(value) && !isNotNull(dfaVar)) { setVariableState(dfaVar, getVariableState(dfaVar).withNullability(Nullness.UNKNOWN)); applyRelation(dfaVar, myFactory.getConstFactory().getNull(), true); } } }
@Override public boolean isNull(DfaValue dfaValue) { if (dfaValue instanceof DfaConstValue) return ((DfaConstValue) dfaValue).getValue() == null; if (dfaValue instanceof DfaVariableValue) { int c1Index = getEqClassIndex(dfaValue); return c1Index >= 0 && c1Index == getEqClassIndex(myFactory.getConstFactory().getNull()); } return false; }
private void removeFromMap(int id, int index) { id = unwrap(myFactory.getValue(id)).getID(); int[] classes = myIdToEqClassesIndices.get(id); if (classes != null) { int i = ArrayUtil.indexOf(classes, index); if (i != -1) { classes = ArrayUtil.remove(classes, i); myIdToEqClassesIndices.put(id, classes); } } }
private void addToMap(int id, int index) { id = unwrap(myFactory.getValue(id)).getID(); int[] classes = myIdToEqClassesIndices.get(id); if (classes == null) { classes = new int[] {index}; myIdToEqClassesIndices.put(id, classes); } else { classes = ArrayUtil.append(classes, index); myIdToEqClassesIndices.put(id, classes); } }
@Nullable private DfaInstructionState[] handleRelationBinop( BinopInstruction instruction, DataFlowRunner runner, DfaMemoryState memState, DfaValue dfaRight, DfaValue dfaLeft) { DfaValueFactory factory = runner.getFactory(); final Instruction next = runner.getInstruction(instruction.getIndex() + 1); DfaRelationValue dfaRelation = factory .getRelationFactory() .createRelation(dfaLeft, dfaRight, instruction.getOperationSign(), false); if (dfaRelation == null) { return null; } myCanBeNullInInstanceof.add(instruction); ArrayList<DfaInstructionState> states = new ArrayList<DfaInstructionState>(); final DfaMemoryState trueCopy = memState.createCopy(); if (trueCopy.applyCondition(dfaRelation)) { trueCopy.push(factory.getConstFactory().getTrue()); instruction.setTrueReachable(); states.add(new DfaInstructionState(next, trueCopy)); } //noinspection UnnecessaryLocalVariable DfaMemoryState falseCopy = memState; if (falseCopy.applyCondition(dfaRelation.createNegated())) { falseCopy.push(factory.getConstFactory().getFalse()); instruction.setFalseReachable(); states.add(new DfaInstructionState(next, falseCopy)); if (instruction instanceof InstanceofInstruction && !falseCopy.isNull(dfaLeft)) { myUsefulInstanceofs.add((InstanceofInstruction) instruction); } } return states.toArray(new DfaInstructionState[states.size()]); }
@Override public DfaInstructionState[] visitTypeCast( TypeCastInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) { final DfaValueFactory factory = runner.getFactory(); DfaValue dfaExpr = factory.createValue(instruction.getCasted()); if (dfaExpr != null) { DfaTypeValue dfaType = (DfaTypeValue) factory.createTypeValue(instruction.getCastTo(), Nullness.UNKNOWN); DfaRelationValue dfaInstanceof = factory.getRelationFactory().createRelation(dfaExpr, dfaType, INSTANCEOF_KEYWORD, false); if (dfaInstanceof != null && !memState.applyInstanceofOrNull(dfaInstanceof)) { onInstructionProducesCCE(instruction); } } if (instruction.getCastTo() instanceof PsiPrimitiveType) { memState.push(runner.getFactory().getBoxedFactory().createUnboxed(memState.pop())); } return nextInstruction(instruction, runner, memState); }
@Override public boolean applyCondition(DfaValue dfaCond) { if (dfaCond instanceof DfaUnknownValue) return true; if (dfaCond instanceof DfaUnboxedValue) { DfaVariableValue dfaVar = ((DfaUnboxedValue) dfaCond).getVariable(); boolean isNegated = dfaVar.isNegated(); DfaVariableValue dfaNormalVar = isNegated ? dfaVar.createNegated() : dfaVar; final DfaValue boxedTrue = myFactory.getBoxedFactory().createBoxed(myFactory.getConstFactory().getTrue()); return applyRelationCondition( myFactory .getRelationFactory() .createRelation(dfaNormalVar, boxedTrue, JavaTokenType.EQEQ, isNegated)); } if (dfaCond instanceof DfaVariableValue) { DfaVariableValue dfaVar = (DfaVariableValue) dfaCond; boolean isNegated = dfaVar.isNegated(); DfaVariableValue dfaNormalVar = isNegated ? dfaVar.createNegated() : dfaVar; DfaConstValue dfaTrue = myFactory.getConstFactory().getTrue(); return applyRelationCondition( myFactory .getRelationFactory() .createRelation(dfaNormalVar, dfaTrue, JavaTokenType.EQEQ, isNegated)); } if (dfaCond instanceof DfaConstValue) { return dfaCond == myFactory.getConstFactory().getTrue() || dfaCond != myFactory.getConstFactory().getFalse(); } if (!(dfaCond instanceof DfaRelationValue)) return true; return applyRelationCondition((DfaRelationValue) dfaCond); }
@Override public void setVarValue(DfaVariableValue var, DfaValue value) { if (var == value) return; value = handleFlush(var, value); flushVariable(var); if (value instanceof DfaUnknownValue) { setVariableState(var, getVariableState(var).withNullable(false)); return; } setVariableState(var, getVariableState(var).withValue(value)); if (value instanceof DfaTypeValue) { setVariableState( var, getVariableState(var).withNullability(((DfaTypeValue) value).getNullness())); DfaRelationValue dfaInstanceof = myFactory .getRelationFactory() .createRelation(var, value, JavaTokenType.INSTANCEOF_KEYWORD, false); if (((DfaTypeValue) value).isNotNull()) { applyCondition(dfaInstanceof); } else { applyInstanceofOrNull(dfaInstanceof); } } else { DfaRelationValue dfaEqual = myFactory.getRelationFactory().createRelation(var, value, JavaTokenType.EQEQ, false); if (dfaEqual == null) return; applyCondition(dfaEqual); if (value instanceof DfaVariableValue) { setVariableState(var, getVariableState((DfaVariableValue) value)); } } if (getVariableState(var).isNotNull()) { applyCondition(compareToNull(var, true)); } }
@Override public DfaInstructionState[] visitAssign( AssignInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) { DfaValue dfaSource = memState.pop(); DfaValue dfaDest = memState.pop(); if (dfaDest instanceof DfaVariableValue) { DfaVariableValue var = (DfaVariableValue) dfaDest; DfaValueFactory factory = runner.getFactory(); if (dfaSource instanceof DfaVariableValue && factory.getVarFactory().getAllQualifiedBy(var).contains(dfaSource)) { Nullness nullability = memState.isNotNull(dfaSource) ? Nullness.NOT_NULL : ((DfaVariableValue) dfaSource).getInherentNullability(); dfaSource = factory.createTypeValue(((DfaVariableValue) dfaSource).getVariableType(), nullability); } if (var.getInherentNullability() == Nullness.NOT_NULL) { checkNotNullable( memState, dfaSource, NullabilityProblem.assigningToNotNull, instruction.getRExpression()); } final PsiModifierListOwner psi = var.getPsiVariable(); if (!(psi instanceof PsiField) || !psi.hasModifierProperty(PsiModifier.VOLATILE)) { memState.setVarValue(var, dfaSource); } } else if (dfaDest instanceof DfaTypeValue && ((DfaTypeValue) dfaDest).isNotNull()) { checkNotNullable( memState, dfaSource, NullabilityProblem.assigningToNotNull, instruction.getRExpression()); } memState.push(dfaDest); return nextInstruction(instruction, runner, memState); }
@Override public boolean checkNotNullable(DfaValue value) { if (value == myFactory.getConstFactory().getNull()) return false; if (value instanceof DfaTypeValue && ((DfaTypeValue) value).isNullable()) return false; if (value instanceof DfaVariableValue) { DfaVariableValue varValue = (DfaVariableValue) value; if (varValue.getVariableType() instanceof PsiPrimitiveType) return true; if (isNotNull(varValue)) return true; if (getVariableState(varValue).isNullable()) return false; } return true; }
@Override public void flushVariable(@NotNull final DfaVariableValue variable) { List<DfaValue> updatedStack = ContainerUtil.map(myStack, value -> handleFlush(variable, value)); myStack.clear(); for (DfaValue value : updatedStack) { myStack.push(value); } doFlush(variable, false); flushDependencies(variable); myUnknownVariables.remove(variable); myUnknownVariables.removeAll(myFactory.getVarFactory().getAllQualifiedBy(variable)); myCachedHash = null; }
private boolean applyUnboxedRelation( @NotNull DfaVariableValue dfaLeft, DfaValue dfaRight, boolean negated) { PsiType type = dfaLeft.getVariableType(); if (!TypeConversionUtil.isPrimitiveWrapper(type)) { return true; } if (negated) { // from the fact "wrappers are not the same" it does not follow that "unboxed values are not // equal" return true; } DfaBoxedValue.Factory boxedFactory = myFactory.getBoxedFactory(); DfaValue unboxedLeft = boxedFactory.createUnboxed(dfaLeft); DfaValue unboxedRight = boxedFactory.createUnboxed(dfaRight); return applyRelation(unboxedLeft, unboxedRight, false) && checkCompareWithBooleanLiteral(unboxedLeft, unboxedRight, false); }
private boolean checkCompareWithBooleanLiteral( DfaValue dfaLeft, DfaValue dfaRight, boolean negated) { if (dfaRight instanceof DfaConstValue) { Object constVal = ((DfaConstValue) dfaRight).getValue(); if (constVal instanceof Boolean) { DfaConstValue negVal = myFactory .getConstFactory() .createFromValue(!((Boolean) constVal).booleanValue(), PsiType.BOOLEAN, null); if (!applyRelation(dfaLeft, negVal, !negated)) { return false; } if (!applyRelation(dfaLeft.createNegated(), negVal, negated)) { return false; } } } return true; }
@NotNull private DfaValue getMethodResultValue( MethodCallInstruction instruction, @Nullable DfaValue qualifierValue, DfaValueFactory factory) { DfaValue precalculated = instruction.getPrecalculatedReturnValue(); if (precalculated != null) { return precalculated; } final PsiType type = instruction.getResultType(); final MethodCallInstruction.MethodType methodType = instruction.getMethodType(); if (methodType == MethodCallInstruction.MethodType.UNBOXING) { return factory.getBoxedFactory().createUnboxed(qualifierValue); } if (methodType == MethodCallInstruction.MethodType.BOXING) { DfaValue boxed = factory.getBoxedFactory().createBoxed(qualifierValue); return boxed == null ? factory.createTypeValue(type, Nullness.NOT_NULL) : boxed; } if (methodType == MethodCallInstruction.MethodType.CAST) { assert qualifierValue != null; if (qualifierValue instanceof DfaConstValue) { Object casted = TypeConversionUtil.computeCastTo(((DfaConstValue) qualifierValue).getValue(), type); return factory .getConstFactory() .createFromValue(casted, type, ((DfaConstValue) qualifierValue).getConstant()); } return qualifierValue; } if (type != null && (type instanceof PsiClassType || type.getArrayDimensions() > 0)) { Nullness nullability = myReturnTypeNullability.get(instruction); if (nullability == Nullness.UNKNOWN && factory.isUnknownMembersAreNullable()) { nullability = Nullness.NULLABLE; } return factory.createTypeValue(type, nullability); } return DfaUnknownValue.getInstance(); }
void doFlush(@NotNull DfaVariableValue varPlain, boolean markUnknown) { DfaVariableValue varNegated = varPlain.getNegatedValue(); final int idPlain = varPlain.getID(); final int idNegated = varNegated == null ? -1 : varNegated.getID(); int[] classes = myIdToEqClassesIndices.get(idPlain); int[] negatedClasses = myIdToEqClassesIndices.get(idNegated); int[] result = ArrayUtil.mergeArrays( ObjectUtils.notNull(classes, ArrayUtil.EMPTY_INT_ARRAY), ObjectUtils.notNull(negatedClasses, ArrayUtil.EMPTY_INT_ARRAY)); int interruptCount = 0; for (int varClassIndex : result) { EqClass varClass = myEqClasses.get(varClassIndex); if ((++interruptCount & 0xf) == 0) { ProgressManager.checkCanceled(); } varClass = new EqClass(varClass); myEqClasses.set(varClassIndex, varClass); for (int id : varClass.toNativeArray()) { int idUnwrapped; if (id == idPlain || id == idNegated || (idUnwrapped = unwrap(myFactory.getValue(id)).getID()) == idPlain || idUnwrapped == idNegated) { varClass.removeValue(id); } } if (varClass.isEmpty()) { myEqClasses.set(varClassIndex, null); for (TLongIterator iterator = myDistinctClasses.iterator(); iterator.hasNext(); ) { long pair = iterator.next(); if (low(pair) == varClassIndex || high(pair) == varClassIndex) { iterator.remove(); } } } else if (varClass.containsConstantsOnly()) { for (TLongIterator iterator = myDistinctClasses.iterator(); iterator.hasNext(); ) { long pair = iterator.next(); if (low(pair) == varClassIndex && myEqClasses.get(high(pair)).containsConstantsOnly() || high(pair) == varClassIndex && myEqClasses.get(low(pair)).containsConstantsOnly()) { iterator.remove(); } } } } removeAllFromMap(idPlain); removeAllFromMap(idNegated); myVariableStates.remove(varPlain); if (varNegated != null) { myVariableStates.remove(varNegated); } if (markUnknown) { myUnknownVariables.add(varPlain); } myCachedNonTrivialEqClasses = null; myCachedDistinctClassPairs = null; myCachedHash = null; }
private void removeAllFromMap(int id) { if (id < 0) return; id = unwrap(myFactory.getValue(id)).getID(); myIdToEqClassesIndices.remove(id); }
private boolean uniteClasses(int c1Index, int c2Index) { EqClass c1 = myEqClasses.get(c1Index); EqClass c2 = myEqClasses.get(c2Index); Set<DfaVariableValue> vars = ContainerUtil.newTroveSet(); Set<DfaVariableValue> negatedVars = ContainerUtil.newTroveSet(); int[] cs = new int[c1.size() + c2.size()]; c1.set(0, cs, 0, c1.size()); c2.set(0, cs, c1.size(), c2.size()); int nConst = 0; for (int c : cs) { DfaValue dfaValue = unwrap(myFactory.getValue(c)); if (dfaValue instanceof DfaConstValue) nConst++; if (dfaValue instanceof DfaVariableValue) { DfaVariableValue variableValue = (DfaVariableValue) dfaValue; if (variableValue.isNegated()) { negatedVars.add(variableValue.createNegated()); } else { vars.add(variableValue); } } if (nConst > 1) return false; } if (ContainerUtil.intersects(vars, negatedVars)) return false; TLongArrayList c2Pairs = new TLongArrayList(); long[] distincts = myDistinctClasses.toArray(); for (long distinct : distincts) { int pc1 = low(distinct); int pc2 = high(distinct); boolean addedToC1 = false; if (pc1 == c1Index || pc2 == c1Index) { addedToC1 = true; } if (pc1 == c2Index || pc2 == c2Index) { if (addedToC1) return false; c2Pairs.add(distinct); } } EqClass newClass = new EqClass(c1); myEqClasses.set(c1Index, newClass); for (int i = 0; i < c2.size(); i++) { int c = c2.get(i); newClass.add(c); removeFromMap(c, c2Index); addToMap(c, c1Index); } for (int i = 0; i < c2Pairs.size(); i++) { long c = c2Pairs.get(i); myDistinctClasses.remove(c); myDistinctClasses.add(createPair(c1Index, low(c) == c2Index ? high(c) : low(c))); } myEqClasses.set(c2Index, null); return true; }
private List<DfaMemoryState> addContractResults( DfaValue[] argValues, MethodContract contract, List<DfaMemoryState> states, MethodCallInstruction instruction, DfaValueFactory factory, Set<DfaMemoryState> finalStates) { DfaConstValue.Factory constFactory = factory.getConstFactory(); List<DfaMemoryState> falseStates = ContainerUtil.newArrayList(); for (int i = 0; i < argValues.length; i++) { DfaValue argValue = argValues[i]; MethodContract.ValueConstraint constraint = contract.arguments[i]; DfaConstValue expectedValue = constraint.getComparisonValue(factory); if (expectedValue == null) continue; boolean invertCondition = constraint.shouldUseNonEqComparison(); DfaValue condition = factory .getRelationFactory() .createRelation(argValue, expectedValue, EQEQ, invertCondition); if (condition == null) { if (!(argValue instanceof DfaConstValue)) { for (DfaMemoryState state : states) { falseStates.add(state.createCopy()); } continue; } condition = constFactory.createFromValue( (argValue == expectedValue) != invertCondition, PsiType.BOOLEAN, null); } List<DfaMemoryState> nextStates = ContainerUtil.newArrayList(); for (DfaMemoryState state : states) { boolean unknownVsNull = expectedValue == constFactory.getNull() && argValue instanceof DfaVariableValue && ((DfaMemoryStateImpl) state) .getVariableState((DfaVariableValue) argValue) .getNullability() == Nullness.UNKNOWN; DfaMemoryState falseCopy = state.createCopy(); if (state.applyCondition(condition)) { if (unknownVsNull && !invertCondition) { state.markEphemeral(); } nextStates.add(state); } if (falseCopy.applyCondition(condition.createNegated())) { if (unknownVsNull && invertCondition) { falseCopy.markEphemeral(); } falseStates.add(falseCopy); } } states = nextStates; } for (DfaMemoryState state : states) { state.push(getDfaContractReturnValue(contract, instruction, factory)); finalStates.add(state); } return falseStates; }
private boolean applyRelationCondition(@NotNull DfaRelationValue dfaRelation) { DfaValue dfaLeft = dfaRelation.getLeftOperand(); DfaValue dfaRight = dfaRelation.getRightOperand(); if (dfaLeft instanceof DfaUnknownValue || dfaRight instanceof DfaUnknownValue) return true; boolean isNegated = dfaRelation.isNegated(); if (dfaLeft instanceof DfaTypeValue && ((DfaTypeValue) dfaLeft).isNotNull() && dfaRight == myFactory.getConstFactory().getNull()) { return isNegated; } if (dfaRight instanceof DfaTypeValue) { if (dfaLeft instanceof DfaVariableValue) { DfaVariableValue dfaVar = (DfaVariableValue) dfaLeft; if (isUnknownState(dfaVar)) return true; if (!dfaRelation.isInstanceOf()) { if (((DfaTypeValue) dfaRight).isNotNull() && isNull(dfaVar)) { return isNegated; } return true; } if (isNegated) { DfaVariableState newState = getVariableState(dfaVar).withNotInstanceofValue((DfaTypeValue) dfaRight); if (newState != null) { setVariableState(dfaVar, newState); return true; } return !getVariableState(dfaVar).isNotNull() && applyRelation(dfaVar, myFactory.getConstFactory().getNull(), false); } if (applyRelation(dfaVar, myFactory.getConstFactory().getNull(), true)) { DfaVariableState newState = getVariableState(dfaVar).withInstanceofValue((DfaTypeValue) dfaRight); if (newState != null) { setVariableState(dfaVar, newState); return true; } } return false; } return true; } if (isEffectivelyNaN(dfaLeft) || isEffectivelyNaN(dfaRight)) { applyEquivalenceRelation(dfaRelation, dfaLeft, dfaRight); return isNegated; } if (canBeNaN(dfaLeft) && canBeNaN(dfaRight)) { if (dfaLeft == dfaRight && dfaLeft instanceof DfaVariableValue && !(((DfaVariableValue) dfaLeft).getVariableType() instanceof PsiPrimitiveType)) { return !isNegated; } applyEquivalenceRelation(dfaRelation, dfaLeft, dfaRight); return true; } return applyEquivalenceRelation(dfaRelation, dfaLeft, dfaRight); }
void flushDependencies(@NotNull DfaVariableValue variable) { for (DfaVariableValue dependent : myFactory.getVarFactory().getAllQualifiedBy(variable)) { doFlush(dependent, false); } }