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 2020/03/02 22:52:49 UTC

[royale-compiler] branch develop updated: Fixes for multiple issues with Bindings and Bindables. Closes #114

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


The following commit(s) were added to refs/heads/develop by this push:
     new 65b2f1b  Fixes for multiple issues with Bindings and Bindables. Closes #114
65b2f1b is described below

commit 65b2f1b9c3476ccb3b7b509b052025bd153504b4
Author: greg-dove <gr...@gmail.com>
AuthorDate: Tue Mar 3 10:44:02 2020 +1300

    Fixes for multiple issues with Bindings and Bindables. Closes #114
---
 .../internal/codegen/js/jx/AccessorEmitter.java    |  41 ++++----
 .../codegen/js/jx/MemberAccessEmitter.java         |  46 +++++++++
 .../codegen/mxml/royale/MXMLRoyaleEmitter.java     |   2 +-
 .../internal/as/codegen/BindableHelper.java        | 103 ++++++++++++++++++++-
 .../as/codegen/ClassDirectiveProcessor.java        |  80 ++++++++--------
 .../codegen/databinding/BindingCodeGenUtils.java   |  80 +++++++++++-----
 .../databinding/MXMLBindingDirectiveHelper.java    |   5 +-
 .../internal/definitions/DefinitionBase.java       |  20 ++--
 .../internal/definitions/VariableDefinition.java   |   9 ++
 .../internal/parsing/mxml/MXMLScopeBuilder.java    |   1 +
 .../compiler/internal/tree/as/VariableNode.java    |  16 +++-
 11 files changed, 302 insertions(+), 101 deletions(-)

diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/AccessorEmitter.java b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/AccessorEmitter.java
index 2b0ab7c..e5a5998 100644
--- a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/AccessorEmitter.java
+++ b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/AccessorEmitter.java
@@ -35,7 +35,7 @@ import org.apache.royale.compiler.definitions.IFunctionDefinition;
 import org.apache.royale.compiler.definitions.INamespaceDefinition;
 import org.apache.royale.compiler.definitions.IParameterDefinition;
 import org.apache.royale.compiler.definitions.ITypeDefinition;
-import org.apache.royale.compiler.definitions.metadata.IMetaTag;
+import org.apache.royale.compiler.internal.as.codegen.BindableHelper;
 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.JSSessionModel.PropertyNodes;
