1 /*******************************************************************************
2 * Copyright (c) 2000, 2004 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v10.html
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package org.eclipse.jdt.internal.compiler.ast;
13 import org.eclipse.jdt.internal.compiler.ASTVisitor;
14 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
15 import org.eclipse.jdt.internal.compiler.codegen.CodeStream;
16 import org.eclipse.jdt.internal.compiler.codegen.Label;
17 import org.eclipse.jdt.internal.compiler.flow.FlowContext;
18 import org.eclipse.jdt.internal.compiler.flow.FlowInfo;
19 import org.eclipse.jdt.internal.compiler.flow.LoopingFlowContext;
20 import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
21 import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
22 import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
23 import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
24 import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
25 import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
26 import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
27 import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
29 public class ForeachStatement extends Statement {
31 public LocalDeclaration elementVariable;
32 public int elementVariableImplicitWidening = -1;
33 public Expression collection;
34 public Statement action;
36 // set the kind of foreach
38 // possible kinds of iterating behavior
39 private static final int ARRAY = 0;
40 private static final int RAW_ITERABLE = 1;
41 private static final int GENERIC_ITERABLE = 2;
43 private TypeBinding collectionElementType;
46 private Label breakLabel;
47 private Label continueLabel;
49 public BlockScope scope;
51 // secret variables for codegen
52 public LocalVariableBinding indexVariable;
53 public LocalVariableBinding collectionVariable; // to store the collection expression value
54 public LocalVariableBinding maxVariable;
55 // secret variable names
56 private static final char[] SecretIndexVariableName = " index".toCharArray(); //$NON-NLS-1$
57 private static final char[] SecretCollectionVariableName = " collection".toCharArray(); //$NON-NLS-1$
58 private static final char[] SecretMaxVariableName = " max".toCharArray(); //$NON-NLS-1$
60 int postCollectionInitStateIndex = -1;
61 int mergedInitStateIndex = -1;
63 public ForeachStatement(
64 LocalDeclaration elementVariable,
65 Expression collection,
68 this.elementVariable = elementVariable;
69 this.collection = collection;
70 this.sourceStart = start;
74 public FlowInfo analyseCode(
75 BlockScope currentScope,
76 FlowContext flowContext,
78 // initialize break and continue labels
79 breakLabel = new Label();
80 continueLabel = new Label();
82 // process the element variable and collection
83 flowInfo = this.elementVariable.analyseCode(scope, flowContext, flowInfo);
84 FlowInfo condInfo = flowInfo.copy().unconditionalInits().discardNullRelatedInitializations();
85 condInfo = this.collection.analyseCode(scope, flowContext, condInfo);
87 // element variable will be assigned when iterating
88 condInfo.markAsDefinitelyAssigned(this.elementVariable.binding);
90 this.postCollectionInitStateIndex = currentScope.methodScope().recordInitializationStates(condInfo);
93 LoopingFlowContext loopingContext = new LoopingFlowContext(flowContext, this, breakLabel, continueLabel, scope);
94 FlowInfo actionInfo = condInfo.initsWhenTrue().copy();
96 if (!(action == null || (action.isEmptyBlock()
97 && currentScope.environment().options.complianceLevel <= ClassFileConstants.JDK1_3))) {
99 if (!this.action.complainIfUnreachable(actionInfo, scope, false)) {
100 actionInfo = action.analyseCode(scope, loopingContext, actionInfo);
103 // code generation can be optimized when no need to continue in the loop
104 exitBranch = condInfo.initsWhenFalse();
105 exitBranch.addInitializationsFrom(flowInfo); // recover null inits from before condition analysis
106 if (!actionInfo.isReachable() && !loopingContext.initsOnContinue.isReachable()) {
107 continueLabel = null;
109 actionInfo = actionInfo.mergedWith(loopingContext.initsOnContinue.unconditionalInits());
110 loopingContext.complainOnDeferredChecks(scope, actionInfo);
111 exitBranch.addPotentialInitializationsFrom(actionInfo.unconditionalInits());
114 exitBranch = condInfo.initsWhenFalse();
117 // we need the variable to iterate the collection even if the
118 // element variable is not used
119 if (!(this.action == null
120 || this.action.isEmptyBlock()
121 || ((this.action.bits & IsUsefulEmptyStatementMASK) != 0))) {
124 this.collectionVariable.useFlag = LocalVariableBinding.USED;
125 this.indexVariable.useFlag = LocalVariableBinding.USED;
126 this.maxVariable.useFlag = LocalVariableBinding.USED;
129 case GENERIC_ITERABLE :
130 this.indexVariable.useFlag = LocalVariableBinding.USED;
135 FlowInfo mergedInfo = FlowInfo.mergedOptimizedBranches(
136 loopingContext.initsOnBreak,
140 true /*for(;;){}while(true); unreachable(); */);
141 mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo);
146 * For statement code generation
148 * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
149 * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
151 public void generateCode(BlockScope currentScope, CodeStream codeStream) {
153 if ((bits & IsReachableMASK) == 0) {
156 int pc = codeStream.position;
157 if (this.action == null
158 || this.action.isEmptyBlock()
159 || ((this.action.bits & IsUsefulEmptyStatementMASK) != 0)) {
160 codeStream.exitUserScope(scope);
161 if (mergedInitStateIndex != -1) {
162 codeStream.removeNotDefinitelyAssignedVariables(currentScope, mergedInitStateIndex);
163 codeStream.addDefinitelyAssignedVariables(currentScope, mergedInitStateIndex);
165 codeStream.recordPositionsFrom(pc, this.sourceStart);
168 // generate the initializations
171 collection.generateCode(scope, codeStream, true);
172 codeStream.store(this.collectionVariable, false);
173 codeStream.iconst_0();
174 codeStream.store(this.indexVariable, false);
175 codeStream.load(this.collectionVariable);
176 codeStream.arraylength();
177 codeStream.store(this.maxVariable, false);
180 case GENERIC_ITERABLE :
181 collection.generateCode(scope, codeStream, true);
182 // declaringClass.iterator();
183 final TypeBinding collectionTypeBinding = collection.resolvedType;
184 MethodBinding iteratorMethodBinding =
187 "iterator".toCharArray(),//$NON-NLS-1$
188 scope.getJavaUtilIterator(),
189 TypeConstants.NoParameters,
190 TypeConstants.NoExceptions,
191 (ReferenceBinding) collectionTypeBinding);
192 if (collectionTypeBinding.isInterface()) {
193 codeStream.invokeinterface(iteratorMethodBinding);
195 codeStream.invokevirtual(iteratorMethodBinding);
197 codeStream.store(this.indexVariable, false);
202 Label actionLabel = new Label(codeStream);
203 Label conditionLabel = new Label(codeStream);
204 breakLabel.initialize(codeStream);
205 if (this.continueLabel != null) {
206 this.continueLabel.initialize(codeStream);
208 // jump over the actionBlock
209 codeStream.goto_(conditionLabel);
211 // generate the loop action
214 // generate the loop action
215 if (this.elementVariable.binding.resolvedPosition != -1) {
218 codeStream.load(this.collectionVariable);
219 codeStream.load(this.indexVariable);
220 codeStream.arrayAt(this.collectionElementType.id);
221 if (this.elementVariableImplicitWidening != -1) {
222 codeStream.generateImplicitConversion(this.elementVariableImplicitWidening);
224 codeStream.store(this.elementVariable.binding, false);
227 case GENERIC_ITERABLE :
228 codeStream.load(this.indexVariable);
229 codeStream.invokeJavaUtilIteratorNext();
230 if (this.elementVariable.binding.type.id != T_JavaLangObject) {
231 if (this.elementVariableImplicitWidening != -1) {
232 codeStream.checkcast(this.collectionElementType);
233 codeStream.generateImplicitConversion(this.elementVariableImplicitWidening);
235 codeStream.checkcast(this.elementVariable.binding.type);
238 codeStream.store(this.elementVariable.binding, false);
241 codeStream.addVisibleLocalVariable(this.elementVariable.binding);
242 if (this.postCollectionInitStateIndex != -1) {
243 codeStream.addDefinitelyAssignedVariables(
245 this.postCollectionInitStateIndex);
248 this.action.generateCode(scope, codeStream);
250 // continuation point
251 int continuationPC = codeStream.position;
252 if (this.continueLabel != null) {
253 this.continueLabel.place();
254 // generate the increments for next iteration
257 codeStream.iinc(this.indexVariable.resolvedPosition, 1);
260 case GENERIC_ITERABLE :
264 // generate the condition
265 conditionLabel.place();
266 if (this.postCollectionInitStateIndex != -1) {
267 codeStream.removeNotDefinitelyAssignedVariables(currentScope, postCollectionInitStateIndex);
271 codeStream.load(this.indexVariable);
272 codeStream.load(this.maxVariable);
273 codeStream.if_icmplt(actionLabel);
276 case GENERIC_ITERABLE :
277 codeStream.load(this.indexVariable);
278 codeStream.invokeJavaUtilIteratorHasNext();
279 codeStream.ifne(actionLabel);
282 codeStream.recordPositionsFrom(continuationPC, this.elementVariable.sourceStart);
285 codeStream.exitUserScope(scope);
286 if (mergedInitStateIndex != -1) {
287 codeStream.removeNotDefinitelyAssignedVariables(currentScope, mergedInitStateIndex);
288 codeStream.addDefinitelyAssignedVariables(currentScope, mergedInitStateIndex);
290 codeStream.recordPositionsFrom(pc, this.sourceStart);
293 public StringBuffer printStatement(int tab, StringBuffer output) {
295 printIndent(tab, output).append("for ("); //$NON-NLS-1$
296 this.elementVariable.print(0, output);
297 output.append(" : ");//$NON-NLS-1$
298 this.collection.print(0, output).append(") "); //$NON-NLS-1$
300 if (this.action == null) {
304 this.action.printStatement(tab + 1, output); //$NON-NLS-1$
309 public void resolve(BlockScope upperScope) {
310 // use the scope that will hold the init declarations
311 scope = new BlockScope(upperScope);
312 this.elementVariable.resolve(scope); // collection expression can see itemVariable
313 TypeBinding elementType = this.elementVariable.type.resolvedType;
314 TypeBinding collectionType = this.collection.resolveType(scope);
315 this.collection.computeConversion(scope, collectionType, collectionType);
316 boolean hasError = elementType == null || collectionType == null;
319 if (collectionType.isArrayType()) { // for(E e : E[])
321 this.collectionElementType = ((ArrayBinding) collectionType).elementsType();
322 if (!collectionElementType.isCompatibleWith(elementType)
323 && !scope.isBoxingCompatibleWith(collectionElementType, elementType)) {
324 scope.problemReporter().notCompatibleTypesErrorInForeach(collection, collectionElementType, elementType);
326 // in case we need to do a conversion
327 int compileTimeTypeID = collectionElementType.id;
328 if (elementType.isBaseType()) {
329 if (!collectionElementType.isBaseType()) {
330 compileTimeTypeID = scope.environment().computeBoxingType(collectionElementType).id;
331 this.elementVariableImplicitWidening = UNBOXING;
332 if (elementType.isBaseType()) {
333 this.elementVariableImplicitWidening |= (elementType.id << 4) + compileTimeTypeID;
336 this.elementVariableImplicitWidening = (elementType.id << 4) + compileTimeTypeID;
339 if (collectionElementType.isBaseType()) {
340 int boxedID = scope.environment().computeBoxingType(collectionElementType).id;
341 this.elementVariableImplicitWidening = BOXING | (compileTimeTypeID << 4) | compileTimeTypeID; // use primitive type in implicit conversion
342 compileTimeTypeID = boxedID;
345 } else if (collectionType instanceof ReferenceBinding) {
346 ReferenceBinding iterableType = ((ReferenceBinding)collectionType).findSuperTypeErasingTo(T_JavaLangIterable, false /*Iterable is not a class*/);
347 if (iterableType != null) {
348 if (iterableType.isParameterizedType()) { // for(E e : Iterable<E>)
349 ParameterizedTypeBinding parameterizedType = (ParameterizedTypeBinding)iterableType;
350 if (parameterizedType.arguments.length == 1) { // per construction can only be one
351 this.kind = GENERIC_ITERABLE;
352 this.collectionElementType = parameterizedType.arguments[0];
353 if (!collectionElementType.isCompatibleWith(elementType)
354 && !scope.isBoxingCompatibleWith(collectionElementType, elementType)) {
355 scope.problemReporter().notCompatibleTypesErrorInForeach(collection, collectionElementType, elementType);
357 int compileTimeTypeID = collectionElementType.id;
358 // no conversion needed as only for reference types
359 if (elementType.isBaseType()) {
360 if (!collectionElementType.isBaseType()) {
361 compileTimeTypeID = scope.environment().computeBoxingType(collectionElementType).id;
362 this.elementVariableImplicitWidening = UNBOXING;
363 if (elementType.isBaseType()) {
364 this.elementVariableImplicitWidening |= (elementType.id << 4) + compileTimeTypeID;
367 this.elementVariableImplicitWidening = (elementType.id << 4) + compileTimeTypeID;
370 if (collectionElementType.isBaseType()) {
371 int boxedID = scope.environment().computeBoxingType(collectionElementType).id;
372 this.elementVariableImplicitWidening = BOXING | (compileTimeTypeID << 4) | compileTimeTypeID; // use primitive type in implicit conversion
373 compileTimeTypeID = boxedID;
377 } else if (iterableType.isGenericType()) { // for (T t : Iterable<T>) - in case used inside Iterable itself
378 if (iterableType.typeVariables().length == 1) {
379 this.kind = GENERIC_ITERABLE;
380 this.collectionElementType = iterableType.typeVariables()[0];
381 if (!collectionElementType.isCompatibleWith(elementType)
382 && !scope.isBoxingCompatibleWith(collectionElementType, elementType)) {
383 scope.problemReporter().notCompatibleTypesErrorInForeach(collection, collectionElementType, elementType);
385 int compileTimeTypeID = collectionElementType.id;
386 // no conversion needed as only for reference types
387 if (elementType.isBaseType()) {
388 if (!collectionElementType.isBaseType()) {
389 compileTimeTypeID = scope.environment().computeBoxingType(collectionElementType).id;
390 this.elementVariableImplicitWidening = UNBOXING;
391 if (elementType.isBaseType()) {
392 this.elementVariableImplicitWidening |= (elementType.id << 4) + compileTimeTypeID;
395 this.elementVariableImplicitWidening = (elementType.id << 4) + compileTimeTypeID;
398 if (collectionElementType.isBaseType()) {
399 int boxedID = scope.environment().computeBoxingType(collectionElementType).id;
400 this.elementVariableImplicitWidening = BOXING | (compileTimeTypeID << 4) | compileTimeTypeID; // use primitive type in implicit conversion
401 compileTimeTypeID = boxedID;
405 } else if (iterableType.isRawType()) { // for(Object o : Iterable)
406 this.kind = RAW_ITERABLE;
407 this.collectionElementType = scope.getJavaLangObject();
408 if (!collectionElementType.isCompatibleWith(elementType)
409 && !scope.isBoxingCompatibleWith(collectionElementType, elementType)) {
410 scope.problemReporter().notCompatibleTypesErrorInForeach(collection, collectionElementType, elementType);
412 // no conversion needed as only for reference types
418 // allocate #index secret variable (of type int)
419 this.indexVariable = new LocalVariableBinding(SecretIndexVariableName, IntBinding, AccDefault, false);
420 scope.addLocalVariable(this.indexVariable);
421 this.indexVariable.setConstant(NotAConstant); // not inlinable
423 // allocate #max secret variable
424 this.maxVariable = new LocalVariableBinding(SecretMaxVariableName, IntBinding, AccDefault, false);
425 scope.addLocalVariable(this.maxVariable);
426 this.maxVariable.setConstant(NotAConstant); // not inlinable
427 // add #array secret variable (of collection type)
428 this.collectionVariable = new LocalVariableBinding(SecretCollectionVariableName, collectionType, AccDefault, false);
429 scope.addLocalVariable(this.collectionVariable);
430 this.collectionVariable.setConstant(NotAConstant); // not inlinable
433 case GENERIC_ITERABLE :
434 // allocate #index secret variable (of type Iterator)
435 this.indexVariable = new LocalVariableBinding(SecretIndexVariableName, scope.getJavaUtilIterator(), AccDefault, false);
436 scope.addLocalVariable(this.indexVariable);
437 this.indexVariable.setConstant(NotAConstant); // not inlinable
440 scope.problemReporter().invalidTypeForCollection(collection);
443 if (action != null) {
444 action.resolve(scope);
448 public void traverse(
450 BlockScope blockScope) {
452 if (visitor.visit(this, blockScope)) {
453 this.elementVariable.traverse(visitor, scope);
454 this.collection.traverse(visitor, scope);
455 if (action != null) {
456 action.traverse(visitor, scope);
459 visitor.endVisit(this, blockScope);