You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@royale.apache.org by jo...@apache.org on 2019/03/20 22:30:09 UTC

[royale-compiler] branch develop updated: compiler-jx: fixed hoisting of chained variable declarations

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


The following commit(s) were added to refs/heads/develop by this push:
     new 5a7dd6d  compiler-jx: fixed hoisting of chained variable declarations
5a7dd6d is described below

commit 5a7dd6d2ae956ca78602fcfe7a8f36a3ad1a638c
Author: Josh Tynjala <jo...@apache.org>
AuthorDate: Wed Mar 20 15:29:57 2019 -0700

    compiler-jx: fixed hoisting of chained variable declarations
---
 .../codegen/js/jx/VarDeclarationEmitter.java       | 163 ++++++++++++++-------
 .../codegen/js/royale/JSRoyaleEmitter.java         |  68 +++++----
 .../codegen/js/royale/TestDefaultInitializers.java |  58 ++++++--
 .../codegen/js/royale/TestRoyaleStatements.java    |   4 +-
 4 files changed, 201 insertions(+), 92 deletions(-)

diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/VarDeclarationEmitter.java b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/VarDeclarationEmitter.java
index 12b1443..70be284 100644
--- a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/VarDeclarationEmitter.java
+++ b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/VarDeclarationEmitter.java
@@ -26,6 +26,7 @@ import org.apache.royale.compiler.definitions.IDefinition;
 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;
+import org.apache.royale.compiler.internal.codegen.js.utils.EmitterUtils;
 import org.apache.royale.compiler.internal.projects.RoyaleJSProject;
 import org.apache.royale.compiler.internal.tree.as.ChainedVariableNode;
 import org.apache.royale.compiler.projects.ICompilerProject;
@@ -33,6 +34,7 @@ import org.apache.royale.compiler.tree.ASTNodeID;
 import org.apache.royale.compiler.tree.as.IASNode;
 import org.apache.royale.compiler.tree.as.IEmbedNode;
 import org.apache.royale.compiler.tree.as.IExpressionNode;
