You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@royale.apache.org by gr...@apache.org on 2019/10/02 08:39:22 UTC

[royale-compiler] 01/03: RoyaleArrayLike support

This is an automated email from the ASF dual-hosted git repository.

gregdove pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/royale-compiler.git

commit 9c0ada6e30f1675f7137a76d3b76f718b5f9f87d
Author: greg-dove <gr...@gmail.com>
AuthorDate: Sun Sep 29 15:09:10 2019 +1300

    RoyaleArrayLike support
---
 .../ArrayLikeConfigurationErrorProblem.java        |  54 ++
 .../problems/ArrayLikeUsageErrorProblem.java       |  48 ++
 compiler-jx/pom.xml                                |   2 +-
 .../internal/codegen/js/jx/ForEachEmitter.java     |  35 +-
 .../internal/codegen/js/jx/MethodEmitter.java      |   4 +-
 .../js/sourcemaps/TestSourceMapStatements.java     |   3 +-
 .../constants/IMetaAttributeConstants.java         |   3 +
 .../compiler/internal/as/codegen/ABCGenerator.java |   2 +
 .../as/codegen/ClassDirectiveProcessor.java        |  68 +-
 .../as/codegen/GlobalDirectiveProcessor.java       |   2 +
 .../as/codegen/InterfaceDirectiveProcessor.java    |   9 +
 .../projects/RoyaleProjectConfigurator.java        |   2 +-
 .../royale/compiler/internal/scopes/ASScope.java   |  18 +-
 .../compiler/internal/tree/as/ForLoopNode.java     |  56 +-
 .../compiler/internal/tree/as/FunctionNode.java    |   2 +-
 .../royale/compiler/internal/tree/as/TreeNode.java |   7 +-
 .../main/java/org/apache/royale/utils/ASTUtil.java |  43 +
 .../org/apache/royale/utils/ArrayLikeUtil.java     | 900 +++++++++++++++++++++
 18 files changed, 1188 insertions(+), 70 deletions(-)

diff --git a/compiler-common/src/main/java/org/apache/royale/compiler/problems/ArrayLikeConfigurationErrorProblem.java b/compiler-common/src/main/java/org/apache/royale/compiler/problems/ArrayLikeConfigurationErrorProblem.java
new file mode 100644
index 0000000..1229c46
--- /dev/null
+++ b/compiler-common/src/main/java/org/apache/royale/compiler/problems/ArrayLikeConfigurationErrorProblem.java
@@ -0,0 +1,54 @@
+/*
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.apache.royale.compiler.problems;
+
+import org.apache.royale.compiler.common.ISourceLocation;
+import org.apache.royale.compiler.problems.annotations.DefaultSeverity;
+
+/**
+ *  When a class or interface is annotated with [ArrayLike] metadata, it needs
+ *  to provide the required minimum set of arguments to allow instances of the class
+ *  to be treated as 'ArrayLike' when encountered in other source code.
+ *  It also requires the concrete presence of the arrayLike iterator factory as a separate
+ *  stand-alone dependency in the current project level compilation. This ensures that there
+ *  is no dependency missing if the ArrayLike definition is in an external library: the supporting
+ *  definition for iterator support must be in the same library
+ *  (which might mean it is repeated across different libraries, but that we can be sure it is available)
+ */
+@DefaultSeverity(CompilerProblemSeverity.ERROR)
+public class ArrayLikeConfigurationErrorProblem extends CompilerProblem
+{
+    public static final String DESCRIPTION =
+        "${description}. ArrayLike definition has an error with a required argument or dependency.";
+    
+    public static final int errorCode = 9999;
+    
+    public ArrayLikeConfigurationErrorProblem(ISourceLocation site, String description)
+    {
+        super(site);
+        this.description = description;
+    }
+    
+    public ArrayLikeConfigurationErrorProblem(String sourcePath, int start, int end, int line, int column, int endLine, int endColumn, String description)
+    {
+        super(sourcePath, start, end, line, column, endLine, endColumn);
+        this.description = description;
+    }
+    public final String description;
+}
diff --git a/compiler-common/src/main/java/org/apache/royale/compiler/problems/ArrayLikeUsageErrorProblem.java b/compiler-common/src/main/java/org/apache/royale/compiler/problems/ArrayLikeUsageErrorProblem.java
new file mode 100644
index 0000000..51320e3
--- /dev/null
+++ b/compiler-common/src/main/java/org/apache/royale/compiler/problems/ArrayLikeUsageErrorProblem.java
@@ -0,0 +1,48 @@
+/*
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.apache.royale.compiler.problems;
+
+import org.apache.royale.compiler.common.ISourceLocation;
+import org.apache.royale.compiler.problems.annotations.DefaultSeverity;
+
+/**
+ *  When an instance of an ArrayLike class is used in an unsupported way, an ArrayLikeUsageErrorProblem
+ *  is created. It needs to be addressed in the source code at the usage site.
+ **/
+@DefaultSeverity(CompilerProblemSeverity.ERROR)
+public class ArrayLikeUsageErrorProblem extends CompilerProblem
+{
+    public static final String DESCRIPTION =
+        "${description}. ArrayLike usage is not valid.";
+    
+    public static final int errorCode = 9998;
+    
+    public ArrayLikeUsageErrorProblem(ISourceLocation site, String description)
+    {
+        super(site);
+        this.description = description;
+    }
+    
+    public ArrayLikeUsageErrorProblem(String sourcePath, int start, int end, int line, int column, int endLine, int endColumn, String description)
+    {
+        super(sourcePath, start, end, line, column, endLine, endColumn);
+        this.description = description;
+    }
+    public final String description;
+}
diff --git a/compiler-jx/pom.xml b/compiler-jx/pom.xml
index d1780ef..d9d9458 100644
--- a/compiler-jx/pom.xml
+++ b/compiler-jx/pom.xml
@@ -86,7 +86,7 @@
           <excludes>
             <exclude>**/TestRoyaleFile.java</exclude>
             <exclude>**/TestRoyaleMXML*.java</exclude>
-            <exclude>**/TestSourceMap*.java</exclude>
+            <exclude>**/TestSourceMapMXML*.java</exclude>
             <exclude>**/TestMXML*.java</exclude>
             <exclude>**/TestRoyaleClass.java</exclude>
             <exclude>**/TestTypedefsCompile.java</exclude>
diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/ForEachEmitter.java b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/ForEachEmitter.java
index 86e6227..0def4ce 100644
--- a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/ForEachEmitter.java
+++ b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/ForEachEmitter.java
@@ -35,7 +35,6 @@ import org.apache.royale.compiler.tree.as.IBinaryOperatorNode;
 import org.apache.royale.compiler.tree.as.IContainerNode;
 import org.apache.royale.compiler.tree.as.IExpressionNode;
 import org.apache.royale.compiler.tree.as.IForLoopNode;
-import org.apache.royale.compiler.tree.as.IIdentifierNode;
 import org.apache.royale.compiler.tree.as.IVariableExpressionNode;
 import org.apache.royale.compiler.tree.as.IVariableNode;
 
@@ -117,6 +116,15 @@ public class ForEachEmitter extends JSSubEmitter implements
                 isProxy = true;
             }
         }
