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

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

commit 308d8867b923c0edff4bf814fa89a175123dcf37
Author: Josh Tynjala <[email protected]>
AuthorDate: Tue Apr 21 14:08:25 2026 -0700

    JSDynamicAccessOverride, JSForInOverride, and JSForEachOverride metadata: 
optionally emit custom method calls when specific syntax is encountered
    
    Intended to allow folks using Royale to emulate classes like 
flash.utils.Dictionary and flash.utils.ByteArray, which may support custom 
behaviors for dynamic access with [] square brackets, for-in loop, and for-each 
loop syntax.
    
    JSDynamicAccessOverride supports getMethod and setMethod properties to get 
and set a key, deleteMethod to delete/remove a key, and inMethod to check if a 
key exists.
    
    JSForInOverride supports the iteratorMethod property to create an iterator 
object with the target's keys. iteratorNextMethod gets the next object from the 
iterator. iteratorHasNextMethod or iteratorDoneMethod is used to determine if a 
next object exists. iteratorHasNextMethod and iteratorDoneMethod are considered 
optional. If they are not defined, a null result from iteratorNextMethod will 
be considered the end of the iterator.
    
    JSForEachOverride supports the same methods, but iterates over the values 
instead of the keys. If JSForEachOverride is omitted, the compiler will 
intelligently use JSForInOverride to loop over the keys, and then get each 
value dynamically at runtime (possibly using JSDynamicAccessOverride, if 
defined).
---
 RELEASE_NOTES.md                                   |   1 +
 .../constants/IJSMetaAttributeConstants.java       |  43 +++
 .../codegen/js/jx/BinaryOperatorEmitter.java       |  91 +++++-
 .../internal/codegen/js/jx/ClassEmitter.java       |  41 +++
 .../codegen/js/jx/DynamicAccessEmitter.java        |  21 ++
 .../internal/codegen/js/jx/ForEachEmitter.java     | 337 +++++++++++++++++++++
 .../internal/codegen/js/jx/ForLoopEmitter.java     | 183 ++++++++++-
 .../codegen/js/jx/UnaryOperatorEmitter.java        |  35 ++-
 .../problems/MissingMetaTagAttributeProblem.java   |  38 +++
 9 files changed, 781 insertions(+), 9 deletions(-)

diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index efcaa3616..c1d1bc817 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -79,6 +79,7 @@ Apache Royale Compiler 1.0.0
 - compiler: Fixed namespace URI when namespace is defined in package.
 - compiler: Fixed warning for comparison of boolean or numeric type with 
`null` when using `-js-default-initializers=false` compiler option.
 - compiler: Fixed JavaScript code generation for E4X wildcard (.*) syntax.
+- compiler: Added `[JSDynamicOverride]`, `[JSForInOverride]`, and 
`[JSForEachOverride]` to customize the behavior of certain syntax when 
targeting JavaScript. Useful for emulating the full features of `Dictionary` 
and `ByteArray` from SWF.
 - debugger: Added missing isolate ID to SWF load and unload events.
 - debugger: Fixed debugger targeting the current JDK version instead of the 
intended minimum JDK version.
 - debugger: Fixed localized messages appearing as unprocessed tokens.
diff --git 
a/compiler-jx/src/main/java/org/apache/royale/compiler/constants/IJSMetaAttributeConstants.java
 
b/compiler-jx/src/main/java/org/apache/royale/compiler/constants/IJSMetaAttributeConstants.java
new file mode 100644
index 000000000..0b11f585c
--- /dev/null
+++ 
b/compiler-jx/src/main/java/org/apache/royale/compiler/constants/IJSMetaAttributeConstants.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.compiler.constants;
+
+public interface IJSMetaAttributeConstants
+{
+    // [JSDynamicOverride]
+    static final String ATTRIBUTE_DYNAMIC_OVERRIDE = "JSDynamicOverride";
+    static final String NAME_DYNAMIC_OVERRIDE_GET_METHOD = "getMethod";
+    static final String NAME_DYNAMIC_OVERRIDE_SET_METHOD = "setMethod";
+    static final String NAME_DYNAMIC_OVERRIDE_DELETE_METHOD = "deleteMethod";
+    static final String NAME_DYNAMIC_OVERRIDE_IN_METHOD = "inMethod";
+
+    // [JSForInOverride]
+    static final String ATTRIBUTE_FOR_IN_OVERRIDE = "JSForInOverride";
+    static final String NAME_FOR_IN_OVERRIDE_ITERATOR_METHOD = 
"iteratorMethod";
+    static final String NAME_FOR_IN_OVERRIDE_ITERATOR_NEXT_METHOD = 
"iteratorNextMethod";
+    static final String NAME_FOR_IN_OVERRIDE_ITERATOR_HAS_NEXT_METHOD = 
"iteratorHasNextMethod";
+    static final String NAME_FOR_IN_OVERRIDE_ITERATOR_DONE_METHOD = 
"iteratorDoneMethod";
+
+    // [JSForEachOverride]
+    static final String ATTRIBUTE_FOR_EACH_OVERRIDE = "JSForEachOverride";
+    static final String NAME_FOR_EACH_OVERRIDE_ITERATOR_METHOD = 
"iteratorMethod";
+    static final String NAME_FOR_EACH_OVERRIDE_ITERATOR_NEXT_METHOD = 
"iteratorNextMethod";
+    static final String NAME_FOR_EACH_OVERRIDE_ITERATOR_HAS_NEXT_METHOD = 
"iteratorHasNextMethod";
+}
diff --git 
a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/BinaryOperatorEmitter.java
 
b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/BinaryOperatorEmitter.java
index 816e6bc6b..904909e4f 100644
--- 
a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/BinaryOperatorEmitter.java
+++ 
b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/BinaryOperatorEmitter.java
@@ -21,6 +21,7 @@ package org.apache.royale.compiler.internal.codegen.js.jx;
 
 import org.apache.royale.compiler.codegen.ISubEmitter;
 import org.apache.royale.compiler.codegen.js.IJSEmitter;
+import org.apache.royale.compiler.constants.IJSMetaAttributeConstants;
 import org.apache.royale.compiler.constants.IASLanguageConstants.BuiltinType;
 import org.apache.royale.compiler.definitions.IDefinition;
 import org.apache.royale.compiler.definitions.IFunctionDefinition;
