public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { this.preAssertInitStateIndex = currentScope.methodScope().recordInitializationStates(flowInfo); Constant cst = this.assertExpression.optimizedBooleanConstant(); if ((this.assertExpression.implicitConversion & TypeIds.UNBOXING) != 0) { this.assertExpression.checkNPE(currentScope, flowContext, flowInfo); } boolean isOptimizedTrueAssertion = cst != Constant.NotAConstant && cst.booleanValue() == true; boolean isOptimizedFalseAssertion = cst != Constant.NotAConstant && cst.booleanValue() == false; flowContext.tagBits |= FlowContext.HIDE_NULL_COMPARISON_WARNING; FlowInfo conditionFlowInfo = this.assertExpression.analyseCode(currentScope, flowContext, flowInfo.copy()); flowContext.extendTimeToLiveForNullCheckedField(1); // survive this assert as a Statement flowContext.tagBits &= ~FlowContext.HIDE_NULL_COMPARISON_WARNING; UnconditionalFlowInfo assertWhenTrueInfo = conditionFlowInfo.initsWhenTrue().unconditionalInits(); FlowInfo assertInfo = conditionFlowInfo.initsWhenFalse(); if (isOptimizedTrueAssertion) { assertInfo.setReachMode(FlowInfo.UNREACHABLE_OR_DEAD); } if (this.exceptionArgument != null) { // only gets evaluated when escaping - results are not taken into account FlowInfo exceptionInfo = this.exceptionArgument.analyseCode(currentScope, flowContext, assertInfo.copy()); if (isOptimizedTrueAssertion) { currentScope.problemReporter().fakeReachable(this.exceptionArgument); } else { flowContext.checkExceptionHandlers( currentScope.getJavaLangAssertionError(), this, exceptionInfo, currentScope); } } if (!isOptimizedTrueAssertion) { // add the assert support in the clinit manageSyntheticAccessIfNecessary(currentScope, flowInfo); } // account for potential AssertionError: flowContext.recordAbruptExit(); if (isOptimizedFalseAssertion) { return flowInfo; // if assertions are enabled, the following code will be unreachable // change this if we need to carry null analysis results of the assert // expression downstream } else { CompilerOptions compilerOptions = currentScope.compilerOptions(); if (!compilerOptions.includeNullInfoFromAsserts) { // keep just the initializations info, don't include assert's null info // merge initialization info's and then add back the null info from flowInfo to // make sure that the empty null info of assertInfo doesnt change flowInfo's null info. return ((flowInfo.nullInfoLessUnconditionalCopy()) .mergedWith(assertInfo.nullInfoLessUnconditionalCopy())) .addNullInfoFrom(flowInfo); } return flowInfo .mergedWith(assertInfo.nullInfoLessUnconditionalCopy()) .addInitializationsFrom(assertWhenTrueInfo.discardInitializationInfo()); // keep the merge from the initial code for the definite assignment // analysis, tweak the null part to influence nulls downstream } }
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { this.breakLabel = new BranchLabel(); this.continueLabel = new BranchLabel(); LoopingFlowContext loopingContext = new LoopingFlowContext( flowContext, flowInfo, this, this.breakLabel, this.continueLabel, currentScope); Constant cst = this.condition.constant; boolean isConditionTrue = cst != Constant.NotAConstant && cst.booleanValue() == true; cst = this.condition.optimizedBooleanConstant(); boolean isConditionOptimizedTrue = cst != Constant.NotAConstant && cst.booleanValue() == true; boolean isConditionOptimizedFalse = cst != Constant.NotAConstant && cst.booleanValue() == false; int previousMode = flowInfo.reachMode(); UnconditionalFlowInfo actionInfo = flowInfo.nullInfoLessUnconditionalCopy(); // we need to collect the contribution to nulls of the coming paths through the // loop, be they falling through normally or branched to break, continue labels // or catch blocks if ((this.action != null) && !this.action.isEmptyBlock()) { actionInfo = this.action.analyseCode(currentScope, loopingContext, actionInfo).unconditionalInits(); // code generation can be optimized when no need to continue in the loop if ((actionInfo.tagBits & loopingContext.initsOnContinue.tagBits & FlowInfo.UNREACHABLE) != 0) { this.continueLabel = null; } } /* Reset reach mode, to address following scenario. * final blank; * do { if (true) break; else blank = 0; } while(false); * blank = 1; // may be initialized already */ actionInfo.setReachMode(previousMode); LoopingFlowContext condLoopContext; FlowInfo condInfo = this.condition.analyseCode( currentScope, (condLoopContext = new LoopingFlowContext(flowContext, flowInfo, this, null, null, currentScope)), (this.action == null ? actionInfo : (actionInfo.mergedWith(loopingContext.initsOnContinue))) .copy()); this.preConditionInitStateIndex = currentScope.methodScope().recordInitializationStates(actionInfo); if (!isConditionOptimizedFalse && this.continueLabel != null) { loopingContext.complainOnDeferredFinalChecks(currentScope, condInfo); condLoopContext.complainOnDeferredFinalChecks(currentScope, condInfo); loopingContext.complainOnDeferredNullChecks( currentScope, flowInfo .unconditionalCopy() .addPotentialNullInfoFrom(condInfo.initsWhenTrue().unconditionalInits())); condLoopContext.complainOnDeferredNullChecks( currentScope, actionInfo.addPotentialNullInfoFrom(condInfo.initsWhenTrue().unconditionalInits())); } // end of loop FlowInfo mergedInfo = FlowInfo.mergedOptimizedBranches( (loopingContext.initsOnBreak.tagBits & FlowInfo.UNREACHABLE) != 0 ? loopingContext.initsOnBreak : flowInfo.unconditionalCopy().addInitializationsFrom(loopingContext.initsOnBreak), // recover upstream null info isConditionOptimizedTrue, (condInfo.tagBits & FlowInfo.UNREACHABLE) == 0 ? flowInfo.addInitializationsFrom(condInfo.initsWhenFalse()) : condInfo, // recover null inits from before condition analysis false, // never consider opt false case for DO loop, since break can always occur // (47776) !isConditionTrue /*do{}while(true); unreachable(); */); this.mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo); return mergedInfo; }
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { boolean nonStatic = !this.binding.isStatic(); boolean wasInsideAssert = ((flowContext.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) != 0); flowInfo = this.receiver .analyseCode(currentScope, flowContext, flowInfo, nonStatic) .unconditionalInits(); // recording the closing of AutoCloseable resources: boolean analyseResources = currentScope.compilerOptions().analyseResourceLeaks; if (analyseResources && CharOperation.equals(TypeConstants.CLOSE, this.selector)) { FakedTrackingVariable trackingVariable = FakedTrackingVariable.getCloseTrackingVariable(this.receiver); if (trackingVariable != null) { // null happens if receiver is not a local variable or not an AutoCloseable if (trackingVariable.methodScope == currentScope.methodScope()) { trackingVariable.markClose(flowInfo, flowContext); } else { trackingVariable.markClosedInNestedMethod(); } } } if (nonStatic) { this.receiver.checkNPE(currentScope, flowContext, flowInfo); // https://bugs.eclipse.org/bugs/show_bug.cgi?id=318682 if (this.receiver.isThis()) { // accessing non-static method without an object currentScope.resetDeclaringClassMethodStaticFlag(this.binding.declaringClass); } } else if (this.receiver.isThis()) { if ((this.receiver.bits & ASTNode.IsImplicitThis) == 0) { // explicit this receiver, not allowed in static context currentScope.resetEnclosingMethodStaticFlag(); } } FlowInfo conditionFlowInfo; if (this.arguments != null) { int length = this.arguments.length; for (int i = 0; i < length; i++) { Expression argument = this.arguments[i]; if ((argument.implicitConversion & TypeIds.UNBOXING) != 0) { argument.checkNPE(currentScope, flowContext, flowInfo); } if (this.receiver.resolvedType != null && this.receiver.resolvedType.id == TypeIds.T_OrgEclipseCoreRuntimeAssert && argument.resolvedType != null && argument.resolvedType.id == TypeIds.T_boolean) { Constant cst = argument.optimizedBooleanConstant(); boolean isOptimizedTrueAssertion = cst != Constant.NotAConstant && cst.booleanValue() == true; boolean isOptimizedFalseAssertion = cst != Constant.NotAConstant && cst.booleanValue() == false; flowContext.tagBits |= FlowContext.HIDE_NULL_COMPARISON_WARNING; conditionFlowInfo = argument.analyseCode(currentScope, flowContext, flowInfo.copy()); if (!wasInsideAssert) { flowContext.tagBits &= ~FlowContext.HIDE_NULL_COMPARISON_WARNING; } UnconditionalFlowInfo assertWhenTrueInfo = conditionFlowInfo.initsWhenTrue().unconditionalInits(); FlowInfo assertInfo = conditionFlowInfo.initsWhenFalse(); if (isOptimizedTrueAssertion) { assertInfo.setReachMode(FlowInfo.UNREACHABLE_OR_DEAD); } if (!isOptimizedFalseAssertion) { // if assertion is not false for sure, only then it makes sense to carry the flow info // ahead. // if the code does reach ahead, it means the assert didn't cause an exit, and so // the expression inside it shouldn't change the prior flowinfo // viz. org.eclipse.core.runtime.Assert.isLegal(false && o != null) // keep the merge from the initial code for the definite assignment // analysis, tweak the null part to influence nulls downstream flowInfo = flowInfo .mergedWith(assertInfo.nullInfoLessUnconditionalCopy()) .addInitializationsFrom(assertWhenTrueInfo.discardInitializationInfo()); } } else { flowInfo = argument.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits(); } if (analyseResources) { // if argument is an AutoCloseable insert info that it *may* be closed (by the target // method, i.e.) flowInfo = FakedTrackingVariable.markPassedToOutside(currentScope, argument, flowInfo, false); } } analyseArguments(currentScope, flowContext, flowInfo, this.binding, this.arguments); } ReferenceBinding[] thrownExceptions; if ((thrownExceptions = this.binding.thrownExceptions) != Binding.NO_EXCEPTIONS) { if ((this.bits & ASTNode.Unchecked) != 0 && this.genericTypeArguments == null) { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=277643, align with javac on JLS 15.12.2.6 thrownExceptions = currentScope.environment().convertToRawTypes(this.binding.thrownExceptions, true, true); } // must verify that exceptions potentially thrown by this expression are caught in the method flowContext.checkExceptionHandlers(thrownExceptions, this, flowInfo.copy(), currentScope); // TODO (maxime) the copy above is needed because of a side effect into // checkExceptionHandlers; consider protecting there instead of here; // NullReferenceTest#test0510 } manageSyntheticAccessIfNecessary(currentScope, flowInfo); return flowInfo; }