+        else if (obj.getNodeID() == ASTNodeID.Op_DescendantsID)
+        {
+            //it should always be XMLList... but check anyway
+            if (((JSRoyaleEmitter)getEmitter()).isXMLList((MemberAccessExpressionNode)obj))
+            {
+                write(".elementNames()");
+                isXML = true;
+            }
+        }
         else if (obj.getNodeID() == ASTNodeID.MemberAccessExpressionID)
         {
             if (((JSRoyaleEmitter)getEmitter()).isXMLList((MemberAccessExpressionNode)obj))
@@ -164,19 +172,28 @@ public class ForEachEmitter extends JSSubEmitter implements
         writeNewline();
         write(ASEmitterTokens.BLOCK_OPEN);
         writeNewline();
-        startMapping(childNode);
+        
         if (childNode instanceof IVariableExpressionNode)
         {
+            startMapping(childNode);
             write(ASEmitterTokens.VAR);
             write(ASEmitterTokens.SPACE);
-            write(((IVariableNode) childNode.getChild(0)).getName());
+            write(((IVariableNode) childNode.getChild(0)).getName()); //it's always a local var
+            //putting this in here instead of common code following the 2 blocks to keep sourcemap tests passing
+            write(ASEmitterTokens.SPACE);
+            write(ASEmitterTokens.EQUAL);
+            write(ASEmitterTokens.SPACE);
+            endMapping(childNode);
         }
-        else
-            write(((IIdentifierNode) childNode).getName());
-        write(ASEmitterTokens.SPACE);
-        write(ASEmitterTokens.EQUAL);
-        write(ASEmitterTokens.SPACE);
-        endMapping(childNode);
+        else { //IdentifierNode
+            getWalker().walk(childNode); //we need to walk here, to deal with non-local var identifiers
+            startMapping(childNode);
+            write(ASEmitterTokens.SPACE);
+            write(ASEmitterTokens.EQUAL);
+            write(ASEmitterTokens.SPACE);
+            endMapping(childNode);
+        }
+      
         startMapping(rnode);
         write(targetName);
         if (isXML)
diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/MethodEmitter.java b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/MethodEmitter.java
index 0fc7b0b..688b190 100644
--- a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/MethodEmitter.java
+++ b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/MethodEmitter.java
@@ -40,6 +40,7 @@ import org.apache.royale.compiler.problems.ICompilerProblem;
 import org.apache.royale.compiler.projects.ICompilerProject;
 import org.apache.royale.compiler.tree.as.IClassNode;
 import org.apache.royale.compiler.tree.as.IFunctionNode;
+import org.apache.royale.utils.ASTUtil;
 
 public class MethodEmitter extends JSSubEmitter implements
         ISubEmitter<IFunctionNode>
@@ -63,7 +64,8 @@ public class MethodEmitter extends JSSubEmitter implements
         ICompilerProject project = getWalker().getProject();
 
         fjs.getDocEmitter().emitMethodDoc(node, project);
-
+        ASTUtil.processFunctionNode(fn, project, true);
+        
         boolean isConstructor = node.isConstructor();
 
         boolean addingBindableImplementsSupport = isConstructor &&
diff --git a/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/js/sourcemaps/TestSourceMapStatements.java b/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/js/sourcemaps/TestSourceMapStatements.java
index d13a577..0e306d1 100644
--- a/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/js/sourcemaps/TestSourceMapStatements.java
+++ b/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/js/sourcemaps/TestSourceMapStatements.java
@@ -291,7 +291,8 @@ public class TestSourceMapStatements extends SourceMapTestBase
         assertMapping(node, 0, 14, 1, 5, 1, 21);   // var foreachiter0
         assertMapping(node, 0, 14, 1, 25, 1, 44);  // foreachiter0_target
         assertMapping(node, 0, 17, 1, 44, 1, 46);  // )
-        assertMapping(node, 0, 9, 3, 0, 3, 4);     // i =
+        assertMapping(node, 0, 9, 3, 0, 3, 1);     // i
+        assertMapping(node, 0, 9, 3, 1, 3, 4);     //  =
         assertMapping(node, 0, 14, 3, 4, 3, 38);   // foreachiter0_target[foreachiter0];
         assertMapping(node, 0, 20, 5, 2, 5, 7);    // break
         assertMapping(node, 0, 25, 5, 7, 5, 8);    // ;
diff --git a/compiler/src/main/java/org/apache/royale/compiler/constants/IMetaAttributeConstants.java b/compiler/src/main/java/org/apache/royale/compiler/constants/IMetaAttributeConstants.java
index 4aba20b..ecf8158 100644
--- a/compiler/src/main/java/org/apache/royale/compiler/constants/IMetaAttributeConstants.java
+++ b/compiler/src/main/java/org/apache/royale/compiler/constants/IMetaAttributeConstants.java
@@ -216,6 +216,9 @@ public interface IMetaAttributeConstants
 
     // [RoyalePrivateConstructor]
     static final String ATTRIBUTE_PRIVATE_CONSTRUCTOR = "RoyalePrivateConstructor";
+    
+    // [RoyaleArrayLike(...args)]
+    static final String ATTRIBUTE_ARRAYLIKE = "RoyaleArrayLike";
 	
 	/**
 	 * List of metadata tags that do not inherit
diff --git a/compiler/src/main/java/org/apache/royale/compiler/internal/as/codegen/ABCGenerator.java b/compiler/src/main/java/org/apache/royale/compiler/internal/as/codegen/ABCGenerator.java
index 0772f61..10e47d8 100644
--- a/compiler/src/main/java/org/apache/royale/compiler/internal/as/codegen/ABCGenerator.java
+++ b/compiler/src/main/java/org/apache/royale/compiler/internal/as/codegen/ABCGenerator.java
@@ -68,6 +68,7 @@ import org.apache.royale.compiler.tree.as.IFunctionNode;
 import org.apache.royale.compiler.tree.as.IParameterNode;
 import org.apache.royale.compiler.units.requests.IABCBytesRequestResult;
 import com.google.common.util.concurrent.Futures;
+import org.apache.royale.utils.ASTUtil;
 
 /**
  * ABCGenerator is the public interface to the code generator.
@@ -713,6 +714,7 @@ public class ABCGenerator implements ICodeGenerator
         {
             assert !methodInfo.isNative() : "Native methods should be handled in the main thread and not be dispatched to a background thread!";
             functionNode.parseFunctionBody(enclosingScope.getProblems());
+            ASTUtil.processFunctionNode(functionNode, enclosingScope.getProject(), false);
             generateMethodBodyForFunction(deferredVisitEnds, methodInfo, functionNode, enclosingScope, null);
         }
         
diff --git a/compiler/src/main/java/org/apache/royale/compiler/internal/as/codegen/ClassDirectiveProcessor.java b/compiler/src/main/java/org/apache/royale/compiler/internal/as/codegen/ClassDirectiveProcessor.java
index 16cfa1e..b8303ff 100644
--- a/compiler/src/main/java/org/apache/royale/compiler/internal/as/codegen/ClassDirectiveProcessor.java
+++ b/compiler/src/main/java/org/apache/royale/compiler/internal/as/codegen/ClassDirectiveProcessor.java
@@ -21,10 +21,7 @@ package org.apache.royale.compiler.internal.as.codegen;
 
 import static org.apache.royale.abc.ABCConstants.*;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
+import java.util.*;
 
 import org.apache.royale.abc.ABCConstants;
 import org.apache.royale.abc.instructionlist.InstructionList;
@@ -51,48 +48,16 @@ import org.apache.royale.compiler.constants.IASKeywordConstants;
 import org.apache.royale.compiler.constants.IASLanguageConstants;
 import org.apache.royale.compiler.constants.IMetaAttributeConstants;
 import org.apache.royale.compiler.constants.INamespaceConstants;
-import org.apache.royale.compiler.definitions.IAccessorDefinition;
-import org.apache.royale.compiler.definitions.IClassDefinition;
-import org.apache.royale.compiler.definitions.IConstantDefinition;
-import org.apache.royale.compiler.definitions.IDefinition;
-import org.apache.royale.compiler.definitions.IInterfaceDefinition;
-import org.apache.royale.compiler.definitions.INamespaceDefinition;
+import org.apache.royale.compiler.definitions.*;
 import org.apache.royale.compiler.definitions.metadata.IMetaTag;
 import org.apache.royale.compiler.definitions.metadata.IMetaTagAttribute;
 import org.apache.royale.compiler.definitions.references.INamespaceReference;
 import org.apache.royale.compiler.exceptions.CodegenInterruptedException;
-import org.apache.royale.compiler.problems.AbstractOutsideClassProblem;
-import org.apache.royale.compiler.problems.BadAccessAbstractMethodProblem;
-import org.apache.royale.compiler.problems.CircularTypeReferenceProblem;
-import org.apache.royale.compiler.problems.ConstructorCannotHaveReturnTypeProblem;
-import org.apache.royale.compiler.problems.ConstructorIsGetterSetterProblem;
-import org.apache.royale.compiler.problems.ConstructorIsStaticProblem;
-import org.apache.royale.compiler.problems.ConstructorMustBePublicOrPrivateProblem;
-import org.apache.royale.compiler.problems.ConstructorMustBePublicProblem;
-import org.apache.royale.compiler.problems.DuplicateClassDefinitionProblem;
-import org.apache.royale.compiler.problems.DynamicNotOnClassProblem;
-import org.apache.royale.compiler.problems.FinalOutsideClassProblem;
-import org.apache.royale.compiler.problems.ForwardReferenceToBaseClassProblem;
-import org.apache.royale.compiler.problems.FunctionNotMarkedOverrideProblem;
-import org.apache.royale.compiler.problems.ICompilerProblem;
-import org.apache.royale.compiler.problems.IncompatibleOverrideProblem;
-import org.apache.royale.compiler.problems.InvalidOverrideProblem;
-import org.apache.royale.compiler.problems.MultipleContructorDefinitionsProblem;
-import org.apache.royale.compiler.problems.NativeVariableProblem;
-import org.apache.royale.compiler.problems.OverrideFinalProblem;
-import org.apache.royale.compiler.problems.OverrideNotFoundProblem;
-import org.apache.royale.compiler.problems.StaticAndOverrideProblem;
-import org.apache.royale.compiler.problems.StaticNamespaceDefinitionProblem;
-import org.apache.royale.compiler.problems.SyntaxProblem;
-import org.apache.royale.compiler.problems.VirtualOutsideClassProblem;
+import org.apache.royale.compiler.internal.tree.as.*;
+import org.apache.royale.compiler.problems.*;
 import org.apache.royale.compiler.projects.ICompilerProject;
 import org.apache.royale.compiler.tree.ASTNodeID;
-import org.apache.royale.compiler.tree.as.IASNode;
-import org.apache.royale.compiler.tree.as.ICommonClassNode;
-import org.apache.royale.compiler.tree.as.IDefinitionNode;
-import org.apache.royale.compiler.tree.as.IExpressionNode;
-import org.apache.royale.compiler.tree.as.ILanguageIdentifierNode;
-import org.apache.royale.compiler.tree.as.INamespaceDecorationNode;
+import org.apache.royale.compiler.tree.as.*;
 import org.apache.royale.compiler.tree.as.ILanguageIdentifierNode.LanguageIdentifierKind;
 import org.apache.royale.compiler.internal.abc.FunctionGeneratorHelper;
 import org.apache.royale.compiler.internal.as.codegen.ICodeGenerator.IConstantValue;
@@ -112,13 +77,8 @@ import org.apache.royale.compiler.internal.projects.CompilerProject;
 import org.apache.royale.compiler.internal.scopes.ASScope;
 import org.apache.royale.compiler.internal.semantics.MethodBodySemanticChecker;
 import org.apache.royale.compiler.internal.semantics.SemanticUtils;
-import org.apache.royale.compiler.internal.tree.as.BaseDefinitionNode;
-import org.apache.royale.compiler.internal.tree.as.ClassNode;
-import org.apache.royale.compiler.internal.tree.as.FunctionNode;
-import org.apache.royale.compiler.internal.tree.as.ImportNode;
-import org.apache.royale.compiler.internal.tree.as.NamespaceIdentifierNode;
-import org.apache.royale.compiler.internal.tree.as.NamespaceNode;
-import org.apache.royale.compiler.internal.tree.as.VariableNode;
+import org.apache.royale.utils.ASTUtil;
+import org.apache.royale.utils.ArrayLikeUtil;
 
 /**
  * A ClassDirectiveProcessor generates an ABC class
@@ -620,8 +580,16 @@ class ClassDirectiveProcessor extends DirectiveProcessor
                     Integer.toString(ctorDef.getNameStart()), true);
             if (metaTag != null)
                 metaTags = MetaTag.addMetaTag(metaTags, metaTag);
-        }        
-
+        }
+    
+        if (ArrayLikeUtil.definitionIsArrayLike(classDefinition)) {
+            ArrayList<ICompilerProblem> problems = new ArrayList<ICompilerProblem>();
+            boolean valid = ArrayLikeUtil.validateArrayLikeDefinition(classDefinition, classScope.getProject(), problems);
+            if (!valid){
+                classScope.getProject().getProblems().addAll(problems);
+            }
+        }
+        
         this.classScope.processMetadata(tv, metaTags);
         tv.visitEnd();
         
@@ -816,7 +784,7 @@ class ClassDirectiveProcessor extends DirectiveProcessor
         final boolean is_constructor = func.isConstructor();
         
         ICompilerProject project = classScope.getProject();
-        
+        ASTUtil.processFunctionNode(func, project, false);
         boolean isBindable = false;
         if (funcDef instanceof AccessorDefinition)
         {
diff --git a/compiler/src/main/java/org/apache/royale/compiler/internal/as/codegen/GlobalDirectiveProcessor.java b/compiler/src/main/java/org/apache/royale/compiler/internal/as/codegen/GlobalDirectiveProcessor.java
index 7f28031..b9d2013 100644
--- a/compiler/src/main/java/org/apache/royale/compiler/internal/as/codegen/GlobalDirectiveProcessor.java
+++ b/compiler/src/main/java/org/apache/royale/compiler/internal/as/codegen/GlobalDirectiveProcessor.java
@@ -60,6 +60,7 @@ import org.apache.royale.compiler.problems.SyntaxProblem;
 import org.apache.royale.compiler.problems.VirtualOutsideClassProblem;
 import org.apache.royale.compiler.projects.ICompilerProject;
 import org.apache.royale.compiler.tree.mxml.IMXMLDocumentNode;
+import org.apache.royale.utils.ASTUtil;
 
 import java.util.LinkedList;
 import java.util.List;
@@ -156,6 +157,7 @@ class GlobalDirectiveProcessor extends DirectiveProcessor
         else
         {
             f.parseFunctionBody(currentScope.getProblems());
+            ASTUtil.processFunctionNode(f, currentScope.getProject(), false);
             return currentScope.getGenerator().generateFunction(f, this.currentScope, null, null);
         }
     }
diff --git a/compiler/src/main/java/org/apache/royale/compiler/internal/as/codegen/InterfaceDirectiveProcessor.java b/compiler/src/main/java/org/apache/royale/compiler/internal/as/codegen/InterfaceDirectiveProcessor.java
index e8409c3..5a47d93 100644
--- a/compiler/src/main/java/org/apache/royale/compiler/internal/as/codegen/InterfaceDirectiveProcessor.java
+++ b/compiler/src/main/java/org/apache/royale/compiler/internal/as/codegen/InterfaceDirectiveProcessor.java
@@ -19,6 +19,7 @@
 
 package org.apache.royale.compiler.internal.as.codegen;
 
+import java.util.ArrayList;
 import java.util.Iterator;
 
 import org.apache.royale.abc.ABCConstants;
@@ -80,6 +81,7 @@ import org.apache.royale.compiler.tree.as.IASNode;
 import org.apache.royale.compiler.tree.as.IExpressionNode;
 import org.apache.royale.compiler.tree.as.IIdentifierNode;
 import org.apache.royale.compiler.tree.as.INamespaceDecorationNode;
+import org.apache.royale.utils.ArrayLikeUtil;
 
 /**
  *  The InterfaceDirectiveProcessor translates an InterfaceNode AST
@@ -252,6 +254,13 @@ public class InterfaceDirectiveProcessor extends DirectiveProcessor
         setup_insns.addInstruction(OP_initproperty, interfaceName);
         
         ITraitVisitor tv = this.interfaceScope.getGlobalScope().traitsVisitor.visitClassTrait(TRAIT_Class, interfaceName, 0, cinfo);
+        if (ArrayLikeUtil.definitionIsArrayLike(interfDef)) {
+            ArrayList<ICompilerProblem> problems = new ArrayList<ICompilerProblem>();
+            boolean valid = ArrayLikeUtil.validateArrayLikeDefinition(interfDef, this.interfaceScope.getProject(), problems);
+            if (!valid){
+                this.interfaceScope.getProject().getProblems().addAll(problems);
+            }
+        }
         this.interfaceScope.processMetadata(tv, interfDef.getAllMetaTags());
         tv.visitEnd();
     }
diff --git a/compiler/src/main/java/org/apache/royale/compiler/internal/projects/RoyaleProjectConfigurator.java b/compiler/src/main/java/org/apache/royale/compiler/internal/projects/RoyaleProjectConfigurator.java
index ef8c367..b8a34be 100644
--- a/compiler/src/main/java/org/apache/royale/compiler/internal/projects/RoyaleProjectConfigurator.java
+++ b/compiler/src/main/java/org/apache/royale/compiler/internal/projects/RoyaleProjectConfigurator.java
@@ -58,7 +58,7 @@ public class RoyaleProjectConfigurator extends Configurator
     /**
      * These imports will eventually come from royale-config.xml.
      * <p>
-     * Note taht we include AIR-specific imports even for non-AIR projects.
+     * Note that we include AIR-specific imports even for non-AIR projects.
      * This does not cause compiler warnings about unknown imports
      * in non-AIR projects because the implicit imports don't get
      * semantically checked.
diff --git a/compiler/src/main/java/org/apache/royale/compiler/internal/scopes/ASScope.java b/compiler/src/main/java/org/apache/royale/compiler/internal/scopes/ASScope.java
index b229304..3893bb4 100644
--- a/compiler/src/main/java/org/apache/royale/compiler/internal/scopes/ASScope.java
+++ b/compiler/src/main/java/org/apache/royale/compiler/internal/scopes/ASScope.java
@@ -52,6 +52,7 @@ import org.apache.royale.compiler.internal.tree.as.ScopedBlockNode;
 import org.apache.royale.compiler.internal.workspaces.Workspace;
 import org.apache.royale.compiler.projects.ICompilerProject;
 import org.apache.royale.compiler.scopes.IDefinitionSet;
+import org.apache.royale.compiler.tree.as.IForLoopNode;
 import org.apache.royale.compiler.tree.as.IScopedNode;
 import org.apache.royale.compiler.units.ICompilationUnit;
 import org.apache.royale.compiler.workspaces.IWorkspace;
@@ -69,6 +70,8 @@ public abstract class ASScope extends ASScopeBase
 
     protected static final NamespaceDefinition.IUseNamespaceDirective[] EMPTY_USE_ARRAY =
             new NamespaceDefinition.IUseNamespaceDirective[0];
+    
+    private static IForLoopNode[] EMPTY_FOREACH_ARRAY = new IForLoopNode[0];
 
     /**
      * Constructor
@@ -122,7 +125,20 @@ public abstract class ASScope extends ASScopeBase
     private NamespaceDefinition.INamespaceDirective lastNamespaceDirective;
 
     private boolean inWith = false;
-
+    
+    private Object forEachs = null;
+    public boolean getHasForEach(){
+        return forEachs != null;
+    }
+    public void addForEach(IForLoopNode value){
+        if (forEachs == null) forEachs = CheapArray.create(1);
+        CheapArray.add(value, forEachs);
+    }
+    public IForLoopNode[] getForEachs(boolean remove){
+        IForLoopNode[] returnForEachs = (IForLoopNode[]) CheapArray.toArray(forEachs, EMPTY_FOREACH_ARRAY);
+        if (remove) forEachs = null;
+        return returnForEachs;
+    }
     /**
      * Sets the scope which lexically contains this scope.
      * 
diff --git a/compiler/src/main/java/org/apache/royale/compiler/internal/tree/as/ForLoopNode.java b/compiler/src/main/java/org/apache/royale/compiler/internal/tree/as/ForLoopNode.java
index 8d01661..9a58bcd 100644
--- a/compiler/src/main/java/org/apache/royale/compiler/internal/tree/as/ForLoopNode.java
+++ b/compiler/src/main/java/org/apache/royale/compiler/internal/tree/as/ForLoopNode.java
@@ -16,12 +16,12 @@
  *  limitations under the License.
  *
  */
-
 package org.apache.royale.compiler.internal.tree.as;
 
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.EnumSet;
+import java.util.List;
 
 import org.apache.royale.compiler.internal.scopes.ASScope;
 import org.apache.royale.compiler.internal.semantics.PostProcessStep;
@@ -34,6 +34,24 @@ import org.apache.royale.compiler.tree.as.IForLoopNode;
 
 public class ForLoopNode extends FixedChildrenNode implements IForLoopNode
 {
+    
+    /**
+     * Support for an externally defined loop mutation (AST manipulation)
+     */
+    public static interface ILoopMutation{
+        IForLoopNode.ForLoopKind getKind();
+        IForLoopNode getLoopTarget();
+        boolean mutatesConditionals();
+        boolean mutatesContents();
+        boolean isValid();
+        IExpressionNode getIterationTarget();
+        List<NodeBase> getAnalyzeRequests();
+        void prepareConditionals(boolean firstUse, FunctionCallNode factoryFuncCall);
+        void prepareContent();
+        void processConditionals(ASScope scope, Collection<ICompilerProblem> problems);
+        void processContents(ASScope scope, Collection<ICompilerProblem> problems);
+    }
+    
     /**
      * Constructor.
      */
@@ -125,6 +143,10 @@ public class ForLoopNode extends FixedChildrenNode implements IForLoopNode
         // Put header statements into the current block
         conditionsStatementsNode.analyze(set, scope, problems);
         contentsNode.analyze(set, scope, problems);
+        // add for-each kind for subsequent post-processing step at function scope level
+        if (kind == ForLoopKind.FOR_EACH) {
+            scope.addForEach(this);
+        }
     }
     
     //
@@ -171,4 +193,36 @@ public class ForLoopNode extends FixedChildrenNode implements IForLoopNode
     {
         return contentsNode;
     }
+    
+    //special case for mutating loop in post-process stage, after definitions are available
+    public Collection<ICompilerProblem> processMutation(ILoopMutation mutation, ASScope scope) {
+        assert mutation != null && mutation.getLoopTarget() == this : "Loop mutation is not aligned with target for mutation";
+        Collection<ICompilerProblem> problems = new ArrayList<ICompilerProblem>();
+        if (mutation.isValid()) {
+            IForLoopNode.ForLoopKind newKind = mutation.getKind();
+            if (kind != newKind) {
+                //handle anything related to that change
+                kind = newKind;
+            }
+            if (mutation.mutatesConditionals()) {
+                //handle anything related to that change
+                mutation.processConditionals(scope, problems);
+            }
+            if (mutation.mutatesContents()) {
+                //handle anything related to that change
+                mutation.processContents(scope, problems);
+            }
+            List<NodeBase> analyzeRequests = mutation.getAnalyzeRequests();
+            if (analyzeRequests != null) {
+                EnumSet<PostProcessStep> set = EnumSet.of(
+                        PostProcessStep.POPULATE_SCOPE);
+                for (NodeBase node: analyzeRequests) {
+                    node.analyze(set,scope, problems);
+                }
+            }
+            
+        }
+        return problems;
+    }
 }