+import org.apache.royale.compiler.tree.as.IFunctionNode;
 import org.apache.royale.compiler.tree.as.IVariableNode;
 
 /**
@@ -56,6 +58,26 @@ public class VarDeclarationEmitter extends JSSubEmitter implements
 
         getModel().getVars().add(node);
         
+        boolean defaultInitializers = false;
+        ICompilerProject project = getProject();
+        if(project instanceof RoyaleJSProject)
+        {
+            RoyaleJSProject fjsProject = (RoyaleJSProject) project; 
+            if(fjsProject.config != null)
+            {
+                defaultInitializers = fjsProject.config.getJsDefaultInitializers();
+            }
+        }
+        boolean needsDefaultValue = EmitterUtils.needsDefaultValue(node, defaultInitializers, getProject());
+
+        IFunctionNode parentFnNode = (IFunctionNode) node.getAncestorOfType(IFunctionNode.class);
+        boolean isHoisting = fjs.isEmittingHoistedNodes(parentFnNode);
+        
+        if (!(node instanceof ChainedVariableNode) && !isHoisting && needsDefaultValue)
+        {
+            write("//");
+        }
+
         if (!(node instanceof ChainedVariableNode) && !node.isConst())
         {
             fjs.emitMemberKeyword(node);
@@ -89,12 +111,12 @@ public class VarDeclarationEmitter extends JSSubEmitter implements
             String opcode = avnode.getNodeID().getParaphrase();
             if (opcode != "AnonymousFunction")
             {
-                fjs.getDocEmitter().emitVarDoc(node, avtypedef, getWalker().getProject());
+                fjs.getDocEmitter().emitVarDoc(node, avtypedef, getProject());
             }
         }
         else
         {
-            fjs.getDocEmitter().emitVarDoc(node, null, getWalker().getProject());
+            fjs.getDocEmitter().emitVarDoc(node, null, getProject());
         }
         endMapping(variableTypeNode);
 
@@ -104,7 +126,12 @@ public class VarDeclarationEmitter extends JSSubEmitter implements
         }
 
         fjs.emitDeclarationName(node);
-        if (avnode != null && !(avnode instanceof IEmbedNode))
+
+        if (avnode == null)
+        {
+            emitDefaultInitializer(node, defaultInitializers);
+        }
+        else if(!(avnode instanceof IEmbedNode))
         {
             if (hasVariableType)
             {
@@ -119,79 +146,117 @@ public class VarDeclarationEmitter extends JSSubEmitter implements
             endMapping(node);
             getEmitter().emitAssignmentCoercion(avnode, variableDef);
         }
-        if (avnode == null)
+
+        emitChainedVariables(node, defaultInitializers, isHoisting);
+    }
+
+    private void emitDefaultInitializer(IVariableNode node, boolean defaultInitializers)
+    {
+        IDefinition typedef = null;
+        IExpressionNode enode = node.getVariableTypeNode();
+        if (enode != null)
         {
-            IDefinition typedef = null;
-            IExpressionNode enode = node.getVariableTypeNode();//getAssignedValueNode();
-            if (enode != null)
-                typedef = enode.resolveType(getWalker().getProject());
-            if (typedef != null)
+            typedef = enode.resolveType(getProject());
+        }
+        if (typedef != null)
+        {
+            if (node.getParent() != null &&
+                    node.getParent().getParent() != null &&
+                    node.getParent().getParent().getNodeID() != ASTNodeID.Op_InID)
             {
-                boolean defaultInitializers = false;
-                ICompilerProject project = getProject();
-                if(project instanceof RoyaleJSProject)
+                String defName = typedef.getQualifiedName();
+                if (defName.equals("int") || defName.equals("uint"))
                 {
-                    RoyaleJSProject fjsProject = (RoyaleJSProject) project; 
-                    if(fjsProject.config != null)
-                    {
-                        defaultInitializers = fjsProject.config.getJsDefaultInitializers();
-                    }
+                    write(ASEmitterTokens.SPACE);
+                    writeToken(ASEmitterTokens.EQUAL);
+                    write("0");
                 }
-                if (node.getParent() != null &&
-                        node.getParent().getParent() != null &&
-                        node.getParent().getParent().getNodeID() != ASTNodeID.Op_InID)
+                else if (defaultInitializers)
                 {
-                    String defName = typedef.getQualifiedName();
-                    if (defName.equals("int") || defName.equals("uint"))
+                    if (defName.equals("Number"))
                     {
                         write(ASEmitterTokens.SPACE);
                         writeToken(ASEmitterTokens.EQUAL);
-                        write("0");
+                        write(IASKeywordConstants.NA_N);
                     }
-                    else if (defaultInitializers)
+                    else if (defName.equals("Boolean"))
                     {
-                        if (defName.equals("Number"))
-                        {
-                            write(ASEmitterTokens.SPACE);
-                            writeToken(ASEmitterTokens.EQUAL);
-                            write(IASKeywordConstants.NA_N);
-                        }
-                        else if (defName.equals("Boolean"))
-                        {
-                            write(ASEmitterTokens.SPACE);
-                            writeToken(ASEmitterTokens.EQUAL);
-                            write(IASKeywordConstants.FALSE);
-                        }
-                        else if (!defName.equals("*"))
-                        {
-                            //type * is meant to default to undefined, so it
-                            //doesn't need to be initialized, but everything
-                            //else should default to null
-                            write(ASEmitterTokens.SPACE);
-                            writeToken(ASEmitterTokens.EQUAL);
-                            write(IASKeywordConstants.NULL);
-                        }
+                        write(ASEmitterTokens.SPACE);
+                        writeToken(ASEmitterTokens.EQUAL);
+                        write(IASKeywordConstants.FALSE);
+                    }
+                    else if (!defName.equals("*"))
+                    {
+                        //type * is meant to default to undefined, so it
+                        //doesn't need to be initialized, but everything
+                        //else should default to null
+                        write(ASEmitterTokens.SPACE);
+                        writeToken(ASEmitterTokens.EQUAL);
+                        write(IASKeywordConstants.NULL);
                     }
                 }
             }
         }
+    }
 
+    private void emitChainedVariables(IVariableNode node, boolean defaultInitializers, boolean isHoisting)
+    {
+        JSRoyaleEmitter fjs = (JSRoyaleEmitter) getEmitter();
         if (!(node instanceof ChainedVariableNode))
         {
             // check for chained variables
             int len = node.getChildCount();
+            boolean splitVariables = false;
+            for (int i = 0; i < len; i++)
+            {
+                IASNode child = node.getChild(i);
+                if (child instanceof ChainedVariableNode)
+                {
+                    ChainedVariableNode varChild = (ChainedVariableNode) child;
+                    //if any of them need a default value, they all should be
+                    //split onto different lines
+                    if(EmitterUtils.needsDefaultValue(varChild, defaultInitializers, getProject()))
+                    {
+                        splitVariables = true;
+                        break;
+                    }
+                }
+            }
+
             for (int i = 0; i < len; i++)
             {
                 IASNode child = node.getChild(i);
                 if (child instanceof ChainedVariableNode)
                 {
-                    startMapping(node, node.getChild(i - 1));
-                    writeToken(ASEmitterTokens.COMMA);
-                    endMapping(node);
+                    if (splitVariables)
+                    {
+                        boolean childNeedsDefault = EmitterUtils.needsDefaultValue((IVariableNode) child, defaultInitializers, getProject());
+                        if (isHoisting && !childNeedsDefault)
+                        {
+                            //this one does not need to be hoisted because it
+                            //already has a default value
+                            continue;
+                        }
+                        startMapping(node, node.getChild(i - 1));
+                        write(ASEmitterTokens.SEMICOLON);
+                        endMapping(node);
+                        writeNewline();
+                        if (!isHoisting && childNeedsDefault)
+                        {
+                            write("//");
+                        }
+                        writeToken(ASEmitterTokens.VAR);
+                    }
+                    else
+                    {
+                        startMapping(node, node.getChild(i - 1));
+                        writeToken(ASEmitterTokens.COMMA);
+                        endMapping(node);
+                    }
                     fjs.emitVarDeclaration((IVariableNode) child);
                 }
             }
+            
         }
     }
-
 }
diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/royale/JSRoyaleEmitter.java b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/royale/JSRoyaleEmitter.java
index e3adc32..834ce4d 100644
--- a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/royale/JSRoyaleEmitter.java
+++ b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/royale/JSRoyaleEmitter.java
@@ -417,7 +417,7 @@ public class JSRoyaleEmitter extends JSGoogEmitter implements IJSRoyaleEmitter
         }
     }
 
-    protected Boolean isEmittingHoistedNodes(IFunctionNode node)
+    public Boolean isEmittingHoistedNodes(IFunctionNode node)
     {
         return emittingHoistedNodes.contains(node);
     }
@@ -502,14 +502,44 @@ public class JSRoyaleEmitter extends JSGoogEmitter implements IJSRoyaleEmitter
             {
                 IVariableDefinition varDef = (IVariableDefinition) localDef;
                 IVariableNode varNode = varDef.getVariableNode();
-                if (!EmitterUtils.needsDefaultValue(varNode, defaultInitializers, getWalker().getProject()))
+                if (varNode == null)
                 {
-                    //already has a default value. no need to hoist.
+                    //no associated node is possible for implicit variables,
+                    //like "arguments"
                     continue;
                 }
-                getWalker().walk(varNode);
-                write(ASEmitterTokens.SEMICOLON);
-                writeNewline();
+                if (varNode instanceof ChainedVariableNode)
+                {
+                    //these will be handled from the first variable in the chain
+                    continue;
+                }
+                if (EmitterUtils.needsDefaultValue(varNode, defaultInitializers, getWalker().getProject()))
+                {
+                    emitVarDeclaration(varNode);
+                    write(ASEmitterTokens.SEMICOLON);
+                    writeNewline();
+                }
+                else
+                {
+                    //when the first varible in a chain doesn't need a default
+                    //value, we need to manually check the rest of the chain
+                    int len = varNode.getChildCount();
+                    for(int i = 0; i < len; i++)
+                    {
+                        IASNode child = varNode.getChild(i);
+                        if(child instanceof ChainedVariableNode)
+                        {
+                            ChainedVariableNode childVarNode = (ChainedVariableNode) child;
+                            if (EmitterUtils.needsDefaultValue(childVarNode, defaultInitializers, getWalker().getProject()))
+                            {
+                                writeToken(ASEmitterTokens.VAR);
+                                emitVarDeclaration(childVarNode);
+                                write(ASEmitterTokens.SEMICOLON);
+                                writeNewline();
+                            }
+                        }
+                    }
+                }
             }
         }
     }
@@ -810,32 +840,6 @@ public class JSRoyaleEmitter extends JSGoogEmitter implements IJSRoyaleEmitter
     @Override
     public void emitVarDeclaration(IVariableNode node)
     {
-        boolean defaultInitializers = false;
-        ICompilerProject project = getWalker().getProject();
-        if(project instanceof RoyaleJSProject)
-        {
-            RoyaleJSProject fjsProject = (RoyaleJSProject) project; 
-            if(fjsProject.config != null)
-            {
-                defaultInitializers = fjsProject.config.getJsDefaultInitializers();
-            }
-        }
-        if (EmitterUtils.needsDefaultValue(node, defaultInitializers, project))
-        {
-            IExpressionNode avnode = node.getAssignedValueNode();
-            if (avnode == null)
-            {
-                IFunctionNode parentFunction = (IFunctionNode) node.getAncestorOfType(IFunctionNode.class);
-                if (!isEmittingHoistedNodes(parentFunction))
-                {
-                    write("//");
-                    writeToken(ASEmitterTokens.VAR);
-                    write(node.getName());
-                    return;
-                }
-            }
-        }
-
         varDeclarationEmitter.emit(node);
     }
 
diff --git a/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/js/royale/TestDefaultInitializers.java b/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/js/royale/TestDefaultInitializers.java
index 6e17484..963d2cf 100644
--- a/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/js/royale/TestDefaultInitializers.java
+++ b/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/js/royale/TestDefaultInitializers.java
@@ -63,7 +63,7 @@ public class TestDefaultInitializers extends ASTestBase
         IFunctionNode node = (IFunctionNode) getNode("var a:Number;",
                 IFunctionNode.class);
         asBlockWalker.visitFunction(node);
-        assertOut("RoyaleTest_A.prototype.royaleTest_a = function() {\n  var /** @type {number} */ a = NaN;\n  //var a;\n}");
+        assertOut("RoyaleTest_A.prototype.royaleTest_a = function() {\n  var /** @type {number} */ a = NaN;\n  //var /** @type {number} */ a = NaN;\n}");
     }
 
     @Test
