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 18cb7cd27 Compiler changes to support various XMLish String coercions 
wihch avoid Language.string and other cases which cause implicit valueOf() 
calls - forcing toString() calls instead in relevant scenarios.
18cb7cd27 is described below

commit 18cb7cd27a8c1bd63069f6c6067f8b0dc9274906
Author: greg-dove <[email protected]>
AuthorDate: Mon Jul 11 17:25:29 2022 +1200

    Compiler changes to support various XMLish String coercions wihch avoid 
Language.string and other cases which cause implicit valueOf() calls - forcing 
toString() calls instead in relevant scenarios.
---
 .../compiler/internal/codegen/js/JSEmitter.java    | 45 +++++++----------
 .../codegen/js/jx/BinaryOperatorEmitter.java       | 56 ++++++++++++++++++++++
 .../codegen/js/jx/FunctionCallEmitter.java         | 30 +++++++++++-
 .../internal/codegen/js/utils/EmitterUtils.java    | 43 +++++++++++++++++
 .../codegen/js/royale/TestRoyaleExpressions.java   |  6 +--
 .../codegen/js/royale/TestRoyaleGlobalClasses.java |  4 +-
 6 files changed, 149 insertions(+), 35 deletions(-)

diff --git 
a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/JSEmitter.java
 
b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/JSEmitter.java
index 6ba481ca3..85f069c5e 100644
--- 
a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/JSEmitter.java
+++ 
b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/JSEmitter.java
@@ -68,38 +68,13 @@ import 
org.apache.royale.compiler.internal.codegen.js.jx.WhileLoopEmitter;
 import org.apache.royale.compiler.internal.codegen.js.jx.WithEmitter;
 import 
org.apache.royale.compiler.internal.codegen.js.royale.JSRoyaleDocEmitter;
 import 
org.apache.royale.compiler.internal.codegen.js.royale.JSRoyaleEmitterTokens;
+import org.apache.royale.compiler.internal.codegen.js.utils.EmitterUtils;
 import org.apache.royale.compiler.internal.definitions.ParameterDefinition;
 import org.apache.royale.compiler.internal.projects.RoyaleJSProject;
 import org.apache.royale.compiler.internal.semantics.SemanticUtils;
 import org.apache.royale.compiler.internal.tree.as.*;
 import org.apache.royale.compiler.projects.ICompilerProject;
-import org.apache.royale.compiler.tree.as.IASNode;
-import org.apache.royale.compiler.tree.as.ICatchNode;
-import org.apache.royale.compiler.tree.as.IContainerNode;
-import org.apache.royale.compiler.tree.as.IDefinitionNode;
-import org.apache.royale.compiler.tree.as.IDynamicAccessNode;
-import org.apache.royale.compiler.tree.as.IExpressionNode;
-import org.apache.royale.compiler.tree.as.IForLoopNode;
-import org.apache.royale.compiler.tree.as.IFunctionNode;
-import org.apache.royale.compiler.tree.as.IFunctionObjectNode;
-import org.apache.royale.compiler.tree.as.IIfNode;
-import org.apache.royale.compiler.tree.as.IImportNode;
-import org.apache.royale.compiler.tree.as.IIterationFlowNode;
-import org.apache.royale.compiler.tree.as.ILanguageIdentifierNode;
-import org.apache.royale.compiler.tree.as.ILiteralContainerNode;
-import org.apache.royale.compiler.tree.as.INumericLiteralNode;
-import org.apache.royale.compiler.tree.as.IObjectLiteralValuePairNode;
-import org.apache.royale.compiler.tree.as.IParameterNode;
-import org.apache.royale.compiler.tree.as.IReturnNode;
-import org.apache.royale.compiler.tree.as.ISwitchNode;
-import org.apache.royale.compiler.tree.as.ITernaryOperatorNode;
-import org.apache.royale.compiler.tree.as.IThrowNode;
-import org.apache.royale.compiler.tree.as.ITryNode;
-import org.apache.royale.compiler.tree.as.ITypeNode;
-import org.apache.royale.compiler.tree.as.ITypedExpressionNode;
-import org.apache.royale.compiler.tree.as.IUnaryOperatorNode;
-import org.apache.royale.compiler.tree.as.IWhileLoopNode;
-import org.apache.royale.compiler.tree.as.IWithNode;
+import org.apache.royale.compiler.tree.as.*;
 
 import com.google.debugging.sourcemap.FilePosition;
 
