/******************************************************************************* * Copyright (c) 2000, 2004 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Common Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/cpl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ 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; boolean thenExit; // 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 |= IsUsefulEmptyStatementMASK; 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 |= IsUsefulEmptyStatementMASK; this.elseStatement = elseStatement; if (elseStatement instanceof IfStatement) elseStatement.bits |= IsElseIfStatement; this.sourceStart = sourceStart; this.sourceEnd = sourceEnd; } public FlowInfo analyseCode( BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { // process the condition flowInfo = condition.analyseCode(currentScope, flowContext, flowInfo); Constant cst = this.condition.optimizedBooleanConstant(); boolean isConditionOptimizedTrue = cst != NotAConstant && cst.booleanValue() == true; boolean isConditionOptimizedFalse = cst != NotAConstant && cst.booleanValue() == false; // process the THEN part FlowInfo thenFlowInfo = flowInfo.initsWhenTrue().copy(); if (isConditionOptimizedFalse) { thenFlowInfo.setReachMode(FlowInfo.UNREACHABLE); } if (this.thenStatement != null) { // Save info for code gen thenInitStateIndex = currentScope.methodScope().recordInitializationStates(thenFlowInfo); if (!thenStatement.complainIfUnreachable(thenFlowInfo, currentScope, false)) { thenFlowInfo = thenStatement.analyseCode(currentScope, flowContext, thenFlowInfo); } } // code gen: optimizing the jump around the ELSE part this.thenExit = !thenFlowInfo.isReachable(); // process the ELSE part FlowInfo elseFlowInfo = flowInfo.initsWhenFalse().copy(); if (isConditionOptimizedTrue) { elseFlowInfo.setReachMode(FlowInfo.UNREACHABLE); } 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 elseInitStateIndex = currentScope.methodScope().recordInitializationStates(elseFlowInfo); if (!elseStatement.complainIfUnreachable(elseFlowInfo, currentScope, false)) { elseFlowInfo = elseStatement.analyseCode(currentScope, flowContext, elseFlowInfo); } } // merge THEN & ELSE initializations FlowInfo mergedInfo = FlowInfo.mergedOptimizedBranches( thenFlowInfo, isConditionOptimizedTrue, elseFlowInfo, isConditionOptimizedFalse, true /*if(true){ return; } fake-reachable(); */); mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo); return mergedInfo; } /** * If code generation * * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream */ public void generateCode(BlockScope currentScope, CodeStream codeStream) { if ((this.bits & IsReachableMASK) == 0) { return; } int pc = codeStream.position; Label endifLabel = new Label(codeStream); // optimizing the then/else part code gen Constant cst; boolean hasThenPart = !(((cst = this.condition.optimizedBooleanConstant()) != NotAConstant && cst.booleanValue() == false) || this.thenStatement == null || this.thenStatement.isEmptyBlock()); boolean hasElsePart = !((cst != NotAConstant && cst.booleanValue() == true) || this.elseStatement == null || this.elseStatement.isEmptyBlock()); if (hasThenPart) { Label falseLabel; // generate boolean condition this.condition.generateOptimizedBoolean( currentScope, codeStream, null, (falseLabel = new Label(codeStream)), true); // May loose some local variable initializations : affecting the local variable attributes if (thenInitStateIndex != -1) { codeStream.removeNotDefinitelyAssignedVariables(currentScope, thenInitStateIndex); codeStream.addDefinitelyAssignedVariables(currentScope, thenInitStateIndex); } // generate then statement this.thenStatement.generateCode(currentScope, codeStream); // jump around the else statement if (hasElsePart && !thenExit) { this.thenStatement.branchChainTo(endifLabel); int position = codeStream.position; codeStream.goto_(endifLabel); codeStream.updateLastRecordedEndPC(position); //goto is tagged as part of the thenAction block } falseLabel.place(); } else { if (hasElsePart) { // generate boolean condition this.condition.generateOptimizedBoolean( currentScope, codeStream, endifLabel, null, true); } else { // generate condition side-effects this.condition.generateCode(currentScope, codeStream, false); codeStream.recordPositionsFrom(pc, this.sourceStart); } } // generate else statement if (hasElsePart) { // May loose some local variable initializations : affecting the local variable attributes if (elseInitStateIndex != -1) { codeStream.removeNotDefinitelyAssignedVariables( currentScope, elseInitStateIndex); codeStream.addDefinitelyAssignedVariables(currentScope, elseInitStateIndex); } this.elseStatement.generateCode(currentScope, codeStream); } endifLabel.place(); // May loose some local variable initializations : affecting the local variable attributes if (mergedInitStateIndex != -1) { codeStream.removeNotDefinitelyAssignedVariables( currentScope, mergedInitStateIndex); } codeStream.recordPositionsFrom(pc, this.sourceStart); } public StringBuffer printStatement(int indent, StringBuffer output) { printIndent(indent, output).append("if ("); //$NON-NLS-1$ condition.printExpression(0, output).append(")\n"); //$NON-NLS-1$ thenStatement.printStatement(indent + 2, output); if (elseStatement != null) { output.append('\n'); printIndent(indent, output); output.append("else\n"); //$NON-NLS-1$ elseStatement.printStatement(indent + 2, output); } return output; } public void resolve(BlockScope scope) { TypeBinding type = condition.resolveTypeExpecting(scope, BooleanBinding); condition.computeConversion(scope, type, type); if (thenStatement != null) thenStatement.resolve(scope); if (elseStatement != null) elseStatement.resolve(scope); } public void traverse( ASTVisitor visitor, BlockScope blockScope) { if (visitor.visit(this, blockScope)) { condition.traverse(visitor, blockScope); if (thenStatement != null) thenStatement.traverse(visitor, blockScope); if (elseStatement != null) elseStatement.traverse(visitor, blockScope); } visitor.endVisit(this, blockScope); } }