import eclipse 3.1 M4 compiler
[org.ibex.tool.git] / src / org / eclipse / jdt / internal / compiler / ast / ForeachStatement.java
diff --git a/src/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java b/src/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java
new file mode 100644 (file)
index 0000000..1c1dbb1
--- /dev/null
@@ -0,0 +1,461 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.compiler.ast;
+
+import org.eclipse.jdt.internal.compiler.ASTVisitor;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.codegen.CodeStream;
+import org.eclipse.jdt.internal.compiler.codegen.Label;
+import org.eclipse.jdt.internal.compiler.flow.FlowContext;
+import org.eclipse.jdt.internal.compiler.flow.FlowInfo;
+import org.eclipse.jdt.internal.compiler.flow.LoopingFlowContext;
+import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
+import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
+import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
+import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
+
+public class ForeachStatement extends Statement {
+    
+       public LocalDeclaration elementVariable;
+       public int elementVariableImplicitWidening = -1; 
+       public Expression collection;
+       public Statement action;
+       
+       // set the kind of foreach
+       private int kind;
+       // possible kinds of iterating behavior
+       private static final int ARRAY = 0;
+       private static final int RAW_ITERABLE = 1;
+       private static final int GENERIC_ITERABLE = 2;
+
+       private TypeBinding collectionElementType;
+
+       // loop labels
+       private Label breakLabel;
+       private Label continueLabel;
+       
+       public BlockScope scope;
+
+       // secret variables for codegen
+       public LocalVariableBinding indexVariable;
+       public LocalVariableBinding collectionVariable; // to store the collection expression value
+       public LocalVariableBinding maxVariable;
+       // secret variable names
+       private static final char[] SecretIndexVariableName = " index".toCharArray(); //$NON-NLS-1$
+       private static final char[] SecretCollectionVariableName = " collection".toCharArray(); //$NON-NLS-1$
+       private static final char[] SecretMaxVariableName = " max".toCharArray(); //$NON-NLS-1$
+       
+       int postCollectionInitStateIndex = -1;
+       int mergedInitStateIndex = -1;
+       
+       public ForeachStatement(
+               LocalDeclaration elementVariable,
+               Expression collection,
+               int start) {
+
+               this.elementVariable = elementVariable;
+               this.collection = collection;
+               this.sourceStart = start;
+               this.kind = -1;
+       }
+
+       public FlowInfo analyseCode(
+               BlockScope currentScope,
+               FlowContext flowContext,
+               FlowInfo flowInfo) {
+               // initialize break and continue labels
+               breakLabel = new Label();
+               continueLabel = new Label();
+
+               // process the element variable and collection
+               flowInfo = this.elementVariable.analyseCode(scope, flowContext, flowInfo);
+               FlowInfo condInfo = flowInfo.copy().unconditionalInits().discardNullRelatedInitializations();
+               condInfo = this.collection.analyseCode(scope, flowContext, condInfo);
+
+               // element variable will be assigned when iterating
+               condInfo.markAsDefinitelyAssigned(this.elementVariable.binding);
+
+               this.postCollectionInitStateIndex = currentScope.methodScope().recordInitializationStates(condInfo);
+               
+               // process the action
+               LoopingFlowContext loopingContext = new LoopingFlowContext(flowContext, this, breakLabel, continueLabel, scope);
+               FlowInfo actionInfo = condInfo.initsWhenTrue().copy();
+               FlowInfo exitBranch;
+               if (!(action == null || (action.isEmptyBlock() 
+                               && currentScope.environment().options.complianceLevel <= ClassFileConstants.JDK1_3))) {
+
+                       if (!this.action.complainIfUnreachable(actionInfo, scope, false)) {
+                               actionInfo = action.analyseCode(scope, loopingContext, actionInfo);
+                       }
+
+                       // code generation can be optimized when no need to continue in the loop
+                       exitBranch = condInfo.initsWhenFalse();
+                       exitBranch.addInitializationsFrom(flowInfo); // recover null inits from before condition analysis                       
+                       if (!actionInfo.isReachable() && !loopingContext.initsOnContinue.isReachable()) {
+                               continueLabel = null;
+                       } else {
+                               actionInfo = actionInfo.mergedWith(loopingContext.initsOnContinue.unconditionalInits());
+                               loopingContext.complainOnDeferredChecks(scope, actionInfo);
+                               exitBranch.addPotentialInitializationsFrom(actionInfo.unconditionalInits());
+                       }
+               } else {
+                       exitBranch = condInfo.initsWhenFalse();
+               }
+
+               // we need the variable to iterate the collection even if the 
+               // element variable is not used
+               if (!(this.action == null
+                               || this.action.isEmptyBlock()
+                               || ((this.action.bits & IsUsefulEmptyStatementMASK) != 0))) {
+                       switch(this.kind) {
+                               case ARRAY :
+                                       this.collectionVariable.useFlag = LocalVariableBinding.USED;
+                                       this.indexVariable.useFlag = LocalVariableBinding.USED;
+                                       this.maxVariable.useFlag = LocalVariableBinding.USED;
+                                       break;
+                               case RAW_ITERABLE :
+                               case GENERIC_ITERABLE :
+                                       this.indexVariable.useFlag = LocalVariableBinding.USED;
+                                       break;
+                       }
+               }
+               //end of loop
+               FlowInfo mergedInfo = FlowInfo.mergedOptimizedBranches(
+                               loopingContext.initsOnBreak, 
+                               false, 
+                               exitBranch, 
+                               false, 
+                               true /*for(;;){}while(true); unreachable(); */);
+               mergedInitStateIndex = currentScope.methodScope().recordInitializationStates(mergedInfo);
+               return mergedInfo;
+       }
+
+       /**
+        * For statement code generation
+        *
+        * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
+        * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
+        */
+       public void generateCode(BlockScope currentScope, CodeStream codeStream) {
+           
+               if ((bits & IsReachableMASK) == 0) {
+                       return;
+               }
+               int pc = codeStream.position;
+               if (this.action == null
+                               || this.action.isEmptyBlock()
+                               || ((this.action.bits & IsUsefulEmptyStatementMASK) != 0)) {
+                       codeStream.exitUserScope(scope);
+                       if (mergedInitStateIndex != -1) {
+                               codeStream.removeNotDefinitelyAssignedVariables(currentScope, mergedInitStateIndex);
+                               codeStream.addDefinitelyAssignedVariables(currentScope, mergedInitStateIndex);                          
+                       }
+                       codeStream.recordPositionsFrom(pc, this.sourceStart);
+                       return;
+               }
+               // generate the initializations
+               switch(this.kind) {
+                       case ARRAY :
+                               collection.generateCode(scope, codeStream, true);
+                               codeStream.store(this.collectionVariable, false);
+                               codeStream.iconst_0();
+                               codeStream.store(this.indexVariable, false);
+                               codeStream.load(this.collectionVariable);
+                               codeStream.arraylength();
+                               codeStream.store(this.maxVariable, false);
+                               break;
+                       case RAW_ITERABLE :
+                       case GENERIC_ITERABLE :
+                               collection.generateCode(scope, codeStream, true);
+                               // declaringClass.iterator();
+                               final TypeBinding collectionTypeBinding = collection.resolvedType;
+                               MethodBinding iteratorMethodBinding =
+                                       new MethodBinding(
+                                                       AccPublic,
+                                                       "iterator".toCharArray(),//$NON-NLS-1$
+                                                       scope.getJavaUtilIterator(),
+                                                       TypeConstants.NoParameters,
+                                                       TypeConstants.NoExceptions,
+                                                       (ReferenceBinding) collectionTypeBinding);
+                               if (collectionTypeBinding.isInterface()) {
+                                       codeStream.invokeinterface(iteratorMethodBinding);
+                               } else {
+                                       codeStream.invokevirtual(iteratorMethodBinding);
+                               }
+                               codeStream.store(this.indexVariable, false);
+                               break;
+               }
+               
+               // label management
+               Label actionLabel = new Label(codeStream);
+               Label conditionLabel = new Label(codeStream);
+               breakLabel.initialize(codeStream);
+               if (this.continueLabel != null) {
+                       this.continueLabel.initialize(codeStream);
+               }
+               // jump over the actionBlock
+               codeStream.goto_(conditionLabel);
+
+               // generate the loop action
+               actionLabel.place();
+
+               // generate the loop action
+               if (this.elementVariable.binding.resolvedPosition != -1) {
+                       switch(this.kind) {
+                               case ARRAY :
+                                       codeStream.load(this.collectionVariable);
+                                       codeStream.load(this.indexVariable);
+                                       codeStream.arrayAt(this.collectionElementType.id);
+                                       if (this.elementVariableImplicitWidening != -1) {
+                                               codeStream.generateImplicitConversion(this.elementVariableImplicitWidening);
+                                       }
+                                       codeStream.store(this.elementVariable.binding, false);
+                                       break;
+                               case RAW_ITERABLE :
+                               case GENERIC_ITERABLE :
+                                       codeStream.load(this.indexVariable);
+                                       codeStream.invokeJavaUtilIteratorNext();
+                                       if (this.elementVariable.binding.type.id != T_JavaLangObject) {
+                                               if (this.elementVariableImplicitWidening != -1) {
+                                                       codeStream.checkcast(this.collectionElementType);
+                                                       codeStream.generateImplicitConversion(this.elementVariableImplicitWidening);
+                                               } else {
+                                                       codeStream.checkcast(this.elementVariable.binding.type);
+                                               }
+                                       }
+                                       codeStream.store(this.elementVariable.binding, false);
+                                       break;
+                       }
+                       codeStream.addVisibleLocalVariable(this.elementVariable.binding);
+                       if (this.postCollectionInitStateIndex != -1) {
+                               codeStream.addDefinitelyAssignedVariables(
+                                       currentScope,
+                                       this.postCollectionInitStateIndex);
+                       }
+               }
+               this.action.generateCode(scope, codeStream);
+
+               // continuation point
+               int continuationPC = codeStream.position;
+               if (this.continueLabel != null) {
+                       this.continueLabel.place();
+                       // generate the increments for next iteration
+                       switch(this.kind) {
+                               case ARRAY :
+                                       codeStream.iinc(this.indexVariable.resolvedPosition, 1);
+                                       break;
+                               case RAW_ITERABLE :
+                               case GENERIC_ITERABLE :
+                                       break;
+                       }
+               }
+               // generate the condition
+               conditionLabel.place();
+               if (this.postCollectionInitStateIndex != -1) {
+                       codeStream.removeNotDefinitelyAssignedVariables(currentScope, postCollectionInitStateIndex);
+               }
+               switch(this.kind) {
+                       case ARRAY :
+                               codeStream.load(this.indexVariable);
+                               codeStream.load(this.maxVariable);
+                               codeStream.if_icmplt(actionLabel);
+                               break;
+                       case RAW_ITERABLE :
+                       case GENERIC_ITERABLE :
+                               codeStream.load(this.indexVariable);
+                               codeStream.invokeJavaUtilIteratorHasNext();
+                               codeStream.ifne(actionLabel);
+                               break;
+               }
+               codeStream.recordPositionsFrom(continuationPC, this.elementVariable.sourceStart);
+
+               breakLabel.place();
+               codeStream.exitUserScope(scope);
+               if (mergedInitStateIndex != -1) {
+                       codeStream.removeNotDefinitelyAssignedVariables(currentScope, mergedInitStateIndex);
+                       codeStream.addDefinitelyAssignedVariables(currentScope, mergedInitStateIndex);                  
+               }
+               codeStream.recordPositionsFrom(pc, this.sourceStart);
+       }
+
+       public StringBuffer printStatement(int tab, StringBuffer output) {
+
+               printIndent(tab, output).append("for ("); //$NON-NLS-1$
+               this.elementVariable.print(0, output); 
+               output.append(" : ");//$NON-NLS-1$
+               this.collection.print(0, output).append(") "); //$NON-NLS-1$
+               //block
+               if (this.action == null) {
+                       output.append(';');
+               } else {
+                       output.append('\n');
+                       this.action.printStatement(tab + 1, output); //$NON-NLS-1$
+               }
+               return output;
+       }
+
+       public void resolve(BlockScope upperScope) {
+               // use the scope that will hold the init declarations
+               scope = new BlockScope(upperScope);
+               this.elementVariable.resolve(scope); // collection expression can see itemVariable
+               TypeBinding elementType = this.elementVariable.type.resolvedType;
+               TypeBinding collectionType = this.collection.resolveType(scope);
+               this.collection.computeConversion(scope, collectionType, collectionType);
+               boolean hasError = elementType == null || collectionType == null;
+
+               if (!hasError) {
+                       if (collectionType.isArrayType()) { // for(E e : E[])
+                               this.kind = ARRAY;
+                               this.collectionElementType = ((ArrayBinding) collectionType).elementsType();
+                               if (!collectionElementType.isCompatibleWith(elementType)
+                                               && !scope.isBoxingCompatibleWith(collectionElementType, elementType)) {
+                                       scope.problemReporter().notCompatibleTypesErrorInForeach(collection, collectionElementType, elementType);
+                               }
+                               // in case we need to do a conversion
+                               int compileTimeTypeID = collectionElementType.id;
+                               if (elementType.isBaseType()) {
+                                       if (!collectionElementType.isBaseType()) {
+                                               compileTimeTypeID = scope.environment().computeBoxingType(collectionElementType).id;
+                                               this.elementVariableImplicitWidening = UNBOXING;
+                                               if (elementType.isBaseType()) {
+                                                       this.elementVariableImplicitWidening |= (elementType.id << 4) + compileTimeTypeID;
+                                               }
+                                       } else {
+                                               this.elementVariableImplicitWidening = (elementType.id << 4) + compileTimeTypeID;
+                                       }
+                               } else {
+                                       if (collectionElementType.isBaseType()) {
+                                               int boxedID = scope.environment().computeBoxingType(collectionElementType).id;
+                                               this.elementVariableImplicitWidening = BOXING | (compileTimeTypeID << 4) | compileTimeTypeID; // use primitive type in implicit conversion
+                                               compileTimeTypeID = boxedID;
+                                       }
+                               }
+                       } else if (collectionType instanceof ReferenceBinding) {
+                           ReferenceBinding iterableType = ((ReferenceBinding)collectionType).findSuperTypeErasingTo(T_JavaLangIterable, false /*Iterable is not a class*/);
+                           if (iterableType != null) {
+                                   if (iterableType.isParameterizedType()) { // for(E e : Iterable<E>)
+                                           ParameterizedTypeBinding parameterizedType = (ParameterizedTypeBinding)iterableType;
+                                               if (parameterizedType.arguments.length == 1) { // per construction can only be one
+                                                       this.kind = GENERIC_ITERABLE;
+                                                       this.collectionElementType = parameterizedType.arguments[0]; 
+                                                       if (!collectionElementType.isCompatibleWith(elementType)
+                                                                       && !scope.isBoxingCompatibleWith(collectionElementType, elementType)) {
+                                                               scope.problemReporter().notCompatibleTypesErrorInForeach(collection, collectionElementType, elementType);
+                                                       }
+                                                       int compileTimeTypeID = collectionElementType.id;
+                                                       // no conversion needed as only for reference types
+                                                       if (elementType.isBaseType()) {
+                                                               if (!collectionElementType.isBaseType()) {
+                                                                       compileTimeTypeID = scope.environment().computeBoxingType(collectionElementType).id;
+                                                                       this.elementVariableImplicitWidening = UNBOXING;
+                                                                       if (elementType.isBaseType()) {
+                                                                               this.elementVariableImplicitWidening |= (elementType.id << 4) + compileTimeTypeID;
+                                                                       }
+                                                               } else {
+                                                                       this.elementVariableImplicitWidening = (elementType.id << 4) + compileTimeTypeID;
+                                                               }
+                                                       } else {
+                                                               if (collectionElementType.isBaseType()) {
+                                                                       int boxedID = scope.environment().computeBoxingType(collectionElementType).id;
+                                                                       this.elementVariableImplicitWidening = BOXING | (compileTimeTypeID << 4) | compileTimeTypeID; // use primitive type in implicit conversion
+                                                                       compileTimeTypeID = boxedID;
+                                                               }
+                                                       }
+                                               }
+                                   } else if (iterableType.isGenericType()) { // for (T t : Iterable<T>) - in case used inside Iterable itself
+                                               if (iterableType.typeVariables().length == 1) {
+                                                       this.kind = GENERIC_ITERABLE;
+                                                       this.collectionElementType = iterableType.typeVariables()[0]; 
+                                                       if (!collectionElementType.isCompatibleWith(elementType)
+                                                                       && !scope.isBoxingCompatibleWith(collectionElementType, elementType)) {
+                                                               scope.problemReporter().notCompatibleTypesErrorInForeach(collection, collectionElementType, elementType);
+                                                       }
+                                                       int compileTimeTypeID = collectionElementType.id;
+                                                       // no conversion needed as only for reference types
+                                                       if (elementType.isBaseType()) {
+                                                               if (!collectionElementType.isBaseType()) {
+                                                                       compileTimeTypeID = scope.environment().computeBoxingType(collectionElementType).id;
+                                                                       this.elementVariableImplicitWidening = UNBOXING;
+                                                                       if (elementType.isBaseType()) {
+                                                                               this.elementVariableImplicitWidening |= (elementType.id << 4) + compileTimeTypeID;
+                                                                       }
+                                                               } else {
+                                                                       this.elementVariableImplicitWidening = (elementType.id << 4) + compileTimeTypeID;
+                                                               }
+                                                       } else {
+                                                               if (collectionElementType.isBaseType()) {
+                                                                       int boxedID = scope.environment().computeBoxingType(collectionElementType).id;
+                                                                       this.elementVariableImplicitWidening = BOXING | (compileTimeTypeID << 4) | compileTimeTypeID; // use primitive type in implicit conversion
+                                                                       compileTimeTypeID = boxedID;
+                                                               }
+                                                       }
+                                               }
+                                       } else if (iterableType.isRawType()) { // for(Object o : Iterable)
+                                               this.kind = RAW_ITERABLE;
+                                               this.collectionElementType = scope.getJavaLangObject();
+                                               if (!collectionElementType.isCompatibleWith(elementType)
+                                                               && !scope.isBoxingCompatibleWith(collectionElementType, elementType)) {
+                                                       scope.problemReporter().notCompatibleTypesErrorInForeach(collection, collectionElementType, elementType);
+                                               }
+                                               // no conversion needed as only for reference types
+                                       }                           
+                           }
+                       }
+                       switch(this.kind) {
+                               case ARRAY :
+                                       // allocate #index secret variable (of type int)
+                                       this.indexVariable = new LocalVariableBinding(SecretIndexVariableName, IntBinding, AccDefault, false);
+                                       scope.addLocalVariable(this.indexVariable);
+                                       this.indexVariable.setConstant(NotAConstant); // not inlinable
+                                       
+                                       // allocate #max secret variable
+                                       this.maxVariable = new LocalVariableBinding(SecretMaxVariableName, IntBinding, AccDefault, false);
+                                       scope.addLocalVariable(this.maxVariable);
+                                       this.maxVariable.setConstant(NotAConstant); // not inlinable
+                                       // add #array secret variable (of collection type)
+                                       this.collectionVariable = new LocalVariableBinding(SecretCollectionVariableName, collectionType, AccDefault, false);
+                                       scope.addLocalVariable(this.collectionVariable);
+                                       this.collectionVariable.setConstant(NotAConstant); // not inlinable
+                                       break;
+                               case RAW_ITERABLE :
+                               case GENERIC_ITERABLE :
+                                       // allocate #index secret variable (of type Iterator)
+                                       this.indexVariable = new LocalVariableBinding(SecretIndexVariableName, scope.getJavaUtilIterator(), AccDefault, false);
+                                       scope.addLocalVariable(this.indexVariable);
+                                       this.indexVariable.setConstant(NotAConstant); // not inlinable
+                                       break;
+                               default :
+                                       scope.problemReporter().invalidTypeForCollection(collection);
+                       }
+               }
+               if (action != null) {
+                       action.resolve(scope);
+               }
+       }
+       
+       public void traverse(
+               ASTVisitor visitor,
+               BlockScope blockScope) {
+
+               if (visitor.visit(this, blockScope)) {
+                       this.elementVariable.traverse(visitor, scope);
+                       this.collection.traverse(visitor, scope);
+                       if (action != null) {
+                               action.traverse(visitor, scope);
+                       }
+               }
+               visitor.endVisit(this, blockScope);
+       }
+}