KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > compiler > ast > ForeachStatement


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.jdt.internal.compiler.ast;
12
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.BranchLabel;
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.flow.UnconditionalFlowInfo;
21 import org.eclipse.jdt.internal.compiler.impl.Constant;
22 import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
23 import org.eclipse.jdt.internal.compiler.lookup.Binding;
24 import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
25 import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
26 import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
27 import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
28 import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
29 import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
30
31 public class ForeachStatement extends Statement {
32     
33     public LocalDeclaration elementVariable;
34     public int elementVariableImplicitWidening = -1;
35     public Expression collection;
36     public Statement action;
37     
38     // set the kind of foreach
39
private int kind;
40     // possible kinds of iterating behavior
41
private static final int ARRAY = 0;
42     private static final int RAW_ITERABLE = 1;
43     private static final int GENERIC_ITERABLE = 2;
44
45     private TypeBinding iteratorReceiverType;
46     private TypeBinding collectionElementType;
47
48     // loop labels
49
private BranchLabel breakLabel;
50     private BranchLabel continueLabel;
51     
52     public BlockScope scope;
53
54     // secret variables for codegen
55
public LocalVariableBinding indexVariable;
56     public LocalVariableBinding collectionVariable; // to store the collection expression value
57
public LocalVariableBinding maxVariable;
58     // secret variable names
59
private static final char[] SecretIndexVariableName = " index".toCharArray(); //$NON-NLS-1$
60
private static final char[] SecretCollectionVariableName = " collection".toCharArray(); //$NON-NLS-1$
61
private static final char[] SecretMaxVariableName = " max".toCharArray(); //$NON-NLS-1$
62

63     int postCollectionInitStateIndex = -1;
64     int mergedInitStateIndex = -1;
65     
66     public ForeachStatement(
67         LocalDeclaration elementVariable,
68         int start) {
69
70         this.elementVariable = elementVariable;
71         this.sourceStart = start;
72         this.kind = -1;
73     }
74
75     public FlowInfo analyseCode(
76         BlockScope currentScope,
77         FlowContext flowContext,
78         FlowInfo flowInfo) {
79         // initialize break and continue labels
80
breakLabel = new BranchLabel();
81         continueLabel = new BranchLabel();
82
83         // process the element variable and collection
84
this.collection.checkNPE(currentScope, flowContext, flowInfo);
85         flowInfo = this.elementVariable.analyseCode(scope, flowContext, flowInfo);
86         FlowInfo condInfo = this.collection.analyseCode(scope, flowContext, flowInfo.copy());
87
88         // element variable will be assigned when iterating
89
condInfo.markAsDefinitelyAssigned(this.elementVariable.binding);
90
91         this.postCollectionInitStateIndex = currentScope.methodScope().recordInitializationStates(condInfo);
92         
93         // process the action
94
LoopingFlowContext loopingContext =
95             new LoopingFlowContext(flowContext, flowInfo, this, breakLabel,
96                 continueLabel, scope);
97         UnconditionalFlowInfo actionInfo =
98             condInfo.nullInfoLessUnconditionalCopy();
99         actionInfo.markAsDefinitelyUnknown(this.elementVariable.binding);
100         FlowInfo exitBranch;
101         if (!(action == null || (action.isEmptyBlock()
102                     && currentScope.compilerOptions().complianceLevel <= ClassFileConstants.JDK1_3))) {
103
104             if (!this.action.complainIfUnreachable(actionInfo, scope, false)) {
105                 actionInfo = action.
106                     analyseCode(scope, loopingContext, actionInfo).
107                     unconditionalCopy();
108             }
109
110             // code generation can be optimized when no need to continue in the loop
111
exitBranch = flowInfo.unconditionalCopy().
112                 addInitializationsFrom(condInfo.initsWhenFalse());
113             // TODO (maxime) no need to test when false: can optimize (same for action being unreachable above)
114
if ((actionInfo.tagBits & loopingContext.initsOnContinue.tagBits &
115                     FlowInfo.UNREACHABLE) != 0) {
116                 continueLabel = null;
117             } else {
118                 actionInfo = actionInfo.mergedWith(loopingContext.initsOnContinue);
119                 loopingContext.complainOnDeferredFinalChecks(scope, actionInfo);
120                 exitBranch.addPotentialInitializationsFrom(actionInfo);
121             }
122         } else {
123             exitBranch = condInfo.initsWhenFalse();
124         }
125
126         // we need the variable to iterate the collection even if the
127
// element variable is not used
128
final boolean hasEmptyAction = this.action == null
129                 || this.action.isEmptyBlock()
130                 || ((this.action.bits & IsUsefulEmptyStatement) != 0);
131
132         switch(this.kind) {
133             case ARRAY :
134                 if (!hasEmptyAction
135                         || this.elementVariable.binding.resolvedPosition != -1) {
136                     this.collectionVariable.useFlag = LocalVariableBinding.USED;
137                     if (this.continueLabel != null) {
138                         this.indexVariable.useFlag = LocalVariableBinding.USED;
139                         this.maxVariable.useFlag = LocalVariableBinding.USED;
140                     }
141                 }
142                 break;
143             case RAW_ITERABLE :
144             case GENERIC_ITERABLE :
145                 this.indexVariable.useFlag = LocalVariableBinding.USED;
146                 break;
147         }
148         //end of loop
149
loopingContext.complainOnDeferredNullChecks(currentScope, actionInfo);
150
151         FlowInfo mergedInfo = FlowInfo.mergedOptimizedBranches(
152                 (loopingContext.initsOnBreak.tagBits &
153                     FlowInfo.UNREACHABLE) != 0 ?
154                     loopingContext.initsOnBreak :
155                     flowInfo.addInitializationsFrom(loopingContext.initsOnBreak), // recover upstream null info
156
false,
157                 exitBranch,
158                 false,
159                 true /*for(;;){}while(true); unreachable(); */);
160         mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo);
161         return mergedInfo;
162     }
163
164     /**
165      * For statement code generation
166      *
167      * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
168      * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
169      */