@@ -83,7 +83,7 @@ public class TestDefaultInitializers extends ASTestBase
         IFunctionNode node = (IFunctionNode) getNode("var a:Boolean;",
                 IFunctionNode.class);
         asBlockWalker.visitFunction(node);
-        assertOut("RoyaleTest_A.prototype.royaleTest_a = function() {\n  var /** @type {boolean} */ a = false;\n  //var a;\n}");
+        assertOut("RoyaleTest_A.prototype.royaleTest_a = function() {\n  var /** @type {boolean} */ a = false;\n  //var /** @type {boolean} */ a = false;\n}");
     }
 
     @Test
@@ -103,7 +103,7 @@ public class TestDefaultInitializers extends ASTestBase
         IFunctionNode node = (IFunctionNode) getNode("var a:int;",
                 IFunctionNode.class);
         asBlockWalker.visitFunction(node);
-        assertOut("RoyaleTest_A.prototype.royaleTest_a = function() {\n  var /** @type {number} */ a = 0;\n  //var a;\n}");
+        assertOut("RoyaleTest_A.prototype.royaleTest_a = function() {\n  var /** @type {number} */ a = 0;\n  //var /** @type {number} */ a = 0;\n}");
     }
 
     @Test
@@ -114,7 +114,7 @@ public class TestDefaultInitializers extends ASTestBase
                 IFunctionNode.class);
         asBlockWalker.visitFunction(node);
         //an exception that always has an initializer
