Skip to content

Commit

Permalink
Add support for folding object spread to PeepholeFoldConstants.
Browse files Browse the repository at this point in the history
We can now:
  - flatten object literals into other object literals
  - access properties of literals assembled with spreading

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=236218232
  • Loading branch information
nreid260 authored and brad4d committed Mar 1, 2019
1 parent 1a1ff15 commit d5f9178
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 25 deletions.
49 changes: 24 additions & 25 deletions src/com/google/javascript/jscomp/PeepholeFoldConstants.java
Expand Up @@ -82,7 +82,8 @@ Node optimizeSubtree(Node subtree) {
return tryFoldTypeof(subtree); return tryFoldTypeof(subtree);


case ARRAYLIT: case ARRAYLIT:
return tryFlattenArray(subtree); case OBJECTLIT:
return tryFlattenArrayOrObjectLit(subtree);


case NOT: case NOT:
case POS: case POS:
Expand Down Expand Up @@ -1415,40 +1416,33 @@ private Node tryFoldArrayAccess(Node n, Node left, Node right) {
} }


/** /**
* Flattens array literals that contain spreads of other array literals. * Flattens array- or object-literals that contain spreads of other literals.
* *
* <p>Does not recurse into nested spreads,because this method is already called as part of a * <p>Does not recurse into nested spreads because this method is already called as part of a
* postorder traversal and nested spreads will already have been flattened * postorder traversal and nested spreads will already have been flattened.
* *
* <p>Example: `[0, ...[1, 2, 3], 4, ...[5]]` => `[0, 1, 2, 3, 4, 5]` * <p>Example: `[0, ...[1, 2, 3], 4, ...[5]]` => `[0, 1, 2, 3, 4, 5]`
*/ */
private Node tryFlattenArray(Node arrayLit) { private Node tryFlattenArrayOrObjectLit(Node parentLit) {
for (Node child = arrayLit.getFirstChild(); child != null; ) { for (Node child = parentLit.getFirstChild(); child != null; ) {
// We have to store the next element here because nodes may be inserted below.
Node spread = child; Node spread = child;
// we have to put child.getNext() here because spread nodes are detached below
child = child.getNext(); child = child.getNext();
if (spread.isSpread() && spread.getOnlyChild().isArrayLit()) {
inlineSpreadInArrayLit(spread); if (!spread.isSpread()) {
continue;
} }
}
return arrayLit;
}


/** Replaces `...[1, 2, 3]` with `1, 2, 3` in an array literal. */ Node innerLit = spread.getOnlyChild();
private void inlineSpreadInArrayLit(Node spread) { if (!parentLit.getToken().equals(innerLit.getToken())) {
Node parentArray = spread.getParent(); continue; // We only want to inline arrays into arrays and objects into objects.
checkArgument(parentArray.isArrayLit(), "This isn't supported for object spread"); }


Node childArray = spread.getOnlyChild(); parentLit.addChildrenAfter(innerLit.removeChildren(), spread);
// We can't use `child.getNext()` because the child is being moved. spread.detach();
for (Node element = childArray.getFirstChild(); reportChangeToEnclosingScope(parentLit);
element != null;
element = childArray.getFirstChild()) {
parentArray.addChildBefore(element.detach(), spread);
} }

return parentLit;
spread.detach();
reportChangeToEnclosingScope(parentArray);
} }


private Node tryFoldStringArrayAccess(Node n, Node left, Node right) { private Node tryFoldStringArrayAccess(Node n, Node left, Node right) {
Expand Down Expand Up @@ -1521,6 +1515,11 @@ private Node tryFoldObjectPropAccess(Node n, Node left, Node right) {
Node value = null; Node value = null;
for (Node c = left.getFirstChild(); c != null; c = c.getNext()) { for (Node c = left.getFirstChild(); c != null; c = c.getNext()) {
switch (c.getToken()) { switch (c.getToken()) {
case SPREAD:
// Reset the search because spread could overwrite any previous result.
key = null;
value = null;
break;
case SETTER_DEF: case SETTER_DEF:
continue; continue;
case COMPUTED_PROP: case COMPUTED_PROP:
Expand Down
36 changes: 36 additions & 0 deletions test/com/google/javascript/jscomp/PeepholeFoldConstantsTest.java
Expand Up @@ -1102,6 +1102,42 @@ public void testFoldArraySpread() {
foldSame("[...[x]] = arr"); foldSame("[...[x]] = arr");
} }


@Test
public void testFoldObjectLitSpreadGetProp() {
numRepetitions = 1;
fold("x = {...{a}}.a", "x = a;");
fold("x = {a, b, ...{c, d, e}}.d", "x = d;");
fold("x = {...{a, b}, c, ...{d, e}}.d", "x = d;");
fold("x = {...{...{a, b}, c, d}, e}.a", "x = a");
fold("x = {...{...{a, b}, c, d}, e}.d", "x = d");
}

@Test
public void testDontFoldNonLiteralObjectSpreadGetProp() {
foldSame("x = {...obj}.a;");
foldSame("x = {a, ...obj, c}.a;");
fold("x = {a, ...obj, c}.c;", "x = c;"); // We assume object spread has no side-effects.
}

@Test
public void testFoldObjectSpread() {
numRepetitions = 1;
fold("x = {...{}}", "x = {}");
fold("x = {a, ...{}, b}", "x = {a, b}");
fold("x = {...{a, b}, c, ...{d, e}}", "x = {a, b, c, d, e}");
fold("x = {...{...{a}, b}, c}", "x = {a, b, c}");
foldSame("({...{x}} = obj)");
}

@Test
public void testDontFoldMixedObjectAndArraySpread() {
numRepetitions = 1;
foldSame("x = [...{}]");
foldSame("x = {...[]}");
fold("x = [a, ...[...{}]]", "x = [a, ...{}]");
fold("x = {a, ...{...[]}}", "x = {a, ...[]}");
}

@Test @Test
public void testFoldComplex() { public void testFoldComplex() {
fold("x = (3 / 1.0) + (1 * 2)", "x = 5"); fold("x = (3 / 1.0) + (1 * 2)", "x = 5");
Expand Down

0 comments on commit d5f9178

Please sign in to comment.