@@ -163,6 +163,8 @@ public class AccessorEmitter extends JSSubEmitter implements
 	                }
 	                if (setterNode != null)
 	                {
+						boolean isClassBindable = BindableHelper.isClassCodeGenBindable(definition);
+
 	                	boolean isBindable = false;                
 	                	IAccessorDefinition setterDef = (IAccessorDefinition)setterNode.getDefinition();
 	                	IAccessorDefinition getterDef = null;
@@ -170,34 +172,25 @@ public class AccessorEmitter extends JSSubEmitter implements
 	                		getterDef = (IAccessorDefinition)getterNode.getDefinition();
 	                	if ((getterDef != null && (setterDef.isBindable() || getterDef.isBindable())))
 	                	{
+							boolean foundExplicitBindableTag = false;
 	                		if (setterDef.isBindable())
 	                		{
-	                			IMetaTag[] tags = setterDef.getMetaTagsByName("Bindable");
-	                			if (tags.length > 1)
-	                        		isBindable = true;
-	                			else if (tags.length == 1)
-	                			{
-	                				if (tags[0].getAllAttributes().length == 0)
-	                					isBindable = true;
-	                			}
-	                			if (tags.length == 0 ) {
-	                				isBindable = definition.isBindable();// if the class itself is marked as Bindable
-								}
+
+								isBindable = BindableHelper.isCodeGenBindableMember(setterDef, isClassBindable);
+								foundExplicitBindableTag = BindableHelper.hasExplicitBindable(setterDef);
 	                		}
-	                		else if (getterDef.isBindable())
+	                		if (getterDef.isBindable())
 	                		{
-	                			IMetaTag[] tags = getterDef.getMetaTagsByName("Bindable");
-	                			if (tags.length > 1)
-	                        		isBindable = true;
-	                			else if (tags.length == 1)
-	                			{
-	                				if (tags[0].getAllAttributes().length == 0)
-	                					isBindable = true;
-	                			}
-								if (tags.length == 0 ) {
-									isBindable = definition.isBindable();// if the class itself is marked as Bindable
-								}
+
+								isBindable = isBindable || BindableHelper.isCodeGenBindableMember(getterDef, isClassBindable);
+								foundExplicitBindableTag = foundExplicitBindableTag || BindableHelper.hasExplicitBindable(getterDef);
+
 	                		}
+
+							if (isClassBindable) {
+								//if we 'foundExplicitBindableTag' such as [Bindable(event='someEvent')], then nothing else matters, even another [Bindable] tag is ignored (Flex)
+								isBindable = !foundExplicitBindableTag;
+							}
 	                	}
 	                    writeNewline();
 	                    writeNewline();
diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/MemberAccessEmitter.java b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/MemberAccessEmitter.java
index b4a8271..51d45b5 100644
--- a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/MemberAccessEmitter.java
+++ b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/MemberAccessEmitter.java
@@ -29,6 +29,7 @@ import org.apache.royale.compiler.definitions.*;
 import org.apache.royale.compiler.definitions.IDefinition;
 import org.apache.royale.compiler.definitions.INamespaceDefinition;
 import org.apache.royale.compiler.definitions.IPackageDefinition;
+import org.apache.royale.compiler.definitions.references.IResolvedQualifiersReference;
 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;
@@ -40,6 +41,7 @@ import org.apache.royale.compiler.internal.codegen.js.jx.BinaryOperatorEmitter.D
 import org.apache.royale.compiler.internal.definitions.AccessorDefinition;
 import org.apache.royale.compiler.internal.definitions.FunctionDefinition;
 import org.apache.royale.compiler.internal.definitions.NamespaceDefinition;
+import org.apache.royale.compiler.internal.definitions.VariableDefinition;
 import org.apache.royale.compiler.internal.projects.RoyaleJSProject;
 import org.apache.royale.compiler.internal.scopes.FunctionScope;
 import org.apache.royale.compiler.internal.semantics.SemanticUtils;
@@ -48,6 +50,7 @@ import org.apache.royale.compiler.projects.ICompilerProject;
 import org.apache.royale.compiler.tree.ASTNodeID;
 import org.apache.royale.compiler.tree.as.*;
 import org.apache.royale.compiler.tree.as.IOperatorNode.OperatorType;
+import org.apache.royale.compiler.tree.mxml.IMXMLSingleDataBindingNode;
 import org.apache.royale.compiler.utils.ASNodeUtils;
 
 import java.util.ArrayList;
@@ -428,6 +431,13 @@ public class MemberAccessEmitter extends JSSubEmitter implements
 				needClosure = !isStatic && parentNodeId != ASTNodeID.FunctionCallID &&
 							parentNodeId != ASTNodeID.MemberAccessExpressionID &&
 							parentNodeId != ASTNodeID.ArrayIndexExpressionID;
+
+				//If binding getterFunctions ever need closures, this seems to be where it would be done (so far not needed, @todo review and remove this when certain)
+				/*if (!needClosure && !isStatic && parentNodeId == ASTNodeID.FunctionCallID) {
+					if (node.getParent().getParent() instanceof IMXMLSingleDataBindingNode) {
+						needClosure = true;
+					}
+				}*/
 		
 				if (needClosure
 						&& getEmitter().getDocEmitter() instanceof JSRoyaleDocEmitter
@@ -458,6 +468,42 @@ public class MemberAccessEmitter extends JSSubEmitter implements
                 {
                     dynamicAccessUnknownMembers = fjsProject.config.getJsDynamicAccessUnknownMembers();
                 }
+                if (!dynamicAccessUnknownMembers) {
+                	//for <fx:Object declarations in mxml, we need to do this by default, because initialization values are set this way already, as are destination bindings, for example.
+					IIdentifierNode checkNode = null;
+					if (leftNode instanceof IIdentifierNode) {
+						//we might be dealing with the direct child member access of an fx:Object
+						checkNode = (IIdentifierNode) leftNode;
+					} else {
+						if (leftNode instanceof MemberAccessExpressionNode) {
+							//if we are nested, check upwards for topmost Identifier node and verify that it is mxml variable of type Object, verifying that we are considered 'untyped' along the way
+							MemberAccessExpressionNode mae = (MemberAccessExpressionNode) leftNode;
+							while (mae != null) {
+								if (mae.getRightOperandNode().resolve(getProject()) == null) {
+									if (mae.getLeftOperandNode() instanceof IIdentifierNode) {
+										checkNode = (IIdentifierNode) mae.getLeftOperandNode();
+										break;
+									} else if (mae.getLeftOperandNode() instanceof MemberAccessExpressionNode) {
+										mae = (MemberAccessExpressionNode) mae.getLeftOperandNode();
+									} else {
+										mae = null;
+									}
+								} else mae = null;
+							}
+						}
+					}
+
+					if (checkNode != null &&
+							checkNode.resolve(getProject()) instanceof VariableDefinition) {
+						VariableDefinition varDef = ((VariableDefinition) (checkNode.resolve(getProject())));
+						if (varDef.isMXMLDeclared()) {
+							IDefinition type = varDef.resolveType(getProject());
+							if (type instanceof IClassDefinition && type.equals(getProject().getBuiltinType(IASLanguageConstants.BuiltinType.OBJECT))) {
+								dynamicAccessUnknownMembers = true;
+							}
+						}
+					}
+				}
             }
 			if (dynamicAccessUnknownMembers && rightNode instanceof IIdentifierNode)
 			{
diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/mxml/royale/MXMLRoyaleEmitter.java b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/mxml/royale/MXMLRoyaleEmitter.java
index 7cd6d24..47162b7 100644
--- a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/mxml/royale/MXMLRoyaleEmitter.java
+++ b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/mxml/royale/MXMLRoyaleEmitter.java
@@ -2956,7 +2956,7 @@ public class MXMLRoyaleEmitter extends MXMLEmitter implements
     	if (!makingSimpleArray)
     	{
             MXMLDescriptorSpecifier ps = getCurrentDescriptor("ps");
-            if (ps.hasObject)
+            if (ps.hasObject || ps.parent == null) //('ps.parent == null' was added to allow a top level fx:Object definition, they were not being output without that)
             {
             	emitInstance(node);
             	return;
diff --git a/compiler/src/main/java/org/apache/royale/compiler/internal/as/codegen/BindableHelper.java b/compiler/src/main/java/org/apache/royale/compiler/internal/as/codegen/BindableHelper.java
index 4bb45a7..299bb41 100644
--- a/compiler/src/main/java/org/apache/royale/compiler/internal/as/codegen/BindableHelper.java
+++ b/compiler/src/main/java/org/apache/royale/compiler/internal/as/codegen/BindableHelper.java
@@ -31,6 +31,8 @@ import org.apache.royale.abc.visitors.ITraitVisitor;
 import org.apache.royale.abc.visitors.ITraitsVisitor;
 import org.apache.royale.compiler.common.DependencyType;
 import org.apache.royale.compiler.constants.IASLanguageConstants;
+import org.apache.royale.compiler.definitions.IAccessorDefinition;
+import org.apache.royale.compiler.definitions.IClassDefinition;
 import org.apache.royale.compiler.definitions.IDefinition;
 import org.apache.royale.compiler.definitions.metadata.IMetaTag;
 import org.apache.royale.compiler.internal.abc.FunctionGeneratorHelper;
@@ -73,16 +75,115 @@ import static org.apache.royale.abc.ABCConstants.TRAIT_Var;
  */
 public class BindableHelper
 {
+
+    /**
+     * Check to see if a class definition is Bindable (for bindable code-gen)
+     * @param classDefinition the Class definition to check
+     * @return true if the definition has [Bindable]
+     */
+    public static boolean isClassCodeGenBindable(IClassDefinition classDefinition) {
+        IMetaTag[] metaTags = classDefinition.getAllMetaTags();
+        boolean isBindable = false;
+        for (IMetaTag metaTag : metaTags)
+        {
+            if (isCodeGenBindable(metaTag)) {
+                //if there is a single [Bindable] tag, then others are ignored, and the class is considered Bindable
+                isBindable = true;
+                break;
+            }
+        }
+        return isBindable;
+    }
+
     /**
      * Check to see if the metaTag is a codegen style [Bindable] tag (without any event names specified)
-     * @param metaTag
+     * @param memberDef the definition to check
+     * @param isClassBindable whether the containing class is [Bindable]
      * @return true if the tag is [Bindable]
      */
+    public static boolean isCodeGenBindableMember(IDefinition memberDef, boolean isClassBindable) {
+        boolean memberHasCodeGenBindable = false;
+        boolean memberHasExplicitBindable = false;
+        boolean resolved = false;
+        IMetaTag[] metaTags = memberDef.getAllMetaTags();
+        for (IMetaTag metaTag : metaTags)
+        {
+            if (!memberHasCodeGenBindable) {
+                memberHasCodeGenBindable = isCodeGenBindable(metaTag);
+                if (memberHasCodeGenBindable) continue;
+            }
+            if (!memberHasExplicitBindable) {
+                memberHasExplicitBindable = isExplicitBindable(metaTag);
+            }
+        }
+
+        if (isClassBindable) {
+            //if a getter/setter member has at least one explicit Bindable tag , then no code-gen is applied, even if it is also has a 'codegen' [Bindable] tag
+           if (memberDef instanceof IAccessorDefinition) {
+                resolved = !memberHasExplicitBindable;
+            } else {
+               //if it is a variable member, extra [Bindable] tags, including explicit Bindable(event='something') tags are ignored (at least for code-gen)
+               resolved = true; // @todo avoid checking at all in the above loop for this case
+           }
+        } else {
+            //in this case other explicit Bindable tags are ignored, a [Bindable] is sufficient @todo avoid checking for 'memberHasExplicitBindable' in the above loop for this case
+            resolved = memberHasCodeGenBindable;
+        }
+        return resolved;
+    }
+
+    public static boolean hasExplicitBindable(IDefinition definition) {
+       boolean hasExplicitBindableTag = false;
+       if (definition != null) {
+           IMetaTag[] metaTags = definition.getAllMetaTags();
+           for (IMetaTag metaTag : metaTags)
+           {
+               if (isExplicitBindable(metaTag)) {
+                   hasExplicitBindableTag = true;
+                   break;
+               }
+           }
+       }
+       return hasExplicitBindableTag;
+    }
+
+
+    public static boolean hasCodegenBindable(IDefinition definition) {
+        boolean hasCodegenBindableTag = false;
+        if (definition != null) {
+            IMetaTag[] metaTags = definition.getAllMetaTags();
+            for (IMetaTag metaTag : metaTags)
+            {
+                if (isCodeGenBindable(metaTag)) {
+                    hasCodegenBindableTag = true;
+                    break;
+                }
+            }
+        }
+        return hasCodegenBindableTag;
+    }
+
+    /**
+     * Check to see if the metaTag is a codegen style [Bindable] tag (without any event names specified)
+     * @param metaTag the MetaTag to check
+     * @return true if the MetaTag is [Bindable]
+     */
     public static boolean isCodeGenBindable(IMetaTag metaTag) {
         return metaTag != null
                 && metaTag.getTagName().equals(BINDABLE)
                 && metaTag.getAllAttributes().length == 0;
     }
+    /**
+     * Check to see if the metaTag is an explicit [Bindable] tag (with some attributes, such as event='someEvent' specified)
+     * @param metaTag the MetaTag to check
+     * @return true if the MetaTag is [Bindable('something')]
+     */
+    public static boolean isExplicitBindable(IMetaTag metaTag) {
+        return metaTag != null
+                && metaTag.getTagName().equals(BINDABLE)
+                && metaTag.getAllAttributes().length > 0;
+    }
+
 
     /**
      * A convenience method to get the next full node after all the Metadata tags, for when
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 82cfa05..2f45c11 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
@@ -744,7 +744,7 @@ class ClassDirectiveProcessor extends DirectiveProcessor
             if (!definition.isContingentNeeded(classScope.getProject()))
                 continue;
 
-            assert (definition instanceof VariableDefinition) : "The code generator only supports contigent variable definitions";
+            assert (definition instanceof VariableDefinition) : "The code generator only supports contingent variable definitions";
             
             final IDefinitionNode node = definition.getNode();
             declareVariable((VariableNode) node, (VariableDefinition)definition, definition.isStatic(),
@@ -779,46 +779,42 @@ class ClassDirectiveProcessor extends DirectiveProcessor
         ICompilerProject project = classScope.getProject();
         ASTUtil.processFunctionNode(func, project);
         boolean isBindable = false;
-
         if (funcDef instanceof AccessorDefinition)
         {
-            boolean isClassBindable = false;
-            IMetaTag[] metaTags = getClassDefinition().getAllMetaTags();
-            for (IMetaTag metaTag : metaTags)
-            {
-                if (BindableHelper.isCodeGenBindable(metaTag)) {
-                    isClassBindable = true;
-                    break;
-                }
-            }
+            boolean isClassBindable = BindableHelper.isClassCodeGenBindable(getClassDefinition());
+
             AccessorDefinition definitionWithBindable = null;
+            boolean foundExplicitBindableTag = false;
 
-            metaTags = funcDef.getAllMetaTags();
-            for (IMetaTag metaTag : metaTags)
-            {
-                if (BindableHelper.isCodeGenBindable(metaTag)) {
-                    isBindable = true;
-                    definitionWithBindable = (AccessorDefinition)funcDef;
-                    break;
-                }
+            isBindable = BindableHelper.isCodeGenBindableMember(funcDef, isClassBindable);
+            if (isBindable) {
+                definitionWithBindable = (AccessorDefinition) funcDef;
             }
+            foundExplicitBindableTag = BindableHelper.hasExplicitBindable(funcDef);
+
             AccessorDefinition otherDef =
                     ((AccessorDefinition)funcDef).resolveCorrespondingAccessor(classScope.getProject());
-            if (!isBindable)
+            if (!isBindable && !foundExplicitBindableTag)
             {
                 if (otherDef != null && otherDef.getContainingScope().equals(funcDef.getContainingScope()))
                 {
-                    metaTags = otherDef.getAllMetaTags();
-                    for (IMetaTag metaTag : metaTags)
-                    {
-                        if (BindableHelper.isCodeGenBindable(metaTag)) {
-                            isBindable = true;
-                            definitionWithBindable = otherDef;
-                            break;
-                        }
+                    isBindable = BindableHelper.isCodeGenBindableMember(otherDef, isClassBindable);
+                    if (isBindable) {
+                        definitionWithBindable = otherDef;
+                    }
+                    foundExplicitBindableTag = BindableHelper.hasExplicitBindable(otherDef);
+                    //if a) class is Bindable, and b)there is a [Bindable] tag, and c) there is also a [Bindable(eventName)] tag
+                    //then we can retain the [Bindable] codegen and reset foundExplicitBinding to false (assumption, check in flex)
+                    if (isClassBindable && isBindable && foundExplicitBindableTag) {
+                        foundExplicitBindableTag = false; //we will ignore it and keep codegen as well
                     }
                 }
             }
+            if (isBindable && otherDef == null) {
+                //no other definition
+                isBindable = false;
+                //warning if explicit [Bindable] ?
+            }
 
             if (isBindable
                     && otherDef instanceof ISetterDefinition
@@ -828,24 +824,32 @@ class ClassDirectiveProcessor extends DirectiveProcessor
                 classScope.addProblem(new BindableGetterCodeGenProblem((IGetterDefinition) funcDef));
             }
             if (isClassBindable) {
+                //add a warning if redundant local 'codegen' [Bindable] tag
+                if (BindableHelper.hasCodegenBindable(definitionWithBindable)) {
+                    classScope.addProblem(new RedundantBindableTagProblem(
+                            BindableHelper.getProblemReportingNode(definitionWithBindable)
+                    ));
+                }
                 if (isBindable) {
-                    if (definitionWithBindable == funcDef) { //if we don't do this, we will end up with two reports
-                        classScope.addProblem(new RedundantBindableTagProblem(
-                                BindableHelper.getProblemReportingNode(definitionWithBindable)
-                        ));
-                    }
-                } else {
-                    isBindable = true;
+                    if (!foundExplicitBindableTag && BindableHelper.hasExplicitBindable(otherDef)) foundExplicitBindableTag = true;
+                }
+                if (foundExplicitBindableTag) {
+                    //logic:
+                    //when class is [Bindable],
+                    //if either getter/setter member has at least one explicit Bindable tag (Bindable(event='something'))
+                    //then no code-gen is applied,
+                    //even if one of them also has a local 'codegen' [Bindable] tag
+                    isBindable = false;
                 }
             }
         }
-        
+
         functionSemanticChecks(func);
 
         Name funcName = funcDef.getMName(classScope.getProject());
         Name bindableName = null;
         boolean wasOverride = false;
-        //we only mutate the setter definitions, leave getters as-is
+        //from here on we are only interested in the setter definitions, leave getters as-is
         isBindable = isBindable && funcDef instanceof ISetterDefinition;
         if (isBindable)
         {
@@ -1471,7 +1475,7 @@ class ClassDirectiveProcessor extends DirectiveProcessor
                 this.cinitInsns.addAll(cgResult);
             else
                 this.iinitInsns.addAll(cgResult);
-            }
+        }
     }
     
     /**
diff --git a/compiler/src/main/java/org/apache/royale/compiler/internal/codegen/databinding/BindingCodeGenUtils.java b/compiler/src/main/java/org/apache/royale/compiler/internal/codegen/databinding/BindingCodeGenUtils.java
index 250e351..b4c6614 100644
--- a/compiler/src/main/java/org/apache/royale/compiler/internal/codegen/databinding/BindingCodeGenUtils.java
+++ b/compiler/src/main/java/org/apache/royale/compiler/internal/codegen/databinding/BindingCodeGenUtils.java
@@ -37,14 +37,16 @@ import org.apache.royale.abc.visitors.IABCVisitor;
 import org.apache.royale.compiler.constants.IASLanguageConstants;
 import org.apache.royale.compiler.definitions.INamespaceDefinition;
 import org.apache.royale.compiler.internal.abc.FunctionGeneratorHelper;
+import org.apache.royale.compiler.internal.as.codegen.CmcEmitter;
 import org.apache.royale.compiler.internal.as.codegen.CodeGeneratorManager;
 import org.apache.royale.compiler.internal.as.codegen.LexicalScope;
 import org.apache.royale.compiler.internal.definitions.NamespaceDefinition;
 import org.apache.royale.compiler.internal.projects.RoyaleProject;
 import org.apache.royale.compiler.internal.scopes.ASScope;
-import org.apache.royale.compiler.internal.tree.as.LiteralNode;
+import org.apache.royale.compiler.internal.tree.as.*;
 import org.apache.royale.compiler.mxml.IMXMLTypeConstants;
 import org.apache.royale.compiler.projects.ICompilerProject;
+import org.apache.royale.compiler.tree.as.IASNode;
 import org.apache.royale.compiler.tree.as.IExpressionNode;
 
 /**
@@ -376,11 +378,12 @@ public class BindingCodeGenUtils
             String functionName,
             List<String> eventNames,
             List<BindingInfo> bindingInfo,
-            IExpressionNode[] params)
+            IExpressionNode[] params,
+            LexicalScope lexicalScope)
     {
         
         log(insns, "enter makeFunctionWatchercg");
-       
+
         Name watcherClassName = project.getFunctionReturnWatcherClassName();
         insns.addInstruction(OP_findpropstrict, watcherClassName);
         // stack: mx.binding.FunctionReturnWatcher
@@ -404,7 +407,7 @@ public class BindingCodeGenUtils
         // stack: this, functionName, watcher class
         
         // arg3: parameterFunction
-        makeParameterFunction(emitter, insns, params);
+        makeParameterFunction(emitter, insns, params, lexicalScope);
         
         // arg4: events
         boolean isStyle = makeEventNameArray(insns, eventNames);
@@ -450,7 +453,7 @@ public class BindingCodeGenUtils
     
     // version 2: just throw
     
-    public static void makeParameterFunction(IABCVisitor emitter, InstructionList ret, IExpressionNode[] params)
+    public static void makeParameterFunction(IABCVisitor emitter, InstructionList ret, IExpressionNode[] params, LexicalScope lexicalScope)
     {
        
         //----------- step 1: build up a method info for the function
@@ -471,26 +474,53 @@ public class BindingCodeGenUtils
            
         parameterFunctionBody.addInstruction(OP_getlocal0);
         parameterFunctionBody.addInstruction(OP_pushscope);
-        
-        if (params.length == 1 && params[0] instanceof LiteralNode)
-        {
-            parameterFunctionBody.addInstruction(OP_pushstring, ((LiteralNode)params[0]).getValue());
-            
-            parameterFunctionBody.addInstruction(OP_newarray, 1);
-            parameterFunctionBody.addInstruction(OP_returnvalue);            
-        }
-        else
-        {
-            parameterFunctionBody.addInstruction(OP_findpropstrict, IMXMLTypeConstants.NAME_ARGUMENTERROR);
-    
-            // Make a new ArugmentError with correct ID
-            parameterFunctionBody.addInstruction(OP_pushstring, "unimp param func");
-            parameterFunctionBody.pushNumericConstant(1069);
-            parameterFunctionBody.addInstruction(OP_constructprop,  
-                    new Object[] {  IMXMLTypeConstants.NAME_ARGUMENTERROR, 2 });     
-            // stack : ArgumentError
-               
-            parameterFunctionBody.addInstruction(OP_throw);   
+
+        if (params.length == 0) {
+            //it is a function call with no params
+            parameterFunctionBody.addInstruction(OP_newarray, 0);
+            parameterFunctionBody.addInstruction(OP_returnvalue);
+        } else {
+
+            //create a temporary ArrayLiteralNode
+            ArrayLiteralNode arrayLit = new ArrayLiteralNode();
+
+            for (IASNode param: params) {
+                ContainerNode parent = (ContainerNode)param.getParent();
+                //make the array literal think it has the param as a child
+                arrayLit.getContentsNode().addItem((NodeBase)param);
+                //(tricky) now reset the parent of the param, avoid errors with ASScope access during traversal,
+                //we only really want the downward traversal from the ArrayLiteralNode as the root, we don't want any sense
+                //that it is *actually* a parent, because it is not ASScope aware.
+                ((NodeBase) param).setParent(parent);
+                if (arrayLit.getSourcePath()==null) {
+                    //give the synthetic node a location to avoid an error elsewhere
+                    //setting the source path here will prevent an assertion failure in a future call to SemanticUtils.getFileName(iNode) if
+                    //asserts are enabled for the compiler runtime (at the time of writing, ant was and maven was not)
+                    arrayLit.setSourceLocation(param);
+                } else {
+                   arrayLit.endAfter(param);
+                }
+            }
+
+            //generate the instruction list for the Arrayliteral
+            InstructionList list = lexicalScope.getGenerator().generateInstructions(arrayLit, CmcEmitter.__array_literal_NT, lexicalScope);
+            if (list != null && !list.isEmpty()) {
+                //output the array literal and return it
+                parameterFunctionBody.addAll(list);
+                parameterFunctionBody.addInstruction(OP_returnvalue);
+            } else {
+                //sanity check, this is the original legacy fail approach @todo review this, hopefully it can be removed if it never happens in practice
+                parameterFunctionBody.addInstruction(OP_findpropstrict, IMXMLTypeConstants.NAME_ARGUMENTERROR);
+
+                // Make a new ArugmentError with correct ID
+                parameterFunctionBody.addInstruction(OP_pushstring, "unimp param func");
+                parameterFunctionBody.pushNumericConstant(1069);
+                parameterFunctionBody.addInstruction(OP_constructprop,
+                        new Object[] {  IMXMLTypeConstants.NAME_ARGUMENTERROR, 2 });
+                // stack : ArgumentError
+
+                parameterFunctionBody.addInstruction(OP_throw);
+            }
         }
         
         // now generate the function
diff --git a/compiler/src/main/java/org/apache/royale/compiler/internal/codegen/databinding/MXMLBindingDirectiveHelper.java b/compiler/src/main/java/org/apache/royale/compiler/internal/codegen/databinding/MXMLBindingDirectiveHelper.java
index 7ea4cc4..a5a2fe6 100644
--- a/compiler/src/main/java/org/apache/royale/compiler/internal/codegen/databinding/MXMLBindingDirectiveHelper.java
+++ b/compiler/src/main/java/org/apache/royale/compiler/internal/codegen/databinding/MXMLBindingDirectiveHelper.java
@@ -288,7 +288,7 @@ public class MXMLBindingDirectiveHelper
            
             ret.addInstruction(OP_pushstring, functionWatcherInfo.getFunctionName());
             InstructionList paramFunction = new InstructionList();
-            BindingCodeGenUtils.makeParameterFunction(emitter, paramFunction, functionWatcherInfo.params);
+            BindingCodeGenUtils.makeParameterFunction(emitter, paramFunction, functionWatcherInfo.params, host.getInstanceScope());
             ret.addAll(paramFunction);
             outputEventNames(ret, functionWatcherInfo.getEventNames());
             outputBindings(ret, functionWatcherInfo.getBindings());
@@ -916,7 +916,8 @@ public class MXMLBindingDirectiveHelper
                     functionWatcherInfo.getFunctionName(),
                     functionWatcherInfo.getEventNames(),
                     functionWatcherInfo.getBindings(),
-                    functionWatcherInfo.params);
+                    functionWatcherInfo.params,
+                    host.getInstanceScope());
         }
         else if ((type == WatcherType.STATIC_PROPERTY) || (type == WatcherType.PROPERTY))
         {
diff --git a/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/DefinitionBase.java b/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/DefinitionBase.java
index 41da6b2..c822686 100644
--- a/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/DefinitionBase.java
+++ b/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/DefinitionBase.java
@@ -1348,17 +1348,23 @@ public abstract class DefinitionBase implements IDocumentableDefinition, IDefini
         boolean bindable = isBindableLocally();
         if (!bindable)
         {
-            IDefinition containing = getParent();
-            if (containing instanceof IClassDefinition
-                    && this.getNamespaceReference() == NamespaceDefinition.getPublicNamespaceDefinition())
-            {
-                DefinitionBase containingBase = (DefinitionBase)containing;
-                bindable = containingBase.isBindableLocally();
-            }
+           bindable = isClassBindable();
         }
         return bindable;
     }
 
+    public boolean isClassBindable() {
+        boolean classBindable = false;
+        IDefinition containing = getParent();
+        if (containing instanceof IClassDefinition
+                && this.getNamespaceReference() == NamespaceDefinition.getPublicNamespaceDefinition())
+        {
+            DefinitionBase containingBase = (DefinitionBase)containing;
+            classBindable = containingBase.isBindableLocally();
+        }
+        return classBindable;
+    }
+
     /**
      * @return a collection of Bindable event names, if any were specified in
      * the metadata, as well as any inherited from the class
diff --git a/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/VariableDefinition.java b/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/VariableDefinition.java
index a465894..2f8a1e9 100644
--- a/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/VariableDefinition.java
+++ b/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/VariableDefinition.java
@@ -136,6 +136,15 @@ public class VariableDefinition extends DefinitionBase implements IVariableDefin
         return dt;
     }
 
+    private boolean mxmlDeclared = false;
+    public void setMxmlDeclared(){
+        mxmlDeclared = true;
+    }
+
+    public boolean isMXMLDeclared() {
+        return mxmlDeclared;
+    }
+
     @Override
     public IVariableNode getNode()
     {
diff --git a/compiler/src/main/java/org/apache/royale/compiler/internal/parsing/mxml/MXMLScopeBuilder.java b/compiler/src/main/java/org/apache/royale/compiler/internal/parsing/mxml/MXMLScopeBuilder.java
index a2af339..74b308d 100644
--- a/compiler/src/main/java/org/apache/royale/compiler/internal/parsing/mxml/MXMLScopeBuilder.java
+++ b/compiler/src/main/java/org/apache/royale/compiler/internal/parsing/mxml/MXMLScopeBuilder.java
@@ -772,6 +772,7 @@ public class MXMLScopeBuilder
         variableDefinition.setTypeReference(typeRef);
         variableDefinition.setNameLocation(idAttribute.getValueStart(), idAttribute.getValueEnd());
         variableDefinition.setBindable();
+        variableDefinition.setMxmlDeclared();
         currentClassScope.addDefinition(variableDefinition);
     }
     
diff --git a/compiler/src/main/java/org/apache/royale/compiler/internal/tree/as/VariableNode.java b/compiler/src/main/java/org/apache/royale/compiler/internal/tree/as/VariableNode.java
index 8deb160..23c8ed4 100644
--- a/compiler/src/main/java/org/apache/royale/compiler/internal/tree/as/VariableNode.java
+++ b/compiler/src/main/java/org/apache/royale/compiler/internal/tree/as/VariableNode.java
@@ -27,6 +27,7 @@ import org.apache.royale.compiler.common.ASImportTarget;
 import org.apache.royale.compiler.common.IImportTarget;
 import org.apache.royale.compiler.definitions.IDefinition;
 import org.apache.royale.compiler.definitions.IVariableDefinition.VariableClassification;
+import org.apache.royale.compiler.internal.definitions.DefinitionBase;
 import org.apache.royale.compiler.tree.ASTNodeID;
 import org.apache.royale.compiler.tree.as.IASNode;
 import org.apache.royale.compiler.tree.as.ICommonClassNode;
@@ -76,14 +77,23 @@ public class VariableNode extends BaseVariableNode
     {
         ASTNodeID nodeID = ASTNodeID.VariableID;
 
-        IDefinition varDef = this.getDefinition();
+        DefinitionBase varDef = this.getDefinition();
         if (varDef != null && varDef.isBindable())
         {
             // Bindable vars with a user-specified event class work the same as normal variables
             // the user is responsible for dispatching the event.
-            List<String> eventNames = varDef.getBindableEventNames();
-            if (eventNames.size() == 0)
+
+            //note: the above statement was inconsistent with Flex if the class itself is also marked [Bindable]
+            //in that case, binding is related to both codegen events and the specified event.
+            if (varDef.isClassBindable()) {
+                //we know that isBindable() must be because of the class [Bindable], regardless of any specific eventNames
                 nodeID = ASTNodeID.BindableVariableID;
+            } else {
+                //as original, only consider to be [Bindable] if eventNames is empty:
+                List<String> eventNames = varDef.getBindableEventNames();
+                if (eventNames.size() == 0)
+                    nodeID = ASTNodeID.BindableVariableID;
+            }
         }
 
         return nodeID;