Java tutorial
/******************************************************************************* * Copyright (c) 2000, 2014 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 383368 - [compiler][null] syntactic null analysis for field references * bug 403147 - [compiler][null] FUP of bug 400761: consolidate interaction between unboxing, NPE, and deferred checking *******************************************************************************/ 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 IfStatement extends Statement { //this class represents the case of only one statement in //either else and/or then branches. public Expression condition; public Statement thenStatement; public Statement elseStatement; // for local variables table attributes int thenInitStateIndex = -1; int elseInitStateIndex = -1; int mergedInitStateIndex = -1; public IfStatement(Expression condition, Statement thenStatement, int sourceStart, int sourceEnd) { this.condition = condition; this.thenStatement = thenStatement; // remember useful empty statement if (thenStatement instanceof EmptyStatement) thenStatement.bits |= IsUsefulEmptyStatement; this.sourceStart = sourceStart; this.sourceEnd = sourceEnd; } public IfStatement(Expression condition, Statement thenStatement, Statement elseStatement, int sourceStart, int sourceEnd) { this.condition = condition; this.thenStatement = thenStatement; // remember useful empty statement if (thenStatement instanceof EmptyStatement) thenStatement.bits |= IsUsefulEmptyStatement; this.elseStatement = elseStatement; if (elseStatement instanceof IfStatement) elseStatement.bits |= IsElseIfStatement; if (elseStatement instanceof EmptyStatement) elseStatement.bits |= IsUsefulEmptyStatement; this.sourceStart = sourceStart; this.sourceEnd = sourceEnd; } @Override public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { // process the condition FlowInfo conditionFlowInfo = this.condition.analyseCode(currentScope, flowContext, flowInfo); int initialComplaintLevel = (flowInfo.reachMode() & FlowInfo.UNREACHABLE) != 0 ? Statement.COMPLAINED_FAKE_REACHABLE : Statement.NOT_COMPLAINED; Constant cst = this.condition.optimizedBooleanConstant(); this.condition.checkNPEbyUnboxing(currentScope, flowContext, flowInfo); boolean isConditionOptimizedTrue = cst != Constant.NotAConstant && cst.booleanValue() == true; boolean isConditionOptimizedFalse = cst != Constant.NotAConstant && cst.booleanValue() == false; flowContext.conditionalLevel++; // process the THEN part FlowInfo thenFlowInfo = conditionFlowInfo.safeInitsWhenTrue(); if (isConditionOptimizedFalse) { thenFlowInfo.setReachMode(FlowInfo.UNREACHABLE_OR_DEAD); } FlowInfo elseFlowInfo = conditionFlowInfo.initsWhenFalse().copy(); if (isConditionOptimizedTrue) { elseFlowInfo.setReachMode(FlowInfo.UNREACHABLE_OR_DEAD); } if (((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) && ((thenFlowInfo.tagBits & FlowInfo.UNREACHABLE) != 0)) { // Mark then block as unreachable // No need if the whole if-else construct itself lies in unreachable code this.bits |= ASTNode.IsThenStatementUnreachable; } else if (((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) && ((elseFlowInfo.tagBits & FlowInfo.UNREACHABLE) != 0)) { // Mark else block as unreachable // No need if the whole if-else construct itself lies in unreachable code this.bits |= ASTNode.IsElseStatementUnreachable; } boolean reportDeadCodeForKnownPattern = !isKnowDeadCodePattern(this.condition) || currentScope.compilerOptions().reportDeadCodeInTrivialIfStatement; if (this.thenStatement != null) { // Save info for code gen this.thenInitStateIndex = currentScope.methodScope().recordInitializationStates(thenFlowInfo); if (isConditionOptimizedFalse || ((this.bits & ASTNode.IsThenStatementUnreachable) != 0)) { if (reportDeadCodeForKnownPattern) { this.thenStatement.complainIfUnreachable(thenFlowInfo, currentScope, initialComplaintLevel, false); } else { // its a known coding pattern which should be tolerated by dead code analysis // according to isKnowDeadCodePattern() this.bits &= ~ASTNode.IsThenStatementUnreachable; } } thenFlowInfo = this.thenStatement.analyseCode(currentScope, flowContext, thenFlowInfo); if (!(this.thenStatement instanceof Block)) flowContext.expireNullCheckedFieldInfo(); } // any null check from the condition is now expired flowContext.expireNullCheckedFieldInfo(); // code gen: optimizing the jump around the ELSE part if ((thenFlowInfo.tagBits & FlowInfo.UNREACHABLE_OR_DEAD) != 0) { this.bits |= ASTNode.ThenExit; } // process the ELSE part if (this.elseStatement != null) { // signal else clause unnecessarily nested, tolerate else-if code pattern if (thenFlowInfo == FlowInfo.DEAD_END && (this.bits & IsElseIfStatement) == 0 // else of an else-if && !(this.elseStatement instanceof IfStatement)) { currentScope.problemReporter().unnecessaryElse(this.elseStatement); } // Save info for code gen this.elseInitStateIndex = currentScope.methodScope().recordInitializationStates(elseFlowInfo); if (isConditionOptimizedTrue || ((this.bits & ASTNode.IsElseStatementUnreachable) != 0)) { if (reportDeadCodeForKnownPattern) { this.elseStatement.complainIfUnreachable(elseFlowInfo, currentScope, initialComplaintLevel, false); } else { // its a known coding pattern which should be tolerated by dead code analysis // according to isKnowDeadCodePattern() this.bits &= ~ASTNode.IsElseStatementUnreachable; } } elseFlowInfo = this.elseStatement.analyseCode(currentScope, flowContext, elseFlowInfo); if (!(this.elseStatement instanceof Block)) flowContext.expireNullCheckedFieldInfo(); } // process AutoCloseable resources closed in only one branch: currentScope.correlateTrackingVarsIfElse(thenFlowInfo, elseFlowInfo); // merge THEN & ELSE initializations FlowInfo mergedInfo = FlowInfo.mergedOptimizedBranchesIfElse(thenFlowInfo, isConditionOptimizedTrue, elseFlowInfo, isConditionOptimizedFalse, true /*if(true){ return; } fake-reachable(); */, flowInfo, this, reportDeadCodeForKnownPattern); this.mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo); flowContext.conditionalLevel--; return mergedInfo; } /** * If 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; } int pc = codeStream.position; BranchLabel endifLabel = new BranchLabel(codeStream); // optimizing the then/else part code gen Constant cst; boolean hasThenPart = !(((cst = this.condition.optimizedBooleanConstant()) != Constant.NotAConstant && cst.booleanValue() == false) || this.thenStatement == null || this.thenStatement.isEmptyBlock()); boolean hasElsePart = !((cst != Constant.NotAConstant && cst.booleanValue() == true) || this.elseStatement == null || this.elseStatement.isEmptyBlock()); if (hasThenPart) { BranchLabel falseLabel = null; // generate boolean condition only if needed if (cst != Constant.NotAConstant && cst.booleanValue() == true) { this.condition.generateCode(currentScope, codeStream, false); } else { this.condition.generateOptimizedBoolean(currentScope, codeStream, null, hasElsePart ? (falseLabel = new BranchLabel(codeStream)) : endifLabel, true/*cst == Constant.NotAConstant*/); } // May loose some local variable initializations : affecting the local variable attributes if (this.thenInitStateIndex != -1) { codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.thenInitStateIndex); codeStream.addDefinitelyAssignedVariables(currentScope, this.thenInitStateIndex); } // generate then statement this.thenStatement.generateCode(currentScope, codeStream); // jump around the else statement if (hasElsePart) { if ((this.bits & ASTNode.ThenExit) == 0) { this.thenStatement.branchChainTo(endifLabel); int position = codeStream.position; codeStream.goto_(endifLabel); //goto is pointing to the last line of the thenStatement codeStream.recordPositionsFrom(position, this.thenStatement.sourceEnd); // generate else statement } // May loose some local variable initializations : affecting the local variable attributes if (this.elseInitStateIndex != -1) { codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.elseInitStateIndex); codeStream.addDefinitelyAssignedVariables(currentScope, this.elseInitStateIndex); } if (falseLabel != null) falseLabel.place(); this.elseStatement.generateCode(currentScope, codeStream); } } else if (hasElsePart) { // generate boolean condition only if needed if (cst != Constant.NotAConstant && cst.booleanValue() == false) { this.condition.generateCode(currentScope, codeStream, false); } else { this.condition.generateOptimizedBoolean(currentScope, codeStream, endifLabel, null, true/*cst == Constant.NotAConstant*/); } // generate else statement // May loose some local variable initializations : affecting the local variable attributes if (this.elseInitStateIndex != -1) { codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.elseInitStateIndex); codeStream.addDefinitelyAssignedVariables(currentScope, this.elseInitStateIndex); } this.elseStatement.generateCode(currentScope, codeStream); } else { // generate condition side-effects this.condition.generateCode(currentScope, codeStream, false); codeStream.recordPositionsFrom(pc, this.sourceStart); } // 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); } endifLabel.place(); codeStream.recordPositionsFrom(pc, this.sourceStart); } @Override public StringBuffer printStatement(int indent, StringBuffer output) { printIndent(indent, output).append("if ("); //$NON-NLS-1$ this.condition.printExpression(0, output).append(")\n"); //$NON-NLS-1$ this.thenStatement.printStatement(indent + 2, output); if (this.elseStatement != null) { output.append('\n'); printIndent(indent, output); output.append("else\n"); //$NON-NLS-1$ this.elseStatement.printStatement(indent + 2, output); } return output; } @Override public void resolve(BlockScope scope) { TypeBinding type = this.condition.resolveTypeExpecting(scope, TypeBinding.BOOLEAN); this.condition.computeConversion(scope, type, type); if (this.thenStatement != null) this.thenStatement.resolve(scope); if (this.elseStatement != null) this.elseStatement.resolve(scope); } @Override public void traverse(ASTVisitor visitor, BlockScope blockScope) { if (visitor.visit(this, blockScope)) { this.condition.traverse(visitor, blockScope); if (this.thenStatement != null) this.thenStatement.traverse(visitor, blockScope); if (this.elseStatement != null) this.elseStatement.traverse(visitor, blockScope); } visitor.endVisit(this, blockScope); } @Override public boolean doesNotCompleteNormally() { return this.thenStatement != null && this.thenStatement.doesNotCompleteNormally() && this.elseStatement != null && this.elseStatement.doesNotCompleteNormally(); } @Override public boolean completesByContinue() { return this.thenStatement != null && this.thenStatement.completesByContinue() || this.elseStatement != null && this.elseStatement.completesByContinue(); } }