@@ -63,8 +63,10 @@ class PeepholeFoldConstants extends AbstractPeepholeOptimization {
63
63
@ Override
64
64
Node optimizeSubtree (Node subtree ) {
65
65
switch (subtree .getToken ()) {
66
+ case OPTCHAIN_CALL :
66
67
case CALL :
67
68
return tryFoldCall (subtree );
69
+
68
70
case NEW :
69
71
return tryFoldCtorCall (subtree );
70
72
@@ -106,10 +108,12 @@ private Node tryFoldBinaryOperator(Node subtree) {
106
108
107
109
// If we've reached here, node is truly a binary operator.
108
110
switch (subtree .getToken ()) {
111
+ case OPTCHAIN_GETPROP :
109
112
case GETPROP :
110
113
return tryFoldGetProp (subtree , left , right );
111
114
112
115
case GETELEM :
116
+ case OPTCHAIN_GETELEM :
113
117
return tryFoldGetElem (subtree , left , right );
114
118
115
119
case INSTANCEOF :
@@ -1284,7 +1288,7 @@ private Node tryFoldCtorCall(Node n) {
1284
1288
* Object.defineProperties(o, {}) -> o
1285
1289
*/
1286
1290
private Node tryFoldCall (Node n ) {
1287
- checkArgument (n .isCall ());
1291
+ checkArgument (n .isCall () || n . isOptChainCall () );
1288
1292
1289
1293
if (NodeUtil .isObjectDefinePropertiesDefinition (n )) {
1290
1294
Node srcObj = n .getLastChild ();
@@ -1344,10 +1348,11 @@ private Node tryFoldInForcedStringContext(Node n) {
1344
1348
}
1345
1349
1346
1350
/**
1347
- * Try to fold array-element. e.g [1, 2, 3][10];
1351
+ * For element access using GETLEM/OPTCHAIN_GETELEM on object literals, arrays or strings, tries
1352
+ * to fold the prop access. e.g. folds array-element [1, 2, 3][1];
1348
1353
*/
1349
1354
private Node tryFoldGetElem (Node n , Node left , Node right ) {
1350
- checkArgument (n .isGetElem ());
1355
+ checkArgument (n .isGetElem () || n . isOptChainGetElem () );
1351
1356
1352
1357
if (left .isObjectLit ()) {
1353
1358
return tryFoldObjectPropAccess (n , left , right );
@@ -1364,10 +1369,12 @@ private Node tryFoldGetElem(Node n, Node left, Node right) {
1364
1369
}
1365
1370
1366
1371
/**
1367
- * Try to fold array-length. e.g [1, 2, 3].length ==> 3, [x, y].length ==> 2
1372
+ * For prop access using GETPROP/OPTCHAIN_GETPROP on object literals, tries to fold their property
1373
+ * access. For prop access on arrays, only tries to fold array-length. e.g [1, 2, 3].length ==> 3,
1374
+ * [x, y].length ==> 2
1368
1375
*/
1369
1376
private Node tryFoldGetProp (Node n , Node left , Node right ) {
1370
- checkArgument (n .isGetProp ());
1377
+ checkArgument (n .isGetProp () || n . isOptChainGetProp () );
1371
1378
1372
1379
if (left .isObjectLit ()) {
1373
1380
return tryFoldObjectPropAccess (n , left , right );
@@ -1420,6 +1427,7 @@ private Node tryFoldArrayAccess(Node n, Node left, Node right) {
1420
1427
double index = right .getDouble ();
1421
1428
int intIndex = (int ) index ;
1422
1429
if (intIndex != index ) {
1430
+ // Ideally this should be caught in the check passes.
1423
1431
report (INVALID_GETELEM_INDEX_ERROR , right );
1424
1432
return n ;
1425
1433
}
@@ -1535,8 +1543,25 @@ private Node tryFoldStringArrayAccess(Node n, Node left, Node right) {
1535
1543
return elem ;
1536
1544
}
1537
1545
1546
+ /**
1547
+ * Tries to fold the node `n` that's accessing an object literal's property. Bails out (skips
1548
+ * folding and returns the same `n` node) with certainty when any of the following holds true:
1549
+ *
1550
+ * <ul>
1551
+ * <li>the access `n` is L-value
1552
+ * <li>the property references super
1553
+ * <li>the object has side-effects other than those preserved by folding the property
1554
+ * <li>property accessed does not exist on the object (might exist on prototype)
1555
+ * </ul>
1556
+ *
1557
+ * <ul>
1558
+ * Examples of folding:
1559
+ * <li>`({a() { return 1; }})?.a` ---> `(function() { return 1; })`
1560
+ * <li>`({a() { return 1; }}).a()` ---> `(function() { return 1; }())`
1561
+ * </ul>
1562
+ */
1538
1563
private Node tryFoldObjectPropAccess (Node n , Node left , Node right ) {
1539
- checkArgument (NodeUtil .isNormalGet (n ));
1564
+ checkArgument (NodeUtil .isNormalOrOptChainGet (n ));
1540
1565
1541
1566
if (!left .isObjectLit () || !right .isString ()) {
1542
1567
return n ;
@@ -1546,6 +1571,7 @@ private Node tryFoldObjectPropAccess(Node n, Node left, Node right) {
1546
1571
// If GETPROP/GETELEM is used as assignment target the object literal is
1547
1572
// acting as a temporary we can't fold it here:
1548
1573
// "{a:x}.a += 1" is not "x += 1"
1574
+ checkState (!NodeUtil .isOptChainNode (n )); // optional chains can not be targets of assign
1549
1575
return n ;
1550
1576
}
1551
1577
@@ -1590,7 +1616,7 @@ private Node tryFoldObjectPropAccess(Node n, Node left, Node right) {
1590
1616
}
1591
1617
1592
1618
// Didn't find a definition of the name in the object literal, it might
1593
- // be coming from the Object prototype
1619
+ // be coming from the Object prototype.
1594
1620
if (value == null ) {
1595
1621
return n ;
1596
1622
}
@@ -1635,6 +1661,28 @@ private Node tryFoldObjectPropAccess(Node n, Node left, Node right) {
1635
1661
1636
1662
value .detach ();
1637
1663
1664
+ Node parent = n .getParent ();
1665
+
1666
+ if (NodeUtil .isOptChainNode (parent )) {
1667
+
1668
+ /**
1669
+ * If the chain continues after `n`, simply doing `n.replaceWith(value)` below would leave the
1670
+ * subsequent nodes in the current chain segment optional, with their start `n` replaced.
1671
+ *
1672
+ * <p>So, we must ensure that all nodes in the chain's current segment are made non-optional.
1673
+ *
1674
+ * <p>This can happen for e.g.
1675
+ *
1676
+ * <ul>
1677
+ * <li>`({a() { return 1; }})?.a()` ---> `(function() { return 1; })()`. Here, parent
1678
+ * OPTCHAIN_CALL node must be converted to CALL.
1679
+ * <li>`({a() { return 1; }})?.a().b.c?.d` ---> `(function() { return 1; })().b.c?.d`. Here,
1680
+ * all nodes upto `({a() { return 1; }})?.a().b.c` must become non-optional
1681
+ * </ul>
1682
+ */
1683
+ Node endOfCurrentChain = NodeUtil .getEndOfOptChainSegment (parent );
1684
+ NodeUtil .convertToNonOptionalChainSegment (endOfCurrentChain );
1685
+ }
1638
1686
if (keyIsGetter ) {
1639
1687
value = IR .call (value );
1640
1688
value .putBooleanProp (Node .FREE_CALL , true );
0 commit comments