@@ -28,6 +29,7 @@ import 
org.apache.royale.compiler.definitions.IFunctionDefinition.FunctionClassi
 import org.apache.royale.compiler.definitions.ITypeDefinition;
 import org.apache.royale.compiler.definitions.IVariableDefinition;
 import 
org.apache.royale.compiler.definitions.IVariableDefinition.VariableClassification;
+import org.apache.royale.compiler.definitions.metadata.IMetaTag;
 import org.apache.royale.compiler.internal.codegen.as.ASEmitterTokens;
 import org.apache.royale.compiler.internal.codegen.js.JSEmitterTokens;
 import org.apache.royale.compiler.internal.codegen.js.JSSubEmitter;
@@ -714,6 +716,8 @@ public class BinaryOperatorEmitter extends JSSubEmitter 
implements
         }
         else
         {
+                       boolean dynamicAssignmentOverride = false;
+                       boolean dynamicInOverride = false;
                        if (isAssignment
                                        && (getProject() instanceof 
RoyaleJSProject && ((RoyaleJSProject) getProject()).config != null && 
((RoyaleJSProject) getProject()).config.getJsVectorEmulationClass() == null)
                                        && node.getLeftOperandNode() instanceof 
MemberAccessExpressionNode
@@ -744,6 +748,44 @@ public class BinaryOperatorEmitter extends JSSubEmitter 
implements
                        {
                                
getWalker().walk(node.getLeftOperandNode().getChild(1));
                        }
+                       else if (isAssignment && node.getLeftOperandNode() 
instanceof IDynamicAccessNode)
+                       {
+                               IDynamicAccessNode dynamicAccessNode = 
(IDynamicAccessNode) node.getLeftOperandNode();
+                               String setMethod = 
getDynamicAccessSetOverride(dynamicAccessNode);
+                               if (setMethod != null)
+                               {
+                                       dynamicAssignmentOverride = true;
+                                       
getWalker().walk(dynamicAccessNode.getLeftOperandNode());
+                                       write(ASEmitterTokens.MEMBER_ACCESS);
+                                       write(setMethod);
+                                       write(ASEmitterTokens.PAREN_OPEN);
+                                       
getWalker().walk(dynamicAccessNode.getRightOperandNode());
+                                       write(ASEmitterTokens.COMMA);
+                               }
+                               else
+                               {
+                                       // normal dynamic access
+                                       
getWalker().walk(node.getLeftOperandNode());
+                               }
+                       }
+                       else if (id == ASTNodeID.Op_InID)
+                       {
+                               String inMethod = 
getDynamicAccessInOverride(node.getRightOperandNode());
+                               if (inMethod != null)
+                               {
+                                       dynamicInOverride = true;
+                                       
getWalker().walk(node.getRightOperandNode());
+                                       write(ASEmitterTokens.MEMBER_ACCESS);
+                                       write(inMethod);
+                                       write(ASEmitterTokens.PAREN_OPEN);
+                                       
getWalker().walk(node.getLeftOperandNode());
+                               }
+                               else
+                               {
+                                       // normal in operator
+                                       
getWalker().walk(node.getLeftOperandNode());
+                               }
+                       }
             else getWalker().walk(node.getLeftOperandNode());
             startMapping(node, node.getLeftOperandNode());
                        boolean xmlAdd = false;
@@ -751,7 +793,7 @@ public class BinaryOperatorEmitter extends JSSubEmitter 
implements
                                //we need to use 'plus' method instead of '+'
                                xmlAdd = true;
                        }
-            if (id != ASTNodeID.Op_CommaID && !xmlAdd)
+            if (id != ASTNodeID.Op_CommaID && !xmlAdd && 
!dynamicAssignmentOverride && !dynamicInOverride)
                 write(ASEmitterTokens.SPACE);
 
             // (erikdebruin) rewrite 'a &&= b' to 'a = a && b'
@@ -770,26 +812,26 @@ public class BinaryOperatorEmitter extends JSSubEmitter 
implements
                 write(ASEmitterTokens.SPACE);
                 write((id == ASTNodeID.Op_LogicalAndAssignID) ? 
ASEmitterTokens.LOGICAL_AND
                         : ASEmitterTokens.LOGICAL_OR);
+               write(ASEmitterTokens.SPACE);
             }
-            else
+            else if (!dynamicAssignmentOverride && !dynamicInOverride)
             {
                                if (xmlAdd) {
                                        write(".plus(");
                                } else {
                                        
write(node.getOperator().getOperatorText());
+                       write(ASEmitterTokens.SPACE);
                                }
             }
 
-            write(ASEmitterTokens.SPACE);
             endMapping(node);
 
                        if (isAssignment)
                        {
                                
getEmitter().emitAssignmentCoercion(node.getRightOperandNode(), 
node.getLeftOperandNode().resolveType(getProject()));
                        }
-                       else
+                       else if (!dynamicInOverride)
                        {
-                               
                                getWalker().walk(node.getRightOperandNode());
                                
                                if (node.getNodeID() == ASTNodeID.Op_InID &&
@@ -804,7 +846,7 @@ public class BinaryOperatorEmitter extends JSSubEmitter 
implements
                                        write(".propertyNames()");
                                }
                        }
-                       if (xmlAdd) {
+                       if (xmlAdd || dynamicAssignmentOverride || 
dynamicInOverride) {
                                write(")");
                        }
         }
@@ -812,6 +854,43 @@ public class BinaryOperatorEmitter extends JSSubEmitter 
implements
         if (ASNodeUtils.hasParenOpen(node))
             write(ASEmitterTokens.PAREN_CLOSE);
     }
