Skip to content

Commit

Permalink
In ES6RewriteDestructuring visitArrayPattern, if the parent of the de…
Browse files Browse the repository at this point in the history
…structuring assignment is not an expr result, for example, a for loop or another assignment, then transformed the destructuring assignment into an arrow function whose result is the original destructuring assignment.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=158769794
  • Loading branch information
EatingW authored and Tyler Breisacher committed Jun 13, 2017
1 parent 481ef6f commit 1ea642a
Show file tree
Hide file tree
Showing 5 changed files with 399 additions and 17 deletions.
77 changes: 61 additions & 16 deletions src/com/google/javascript/jscomp/Es6RewriteDestructuring.java
Expand Up @@ -53,6 +53,9 @@ public void hotSwapScript(Node scriptRoot, Node originalRoot) {
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
switch (n.getToken()) {
case FUNCTION:
visitFunction(t, n);
break;
case PARAM_LIST:
visitParamList(t, n, parent);
break;
Expand Down Expand Up @@ -82,6 +85,19 @@ public void visit(NodeTraversal t, Node n, Node parent) {
}
}

/**
* If the function is an arrow function, wrap the body in a block if it is not already a block.
*/
private void visitFunction(NodeTraversal t, Node function) {
Node body = function.getLastChild();
if (!body.isNormalBlock()) {
body.detach();
Node replacement = IR.block(IR.returnNode(body)).useSourceInfoIfMissingFromForTree(body);
function.addChildToBack(replacement);
t.reportCodeChange();
}
}

/**
* Processes trailing default and rest parameters.
*/
Expand Down Expand Up @@ -203,7 +219,8 @@ private void visitForOf(Node node) {
}

private void visitObjectPattern(NodeTraversal t, Node objectPattern, Node parent) {
Node rhs, nodeToDetach;
Node rhs;
Node nodeToDetach;
if (NodeUtil.isNameDeclaration(parent) && !NodeUtil.isEnhancedFor(parent.getParent())) {
rhs = objectPattern.getNext();
nodeToDetach = parent;
Expand Down Expand Up @@ -244,7 +261,8 @@ private void visitObjectPattern(NodeTraversal t, Node objectPattern, Node parent
for (Node child = objectPattern.getFirstChild(), next; child != null; child = next) {
next = child.getNext();

Node newLHS, newRHS;
Node newLHS;
Node newRHS;
if (child.isStringKey()) {
if (!child.hasChildren()) { // converting shorthand
Node name = IR.name(child.getString());
Expand Down Expand Up @@ -314,35 +332,35 @@ private void visitObjectPattern(NodeTraversal t, Node objectPattern, Node parent
}

private void visitArrayPattern(NodeTraversal t, Node arrayPattern, Node parent) {
Node rhs, nodeToDetach;
if (NodeUtil.isNameDeclaration(parent) && !NodeUtil.isEnhancedFor(parent.getParent())) {
rhs = arrayPattern.getNext();
nodeToDetach = parent;
replaceArrayPattern(t, arrayPattern, arrayPattern.getNext(), parent, parent);
} else if (parent.isAssign()) {
rhs = arrayPattern.getNext();
nodeToDetach = parent.getParent();
Preconditions.checkState(nodeToDetach.isExprResult());
if (parent.getParent().isExprResult()) {
replaceArrayPattern(t, arrayPattern, arrayPattern.getNext(), parent, parent.getParent());
} else {
wrapAssignmentInCallToArrow(t, parent);
}
} else if (parent.isArrayPattern()
|| parent.isRest()
|| parent.isDefaultValue()
|| parent.isStringKey()) {
// This is a nested array pattern. Don't do anything now; we'll visit it
// after visiting the parent.
return;
} else if (NodeUtil.isEnhancedFor(parent) || NodeUtil.isEnhancedFor(parent.getParent())) {
visitDestructuringPatternInEnhancedFor(arrayPattern);
return;
} else if (parent.isCatch()) {
visitDestructuringPatternInCatch(arrayPattern);
return;
} else {
throw new IllegalStateException("Unexpected ARRAY_PATTERN parent: " + parent);
}
}

// Convert 'var [x, y] = rhs' to:
// var temp = $jscomp.makeIterator(rhs);
// var x = temp.next().value;
// var y = temp.next().value;
/**
* Convert 'var [x, y] = rhs' to: var temp = $jscomp.makeIterator(rhs); var x = temp.next().value;
* var y = temp.next().value;
*/
private void replaceArrayPattern(
NodeTraversal t, Node arrayPattern, Node rhs, Node parent, Node nodeToDetach) {
String tempVarName = DESTRUCTURING_TEMP_VAR + (destructuringVarCounter++);
Node tempDecl = IR.var(
IR.name(tempVarName),
Expand All @@ -361,7 +379,8 @@ private void visitArrayPattern(NodeTraversal t, Node arrayPattern, Node parent)
continue;
}

Node newLHS, newRHS;
Node newLHS;
Node newRHS;
if (child.isDefaultValue()) {
// [x = defaultValue] = rhs;
// becomes
Expand Down Expand Up @@ -423,6 +442,32 @@ private void visitArrayPattern(NodeTraversal t, Node arrayPattern, Node parent)
t.reportCodeChange();
}

/**
* Convert the assignment '[x, y] = rhs' (used as an expression and not an expr result) to: (() =>
* { var temp = $jscomp.makeIterator(rhs); var x = temp.next().value; var y = temp.next().value;
* return temp; })
*/
private void wrapAssignmentInCallToArrow(NodeTraversal t, Node assignment) {
String tempVarName = DESTRUCTURING_TEMP_VAR + (destructuringVarCounter++);
Node rhs = assignment.getLastChild().detach();
Node newAssignment = IR.let(IR.name(tempVarName), rhs);
Node replacementExpr = IR.assign(assignment.getFirstChild().detach(), IR.name(tempVarName));
Node exprResult = IR.exprResult(replacementExpr);
Node returnNode = IR.returnNode(IR.name(tempVarName));
Node block = IR.block(newAssignment, exprResult, returnNode);
Node call = IR.call(IR.arrowFunction(IR.name(""), IR.paramList(), block));
call.useSourceInfoIfMissingFromForTree(assignment);
call.putBooleanProp(Node.FREE_CALL, true);
assignment.getParent().replaceChild(assignment, call);
NodeUtil.markNewScopesChanged(call, compiler);
replaceArrayPattern(
t,
replacementExpr.getFirstChild(),
replacementExpr.getLastChild(),
replacementExpr,
exprResult);
}

private void visitDestructuringPatternInEnhancedFor(Node pattern) {
Preconditions.checkArgument(pattern.isDestructuringPattern());
String tempVarName = DESTRUCTURING_TEMP_VAR + (destructuringVarCounter++);
Expand Down
2 changes: 1 addition & 1 deletion src/com/google/javascript/jscomp/TranspilationPasses.java
Expand Up @@ -44,10 +44,10 @@ public static void addEs2017Passes(List<PassFactory> passes) {
public static void addEs6EarlyPasses(List<PassFactory> passes) {
passes.add(es6SuperCheck);
passes.add(es6ConvertSuper);
passes.add(es6RewriteArrowFunction);
passes.add(es6RenameVariablesInParamLists);
passes.add(es6SplitVariableDeclarations);
passes.add(es6RewriteDestructuring);
passes.add(es6RewriteArrowFunction);
}

/**
Expand Down
133 changes: 133 additions & 0 deletions test/com/google/javascript/jscomp/Es6RewriteDestructuringTest.java
Expand Up @@ -638,6 +638,139 @@ public void testTypeCheck_inlineAnnotations() {
TYPE_MISMATCH_WARNING);
}

public void testDestructuringNotInExprResult() {
test(
LINE_JOINER.join("var x, a, b;", "x = ([a,b] = [1,2])"),
LINE_JOINER.join(
"var x,a,b;",
"x = (()=>{",
" let $jscomp$destructuring$var0 = [1,2];",
" var $jscomp$destructuring$var1 = $jscomp.makeIterator($jscomp$destructuring$var0);",
" a = $jscomp$destructuring$var1.next().value;",
" b = $jscomp$destructuring$var1.next().value;",
" return $jscomp$destructuring$var0;",
"})();"));

test(
LINE_JOINER.join(
"var foo = function () {", "var x, a, b;", "x = ([a,b] = [1,2]);", "}", "foo();"),
LINE_JOINER.join(
"var foo = function () {",
" var x, a, b;",
" x = (()=>{",
" let $jscomp$destructuring$var0 = [1,2];",
" var $jscomp$destructuring$var1 = $jscomp.makeIterator($jscomp$destructuring$var0);",
" a = $jscomp$destructuring$var1.next().value;",
" b = $jscomp$destructuring$var1.next().value;",
" return $jscomp$destructuring$var0;",
" })();",
"}",
"foo();"));

test(
LINE_JOINER.join("var prefix;", "for (;;[, prefix] = /\\.?([^.]+)$/.exec(prefix)){", "}"),
LINE_JOINER.join(
"var prefix;",
"for (;;(() => {",
" let $jscomp$destructuring$var0 = /\\.?([^.]+)$/.exec(prefix)",
" var $jscomp$destructuring$var1 = ",
"$jscomp.makeIterator($jscomp$destructuring$var0);",
" $jscomp$destructuring$var1.next();",
" prefix = $jscomp$destructuring$var1.next().value;",
" return $jscomp$destructuring$var0;",
" })()){",
"}"));

test(
LINE_JOINER.join(
"var prefix;",
"for (;;[, prefix] = /\\.?([^.]+)$/.exec(prefix)){",
" console.log(prefix);",
"}"),
LINE_JOINER.join(
"var prefix;",
"for (;;(() => {",
" let $jscomp$destructuring$var0 = /\\.?([^.]+)$/.exec(prefix)",
" var $jscomp$destructuring$var1 = ",
"$jscomp.makeIterator($jscomp$destructuring$var0);",
" $jscomp$destructuring$var1.next();",
" prefix = $jscomp$destructuring$var1.next().value;",
" return $jscomp$destructuring$var0;",
" })()){",
" console.log(prefix);",
"}"));

test(
LINE_JOINER.join("for (var x = 1; x < 3; [x,] = [3,4]){", " console.log(x);", "}"),
LINE_JOINER.join(
"for (var x = 1; x < 3; (()=>{",
" let $jscomp$destructuring$var0 = [3,4]",
" var $jscomp$destructuring$var1 = $jscomp.makeIterator($jscomp$destructuring$var0);",
" x = $jscomp$destructuring$var1.next().value;",
" return $jscomp$destructuring$var0;",
" })()){",
"console.log(x);",
"}"));
}

public void testNestedDestructuring() {
test(
"var [[x]] = [[1]];",
LINE_JOINER.join(
"var $jscomp$destructuring$var0 = $jscomp.makeIterator([[1]]);",
"var $jscomp$destructuring$var1 = ",
"$jscomp.makeIterator($jscomp$destructuring$var0.next().value);",
"var x = $jscomp$destructuring$var1.next().value;"));

test(
"var [[x,y],[z]] = [[1,2],[3]];",
LINE_JOINER.join(
"var $jscomp$destructuring$var0 = $jscomp.makeIterator([[1,2],[3]]);",
"var $jscomp$destructuring$var1 = ",
"$jscomp.makeIterator($jscomp$destructuring$var0.next().value);",
"var x = $jscomp$destructuring$var1.next().value;",
"var y = $jscomp$destructuring$var1.next().value;",
"var $jscomp$destructuring$var2 = ",
"$jscomp.makeIterator($jscomp$destructuring$var0.next().value);",
"var z = $jscomp$destructuring$var2.next().value;"));

test(
"var [[x,y],z] = [[1,2],3];",
LINE_JOINER.join(
"var $jscomp$destructuring$var0 = $jscomp.makeIterator([[1,2],3]);",
"var $jscomp$destructuring$var1 = ",
"$jscomp.makeIterator($jscomp$destructuring$var0.next().value);",
"var x = $jscomp$destructuring$var1.next().value;",
"var y = $jscomp$destructuring$var1.next().value;",
"var z = $jscomp$destructuring$var0.next().value;"));
}

public void testTryCatch() {
test(
LINE_JOINER.join("var x = 1;", "try {", " throw [];", "} catch ([x]) {}"),
LINE_JOINER.join(
"var x = 1;",
"try {",
" throw [];",
"} catch ($jscomp$destructuring$var0) {",
" var $jscomp$destructuring$var1 = $jscomp.makeIterator($jscomp$destructuring$var0);",
" let x = $jscomp$destructuring$var1.next().value;",
"}"));

test(
LINE_JOINER.join("var x = 1;", "try {", " throw [[]];", "} catch ([[x]]) {}"),
LINE_JOINER.join(
"var x = 1;",
"try {",
" throw [[]];",
"} catch ($jscomp$destructuring$var0) {",
" var $jscomp$destructuring$var1 = $jscomp.makeIterator($jscomp$destructuring$var0);",
" var $jscomp$destructuring$var2 = ",
"$jscomp.makeIterator($jscomp$destructuring$var1.next().value);",
" let x = $jscomp$destructuring$var2.next().value;",
"}"));
}

@Override
protected Compiler createCompiler() {
return new NoninjectingCompiler();
Expand Down

0 comments on commit 1ea642a

Please sign in to comment.