@Override public DfaInstructionState[] visitPush( PushInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) { if (myContext == instruction.getPlace()) { final Map<DfaVariableValue, DfaVariableState> map = ((ValuableDataFlowRunner.MyDfaMemoryState) memState).getVariableStates(); for (Map.Entry<DfaVariableValue, DfaVariableState> entry : map.entrySet()) { ValuableDataFlowRunner.ValuableDfaVariableState state = (ValuableDataFlowRunner.ValuableDfaVariableState) entry.getValue(); DfaVariableValue variableValue = entry.getKey(); final PsiExpression psiExpression = state.myExpression; if (psiExpression != null && variableValue.getQualifier() == null) { myValues.put(variableValue.getPsiVariable(), psiExpression); } } DfaValue value = instruction.getValue(); if (value instanceof DfaVariableValue && ((DfaVariableValue) value).getQualifier() == null) { if (memState.isNotNull((DfaVariableValue) value)) { myNotNulls.add(((DfaVariableValue) value).getPsiVariable()); } if (memState.isNull(value)) { myNulls.add(((DfaVariableValue) value).getPsiVariable()); } } } return super.visitPush(instruction, runner, memState); }
public DfaInstructionState[] visitBinop( BinopInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) { memState.pop(); memState.pop(); memState.push(DfaUnknownValue.getInstance()); return nextInstruction(instruction, runner, memState); }
public DfaInstructionState[] visitPush( PushInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) { if (instruction.isFieldReference()) { memState.fieldReferenced(); } memState.push(instruction.getValue()); return nextInstruction(instruction, runner, memState); }
public DfaInstructionState[] visitNot( NotInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) { DfaValue dfaValue = memState.pop(); dfaValue = dfaValue.createNegated(); memState.push(dfaValue); return nextInstruction(instruction, runner, memState); }
public DfaInstructionState[] visitFlushVariable( FlushVariableInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) { final DfaVariableValue variable = instruction.getVariable(); if (variable != null) { memState.flushVariableOutOfScope(variable); } else { memState.flushFields(runner); } return nextInstruction(instruction, runner, memState); }
public DfaInstructionState[] visitMethodCall( MethodCallInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) { //noinspection UnusedDeclaration for (PsiExpression arg : instruction.getArgs()) { memState.pop(); } memState.pop(); // qualifier memState.push(DfaUnknownValue.getInstance()); return nextInstruction(instruction, runner, memState); }
@Override public DfaInstructionState[] visitInstanceof( InstanceofInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) { memState.pop(); memState.pop(); memState.push( new DfaInstanceofValue( runner.getFactory(), instruction.getLeft(), instruction.getCastType())); return new DfaInstructionState[] { new DfaInstructionState(runner.getInstruction(instruction.getIndex() + 1), memState) }; }
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; }
private static void forceNotNull(DataFlowRunner runner, DfaMemoryState memState, DfaValue arg) { if (arg instanceof DfaVariableValue) { DfaVariableValue var = (DfaVariableValue) arg; memState.setVarValue( var, runner.getFactory().createTypeValue(var.getVariableType(), Nullness.NOT_NULL)); } }
@Override public DfaInstructionState[] visitAssign( AssignInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) { final Instruction nextInstruction = runner.getInstruction(instruction.getIndex() + 1); final DfaValue dfaSource = memState.pop(); final DfaValue dfaDest = memState.pop(); if (dfaDest instanceof DfaVariableValue) { DfaVariableValue var = (DfaVariableValue) dfaDest; final PsiExpression rightValue = instruction.getRExpression(); final PsiElement parent = rightValue == null ? null : rightValue.getParent(); 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; 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 PsiExpression curValue = curState.myExpression; final PsiExpression nextValue; if (type == JavaTokenType.PLUSEQ && prevValue != null) { PsiExpression tmpExpression; try { tmpExpression = JavaPsiFacade.getElementFactory(myContext.getProject()) .createExpressionFromText( prevValue.getText() + "+" + rightValue.getText(), rightValue); } catch (Exception e) { tmpExpression = curValue == null ? rightValue : curValue; } nextValue = tmpExpression; } else { nextValue = curValue == null ? rightValue : curValue; } curState.myExpression = nextValue; } memState.push(dfaDest); return new DfaInstructionState[] {new DfaInstructionState(nextInstruction, memState)}; }
@Override public DfaInstructionState[] visitCheckReturnValue( CheckReturnValueInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) { final DfaValue retValue = memState.pop(); checkNotNullable( memState, retValue, NullabilityProblem.nullableReturn, instruction.getReturn()); return nextInstruction(instruction, runner, memState); }
private DfaMemoryStateImpl createClosureState(DfaMemoryState memState) { DfaMemoryStateImpl copy = (DfaMemoryStateImpl) memState.createCopy(); copy.flushFields(getFields()); Set<DfaVariableValue> vars = new HashSet<DfaVariableValue>(copy.getVariableStates().keySet()); for (DfaVariableValue value : vars) { copy.flushDependencies(value); } return copy; }
@Override protected boolean checkNotNullable( DfaMemoryState state, DfaValue value, NullabilityProblem problem, PsiElement anchor) { boolean ok = super.checkNotNullable(state, value, problem, anchor); if (!ok && anchor != null) { myProblems.putValue(problem, anchor); } Pair<NullabilityProblem, PsiElement> key = Pair.create(problem, anchor); StateInfo info = myStateInfos.get(key); if (info == null) { myStateInfos.put(key, info = new StateInfo()); } if (state.isEphemeral() && !ok) { info.ephemeralNpe = true; } else if (!state.isEphemeral()) { if (ok) info.normalOk = true; else info.normalNpe = true; } return ok; }
@Override public DfaInstructionState[] visitFieldReference( FieldReferenceInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) { final DfaValue qualifier = memState.pop(); if (!checkNotNullable( memState, qualifier, NullabilityProblem.fieldAccessNPE, instruction.getElementToAssert())) { forceNotNull(runner, memState, qualifier); } return nextInstruction(instruction, runner, memState); }
@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); }
private DfaValue popQualifier( MethodCallInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) { @NotNull final DfaValue qualifier = memState.pop(); boolean unboxing = instruction.getMethodType() == MethodCallInstruction.MethodType.UNBOXING; NullabilityProblem problem = unboxing ? NullabilityProblem.unboxingNullable : NullabilityProblem.callNPE; PsiExpression anchor = unboxing ? instruction.getContext() : instruction.getCallExpression(); if (!checkNotNullable(memState, qualifier, problem, anchor)) { forceNotNull(runner, memState, qualifier); } return qualifier; }
@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 DfaInstructionState[] visitMethodCall( final MethodCallInstruction instruction, final DataFlowRunner runner, final DfaMemoryState memState) { DfaValue[] argValues = popCallArguments(instruction, runner, memState); final DfaValue qualifier = popQualifier(instruction, runner, memState); List<DfaMemoryState> currentStates = ContainerUtil.newArrayList(memState); Set<DfaMemoryState> finalStates = ContainerUtil.newLinkedHashSet(); if (argValues != null) { for (MethodContract contract : instruction.getContracts()) { currentStates = addContractResults( argValues, contract, currentStates, instruction, runner.getFactory(), finalStates); } } for (DfaMemoryState state : currentStates) { state.push(getMethodResultValue(instruction, qualifier, runner.getFactory())); finalStates.add(state); } return ContainerUtil.map2Array( finalStates, DfaInstructionState.class, new Function<DfaMemoryState, DfaInstructionState>() { @Override public DfaInstructionState fun(DfaMemoryState state) { if (instruction.shouldFlushFields()) { state.flushFields(); } return new DfaInstructionState( runner.getInstruction(instruction.getIndex() + 1), state); } }); }
@Override public DfaInstructionState[] visitBinop( BinopInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) { myReachable.add(instruction); DfaValue dfaRight = memState.pop(); DfaValue dfaLeft = memState.pop(); final IElementType opSign = instruction.getOperationSign(); if (opSign != null) { DfaInstructionState[] states = handleConstantComparison(instruction, runner, memState, dfaRight, dfaLeft, opSign); if (states == null) { states = handleRelationBinop(instruction, runner, memState, dfaRight, dfaLeft); } if (states != null) { return states; } if (PLUS == opSign) { memState.push(instruction.getNonNullStringValue(runner.getFactory())); } else { if (instruction instanceof InstanceofInstruction) { handleInstanceof((InstanceofInstruction) instruction, dfaRight, dfaLeft); } memState.push(DfaUnknownValue.getInstance()); } } else { memState.push(DfaUnknownValue.getInstance()); } instruction.setTrueReachable(); // Not a branching instruction actually. instruction.setFalseReachable(); return nextInstruction(instruction, runner, memState); }
@Override public DfaInstructionState[] visitPush( PushInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) { if (instruction.isReferenceRead()) { DfaValue dfaValue = instruction.getValue(); if (dfaValue instanceof DfaVariableValue) { DfaConstValue constValue = memState.getConstantValue((DfaVariableValue) dfaValue); myPossibleVariableValues.putValue( instruction, constValue != null && (constValue.getValue() == null || constValue.getValue() instanceof Boolean) ? constValue : ANY_VALUE); } } return super.visitPush(instruction, runner, memState); }
@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()]); }
@Nullable private DfaValue[] popCallArguments( MethodCallInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) { final PsiExpression[] args = instruction.getArgs(); PsiMethod method = instruction.getTargetMethod(); boolean varargCall = instruction.isVarArgCall(); DfaValue[] argValues; if (method == null || instruction.getContracts().isEmpty()) { argValues = null; } else { int paramCount = method.getParameterList().getParametersCount(); if (paramCount == args.length || method.isVarArgs() && args.length >= paramCount - 1) { argValues = new DfaValue[paramCount]; if (varargCall) { argValues[paramCount - 1] = DfaUnknownValue.getInstance(); } } else { argValues = null; } } for (int i = 0; i < args.length; i++) { final DfaValue arg = memState.pop(); int paramIndex = args.length - i - 1; if (argValues != null && (paramIndex < argValues.length - 1 || !varargCall)) { argValues[paramIndex] = arg; } PsiExpression expr = args[paramIndex]; Nullness requiredNullability = instruction.getArgRequiredNullability(expr); if (requiredNullability == Nullness.NOT_NULL) { if (!checkNotNullable( memState, arg, NullabilityProblem.passingNullableToNotNullParameter, expr)) { forceNotNull(runner, memState, arg); } } else if (requiredNullability == Nullness.UNKNOWN) { checkNotNullable( memState, arg, NullabilityProblem.passingNullableArgumentToNonAnnotatedParameter, expr); } } return argValues; }
@Nullable private static DfaInstructionState[] checkComparingWithConstant( BinopInstruction instruction, DataFlowRunner runner, DfaMemoryState memState, DfaVariableValue var, IElementType opSign, double comparedWith) { DfaConstValue knownConstantValue = memState.getConstantValue(var); Object knownValue = knownConstantValue == null ? null : knownConstantValue.getValue(); if (knownValue instanceof Number) { double knownDouble = ((Number) knownValue).doubleValue(); return checkComparisonWithKnownRange( instruction, runner, memState, opSign, comparedWith, knownDouble, knownDouble); } PsiType varType = var.getVariableType(); if (!(varType instanceof PsiPrimitiveType)) return null; if (varType == PsiType.FLOAT || varType == PsiType.DOUBLE) return null; double minValue = varType == PsiType.BYTE ? Byte.MIN_VALUE : varType == PsiType.SHORT ? Short.MIN_VALUE : varType == PsiType.INT ? Integer.MIN_VALUE : varType == PsiType.CHAR ? Character.MIN_VALUE : Long.MIN_VALUE; double maxValue = varType == PsiType.BYTE ? Byte.MAX_VALUE : varType == PsiType.SHORT ? Short.MAX_VALUE : varType == PsiType.INT ? Integer.MAX_VALUE : varType == PsiType.CHAR ? Character.MAX_VALUE : Long.MAX_VALUE; return checkComparisonWithKnownRange( instruction, runner, memState, opSign, comparedWith, minValue, maxValue); }
public DfaInstructionState[] visitConditionalGoto( ConditionalGotoInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) { DfaValue cond = memState.pop(); DfaValue condTrue; DfaValue condFalse; if (instruction.isNegated()) { condFalse = cond; condTrue = cond.createNegated(); } else { condTrue = cond; condFalse = cond.createNegated(); } if (condTrue == runner.getFactory().getConstFactory().getTrue()) { markBranchReachable(instruction, true); return new DfaInstructionState[] { new DfaInstructionState(runner.getInstruction(instruction.getOffset()), memState) }; } if (condFalse == runner.getFactory().getConstFactory().getTrue()) { markBranchReachable(instruction, false); return nextInstruction(instruction, runner, memState); } ArrayList<DfaInstructionState> result = new ArrayList<DfaInstructionState>(); DfaMemoryState thenState = memState.createCopy(); DfaMemoryState elseState = memState.createCopy(); if (thenState.applyCondition(condTrue)) { result.add( new DfaInstructionState(runner.getInstruction(instruction.getOffset()), thenState)); markBranchReachable(instruction, true); } if (elseState.applyCondition(condFalse)) { result.add( new DfaInstructionState(runner.getInstruction(instruction.getIndex() + 1), elseState)); markBranchReachable(instruction, false); } return result.toArray(new DfaInstructionState[result.size()]); }
private static DfaInstructionState[] alwaysTrue( BinopInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) { memState.push(runner.getFactory().getConstFactory().getTrue()); instruction.setTrueReachable(); return nextInstruction(instruction, runner, memState); }
public DfaInstructionState[] visitFieldReference( FieldReferenceInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) { memState.pop(); return nextInstruction(instruction, runner, memState); }
public DfaInstructionState[] visitAssign( AssignInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) { memState.pop(); memState.push(memState.pop()); return nextInstruction(instruction, runner, memState); }
public DfaInstructionState[] visitEmptyStack( EmptyStackInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) { memState.emptyStack(); return nextInstruction(instruction, runner, memState); }
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; }
public DfaInstructionState[] visitCheckReturnValue( CheckReturnValueInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) { memState.pop(); return nextInstruction(instruction, runner, memState); }