-        assertOut("RoyaleTest_A.prototype.royaleTest_a = function() {\n  var /** @type {number} */ a = 0;\n  //var a;\n}");
+        assertOut("RoyaleTest_A.prototype.royaleTest_a = function() {\n  var /** @type {number} */ a = 0;\n  //var /** @type {number} */ a = 0;\n}");
     }
 
     @Test
@@ -124,7 +124,7 @@ public class TestDefaultInitializers extends ASTestBase
         IFunctionNode node = (IFunctionNode) getNode("var a:uint;",
                 IFunctionNode.class);
         asBlockWalker.visitFunction(node);
-        assertOut("RoyaleTest_A.prototype.royaleTest_a = function() {\n  var /** @type {number} */ a = 0;\n  //var a;\n}");
+        assertOut("RoyaleTest_A.prototype.royaleTest_a = function() {\n  var /** @type {number} */ a = 0;\n  //var /** @type {number} */ a = 0;\n}");
     }
 
     @Test
@@ -135,7 +135,7 @@ public class TestDefaultInitializers extends ASTestBase
                 IFunctionNode.class);
         asBlockWalker.visitFunction(node);
         //an exception that always has an initializer
-        assertOut("RoyaleTest_A.prototype.royaleTest_a = function() {\n  var /** @type {number} */ a = 0;\n  //var a;\n}");
+        assertOut("RoyaleTest_A.prototype.royaleTest_a = function() {\n  var /** @type {number} */ a = 0;\n  //var /** @type {number} */ a = 0;\n}");
     }
 
     @Test