@@ -549,18 +524,22 @@ public class JSEmitter extends ASEmitter implements 
IJSEmitter
         IDefinition assignedDef = null;
         IDefinition assignedTypeDef = null;
         ICompilerProject project = getWalker().getProject();
+        boolean isXML = false;
         if (assignedNode != null)
         {
             assignedDef = assignedNode.resolve(project);
             assignedTypeDef = assignedNode.resolveType(project);
-            if 
(project.getBuiltinType(BuiltinType.ANY_TYPE).equals(assignedTypeDef))
+            if (assignedTypeDef == null || 
project.getBuiltinType(BuiltinType.ANY_TYPE).equals(assignedTypeDef))
             {
                 IDefinition resolvedXMLDef = 
SemanticUtils.resolveXML(assignedNode, project);
                 if (resolvedXMLDef != null)
                 {
                     assignedDef = resolvedXMLDef;
                     assignedTypeDef = 
SemanticUtils.resolveTypeXML(assignedNode, project);
+                    isXML = true;
                 }
+            } else if (SemanticUtils.isXMLish(assignedTypeDef, project)) {
+                isXML = true;
             }
         }
                String coercionStart = null;
@@ -724,7 +703,15 @@ public class JSEmitter extends ASEmitter implements 
IJSEmitter
             }
             if (emitStringCoercion)
             {
-                coercionStart = "org.apache.royale.utils.Language.string(";
+                if (isXML) {
+                    if (EmitterUtils.xmlRequiresNullCheck((NodeBase) 
assignedNode, project)) {
+                        //if it could be a null reference use the 
XMLList.coerce_string method, which retains null
+                        coercionStart = "XMLList.coerce_string(";
+                    } else {
+                        coercionStart = "String(";
+                    }
+                }
+                else coercionStart = 
"org.apache.royale.utils.Language.string(";
             }
         }
         if ( assignedDef != null
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 38a3f3e27..5a1890d46 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
@@ -34,6 +34,7 @@ import 
org.apache.royale.compiler.internal.codegen.js.JSSubEmitter;
 import org.apache.royale.compiler.internal.codegen.js.royale.JSRoyaleEmitter;
 import 
org.apache.royale.compiler.internal.codegen.js.royale.JSRoyaleEmitterTokens;
 import org.apache.royale.compiler.internal.codegen.js.goog.JSGoogEmitterTokens;
+import org.apache.royale.compiler.internal.codegen.js.utils.EmitterUtils;
 import org.apache.royale.compiler.internal.definitions.AccessorDefinition;
 import org.apache.royale.compiler.internal.definitions.AppliedVectorDefinition;
 import org.apache.royale.compiler.internal.definitions.NamespaceDefinition;
@@ -601,6 +602,61 @@ public class BinaryOperatorEmitter extends JSSubEmitter 
implements
                                                }
                                        }
                                }