+
diff --git a/compiler/src/main/java/org/apache/royale/compiler/internal/tree/as/FunctionNode.java b/compiler/src/main/java/org/apache/royale/compiler/internal/tree/as/FunctionNode.java
index 54c836c..8fc51e3 100644
--- a/compiler/src/main/java/org/apache/royale/compiler/internal/tree/as/FunctionNode.java
+++ b/compiler/src/main/java/org/apache/royale/compiler/internal/tree/as/FunctionNode.java
@@ -832,7 +832,7 @@ public class FunctionNode extends BaseTypedDefinitionNode implements IFunctionNo
             assert openT != null : "Expected '{' token.";
 
             final String sourcePath = getSourcePath();
-            assert sourcePath != null && !sourcePath.isEmpty() : "Souce path not set.";
+            assert sourcePath != null && !sourcePath.isEmpty() : "Source path not set.";
 
             final FileNode fileNode = (FileNode)getAncestorOfType(FileNode.class);
             assert fileNode != null : "FileNode not found: function " + getName();
diff --git a/compiler/src/main/java/org/apache/royale/compiler/internal/tree/as/TreeNode.java b/compiler/src/main/java/org/apache/royale/compiler/internal/tree/as/TreeNode.java
index e9878fc..b3c0755 100644
--- a/compiler/src/main/java/org/apache/royale/compiler/internal/tree/as/TreeNode.java
+++ b/compiler/src/main/java/org/apache/royale/compiler/internal/tree/as/TreeNode.java
@@ -148,15 +148,14 @@ public abstract class TreeNode extends NodeBase
     {
         if (children instanceof List)
         {
-            //TODO update offsets
-            CheapArray.replace(position, child, emptyNodeArray);
+            ((List) children).add(position, child);
         }
         else
         {
             ArrayList<IASNode> newArray = new ArrayList<IASNode>();
             Collections.addAll(newArray, (IASNode[])children);
             newArray.add(position, child);
-            children = newArray.toArray();
+            children = CheapArray.toArray(newArray, emptyNodeArray);
         }
         child.setParent(this);
     }