@@ -145,7 +145,7 @@ public class TestDefaultInitializers extends ASTestBase
         IFunctionNode node = (IFunctionNode) getNode("var a:String;",
                 IFunctionNode.class);
         asBlockWalker.visitFunction(node);
-        assertOut("RoyaleTest_A.prototype.royaleTest_a = function() {\n  var /** @type {string} */ a = null;\n  //var a;\n}");
+        assertOut("RoyaleTest_A.prototype.royaleTest_a = function() {\n  var /** @type {string} */ a = null;\n  //var /** @type {string} */ a = null;\n}");
     }
 
     @Test
@@ -165,7 +165,7 @@ public class TestDefaultInitializers extends ASTestBase
         IFunctionNode node = (IFunctionNode) getNode("var a:Object;",
                 IFunctionNode.class);
         asBlockWalker.visitFunction(node);
-        assertOut("RoyaleTest_A.prototype.royaleTest_a = function() {\n  var /** @type {Object} */ a = null;\n  //var a;\n}");
+        assertOut("RoyaleTest_A.prototype.royaleTest_a = function() {\n  var /** @type {Object} */ a = null;\n  //var /** @type {Object} */ a = null;\n}");
     }
 
     @Test
@@ -185,7 +185,7 @@ public class TestDefaultInitializers extends ASTestBase
         IFunctionNode node = (IFunctionNode) getNode("var a:Array;",
                 IFunctionNode.class);
         asBlockWalker.visitFunction(node);
-        assertOut("RoyaleTest_A.prototype.royaleTest_a = function() {\n  var /** @type {Array} */ a = null;\n  //var a;\n}");
+        assertOut("RoyaleTest_A.prototype.royaleTest_a = function() {\n  var /** @type {Array} */ a = null;\n  //var /** @type {Array} */ a = null;\n}");
     }
 
     @Test
@@ -199,6 +199,46 @@ public class TestDefaultInitializers extends ASTestBase
     }
 
     @Test