+                       } else if (id == ASTNodeID.Op_AddID) {
+                               IDefinition rightDef = 
node.getRightOperandNode().resolveType(getProject());
+                               boolean leftIsXMLish = 
(SemanticUtils.isXMLish(node.getLeftOperandNode(), getProject())) || 
SemanticUtils.isXMLish(leftDef, getProject());
+                               boolean rightIsXMLish = 
(SemanticUtils.isXMLish(node.getRightOperandNode(), getProject())) || 
SemanticUtils.isXMLish(rightDef, getProject());
+                               boolean process;
+                               if (leftIsXMLish) {
+                                       process = !rightIsXMLish;
+                               } else {
+                                       process = rightIsXMLish;
+                               }
+                               if (process) {
+                                       IASNode codeContext = 
node.getAncestorOfType(IClassNode.class);
+                                       boolean isFrameworkXML = false;
+                                       if (codeContext instanceof IClassNode) {
+                                               if (((IClassNode) 
codeContext).getQualifiedName().equals("XML") || ((IClassNode) 
codeContext).getQualifiedName().equals("XMLList")) {
+                                                       //we will ignore the 
internal code of the emulation support classes for these cases
+                                                       isFrameworkXML = true;
+                                               }
+                                       }
+                                       if (!isFrameworkXML) {
+                                               IExpressionNode leftOperand = 
node.getLeftOperandNode();
+                                               IExpressionNode rightOperand = 
node.getRightOperandNode();
+                                               FunctionCallNode 
functionCallNode;
+                                               if (leftIsXMLish) {
+                                                       //wrap in string 
coercion
+                                                       if 
(EmitterUtils.xmlRequiresNullCheck((NodeBase) leftOperand, getProject())) {
+                                                               //if it is a 
simple identifier, then it could be a null reference so use the 
XMLList.coerce_string method, which retains null
+                                                               
functionCallNode = EmitterUtils.wrapXMLListStringCoercion((NodeBase) 
leftOperand);
+                                                       } else {
+                                                               //if it is a 
member access expression or something else then assume we don't have to check 
for null
+                                                               
functionCallNode = EmitterUtils.wrapSimpleStringCoercion((NodeBase) 
leftOperand);
+                                                       }
+                                                       
functionCallNode.setParent((NodeBase) node);
+                                                       leftOperand = 
functionCallNode;
+                                               } else {
+                                                       //wrap in string 
coercion
+                                                       if 
(EmitterUtils.xmlRequiresNullCheck((NodeBase) rightOperand, getProject())) {
+                                                               //if it is a 
simple identifier, then it could be a null reference so use the 
XMLList.coerce_string method, which retains null
+                                                               
functionCallNode = EmitterUtils.wrapXMLListStringCoercion((NodeBase) 
rightOperand);
+                                                       } else {
+                                                               //if it is a 
member access expression or something else then assume we don't have to check 
for null
+                                                               
functionCallNode = EmitterUtils.wrapSimpleStringCoercion((NodeBase) 
rightOperand);
+                                                       }
+                                                       
functionCallNode.setParent((NodeBase) node);
+                                                       rightOperand = 
functionCallNode;
+                                               }
+                                               getWalker().walk(leftOperand);
+                                               write(ASEmitterTokens.SPACE);
+                                               
writeToken(ASEmitterTokens.PLUS);
+                                               write(ASEmitterTokens.SPACE);
+                                               getWalker().walk(rightOperand);
+                                               return;
+                                       }
+                               }
+
                        }
                        
                
diff --git 
a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/FunctionCallEmitter.java
 
b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/FunctionCallEmitter.java
index 3653baeb9..75e478d83 100644
--- 
a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/FunctionCallEmitter.java
+++ 
b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/FunctionCallEmitter.java
@@ -39,6 +39,7 @@ import 
org.apache.royale.compiler.internal.codegen.js.utils.EmitterUtils;
 import org.apache.royale.compiler.internal.definitions.*;
 import org.apache.royale.compiler.internal.projects.RoyaleJSProject;
 import org.apache.royale.compiler.internal.scopes.FunctionScope;
+import org.apache.royale.compiler.internal.semantics.SemanticUtils;
 import org.apache.royale.compiler.internal.tree.as.*;
 import org.apache.royale.compiler.problems.ProjectSpecificErrorProblem;
 import org.apache.royale.compiler.problems.TooFewFunctionParametersProblem;
@@ -68,6 +69,7 @@ public class FunctionCallEmitter extends JSSubEmitter 
implements ISubEmitter<IFu
             cnode = cnode.getChild(0);
         String postCallAppend = null;
         ASTNodeID id = cnode.getNodeID();
+        boolean numericCast = false;
         if (id != ASTNodeID.SuperID)
         {
             IDefinition def = null;
@@ -97,6 +99,7 @@ public class FunctionCallEmitter extends JSSubEmitter 
implements ISubEmitter<IFu
                 )
                 {
                     omitNew = true;
+                    numericCast = ((IdentifierNode) 
nameNode).getName().equals(IASLanguageConstants.Number);
                 }
                 
                 if (!((node.getChild(1) instanceof VectorLiteralNode)))
@@ -221,7 +224,10 @@ public class FunctionCallEmitter extends JSSubEmitter 
implements ISubEmitter<IFu
                         && !(NativeUtils.isJSNative(def.getBaseName()))
                         && !def.getBaseName().equals(IASLanguageConstants.XML)
                         && 
