import eclipse 3.1 M4 compiler
[org.ibex.tool.git] / src / org / eclipse / jdt / internal / compiler / ast / ForeachStatement.java
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
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.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;
28
29 public class ForeachStatement extends Statement {
30     
31         public LocalDeclaration elementVariable;
32         public int elementVariableImplicitWidening = -1; 
33         public Expression collection;
34         public Statement action;
35         
36         // set the kind of foreach
37         private int kind;
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;
42
43         private TypeBinding collectionElementType;
44
45         // loop labels
46         private Label breakLabel;
47         private Label continueLabel;
48         
49         public BlockScope scope;
50
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$
59         
60         int postCollectionInitStateIndex = -1;
61         int mergedInitStateIndex = -1;
62         
63         public ForeachStatement(
64                 LocalDeclaration elementVariable,
65                 Expression collection,
66                 int start) {
67
68                 this.elementVariable = elementVariable;
69                 this.collection = collection;
70                 this.sourceStart = start;
71                 this.kind = -1;
72         }
73
74         public FlowInfo analyseCode(
75                 BlockScope currentScope,
76                 FlowContext flowContext,
77                 FlowInfo flowInfo) {
78                 // initialize break and continue labels
79                 breakLabel = new Label();
80                 continueLabel = new Label();
81
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);
86
87                 // element variable will be assigned when iterating
88                 condInfo.markAsDefinitelyAssigned(this.elementVariable.binding);
89
90                 this.postCollectionInitStateIndex = currentScope.methodScope().recordInitializationStates(condInfo);
91                 
92                 // process the action
93                 LoopingFlowContext loopingContext = new LoopingFlowContext(flowContext, this, breakLabel, continueLabel, scope);
94                 FlowInfo actionInfo = condInfo.initsWhenTrue().copy();
95                 FlowInfo exitBranch;
96                 if (!(action == null || (action.isEmptyBlock() 
97                                 && currentScope.environment().options.complianceLevel <= ClassFileConstants.JDK1_3))) {
98
99                         if (!this.action.complainIfUnreachable(actionInfo, scope, false)) {
100                                 actionInfo = action.analyseCode(scope, loopingContext, actionInfo);
101                         }
102
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;
108                         } else {
109                                 actionInfo = actionInfo.mergedWith(loopingContext.initsOnContinue.unconditionalInits());
110                                 loopingContext.complainOnDeferredChecks(scope, actionInfo);
111                                 exitBranch.addPotentialInitializationsFrom(actionInfo.unconditionalInits());
112                         }
113                 } else {
114                         exitBranch = condInfo.initsWhenFalse();
115                 }
116
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))) {
122                         switch(this.kind) {
123                                 case ARRAY :
124                                         this.collectionVariable.useFlag = LocalVariableBinding.USED;
125                                         this.indexVariable.useFlag = LocalVariableBinding.USED;
126                                         this.maxVariable.useFlag = LocalVariableBinding.USED;
127                                         break;
128                                 case RAW_ITERABLE :
129                                 case GENERIC_ITERABLE :
130                                         this.indexVariable.useFlag = LocalVariableBinding.USED;
131                                         break;
132                         }
133                 }
134                 //end of loop
135                 FlowInfo mergedInfo = FlowInfo.mergedOptimizedBranches(
136                                 loopingContext.initsOnBreak, 
137                                 false, 
138                                 exitBranch, 
139                                 false, 
140                                 true /*for(;;){}while(true); unreachable(); */);
141                 mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo);
142                 return mergedInfo;
143         }
144
145         /**
146          * For statement code generation
147          *
148          * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
149          * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
150          */
151         public void generateCode(BlockScope currentScope, CodeStream codeStream) {
152             
153                 if ((bits & IsReachableMASK) == 0) {
154                         return;
155                 }
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);                          
164                         }
165                         codeStream.recordPositionsFrom(pc, this.sourceStart);
166                         return;
167                 }
168                 // generate the initializations
169                 switch(this.kind) {
170                         case ARRAY :
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);
178                                 break;
179                         case RAW_ITERABLE :
180                         case GENERIC_ITERABLE :
181                                 collection.generateCode(scope, codeStream, true);
182                                 // declaringClass.iterator();
183                                 final TypeBinding collectionTypeBinding = collection.resolvedType;
184                                 MethodBinding iteratorMethodBinding =
185                                         new MethodBinding(
186                                                         AccPublic,
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);
194                                 } else {
195                                         codeStream.invokevirtual(iteratorMethodBinding);
196                                 }
197                                 codeStream.store(this.indexVariable, false);
198                                 break;
199                 }
200                 
201                 // label management
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);
207                 }
208                 // jump over the actionBlock
209                 codeStream.goto_(conditionLabel);
210
211                 // generate the loop action
212                 actionLabel.place();
213
214                 // generate the loop action
215                 if (this.elementVariable.binding.resolvedPosition != -1) {
216                         switch(this.kind) {
217                                 case ARRAY :
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);
223                                         }
224                                         codeStream.store(this.elementVariable.binding, false);
225                                         break;
226                                 case RAW_ITERABLE :
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);
234                                                 } else {
235                                                         codeStream.checkcast(this.elementVariable.binding.type);
236                                                 }
237                                         }
238                                         codeStream.store(this.elementVariable.binding, false);
239                                         break;
240                         }
241                         codeStream.addVisibleLocalVariable(this.elementVariable.binding);
242                         if (this.postCollectionInitStateIndex != -1) {
243                                 codeStream.addDefinitelyAssignedVariables(
244                                         currentScope,
245                                         this.postCollectionInitStateIndex);
246                         }
247                 }
248                 this.action.generateCode(scope, codeStream);
249
250                 // continuation point
251                 int continuationPC = codeStream.position;
252                 if (this.continueLabel != null) {
253                         this.continueLabel.place();
254                         // generate the increments for next iteration
255                         switch(this.kind) {
256                                 case ARRAY :
257                                         codeStream.iinc(this.indexVariable.resolvedPosition, 1);
258                                         break;
259                                 case RAW_ITERABLE :
260                                 case GENERIC_ITERABLE :
261                                         break;
262                         }
263                 }
264                 // generate the condition
265                 conditionLabel.place();
266                 if (this.postCollectionInitStateIndex != -1) {
267                         codeStream.removeNotDefinitelyAssignedVariables(currentScope, postCollectionInitStateIndex);
268                 }
269                 switch(this.kind) {
270                         case ARRAY :
271                                 codeStream.load(this.indexVariable);
272                                 codeStream.load(this.maxVariable);
273                                 codeStream.if_icmplt(actionLabel);
274                                 break;
275                         case RAW_ITERABLE :
276                         case GENERIC_ITERABLE :
277                                 codeStream.load(this.indexVariable);
278                                 codeStream.invokeJavaUtilIteratorHasNext();
279                                 codeStream.ifne(actionLabel);
280                                 break;
281                 }
282                 codeStream.recordPositionsFrom(continuationPC, this.elementVariable.sourceStart);
283
284                 breakLabel.place();
285                 codeStream.exitUserScope(scope);
286                 if (mergedInitStateIndex != -1) {
287                         codeStream.removeNotDefinitelyAssignedVariables(currentScope, mergedInitStateIndex);
288                         codeStream.addDefinitelyAssignedVariables(currentScope, mergedInitStateIndex);                  
289                 }
290                 codeStream.recordPositionsFrom(pc, this.sourceStart);
291         }
292
293         public StringBuffer printStatement(int tab, StringBuffer output) {
294
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$
299                 //block
300                 if (this.action == null) {
301                         output.append(';');
302                 } else {
303                         output.append('\n');
304                         this.action.printStatement(tab + 1, output); //$NON-NLS-1$
305                 }
306                 return output;
307         }
308
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;
317
318                 if (!hasError) {
319                         if (collectionType.isArrayType()) { // for(E e : E[])
320                                 this.kind = ARRAY;
321                                 this.collectionElementType = ((ArrayBinding) collectionType).elementsType();
322                                 if (!collectionElementType.isCompatibleWith(elementType)
323                                                 && !scope.isBoxingCompatibleWith(collectionElementType, elementType)) {
324                                         scope.problemReporter().notCompatibleTypesErrorInForeach(collection, collectionElementType, elementType);
325                                 }
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;
334                                                 }
335                                         } else {
336                                                 this.elementVariableImplicitWidening = (elementType.id << 4) + compileTimeTypeID;
337                                         }
338                                 } else {
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;
343                                         }
344                                 }
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);
356                                                         }
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;
365                                                                         }
366                                                                 } else {
367                                                                         this.elementVariableImplicitWidening = (elementType.id << 4) + compileTimeTypeID;
368                                                                 }
369                                                         } else {
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;
374                                                                 }
375                                                         }
376                                                 }
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);
384                                                         }
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;
393                                                                         }
394                                                                 } else {
395                                                                         this.elementVariableImplicitWidening = (elementType.id << 4) + compileTimeTypeID;
396                                                                 }
397                                                         } else {
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;
402                                                                 }
403                                                         }
404                                                 }
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);
411                                                 }
412                                                 // no conversion needed as only for reference types
413                                         }                           
414                             }
415                         }
416                         switch(this.kind) {
417                                 case ARRAY :
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
422                                         
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
431                                         break;
432                                 case RAW_ITERABLE :
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
438                                         break;
439                                 default :
440                                         scope.problemReporter().invalidTypeForCollection(collection);
441                         }
442                 }
443                 if (action != null) {
444                         action.resolve(scope);
445                 }
446         }
447         
448         public void traverse(
449                 ASTVisitor visitor,
450                 BlockScope blockScope) {
451
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);
457                         }
458                 }
459                 visitor.endVisit(this, blockScope);
460         }
461 }