+
+       private String getDynamicAccessSetOverride(IASNode leftSide)
+       {
+               if (!(leftSide instanceof DynamicAccessNode))
+               {
+                       return null;
+               }
+                       
+               IDynamicAccessNode dynamicAccessNode = (IDynamicAccessNode) 
leftSide;
+               IExpressionNode dynamicLeftOperandNode = 
dynamicAccessNode.getLeftOperandNode();
+               ITypeDefinition dynamicLeftType = 
dynamicLeftOperandNode.resolveType(getProject());
+               if (dynamicLeftType == null)
+               {
+                       return null;
+               }
+               IMetaTag dynamicOverrideMeta = 
dynamicLeftType.getMetaTagByName(IJSMetaAttributeConstants.ATTRIBUTE_DYNAMIC_OVERRIDE);
+               if (dynamicOverrideMeta == null)
+               {
+                       return null;
+               }
+               return 
dynamicOverrideMeta.getAttributeValue(IJSMetaAttributeConstants.NAME_DYNAMIC_OVERRIDE_SET_METHOD);
+       }
+
+       private String getDynamicAccessInOverride(IExpressionNode rightSideOfIn)
+       {
+               ITypeDefinition typeDef = 
rightSideOfIn.resolveType(getProject());
+               if (typeDef == null)
+               {
+                       return null;
+               }
+               IMetaTag dynamicOverrideMeta = 
typeDef.getMetaTagByName(IJSMetaAttributeConstants.ATTRIBUTE_DYNAMIC_OVERRIDE);
+               if (dynamicOverrideMeta == null)
+               {
+                       return null;
+               }
+               return 
dynamicOverrideMeta.getAttributeValue(IJSMetaAttributeConstants.NAME_DYNAMIC_OVERRIDE_IN_METHOD);
+       }
     
     public static enum DatePropertiesGetters
     {
diff --git 
a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/ClassEmitter.java
 
b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/ClassEmitter.java
index b3f2def6e..83a34c395 100644
--- 
a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/ClassEmitter.java
+++ 
b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/ClassEmitter.java
@@ -25,10 +25,12 @@ import java.util.List;
 import org.apache.royale.compiler.asdoc.royale.ASDocComment;
 import org.apache.royale.compiler.codegen.ISubEmitter;
 import org.apache.royale.compiler.codegen.js.IJSEmitter;
+import org.apache.royale.compiler.constants.IJSMetaAttributeConstants;
 import org.apache.royale.compiler.definitions.IClassDefinition;
 import org.apache.royale.compiler.definitions.IDefinition;
 import org.apache.royale.compiler.definitions.IFunctionDefinition;
 import org.apache.royale.compiler.definitions.INamespaceDefinition;
+import org.apache.royale.compiler.definitions.metadata.IMetaTag;
 import org.apache.royale.compiler.internal.codegen.as.ASEmitterTokens;
 import org.apache.royale.compiler.internal.codegen.js.JSSubEmitter;
 import org.apache.royale.compiler.internal.codegen.js.goog.JSGoogEmitterTokens;
@@ -40,6 +42,7 @@ import 
org.apache.royale.compiler.internal.scopes.ASProjectScope;
 import org.apache.royale.compiler.internal.scopes.FunctionScope;
 import org.apache.royale.compiler.internal.tree.as.IdentifierNode;
 import org.apache.royale.compiler.internal.tree.as.VariableNode;
+import org.apache.royale.compiler.problems.MissingMetaTagAttributeProblem;
 import org.apache.royale.compiler.tree.ASTNodeID;
 import org.apache.royale.compiler.tree.as.*;
 import org.apache.royale.compiler.units.ICompilationUnit;
@@ -57,6 +60,8 @@ public class ClassEmitter extends JSSubEmitter implements
     @Override
     public void emit(IClassNode node)
     {
+        verifyMetaOverrides(node);
+
         boolean keepASDoc = false;
         boolean verbose = false;
         RoyaleJSProject project = 
(RoyaleJSProject)getEmitter().getWalker().getProject();
@@ -315,4 +320,40 @@ public class ClassEmitter extends JSSubEmitter implements
             endMapping(dnode);
         }
     }
+
+    private void verifyMetaOverrides(IClassNode c)
+    {
+        for (IMetaTag forInOverrideMeta : 
c.getMetaTagsByName(IJSMetaAttributeConstants.ATTRIBUTE_FOR_IN_OVERRIDE))
+        {
+            final String iteratorMethodName = 
forInOverrideMeta.getAttributeValue(IJSMetaAttributeConstants.NAME_FOR_IN_OVERRIDE_ITERATOR_METHOD);
+            final String iteratorNextMethodName = 
forInOverrideMeta.getAttributeValue(IJSMetaAttributeConstants.NAME_FOR_IN_OVERRIDE_ITERATOR_NEXT_METHOD);
+
+            if (iteratorMethodName == null || iteratorMethodName.length() == 0)
+            {
+                getProject().getProblems().add(new 
MissingMetaTagAttributeProblem(forInOverrideMeta, 
IJSMetaAttributeConstants.NAME_FOR_IN_OVERRIDE_ITERATOR_METHOD));
+                return;
+            }
+
+            if (iteratorNextMethodName == null || 
iteratorNextMethodName.length() == 0)
+            {
+                getProject().getProblems().add(new 
MissingMetaTagAttributeProblem(forInOverrideMeta, 
IJSMetaAttributeConstants.NAME_FOR_IN_OVERRIDE_ITERATOR_NEXT_METHOD));
+            }
+        }
+        for (IMetaTag forEachOverrideMeta : 
c.getMetaTagsByName(IJSMetaAttributeConstants.ATTRIBUTE_FOR_EACH_OVERRIDE))
+        {
+            final String iteratorMethodName = 
forEachOverrideMeta.getAttributeValue(IJSMetaAttributeConstants.NAME_FOR_EACH_OVERRIDE_ITERATOR_METHOD);
+            final String iteratorNextMethodName = 
forEachOverrideMeta.getAttributeValue(IJSMetaAttributeConstants.NAME_FOR_EACH_OVERRIDE_ITERATOR_NEXT_METHOD);
+
+            if (iteratorMethodName == null || iteratorMethodName.length() == 0)
+            {
+                getProject().getProblems().add(new 
MissingMetaTagAttributeProblem(forEachOverrideMeta, 
IJSMetaAttributeConstants.NAME_FOR_EACH_OVERRIDE_ITERATOR_METHOD));
+                return;
+            }
+
+            if (iteratorNextMethodName == null || 
iteratorNextMethodName.length() == 0)
+            {
+                getProject().getProblems().add(new 
MissingMetaTagAttributeProblem(forEachOverrideMeta, 
IJSMetaAttributeConstants.NAME_FOR_EACH_OVERRIDE_ITERATOR_NEXT_METHOD));
+            }
+        }
+    }
 }
