org.eclipse.jdt.internal.compiler.ast.WhileStatement Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ecj Show documentation
Show all versions of ecj Show documentation
Eclipse Compiler for Java(TM)
/*******************************************************************************
* Copyright (c) 2000, 2020 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* Stephan Herrmann - Contributions for
* bug 319201 - [null] no warning when unboxing SingleNameReference causes NPE
* bug 349326 - [1.7] new warning for missing try-with-resources
* bug 345305 - [compiler][null] Compiler misidentifies a case of "variable can only be null"
* bug 403147 - [compiler][null] FUP of bug 400761: consolidate interaction between unboxing, NPE, and deferred checking
* Bug 415790 - [compiler][resource]Incorrect potential resource leak warning in for loop with close in try/catch
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.impl.*;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.codegen.*;
import org.eclipse.jdt.internal.compiler.flow.*;
import org.eclipse.jdt.internal.compiler.lookup.*;
public class WhileStatement extends Statement {
public Expression condition;
public Statement action;
private BranchLabel breakLabel, continueLabel;
int preCondInitStateIndex = -1;
int condIfTrueInitStateIndex = -1;
int mergedInitStateIndex = -1;
public WhileStatement(Expression condition, Statement action, int s, int e) {
this.condition = condition;
this.action = action;
// remember useful empty statement
if (action instanceof EmptyStatement) action.bits |= IsUsefulEmptyStatement;
this.sourceStart = s;
this.sourceEnd = e;
}
@Override
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
this.breakLabel = new BranchLabel();
this.continueLabel = new BranchLabel();
int initialComplaintLevel = (flowInfo.reachMode() & FlowInfo.UNREACHABLE) != 0 ? Statement.COMPLAINED_FAKE_REACHABLE : Statement.NOT_COMPLAINED;
Constant cst = this.condition.constant;
boolean isConditionTrue = cst != Constant.NotAConstant && cst.booleanValue() == true;
boolean isConditionFalse = cst != Constant.NotAConstant && cst.booleanValue() == false;
cst = this.condition.optimizedBooleanConstant();
boolean isConditionOptimizedTrue = cst != Constant.NotAConstant && cst.booleanValue() == true;
boolean isConditionOptimizedFalse = cst != Constant.NotAConstant && cst.booleanValue() == false;
this.preCondInitStateIndex = currentScope.methodScope().recordInitializationStates(flowInfo);
LoopingFlowContext condLoopContext;
FlowInfo condInfo = 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
condInfo = this.condition.analyseCode(
currentScope,
(condLoopContext =
new LoopingFlowContext(flowContext, flowInfo, this, null,
null, currentScope, true)),
condInfo);
this.condition.checkNPEbyUnboxing(currentScope, flowContext, flowInfo);
LoopingFlowContext loopingContext;
FlowInfo actionInfo;
FlowInfo exitBranch;
if (this.action == null
|| (this.action.isEmptyBlock() && currentScope.compilerOptions().complianceLevel <= ClassFileConstants.JDK1_3)) {
condLoopContext.complainOnDeferredFinalChecks(currentScope,
condInfo);
condLoopContext.complainOnDeferredNullChecks(currentScope,
condInfo.unconditionalInits());
if (isConditionTrue) {
return FlowInfo.DEAD_END;
} else {
FlowInfo mergedInfo = flowInfo.copy().addInitializationsFrom(condInfo.initsWhenFalse());
if (isConditionOptimizedTrue){
mergedInfo.setReachMode(FlowInfo.UNREACHABLE_OR_DEAD);
}
this.mergedInitStateIndex =
currentScope.methodScope().recordInitializationStates(mergedInfo);
return mergedInfo;
}
} else {
// in case the condition was inlined to false, record the fact that there is no way to reach any
// statement inside the looping action
loopingContext =
new LoopingFlowContext(
flowContext,
flowInfo,
this,
this.breakLabel,
this.continueLabel,
currentScope,
true);
loopingContext.copyNullCheckedFieldsFrom(condLoopContext);
if (isConditionFalse) {
actionInfo = FlowInfo.DEAD_END;
} else {
actionInfo = condInfo.initsWhenTrue().copy();
if (isConditionOptimizedFalse){
actionInfo.setReachMode(FlowInfo.UNREACHABLE_OR_DEAD);
}
}
// for computing local var attributes
this.condIfTrueInitStateIndex =
currentScope.methodScope().recordInitializationStates(
condInfo.initsWhenTrue());
if (this.action.complainIfUnreachable(actionInfo, currentScope, initialComplaintLevel, true) < Statement.COMPLAINED_UNREACHABLE) {
this.condition.updateFlowOnBooleanResult(actionInfo, true);
actionInfo = this.action.analyseCode(currentScope, loopingContext, actionInfo);
}
// code generation can be optimized when no need to continue in the loop
exitBranch = flowInfo.copy();
// need to start over from flowInfo so as to get null inits
int combinedTagBits = actionInfo.tagBits & loopingContext.initsOnContinue.tagBits;
if ((combinedTagBits & FlowInfo.UNREACHABLE) != 0) {
if ((combinedTagBits & FlowInfo.UNREACHABLE_OR_DEAD) != 0)
this.continueLabel = null;
exitBranch.addInitializationsFrom(condInfo.initsWhenFalse());
actionInfo = actionInfo.mergedWith(loopingContext.initsOnContinue.unconditionalInits());
condLoopContext.complainOnDeferredNullChecks(currentScope,
actionInfo, false);
loopingContext.complainOnDeferredNullChecks(currentScope,
actionInfo, false);
} else {
condLoopContext.complainOnDeferredFinalChecks(currentScope,
condInfo);
actionInfo = actionInfo.mergedWith(loopingContext.initsOnContinue.unconditionalInits());
condLoopContext.complainOnDeferredNullChecks(currentScope,
actionInfo);
loopingContext.complainOnDeferredFinalChecks(currentScope,
actionInfo);
loopingContext.complainOnDeferredNullChecks(currentScope,
actionInfo);
exitBranch.
addPotentialInitializationsFrom(
actionInfo.unconditionalInits()).
addInitializationsFrom(condInfo.initsWhenFalse());
}
if (loopingContext.hasEscapingExceptions()) { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=321926
FlowInfo loopbackFlowInfo = flowInfo.copy();
if (this.continueLabel != null) { // we do get to the bottom
// loopback | (loopback + action):
loopbackFlowInfo = loopbackFlowInfo.mergedWith(loopbackFlowInfo.unconditionalCopy().addNullInfoFrom(actionInfo).unconditionalInits());
}
loopingContext.simulateThrowAfterLoopBack(loopbackFlowInfo);
}
}
// end of loop
FlowInfo mergedInfo = FlowInfo.mergedOptimizedBranches(
(loopingContext.initsOnBreak.tagBits &
FlowInfo.UNREACHABLE) != 0 ?
loopingContext.initsOnBreak :
flowInfo.addInitializationsFrom(loopingContext.initsOnBreak), // recover upstream null info
isConditionOptimizedTrue,
exitBranch,
isConditionOptimizedFalse,
!isConditionTrue /*while(true); unreachable(); */);
this.mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo);
this.condition.updateFlowOnBooleanResult(mergedInfo, false);
return mergedInfo;
}
/**
* While code generation
*
* @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
* @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
*/
@Override
public void generateCode(BlockScope currentScope, CodeStream codeStream) {
if ((this.bits & IsReachable) == 0) {
return;
}
if (this.condition != null && this.condition.containsPatternVariable()) {
this.condition.addPatternVariables(currentScope, codeStream);
}
int pc = codeStream.position;
Constant cst = this.condition.optimizedBooleanConstant();
boolean isConditionOptimizedFalse = cst != Constant.NotAConstant && cst.booleanValue() == false;
if (isConditionOptimizedFalse) {
this.condition.generateCode(currentScope, codeStream, false);
// May loose some local variable initializations : affecting the local variable attributes
if (this.mergedInitStateIndex != -1) {
codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.mergedInitStateIndex);
codeStream.addDefinitelyAssignedVariables(currentScope, this.mergedInitStateIndex);
}
codeStream.recordPositionsFrom(pc, this.sourceStart);
return;
}
this.breakLabel.initialize(codeStream);
// generate condition
if (this.continueLabel == null) {
// no need to reverse condition
if (this.condition.constant == Constant.NotAConstant) {
this.condition.generateOptimizedBoolean(
currentScope,
codeStream,
null,
this.breakLabel,
true);
}
} else {
this.continueLabel.initialize(codeStream);
if (!(((this.condition.constant != Constant.NotAConstant)
&& (this.condition.constant.booleanValue() == true))
|| (this.action == null)
|| this.action.isEmptyBlock())) {
int jumpPC = codeStream.position;
codeStream.goto_(this.continueLabel);
codeStream.recordPositionsFrom(jumpPC, this.condition.sourceStart);
}
}
// generate the action
BranchLabel actionLabel = new BranchLabel(codeStream);
if (this.action != null) {
actionLabel.tagBits |= BranchLabel.USED;
// Required to fix 1PR0XVS: LFRE:WINNT - Compiler: variable table for method appears incorrect
if (this.condIfTrueInitStateIndex != -1) {
// insert all locals initialized inside the condition into the action generated prior to the condition
codeStream.addDefinitelyAssignedVariables(
currentScope,
this.condIfTrueInitStateIndex);
}
actionLabel.place();
this.action.generateCode(currentScope, codeStream);
// May loose some local variable initializations : affecting the local variable attributes
if (this.preCondInitStateIndex != -1) {
codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.preCondInitStateIndex);
}
} else {
actionLabel.place();
}
// output condition and branch back to the beginning of the repeated action
if (this.continueLabel != null) {
this.continueLabel.place();
this.condition.generateOptimizedBoolean(
currentScope,
codeStream,
actionLabel,
null,
true);
}
// May loose some local variable initializations : affecting the local variable attributes
if (this.mergedInitStateIndex != -1) {
codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.mergedInitStateIndex);
codeStream.addDefinitelyAssignedVariables(currentScope, this.mergedInitStateIndex);
}
this.breakLabel.place();
codeStream.recordPositionsFrom(pc, this.sourceStart);
}
@Override
public void resolve(BlockScope scope) {
if (this.condition.containsPatternVariable()) {
this.condition.collectPatternVariablesToScope(null, scope);
LocalVariableBinding[] patternVariablesInTrueScope = this.condition.getPatternVariablesWhenTrue();
LocalVariableBinding[] patternVariablesInFalseScope = this.condition.getPatternVariablesWhenFalse();
TypeBinding type = this.condition.resolveTypeExpecting(scope, TypeBinding.BOOLEAN);
this.condition.computeConversion(scope, type, type);
if (this.action != null) {
this.action.resolveWithPatternVariablesInScope(patternVariablesInTrueScope, scope);
this.action.promotePatternVariablesIfApplicable(patternVariablesInFalseScope,
() -> !this.action.breaksOut(null));
}
} else {
TypeBinding type = this.condition.resolveTypeExpecting(scope, TypeBinding.BOOLEAN);
this.condition.computeConversion(scope, type, type);
if (this.action != null)
this.action.resolve(scope);
}
}
@Override
public StringBuffer printStatement(int tab, StringBuffer output) {
printIndent(tab, output).append("while ("); //$NON-NLS-1$
this.condition.printExpression(0, output).append(')');
if (this.action == null)
output.append(';');
else
this.action.printStatement(tab + 1, output);
return output;
}
@Override
public void traverse(
ASTVisitor visitor,
BlockScope blockScope) {
if (visitor.visit(this, blockScope)) {
this.condition.traverse(visitor, blockScope);
if (this.action != null)
this.action.traverse(visitor, blockScope);
}
visitor.endVisit(this, blockScope);
}
@Override
public boolean doesNotCompleteNormally() {
Constant cst = this.condition.constant;
boolean isConditionTrue = cst == null || cst != Constant.NotAConstant && cst.booleanValue() == true;
cst = this.condition.optimizedBooleanConstant();
boolean isConditionOptimizedTrue = cst == null ? true : cst != Constant.NotAConstant && cst.booleanValue() == true;
return (isConditionTrue || isConditionOptimizedTrue) && (this.action == null || !this.action.breaksOut(null));
}
@Override
public boolean completesByContinue() {
return this.action.continuesAtOuterLabel();
}
@Override
public boolean canCompleteNormally() {
Constant cst = this.condition.constant;
boolean isConditionTrue = cst == null || cst != Constant.NotAConstant && cst.booleanValue() == true;
cst = this.condition.optimizedBooleanConstant();
boolean isConditionOptimizedTrue = cst == null ? true : cst != Constant.NotAConstant && cst.booleanValue() == true;
if (!(isConditionTrue || isConditionOptimizedTrue))
return true;
return this.action != null && this.action.breaksOut(null);
}
@Override
public boolean continueCompletes() {
return this.action.continuesAtOuterLabel();
}
}