@@ -245,7 +244,7 @@ public abstract class TreeNode extends NodeBase
         if (child != null)
         {
             //use safe remove call
-            CheapArray.remove(child, children, emptyNodeArray);
+            children = CheapArray.remove(child, children, emptyNodeArray);
             child.setParent(null);
         }
     }
diff --git a/compiler/src/main/java/org/apache/royale/utils/ASTUtil.java b/compiler/src/main/java/org/apache/royale/utils/ASTUtil.java
new file mode 100644
index 0000000..8cd5aaf
--- /dev/null
+++ b/compiler/src/main/java/org/apache/royale/utils/ASTUtil.java
@@ -0,0 +1,43 @@
+/*
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+package org.apache.royale.utils;
+
+
+import org.apache.royale.compiler.internal.tree.as.FunctionNode;
+import org.apache.royale.compiler.projects.ICompilerProject;
+
+public class ASTUtil {
+    
+    public static void processFunctionNode(FunctionNode funcNode, ICompilerProject project, boolean dynamicTarget) {
+        if (funcNode.isImplicit()) {
+            //we don't care about implicit nodes (e.g. generated constructors)
+            //and they may not have all the 'real' definition data populated anyway, such as contained ASScope
+            return;
+        }
+        ArrayLikeUtil.preProcessGetterSetters(project, funcNode.getScopedNode(), null);
+        if (funcNode.getDefinition().getContainedScope().getHasForEach()) {
+            //pre-process for 'ArrayLike' for-each mutations
+            ArrayLikeUtil.preProcessForEachLoops(funcNode.getDefinition().getContainedScope(), project, dynamicTarget);
+        }
+       
+    }
+    
+    
+}
diff --git a/compiler/src/main/java/org/apache/royale/utils/ArrayLikeUtil.java b/compiler/src/main/java/org/apache/royale/utils/ArrayLikeUtil.java
new file mode 100644
index 0000000..e32b891
--- /dev/null
+++ b/compiler/src/main/java/org/apache/royale/utils/ArrayLikeUtil.java
@@ -0,0 +1,900 @@
+/*
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.apache.royale.utils;
+
+import org.apache.royale.compiler.common.SourceLocation;
+import org.apache.royale.compiler.constants.IASLanguageConstants;
+import org.apache.royale.compiler.constants.IMetaAttributeConstants;
+import org.apache.royale.compiler.definitions.*;
+import org.apache.royale.compiler.definitions.metadata.IMetaTag;
+import org.apache.royale.compiler.internal.definitions.FunctionDefinition;
+import org.apache.royale.compiler.internal.parsing.as.ASToken;
+import org.apache.royale.compiler.internal.parsing.as.ASTokenTypes;
+import org.apache.royale.compiler.internal.scopes.ASProjectScope;
+import org.apache.royale.compiler.internal.scopes.ASScope;
+import org.apache.royale.compiler.internal.semantics.PostProcessStep;
+import org.apache.royale.compiler.internal.semantics.SemanticUtils;
+import org.apache.royale.compiler.internal.tree.as.*;
+import org.apache.royale.compiler.problems.ArrayLikeConfigurationErrorProblem;
+import org.apache.royale.compiler.problems.ArrayLikeUsageErrorProblem;
+import org.apache.royale.compiler.problems.ICompilerProblem;
+import org.apache.royale.compiler.projects.ICompilerProject;
+import org.apache.royale.compiler.tree.as.*;
+
+import java.util.*;
+
+/**
+ * Support for processsing a variety of characteristics via metadata on classes or interfaces
+ * that translate Array like access and iteration from as3 language level to implementation support for
+ * the annotated definition(s)
+ * example:
+ * [RoyaleArrayLike(getValue="getItemAt",setValue="setItemAt",length="length",lengthAccess="getter")]
+ *
+ * This class contains utilities to support both mutating AST of 'for each' loops with target (rightOperand) types that
+ * are classes or interfaces which have [RoyaleArrayLike] metadata. It also supports mutation of array-like
+ * dynamic access or assignment when the dynamic access keys are numeric types (int, uint, Number).
+ * Subclasses of RoyaleArrayLike classes or implementers of interfaces which are RoyaleArrayLike
+ * 'inherit' the RoyaleArrayLike special treatment by the compiler.
+ */
+public class ArrayLikeUtil
+{
+    
+    //the metadata arg that specifies the member name for accessing length value of and instance of the decorated type
+    final private static String LENGTH_ARG = "length";
+    //the metadata arg that specifies the type of access that the length arg specifies ('getter' or 'method', default value is 'getter', see defaults below)
+    final private static String LENGTH_ACCESS_ARG = "lengthAccess";
+    //the metadata arg that specifies how numeric index level 'get' is performed
+    final private static String GETVALUE_ARG = "getValue";
+    //the metadata arg that specifies how numeric index level 'set' is performed
+    final private static String SETVALUE_ARG = "setValue";
+    //the metadata arg that specifies the order of arguments used in the setter ('value,index' or 'index,value', default value is 'value,index')
+    final private static String SETTER_ARG_SEQUENCE_ARG = "setterArgSequence";
+    
+    
+    //the valid SETTER_ARG_SEQUENCE_ARG value options.
+    //Whether the 'setter' has the index arg first or the value to be set at the specified index (or vice versa)
+    //checking the method signature in the class to determine this is not appropriate, because in the general case
+    //there may be classes whose values are also typed as numeric, so it should be specified. (default is value,index, see defaults below)
+    final private static String SETTER_ARG_SEQUENCE_VALUE_INDEX = "value,index";
+    final private static String SETTER_ARG_SEQUENCE_INDEX_VALUE = "index,value";
+    
+    //the valid options for length access. Used when generated iteration support in for-each loops
+    final private static String LENGTH_ACCESS_METHOD = "method";
+    final private static String LENGTH_ACCESS_GETTER = "getter";
+    
+    //a value for either getValue or setValue that indicates regular array access should be used,
+    //This results in no alteration compared to original source code for either the 'getter' or 'setter'
+    // (or both, if both 'getValue' and 'setValue' have this value)
+    final private static String INDEX_ACCESS_UNCHANGED = "[]";
+    
+    //default values for certain args that can be omitted.
+    final private static String LENGTH_ACCESS_DEFAULT = LENGTH_ACCESS_GETTER;
+    final private static String SETTER_ARG_SEQUENCE_DEFAULT = SETTER_ARG_SEQUENCE_VALUE_INDEX;
+    
+    //this is currently 'assumed safe', based on the chance of naming collision with an actual user-coded name
+    //being extremely remote. It also assumes that the target platform will optimize its own local variable names,
+    //there is no attempt to make a 'short' name.
+    final private static String ARRAY_LIKE_FOREACH_ITERATOR_VARNAME_BASE = "royale$for$Each$Iterator";
+    
+    /**
+     * @todo Consider the possibility of specific iterator factories instead of one generic function for all... the qName for the specific function could be specified in the Metadata def.
+     * These could simply take the instance as an argument and already know the way to construct the 'iterator' because they are specific
+     * (e.g. collections could have a specific iterator function, XMLish its own, BinaryData its own etc).
+     */
+    private final static String ARRAYLIKE_GENERIC_SUPPORT_ITERATOR_FACTORY_FUNC_QNAME = "org.apache.royale.language.iterator.arrayLike";
+    private final static String ARRAYLIKE_GENERIC_SUPPORT_ITERATOR_FACTORY_FUNC = "arrayLike";
+    
+    final static String ARRAYLIKE_HAS_NEXT = "hasNext";
+    final static String ARRAYLIKE_GET_NEXT = "next";
+    
+    private static String wrapQuotes(String original){
+        return "'" + original + "'";
+    }
+    
+    public static void preProcessGetterSetters(ICompilerProject project, ContainerNode node, NodeBase lower ) {
+        NodeBase parent = lower != null ? lower : node;
+        int childCount = parent.getChildCount();
+        for (int i=0; i < childCount; i++) {
+            IASNode child = parent.getChild(i);
+            
+            if (child instanceof BinaryOperatorAssignmentNode) {
+                if (((BinaryOperatorAssignmentNode) child).getLeftOperandNode() instanceof DynamicAccessNode) {
+                    DynamicAccessNode dynNode = (DynamicAccessNode) ((BinaryOperatorAssignmentNode) child).getLeftOperandNode();
+                    if (isArrayLikeCandidate(dynNode, project)) {
+                        IDefinition target = dynNode.getLeftOperandNode().resolveType(project);
+                        IDefinition metaSource = ArrayLikeUtil.resolveArrayLikeDefinitionSource(target, project);
+                        IMetaTag arrayLikeTag = ArrayLikeUtil.getArrayLikeMetaData(metaSource);
+                        String setterArg;
+                        setterArg = getSetterArg(arrayLikeTag);
+                        if (!setterArg.equals("[]")) { //otherwise we leave it as is
+                            //get the FunctionCallNode replacement's arguments from the original assignment
+                            ExpressionNodeBase indexArg = (ExpressionNodeBase) dynNode.getRightOperandNode();
+                            ExpressionNodeBase valueArg = (ExpressionNodeBase) ((BinaryOperatorAssignmentNode) child).getRightOperandNode();
+                            
+                            FunctionCallNode replacement = createDynamicAccessMutation(dynNode, setterArg);
+                            
+                            String argSequence = getSetterArgSequenceArg(arrayLikeTag);
+                            if (argSequence.equals(SETTER_ARG_SEQUENCE_VALUE_INDEX)) {
+                                replacement.getArgumentsNode().addChild(valueArg);
+                                replacement.getArgumentsNode().addChild(indexArg);
+                            } else { //index,value instead
+                                replacement.getArgumentsNode().addChild(indexArg);
+                                replacement.getArgumentsNode().addChild(valueArg);
+                            }
+                            replacement.getArgumentsNode().setParent(replacement);
+                            if (parent instanceof ContainerNode) {
+                                ((ContainerNode) parent).removeItem((NodeBase) child);
+                                ((ContainerNode) parent).addChild(replacement, i);
+                                ((BinaryOperatorAssignmentNode) child).setParent(null);
+                                child = replacement; //we need to allow for recursive checking of the assigned value as well as some other 'getter'
+                            } /*else {
+                                System.out.println("Problem: " + (node.getNodeID() + ":" + node.getLine()));
+                            }*/
+                        }
+                    }
+                }
+            } else if (child instanceof DynamicAccessNode) {
+                DynamicAccessNode dynNode = (DynamicAccessNode)child;
+                if (isArrayLikeCandidate(dynNode, project)) {
+                    if (parent instanceof BaseStatementExpressionNode) {
+                        IDefinition target = dynNode.getLeftOperandNode().resolveType(project);
+                        IDefinition metaSource = ArrayLikeUtil.resolveArrayLikeDefinitionSource(target, project);
+                        IMetaTag arrayLikeTag = ArrayLikeUtil.getArrayLikeMetaData(metaSource);
+                        String getterArg = getGetterArg(arrayLikeTag);
+                        if (!getterArg.equals("[]")) {
+                            ExpressionNodeBase indexArg = (ExpressionNodeBase) dynNode.getRightOperandNode();
+                            FunctionCallNode replacement = createDynamicAccessMutation(dynNode, getterArg);
+                            replacement.getArgumentsNode().addChild(indexArg);
+                            replacement.getArgumentsNode().setParent(replacement);
+                            ((BaseStatementExpressionNode) parent).setStatementExpression(replacement);
+                            replacement.setParent( parent);
+                            dynNode.setParent(null);
+                            child = replacement;
+                        }
+                    }
+                    else if (parent instanceof ContainerNode) {
+                        IDefinition target = dynNode.getLeftOperandNode().resolveType(project);
+                        IDefinition metaSource = ArrayLikeUtil.resolveArrayLikeDefinitionSource(target, project);
+                        IMetaTag arrayLikeTag = ArrayLikeUtil.getArrayLikeMetaData(metaSource);
+                        String getterArg = getGetterArg(arrayLikeTag);
+                        if (!getterArg.equals("[]")) { //otherwise we leave it as is
+                            ExpressionNodeBase indexArg = (ExpressionNodeBase) dynNode.getRightOperandNode();
+                            FunctionCallNode replacement = createDynamicAccessMutation(dynNode, getterArg);
+                            replacement.getArgumentsNode().addChild(indexArg);
+                            replacement.getArgumentsNode().setParent(replacement);
+                            ((ContainerNode) parent).removeItem((NodeBase) child);
+                            ((ContainerNode) parent).addChild(replacement, i);
+                            dynNode.setParent(null);
+                            child = replacement;
+                        }
+                    } else if (parent instanceof BinaryOperatorNodeBase) {
+                        BinaryOperatorNodeBase binaryOp = (BinaryOperatorNodeBase) parent;
+                        IDefinition target = dynNode.getLeftOperandNode().resolveType(project);
+                        IDefinition metaSource = ArrayLikeUtil.resolveArrayLikeDefinitionSource(target, project);
+                        IMetaTag arrayLikeTag = ArrayLikeUtil.getArrayLikeMetaData(metaSource);
+                        String getterArg = getGetterArg(arrayLikeTag);
+                        if (!getterArg.equals("[]")) { //otherwise we leave it as is
+                            ExpressionNodeBase indexArg = (ExpressionNodeBase) dynNode.getRightOperandNode();
+                            FunctionCallNode replacement = createDynamicAccessMutation(dynNode, getterArg);
+                            replacement.getArgumentsNode().addChild(indexArg);
+                            replacement.getArgumentsNode().setParent(replacement);
+                            if (binaryOp.getLeftOperandNode() == child) {
+                                binaryOp.setLeftOperandNode(replacement);
+                            } else {
+                                binaryOp.setRightOperandNode(replacement);
+                            }
+                            replacement.setParent(binaryOp);
+                            dynNode.setParent(null);
+                            child = replacement;
+                        }
+                    } else if (parent instanceof UnaryOperatorNodeBase) {
+                        UnaryOperatorNodeBase unaryOperatorNodeBase = (UnaryOperatorNodeBase) parent;
+                        ArrayLikeUsageErrorProblem usageError;
+                        SourceLocation loc = new SourceLocation();
+                        loc.setSourcePath(unaryOperatorNodeBase.getSourcePath());
+                        if (unaryOperatorNodeBase.getOperandNode().getAbsoluteStart() > unaryOperatorNodeBase.getOperatorAbsoluteStart()) {
+                            //prepended unary operator
+                            loc.setColumn(unaryOperatorNodeBase.getColumn());
+                            loc.setLine(unaryOperatorNodeBase.getLine());
+                            loc.setEndColumn(unaryOperatorNodeBase.getOperatorAbsoluteStart()-unaryOperatorNodeBase.getOperandNode().getAbsoluteStart());
+                            loc.setEndLine(unaryOperatorNodeBase.getLine());
+                            loc.setStart(unaryOperatorNodeBase.getStart());
+                            loc.setEnd(unaryOperatorNodeBase.getOperatorAbsoluteStart()-1);
+                        } else {
+                            //post-pended unary operator
+                            loc.setColumn(unaryOperatorNodeBase.getOperandNode().getEndColumn());
+                            loc.setLine(unaryOperatorNodeBase.getOperandNode().getEndLine());
+                            loc.setEndColumn(unaryOperatorNodeBase.getEndColumn());
+                            loc.setEndLine(unaryOperatorNodeBase.getEndLine());
+                            loc.setStart(unaryOperatorNodeBase.getOperandNode().getAbsoluteStart());
+                            loc.setStart(unaryOperatorNodeBase.getAbsoluteEnd());
+                        }
+                        usageError = new ArrayLikeUsageErrorProblem(loc,
+                                "Unary Operation not supported");
+                       
+                        project.getProblems().add(usageError);
+                    } /*else {
+                        System.out.println("Skipped: " + (child.getNodeID()));
+                    }*/
+                
+                }
+            }/* else {
+                System.out.println("Skipped: " + (child.getNodeID()));
+            }*/
+    
+            if (child instanceof FixedChildrenNode || child instanceof TreeNode) {
+                preProcessGetterSetters(/*searchScope,*/ project, node, (NodeBase) child);
+            }
+        }
+    }
+    
+    private static boolean isArrayLikeCandidate(DynamicAccessNode dynNode, ICompilerProject project) {
+        boolean isCandidate = false;
+        //first check to see if the access is numeric... if it is not then we consider that it is not a candidate
+        IDefinition dynType = dynNode.getRightOperandNode().resolveType(project);
+        if (project.getBuiltinType(IASLanguageConstants.BuiltinType.NUMBER).equals(dynType)
+                || project.getBuiltinType(IASLanguageConstants.BuiltinType.UINT).equals(dynType)
+                || project.getBuiltinType(IASLanguageConstants.BuiltinType.INT).equals(dynType)
+        ) {
+            IDefinition target = dynNode.getLeftOperandNode().resolveType(project);
+            isCandidate = ArrayLikeUtil.isArrayLike(target, project);
+        }
+        return isCandidate;
+    }
+    
+    private static FunctionCallNode createDynamicAccessMutation(DynamicAccessNode original, String methodName){
+        ExpressionNodeBase base = (ExpressionNodeBase) original.getLeftOperandNode();
+        IdentifierNode methodCallName = new IdentifierNode(methodName);
+        MemberAccessExpressionNode nameNode;
+        nameNode = new MemberAccessExpressionNode(base, null, methodCallName);
+        base.setParent(nameNode);
+        methodCallName.setParent(nameNode);
+        FunctionCallNode replacement = new FunctionCallNode(nameNode);
+        nameNode.setParent(replacement);
+        //the scaffolding for the replacement is set up... adding the arguments will be managed by the call site
+        return replacement;
+    }
+    
+    /**
+     * main support for loop mutations to support RoyaleArrayLikes as targets of 'for each' loops
+     * @param searchScope
+     * @param project
+     * @param useDynamicAccess
+     */
+    public static void preProcessForEachLoops(ASScope searchScope,ICompilerProject project, boolean useDynamicAccess) {
+
+        ScopedBlockNode funcScopeNode = (ScopedBlockNode) searchScope.getScopeNode();
+        
+        IForLoopNode[] forEachs = searchScope.getForEachs(true);
+        List<IForLoopNode> forEachList = Arrays.asList(forEachs);
+        boolean importAdded = false;
+        ArrayList<String> usedIterators = new ArrayList<String>();
+        for (IForLoopNode loopNode : forEachList) {
+            int depth = 0;
+            IASNode nodeCheck = loopNode;
+            while (nodeCheck.getParent() != null && nodeCheck.getParent() != funcScopeNode) {
+                if (nodeCheck.getParent() instanceof IForLoopNode && forEachList.indexOf(nodeCheck.getParent()) != -1) {
+                    depth++;
+                }
+                nodeCheck = nodeCheck.getParent();
+            }
+            //create a valid name for the iterator at the current depth of for-each loops, re-using previous declared variables, where possible
+            String arrIter = ARRAY_LIKE_FOREACH_ITERATOR_VARNAME_BASE + depth;
+            IDefinition targetType;
+    
+            final IDefinition xmlListDef = project.getBuiltinType(IASLanguageConstants.BuiltinType.XMLLIST);
+            try {
+                //check the rightOperandType
+                BinaryOperatorInNode conditionalExpressions = (BinaryOperatorInNode) loopNode.getConditionalExpressionNodes()[0];
+                if (conditionalExpressions.getRightOperandNode() instanceof IFunctionCallNode) {
+                    IASNode nameNode = ((FunctionCallNode) conditionalExpressions.getRightOperandNode()).getNameNode();
+                    if (nameNode instanceof IMemberAccessExpressionNode) {
+                        IFunctionDefinition funcDef = (IFunctionDefinition) ((IMemberAccessExpressionNode) nameNode).getRightOperandNode().resolve(project);
+                        targetType = funcDef.resolveReturnType(project);
+                    } else {
+                        //back to error until we cover all cases
+                        targetType = conditionalExpressions.getRightOperandNode().resolveType(project);
+                    }
+                } else {
+                    if (conditionalExpressions.getRightOperandNode() instanceof IMemberAccessExpressionNode) {
+                        //this does not resolve... check for XMLish on the left
+                        targetType = conditionalExpressions.getRightOperandNode().resolveType(project);
+                        IExpressionNode left = ((IMemberAccessExpressionNode) conditionalExpressions.getRightOperandNode()).getLeftOperandNode();
+                        while (targetType == null && left != null) {
+                            targetType = left.resolveType(project);
+                            if (targetType == null) {
+                                if (left instanceof IMemberAccessExpressionNode || left instanceof IDynamicAccessNode) {
+                                    left = ((IBinaryOperatorNode) left).getLeftOperandNode();
+                                }
+                            } else {
+                                if (SemanticUtils.isXMLish(targetType, project)) {
+                                    //assume we have an XMLList
+                                    targetType = xmlListDef;
+                                }
+                                if (targetType != xmlListDef) {
+                                    //this does not make sense, so ignore it or add a problem?
+                                    targetType = null;
+                                    left = null;
+    
+                                } /*else {
+                                    System.out.println("Searching, found left target type to "+targetType.getQualifiedName());
+                                }*/
+    
+                            }
+                        }
+    
+                    } else {
+                        targetType = conditionalExpressions.getRightOperandNode().resolveType(project);
+                    }
+    
+                }
+    
+            } catch (Exception e) {
+               /* System.out.println("Failed to resolve the target type ");
+                System.out.println(e.getStackTrace());*/
+                continue;
+            }
+    
+            if (ArrayLikeUtil.isArrayLike(targetType, project)) {
+                //    System.out.println("processing ArrayLike "+targetType.getQualifiedName());
+        
+                if (!importAdded) {
+                    if (searchScope.getImports() != null) {
+                        List<String> imports = Arrays.asList(searchScope.getImports());
+                        if (imports.contains(ArrayLikeUtil.ARRAYLIKE_GENERIC_SUPPORT_ITERATOR_FACTORY_FUNC_QNAME)) {
+                            importAdded = true;
+                        }
+                    }
+                    if (!importAdded) {
+                        searchScope.addImport(ArrayLikeUtil.ARRAYLIKE_GENERIC_SUPPORT_ITERATOR_FACTORY_FUNC_QNAME);
+                        importAdded = true;
+                    }
+                }
+        
+        
+                ASProjectScope projectScope = (ASProjectScope) project.getScope();
+                boolean alreadyUsed = usedIterators.contains(arrIter); //otherwise we need a typed var declaration
+                if (!alreadyUsed)
+                    usedIterators.add(arrIter); // we can use it again without var declaration in the current scope
+        
+                IDefinition metaSource = ArrayLikeUtil.resolveArrayLikeDefinitionSource(targetType, project);
+                IMetaTag arrayLikeTag = ArrayLikeUtil.getArrayLikeMetaData(metaSource);
+        
+                //change the loop node to: for(arrIter:Object = arrayLike(instance,"{lengthCheck}","{getterCheck}" or (null if getterCheck=="[]"), Boolean(lengthAccess=="method")); arrIter.hasNext();)
+                // {
+                // {!alreadyUsed: var} originalName{!alreadyUsed: :OriginalType} =  arrIter.getNext();
+                // {originalBody Loop body}
+                // }
+        
+                ForLoopNode.ILoopMutation mutation = ArrayLikeUtil.createArrayLikeLoopMutation((ForLoopNode) loopNode, arrIter, useDynamicAccess);
+        
+                String generatorFuncName = ArrayLikeUtil.ARRAYLIKE_GENERIC_SUPPORT_ITERATOR_FACTORY_FUNC;
+                IdentifierNode funcName = new IdentifierNode(generatorFuncName);
+                FunctionCallNode specificIteratorFunc = new FunctionCallNode(funcName);
+                specificIteratorFunc.getNameNode().setParent(specificIteratorFunc);
+        
+                //now add the arguments
+                //use the original iteration target node {from: for each(iteratee in target) }, reparent it to the generator function args as the target 'instance'
+                specificIteratorFunc.getArgumentsNode().addItem((NodeBase) mutation.getIterationTarget());
+                specificIteratorFunc.getArgumentsNode().setParent(specificIteratorFunc);
+                //now add the metadata driven args
+                //length check:
+                LiteralNode metaArg = new LiteralNode(ILiteralNode.LiteralType.STRING, wrapQuotes(ArrayLikeUtil.getLengthArg(arrayLikeTag)));
+                metaArg.setSynthetic(true); //may not be needed
+                specificIteratorFunc.getArgumentsNode().addItem(metaArg);
+                //index getter:
+                String getter = ArrayLikeUtil.getGetterArg(arrayLikeTag);
+                if (getter.equals(INDEX_ACCESS_UNCHANGED)) {
+                    // pass null for the getter
+                    metaArg = new LiteralNode(ILiteralNode.LiteralType.NULL, "null");
+                    metaArg.setSynthetic(true); //may not be needed
+                } else {
+                    metaArg = new LiteralNode(ILiteralNode.LiteralType.STRING, wrapQuotes(ArrayLikeUtil.getGetterArg(arrayLikeTag)));
+                    metaArg.setSynthetic(true); //may not be needed
+                }
+                specificIteratorFunc.getArgumentsNode().addItem(metaArg);
+                //boolean indicating true if the length check is a method call, false if reguler 'getter' style:
+                metaArg = new LiteralNode(ILiteralNode.LiteralType.BOOLEAN, ArrayLikeUtil.getLengthAccessArg(arrayLikeTag).equals("method") ? "true" : "false");
+                metaArg.setSynthetic(true); //may not be needed
+                specificIteratorFunc.getArgumentsNode().addItem(metaArg);
+        
+                mutation.prepareConditionals(!alreadyUsed, specificIteratorFunc);
+                mutation.prepareContent();
+        
+                Collection<ICompilerProblem> problems = ((ForLoopNode) loopNode).processMutation(mutation, searchScope);
+                if (problems.size() > 0) {
+                    project.getProblems().addAll(problems);
+                }
+            }
+    
+            //@todo if the targetType is null we could output an opt-in or opt-out (via config) warning suggesting that Strongly typed for each loop targets are more reliable
+            //That would help people a lot when porting things to dynamic targets like js. for each(var something:Object in event.target.someField).... is sometimes a problem
+
+            
+            /*if (targetType == null){
+                System.out.println("FOUND NULL FOREACH TARGET TYPE AT "+loopNode.getLine()+","+loopNode.getSourcePath());
+            } else {
+                System.out.println("Target type was "+targetType.getQualifiedName() + " at " + loopNode.getLine() +"," +loopNode.getSourcePath());
+            }*/
+        }
+    
+    }
+    
+    private static HashMap<String, String> arrayLikeLookups = new HashMap<String, String>(20);
+
+    /**
+     *
+     * @param definition the definition to check
+     * @return true if this specific definition has the ArrayLike annotation
+     */
+    public static boolean definitionIsArrayLike(IDefinition definition) {
+        return ((definition instanceof IClassDefinition || definition instanceof IInterfaceDefinition) &&
+                definition.hasMetaTagByName(IMetaAttributeConstants.ATTRIBUTE_ARRAYLIKE));
+    }
+    
+    /**
+     * Method to check an annotated definition in the current project, and verify that it is valid
+     * This method poplulates the external problems collection argument with the first observed problem
+     * @param definition the definition to verify
+     * @param project the current project
+     * @param problems a problem list to populate with any detected issues
+     * @return boolean true if there were no issues, otherwise false
+     */
+    public static boolean validateArrayLikeDefinition(IDefinition definition, ICompilerProject project, List<ICompilerProblem> problems) {
+        IMetaTag arrayLikeTag  = definition.getMetaTagByName(IMetaAttributeConstants.ATTRIBUTE_ARRAYLIKE);
+        
+        //mandatory
+        String lengthCheck = arrayLikeTag.getAttributeValue(LENGTH_ARG);
+        boolean pass = true;
+        if (lengthCheck == null)  {
+            pass = false;
+            if (problems != null) {
+                problems.add(new ArrayLikeConfigurationErrorProblem(arrayLikeTag, "Missing '"+LENGTH_ARG+"' metadata argument"));
+            }
+        } //verify further after we check the lengthAccess arg
+        if (pass) {
+            String getterCheck = arrayLikeTag.getAttributeValue(GETVALUE_ARG);
+            if (getterCheck == null)  {
+                pass = false;
+                if (problems != null) {
+                    problems.add(new ArrayLikeConfigurationErrorProblem(arrayLikeTag, "Missing '"+GETVALUE_ARG+"' metadata argument"));
+                }
+            } else {
+                //check the definition
+                if (getterCheck != INDEX_ACCESS_UNCHANGED) { //'[]' is a special case where regular Array access is not changed
+                    //@todo extra safety: check the definition has the method that is specified in the metadata, add a problem if not.
+                }
+            }
+        }
+        String setterCheck = null;
+        if (pass) {
+            setterCheck = arrayLikeTag.getAttributeValue(SETVALUE_ARG);
+            if (setterCheck == null)  {
+                pass = false;
+                if (problems != null) {
+                    problems.add(new ArrayLikeConfigurationErrorProblem(arrayLikeTag, "Missing '"+SETVALUE_ARG+"' metadata argument"));
+                }
+            } else {
+                //check the definition
+                if (setterCheck != INDEX_ACCESS_UNCHANGED) {//'[]' is a special case where regular Array access is not changed
+                    //@todo extra safety: check the definition has the method that is specified in the metadata, add a problem if not.
+                }
+            }
+        }
+        if (pass) {
+            if (!setterCheck.equals(INDEX_ACCESS_UNCHANGED)) {//otherwise we can ignore the setter arg sequence, even if it is (incorrectly) specified.
+                String setterSequenceCheck = arrayLikeTag.getAttributeValue(SETTER_ARG_SEQUENCE_ARG);
+                if (setterSequenceCheck == null) {
+                    setterSequenceCheck = SETTER_ARG_SEQUENCE_DEFAULT;
+                }
+                if (!setterSequenceCheck.equals(SETTER_ARG_SEQUENCE_VALUE_INDEX) && !setterSequenceCheck.equals(SETTER_ARG_SEQUENCE_INDEX_VALUE))  {
+                    pass = false;
+                    if (problems != null) {
+                        problems.add(new ArrayLikeConfigurationErrorProblem(arrayLikeTag, "Missing '"+SETVALUE_ARG+"' metadata argument"));
+                    }
+                } else {
+                    //check the definition
+                    //@todo extra safety: check the definition has the method signature that is specified in the metadata, add a problem if not.
+                    //the index argument should be a numeric type, and the specified method should have the two arguments
+                }
+            }
+        }
+        if (pass) {
+            //can be method or getter, defaults to getter
+            String lengthAccess = arrayLikeTag.getAttributeValue(LENGTH_ACCESS_ARG);
+            if (lengthAccess == null) lengthAccess = LENGTH_ACCESS_DEFAULT;
+            //only "getter" or "method" are allowed options
+            if (!lengthAccess.equals(LENGTH_ACCESS_GETTER) && !lengthAccess.equals(LENGTH_ACCESS_METHOD)) {
+                pass = false;
+                if (problems != null) {
+                    //problems.add(new ArrayLikeConfigurationErrorProblem(definition.getNode(), "Metadata argument for 'lengthAccess' missing or invalid"));
+                    problems.add(new ArrayLikeConfigurationErrorProblem(arrayLikeTag, "Metadata argument for '"+LENGTH_ACCESS_ARG+"' missing or invalid"));
+                }
+            } else {
+                //@todo extra safety: check the definition has the length getter or method that is specified that is specified in the metadata, add a problem if not.
+            }
+        }
+        if (pass) {
+            ASProjectScope projectScope = ((ASProjectScope)project.getScope());
+            IDefinition def = projectScope.findDefinitionByName(ARRAYLIKE_GENERIC_SUPPORT_ITERATOR_FACTORY_FUNC_QNAME);
+            if (!(def instanceof FunctionDefinition)) {
+                //@todo extra safety: this may not be sufficient to verify a concrete reference
+                problems.add(new ArrayLikeConfigurationErrorProblem(arrayLikeTag, "The RoyaleArrayLike Tag is valid, but there is a missing concrete reference in the project to: "+ARRAYLIKE_GENERIC_SUPPORT_ITERATOR_FACTORY_FUNC_QNAME+ " (which is required)"));
+                pass = false;
+            }
+        }
+    
+        return pass;
+    }
+    
+    /**
+     * Checks whether an instance is ArrayLike at a usage site.
+     * This is typically either array access or assignment, or for the target of a 'for each' loop
+     * @param definition the definition check to verify if an instance is ArrayLike at a usage site
+     * @return true if this definition either has its own ArrayLike annotation, or inherits via its ancestors or interfaces
+     */
+    synchronized public static boolean isArrayLike(IDefinition definition, ICompilerProject project) {
+        if (definition != null) {
+            if (definition instanceof IClassDefinition) {
+                String qName = definition.getQualifiedName();
+                if (arrayLikeLookups.containsKey(qName)) return arrayLikeLookups.get(qName) != null;
+                return checkClass((IClassDefinition) definition, project);
+            } else if (definition instanceof IInterfaceDefinition) {
+                String qName = definition.getQualifiedName();
+                if (arrayLikeLookups.containsKey(qName)) return arrayLikeLookups.get(qName) != null;
+                return checkInterface((IInterfaceDefinition) definition, project);
+            }
+        }
+        return false;
+    }
+    
+    private static boolean checkClass(IClassDefinition definition, ICompilerProject project){
+        String qName = definition.getQualifiedName();
+        if (arrayLikeLookups.containsKey(qName)) return arrayLikeLookups.get(qName) != null;
+        boolean isArrayLike = false;
+        //check the class
+        isArrayLike = definition.hasMetaTagByName(IMetaAttributeConstants.ATTRIBUTE_ARRAYLIKE);
+        String originalQName = qName;
+        if (!isArrayLike) {
+            //check this definition's interfaces and their ancestry
+            Set<IInterfaceDefinition> interfaces = definition.resolveAllInterfaces(project);
+            for (IInterfaceDefinition interfaceDef:interfaces) {
+                isArrayLike = checkInterface(interfaceDef, project);
+                if (isArrayLike) {
+                    qName = arrayLikeLookups.get(interfaceDef.getQualifiedName());
+                    break;
+                }
+            }
+            
+            //check this definition's parent class (recursive check)
+            if (!isArrayLike) {
+                IClassDefinition baseClassDef = definition.resolveBaseClass(project);
+                if (baseClassDef != null) {
+                    isArrayLike = checkClass(baseClassDef, project);
+                    if (isArrayLike) {
+                        //we need to share the same lookup that was used when checking the parent chain
+                        qName = arrayLikeLookups.get(baseClassDef.getQualifiedName());
+                    }
+                }
+            }
+        }
+        arrayLikeLookups.put(originalQName, isArrayLike ? qName : null);
+        return isArrayLike;
+    }
+    
+    private static boolean checkInterface(IInterfaceDefinition interfaceDefinition, ICompilerProject project){
+        String qName = interfaceDefinition.getQualifiedName();
+        if (arrayLikeLookups.containsKey(qName)) return arrayLikeLookups.get(qName) != null;
+        
+        boolean isArrayLike = interfaceDefinition.hasMetaTagByName(IMetaAttributeConstants.ATTRIBUTE_ARRAYLIKE);
+        String originalQName = qName;
+        if (!isArrayLike) qName = null;
+        ArrayList<String> ancestry = null;
+        if (!isArrayLike) {
+            // if we find an ancestor in the interface extension chain, we can cache the lookups back to it as the source of ArrayLike data
+            ancestry = new ArrayList<String>();
+            Iterator<IInterfaceDefinition> interfaceIterator = interfaceDefinition.interfaceIterator(project, false);
+            while (interfaceIterator.hasNext()) {
+                interfaceDefinition = interfaceIterator.next();
+                qName = interfaceDefinition.getQualifiedName();
+                if (arrayLikeLookups.containsKey(qName) ){
+                    //we don't have to keep going, we now already know the answer
+                    qName = arrayLikeLookups.get(qName); //this will either be null or the correct mapping to the ArrayLike source definition qName
+                   
+                    break;
+                }
+                ancestry.add(qName);
+                isArrayLike = interfaceDefinition.hasMetaTagByName(IMetaAttributeConstants.ATTRIBUTE_ARRAYLIKE);
+                if (isArrayLike) break;
+                qName = null;
+            }
+        }
+        if (ancestry != null) {
+            //process ancestry lookups
+            for (String ancestorQName: ancestry) arrayLikeLookups.put(ancestorQName, qName);
+        }
+        
+        arrayLikeLookups.put(originalQName, qName);
+        return isArrayLike;
+    }
+    
+    public static IDefinition resolveArrayLikeDefinitionSource(IDefinition sourceDefinition, ICompilerProject project){
+        IDefinition metaSource = null;
+        if (sourceDefinition != null){
+            String sourceDefinitionQName = sourceDefinition.getQualifiedName();
+            if (arrayLikeLookups.containsKey(sourceDefinitionQName)) {
+                String resolvedQName = arrayLikeLookups.get(sourceDefinitionQName);
+                if (!sourceDefinitionQName.equals(resolvedQName)) {
+                    metaSource = project.resolveQNameToDefinition(resolvedQName);
+                } else {
+                    metaSource = sourceDefinition;
+                }
+            }
+        }
+        return metaSource;
+    }
+    
+    
+    
+    /**
+     * Before calling the following methods, isArrayLike should be verified, and the source of the metadata should be used
+     * via resolveArrayLikeDefinitionSource
+     * @param definition
+     * @return
+     */
+    public static IMetaTag getArrayLikeMetaData(IDefinition definition){
+        return definition.getMetaTagByName(IMetaAttributeConstants.ATTRIBUTE_ARRAYLIKE);
+    }
+    
+    public static String getLengthArg(IMetaTag arrayLikeTag) {
+        return arrayLikeTag.getAttributeValue(LENGTH_ARG);
+    }
+    
+    public static String getGetterArg(IMetaTag arrayLikeTag) {
+        return arrayLikeTag.getAttributeValue(GETVALUE_ARG);
+    }
+    
+    public static String getSetterArg(IMetaTag arrayLikeTag) {
+        return arrayLikeTag.getAttributeValue(SETVALUE_ARG);
+    }
+    
+    public static String getSetterArgSequenceArg(IMetaTag arrayLikeTag) {
+        String val = arrayLikeTag.getAttributeValue(SETTER_ARG_SEQUENCE_ARG);
+        if (val == null) val = SETTER_ARG_SEQUENCE_DEFAULT;
+        return val;
+    }
+    
+    public static String getLengthAccessArg(IMetaTag arrayLikeTag) {
+        String val = arrayLikeTag.getAttributeValue(LENGTH_ACCESS_ARG);
+        if (val == null) val = LENGTH_ACCESS_DEFAULT;
+        return val;
+    }
+    
+    
+    public static ArrayLikeLoopMutation createArrayLikeLoopMutation(ForLoopNode loopNode, String arrIterName, boolean useDynamicAccess) {
+        return new ArrayLikeLoopMutation(loopNode, arrIterName, useDynamicAccess);
+    }
+
+}
+
+
+/**
+ * Support for mutating a 'for each' loop with the following signature
+ * for each(var iteratee:IterateeType in someInstance){ //swf has implicit cast here.
+ *     {loop body}
+ * }
+ *
+ * into the following style of loop:
+ * for(var iteratorVarName:Object=arraylike(someInstance, { plus metadata driven data here}); iteratorVarName.hasNext(); ) {
+ *     var iteratee:IterateeType = iteratorVarName.getNext(); //with implicit cast here
+ *      {loop body}
+ * }
+ * someInstance is a class annotated with [RoyaleArrayLike] metadata that determines its eligibility for applying this change
+ * and also the specific data to pass to the arrayLike function which serves as an 'iterator factory' for the above 'for each' support
+ * An example would be [RoyaleArrayLike(getValue="getItemAt",setValue="setItemAt",setterArgSequence="item,index",length="length",lengthAccess="getter")]
+ * (The setValue and setterArgSequence metatag params are not needed in terms of for-each support... they, along with the getValue arg are to support
+ * transforming Array Access via [numericKey] Dynamic access to method call access for setting and getting values)
+ * This only works when the compiler knows the type of 'someInstance', otherwise it cannot check to see if it has an ArrayLike metatag
+ */
+class ArrayLikeLoopMutation implements ForLoopNode.ILoopMutation {
+    
+    
+    /**
+     * Constructor.
+     */
+    public ArrayLikeLoopMutation(ForLoopNode target, String iteratorVarName, boolean useDynamicAccess)
+    {
+        super();
+        this.target = target;
+        this.iteratorVarName = iteratorVarName;
+        this.useDynamicAccess = useDynamicAccess;
+    }
+    
+    final private IForLoopNode.ForLoopKind kind = IForLoopNode.ForLoopKind.FOR;
+    private boolean useDynamicAccess;
+    private String iteratorVarName;
+    private NodeBase iteratee;
+    private FunctionCallNode iterateeValueNode;
+    private ForLoopNode target;
+    private ArrayList<NodeBase> analyzeRequests;
+    private ContainerNode mutatedConditionals = new ContainerNode();
+    
+    public boolean isValid(){
+        return mutatedConditionals !=null
+                && mutatedConditionals.getChildCount() == 3
+                && iteratee != null;
+    }
+    
+    @Override
+    public List<NodeBase> getAnalyzeRequests() {
+        return analyzeRequests;
+    }
+    
+    private FunctionCallNode createIteratorCall(String methodName){
+        IdentifierNode leftOp = new IdentifierNode(this.iteratorVarName);
+        ExpressionNodeBase nameNode;
+        if (useDynamicAccess) {
+            //this is the easiest way to keep things lightweight for JS without needing an actual iterator class or interface
+            //use iterator['hasNext']()/iterator['getNext']() etc
+            LiteralNode rightOp = new LiteralNode(ILiteralNode.LiteralType.STRING, "'"+methodName+"'");
+            DynamicAccessNode dynNode = new DynamicAccessNode(leftOp);
+            dynNode.setRightOperandNode(rightOp);
+            rightOp.setParent(dynNode);
+            leftOp.setParent(dynNode);
+            nameNode = dynNode;
+        } else {
+            IdentifierNode rightOp = new IdentifierNode(methodName);
+            nameNode = new MemberAccessExpressionNode(leftOp,null, rightOp);
+            leftOp.setParent(nameNode);
+            rightOp.setParent(nameNode);
+        }
+        FunctionCallNode functionCallNode = new FunctionCallNode(nameNode);
+        nameNode.setParent(functionCallNode);
+        functionCallNode.getArgumentsNode().setParent(functionCallNode);
+        return functionCallNode;
+    }
+    
+    private NodeBase setupIterator(boolean firstUse, FunctionCallNode assignedValue){
+        
+        IdentifierNode varName = new IdentifierNode(this.iteratorVarName);
+        
+        NodeBase setupNode;
+        if (firstUse) {
+            //create something like: var iteratorName:Object = {functionCallNode}
+            IdentifierNode typeNode = new IdentifierNode(IASLanguageConstants.Object);
+            VariableNode varNode =  new VariableNode(varName, typeNode);
+            varNode.setKeyword(new ASToken(ASToken.TOKEN_KEYWORD_VAR, -1,-1,-1,-1,"var"));
+            varNode.getKeywordNode().setParent(varNode);
+            varNode.setAssignedValue(null, assignedValue);
+            //explicitly set parent <--> child relationships
+            varNode.addChild(varNode.getKeywordNode());
+            varNode.addChild(varName);
+            varNode.addChild(typeNode);
+            varNode.addChild(assignedValue);
+            setupNode = new VariableExpressionNode(varNode);
+        } else {
+            //recycledIteratorName = {functionCallNode}
+            ASToken assignOp = new ASToken(ASTokenTypes.TOKEN_OPERATOR_ASSIGNMENT,-1,-1,-1,-1,"=");
+            setupNode = new BinaryOperatorAssignmentNode(assignOp, varName, assignedValue);
+            //explicitly set parent <--> child relationships
+            varName.setParent(setupNode);
+            assignedValue.setParent(setupNode);
+        }
+        return setupNode;
+    }
+    
+    private NodeBase setupIteratee(){
+        //original part of conditionals group should be either a VariableExpressionNode or an IdentifierNode
+        IExpressionNode original = ((BinaryOperatorInNode)(target.getConditionalExpressionNodes()[0])).getLeftOperandNode();
+        NodeBase setupNode;
+        FunctionCallNode assignedValue = createIteratorCall(ArrayLikeUtil.ARRAYLIKE_GET_NEXT);
+        
+        if (original instanceof VariableExpressionNode) {
+            VariableNode varNode = (VariableNode)((VariableExpressionNode) original).getTargetVariable();
+            //add the assigned value
+            varNode.setAssignedValue(null, assignedValue);
+            assignedValue.setParent(varNode);
+            setupNode = varNode;
+        } else if (original instanceof IdentifierNode) {
+            ASToken assignOp = new ASToken(ASTokenTypes.TOKEN_OPERATOR_ASSIGNMENT,-1,-1,-1,-1,"=");
+            BinaryOperatorAssignmentNode assignmentNode = new BinaryOperatorAssignmentNode(assignOp,(IdentifierNode)original, assignedValue);
+            ((IdentifierNode) original).setParent(assignmentNode);
+            assignedValue.setParent(assignmentNode);
+            setupNode = assignmentNode;
+        } else {
+            setupNode = null;
+            //System.out.println("setupIteratee::UNEXPECTED");
+        }
+        if (setupNode != null) iterateeValueNode = assignedValue; //so we can run analyze on only the new content 'assignedValue'
+        return setupNode;
+    }
+    
+    public ForLoopNode getLoopTarget(){
+        return target;
+    }
+    
+    public IExpressionNode getIterationTarget(){
+        //don't use copy/clone
+        return ((BinaryOperatorInNode)(target.getConditionalExpressionNodes()[0])).getRightOperandNode();
+    }
+    
+    public void prepareConditionals(boolean firstUse, FunctionCallNode factoryFuncCall){
+        assert !conditionalsChanged : "populateConditionals was already called";
+        //1st part of the substituted for(;;) loop
+        mutatedConditionals.addItem(setupIterator(firstUse, factoryFuncCall));
+        //2nd part of the  substituted for(;;) loop
+        mutatedConditionals.addItem(createIteratorCall(ArrayLikeUtil.ARRAYLIKE_HAS_NEXT));
+        //3rd part of the substituted for(;;) loop is a NilNode in this case
+        mutatedConditionals.addItem(new NilNode());
+        conditionalsChanged = true;
+    }
+    
+    public void prepareContent(){
+        assert !contentsChanged : "populateContent was already called";
+        iteratee = setupIteratee();
+        contentsChanged = iteratee != null;
+    }
+    
+    public void processConditionals(ASScope scope, Collection<ICompilerProblem> problems){
+        ContainerNode conditionals = target.getConditionalsContainerNode();
+        conditionals.removeAllChildren();
+        conditionals.addChild((NodeBase) mutatedConditionals.getChild(0),0);
+        conditionals.addChild((NodeBase) mutatedConditionals.getChild(1),1);
+        conditionals.addChild((NodeBase) mutatedConditionals.getChild(2),2);
+        
+        if (analyzeRequests == null) {
+            analyzeRequests = new ArrayList<NodeBase>();
+        }
+        for (int i=0;i<3;i++) {
+            analyzeRequests.add((NodeBase) conditionals.getChild(i));
+        }
+    }
+    
+    public void processContents(ASScope scope, Collection<ICompilerProblem> problems){
+        BlockNode block = target.getContentsNode();
+        //'insert' at position 0 to prepend this to the original loop's block content
+        block.addChild(iteratee, 0);
+        EnumSet<PostProcessStep> set = EnumSet.of(
+                PostProcessStep.POPULATE_SCOPE);
+        //only add the iteratee to the analysis. re-evaluating the original variable node (if it is not a pre-existing identifier)
+        //would add it to the scope's definition store a second time, and we don't want that (probably no harm, but it would add a warning for duplicate variable declaration).
+        if (analyzeRequests == null) {
+            analyzeRequests = new ArrayList<NodeBase>();
+        }
+        analyzeRequests.add(iterateeValueNode);
+    }
+    
+    @Override
+    public IForLoopNode.ForLoopKind getKind() {
+        return kind;
+    }
+    
+    private boolean conditionalsChanged;
+    @Override
+    public boolean mutatesConditionals() {
+        return conditionalsChanged;
+    }
+    
+    private boolean contentsChanged;
+    @Override
+    public boolean mutatesContents() {
+        return contentsChanged;
+    }
+    
+}