/*******************************************************************************
* 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.IForeachStatement;
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.ArrayBinding;
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.ReferenceBinding;
import org.eclipse.wst.jsdt.internal.compiler.lookup.TypeBinding;
public class ForeachStatement extends Statement implements IForeachStatement {
public LocalDeclaration elementVariable;
public int elementVariableImplicitWidening = -1;
public Expression collection;
public Statement action;
// set the kind of foreach
private int kind;
// possible kinds of iterating behavior
private static final int ARRAY = 0;
private static final int RAW_ITERABLE = 1;
private static final int GENERIC_ITERABLE = 2;
private TypeBinding iteratorReceiverType;
private TypeBinding collectionElementType;
public BlockScope scope;
// secret variables for codegen
public LocalVariableBinding indexVariable;
public LocalVariableBinding collectionVariable; // to store the collection expression value
public LocalVariableBinding maxVariable;
// secret variable names
private static final char[] SecretIndexVariableName = " index".toCharArray(); //$NON-NLS-1$
private static final char[] SecretCollectionVariableName = " collection".toCharArray(); //$NON-NLS-1$
private static final char[] SecretMaxVariableName = " max".toCharArray(); //$NON-NLS-1$
int postCollectionInitStateIndex = -1;
int mergedInitStateIndex = -1;
public ForeachStatement(
LocalDeclaration elementVariable,
int start) {
this.elementVariable = elementVariable;
this.sourceStart = start;
this.kind = -1;
}
public FlowInfo analyseCode(
BlockScope currentScope,
FlowContext flowContext,
FlowInfo flowInfo) {
// initialize break and continue labels
boolean isContinue=true;
// process the element variable and collection
this.collection.checkNPE(currentScope, flowContext, flowInfo);
flowInfo = this.elementVariable.analyseCode(scope, flowContext, flowInfo);
FlowInfo condInfo = this.collection.analyseCode(scope, flowContext, flowInfo.copy());
// element variable will be assigned when iterating
condInfo.markAsDefinitelyAssigned(this.elementVariable.binding);
// this.postCollectionInitStateIndex = currentScope.methodScope().recordInitializationStates(condInfo);
// process the action
LoopingFlowContext loopingContext =
new LoopingFlowContext(flowContext, flowInfo, this, scope);
UnconditionalFlowInfo actionInfo =
condInfo.nullInfoLessUnconditionalCopy();
actionInfo.markAsDefinitelyUnknown(this.elementVariable.binding);
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) {
isContinue = false;
} 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);
switch(this.kind) {
case ARRAY :
if (!hasEmptyAction
|| this.elementVariable.binding.resolvedPosition != -1) {
this.collectionVariable.useFlag = LocalVariableBinding.USED;
if (isContinue) {
this.indexVariable.useFlag = LocalVariableBinding.USED;
this.maxVariable.useFlag = LocalVariableBinding.USED;
}
}
break;
case RAW_ITERABLE :
case GENERIC_ITERABLE :
this.indexVariable.useFlag = LocalVariableBinding.USED;
break;
}
//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 indent, StringBuffer output) {
printIndent(indent, output).append("for ("); //$NON-NLS-1$
this.elementVariable.printAsExpression(0, output);
output.append(" : ");//$NON-NLS-1$
this.collection.print(0, output).append(") "); //$NON-NLS-1$
//block
if (this.action == null) {
output.append(';');
} else {
output.append('\n');
this.action.printStatement(indent + 1, output);
}
return output;
}
public void resolve(BlockScope upperScope) {
// use the scope that will hold the init declarations
scope = new BlockScope(upperScope);
this.elementVariable.resolve(scope); // collection expression can see itemVariable
TypeBinding elementType = this.elementVariable.type.resolvedType;
TypeBinding collectionType = this.collection == null ? null : this.collection.resolveType(scope);
if (elementType != null && collectionType != null) {
if (collectionType.isArrayType()) { // for(E e : E[])
this.kind = ARRAY;
this.collectionElementType = ((ArrayBinding) collectionType).elementsType();
// in case we need to do a conversion
int compileTimeTypeID = collectionElementType.id;
if (elementType.isBaseType()) {
if (!collectionElementType.isBaseType()) {
compileTimeTypeID = scope.environment().computeBoxingType(collectionElementType).id;
this.elementVariableImplicitWidening = UNBOXING;
if (elementType.isBaseType()) {
this.elementVariableImplicitWidening |= (elementType.id << 4) + compileTimeTypeID;
}
} else {
this.elementVariableImplicitWidening = (elementType.id << 4) + compileTimeTypeID;
}
} else {
if (collectionElementType.isBaseType()) {
int boxedID = scope.environment().computeBoxingType(collectionElementType).id;
this.elementVariableImplicitWidening = BOXING | (compileTimeTypeID << 4) | compileTimeTypeID; // use primitive type in implicit conversion
compileTimeTypeID = boxedID;
}
}
} else if (collectionType instanceof ReferenceBinding) {
ReferenceBinding iterableType = ((ReferenceBinding)collectionType).findSuperTypeErasingTo(T_JavaLangIterable, false /*Iterable is not a class*/);
checkIterable: {
if (iterableType == null) break checkIterable;
this.iteratorReceiverType = collectionType;
if (((ReferenceBinding)iteratorReceiverType).findSuperTypeErasingTo(T_JavaLangIterable, false) == null) {
this.iteratorReceiverType = iterableType; // handle indirect inheritance thru variable secondary bound
}
break checkIterable;
}
}
switch(this.kind) {
case ARRAY :
// allocate #index secret variable (of type int)
this.indexVariable = new LocalVariableBinding(SecretIndexVariableName, TypeBinding.INT, ClassFileConstants.AccDefault, false);
scope.addLocalVariable(this.indexVariable);
// allocate #max secret variable
this.maxVariable = new LocalVariableBinding(SecretMaxVariableName, TypeBinding.INT, ClassFileConstants.AccDefault, false);
scope.addLocalVariable(this.maxVariable);
// add #array secret variable (of collection type)
this.collectionVariable = new LocalVariableBinding(SecretCollectionVariableName, collectionType, ClassFileConstants.AccDefault, false);
scope.addLocalVariable(this.collectionVariable);
break;
case RAW_ITERABLE :
case GENERIC_ITERABLE :
// // allocate #index secret variable (of type Iterator)
// this.indexVariable = new LocalVariableBinding(SecretIndexVariableName, scope.getJavaUtilIterator(), ClassFileConstants.AccDefault, false);
// scope.addLocalVariable(this.indexVariable);
// this.indexVariable.setConstant(Constant.NotAConstant); // not inlinable
break;
}
}
if (action != null) {
action.resolve(scope);
}
}
public void traverse(
ASTVisitor visitor,
BlockScope blockScope) {
if (visitor.visit(this, blockScope)) {
this.elementVariable.traverse(visitor, scope);
this.collection.traverse(visitor, scope);
if (action != null) {
action.traverse(visitor, scope);
}
}
visitor.endVisit(this, blockScope);
}
public int getASTType() {
return IASTNode.FOR_EACH_STATEMENT;
}
}