!def.getBaseName().equals(IASLanguageConstants.XMLList);
-                
+
+                if (!isClassCast && def != null && 
def.getQualifiedName().equals( IASLanguageConstants.Number)){
+                    numericCast = true;
+                }
             }
             
             if (node.isNewExpression())
@@ -244,6 +250,7 @@ public class FunctionCallEmitter extends JSSubEmitter 
implements ISubEmitter<IFu
                             write(JSRoyaleEmitterTokens.UNDERSCORE);
                         write(def.getQualifiedName());
                         endMapping(nameNode);
+                        numericCast = true;
                     } else if( def.getQualifiedName().equals("QName")
                             && getModel().defaultXMLNamespaceActive
                             //is the execution context relevant
@@ -416,6 +423,7 @@ public class FunctionCallEmitter extends JSSubEmitter 
implements ISubEmitter<IFu
                         if (isInt)
                             write(JSRoyaleEmitterTokens.UNDERSCORE);
                         endMapping(node.getNameNode());
+                        numericCast = true;
                     }
                     else if (def.getBaseName().equals("sortOn"))
                        {
@@ -721,6 +729,26 @@ public class FunctionCallEmitter extends JSSubEmitter 
implements ISubEmitter<IFu
                     getWalker().walk(node.getNameNode());
                 }
 
+                if (numericCast ) {
+                    if (node.getArgumentsNode().getChildCount() == 1  && 
SemanticUtils.isXMLish((IExpressionNode) node.getArgumentsNode().getChild(0), 
getProject())) {
+                        //"patch" the argument node by wrapping in string 
coercion
+                        ContainerNode args = node.getArgumentsNode();
+                        NodeBase origArg = (NodeBase) args.getChild(0);
+                        args.removeItem(origArg);
+
+                        FunctionCallNode stringCast;
+
+                        if (EmitterUtils.xmlRequiresNullCheck(origArg, 
getProject())) {
+                            //if it is a simple identifier, then it could be a 
null reference so use the XMLList.coerce_string method, which retains null
+                            stringCast = 
EmitterUtils.wrapXMLListStringCoercion(origArg);
+                        } else {
+                            //if it is a member access expression or something 
else then assume we don't have to check for null
+                            stringCast = 
EmitterUtils.wrapSimpleStringCoercion(origArg);
+                        }
+
+                        args.addItemAfterNormalization(stringCast);
+                    }
+                }
 
                 getEmitter().emitArguments(node.getArgumentsNode());
     
diff --git 
a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/utils/EmitterUtils.java
 
b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/utils/EmitterUtils.java
index d5a9b0fcc..6f0058460 100644
--- 
a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/utils/EmitterUtils.java
+++ 
b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/utils/EmitterUtils.java
@@ -975,4 +975,47 @@ public class EmitterUtils
         return base + "_" + DefinitionUtils.deltaFromObject(definition, 
project) +"_";
     }
 
+    public static final FunctionCallNode wrapXMLListStringCoercion(NodeBase 
orig) {
+        //create a call to XMLList.coerce_string(orig)
+        IdentifierNode xmllistClassRef = new 
IdentifierNode(IASLanguageConstants.XMLList);
+        IdentifierNode castMethod = new IdentifierNode("coerce_string");
+
+        MemberAccessExpressionNode castNode = new 
MemberAccessExpressionNode(xmllistClassRef,null, castMethod);
+        xmllistClassRef.setParent(castNode);
+        castMethod.setParent(castNode);
+        FunctionCallNode stringCast = new FunctionCallNode(castNode);
+        //explicit parent setting for name and arguments:
+        castNode.setParent(stringCast);
+        stringCast.getArgumentsNode().setParent(stringCast);
+        //wrap the original node
+        stringCast.getArgumentsNode().addItem((NodeBase) orig);
+        return stringCast;
+    }
+
+
+    public static final FunctionCallNode wrapSimpleStringCoercion(NodeBase 
orig) {
+        //create a call to String(orig)
+        IdentifierNode castNode = new 
IdentifierNode(IASLanguageConstants.String);
+        FunctionCallNode stringCast = new FunctionCallNode(castNode);
+        //explicit parent setting for name and arguments:
+        castNode.setParent(stringCast);
+        stringCast.getArgumentsNode().setParent(stringCast);
+        //wrap the original node
+        stringCast.getArgumentsNode().addItem((NodeBase) orig);
+        return stringCast;
+    }
+
+    public static final boolean xmlRequiresNullCheck(NodeBase orig, 
ICompilerProject project) {
+        boolean ret = false;
+        if (orig instanceof IIdentifierNode) {
+            ret = true;
+        } else if (orig instanceof IBinaryOperatorNode) {
+            //should cover IDynamicAccessNode and IMemberAccessExpressionNode
+            //if the left node is xmlish, then we don't need null check
+            ret = !isLeftNodeXMLish(((IBinaryOperatorNode) 
orig).getLeftOperandNode(),project);
+        }
+
+        return ret;
+    }
+
 }
