Java tutorial
/******************************************************************************* * Copyright (c) 2000, 2015 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 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.codegen.*; import org.eclipse.jdt.internal.compiler.flow.*; import org.eclipse.jdt.internal.compiler.lookup.*; public class DoStatement extends Statement { public Expression condition; public Statement action; private BranchLabel breakLabel, continueLabel; // for local variables table attributes int mergedInitStateIndex = -1; int preConditionInitStateIndex = -1; public DoStatement(Expression condition, Statement action, int sourceStart, int sourceEnd) { this.sourceStart = sourceStart; this.sourceEnd = sourceEnd; this.condition = condition; this.action = action; // remember useful empty statement if (action instanceof EmptyStatement) action.bits |= ASTNode.IsUsefulEmptyStatement; } @Override 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, false); 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(); FlowInfo initsOnCondition = flowInfo; 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_OR_DEAD) != 0) { this.continueLabel = null; } if ((this.condition.implicitConversion & TypeIds.UNBOXING) != 0) { initsOnCondition = flowInfo.unconditionalInits() .addInitializationsFrom(actionInfo.mergedWith(loopingContext.initsOnContinue)); } } this.condition.checkNPEbyUnboxing(currentScope, flowContext, initsOnCondition); /* 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, true)), (this.action == null ? actionInfo : (actionInfo.mergedWith(loopingContext.initsOnContinue))) .copy()); /* https://bugs.eclipse.org/bugs/show_bug.cgi?id=367023, we reach the condition at the bottom via two arcs, one by free fall and another by continuing... Merge initializations propagated through the two pathways, cf, while and for loops. */ this.preConditionInitStateIndex = currentScope.methodScope() .recordInitializationStates(actionInfo.mergedWith(loopingContext.initsOnContinue)); 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())); } else { loopingContext.complainOnDeferredNullChecks(currentScope, flowInfo.unconditionalCopy() .addPotentialNullInfoFrom(condInfo.initsWhenTrue().unconditionalInits()), false); condLoopContext.complainOnDeferredNullChecks(currentScope, actionInfo.addPotentialNullInfoFrom(condInfo.initsWhenTrue().unconditionalInits()), false); } if (loopingContext.hasEscapingExceptions()) { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=321926 FlowInfo loopbackFlowInfo = flowInfo.copy(); // loopback | (loopback + action + condition): loopbackFlowInfo = loopbackFlowInfo.mergedWith(loopbackFlowInfo.unconditionalCopy() .addNullInfoFrom(condInfo.initsWhenTrue()).unconditionalInits()); loopingContext.simulateThrowAfterLoopBack(loopbackFlowInfo); } // 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.copy().addInitializationsFrom(condInfo.initsWhenFalse()) // https://bugs.eclipse.org/bugs/show_bug.cgi?id=380927 : 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; } /** * Do statement code generation * */ @Override public void generateCode(BlockScope currentScope, CodeStream codeStream) { if ((this.bits & ASTNode.IsReachable) == 0) { return; } int pc = codeStream.position; // labels management BranchLabel actionLabel = new BranchLabel(codeStream); if (this.action != null) actionLabel.tagBits |= BranchLabel.USED; actionLabel.place(); this.breakLabel.initialize(codeStream); boolean hasContinueLabel = this.continueLabel != null; if (hasContinueLabel) { this.continueLabel.initialize(codeStream); } // generate action if (this.action != null) { this.action.generateCode(currentScope, codeStream); } // continue label (135602) if (hasContinueLabel) { this.continueLabel.place(); // May loose some local variable initializations : affecting the local variable attributes if (this.preConditionInitStateIndex != -1) { codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.preConditionInitStateIndex); codeStream.addDefinitelyAssignedVariables(currentScope, this.preConditionInitStateIndex); } // generate condition Constant cst = this.condition.optimizedBooleanConstant(); boolean isConditionOptimizedFalse = cst != Constant.NotAConstant && cst.booleanValue() == false; if (isConditionOptimizedFalse) { this.condition.generateCode(currentScope, codeStream, false); } else { 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); } if (this.breakLabel.forwardReferenceCount() > 0) { this.breakLabel.place(); } codeStream.recordPositionsFrom(pc, this.sourceStart); } @Override public StringBuffer printStatement(int indent, StringBuffer output) { printIndent(indent, output).append("do"); //$NON-NLS-1$ if (this.action == null) output.append(" ;\n"); //$NON-NLS-1$ else { output.append('\n'); this.action.printStatement(indent + 1, output).append('\n'); } output.append("while ("); //$NON-NLS-1$ return this.condition.printExpression(0, output).append(");"); //$NON-NLS-1$ } @Override public void resolve(BlockScope scope) { TypeBinding type = this.condition.resolveTypeExpecting(scope, TypeBinding.BOOLEAN); this.condition.computeConversion(scope, type, type); if (this.action != null) this.action.resolve(scope); } @Override public void traverse(ASTVisitor visitor, BlockScope scope) { if (visitor.visit(this, scope)) { if (this.action != null) { this.action.traverse(visitor, scope); } this.condition.traverse(visitor, scope); } visitor.endVisit(this, scope); } @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; if (isConditionTrue || isConditionOptimizedTrue) return this.action == null || !this.action.breaksOut(null); if (this.action == null || this.action.breaksOut(null)) return false; return this.action.doesNotCompleteNormally() && !this.action.completesByContinue(); } @Override public boolean completesByContinue() { return this.action.continuesAtOuterLabel(); } }