170     public void generateCode(BlockScope currentScope, CodeStream codeStream) {
171         
172         if ((bits & IsReachable) == 0) {
173             return;
174         }
175         int pc = codeStream.position;
176         final boolean hasEmptyAction = this.action == null
177             || this.action.isEmptyBlock()
178             || ((this.action.bits & IsUsefulEmptyStatement) != 0);
179
180         if (hasEmptyAction
181                 && this.elementVariable.binding.resolvedPosition == -1
182                 && this.kind == ARRAY) {
183             collection.generateCode(scope, codeStream, false);
184             codeStream.exitUserScope(scope);
185             if (mergedInitStateIndex != -1) {
186                 codeStream.removeNotDefinitelyAssignedVariables(currentScope, mergedInitStateIndex);
187                 codeStream.addDefinitelyAssignedVariables(currentScope, mergedInitStateIndex);
188             }
189             codeStream.recordPositionsFrom(pc, this.sourceStart);
190             return;
191         }
192
193         // generate the initializations
194
switch(this.kind) {
195             case ARRAY :
196                 collection.generateCode(scope, codeStream, true);
197                 codeStream.store(this.collectionVariable, true);
198                 if (this.continueLabel != null) {
199                     // int length = (collectionVariable = [collection]).length;
200
codeStream.arraylength();
201                     codeStream.store(this.maxVariable, false);
202                     codeStream.iconst_0();
203                     codeStream.store(this.indexVariable, false);
204                 } else {
205                     // leave collectionVariable on execution stack (will be consumed when swapping condition further down)
206
}
207                 break;
208             case RAW_ITERABLE :
209             case GENERIC_ITERABLE :
210                 collection.generateCode(scope, codeStream, true);
211                 // declaringClass.iterator();
212
MethodBinding iteratorMethodBinding =
213                     new MethodBinding(
214                             ClassFileConstants.AccPublic,
215                             "iterator".toCharArray(),//$NON-NLS-1$
216
scope.getJavaUtilIterator(),
217                             Binding.NO_PARAMETERS,
218                             Binding.NO_EXCEPTIONS,
219                             (ReferenceBinding) this.iteratorReceiverType.erasure());
220                 if (this.iteratorReceiverType.isInterface()) {
221                     codeStream.invokeinterface(iteratorMethodBinding);
222                 } else {
223                     codeStream.invokevirtual(iteratorMethodBinding);
224                 }
225                 codeStream.store(this.indexVariable, false);
226                 break;
227         }
228         // label management
229
BranchLabel actionLabel = new BranchLabel(codeStream);
230         actionLabel.tagBits |= BranchLabel.USED;
231         BranchLabel conditionLabel = new BranchLabel(codeStream);
232         conditionLabel.tagBits |= BranchLabel.USED;
233         breakLabel.initialize(codeStream);
234         if (this.continueLabel == null) {
235             // generate the condition (swapped for optimizing)
236
conditionLabel.place();
237             int conditionPC = codeStream.position;
238             switch(this.kind) {
239                 case ARRAY :
240                     // inline the arraylength call
241
// collectionVariable is already on execution stack
242
codeStream.arraylength();
243                     codeStream.ifeq(breakLabel);
244                     break;
245                 case RAW_ITERABLE :
246                 case GENERIC_ITERABLE :
247                     codeStream.load(this.indexVariable);
248                     codeStream.invokeJavaUtilIteratorHasNext();
249                     codeStream.ifeq(breakLabel);
250                     break;
251             }
252             codeStream.recordPositionsFrom(conditionPC, this.elementVariable.sourceStart);
253         } else {
254             this.continueLabel.initialize(codeStream);
255             this.continueLabel.tagBits |= BranchLabel.USED;
256             // jump over the actionBlock
257
codeStream.goto_(conditionLabel);
258         }
259
260         // generate the loop action
261
actionLabel.place();
262
263         // generate the loop action
264
switch(this.kind) {
265             case ARRAY :
266                 if (this.elementVariable.binding.resolvedPosition != -1) {
267                     codeStream.load(this.collectionVariable);
268                     if (this.continueLabel == null) {
269                         codeStream.iconst_0(); // no continue, thus simply hardcode offset 0
270
} else {
271                         codeStream.load(this.indexVariable);
272                     }
273                     codeStream.arrayAt(this.collectionElementType.id);
274                     if (this.elementVariableImplicitWidening != -1) {
275                         codeStream.generateImplicitConversion(this.elementVariableImplicitWidening);
276                     }
277                     codeStream.store(this.elementVariable.binding, false);
278                     codeStream.addVisibleLocalVariable(this.elementVariable.binding);
279                     if (this.postCollectionInitStateIndex != -1) {
280                         codeStream.addDefinitelyAssignedVariables(
281                             currentScope,
282                             this.postCollectionInitStateIndex);
283                     }
284                 }
285                 break;
286             case RAW_ITERABLE :
287             case GENERIC_ITERABLE :
288                 codeStream.load(this.indexVariable);
289                 codeStream.invokeJavaUtilIteratorNext();
290                 if (this.elementVariable.binding.type.id != T_JavaLangObject) {
291                     if (this.elementVariableImplicitWidening != -1) {
292                         codeStream.checkcast(this.collectionElementType);
293                         codeStream.generateImplicitConversion(this.elementVariableImplicitWidening);
294                     } else {
295                         codeStream.checkcast(this.elementVariable.binding.type);
296                     }
297                 }
298                 if (this.elementVariable.binding.resolvedPosition == -1) {
299                     codeStream.pop();
300                 } else {
301                     codeStream.store(this.elementVariable.binding, false);
302                     codeStream.addVisibleLocalVariable(this.elementVariable.binding);
303                     if (this.postCollectionInitStateIndex != -1) {
304                         codeStream.addDefinitelyAssignedVariables(
305                             currentScope,
306                             this.postCollectionInitStateIndex);
307                     }
308                 }
309                 break;
310         }
311
312         if (!hasEmptyAction) {
313             this.action.generateCode(scope, codeStream);
314         }
315         codeStream.removeVariable(this.elementVariable.binding);
316         if (this.postCollectionInitStateIndex != -1) {
317             codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.postCollectionInitStateIndex);
318         }
319         // continuation point
320
if (this.continueLabel != null) {
321             this.continueLabel.place();
322             int continuationPC = codeStream.position;
323             // generate the increments for next iteration
324
switch(this.kind) {
325                 case ARRAY :
326                     if (!hasEmptyAction || this.elementVariable.binding.resolvedPosition >= 0) {
327                         codeStream.iinc(this.indexVariable.resolvedPosition, 1);
328                     }
329                     // generate the condition
330
conditionLabel.place();
331                     codeStream.load(this.indexVariable);
332                     codeStream.load(this.maxVariable);
333                     codeStream.if_icmplt(actionLabel);
334                     break;
335                 case RAW_ITERABLE :
336                 case GENERIC_ITERABLE :
337                     // generate the condition
338
conditionLabel.place();
339                     codeStream.load(this.indexVariable);
340                     codeStream.invokeJavaUtilIteratorHasNext();
341                     codeStream.ifne(actionLabel);
342                     break;
343             }
344             codeStream.recordPositionsFrom(continuationPC, this.elementVariable.sourceStart);
345         }
346         codeStream.exitUserScope(scope);
347         if (mergedInitStateIndex != -1) {
348             codeStream.removeNotDefinitelyAssignedVariables(currentScope, mergedInitStateIndex);
349             codeStream.addDefinitelyAssignedVariables(currentScope, mergedInitStateIndex);
350         }
351         breakLabel.place();
352         codeStream.recordPositionsFrom(pc, this.sourceStart);
353     }
354
355     public StringBuffer JavaDoc printStatement(int indent, StringBuffer JavaDoc output) {
356
357         printIndent(indent, output).append("for ("); //$NON-NLS-1$
358
this.elementVariable.printAsExpression(0, output);
359         output.append(" : ");//$NON-NLS-1$
360
this.collection.print(0, output).append(") "); //$NON-NLS-1$
361
//block
362
if (this.action == null) {
363             output.append(';');
364         } else {
365             output.append('\n');
366             this.action.printStatement(indent + 1, output);
367         }
368         return output;
369     }
370
371     public void resolve(BlockScope upperScope) {
372         // use the scope that will hold the init declarations
373
scope = new BlockScope(upperScope);
374         this.elementVariable.resolve(scope); // collection expression can see itemVariable
375
TypeBinding elementType = this.elementVariable.type.resolvedType;
376         TypeBinding collectionType = this.collection == null ? null : this.collection.resolveType(scope);
377
378         if (elementType != null && collectionType != null) {
379             if (collectionType.isArrayType()) { // for(E e : E[])
380
this.kind = ARRAY;
381                 this.collection.computeConversion(scope,collectionType, collectionType);
382                 this.collectionElementType = ((ArrayBinding) collectionType).elementsType();
383                 if (!collectionElementType.isCompatibleWith(elementType)
384                         && !scope.isBoxingCompatibleWith(collectionElementType, elementType)) {
385                     scope.problemReporter().notCompatibleTypesErrorInForeach(collection, collectionElementType, elementType);
386                 }
387                 // in case we need to do a conversion
388
int compileTimeTypeID = collectionElementType.id;
389                 if (elementType.isBaseType()) {
390                     if (!collectionElementType.isBaseType()) {
391                         compileTimeTypeID = scope.environment().computeBoxingType(collectionElementType).id;
392                         this.elementVariableImplicitWidening = UNBOXING;
393                         if (elementType.isBaseType()) {
394                             this.elementVariableImplicitWidening |= (elementType.id << 4) + compileTimeTypeID;
395                             scope.problemReporter().autoboxing(collection, collectionElementType, elementType);
396                         }
397                     } else {
398                         this.elementVariableImplicitWidening = (elementType.id << 4) + compileTimeTypeID;
399                     }
400                 } else {
401                     if (collectionElementType.isBaseType()) {
402                         int boxedID = scope.environment().computeBoxingType(collectionElementType).id;
403                         this.elementVariableImplicitWidening = BOXING | (compileTimeTypeID << 4) | compileTimeTypeID; // use primitive type in implicit conversion
404
compileTimeTypeID = boxedID;
405                         scope.problemReporter().autoboxing(collection, collectionElementType, elementType);
406                     }
407                 }
408             } else if (collectionType instanceof ReferenceBinding) {
409                 ReferenceBinding iterableType = ((ReferenceBinding)collectionType).findSuperTypeErasingTo(T_JavaLangIterable, false /*Iterable is not a class*/);
410                 checkIterable: {
411                     if (iterableType == null) break checkIterable;
412                     
413                     this.iteratorReceiverType = collectionType.erasure();
414                     if (((ReferenceBinding)iteratorReceiverType).findSuperTypeErasingTo(T_JavaLangIterable, false) == null) {
415                         this.iteratorReceiverType = iterableType; // handle indirect inheritance thru variable secondary bound
416
this.collection.computeConversion(scope, iterableType, collectionType);
417                     } else {
418                         this.collection.computeConversion(scope, collectionType, collectionType);
419                     }
420
421                     TypeBinding[] arguments = null;
422                     switch (iterableType.kind()) {
423                         case Binding.RAW_TYPE : // for(Object o : Iterable)
424
this.kind = RAW_ITERABLE;
425                             this.collectionElementType = scope.getJavaLangObject();
426                             if (!collectionElementType.isCompatibleWith(elementType)
427                                     && !scope.isBoxingCompatibleWith(collectionElementType, elementType)) {
428                                 scope.problemReporter().notCompatibleTypesErrorInForeach(collection, collectionElementType, elementType);
429                             }
430                             // no conversion needed as only for reference types
431
break checkIterable;
432                             
433                         case Binding.GENERIC_TYPE : // for (T t : Iterable<T>) - in case used inside Iterable itself
434
arguments = iterableType.typeVariables();
435                             break;
436                             
437                         case Binding.PARAMETERIZED_TYPE : // for(E e : Iterable<E>)
438
arguments = ((ParameterizedTypeBinding)iterableType).arguments;
439                             break;
440                             
441                         default:
442                             break checkIterable;
443                     }
444                     // generic or parameterized case
445
if (arguments.length != 1) break checkIterable; // per construction can only be one
446
this.kind = GENERIC_ITERABLE;
447                     
448                     this.collectionElementType = arguments[0];
449                     if (!collectionElementType.isCompatibleWith(elementType)
450                             && !scope.isBoxingCompatibleWith(collectionElementType, elementType)) {
451                         scope.problemReporter().notCompatibleTypesErrorInForeach(collection, collectionElementType, elementType);
452                     }
453                     int compileTimeTypeID = collectionElementType.id;
454                     // no conversion needed as only for reference types
455
if (elementType.isBaseType()) {
456                         if (!collectionElementType.isBaseType()) {
457                             compileTimeTypeID = scope.environment().computeBoxingType(collectionElementType).id;
458                             this.elementVariableImplicitWidening = UNBOXING;
459                             if (elementType.isBaseType()) {
460                                 this.elementVariableImplicitWidening |= (elementType.id << 4) + compileTimeTypeID;
461                             }
462                         } else {
463                             this.elementVariableImplicitWidening = (elementType.id << 4) + compileTimeTypeID;
464                         }
465                     } else {
466                         if (collectionElementType.isBaseType()) {
467                             int boxedID = scope.environment().computeBoxingType(collectionElementType).id;
468                             this.elementVariableImplicitWidening = BOXING | (compileTimeTypeID << 4) | compileTimeTypeID; // use primitive type in implicit conversion
469
compileTimeTypeID = boxedID;
470                         }
471                     }
472                 }
473             }
474             switch(this.kind) {
475                 case ARRAY :
476                     // allocate #index secret variable (of type int)
477
this.indexVariable = new LocalVariableBinding(SecretIndexVariableName, TypeBinding.INT, ClassFileConstants.AccDefault, false);
478                     scope.addLocalVariable(this.indexVariable);
479                     this.indexVariable.setConstant(Constant.NotAConstant); // not inlinable
480

481                     // allocate #max secret variable
482
this.maxVariable = new LocalVariableBinding(SecretMaxVariableName, TypeBinding.INT, ClassFileConstants.AccDefault, false);
483                     scope.addLocalVariable(this.maxVariable);
484                     this.maxVariable.setConstant(Constant.NotAConstant); // not inlinable
485
// add #array secret variable (of collection type)
486
this.collectionVariable = new LocalVariableBinding(SecretCollectionVariableName, collectionType, ClassFileConstants.AccDefault, false);
487                     scope.addLocalVariable(this.collectionVariable);
488                     this.collectionVariable.setConstant(Constant.NotAConstant); // not inlinable
489
break;
490                 case RAW_ITERABLE :
491                 case GENERIC_ITERABLE :
492                     // allocate #index secret variable (of type Iterator)
493
this.indexVariable = new LocalVariableBinding(SecretIndexVariableName, scope.getJavaUtilIterator(), ClassFileConstants.AccDefault, false);
494                     scope.addLocalVariable(this.indexVariable);
495                     this.indexVariable.setConstant(Constant.NotAConstant); // not inlinable
496
break;
497                 default :
498                     scope.problemReporter().invalidTypeForCollection(collection);
499             }
500         }
501         if (action != null) {
502             action.resolve(scope);
503         }
504     }
505     
506     public void traverse(
507         ASTVisitor visitor,
508         BlockScope blockScope) {
509
510         if (visitor.visit(this, blockScope)) {
511             this.elementVariable.traverse(visitor, scope);
512             this.collection.traverse(visitor, scope);
513             if (action != null) {
514                 action.traverse(visitor, scope);
515             }
516         }
517         visitor.endVisit(this, blockScope);
518     }
519 }
520
Popular Tags