diff --git 
a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/DynamicAccessEmitter.java
 
b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/DynamicAccessEmitter.java
index d31736107..b0392de31 100644
--- 
a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/DynamicAccessEmitter.java
+++ 
b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/DynamicAccessEmitter.java
@@ -23,7 +23,9 @@ import org.apache.royale.compiler.codegen.IDocEmitter;
 import org.apache.royale.compiler.codegen.ISubEmitter;
 import org.apache.royale.compiler.codegen.js.IJSEmitter;
 import org.apache.royale.compiler.constants.IASLanguageConstants;
+import org.apache.royale.compiler.constants.IJSMetaAttributeConstants;
 import org.apache.royale.compiler.definitions.ITypeDefinition;
+import org.apache.royale.compiler.definitions.metadata.IMetaTag;
 import org.apache.royale.compiler.internal.codegen.as.ASEmitterTokens;
 import org.apache.royale.compiler.internal.codegen.js.JSSubEmitter;
 import 
org.apache.royale.compiler.internal.codegen.js.royale.JSRoyaleDocEmitter;
@@ -89,6 +91,25 @@ public class DynamicAccessEmitter extends JSSubEmitter 
implements
                        return;
                }
        }
+
+               ITypeDefinition leftType = 
leftOperandNode.resolveType(getProject());
+               if (leftType != null)
+               {
+                       IMetaTag dynamicOverrideMeta = 
leftType.getMetaTagByName(IJSMetaAttributeConstants.ATTRIBUTE_DYNAMIC_OVERRIDE);
+                       if (dynamicOverrideMeta != null)
+                       {
+                               String getMethod = 
dynamicOverrideMeta.getAttributeValue(IJSMetaAttributeConstants.NAME_DYNAMIC_OVERRIDE_GET_METHOD);
+                               if (getMethod != null)
+                               {
+                                       write(ASEmitterTokens.MEMBER_ACCESS);
+                                       write(getMethod);
+                                       write(ASEmitterTokens.PAREN_OPEN);
+                               getWalker().walk(rightOperandNode);
+                                       write(ASEmitterTokens.PAREN_CLOSE);
+                                       return;
+                               }
+                       }
+               }
        
         startMapping(node, leftOperandNode);
         write(ASEmitterTokens.SQUARE_OPEN);
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 071a48140..be8d1400f 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
@@ -22,9 +22,12 @@ package org.apache.royale.compiler.internal.codegen.js.jx;
 import org.apache.royale.compiler.codegen.ISubEmitter;
 import org.apache.royale.compiler.codegen.js.IJSEmitter;
 import org.apache.royale.compiler.constants.IASLanguageConstants;
+import org.apache.royale.compiler.constants.IJSMetaAttributeConstants;
 import org.apache.royale.compiler.definitions.IClassDefinition;
 import org.apache.royale.compiler.definitions.IDefinition;
 import org.apache.royale.compiler.definitions.IFunctionDefinition;
+import org.apache.royale.compiler.definitions.ITypeDefinition;
+import org.apache.royale.compiler.definitions.metadata.IMetaTag;
 import org.apache.royale.compiler.internal.codegen.as.ASEmitterTokens;
 import org.apache.royale.compiler.internal.codegen.js.JSSubEmitter;
 import org.apache.royale.compiler.internal.codegen.js.royale.JSRoyaleEmitter;
@@ -55,6 +58,24 @@ public class ForEachEmitter extends JSSubEmitter implements
         IExpressionNode childNode = bnode.getLeftOperandNode();
         IExpressionNode rnode = bnode.getRightOperandNode();
 
+        ITypeDefinition rtype = rnode.resolveType(getProject());
+        if (rtype != null)
+        {
+            IMetaTag forEachOverrideMeta = 
rtype.getMetaTagByName(IJSMetaAttributeConstants.ATTRIBUTE_FOR_EACH_OVERRIDE);
+            if (forEachOverrideMeta != null)
+            {
+                emitForEachOverride(node, rtype, forEachOverrideMeta);
+                return;
+            }
+            // it's possible to use ForInOverride for for-each loops too
+            IMetaTag forInOverrideMeta = 
rtype.getMetaTagByName(IJSMetaAttributeConstants.ATTRIBUTE_FOR_IN_OVERRIDE);
+            if (forInOverrideMeta != null)
+            {
+                emitForInOverride(node, rtype, forInOverrideMeta);
+                return;
+            }
+        }
+
         final String iterName = getModel().getCurrentForeachName();
         getModel().incForeachLoopCount();
         final String targetName = iterName + "_target";
@@ -317,4 +338,320 @@ public class ForEachEmitter extends JSSubEmitter 
implements
     }
     */
 
