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" +