From d5f9178d304ae5b3c7244b0ced06b24898605bbb Mon Sep 17 00:00:00 2001 From: nickreid Date: Thu, 28 Feb 2019 16:21:27 -0800 Subject: [PATCH] Add support for folding object spread to `PeepholeFoldConstants`. 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 --- .../jscomp/PeepholeFoldConstants.java | 49 +++++++++---------- .../jscomp/PeepholeFoldConstantsTest.java | 36 ++++++++++++++ 2 files changed, 60 insertions(+), 25 deletions(-) diff --git a/src/com/google/javascript/jscomp/PeepholeFoldConstants.java b/src/com/google/javascript/jscomp/PeepholeFoldConstants.java index 8eb702f37e6..ec12ac8aa53 100644 --- a/src/com/google/javascript/jscomp/PeepholeFoldConstants.java +++ b/src/com/google/javascript/jscomp/PeepholeFoldConstants.java @@ -82,7 +82,8 @@ Node optimizeSubtree(Node subtree) { return tryFoldTypeof(subtree); case ARRAYLIT: - return tryFlattenArray(subtree); + case OBJECTLIT: + return tryFlattenArrayOrObjectLit(subtree); case NOT: case POS: @@ -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. * - *

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

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. * *

Example: `[0, ...[1, 2, 3], 4, ...[5]]` => `[0, 1, 2, 3, 4, 5]` */ - private Node tryFlattenArray(Node arrayLit) { - for (Node child = arrayLit.getFirstChild(); child != null; ) { + private Node tryFlattenArrayOrObjectLit(Node parentLit) { + for (Node child = parentLit.getFirstChild(); child != null; ) { + // We have to store the next element here because nodes may be inserted below. Node spread = child; - // we have to put child.getNext() here because spread nodes are detached below 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. */ - private void inlineSpreadInArrayLit(Node spread) { - Node parentArray = spread.getParent(); - checkArgument(parentArray.isArrayLit(), "This isn't supported for object spread"); + Node innerLit = spread.getOnlyChild(); + if (!parentLit.getToken().equals(innerLit.getToken())) { + continue; // We only want to inline arrays into arrays and objects into objects. + } - Node childArray = spread.getOnlyChild(); - // We can't use `child.getNext()` because the child is being moved. - for (Node element = childArray.getFirstChild(); - element != null; - element = childArray.getFirstChild()) { - parentArray.addChildBefore(element.detach(), spread); + parentLit.addChildrenAfter(innerLit.removeChildren(), spread); + spread.detach(); + reportChangeToEnclosingScope(parentLit); } - - spread.detach(); - reportChangeToEnclosingScope(parentArray); + return parentLit; } private Node tryFoldStringArrayAccess(Node n, Node left, Node right) { @@ -1521,6 +1515,11 @@ private Node tryFoldObjectPropAccess(Node n, Node left, Node right) { Node value = null; for (Node c = left.getFirstChild(); c != null; c = c.getNext()) { switch (c.getToken()) { + case SPREAD: + // Reset the search because spread could overwrite any previous result. + key = null; + value = null; + break; case SETTER_DEF: continue; case COMPUTED_PROP: diff --git a/test/com/google/javascript/jscomp/PeepholeFoldConstantsTest.java b/test/com/google/javascript/jscomp/PeepholeFoldConstantsTest.java index b01566bf00c..9ec3f36ec3e 100644 --- a/test/com/google/javascript/jscomp/PeepholeFoldConstantsTest.java +++ b/test/com/google/javascript/jscomp/PeepholeFoldConstantsTest.java @@ -1102,6 +1102,42 @@ public void testFoldArraySpread() { 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 public void testFoldComplex() { fold("x = (3 / 1.0) + (1 * 2)", "x = 5");