+    private void emitForEachOverride(IForLoopNode node, ITypeDefinition rtype, 
IMetaTag forEachOverrideMeta)
+    {
+        IContainerNode cnode = node.getConditionalsContainerNode();
+        IBinaryOperatorNode bnode = (IBinaryOperatorNode) cnode.getChild(0);
+        IExpressionNode childNode = bnode.getLeftOperandNode();
+        IExpressionNode rnode = bnode.getRightOperandNode();
+
+        final String iterBaseName = getModel().getCurrentForeachName();
+        getModel().incForeachLoopCount();
+        final String iterTargetName = iterBaseName + "_target";
+        final String iterResultName = iterBaseName + "_iterator";
+        final String iterKeyName = iterBaseName + "_key";
+
+        final String iteratorMethodName = 
forEachOverrideMeta.getAttributeValue(IJSMetaAttributeConstants.NAME_FOR_EACH_OVERRIDE_ITERATOR_METHOD);
+        final String iteratorNextMethodName = 
forEachOverrideMeta.getAttributeValue(IJSMetaAttributeConstants.NAME_FOR_EACH_OVERRIDE_ITERATOR_NEXT_METHOD);
+        final String iteratorHasNextMethodName = 
forEachOverrideMeta.getAttributeValue(IJSMetaAttributeConstants.NAME_FOR_EACH_OVERRIDE_ITERATOR_HAS_NEXT_METHOD);
+        final String iteratorDoneMethodName = 
forEachOverrideMeta.getAttributeValue(IJSMetaAttributeConstants.NAME_FOR_IN_OVERRIDE_ITERATOR_DONE_METHOD);
+
+        if (iteratorMethodName == null || iteratorNextMethodName == null)
+        {
+            return;
+        }
+
+        writeToken(ASEmitterTokens.VAR);
+        writeToken(iterTargetName);
+        writeToken(ASEmitterTokens.EQUAL);
+        getWalker().walk(rnode);
+        write(ASEmitterTokens.SEMICOLON);
+        writeNewline();
+
+        // if the target is null, the loop will be skipped without any
+        // exceptions at run-time
+        writeToken(ASEmitterTokens.IF);
+        write(ASEmitterTokens.PAREN_OPEN);
+        write(iterTargetName);
+        write(ASEmitterTokens.PAREN_CLOSE);
+        writeNewline();
+        write(ASEmitterTokens.BLOCK_OPEN);
+        indentPush();
+        writeNewline();
+
+        writeToken(ASEmitterTokens.VAR);
+        writeToken(iterResultName);
+        writeToken(ASEmitterTokens.EQUAL);
+        write(iterTargetName);
+        write(ASEmitterTokens.MEMBER_ACCESS);
+        write(iteratorMethodName);
+        write(ASEmitterTokens.PAREN_OPEN);
+        write(ASEmitterTokens.PAREN_CLOSE);
+        write(ASEmitterTokens.SEMICOLON);
+        writeNewline();
+
+        writeToken(ASEmitterTokens.WHILE);
+        write(ASEmitterTokens.PAREN_OPEN);
+        if (iteratorHasNextMethodName != null)
+        {
+            write(iterResultName);
+            write(ASEmitterTokens.MEMBER_ACCESS);
+            write(iteratorHasNextMethodName);
+            write(ASEmitterTokens.PAREN_OPEN);
+            write(ASEmitterTokens.PAREN_CLOSE);
+        }
+        else if (iteratorDoneMethodName != null)
+        {
+            write("!");
+            write(iterResultName);
+            write(ASEmitterTokens.MEMBER_ACCESS);
+            write(iteratorDoneMethodName);
+            write(ASEmitterTokens.PAREN_OPEN);
+            write(ASEmitterTokens.PAREN_CLOSE);
+        }
+        else
+        {
+            write(ASEmitterTokens.TRUE);
+        }
+        write(ASEmitterTokens.PAREN_CLOSE);
+        writeNewline();
+        write(ASEmitterTokens.BLOCK_OPEN);
+        indentPush();
+        writeNewline();
+
+        if (iteratorHasNextMethodName == null && iteratorDoneMethodName == 
null)
+        {
+            writeToken(ASEmitterTokens.VAR);
+            writeToken(iterKeyName);
+            writeToken(ASEmitterTokens.EQUAL);
+            write(iterResultName);
+            write(ASEmitterTokens.MEMBER_ACCESS);
+            write(iteratorNextMethodName);
+            write(ASEmitterTokens.PAREN_OPEN);
+            write(ASEmitterTokens.PAREN_CLOSE);
+            write(ASEmitterTokens.SEMICOLON);
+            writeNewline();
+
+            writeToken(ASEmitterTokens.IF);
+            write(ASEmitterTokens.PAREN_OPEN);
+            writeToken(iterKeyName);
+            writeToken("==");
+            write(ASEmitterTokens.UNDEFINED);
+            writeToken(ASEmitterTokens.PAREN_CLOSE);
+            write("break");
+            write(ASEmitterTokens.SEMICOLON);
+            writeNewline();
+        }
+        
+        if (childNode instanceof IVariableExpressionNode)
+        {
+            startMapping(childNode);
+            write(ASEmitterTokens.VAR);
+            write(ASEmitterTokens.SPACE);
+            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 { //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);
+        }
+
+        if (iteratorHasNextMethodName == null && iteratorDoneMethodName == 
null)
+        {
+            write(iterKeyName);
+            write(ASEmitterTokens.SEMICOLON);
+            writeNewline();
+        }
+        else
+        {
+            write(iterResultName);
+            write(ASEmitterTokens.MEMBER_ACCESS);
+            write(iteratorNextMethodName);
+            write(ASEmitterTokens.PAREN_OPEN);
+            write(ASEmitterTokens.PAREN_CLOSE);
+            write(ASEmitterTokens.SEMICOLON);
+            writeNewline();
+        }
+
+        getWalker().walk(node.getStatementContentsNode());
+
+        write(ASEmitterTokens.BLOCK_CLOSE);
+        indentPop();
+        writeNewline();
+
+        write(ASEmitterTokens.BLOCK_CLOSE);
+        indentPop();
+        writeNewline();
+    }
+
+    private void emitForInOverride(IForLoopNode node, ITypeDefinition rtype, 
IMetaTag forInOverrideMeta)
+    {
+        IContainerNode cnode = node.getConditionalsContainerNode();
+        IBinaryOperatorNode bnode = (IBinaryOperatorNode) cnode.getChild(0);
+        IExpressionNode childNode = bnode.getLeftOperandNode();
+        IExpressionNode rnode = bnode.getRightOperandNode();
+
+        IMetaTag dynamicOverrideMeta = 
rtype.getMetaTagByName(IJSMetaAttributeConstants.ATTRIBUTE_DYNAMIC_OVERRIDE);
+        String getMethod = null;
+        if (dynamicOverrideMeta != null)
+        {
+            getMethod = 
dynamicOverrideMeta.getAttributeValue(IJSMetaAttributeConstants.NAME_DYNAMIC_OVERRIDE_GET_METHOD);
+        }
+
+        final String iterBaseName = getModel().getCurrentForeachName();
+        getModel().incForeachLoopCount();
+        final String iterTargetName = iterBaseName + "_target";
+        final String iterResultName = iterBaseName + "_iterator";
+        final String iterKeyName = iterBaseName + "_key";
+
+        final String iteratorMethodName = 
forInOverrideMeta.getAttributeValue(IJSMetaAttributeConstants.NAME_FOR_IN_OVERRIDE_ITERATOR_METHOD);
+        final String iteratorNextMethodName = 
forInOverrideMeta.getAttributeValue(IJSMetaAttributeConstants.NAME_FOR_IN_OVERRIDE_ITERATOR_NEXT_METHOD);
+        final String iteratorHasNextMethodName = 
forInOverrideMeta.getAttributeValue(IJSMetaAttributeConstants.NAME_FOR_IN_OVERRIDE_ITERATOR_HAS_NEXT_METHOD);
+        final String iteratorDoneMethodName = 
forInOverrideMeta.getAttributeValue(IJSMetaAttributeConstants.NAME_FOR_IN_OVERRIDE_ITERATOR_DONE_METHOD);
+
+        if (iteratorMethodName == null || iteratorNextMethodName == null)
+        {
+            return;
+        }
+
+        writeToken(ASEmitterTokens.VAR);
+        writeToken(iterTargetName);
+        writeToken(ASEmitterTokens.EQUAL);
+        getWalker().walk(rnode);
+        write(ASEmitterTokens.SEMICOLON);
+        writeNewline();
+
+        // if the target is null, the loop will be skipped without any
+        // exceptions at run-time
+        writeToken(ASEmitterTokens.IF);
+        write(ASEmitterTokens.PAREN_OPEN);
+        write(iterTargetName);
+        write(ASEmitterTokens.PAREN_CLOSE);
+        writeNewline();
+        write(ASEmitterTokens.BLOCK_OPEN);
+        indentPush();
+        writeNewline();
+
+        writeToken(ASEmitterTokens.VAR);
+        writeToken(iterResultName);
+        writeToken(ASEmitterTokens.EQUAL);
+        write(iterTargetName);
+        write(ASEmitterTokens.MEMBER_ACCESS);
+        write(iteratorMethodName);
+        write(ASEmitterTokens.PAREN_OPEN);
+        write(ASEmitterTokens.PAREN_CLOSE);
+        write(ASEmitterTokens.SEMICOLON);
+        writeNewline();
+
+        writeToken(ASEmitterTokens.WHILE);
+        write(ASEmitterTokens.PAREN_OPEN);
+        if (iteratorHasNextMethodName != null)
+        {
+            write(iterResultName);
+            write(ASEmitterTokens.MEMBER_ACCESS);
+            write(iteratorHasNextMethodName);
+            write(ASEmitterTokens.PAREN_OPEN);
+            write(ASEmitterTokens.PAREN_CLOSE);
+        }
+        else if (iteratorDoneMethodName != null)
+        {
+            write("!");
+            write(iterResultName);
+            write(ASEmitterTokens.MEMBER_ACCESS);
+            write(iteratorDoneMethodName);
+            write(ASEmitterTokens.PAREN_OPEN);
+            write(ASEmitterTokens.PAREN_CLOSE);
+        }
+        else
+        {
+            write(ASEmitterTokens.TRUE);
+        }
+        write(ASEmitterTokens.PAREN_CLOSE);
+        writeNewline();
+        write(ASEmitterTokens.BLOCK_OPEN);
+        indentPush();
+        writeNewline();
+
+        writeToken(ASEmitterTokens.VAR);
+        writeToken(iterKeyName);
+        writeToken(ASEmitterTokens.EQUAL);
+        write(iterResultName);
+        write(ASEmitterTokens.MEMBER_ACCESS);
+        write(iteratorNextMethodName);
+        write(ASEmitterTokens.PAREN_OPEN);
+        write(ASEmitterTokens.PAREN_CLOSE);
+        write(ASEmitterTokens.SEMICOLON);
+        writeNewline();
+
+        if (iteratorHasNextMethodName == null && iteratorDoneMethodName == 
null)
+        {
+            writeToken(ASEmitterTokens.IF);
+            write(ASEmitterTokens.PAREN_OPEN);
+            writeToken(iterKeyName);
+            writeToken("==");
+            write(ASEmitterTokens.UNDEFINED);
+            writeToken(ASEmitterTokens.PAREN_CLOSE);
+            write("break");
+            write(ASEmitterTokens.SEMICOLON);
+            writeNewline();
+        }
+        
+        if (childNode instanceof IVariableExpressionNode)
+        {
+            startMapping(childNode);
+            write(ASEmitterTokens.VAR);
+            write(ASEmitterTokens.SPACE);
+            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 { //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);
+        }
+
+        write(iterTargetName);
+        if (getMethod != null)
+        {
+            write(ASEmitterTokens.MEMBER_ACCESS);
+            write(getMethod);
+            write(ASEmitterTokens.PAREN_OPEN);
+            write(iterKeyName);
+            write(ASEmitterTokens.PAREN_CLOSE);
+        }
+        else
+        {
+            write(ASEmitterTokens.SQUARE_OPEN);
+            write(iterKeyName);
+            write(ASEmitterTokens.SQUARE_CLOSE);
+        }
+        write(ASEmitterTokens.SEMICOLON);
+        writeNewline();
+
+        getWalker().walk(node.getStatementContentsNode());
+
+        write(ASEmitterTokens.BLOCK_CLOSE);
+        indentPop();
+        writeNewline();
+
+        write(ASEmitterTokens.BLOCK_CLOSE);
+        indentPop();
+        writeNewline();
+    }
+
 }
diff --git 
a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/ForLoopEmitter.java
 
b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/ForLoopEmitter.java
index 21ee8d98c..0a4ad168d 100644
--- 
a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/ForLoopEmitter.java
+++ 
b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/ForLoopEmitter.java
@@ -20,13 +20,20 @@ package org.apache.royale.compiler.internal.codegen.js.jx;
 
 import org.apache.royale.compiler.codegen.ISubEmitter;
 import org.apache.royale.compiler.codegen.js.IJSEmitter;
+import org.apache.royale.compiler.constants.IJSMetaAttributeConstants;
+import org.apache.royale.compiler.definitions.ITypeDefinition;
+import org.apache.royale.compiler.definitions.metadata.IMetaTag;
 import org.apache.royale.compiler.internal.codegen.as.ASEmitterTokens;
 import org.apache.royale.compiler.internal.codegen.js.JSSubEmitter;
 import org.apache.royale.compiler.internal.codegen.js.utils.EmitterUtils;
 import org.apache.royale.compiler.tree.ASTNodeID;
 import org.apache.royale.compiler.tree.as.IASNode;
+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.IVariableExpressionNode;
+import org.apache.royale.compiler.tree.as.IVariableNode;
 
 public class ForLoopEmitter extends JSSubEmitter implements
         ISubEmitter<IForLoopNode>
@@ -40,14 +47,32 @@ public class ForLoopEmitter extends JSSubEmitter implements
     public void emit(IForLoopNode node)
     {
         IContainerNode statementContentsNode = (IContainerNode) 
node.getStatementContentsNode();
+        IContainerNode cnode = node.getConditionalsContainerNode();
+        final IASNode node0 = cnode.getChild(0);
+
+        if (node0.getNodeID() == ASTNodeID.Op_InID)
+        {
+            IBinaryOperatorNode inNode = (IBinaryOperatorNode) node0;
+            IExpressionNode rnode = inNode.getRightOperandNode();
+
+
+            ITypeDefinition rtype = rnode.resolveType(getProject());
+            if (rtype != null)
+            {
+                IMetaTag forInOverrideMeta = 
rtype.getMetaTagByName(IJSMetaAttributeConstants.ATTRIBUTE_FOR_IN_OVERRIDE);
+                if (forInOverrideMeta != null)
+                {
+                    emitForInOverride(node, rtype, forInOverrideMeta);
+                    return;
+                }
+            }
+        }
 
         startMapping(node);
         writeToken(ASEmitterTokens.FOR);
         write(ASEmitterTokens.PAREN_OPEN);
         endMapping(node);
 
-        IContainerNode cnode = node.getConditionalsContainerNode();
-        final IASNode node0 = cnode.getChild(0);
         if (node0.getNodeID() == ASTNodeID.Op_InID)
         {
             //for(in)
@@ -125,4 +150,158 @@ public class ForLoopEmitter extends JSSubEmitter 
implements
             getWalker().walk(node2);
         }
     }
+
+    private void emitForInOverride(IForLoopNode node, ITypeDefinition rtype, 
IMetaTag forInOverrideMeta)
+    {
+        IContainerNode cnode = node.getConditionalsContainerNode();
+        IBinaryOperatorNode bnode = (IBinaryOperatorNode) cnode.getChild(0);
+        IExpressionNode childNode = bnode.getLeftOperandNode();
+        IExpressionNode rnode = bnode.getRightOperandNode();
+
+        final String iterBaseName = getModel().getCurrentForeachName();
+        getModel().incForeachLoopCount();
+        final String iterTargetName = iterBaseName + "_target";
+        final String iterResultName = iterBaseName + "_iterator";
+        final String iterKeyName = iterBaseName + "_key";
+
+        final String iteratorMethodName = 
forInOverrideMeta.getAttributeValue(IJSMetaAttributeConstants.NAME_FOR_IN_OVERRIDE_ITERATOR_METHOD);
+        final String iteratorNextMethodName = 
forInOverrideMeta.getAttributeValue(IJSMetaAttributeConstants.NAME_FOR_IN_OVERRIDE_ITERATOR_NEXT_METHOD);
+        final String iteratorHasNextMethodName = 
forInOverrideMeta.getAttributeValue(IJSMetaAttributeConstants.NAME_FOR_IN_OVERRIDE_ITERATOR_HAS_NEXT_METHOD);
+        final String iteratorDoneMethodName = 
forInOverrideMeta.getAttributeValue(IJSMetaAttributeConstants.NAME_FOR_IN_OVERRIDE_ITERATOR_DONE_METHOD);
+
+        if (iteratorMethodName == null || iteratorNextMethodName == null)
+        {
+            return;
+        }
+
+        writeToken(ASEmitterTokens.VAR);
+        writeToken(iterTargetName);
+        writeToken(ASEmitterTokens.EQUAL);
+        getWalker().walk(rnode);
+        write(ASEmitterTokens.SEMICOLON);
+        writeNewline();
+
+        // if the target is null, the loop will be skipped without any
+        // exceptions at run-time
+        writeToken(ASEmitterTokens.IF);
+        write(ASEmitterTokens.PAREN_OPEN);
+        write(iterTargetName);
+        write(ASEmitterTokens.PAREN_CLOSE);
+        writeNewline();
+        write(ASEmitterTokens.BLOCK_OPEN);
+        indentPush();
+        writeNewline();
+
+        writeToken(ASEmitterTokens.VAR);
+        writeToken(iterResultName);
+        writeToken(ASEmitterTokens.EQUAL);
+        write(iterTargetName);
+        write(ASEmitterTokens.MEMBER_ACCESS);
+        write(iteratorMethodName);
+        write(ASEmitterTokens.PAREN_OPEN);
+        write(ASEmitterTokens.PAREN_CLOSE);
+        write(ASEmitterTokens.SEMICOLON);
+        writeNewline();
+
+        writeToken(ASEmitterTokens.WHILE);
+        write(ASEmitterTokens.PAREN_OPEN);
+        if (iteratorHasNextMethodName != null)
+        {
+            write(iterResultName);
+            write(ASEmitterTokens.MEMBER_ACCESS);
+            write(iteratorHasNextMethodName);
+            write(ASEmitterTokens.PAREN_OPEN);
+            write(ASEmitterTokens.PAREN_CLOSE);
+        }
+        else if (iteratorDoneMethodName != null)
+        {
+            write("!");
+            write(iterResultName);
+            write(ASEmitterTokens.MEMBER_ACCESS);
+            write(iteratorDoneMethodName);
+            write(ASEmitterTokens.PAREN_OPEN);
+            write(ASEmitterTokens.PAREN_CLOSE);
+        }
+        else
+        {
+            write(ASEmitterTokens.TRUE);
+        }
+        write(ASEmitterTokens.PAREN_CLOSE);
+        writeNewline();
+        write(ASEmitterTokens.BLOCK_OPEN);
+        indentPush();
+        writeNewline();
+        
+        if (iteratorHasNextMethodName == null && iteratorDoneMethodName == 
null)
+        {
+            writeToken(ASEmitterTokens.VAR);
+            writeToken(iterKeyName);
+            writeToken(ASEmitterTokens.EQUAL);
+            write(iterResultName);
+            write(ASEmitterTokens.MEMBER_ACCESS);
+            write(iteratorNextMethodName);
+            write(ASEmitterTokens.PAREN_OPEN);
+            write(ASEmitterTokens.PAREN_CLOSE);
+            write(ASEmitterTokens.SEMICOLON);
+            writeNewline();
+
+            writeToken(ASEmitterTokens.IF);
+            write(ASEmitterTokens.PAREN_OPEN);
+            writeToken(iterKeyName);
+            writeToken("==");
+            write(ASEmitterTokens.UNDEFINED);
+            writeToken(ASEmitterTokens.PAREN_CLOSE);
+            write("break");
+            write(ASEmitterTokens.SEMICOLON);
+            writeNewline();
+        }
+
+        if (childNode instanceof IVariableExpressionNode)
+        {
+            startMapping(childNode);
+            write(ASEmitterTokens.VAR);
+            write(ASEmitterTokens.SPACE);
+            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 { //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);
+        }
+
+        if (iteratorHasNextMethodName == null && iteratorDoneMethodName == 
null)
+        {
+            write(iterKeyName);
+            write(ASEmitterTokens.SEMICOLON);
+            writeNewline();
+        }
+        else
+        {
+            write(iterResultName);
+            write(ASEmitterTokens.MEMBER_ACCESS);
+            write(iteratorNextMethodName);
+            write(ASEmitterTokens.PAREN_OPEN);
+            write(ASEmitterTokens.PAREN_CLOSE);
+            write(ASEmitterTokens.SEMICOLON);
+            writeNewline();
+        }
+
+        getWalker().walk(node.getStatementContentsNode());
+
+        write(ASEmitterTokens.BLOCK_CLOSE);
+        indentPop();
+        writeNewline();
+
+        write(ASEmitterTokens.BLOCK_CLOSE);
+        indentPop();
+        writeNewline();
+    }
 }
diff --git 
a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/UnaryOperatorEmitter.java
 
b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/UnaryOperatorEmitter.java
index 2bc5a79a1..72475713c 100644
--- 
a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/UnaryOperatorEmitter.java
+++ 
b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/UnaryOperatorEmitter.java
@@ -21,6 +21,9 @@ package org.apache.royale.compiler.internal.codegen.js.jx;
 
 import org.apache.royale.compiler.codegen.ISubEmitter;
 import org.apache.royale.compiler.codegen.js.IJSEmitter;
+import org.apache.royale.compiler.constants.IJSMetaAttributeConstants;
+import org.apache.royale.compiler.definitions.ITypeDefinition;
+import org.apache.royale.compiler.definitions.metadata.IMetaTag;
 import org.apache.royale.compiler.internal.codegen.as.ASEmitterTokens;
 import org.apache.royale.compiler.internal.codegen.js.JSSubEmitter;
 import org.apache.royale.compiler.internal.codegen.js.royale.JSRoyaleEmitter;
@@ -29,6 +32,7 @@ import 
org.apache.royale.compiler.internal.definitions.AppliedVectorDefinition;
 import org.apache.royale.compiler.internal.projects.RoyaleJSProject;
 import org.apache.royale.compiler.internal.tree.as.*;
 import org.apache.royale.compiler.tree.ASTNodeID;
+import org.apache.royale.compiler.tree.as.IDynamicAccessNode;
 import org.apache.royale.compiler.tree.as.IExpressionNode;
 import org.apache.royale.compiler.tree.as.ILiteralNode;
 import org.apache.royale.compiler.tree.as.IUnaryOperatorNode;
@@ -135,10 +139,39 @@ public class UnaryOperatorEmitter extends JSSubEmitter 
implements
 
     protected void emitDeleteOperator(IUnaryOperatorNode node)
     {
+        IExpressionNode operandNode = node.getOperandNode();
+        if (operandNode instanceof IDynamicAccessNode)
+        {
+            IDynamicAccessNode dynamicAccessNode = (IDynamicAccessNode) 
operandNode;
+            IExpressionNode leftOperandNode = 
dynamicAccessNode.getLeftOperandNode();
+            ITypeDefinition leftType = 
leftOperandNode.resolveType(getProject());
+            if (leftType != null)
+            {
+                IMetaTag dynamicOverrideMeta = 
leftType.getMetaTagByName(IJSMetaAttributeConstants.ATTRIBUTE_DYNAMIC_OVERRIDE);
+                if (dynamicOverrideMeta != null)
+                {
+                    String deleteMethod = 
dynamicOverrideMeta.getAttributeValue(IJSMetaAttributeConstants.NAME_DYNAMIC_OVERRIDE_DELETE_METHOD);
+                    if (deleteMethod != null)
+                    {
+                        
getWalker().walk(dynamicAccessNode.getLeftOperandNode());
+                        startMapping(node);
+                        write(ASEmitterTokens.MEMBER_ACCESS);
+                        write(deleteMethod);
+                        write(ASEmitterTokens.PAREN_OPEN);
+                        endMapping(node);
+                        
getWalker().walk(dynamicAccessNode.getRightOperandNode());
+                        startMapping(node);
+                        write(ASEmitterTokens.PAREN_CLOSE);
+                        endMapping(node);
+                        return;
+                    }
+                }
+            }
+        }
         startMapping(node);
         writeToken(node.getOperator().getOperatorText());
         endMapping(node);
-        getWalker().walk(node.getOperandNode());
+        getWalker().walk(operandNode);
     }
 
     protected void emitVoidOperator(IUnaryOperatorNode node)
diff --git 
a/compiler/src/main/java/org/apache/royale/compiler/problems/MissingMetaTagAttributeProblem.java
 
b/compiler/src/main/java/org/apache/royale/compiler/problems/MissingMetaTagAttributeProblem.java
new file mode 100644
index 000000000..5c80fc7b6
--- /dev/null
+++ 
b/compiler/src/main/java/org/apache/royale/compiler/problems/MissingMetaTagAttributeProblem.java
@@ -0,0 +1,38 @@
+/*
+ *
+ *  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.definitions.metadata.IMetaTag;
+
+public final class MissingMetaTagAttributeProblem extends CompilerProblem
+{
+    public static final String DESCRIPTION =
+        "${metaTagName} metadata requires '${attribute}' attribute";
+    
+    public MissingMetaTagAttributeProblem(IMetaTag site, String attribute)
+    {
+        super(site);
+        metaTagName = site.getTagName();
+        this.attribute = attribute;
+    }
+
+    public String metaTagName;
+    public String attribute;
+}


Reply via email to