diff --git 
a/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/js/royale/TestRoyaleExpressions.java
 
b/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/js/royale/TestRoyaleExpressions.java
index 38260fae7..7c40e166e 100644
--- 
a/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/js/royale/TestRoyaleExpressions.java
+++ 
b/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/js/royale/TestRoyaleExpressions.java
@@ -472,7 +472,7 @@ public class TestRoyaleExpressions extends 
TestGoogExpressions
     {
         IBinaryOperatorNode node = getBinaryNode("var var1:String;var 
var2:XML;var1 = var2.child");
         asBlockWalker.visitBinaryOperator(node);
-        assertOut("var1 = 
org.apache.royale.utils.Language.string(var2.child('child'))");
+        assertOut("var1 = String(var2.child('child'))");
     }
 
     @Test
@@ -1976,7 +1976,7 @@ public class TestRoyaleExpressions extends 
TestGoogExpressions
     {
         IReturnNode node = (IReturnNode) getNode("function():String { var 
a:XML; return a.child; }", IReturnNode.class);
         asBlockWalker.visitReturn(node);
-        assertOut("return 
org.apache.royale.utils.Language.string(a.child('child'))");
+        assertOut("return String(a.child('child'))");
     }
 
     @Test
@@ -2144,7 +2144,7 @@ public class TestRoyaleExpressions extends 
TestGoogExpressions
     {
         IFunctionCallNode node = (IFunctionCallNode) getNode("function 
a(foo:String):void {}; var b:XML; a(b.child);", IFunctionCallNode.class);
         asBlockWalker.visitFunctionCall(node);
-        
assertOut("a(org.apache.royale.utils.Language.string(b.child('child')))");
+        assertOut("a(String(b.child('child')))");
     }
 
     @Test
diff --git 
a/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/js/royale/TestRoyaleGlobalClasses.java
 
b/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/js/royale/TestRoyaleGlobalClasses.java
index d93c34ee6..582b920b9 100644
--- 
a/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/js/royale/TestRoyaleGlobalClasses.java
+++ 
b/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/js/royale/TestRoyaleGlobalClasses.java
@@ -1188,7 +1188,7 @@ public class TestRoyaleGlobalClasses extends 
TestGoogGlobalClasses
         IASNode parentNode = node.getParent();
         node = (IVariableNode) parentNode.getChild(1);
         asBlockWalker.visitVariable(node);
-        assertOut("var /** @type {string} */ b = 
org.apache.royale.utils.Language.string(a.attribute('attr1'))");
+        assertOut("var /** @type {string} */ b = 
String(a.attribute('attr1'))");
     }
     
     @Test
@@ -1196,7 +1196,7 @@ public class TestRoyaleGlobalClasses extends 
TestGoogGlobalClasses
     {
        IBinaryOperatorNode node = (IBinaryOperatorNode)getNode("var a:XML = 
new XML(\"<top attr1='cat'><child attr2='dog'><grandchild 
attr3='fish'>text</grandchild></child></top>\");var b:String; b = a.@attr1;", 
IBinaryOperatorNode.class);
         asBlockWalker.visitBinaryOperator(node);
-        assertOut("b = 
org.apache.royale.utils.Language.string(a.attribute('attr1'))");
+        assertOut("b = String(a.attribute('attr1'))");
     }
     
     @Test

Reply via email to