+    public void testVarDeclaration_defaultInitializers_withChainedAndAllInitialized()
+    {
+        createConfig(true);
+        IVariableNode node = (IVariableNode) getNode("var a:Number = 0, b:Number = 0, c:Number = 0;",
+                IVariableNode.class);
+        asBlockWalker.visitVariable(node);
+        assertOut("var /** @type {number} */ a = 0, /** @type {number} */ b = 0, /** @type {number} */ c = 0");
+    }
+
+    @Test
+    public void testVarDeclaration_defaultInitializers_withChainedAndNoneInitialized()
+    {
+        createConfig(true);
+        IFunctionNode node = (IFunctionNode) getNode("var a:Number, b:Number, c:Number;",
+                IFunctionNode.class);
+        asBlockWalker.visitFunction(node);
+        assertOut("RoyaleTest_A.prototype.royaleTest_a = function() {\n  var /** @type {number} */ a = NaN;\n  var /** @type {number} */ b = NaN;\n  var /** @type {number} */ c = NaN;\n  //var /** @type {number} */ a = NaN;\n  //var /** @type {number} */ b = NaN;\n  //var /** @type {number} */ c = NaN;\n}");
+    }
+
+    @Test
+    public void testVarDeclaration_defaultInitializers_withChainedAndFirstInitialized()
+    {
+        createConfig(true);
+        IFunctionNode node = (IFunctionNode) getNode("var a:Number = 1, b:Number, c:Number;",
+                IFunctionNode.class);
+        asBlockWalker.visitFunction(node);
+        assertOut("RoyaleTest_A.prototype.royaleTest_a = function() {\n  var /** @type {number} */ b = NaN;\n  var /** @type {number} */ c = NaN;\n  var /** @type {number} */ a = 1;\n  //var /** @type {number} */ b = NaN;\n  //var /** @type {number} */ c = NaN;\n}");
+    }
+
+    @Test
+    public void testVarDeclaration_defaultInitializers_withChainedAndLastInitialized()
+    {
+        createConfig(true);
+        IFunctionNode node = (IFunctionNode) getNode("var a:Number, b:Number, c:Number = 1;",
+                IFunctionNode.class);
+        asBlockWalker.visitFunction(node);
+        assertOut("RoyaleTest_A.prototype.royaleTest_a = function() {\n  var /** @type {number} */ a = NaN;\n  var /** @type {number} */ b = NaN;\n  //var /** @type {number} */ a = NaN;\n  //var /** @type {number} */ b = NaN;\n  var /** @type {number} */ c = 1;\n}");
+    }
+
+    @Test
     public void testFieldDeclaration_defaultInitializers_withNumberType()
     {
         createConfig(true);
diff --git a/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/js/royale/TestRoyaleStatements.java b/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/js/royale/TestRoyaleStatements.java
index a3f8c9f..d71b6d6 100644
--- a/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/js/royale/TestRoyaleStatements.java
+++ b/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/js/royale/TestRoyaleStatements.java
@@ -96,7 +96,7 @@ public class TestRoyaleStatements extends TestGoogStatements
         IFunctionNode node = (IFunctionNode) getNode("var a:int;",
             IFunctionNode.class);
         asBlockWalker.visitFunction(node);
-        assertOut("RoyaleTest_A.prototype.royaleTest_a = function() {\n  var /** @type {number} */ a = 0;\n  //var a;\n}");
+        assertOut("RoyaleTest_A.prototype.royaleTest_a = function() {\n  var /** @type {number} */ a = 0;\n  //var /** @type {number} */ a = 0;\n}");
     }
 
     @Test
@@ -738,7 +738,7 @@ public class TestRoyaleStatements extends TestGoogStatements
         		              "  } finally {\n" +
         		              "  }\n" +
         		              "  if (d) {\n" +
-        		              "    //var len;\n" +
+        		              "    //var /** @type {number} */ len = 0;\n" +
         		              "    for (var /** @type {number} */ i = 0; i < len; i++)\n" +
         		              "      break;\n" +
         		              "  }\n" +