Bug 1566141 - fix fold constants for nullish coalesing r=jorendorff
authoryulia <ystartsev@mozilla.com>
Fri, 15 Nov 2019 17:15:24 +0000
changeset 502242 14a04a7ccab4071b62f36bffc60362a790a40618
parent 502241 b35b1431e4e2abcb9a4369c8fe2aa17a9321fdb1
child 502243 65e4b8d04024e7341da9bad80ff02a6b0df9abc0
push id114172
push userdluca@mozilla.com
push dateTue, 19 Nov 2019 11:31:10 +0000
treeherdermozilla-inbound@b5c5ba07d3db [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs1566141
milestone72.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1566141 - fix fold constants for nullish coalesing r=jorendorff Differential Revision: https://phabricator.services.mozilla.com/D52694
js/src/frontend/FoldConstants.cpp
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -689,55 +689,61 @@ static bool FoldUnaryArithmetic(JSContex
                         handler->newNumber(d, NoDecimal, node->pn_pos))) {
       return false;
     }
   }
 
   return true;
 }
 
-static bool FoldAndOr(JSContext* cx, ParseNode** nodePtr) {
+static bool FoldAndOrCoalesce(JSContext* cx, ParseNode** nodePtr) {
   ListNode* node = &(*nodePtr)->as<ListNode>();
 
   MOZ_ASSERT(node->isKind(ParseNodeKind::AndExpr) ||
              node->isKind(ParseNodeKind::CoalesceExpr) ||
              node->isKind(ParseNodeKind::OrExpr));
 
-  bool isOrNode = node->isKind(ParseNodeKind::OrExpr) ||
-                  node->isKind(ParseNodeKind::CoalesceExpr);
+  bool isOrNode = node->isKind(ParseNodeKind::OrExpr);
+  bool isAndNode = node->isKind(ParseNodeKind::AndExpr);
+  bool isCoalesceNode = node->isKind(ParseNodeKind::CoalesceExpr);
   ParseNode** elem = node->unsafeHeadReference();
   do {
     Truthiness t = Boolish(*elem);
 
     // If we don't know the constant-folded node's truthiness, we can't
     // reduce this node with its surroundings.  Continue folding any
     // remaining nodes.
     if (t == Unknown) {
       elem = &(*elem)->pn_next;
       continue;
     }
 
+    bool isTruthyCoalesceNode =
+        isCoalesceNode && !((*elem)->isKind(ParseNodeKind::NullExpr) ||
+                            (*elem)->isKind(ParseNodeKind::RawUndefinedExpr));
+    bool canShortCircuit = (isOrNode && t == Truthy) ||
+                           (isAndNode && t == Falsy) || isTruthyCoalesceNode;
+
     // If the constant-folded node's truthiness will terminate the
-    // condition -- `a || true || expr` or |b && false && expr| -- then
-    // trailing nodes will never be evaluated.  Truncate the list after
-    // the known-truthiness node, as it's the overall result.
-    if ((t == Truthy) == isOrNode) {
+    // condition -- `a || true || expr` or `b && false && expr` or
+    // `false ?? c ?? expr` -- then trailing nodes will never be
+    // evaluated.  Truncate the list after the known-truthiness node,
+    // as it's the overall result.
+    if (canShortCircuit) {
       for (ParseNode* next = (*elem)->pn_next; next; next = next->pn_next) {
         node->unsafeDecrementCount();
       }
 
       // Terminate the original and/or list at the known-truthiness
       // node.
       (*elem)->pn_next = nullptr;
       elem = &(*elem)->pn_next;
       break;
     }
 
-    MOZ_ASSERT((t == Truthy) == !isOrNode);
-
     // We've encountered a vacuous node that'll never short-circuit
     // evaluation.
     if ((*elem)->pn_next) {
       // This node is never the overall result when there are
       // subsequent nodes.  Remove it.
       ParseNode* elt = *elem;
       *elem = elt->pn_next;
       node->unsafeDecrementCount();
@@ -1343,22 +1349,26 @@ class FoldVisitor : public RewritingPars
   }
 
   bool visitUrshExpr(ParseNode*& pn) {
     return Base::visitUrshExpr(pn) && FoldBinaryArithmetic(cx_, handler, &pn);
   }
 
   bool visitAndExpr(ParseNode*& pn) {
     // Note that this does result in the unfortunate fact that dead arms of this
-    // node get constant folded. The same goes for visitOr.
-    return Base::visitAndExpr(pn) && FoldAndOr(cx_, &pn);
+    // node get constant folded. The same goes for visitOr and visitCoalesce.
+    return Base::visitAndExpr(pn) && FoldAndOrCoalesce(cx_, &pn);
   }
 
   bool visitOrExpr(ParseNode*& pn) {
-    return Base::visitOrExpr(pn) && FoldAndOr(cx_, &pn);
+    return Base::visitOrExpr(pn) && FoldAndOrCoalesce(cx_, &pn);
+  }
+
+  bool visitCoalesceExpr(ParseNode*& pn) {
+    return Base::visitCoalesceExpr(pn) && FoldAndOrCoalesce(cx_, &pn);
   }
 
   bool visitConditionalExpr(ParseNode*& pn) {
     // Don't call base-class visitConditional because FoldConditional processes
     // pn's child nodes specially to save stack space.
     return FoldConditional(cx_, handler, &pn);
   }