001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.compiler.ast;
011:
012: import org.eclipse.jdt.internal.compiler.ASTVisitor;
013: import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
014: import org.eclipse.jdt.internal.compiler.codegen.CodeStream;
015: import org.eclipse.jdt.internal.compiler.codegen.BranchLabel;
016: import org.eclipse.jdt.internal.compiler.flow.FlowContext;
017: import org.eclipse.jdt.internal.compiler.flow.FlowInfo;
018: import org.eclipse.jdt.internal.compiler.flow.LoopingFlowContext;
019: import org.eclipse.jdt.internal.compiler.flow.UnconditionalFlowInfo;
020: import org.eclipse.jdt.internal.compiler.impl.Constant;
021: import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
022: import org.eclipse.jdt.internal.compiler.lookup.Binding;
023: import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
024: import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
025: import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
026: import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
027: import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
028: import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
029:
030: public class ForeachStatement extends Statement {
031:
032: public LocalDeclaration elementVariable;
033: public int elementVariableImplicitWidening = -1;
034: public Expression collection;
035: public Statement action;
036:
037: // set the kind of foreach
038: private int kind;
039: // possible kinds of iterating behavior
040: private static final int ARRAY = 0;
041: private static final int RAW_ITERABLE = 1;
042: private static final int GENERIC_ITERABLE = 2;
043:
044: private TypeBinding iteratorReceiverType;
045: private TypeBinding collectionElementType;
046:
047: // loop labels
048: private BranchLabel breakLabel;
049: private BranchLabel continueLabel;
050:
051: public BlockScope scope;
052:
053: // secret variables for codegen
054: public LocalVariableBinding indexVariable;
055: public LocalVariableBinding collectionVariable; // to store the collection expression value
056: public LocalVariableBinding maxVariable;
057: // secret variable names
058: private static final char[] SecretIndexVariableName = " index".toCharArray(); //$NON-NLS-1$
059: private static final char[] SecretCollectionVariableName = " collection".toCharArray(); //$NON-NLS-1$
060: private static final char[] SecretMaxVariableName = " max".toCharArray(); //$NON-NLS-1$
061:
062: int postCollectionInitStateIndex = -1;
063: int mergedInitStateIndex = -1;
064:
065: public ForeachStatement(LocalDeclaration elementVariable, int start) {
066:
067: this .elementVariable = elementVariable;
068: this .sourceStart = start;
069: this .kind = -1;
070: }
071:
072: public FlowInfo analyseCode(BlockScope currentScope,
073: FlowContext flowContext, FlowInfo flowInfo) {
074: // initialize break and continue labels
075: breakLabel = new BranchLabel();
076: continueLabel = new BranchLabel();
077:
078: // process the element variable and collection
079: this .collection.checkNPE(currentScope, flowContext, flowInfo);
080: flowInfo = this .elementVariable.analyseCode(scope, flowContext,
081: flowInfo);
082: FlowInfo condInfo = this .collection.analyseCode(scope,
083: flowContext, flowInfo.copy());
084:
085: // element variable will be assigned when iterating
086: condInfo.markAsDefinitelyAssigned(this .elementVariable.binding);
087:
088: this .postCollectionInitStateIndex = currentScope.methodScope()
089: .recordInitializationStates(condInfo);
090:
091: // process the action
092: LoopingFlowContext loopingContext = new LoopingFlowContext(
093: flowContext, flowInfo, this , breakLabel, continueLabel,
094: scope);
095: UnconditionalFlowInfo actionInfo = condInfo
096: .nullInfoLessUnconditionalCopy();
097: actionInfo
098: .markAsDefinitelyUnknown(this .elementVariable.binding);
099: FlowInfo exitBranch;
100: if (!(action == null || (action.isEmptyBlock() && currentScope
101: .compilerOptions().complianceLevel <= ClassFileConstants.JDK1_3))) {
102:
103: if (!this .action.complainIfUnreachable(actionInfo, scope,
104: false)) {
105: actionInfo = action.analyseCode(scope, loopingContext,
106: actionInfo).unconditionalCopy();
107: }
108:
109: // code generation can be optimized when no need to continue in the loop
110: exitBranch = flowInfo.unconditionalCopy()
111: .addInitializationsFrom(condInfo.initsWhenFalse());
112: // TODO (maxime) no need to test when false: can optimize (same for action being unreachable above)
113: if ((actionInfo.tagBits
114: & loopingContext.initsOnContinue.tagBits & FlowInfo.UNREACHABLE) != 0) {
115: continueLabel = null;
116: } else {
117: actionInfo = actionInfo
118: .mergedWith(loopingContext.initsOnContinue);
119: loopingContext.complainOnDeferredFinalChecks(scope,
120: actionInfo);
121: exitBranch.addPotentialInitializationsFrom(actionInfo);
122: }
123: } else {
124: exitBranch = condInfo.initsWhenFalse();
125: }
126:
127: // we need the variable to iterate the collection even if the
128: // element variable is not used
129: final boolean hasEmptyAction = this .action == null
130: || this .action.isEmptyBlock()
131: || ((this .action.bits & IsUsefulEmptyStatement) != 0);
132:
133: switch (this .kind) {
134: case ARRAY:
135: if (!hasEmptyAction
136: || this .elementVariable.binding.resolvedPosition != -1) {
137: this .collectionVariable.useFlag = LocalVariableBinding.USED;
138: if (this .continueLabel != null) {
139: this .indexVariable.useFlag = LocalVariableBinding.USED;
140: this .maxVariable.useFlag = LocalVariableBinding.USED;
141: }
142: }
143: break;
144: case RAW_ITERABLE:
145: case GENERIC_ITERABLE:
146: this .indexVariable.useFlag = LocalVariableBinding.USED;
147: break;
148: }
149: //end of loop
150: loopingContext.complainOnDeferredNullChecks(currentScope,
151: actionInfo);
152:
153: FlowInfo mergedInfo = FlowInfo
154: .mergedOptimizedBranches(
155: (loopingContext.initsOnBreak.tagBits & FlowInfo.UNREACHABLE) != 0 ? loopingContext.initsOnBreak
156: : flowInfo
157: .addInitializationsFrom(loopingContext.initsOnBreak), // recover upstream null info
158: false, exitBranch, false, true /*for(;;){}while(true); unreachable(); */);
159: mergedInitStateIndex = currentScope.methodScope()
160: .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,
171: CodeStream codeStream) {
172:
173: if ((bits & IsReachable) == 0) {
174: return;
175: }
176: int pc = codeStream.position;
177: final boolean hasEmptyAction = this .action == null
178: || this .action.isEmptyBlock()
179: || ((this .action.bits & IsUsefulEmptyStatement) != 0);
180:
181: if (hasEmptyAction
182: && this .elementVariable.binding.resolvedPosition == -1
183: && this .kind == ARRAY) {
184: collection.generateCode(scope, codeStream, false);
185: codeStream.exitUserScope(scope);
186: if (mergedInitStateIndex != -1) {
187: codeStream.removeNotDefinitelyAssignedVariables(
188: currentScope, mergedInitStateIndex);
189: codeStream.addDefinitelyAssignedVariables(currentScope,
190: mergedInitStateIndex);
191: }
192: codeStream.recordPositionsFrom(pc, this .sourceStart);
193: return;
194: }
195:
196: // generate the initializations
197: switch (this .kind) {
198: case ARRAY:
199: collection.generateCode(scope, codeStream, true);
200: codeStream.store(this .collectionVariable, true);
201: codeStream.addVariable(this .collectionVariable);
202: if (this .continueLabel != null) {
203: // int length = (collectionVariable = [collection]).length;
204: codeStream.arraylength();
205: codeStream.store(this .maxVariable, false);
206: codeStream.addVariable(this .maxVariable);
207: codeStream.iconst_0();
208: codeStream.store(this .indexVariable, false);
209: codeStream.addVariable(this .indexVariable);
210: } else {
211: // leave collectionVariable on execution stack (will be consumed when swapping condition further down)
212: }
213: break;
214: case RAW_ITERABLE:
215: case GENERIC_ITERABLE:
216: collection.generateCode(scope, codeStream, true);
217: // declaringClass.iterator();
218: MethodBinding iteratorMethodBinding = new MethodBinding(
219: ClassFileConstants.AccPublic,
220: "iterator".toCharArray(),//$NON-NLS-1$
221: scope.getJavaUtilIterator(), Binding.NO_PARAMETERS,
222: Binding.NO_EXCEPTIONS,
223: (ReferenceBinding) this .iteratorReceiverType
224: .erasure());
225: if (this .iteratorReceiverType.isInterface()) {
226: codeStream.invokeinterface(iteratorMethodBinding);
227: } else {
228: codeStream.invokevirtual(iteratorMethodBinding);
229: }
230: codeStream.store(this .indexVariable, false);
231: codeStream.addVariable(this .indexVariable);
232: break;
233: }
234: // label management
235: BranchLabel actionLabel = new BranchLabel(codeStream);
236: actionLabel.tagBits |= BranchLabel.USED;
237: BranchLabel conditionLabel = new BranchLabel(codeStream);
238: conditionLabel.tagBits |= BranchLabel.USED;
239: breakLabel.initialize(codeStream);
240: if (this .continueLabel == null) {
241: // generate the condition (swapped for optimizing)
242: conditionLabel.place();
243: int conditionPC = codeStream.position;
244: switch (this .kind) {
245: case ARRAY:
246: // inline the arraylength call
247: // collectionVariable is already on execution stack
248: codeStream.arraylength();
249: codeStream.ifeq(breakLabel);
250: break;
251: case RAW_ITERABLE:
252: case GENERIC_ITERABLE:
253: codeStream.load(this .indexVariable);
254: codeStream.invokeJavaUtilIteratorHasNext();
255: codeStream.ifeq(breakLabel);
256: break;
257: }
258: codeStream.recordPositionsFrom(conditionPC,
259: this .elementVariable.sourceStart);
260: } else {
261: this .continueLabel.initialize(codeStream);
262: this .continueLabel.tagBits |= BranchLabel.USED;
263: // jump over the actionBlock
264: codeStream.goto_(conditionLabel);
265: }
266:
267: // generate the loop action
268: actionLabel.place();
269:
270: // generate the loop action
271: switch (this .kind) {
272: case ARRAY:
273: if (this .elementVariable.binding.resolvedPosition != -1) {
274: codeStream.load(this .collectionVariable);
275: if (this .continueLabel == null) {
276: codeStream.iconst_0(); // no continue, thus simply hardcode offset 0
277: } else {
278: codeStream.load(this .indexVariable);
279: }
280: codeStream.arrayAt(this .collectionElementType.id);
281: if (this .elementVariableImplicitWidening != -1) {
282: codeStream
283: .generateImplicitConversion(this .elementVariableImplicitWidening);
284: }
285: codeStream.store(this .elementVariable.binding, false);
286: codeStream
287: .addVisibleLocalVariable(this .elementVariable.binding);
288: if (this .postCollectionInitStateIndex != -1) {
289: codeStream.addDefinitelyAssignedVariables(
290: currentScope,
291: this .postCollectionInitStateIndex);
292: }
293: }
294: break;
295: case RAW_ITERABLE:
296: case GENERIC_ITERABLE:
297: codeStream.load(this .indexVariable);
298: codeStream.invokeJavaUtilIteratorNext();
299: if (this .elementVariable.binding.type.id != T_JavaLangObject) {
300: if (this .elementVariableImplicitWidening != -1) {
301: codeStream.checkcast(this .collectionElementType);
302: codeStream
303: .generateImplicitConversion(this .elementVariableImplicitWidening);
304: } else {
305: codeStream
306: .checkcast(this .elementVariable.binding.type);
307: }
308: }
309: if (this .elementVariable.binding.resolvedPosition == -1) {
310: codeStream.pop();
311: } else {
312: codeStream.store(this .elementVariable.binding, false);
313: codeStream
314: .addVisibleLocalVariable(this .elementVariable.binding);
315: if (this .postCollectionInitStateIndex != -1) {
316: codeStream.addDefinitelyAssignedVariables(
317: currentScope,
318: this .postCollectionInitStateIndex);
319: }
320: }
321: break;
322: }
323:
324: if (!hasEmptyAction) {
325: this .action.generateCode(scope, codeStream);
326: }
327: codeStream.removeVariable(this .elementVariable.binding);
328: if (this .postCollectionInitStateIndex != -1) {
329: codeStream.removeNotDefinitelyAssignedVariables(
330: currentScope, this .postCollectionInitStateIndex);
331: }
332: // continuation point
333: if (this .continueLabel != null) {
334: this .continueLabel.place();
335: int continuationPC = codeStream.position;
336: // generate the increments for next iteration
337: switch (this .kind) {
338: case ARRAY:
339: if (!hasEmptyAction
340: || this .elementVariable.binding.resolvedPosition >= 0) {
341: codeStream.iinc(
342: this .indexVariable.resolvedPosition, 1);
343: }
344: // generate the condition
345: conditionLabel.place();
346: codeStream.load(this .indexVariable);
347: codeStream.load(this .maxVariable);
348: codeStream.if_icmplt(actionLabel);
349: break;
350: case RAW_ITERABLE:
351: case GENERIC_ITERABLE:
352: // generate the condition
353: conditionLabel.place();
354: codeStream.load(this .indexVariable);
355: codeStream.invokeJavaUtilIteratorHasNext();
356: codeStream.ifne(actionLabel);
357: break;
358: }
359: codeStream.recordPositionsFrom(continuationPC,
360: this .elementVariable.sourceStart);
361: }
362: switch (this .kind) {
363: case ARRAY:
364: codeStream.removeVariable(this .indexVariable);
365: codeStream.removeVariable(this .maxVariable);
366: codeStream.removeVariable(this .collectionVariable);
367: break;
368: case RAW_ITERABLE:
369: case GENERIC_ITERABLE:
370: // generate the condition
371: codeStream.removeVariable(this .indexVariable);
372: break;
373: }
374: codeStream.exitUserScope(scope);
375: if (mergedInitStateIndex != -1) {
376: codeStream.removeNotDefinitelyAssignedVariables(
377: currentScope, mergedInitStateIndex);
378: codeStream.addDefinitelyAssignedVariables(currentScope,
379: mergedInitStateIndex);
380: }
381: breakLabel.place();
382: codeStream.recordPositionsFrom(pc, this .sourceStart);
383: }
384:
385: public StringBuffer printStatement(int indent, StringBuffer output) {
386:
387: printIndent(indent, output).append("for ("); //$NON-NLS-1$
388: this .elementVariable.printAsExpression(0, output);
389: output.append(" : ");//$NON-NLS-1$
390: this .collection.print(0, output).append(") "); //$NON-NLS-1$
391: //block
392: if (this .action == null) {
393: output.append(';');
394: } else {
395: output.append('\n');
396: this .action.printStatement(indent + 1, output);
397: }
398: return output;
399: }
400:
401: public void resolve(BlockScope upperScope) {
402: // use the scope that will hold the init declarations
403: scope = new BlockScope(upperScope);
404: this .elementVariable.resolve(scope); // collection expression can see itemVariable
405: TypeBinding elementType = this .elementVariable.type.resolvedType;
406: TypeBinding collectionType = this .collection == null ? null
407: : this .collection.resolveType(scope);
408:
409: if (elementType != null && collectionType != null) {
410: if (collectionType.isArrayType()) { // for(E e : E[])
411: this .kind = ARRAY;
412: this .collection.computeConversion(scope,
413: collectionType, collectionType);
414: this .collectionElementType = ((ArrayBinding) collectionType)
415: .elementsType();
416: if (!collectionElementType
417: .isCompatibleWith(elementType)
418: && !scope.isBoxingCompatibleWith(
419: collectionElementType, elementType)) {
420: scope.problemReporter()
421: .notCompatibleTypesErrorInForeach(
422: collection, collectionElementType,
423: elementType);
424: }
425: // in case we need to do a conversion
426: int compileTimeTypeID = collectionElementType.id;
427: if (elementType.isBaseType()) {
428: if (!collectionElementType.isBaseType()) {
429: compileTimeTypeID = scope.environment()
430: .computeBoxingType(
431: collectionElementType).id;
432: this .elementVariableImplicitWidening = UNBOXING;
433: if (elementType.isBaseType()) {
434: this .elementVariableImplicitWidening |= (elementType.id << 4)
435: + compileTimeTypeID;
436: scope.problemReporter().autoboxing(
437: collection, collectionElementType,
438: elementType);
439: }
440: } else {
441: this .elementVariableImplicitWidening = (elementType.id << 4)
442: + compileTimeTypeID;
443: }
444: } else {
445: if (collectionElementType.isBaseType()) {
446: int boxedID = scope.environment()
447: .computeBoxingType(
448: collectionElementType).id;
449: this .elementVariableImplicitWidening = BOXING
450: | (compileTimeTypeID << 4)
451: | compileTimeTypeID; // use primitive type in implicit conversion
452: compileTimeTypeID = boxedID;
453: scope.problemReporter().autoboxing(collection,
454: collectionElementType, elementType);
455: }
456: }
457: } else if (collectionType instanceof ReferenceBinding) {
458: ReferenceBinding iterableType = ((ReferenceBinding) collectionType)
459: .findSuperTypeErasingTo(T_JavaLangIterable,
460: false /*Iterable is not a class*/);
461: checkIterable: {
462: if (iterableType == null)
463: break checkIterable;
464:
465: this .iteratorReceiverType = collectionType
466: .erasure();
467: if (((ReferenceBinding) iteratorReceiverType)
468: .findSuperTypeErasingTo(T_JavaLangIterable,
469: false) == null) {
470: this .iteratorReceiverType = iterableType; // handle indirect inheritance thru variable secondary bound
471: this .collection.computeConversion(scope,
472: iterableType, collectionType);
473: } else {
474: this .collection.computeConversion(scope,
475: collectionType, collectionType);
476: }
477:
478: TypeBinding[] arguments = null;
479: switch (iterableType.kind()) {
480: case Binding.RAW_TYPE: // for(Object o : Iterable)
481: this .kind = RAW_ITERABLE;
482: this .collectionElementType = scope
483: .getJavaLangObject();
484: if (!collectionElementType
485: .isCompatibleWith(elementType)
486: && !scope.isBoxingCompatibleWith(
487: collectionElementType,
488: elementType)) {
489: scope.problemReporter()
490: .notCompatibleTypesErrorInForeach(
491: collection,
492: collectionElementType,
493: elementType);
494: }
495: // no conversion needed as only for reference types
496: break checkIterable;
497:
498: case Binding.GENERIC_TYPE: // for (T t : Iterable<T>) - in case used inside Iterable itself
499: arguments = iterableType.typeVariables();
500: break;
501:
502: case Binding.PARAMETERIZED_TYPE: // for(E e : Iterable<E>)
503: arguments = ((ParameterizedTypeBinding) iterableType).arguments;
504: break;
505:
506: default:
507: break checkIterable;
508: }
509: // generic or parameterized case
510: if (arguments.length != 1)
511: break checkIterable; // per construction can only be one
512: this .kind = GENERIC_ITERABLE;
513:
514: this .collectionElementType = arguments[0];
515: if (!collectionElementType
516: .isCompatibleWith(elementType)
517: && !scope.isBoxingCompatibleWith(
518: collectionElementType, elementType)) {
519: scope.problemReporter()
520: .notCompatibleTypesErrorInForeach(
521: collection,
522: collectionElementType,
523: elementType);
524: }
525: int compileTimeTypeID = collectionElementType.id;
526: // no conversion needed as only for reference types
527: if (elementType.isBaseType()) {
528: if (!collectionElementType.isBaseType()) {
529: compileTimeTypeID = scope.environment()
530: .computeBoxingType(
531: collectionElementType).id;
532: this .elementVariableImplicitWidening = UNBOXING;
533: if (elementType.isBaseType()) {
534: this .elementVariableImplicitWidening |= (elementType.id << 4)
535: + compileTimeTypeID;
536: }
537: } else {
538: this .elementVariableImplicitWidening = (elementType.id << 4)
539: + compileTimeTypeID;
540: }
541: } else {
542: if (collectionElementType.isBaseType()) {
543: int boxedID = scope.environment()
544: .computeBoxingType(
545: collectionElementType).id;
546: this .elementVariableImplicitWidening = BOXING
547: | (compileTimeTypeID << 4)
548: | compileTimeTypeID; // use primitive type in implicit conversion
549: compileTimeTypeID = boxedID;
550: }
551: }
552: }
553: }
554: switch (this .kind) {
555: case ARRAY:
556: // allocate #index secret variable (of type int)
557: this .indexVariable = new LocalVariableBinding(
558: SecretIndexVariableName, TypeBinding.INT,
559: ClassFileConstants.AccDefault, false);
560: scope.addLocalVariable(this .indexVariable);
561: this .indexVariable.setConstant(Constant.NotAConstant); // not inlinable
562:
563: // allocate #max secret variable
564: this .maxVariable = new LocalVariableBinding(
565: SecretMaxVariableName, TypeBinding.INT,
566: ClassFileConstants.AccDefault, false);
567: scope.addLocalVariable(this .maxVariable);
568: this .maxVariable.setConstant(Constant.NotAConstant); // not inlinable
569: // add #array secret variable (of collection type)
570: this .collectionVariable = new LocalVariableBinding(
571: SecretCollectionVariableName, collectionType,
572: ClassFileConstants.AccDefault, false);
573: scope.addLocalVariable(this .collectionVariable);
574: this .collectionVariable
575: .setConstant(Constant.NotAConstant); // not inlinable
576: break;
577: case RAW_ITERABLE:
578: case GENERIC_ITERABLE:
579: // allocate #index secret variable (of type Iterator)
580: this .indexVariable = new LocalVariableBinding(
581: SecretIndexVariableName, scope
582: .getJavaUtilIterator(),
583: ClassFileConstants.AccDefault, false);
584: scope.addLocalVariable(this .indexVariable);
585: this .indexVariable.setConstant(Constant.NotAConstant); // not inlinable
586: break;
587: default:
588: scope.problemReporter().invalidTypeForCollection(
589: collection);
590: }
591: }
592: if (action != null) {
593: action.resolve(scope);
594: }
595: }
596:
597: public void traverse(ASTVisitor visitor, BlockScope blockScope) {
598:
599: if (visitor.visit(this, blockScope)) {
600: this.elementVariable.traverse(visitor, scope);
601: this.collection.traverse(visitor, scope);
602: if (action != null) {
603: action.traverse(visitor, scope);
604: }
605: }
606: visitor.endVisit(this, blockScope);
607: }
608: }
|