/******************************************************************************* * Copyright (c) 2000, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.wst.jsdt.internal.compiler.ast; import org.eclipse.wst.jsdt.core.ast.IASTNode; import org.eclipse.wst.jsdt.core.ast.IForInStatement; import org.eclipse.wst.jsdt.internal.compiler.ASTVisitor; import org.eclipse.wst.jsdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.wst.jsdt.internal.compiler.flow.FlowContext; import org.eclipse.wst.jsdt.internal.compiler.flow.FlowInfo; import org.eclipse.wst.jsdt.internal.compiler.flow.LoopingFlowContext; import org.eclipse.wst.jsdt.internal.compiler.flow.UnconditionalFlowInfo; import org.eclipse.wst.jsdt.internal.compiler.lookup.BlockScope; import org.eclipse.wst.jsdt.internal.compiler.lookup.LocalVariableBinding; import org.eclipse.wst.jsdt.internal.compiler.lookup.TypeBinding; public class ForInStatement extends Statement implements IForInStatement { public Statement iterationVariable; public Expression collection; public Statement action; //when there is no local declaration, there is no need of a new scope //scope is positionned either to a new scope, or to the "upper"scope (see resolveType) public boolean neededScope; public BlockScope scope; // for local variables table attributes int preCondInitStateIndex = -1; int condIfTrueInitStateIndex = -1; int mergedInitStateIndex = -1; public ForInStatement( Statement iterationVariable, Expression collection, Statement action, boolean neededScope, int s, int e) { this.sourceStart = s; this.sourceEnd = e; this.iterationVariable = iterationVariable; this.collection = collection; this.action = action; // remember useful empty statement if (action instanceof EmptyStatement) action.bits |= IsUsefulEmptyStatement; this.neededScope = neededScope; } public FlowInfo analyseCode( BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { // process the element variable and collection this.collection.checkNPE(currentScope, flowContext, flowInfo); flowInfo = this.iterationVariable.analyseCode(scope, flowContext, flowInfo); FlowInfo condInfo = this.collection.analyseCode(scope, flowContext, flowInfo.copy()); LocalVariableBinding iterationVariableBinding=null; if (this.iterationVariable instanceof LocalDeclaration) iterationVariableBinding=((LocalDeclaration)this.iterationVariable).binding; else if (this.iterationVariable instanceof SingleNameReference) { SingleNameReference singleNameReference =(SingleNameReference)this.iterationVariable; if (singleNameReference.binding instanceof LocalVariableBinding) iterationVariableBinding=(LocalVariableBinding)singleNameReference.binding; } // element variable will be assigned when iterating if (iterationVariableBinding!=null) condInfo.markAsDefinitelyAssigned(iterationVariableBinding); // this.postCollectionInitStateIndex = currentScope.methodScope().recordInitializationStates(condInfo); // process the action LoopingFlowContext loopingContext = new LoopingFlowContext(flowContext, flowInfo, this, scope); UnconditionalFlowInfo actionInfo = condInfo.nullInfoLessUnconditionalCopy(); if (iterationVariableBinding!=null) actionInfo.markAsDefinitelyUnknown(iterationVariableBinding); FlowInfo exitBranch; if (!(action == null || (action.isEmptyBlock() && currentScope.compilerOptions().complianceLevel <= ClassFileConstants.JDK1_3))) { if (!this.action.complainIfUnreachable(actionInfo, scope, false)) { actionInfo = action. analyseCode(scope, loopingContext, actionInfo). unconditionalCopy(); } // code generation can be optimized when no need to continue in the loop exitBranch = flowInfo.unconditionalCopy(). addInitializationsFrom(condInfo.initsWhenFalse()); // TODO (maxime) no need to test when false: can optimize (same for action being unreachable above) if ((actionInfo.tagBits & loopingContext.initsOnContinue.tagBits & FlowInfo.UNREACHABLE) != 0) { } else { actionInfo = actionInfo.mergedWith(loopingContext.initsOnContinue); exitBranch.addPotentialInitializationsFrom(actionInfo); } } else { exitBranch = condInfo.initsWhenFalse(); } // we need the variable to iterate the collection even if the // element variable is not used // final boolean hasEmptyAction = this.action == null // || this.action.isEmptyBlock() // || ((this.action.bits & IsUsefulEmptyStatement) != 0); //end of loop loopingContext.complainOnDeferredNullChecks(currentScope, actionInfo); FlowInfo mergedInfo = FlowInfo.mergedOptimizedBranches( (loopingContext.initsOnBreak.tagBits & FlowInfo.UNREACHABLE) != 0 ? loopingContext.initsOnBreak : flowInfo.addInitializationsFrom(loopingContext.initsOnBreak), // recover upstream null info false, exitBranch, false, true /*for(;;){}while(true); unreachable(); */); // mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo); return mergedInfo; } public StringBuffer printStatement(int tab, StringBuffer output) { printIndent(tab, output).append("for ("); //$NON-NLS-1$ //inits if (iterationVariable != null) { if (iterationVariable instanceof AbstractVariableDeclaration) { AbstractVariableDeclaration variable = (AbstractVariableDeclaration) iterationVariable; variable.printAsExpression(0, output); } else iterationVariable.print(0, output); } output.append(" in "); //$NON-NLS-1$ //cond if (collection != null) collection.printExpression(0, output); output.append(") "); //$NON-NLS-1$ //block if (action == null) output.append(';'); else { output.append('\n'); action.printStatement(tab + 1, output); } return output; } public void resolve(BlockScope upperScope) { // use the scope that will hold the init declarations scope = neededScope ? new BlockScope(upperScope) : upperScope; if (iterationVariable != null) { if (iterationVariable instanceof Expression) { Expression expression = (Expression) iterationVariable; expression.resolveType(scope, true, null); // TODO: show a warning message here saying this var is at global scope } else iterationVariable.resolve(scope); } if (collection != null) { TypeBinding type = collection.resolveTypeExpecting(scope, TypeBinding.BOOLEAN); } if (action != null) action.resolve(scope); } public void traverse(ASTVisitor visitor, BlockScope blockScope) { BlockScope visitScope = (this.scope != null) ? this.scope : blockScope; if (visitor.visit(this, blockScope)) { if (iterationVariable != null) { iterationVariable.traverse(visitor, visitScope); } if (collection != null) collection.traverse(visitor, visitScope); if (action != null) action.traverse(visitor, visitScope); } visitor.endVisit(this, blockScope); } public int getASTType() { return IASTNode.FOR_IN_STATEMENT; } }