bug 1525331 - Make ParseNode::pn_type const. r=jorendorff
authorAshley Hauck <khyperia@mozilla.com>
Wed, 06 Feb 2019 20:32:21 +0000
changeset 515338 aba804ffb8e8410a1ff38cd1bdfe70de1107d23a
parent 515337 84272e744f16292325d580723a9ba17de7b6b8f2
child 515339 a226aae4349fe5e23fbd453f7a965a090abca1fc
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs1525331
milestone67.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 1525331 - Make ParseNode::pn_type const. r=jorendorff Differential Revision: https://phabricator.services.mozilla.com/D18729
js/src/frontend/BinASTParser.cpp
js/src/frontend/BinASTParser.h
js/src/frontend/BinSource.yaml
js/src/frontend/FoldConstants.cpp
js/src/frontend/FoldConstants.h
js/src/frontend/FullParseHandler.h
js/src/frontend/ParseNode.cpp
js/src/frontend/ParseNode.h
js/src/frontend/Parser.cpp
--- a/js/src/frontend/BinASTParser.cpp
+++ b/js/src/frontend/BinASTParser.cpp
@@ -3755,19 +3755,18 @@ JS::Result<ParseNode*> BinASTParser<Tok>
   BINJS_MOZ_TRY_DECL(name, parseIdentifierExpression());
 
   MOZ_ASSERT(name->isKind(ParseNodeKind::Name));
   MOZ_ASSERT(!factory_.isUsableAsObjectPropertyName(name));
   BINJS_TRY_DECL(propName, factory_.newObjectLiteralPropertyName(
                                name->template as<NameNode>().name(),
                                tokenizer_->pos(start)));
 
-  BINJS_TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(
-                             propName, name, AccessorType::None));
-  result->setKind(ParseNodeKind::Shorthand);
+  BINJS_TRY_DECL(result,
+                 factory_.newShorthandPropertyDefinition(propName, name));
   return result;
 }
 
 template <typename Tok>
 JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceSpreadElement(
     const size_t start, const BinKind kind, const BinFields& fields) {
   return raiseError(
       "FIXME: Not implemented yet in this preview release (SpreadElement)");
@@ -4186,34 +4185,34 @@ JS::Result<ParseNode*> BinASTParser<Tok>
   const BinField expected_fields[2] = {BinField::Kind, BinField::Declarators};
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
   AutoVariableDeclarationKind kindGuard(this);
 
   BINJS_MOZ_TRY_DECL(kind_, parseVariableDeclarationKind());
   // Restored by `kindGuard`.
   variableDeclarationKind_ = kind_;
-  BINJS_MOZ_TRY_DECL(declarators, parseListOfVariableDeclarator());
-
-  // By specification, the list may not be empty.
-  if (declarators->empty()) {
-    return raiseEmpty("VariableDeclaration");
-  }
-
-  ParseNodeKind pnk;
+  ParseNodeKind declarationListKind;
   switch (kind_) {
     case VariableDeclarationKind::Var:
-      pnk = ParseNodeKind::VarStmt;
+      declarationListKind = ParseNodeKind::VarStmt;
       break;
     case VariableDeclarationKind::Let:
       return raiseError("Let is not supported in this preview release");
     case VariableDeclarationKind::Const:
       return raiseError("Const is not supported in this preview release");
   }
-  declarators->setKind(pnk);
+  BINJS_MOZ_TRY_DECL(declarators,
+                     parseListOfVariableDeclarator(declarationListKind));
+
+  // By specification, the list may not be empty.
+  if (declarators->empty()) {
+    return raiseEmpty("VariableDeclaration");
+  }
+
   auto result = declarators;
   return result;
 }
 
 /*
  interface VariableDeclarator : Node {
     Binding binding;
     Expression? init;
@@ -4803,25 +4802,25 @@ JS::Result<ListNode*> BinASTParser<Tok>:
     factory_.addCaseStatementToList(result, item);
   }
 
   MOZ_TRY(guard.done());
   return result;
 }
 
 template <typename Tok>
-JS::Result<ListNode*> BinASTParser<Tok>::parseListOfVariableDeclarator() {
+JS::Result<ListNode*> BinASTParser<Tok>::parseListOfVariableDeclarator(
+    ParseNodeKind declarationListKind) {
   uint32_t length;
   AutoList guard(*tokenizer_);
 
   const auto start = tokenizer_->offset();
   MOZ_TRY(tokenizer_->enterList(length, guard));
-  BINJS_TRY_DECL(result, factory_.newDeclarationList(
-                             ParseNodeKind::ConstDecl /* Placeholder */,
-                             tokenizer_->pos(start)));
+  BINJS_TRY_DECL(result, factory_.newDeclarationList(declarationListKind,
+                                                     tokenizer_->pos(start)));
 
   for (uint32_t i = 0; i < length; ++i) {
     BINJS_MOZ_TRY_DECL(item, parseVariableDeclarator());
     result->appendWithoutOrderAssumption(item);
   }
 
   MOZ_TRY(guard.done());
   return result;
--- a/js/src/frontend/BinASTParser.h
+++ b/js/src/frontend/BinASTParser.h
@@ -470,17 +470,18 @@ class BinASTParser : public BinASTParser
       AssertedScopeKind scopeKind,
       MutableHandle<GCVector<JSAtom*>> positionalParams);
   JS::Result<ListNode*> parseListOfDirective();
   JS::Result<ListNode*> parseListOfObjectProperty();
   JS::Result<ListNode*> parseListOfOptionalSpreadElementOrExpression();
   JS::Result<ListNode*> parseListOfParameter();
   JS::Result<ListNode*> parseListOfStatement();
   JS::Result<ListNode*> parseListOfSwitchCase();
-  JS::Result<ListNode*> parseListOfVariableDeclarator();
+  JS::Result<ListNode*> parseListOfVariableDeclarator(
+      ParseNodeKind declarationListKind);
 
   // ----- Default values (by lexicographical order)
   JS::Result<ParseNode*> parseOptionalBinding();
   JS::Result<ParseNode*> parseOptionalBindingIdentifier();
   JS::Result<LexicalScopeNode*> parseOptionalCatchClause();
   JS::Result<ParseNode*> parseOptionalExpression();
   JS::Result<ParseNode*> parseOptionalSpreadElementOrExpression();
   JS::Result<ParseNode*> parseOptionalStatement();
--- a/js/src/frontend/BinSource.yaml
+++ b/js/src/frontend/BinSource.yaml
@@ -1241,21 +1241,22 @@ ListOfSwitchCase:
   type-ok:
     ListNode*
   init:
     BINJS_TRY_DECL(result, factory_.newStatementList(tokenizer_->pos(start)));
   append:
     factory_.addCaseStatementToList(result, item);
 
 ListOfVariableDeclarator:
+  extra-params: ParseNodeKind declarationListKind
   type-ok:
     ListNode*
   init: |
     BINJS_TRY_DECL(result,
-                   factory_.newDeclarationList(ParseNodeKind::ConstDecl /* Placeholder */,
+                   factory_.newDeclarationList(declarationListKind,
                    tokenizer_->pos(start)));
 
 LiteralBooleanExpression:
   build: |
     BINJS_TRY_DECL(result,
                    factory_.newBooleanLiteral(value, tokenizer_->pos(start)));
 
 LiteralNumericExpression:
@@ -1372,19 +1373,17 @@ ShorthandProperty:
   build: |
     MOZ_ASSERT(name->isKind(ParseNodeKind::Name));
     MOZ_ASSERT(!factory_.isUsableAsObjectPropertyName(name));
     BINJS_TRY_DECL(propName,
                    factory_.newObjectLiteralPropertyName(name->template as<NameNode>().name(),
                                                          tokenizer_->pos(start)));
 
     BINJS_TRY_DECL(result,
-                   factory_.newObjectMethodOrPropertyDefinition(propName, name,
-                                                                AccessorType::None));
-    result->setKind(ParseNodeKind::Shorthand);
+                   factory_.newShorthandPropertyDefinition(propName, name));
 
 SwitchCase:
   type-ok:
     CaseClause*
   build: |
     BINJS_TRY_DECL(result, factory_.newCaseOrDefault(start, test, consequent));
 
 SwitchDefault:
@@ -1572,34 +1571,35 @@ VariableDeclaration:
   init:
     AutoVariableDeclarationKind kindGuard(this);
 
   fields:
     kind:
       after: |
         // Restored by `kindGuard`.
         variableDeclarationKind_ = kind_;
+        ParseNodeKind declarationListKind;
+        switch (kind_) {
+        case VariableDeclarationKind::Var:
+          declarationListKind = ParseNodeKind::VarStmt;
+          break;
+        case VariableDeclarationKind::Let:
+          return raiseError("Let is not supported in this preview release");
+        case VariableDeclarationKind::Const:
+          return raiseError("Const is not supported in this preview release");
+        }
+    declarators:
+      extra-args: declarationListKind
 
   build: |
     // By specification, the list may not be empty.
     if (declarators->empty()) {
       return raiseEmpty("VariableDeclaration");
     }
 
-    ParseNodeKind pnk;
-    switch (kind_) {
-      case VariableDeclarationKind::Var:
-        pnk = ParseNodeKind::VarStmt;
-        break;
-      case VariableDeclarationKind::Let:
-        return raiseError("Let is not supported in this preview release");
-      case VariableDeclarationKind::Const:
-        return raiseError("Const is not supported in this preview release");
-    }
-    declarators->setKind(pnk);
     auto result = declarators;
 
 VariableDeclarator:
   build: |
     ParseNode* result;
     if (binding->isKind(ParseNodeKind::Name)) {
       // `var foo [= bar]``
       NameNode* bindingNameNode = &binding->template as<NameNode>();
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -23,16 +23,31 @@ using namespace js::frontend;
 using JS::GenericNaN;
 using JS::ToInt32;
 using JS::ToUint32;
 using mozilla::IsNaN;
 using mozilla::IsNegative;
 using mozilla::NegativeInfinity;
 using mozilla::PositiveInfinity;
 
+// Don't use ReplaceNode directly, because we want the constant folder to keep
+// the attributes isInParens and isDirectRHSAnonFunction of the old node being
+// replaced.
+inline MOZ_MUST_USE bool TryReplaceNode(ParseNode** pnp, ParseNode* pn) {
+  // convenience check: can call TryReplaceNode(pnp, alloc_parsenode())
+  // directly, without having to worry about alloc returning null.
+  if (!pn) {
+    return false;
+  }
+  pn->setInParens((*pnp)->isInParens());
+  pn->setDirectRHSAnonFunction((*pnp)->isDirectRHSAnonFunction());
+  ReplaceNode(pnp, pn);
+  return true;
+}
+
 static bool ContainsHoistedDeclaration(JSContext* cx, ParseNode* node,
                                        bool* result);
 
 static bool ListContainsHoistedDeclaration(JSContext* cx, ListNode* list,
                                            bool* result) {
   for (ParseNode* node : list->contents()) {
     if (!ContainsHoistedDeclaration(cx, node, result)) {
       return false;
@@ -418,42 +433,44 @@ restart:
 
   MOZ_CRASH("invalid node kind");
 }
 
 /*
  * Fold from one constant type to another.
  * XXX handles only strings and numbers for now
  */
-static bool FoldType(JSContext* cx, ParseNode* pn, ParseNodeKind kind) {
+static bool FoldType(JSContext* cx, FullParseHandler* handler, ParseNode** pnp,
+                     ParseNodeKind kind) {
+  const ParseNode* pn = *pnp;
   if (!pn->isKind(kind)) {
     switch (kind) {
       case ParseNodeKind::NumberExpr:
         if (pn->isKind(ParseNodeKind::StringExpr)) {
           double d;
           if (!StringToNumber(cx, pn->as<NameNode>().atom(), &d)) {
             return false;
           }
-          pn->setKind(ParseNodeKind::NumberExpr);
-          pn->setOp(JSOP_DOUBLE);
-          pn->as<NumericLiteral>().setValue(d);
-          pn->as<NumericLiteral>().setDecimalPoint(NoDecimal);
+          if (!TryReplaceNode(pnp,
+                              handler->newNumber(d, NoDecimal, pn->pn_pos))) {
+            return false;
+          }
         }
         break;
 
       case ParseNodeKind::StringExpr:
         if (pn->isKind(ParseNodeKind::NumberExpr)) {
           JSAtom* atom = NumberToAtom(cx, pn->as<NumericLiteral>().value());
           if (!atom) {
             return false;
           }
-          pn->setKind(ParseNodeKind::StringExpr);
-          pn->setOp(JSOP_STRING);
-          pn->as<NameNode>().setAtom(atom);
-          pn->as<NameNode>().setInitializer(nullptr);
+          if (!TryReplaceNode(pnp,
+                              handler->newStringLiteral(atom, pn->pn_pos))) {
+            return false;
+          }
         }
         break;
 
       default:
         MOZ_CRASH("Invalid type in constant folding FoldType");
     }
   }
   return true;
@@ -516,44 +533,43 @@ static Truthiness Boolish(ParseNode* pn)
       return IsEffectless(pn) ? Falsy : Unknown;
     }
 
     default:
       return Unknown;
   }
 }
 
-static bool SimplifyCondition(JSContext* cx, ParseNode** nodePtr) {
+static bool SimplifyCondition(JSContext* cx, FullParseHandler* handler,
+                              ParseNode** nodePtr) {
   // Conditions fold like any other expression, but then they sometimes can be
   // further folded to constants. *nodePtr should already have been
   // constant-folded.
 
   ParseNode* node = *nodePtr;
   Truthiness t = Boolish(node);
   if (t != Unknown) {
     // We can turn function nodes into constant nodes here, but mutating
     // function nodes is tricky --- in particular, mutating a function node
     // that appears on a method list corrupts the method list. However,
     // methods are M's in statements of the form 'this.foo = M;', which we
     // never fold, so we're okay.
-    if (t == Truthy) {
-      node->setKind(ParseNodeKind::TrueExpr);
-      node->setOp(JSOP_TRUE);
-    } else {
-      node->setKind(ParseNodeKind::FalseExpr);
-      node->setOp(JSOP_FALSE);
+    if (!TryReplaceNode(
+            nodePtr, handler->newBooleanLiteral(t == Truthy, node->pn_pos))) {
+      return false;
     }
   }
 
   return true;
 }
 
-static bool FoldTypeOfExpr(JSContext* cx, UnaryNode* node) {
+static bool FoldTypeOfExpr(JSContext* cx, FullParseHandler* handler,
+                           ParseNode** nodePtr) {
+  UnaryNode* node = &(*nodePtr)->as<UnaryNode>();
   MOZ_ASSERT(node->isKind(ParseNodeKind::TypeOfExpr));
-
   ParseNode* expr = node->kid();
 
   // Constant-fold the entire |typeof| if given a constant with known type.
   RootedPropertyName result(cx);
   if (expr->isKind(ParseNodeKind::StringExpr) ||
       expr->isKind(ParseNodeKind::TemplateStringExpr)) {
     result = cx->names().string;
   } else if (expr->isKind(ParseNodeKind::NumberExpr)) {
@@ -569,79 +585,97 @@ static bool FoldTypeOfExpr(JSContext* cx
   } else if (expr->isKind(ParseNodeKind::TrueExpr) ||
              expr->isKind(ParseNodeKind::FalseExpr)) {
     result = cx->names().boolean;
   } else if (expr->is<FunctionNode>()) {
     result = cx->names().function;
   }
 
   if (result) {
-    node->setKind(ParseNodeKind::StringExpr);
-    node->setOp(JSOP_NOP);
-    node->as<NameNode>().setAtom(result);
-    node->as<NameNode>().setInitializer(nullptr);
+    if (!TryReplaceNode(nodePtr,
+                        handler->newStringLiteral(result, node->pn_pos))) {
+      return false;
+    }
   }
 
   return true;
 }
 
-static bool FoldDeleteExpr(JSContext* cx, UnaryNode* node) {
+static bool FoldDeleteExpr(JSContext* cx, FullParseHandler* handler,
+                           ParseNode** nodePtr) {
+  UnaryNode* node = &(*nodePtr)->as<UnaryNode>();
+
   MOZ_ASSERT(node->isKind(ParseNodeKind::DeleteExpr));
   ParseNode* expr = node->kid();
 
   // Expression deletion evaluates the expression, then evaluates to true.
   // For effectless expressions, eliminate the expression evaluation.
   if (IsEffectless(expr)) {
-    node->setKind(ParseNodeKind::TrueExpr);
-    node->setOp(JSOP_TRUE);
+    if (!TryReplaceNode(nodePtr,
+                        handler->newBooleanLiteral(true, node->pn_pos))) {
+      return false;
+    }
   }
 
   return true;
 }
 
-static bool FoldDeleteElement(JSContext* cx, UnaryNode* node) {
+static bool FoldDeleteElement(JSContext* cx, FullParseHandler* handler,
+                              ParseNode** nodePtr) {
+  UnaryNode* node = &(*nodePtr)->as<UnaryNode>();
   MOZ_ASSERT(node->isKind(ParseNodeKind::DeleteElemExpr));
   ParseNode* expr = node->kid();
 
   // If we're deleting an element, but constant-folding converted our
   // element reference into a dotted property access, we must *also*
   // morph the node's kind.
   //
   // In principle this also applies to |super["foo"] -> super.foo|,
   // but we don't constant-fold |super["foo"]| yet.
   MOZ_ASSERT(expr->isKind(ParseNodeKind::ElemExpr) ||
              expr->isKind(ParseNodeKind::DotExpr));
   if (expr->isKind(ParseNodeKind::DotExpr)) {
-    node->setKind(ParseNodeKind::DeletePropExpr);
+    // newDelete will detect and use DeletePropExpr
+    if (!TryReplaceNode(nodePtr,
+                        handler->newDelete(node->pn_pos.begin, expr))) {
+      return false;
+    }
+    MOZ_ASSERT((*nodePtr)->getKind() == ParseNodeKind::DeletePropExpr);
   }
 
   return true;
 }
 
-static bool FoldNot(JSContext* cx, UnaryNode* node) {
+static bool FoldNot(JSContext* cx, FullParseHandler* handler,
+                    ParseNode** nodePtr) {
+  UnaryNode* node = &(*nodePtr)->as<UnaryNode>();
   MOZ_ASSERT(node->isKind(ParseNodeKind::NotExpr));
 
-  if (!SimplifyCondition(cx, node->unsafeKidReference())) {
+  if (!SimplifyCondition(cx, handler, node->unsafeKidReference())) {
     return false;
   }
 
   ParseNode* expr = node->kid();
 
   if (expr->isKind(ParseNodeKind::TrueExpr) ||
       expr->isKind(ParseNodeKind::FalseExpr)) {
     bool newval = !expr->isKind(ParseNodeKind::TrueExpr);
 
-    node->setKind(newval ? ParseNodeKind::TrueExpr : ParseNodeKind::FalseExpr);
-    node->setOp(newval ? JSOP_TRUE : JSOP_FALSE);
+    if (!TryReplaceNode(nodePtr,
+                        handler->newBooleanLiteral(newval, node->pn_pos))) {
+      return false;
+    }
   }
 
   return true;
 }
 
-static bool FoldUnaryArithmetic(JSContext* cx, UnaryNode* node) {
+static bool FoldUnaryArithmetic(JSContext* cx, FullParseHandler* handler,
+                                ParseNode** nodePtr) {
+  UnaryNode* node = &(*nodePtr)->as<UnaryNode>();
   MOZ_ASSERT(node->isKind(ParseNodeKind::BitNotExpr) ||
                  node->isKind(ParseNodeKind::PosExpr) ||
                  node->isKind(ParseNodeKind::NegExpr),
              "need a different method for this node kind");
 
   ParseNode* expr = node->kid();
 
   if (expr->isKind(ParseNodeKind::NumberExpr) ||
@@ -654,20 +688,20 @@ static bool FoldUnaryArithmetic(JSContex
     if (node->isKind(ParseNodeKind::BitNotExpr)) {
       d = ~ToInt32(d);
     } else if (node->isKind(ParseNodeKind::NegExpr)) {
       d = -d;
     } else {
       MOZ_ASSERT(node->isKind(ParseNodeKind::PosExpr));  // nothing to do
     }
 
-    node->setKind(ParseNodeKind::NumberExpr);
-    node->setOp(JSOP_DOUBLE);
-    node->as<NumericLiteral>().setValue(d);
-    node->as<NumericLiteral>().setDecimalPoint(NoDecimal);
+    if (!TryReplaceNode(nodePtr,
+                        handler->newNumber(d, NoDecimal, node->pn_pos))) {
+      return false;
+    }
   }
 
   return true;
 }
 
 static bool FoldAndOr(JSContext* cx, ParseNode** nodePtr) {
   ListNode* node = &(*nodePtr)->as<ListNode>();
 
@@ -728,58 +762,59 @@ static bool FoldAndOr(JSContext* cx, Par
   if (node->count() == 1) {
     ParseNode* first = node->head();
     ReplaceNode(nodePtr, first);
   }
 
   return true;
 }
 
-static bool Fold(JSContext* cx, ParseNode** pnp);
+static bool Fold(JSContext* cx, FullParseHandler* handler, ParseNode** pnp);
 
-static bool FoldConditional(JSContext* cx, ParseNode** nodePtr) {
+static bool FoldConditional(JSContext* cx, FullParseHandler* handler,
+                            ParseNode** nodePtr) {
   ParseNode** nextNode = nodePtr;
 
   do {
     // |nextNode| on entry points to the C?T:F expression to be folded.
     // Reset it to exit the loop in the common case where F isn't another
     // ?: expression.
     nodePtr = nextNode;
     nextNode = nullptr;
 
     TernaryNode* node = &(*nodePtr)->as<TernaryNode>();
     MOZ_ASSERT(node->isKind(ParseNodeKind::ConditionalExpr));
 
     ParseNode** expr = node->unsafeKid1Reference();
-    if (!Fold(cx, expr)) {
+    if (!Fold(cx, handler, expr)) {
       return false;
     }
-    if (!SimplifyCondition(cx, expr)) {
+    if (!SimplifyCondition(cx, handler, expr)) {
       return false;
     }
 
     ParseNode** ifTruthy = node->unsafeKid2Reference();
-    if (!Fold(cx, ifTruthy)) {
+    if (!Fold(cx, handler, ifTruthy)) {
       return false;
     }
 
     ParseNode** ifFalsy = node->unsafeKid3Reference();
 
     // If our C?T:F node has F as another ?: node, *iteratively* constant-
     // fold F *after* folding C and T (and possibly eliminating C and one
     // of T/F entirely); otherwise fold F normally.  Making |nextNode| non-
     // null causes this loop to run again to fold F.
     //
     // Conceivably we could instead/also iteratively constant-fold T, if T
     // were more complex than F.  Such an optimization is unimplemented.
     if ((*ifFalsy)->isKind(ParseNodeKind::ConditionalExpr)) {
       MOZ_ASSERT((*ifFalsy)->is<TernaryNode>());
       nextNode = ifFalsy;
     } else {
-      if (!Fold(cx, ifFalsy)) {
+      if (!Fold(cx, handler, ifFalsy)) {
         return false;
       }
     }
 
     // Try to constant-fold based on the condition expression.
     Truthiness t = Boolish(*expr);
     if (t == Unknown) {
       continue;
@@ -795,53 +830,54 @@ static bool FoldConditional(JSContext* c
       nextNode = (*nextNode == replacement) ? nodePtr : nullptr;
     }
     ReplaceNode(nodePtr, replacement);
   } while (nextNode);
 
   return true;
 }
 
-static bool FoldIf(JSContext* cx, ParseNode** nodePtr) {
+static bool FoldIf(JSContext* cx, FullParseHandler* handler,
+                   ParseNode** nodePtr) {
   ParseNode** nextNode = nodePtr;
 
   do {
     // |nextNode| on entry points to the initial |if| to be folded.  Reset
     // it to exit the loop when the |else| arm isn't another |if|.
     nodePtr = nextNode;
     nextNode = nullptr;
 
     TernaryNode* node = &(*nodePtr)->as<TernaryNode>();
     MOZ_ASSERT(node->isKind(ParseNodeKind::IfStmt));
 
     ParseNode** expr = node->unsafeKid1Reference();
-    if (!Fold(cx, expr)) {
+    if (!Fold(cx, handler, expr)) {
       return false;
     }
-    if (!SimplifyCondition(cx, expr)) {
+    if (!SimplifyCondition(cx, handler, expr)) {
       return false;
     }
 
     ParseNode** consequent = node->unsafeKid2Reference();
-    if (!Fold(cx, consequent)) {
+    if (!Fold(cx, handler, consequent)) {
       return false;
     }
 
     ParseNode** alternative = node->unsafeKid3Reference();
     if (*alternative) {
       // If in |if (C) T; else F;| we have |F| as another |if|,
       // *iteratively* constant-fold |F| *after* folding |C| and |T| (and
       // possibly completely replacing the whole thing with |T| or |F|);
       // otherwise fold F normally.  Making |nextNode| non-null causes
       // this loop to run again to fold F.
       if ((*alternative)->isKind(ParseNodeKind::IfStmt)) {
         MOZ_ASSERT((*alternative)->is<TernaryNode>());
         nextNode = alternative;
       } else {
-        if (!Fold(cx, alternative)) {
+        if (!Fold(cx, handler, alternative)) {
           return false;
         }
       }
     }
 
     // Eliminate the consequent or alternative if the condition has
     // constant truthiness.
     Truthiness t = Boolish(*expr);
@@ -876,18 +912,19 @@ static bool FoldIf(JSContext* cx, ParseN
     if (!performReplacement) {
       continue;
     }
 
     if (!replacement) {
       // If there's no replacement node, we have a constantly-false |if|
       // with no |else|.  Replace the entire thing with an empty
       // statement list.
-      node->setKind(ParseNodeKind::StatementList);
-      node->as<ListNode>().makeEmpty();
+      if (!TryReplaceNode(nodePtr, handler->newStatementList(node->pn_pos))) {
+        return false;
+      }
     } else {
       // Replacement invalidates |nextNode|, so reset it (if the
       // replacement requires folding) or clear it (if |alternative|
       // is dead code) as needed.
       if (nextNode) {
         nextNode = (*nextNode == replacement) ? nodePtr : nullptr;
       }
       ReplaceNode(nodePtr, replacement);
@@ -924,86 +961,87 @@ static double ComputeBinary(ParseNodeKin
 
   MOZ_ASSERT(kind == ParseNodeKind::LshExpr || kind == ParseNodeKind::RshExpr);
 
   int32_t i = ToInt32(left);
   uint32_t j = ToUint32(right) & 31;
   return int32_t((kind == ParseNodeKind::LshExpr) ? uint32_t(i) << j : i >> j);
 }
 
-static bool FoldBinaryArithmetic(JSContext* cx, ListNode* node) {
+static bool FoldBinaryArithmetic(JSContext* cx, FullParseHandler* handler,
+                                 ParseNode** nodePtr) {
+  ListNode* node = &(*nodePtr)->as<ListNode>();
   MOZ_ASSERT(node->isKind(ParseNodeKind::SubExpr) ||
              node->isKind(ParseNodeKind::MulExpr) ||
              node->isKind(ParseNodeKind::LshExpr) ||
              node->isKind(ParseNodeKind::RshExpr) ||
              node->isKind(ParseNodeKind::UrshExpr) ||
              node->isKind(ParseNodeKind::DivExpr) ||
              node->isKind(ParseNodeKind::ModExpr));
   MOZ_ASSERT(node->count() >= 2);
 
   // Fold each operand to a number if possible.
   ParseNode** listp = node->unsafeHeadReference();
   for (; *listp; listp = &(*listp)->pn_next) {
-    if (!FoldType(cx, *listp, ParseNodeKind::NumberExpr)) {
+    if (!FoldType(cx, handler, listp, ParseNodeKind::NumberExpr)) {
       return false;
     }
   }
   node->unsafeReplaceTail(listp);
 
   // Now fold all leading numeric terms together into a single number.
   // (Trailing terms for the non-shift operations can't be folded together
   // due to floating point imprecision.  For example, if |x === -2**53|,
   // |x - 1 - 1 === -2**53| but |x - 2 === -2**53 - 2|.  Shifts could be
   // folded, but it doesn't seem worth the effort.)
-  ParseNode* elem = node->head();
-  ParseNode* next = elem->pn_next;
-  if (elem->isKind(ParseNodeKind::NumberExpr)) {
+  ParseNode** elem = node->unsafeHeadReference();
+  ParseNode** next = &(*elem)->pn_next;
+  if ((*elem)->isKind(ParseNodeKind::NumberExpr)) {
     ParseNodeKind kind = node->getKind();
     while (true) {
-      if (!next || !next->isKind(ParseNodeKind::NumberExpr)) {
+      if (!*next || !(*next)->isKind(ParseNodeKind::NumberExpr)) {
         break;
       }
 
-      double d = ComputeBinary(kind, elem->as<NumericLiteral>().value(),
-                               next->as<NumericLiteral>().value());
-
-      next = next->pn_next;
-      elem->pn_next = next;
+      double d = ComputeBinary(kind, (*elem)->as<NumericLiteral>().value(),
+                               (*next)->as<NumericLiteral>().value());
 
-      elem->setKind(ParseNodeKind::NumberExpr);
-      elem->setOp(JSOP_DOUBLE);
-      elem->as<NumericLiteral>().setValue(d);
-      elem->as<NumericLiteral>().setDecimalPoint(NoDecimal);
+      TokenPos pos((*elem)->pn_pos.begin, (*next)->pn_pos.end);
+      if (!TryReplaceNode(elem, handler->newNumber(d, NoDecimal, pos))) {
+        return false;
+      }
 
+      (*elem)->pn_next = (*next)->pn_next;
+      next = &(*elem)->pn_next;
       node->unsafeDecrementCount();
     }
 
     if (node->count() == 1) {
-      MOZ_ASSERT(node->head() == elem);
-      MOZ_ASSERT(elem->isKind(ParseNodeKind::NumberExpr));
+      MOZ_ASSERT(node->head() == *elem);
+      MOZ_ASSERT((*elem)->isKind(ParseNodeKind::NumberExpr));
 
-      double d = elem->as<NumericLiteral>().value();
-      node->setKind(ParseNodeKind::NumberExpr);
-      node->setOp(JSOP_DOUBLE);
-      node->as<NumericLiteral>().setValue(d);
-      node->as<NumericLiteral>().setDecimalPoint(NoDecimal);
+      if (!TryReplaceNode(nodePtr, *elem)) {
+        return false;
+      }
     }
   }
 
   return true;
 }
 
-static bool FoldExponentiation(JSContext* cx, ListNode* node) {
+static bool FoldExponentiation(JSContext* cx, FullParseHandler* handler,
+                               ParseNode** nodePtr) {
+  ListNode* node = &(*nodePtr)->as<ListNode>();
   MOZ_ASSERT(node->isKind(ParseNodeKind::PowExpr));
   MOZ_ASSERT(node->count() >= 2);
 
   // Fold each operand, ideally into a number.
   ParseNode** listp = node->unsafeHeadReference();
   for (; *listp; listp = &(*listp)->pn_next) {
-    if (!FoldType(cx, *listp, ParseNodeKind::NumberExpr)) {
+    if (!FoldType(cx, handler, listp, ParseNodeKind::NumberExpr)) {
       return false;
     }
   }
 
   node->unsafeReplaceTail(listp);
 
   // Unlike all other binary arithmetic operators, ** is right-associative:
   // 2**3**5 is 2**(3**5), not (2**3)**5.  As list nodes singly-link their
@@ -1019,40 +1057,39 @@ static bool FoldExponentiation(JSContext
   if (!base->isKind(ParseNodeKind::NumberExpr) ||
       !exponent->isKind(ParseNodeKind::NumberExpr)) {
     return true;
   }
 
   double d1 = base->as<NumericLiteral>().value();
   double d2 = exponent->as<NumericLiteral>().value();
 
-  node->setKind(ParseNodeKind::NumberExpr);
-  node->setOp(JSOP_DOUBLE);
-  node->as<NumericLiteral>().setValue(ecmaPow(d1, d2));
-  node->as<NumericLiteral>().setDecimalPoint(NoDecimal);
-  return true;
+  return TryReplaceNode(
+      nodePtr, handler->newNumber(ecmaPow(d1, d2), NoDecimal, node->pn_pos));
 }
 
-static bool FoldElement(JSContext* cx, ParseNode** nodePtr) {
+static bool FoldElement(JSContext* cx, FullParseHandler* handler,
+                        ParseNode** nodePtr) {
   PropertyByValue* elem = &(*nodePtr)->as<PropertyByValue>();
 
   ParseNode* expr = &elem->expression();
   ParseNode* key = &elem->key();
   PropertyName* name = nullptr;
   if (key->isKind(ParseNodeKind::StringExpr)) {
     JSAtom* atom = key->as<NameNode>().atom();
     uint32_t index;
 
     if (atom->isIndex(&index)) {
       // Optimization 1: We have something like expr["100"]. This is
       // equivalent to expr[100] which is faster.
-      key->setKind(ParseNodeKind::NumberExpr);
-      key->setOp(JSOP_DOUBLE);
-      key->as<NumericLiteral>().setValue(index);
-      key->as<NumericLiteral>().setDecimalPoint(NoDecimal);
+      if (!TryReplaceNode(elem->unsafeRightReference(),
+                          handler->newNumber(index, NoDecimal, key->pn_pos))) {
+        return false;
+      }
+      key = &elem->key();
     } else {
       name = atom->asPropertyName();
     }
   } else if (key->isKind(ParseNodeKind::NumberExpr)) {
     double number = key->as<NumericLiteral>().value();
     if (number != ToUint32(number)) {
       // Optimization 2: We have something like expr[3.14]. The number
       // isn't an array index, so it converts to a string ("3.14"),
@@ -1067,278 +1104,272 @@ static bool FoldElement(JSContext* cx, P
 
   // If we don't have a name, we can't optimize to getprop.
   if (!name) {
     return true;
   }
 
   // Optimization 3: We have expr["foo"] where foo is not an index.  Convert
   // to a property access (like expr.foo) that optimizes better downstream.
-  key->setKind(ParseNodeKind::PropertyNameExpr);
-  key->setOp(JSOP_NOP);
-  key->as<NameNode>().setAtom(name);
-  key->as<NameNode>().setInitializer(nullptr);
 
-  (*nodePtr)->setKind(ParseNodeKind::DotExpr);
-  (*nodePtr)->setOp(JSOP_NOP);
-  *(*nodePtr)->as<PropertyAccess>().unsafeLeftReference() = expr;
-  *(*nodePtr)->as<PropertyAccess>().unsafeRightReference() = key;
-  (*nodePtr)->as<PropertyAccess>().setInParens(elem->isInParens());
+  NameNode* propertyNameExpr = handler->newPropertyName(name, key->pn_pos);
+  if (!propertyNameExpr) {
+    return false;
+  }
+  if (!TryReplaceNode(nodePtr,
+                      handler->newPropertyAccess(expr, propertyNameExpr))) {
+    return false;
+  }
 
   return true;
 }
 
-static bool FoldAdd(JSContext* cx, ParseNode** nodePtr) {
+static bool FoldAdd(JSContext* cx, FullParseHandler* handler,
+                    ParseNode** nodePtr) {
   ListNode* node = &(*nodePtr)->as<ListNode>();
 
   MOZ_ASSERT(node->isKind(ParseNodeKind::AddExpr));
   MOZ_ASSERT(node->count() >= 2);
 
   // Fold leading numeric operands together:
   //
   //   (1 + 2 + x)  becomes  (3 + x)
   //
   // Don't go past the leading operands: additions after a string are
   // string concatenations, not additions: ("1" + 2 + 3 === "123").
-  ParseNode* current = node->head();
-  ParseNode* next = current->pn_next;
-  if (current->isKind(ParseNodeKind::NumberExpr)) {
+  ParseNode** current = node->unsafeHeadReference();
+  ParseNode** next = &(*current)->pn_next;
+  if ((*current)->isKind(ParseNodeKind::NumberExpr)) {
     do {
-      if (!next->isKind(ParseNodeKind::NumberExpr)) {
+      if (!(*next)->isKind(ParseNodeKind::NumberExpr)) {
         break;
       }
 
-      NumericLiteral* num = &current->as<NumericLiteral>();
+      double left = (*current)->as<NumericLiteral>().value();
+      double right = (*next)->as<NumericLiteral>().value();
+      TokenPos pos((*current)->pn_pos.begin, (*next)->pn_pos.end);
 
-      num->setValue(num->value() + next->as<NumericLiteral>().value());
-      current->pn_next = next->pn_next;
-      next = current->pn_next;
+      if (!TryReplaceNode(current,
+                          handler->newNumber(left + right, NoDecimal, pos))) {
+        return false;
+      }
+
+      (*current)->pn_next = (*next)->pn_next;
+      next = &(*current)->pn_next;
 
       node->unsafeDecrementCount();
-    } while (next);
+    } while (*next);
   }
 
   // If any operands remain, attempt string concatenation folding.
   do {
     // If no operands remain, we're done.
-    if (!next) {
+    if (!*next) {
       break;
     }
 
     // (number + string) is string concatenation *only* at the start of
     // the list: (x + 1 + "2" !== x + "12") when x is a number.
-    if (current->isKind(ParseNodeKind::NumberExpr) &&
-        next->isKind(ParseNodeKind::StringExpr)) {
-      if (!FoldType(cx, current, ParseNodeKind::StringExpr)) {
+    if ((*current)->isKind(ParseNodeKind::NumberExpr) &&
+        (*next)->isKind(ParseNodeKind::StringExpr)) {
+      if (!FoldType(cx, handler, current, ParseNodeKind::StringExpr)) {
         return false;
       }
-      next = current->pn_next;
+      next = &(*current)->pn_next;
     }
 
     // The first string forces all subsequent additions to be
     // string concatenations.
     do {
-      if (current->isKind(ParseNodeKind::StringExpr)) {
+      if ((*current)->isKind(ParseNodeKind::StringExpr)) {
         break;
       }
 
       current = next;
-      next = next->pn_next;
-    } while (next);
+      next = &(*current)->pn_next;
+    } while (*next);
 
     // If there's nothing left to fold, we're done.
-    if (!next) {
+    if (!*next) {
       break;
     }
 
     RootedString combination(cx);
     RootedString tmp(cx);
     do {
       // Create a rope of the current string and all succeeding
       // constants that we can convert to strings, then atomize it
       // and replace them all with that fresh string.
-      MOZ_ASSERT(current->isKind(ParseNodeKind::StringExpr));
+      MOZ_ASSERT((*current)->isKind(ParseNodeKind::StringExpr));
 
-      combination = current->as<NameNode>().atom();
+      combination = (*current)->as<NameNode>().atom();
 
       do {
         // Try folding the next operand to a string.
-        if (!FoldType(cx, next, ParseNodeKind::StringExpr)) {
+        if (!FoldType(cx, handler, next, ParseNodeKind::StringExpr)) {
           return false;
         }
 
         // Stop glomming once folding doesn't produce a string.
-        if (!next->isKind(ParseNodeKind::StringExpr)) {
+        if (!(*next)->isKind(ParseNodeKind::StringExpr)) {
           break;
         }
 
         // Add this string to the combination and remove the node.
-        tmp = next->as<NameNode>().atom();
+        tmp = (*next)->as<NameNode>().atom();
         combination = ConcatStrings<CanGC>(cx, combination, tmp);
         if (!combination) {
           return false;
         }
 
-        next = next->pn_next;
-        current->pn_next = next;
+        (*current)->pn_next = (*next)->pn_next;
+        next = &(*current)->pn_next;
 
         node->unsafeDecrementCount();
-      } while (next);
+      } while (*next);
 
       // Replace |current|'s string with the entire combination.
-      MOZ_ASSERT(current->isKind(ParseNodeKind::StringExpr));
+      MOZ_ASSERT((*current)->isKind(ParseNodeKind::StringExpr));
       combination = AtomizeString(cx, combination);
       if (!combination) {
         return false;
       }
-      current->as<NameNode>().setAtom(&combination->asAtom());
+      (*current)->as<NameNode>().setAtom(&combination->asAtom());
 
       // If we're out of nodes, we're done.
-      if (!next) {
+      if (!*next) {
         break;
       }
 
       current = next;
-      next = current->pn_next;
+      next = &(*current)->pn_next;
 
       // If we're out of nodes *after* the non-foldable-to-string
       // node, we're done.
-      if (!next) {
+      if (!*next) {
         break;
       }
 
       // Otherwise find the next node foldable to a string, and loop.
       do {
         current = next;
-        next = current->pn_next;
 
-        if (!FoldType(cx, current, ParseNodeKind::StringExpr)) {
+        if (!FoldType(cx, handler, current, ParseNodeKind::StringExpr)) {
           return false;
         }
-        next = current->pn_next;
-      } while (!current->isKind(ParseNodeKind::StringExpr) && next);
-    } while (next);
+        next = &(*current)->pn_next;
+      } while (!(*current)->isKind(ParseNodeKind::StringExpr) && *next);
+    } while (*next);
   } while (false);
 
-  MOZ_ASSERT(!next, "must have considered all nodes here");
-  MOZ_ASSERT(!current->pn_next, "current node must be the last node");
+  MOZ_ASSERT(!*next, "must have considered all nodes here");
+  MOZ_ASSERT(!(*current)->pn_next, "current node must be the last node");
 
-  node->unsafeReplaceTail(&current->pn_next);
+  node->unsafeReplaceTail(&(*current)->pn_next);
 
   if (node->count() == 1) {
     // We reduced the list to a constant.  Replace the ParseNodeKind::Add node
     // with that constant.
-    ReplaceNode(nodePtr, current);
+    ReplaceNode(nodePtr, *current);
   }
 
   return true;
 }
 
 class FoldVisitor : public ParseNodeVisitor<FoldVisitor> {
   using Base = ParseNodeVisitor;
 
+  FullParseHandler* handler;
+
  public:
-  explicit FoldVisitor(JSContext* cx) : ParseNodeVisitor(cx) {}
+  explicit FoldVisitor(JSContext* cx, FullParseHandler* handler)
+      : ParseNodeVisitor(cx), handler(handler) {}
 
   bool visitElemExpr(ParseNode*& pn) {
-    return Base::visitElemExpr(pn) && FoldElement(cx, &pn);
+    return Base::visitElemExpr(pn) && FoldElement(cx, handler, &pn);
   }
 
   bool visitTypeOfExpr(ParseNode*& pn) {
-    return Base::visitTypeOfExpr(pn) &&
-           FoldTypeOfExpr(cx, &pn->as<UnaryNode>());
+    return Base::visitTypeOfExpr(pn) && FoldTypeOfExpr(cx, handler, &pn);
   }
 
   bool visitDeleteExpr(ParseNode*& pn) {
-    return Base::visitDeleteExpr(pn) &&
-           FoldDeleteExpr(cx, &pn->as<UnaryNode>());
+    return Base::visitDeleteExpr(pn) && FoldDeleteExpr(cx, handler, &pn);
   }
 
   bool visitDeleteElemExpr(ParseNode*& pn) {
-    return Base::visitDeleteElemExpr(pn) &&
-           FoldDeleteElement(cx, &pn->as<UnaryNode>());
+    return Base::visitDeleteElemExpr(pn) && FoldDeleteElement(cx, handler, &pn);
   }
 
   bool visitNotExpr(ParseNode*& pn) {
-    return Base::visitNotExpr(pn) && FoldNot(cx, &pn->as<UnaryNode>());
+    return Base::visitNotExpr(pn) && FoldNot(cx, handler, &pn);
   }
 
   bool visitBitNotExpr(ParseNode*& pn) {
-    return Base::visitBitNotExpr(pn) &&
-           FoldUnaryArithmetic(cx, &pn->as<UnaryNode>());
+    return Base::visitBitNotExpr(pn) && FoldUnaryArithmetic(cx, handler, &pn);
   }
 
   bool visitPosExpr(ParseNode*& pn) {
-    return Base::visitPosExpr(pn) &&
-           FoldUnaryArithmetic(cx, &pn->as<UnaryNode>());
+    return Base::visitPosExpr(pn) && FoldUnaryArithmetic(cx, handler, &pn);
   }
 
   bool visitNegExpr(ParseNode*& pn) {
-    return Base::visitNegExpr(pn) &&
-           FoldUnaryArithmetic(cx, &pn->as<UnaryNode>());
+    return Base::visitNegExpr(pn) && FoldUnaryArithmetic(cx, handler, &pn);
   }
 
   bool visitPowExpr(ParseNode*& pn) {
-    return Base::visitPowExpr(pn) &&
-           FoldExponentiation(cx, &pn->as<ListNode>());
+    return Base::visitPowExpr(pn) && FoldExponentiation(cx, handler, &pn);
   }
 
   bool visitMulExpr(ParseNode*& pn) {
-    return Base::visitMulExpr(pn) &&
-           FoldBinaryArithmetic(cx, &pn->as<ListNode>());
+    return Base::visitMulExpr(pn) && FoldBinaryArithmetic(cx, handler, &pn);
   }
 
   bool visitDivExpr(ParseNode*& pn) {
-    return Base::visitDivExpr(pn) &&
-           FoldBinaryArithmetic(cx, &pn->as<ListNode>());
+    return Base::visitDivExpr(pn) && FoldBinaryArithmetic(cx, handler, &pn);
   }
 
   bool visitModExpr(ParseNode*& pn) {
-    return Base::visitModExpr(pn) &&
-           FoldBinaryArithmetic(cx, &pn->as<ListNode>());
+    return Base::visitModExpr(pn) && FoldBinaryArithmetic(cx, handler, &pn);
   }
 
   bool visitAddExpr(ParseNode*& pn) {
-    return Base::visitAddExpr(pn) && FoldAdd(cx, &pn);
+    return Base::visitAddExpr(pn) && FoldAdd(cx, handler, &pn);
   }
 
   bool visitSubExpr(ParseNode*& pn) {
-    return Base::visitSubExpr(pn) &&
-           FoldBinaryArithmetic(cx, &pn->as<ListNode>());
+    return Base::visitSubExpr(pn) && FoldBinaryArithmetic(cx, handler, &pn);
   }
 
   bool visitLshExpr(ParseNode*& pn) {
-    return Base::visitLshExpr(pn) &&
-           FoldBinaryArithmetic(cx, &pn->as<ListNode>());
+    return Base::visitLshExpr(pn) && FoldBinaryArithmetic(cx, handler, &pn);
   }
 
   bool visitRshExpr(ParseNode*& pn) {
-    return Base::visitRshExpr(pn) &&
-           FoldBinaryArithmetic(cx, &pn->as<ListNode>());
+    return Base::visitRshExpr(pn) && FoldBinaryArithmetic(cx, handler, &pn);
   }
 
   bool visitUrshExpr(ParseNode*& pn) {
-    return Base::visitUrshExpr(pn) &&
-           FoldBinaryArithmetic(cx, &pn->as<ListNode>());
+    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);
   }
 
   bool visitOrExpr(ParseNode*& pn) {
     return Base::visitOrExpr(pn) && FoldAndOr(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, &pn);
+    return FoldConditional(cx, handler, &pn);
   }
 
  private:
   bool internalVisitCall(BinaryNode* node) {
     MOZ_ASSERT(node->isKind(ParseNodeKind::CallExpr) ||
                node->isKind(ParseNodeKind::SuperCallExpr) ||
                node->isKind(ParseNodeKind::NewExpr) ||
                node->isKind(ParseNodeKind::TaggedTemplateExpr));
@@ -1388,51 +1419,51 @@ class FoldVisitor : public ParseNodeVisi
 
   bool visitTaggedTemplateExpr(ParseNode*& pn) {
     return internalVisitCall(&pn->as<BinaryNode>());
   }
 
   bool visitIfStmt(ParseNode*& pn) {
     // Don't call base-class visitIf because FoldIf processes pn's child nodes
     // specially to save stack space.
-    return FoldIf(cx, &pn);
+    return FoldIf(cx, handler, &pn);
   }
 
   bool visitForStmt(ParseNode*& pn) {
     if (!Base::visitForStmt(pn)) {
       return false;
     }
 
     ForNode& stmt = pn->as<ForNode>();
     if (stmt.left()->isKind(ParseNodeKind::ForHead)) {
       TernaryNode& head = stmt.left()->as<TernaryNode>();
       ParseNode** test = head.unsafeKid2Reference();
       if (*test) {
-        if (!SimplifyCondition(cx, test)) {
+        if (!SimplifyCondition(cx, handler, test)) {
           return false;
         }
         if ((*test)->isKind(ParseNodeKind::TrueExpr)) {
           *test = nullptr;
         }
       }
     }
 
     return true;
   }
 
   bool visitWhileStmt(ParseNode*& pn) {
     BinaryNode& node = pn->as<BinaryNode>();
     return Base::visitWhileStmt(pn) &&
-           SimplifyCondition(cx, node.unsafeLeftReference());
+           SimplifyCondition(cx, handler, node.unsafeLeftReference());
   }
 
   bool visitDoWhileStmt(ParseNode*& pn) {
     BinaryNode& node = pn->as<BinaryNode>();
     return Base::visitDoWhileStmt(pn) &&
-           SimplifyCondition(cx, node.unsafeRightReference());
+           SimplifyCondition(cx, handler, node.unsafeRightReference());
   }
 
   bool visitFunction(ParseNode*& pn) {
     FunctionNode& node = pn->as<FunctionNode>();
 
     // Don't constant-fold inside "use asm" code, as this could create a parse
     // tree that doesn't type-check as asm.js.
     if (node.funbox()->useAsmOrInsideUseAsm()) {
@@ -1481,26 +1512,20 @@ class FoldVisitor : public ParseNodeVisi
         }
       }
       list->unsetHasNonConstInitializer();
     }
     return true;
   }
 };
 
-bool Fold(JSContext* cx, ParseNode** pnp) {
-  FoldVisitor visitor(cx);
+bool Fold(JSContext* cx, FullParseHandler* handler, ParseNode** pnp) {
+  FoldVisitor visitor(cx, handler);
   return visitor.visit(*pnp);
 }
 
 bool frontend::FoldConstants(JSContext* cx, ParseNode** pnp,
-                             PerHandlerParser<FullParseHandler>* parser) {
-  // Don't constant-fold inside "use asm" code, as this could create a parse
-  // tree that doesn't type-check as asm.js.
-  if (parser->pc->useAsmOrInsideUseAsm()) {
-    return true;
-  }
-
+                             FullParseHandler* handler) {
   AutoTraceLog traceLog(TraceLoggerForCurrentThread(cx),
                         TraceLogger_BytecodeFoldConstants);
 
-  return Fold(cx, pnp);
+  return Fold(cx, handler, pnp);
 }
--- a/js/src/frontend/FoldConstants.h
+++ b/js/src/frontend/FoldConstants.h
@@ -26,21 +26,21 @@ class PerHandlerParser;
 // Usage:
 //    pn = parser->statement();
 //    if (!pn) {
 //        return false;
 //    }
 //    if (!FoldConstants(cx, &pn, parser)) {
 //        return false;
 //    }
-extern MOZ_MUST_USE bool FoldConstants(
-    JSContext* cx, ParseNode** pnp, PerHandlerParser<FullParseHandler>* parser);
+extern MOZ_MUST_USE bool FoldConstants(JSContext* cx, ParseNode** pnp,
+                                       FullParseHandler* parser);
 
-inline MOZ_MUST_USE bool FoldConstants(
-    JSContext* cx, typename SyntaxParseHandler::Node* pnp,
-    PerHandlerParser<SyntaxParseHandler>* parser) {
+inline MOZ_MUST_USE bool FoldConstants(JSContext* cx,
+                                       typename SyntaxParseHandler::Node* pnp,
+                                       SyntaxParseHandler* parser) {
   return true;
 }
 
 } /* namespace frontend */
 } /* namespace js */
 
 #endif /* frontend_FoldConstants_h */
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -782,16 +782,22 @@ class FullParseHandler {
   BinaryNodeType newObjectMethodOrPropertyDefinition(Node key, Node value,
                                                      AccessorType atype) {
     MOZ_ASSERT(isUsableAsObjectPropertyName(key));
 
     return newBinary(ParseNodeKind::Colon, key, value,
                      AccessorTypeToJSOp(atype));
   }
 
+  BinaryNodeType newShorthandPropertyDefinition(Node key, Node value) {
+    MOZ_ASSERT(isUsableAsObjectPropertyName(key));
+
+    return newBinary(ParseNodeKind::Shorthand, key, value, JSOP_INITPROP);
+  }
+
   void setFunctionFormalParametersAndBody(FunctionNodeType funNode,
                                           ListNodeType paramsBody) {
     MOZ_ASSERT_IF(paramsBody, paramsBody->isKind(ParseNodeKind::ParamsBody));
     funNode->setBody(paramsBody);
   }
   void setFunctionBox(FunctionNodeType funNode, FunctionBox* funbox) {
     funNode->setFunbox(funbox);
     funbox->functionNode = funNode;
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -73,17 +73,17 @@ ParseNode* ParseNode::appendOrCreateList
     //
     // (**) is right-associative; per spec |a ** b ** c| parses as
     // (** a (** b c)). But we treat this the same way, creating a list
     // node: (** a b c). All consumers must understand that this must be
     // processed with a right fold, whereas the list (+ a b c) must be
     // processed with a left fold because (+) is left-associative.
     //
     if (left->isKind(kind) &&
-        (kind == ParseNodeKind::PowExpr ? !left->pn_parens
+        (kind == ParseNodeKind::PowExpr ? !left->isInParens()
                                         : left->isBinaryOperation())) {
       ListNode* list = &left->as<ListNode>();
 
       list->append(right);
       list->pn_pos.end = right->pn_pos.end;
 
       return list;
     }
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -621,17 +621,17 @@ static inline bool IsConstructorKind(Fun
 
 static inline bool IsMethodDefinitionKind(FunctionSyntaxKind kind) {
   return IsConstructorKind(kind) || kind == FunctionSyntaxKind::Method ||
          kind == FunctionSyntaxKind::Getter ||
          kind == FunctionSyntaxKind::Setter;
 }
 
 class ParseNode {
-  ParseNodeKind pn_type; /* ParseNodeKind::PNK_* type */
+  const ParseNodeKind pn_type; /* ParseNodeKind::PNK_* type */
 
   // pn_op is not declared as the correct enum type due to difficulties with
   // MS bitfield layout rules and a GCC bug.  See
   // https://bugzilla.mozilla.org/show_bug.cgi?id=1383157#c4 for details.
   uint8_t pn_op;            /* see JSOp enum and jsopcode.tbl */
   bool pn_parens : 1;       /* this expr was enclosed in parens */
   bool pn_rhs_anon_fun : 1; /* this expr is anonymous function or class that
                              * is a direct RHS of ParseNodeKind::Assign or
@@ -667,20 +667,16 @@ class ParseNode {
   JSOp getOp() const { return JSOp(pn_op); }
   void setOp(JSOp op) { pn_op = op; }
   bool isOp(JSOp op) const { return getOp() == op; }
 
   ParseNodeKind getKind() const {
     MOZ_ASSERT(pn_type < ParseNodeKind::Limit);
     return pn_type;
   }
-  void setKind(ParseNodeKind kind) {
-    MOZ_ASSERT(kind < ParseNodeKind::Limit);
-    pn_type = kind;
-  }
   bool isKind(ParseNodeKind kind) const { return getKind() == kind; }
 
   ParseNodeArity getArity() const {
     return ParseNodeKindArity[size_t(getKind())];
   }
   bool isArity(ParseNodeArity a) const { return getArity() == a; }
 
   bool isBinaryOperation() const {
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -390,18 +390,22 @@ typename ParseHandler::ListNodeType Gene
     return null();
   }
   if (tt != TokenKind::Eof) {
     error(JSMSG_GARBAGE_AFTER_INPUT, "script", TokenKindToDesc(tt));
     return null();
   }
   if (foldConstants) {
     Node node = stmtList;
-    if (!FoldConstants(context, &node, this)) {
-      return null();
+    // Don't constant-fold inside "use asm" code, as this could create a parse
+    // tree that doesn't type-check as asm.js.
+    if (!pc->useAsmOrInsideUseAsm()) {
+      if (!FoldConstants(context, &node, &handler)) {
+        return null();
+      }
     }
     stmtList = handler.asList(node);
   }
 
   return stmtList;
 }
 
 /*
@@ -1367,18 +1371,22 @@ LexicalScopeNode* Parser<FullParseHandle
     }
     MOZ_ASSERT(!si.done(),
                "Eval must have found an enclosing function box scope that "
                "allows super.property");
   }
 #endif
 
   ParseNode* node = body;
-  if (!FoldConstants(context, &node, this)) {
-    return nullptr;
+  // Don't constant-fold inside "use asm" code, as this could create a parse
+  // tree that doesn't type-check as asm.js.
+  if (!pc->useAsmOrInsideUseAsm()) {
+    if (!FoldConstants(context, &node, &handler)) {
+      return null();
+    }
   }
   body = handler.asLexicalScope(node);
 
   if (!this->setSourceMapInfo()) {
     return nullptr;
   }
 
   // For eval scripts, since all bindings are automatically considered
@@ -1415,18 +1423,22 @@ ListNode* Parser<FullParseHandler, Unit>
     return nullptr;
   }
 
   if (!checkStatementsEOF()) {
     return nullptr;
   }
 
   ParseNode* node = body;
-  if (!FoldConstants(context, &node, this)) {
-    return null();
+  // Don't constant-fold inside "use asm" code, as this could create a parse
+  // tree that doesn't type-check as asm.js.
+  if (!pc->useAsmOrInsideUseAsm()) {
+    if (!FoldConstants(context, &node, &handler)) {
+      return null();
+    }
   }
   body = &node->as<ListNode>();
 
   if (!this->setSourceMapInfo()) {
     return nullptr;
   }
 
   // For global scripts, whether bindings are closed over or not doesn't
@@ -1503,18 +1515,22 @@ ModuleNode* Parser<FullParseHandler, Uni
       errorNoOffset(JSMSG_MISSING_EXPORT, str.get());
       return null();
     }
 
     p->value()->setClosedOver();
   }
 
   ParseNode* node = stmtList;
-  if (!FoldConstants(context, &node, this)) {
-    return null();
+  // Don't constant-fold inside "use asm" code, as this could create a parse
+  // tree that doesn't type-check as asm.js.
+  if (!pc->useAsmOrInsideUseAsm()) {
+    if (!FoldConstants(context, &node, &handler)) {
+      return null();
+    }
   }
   stmtList = &node->as<ListNode>();
 
   if (!this->setSourceMapInfo()) {
     return null();
   }
 
   if (!propagateFreeNamesAndMarkClosedOverBindings(modulepc.varScope())) {
@@ -1835,18 +1851,22 @@ FunctionNode* Parser<FullParseHandler, U
     return null();
   }
   if (tt != TokenKind::Eof) {
     error(JSMSG_GARBAGE_AFTER_INPUT, "function body", TokenKindToDesc(tt));
     return null();
   }
 
   ParseNode* node = funNode;
-  if (!FoldConstants(context, &node, this)) {
-    return null();
+  // Don't constant-fold inside "use asm" code, as this could create a parse
+  // tree that doesn't type-check as asm.js.
+  if (!pc->useAsmOrInsideUseAsm()) {
+    if (!FoldConstants(context, &node, &handler)) {
+      return null();
+    }
   }
   funNode = &node->as<FunctionNode>();
 
   if (!this->setSourceMapInfo()) {
     return null();
   }
 
   return funNode;
@@ -2981,18 +3001,22 @@ FunctionNode* Parser<FullParseHandler, U
 
   if (!functionFormalParametersAndBody(InAllowed, yieldHandling, &funNode,
                                        syntaxKind)) {
     MOZ_ASSERT(directives == newDirectives);
     return null();
   }
 
   ParseNode* node = funNode;
-  if (!FoldConstants(context, &node, this)) {
-    return null();
+  // Don't constant-fold inside "use asm" code, as this could create a parse
+  // tree that doesn't type-check as asm.js.
+  if (!pc->useAsmOrInsideUseAsm()) {
+    if (!FoldConstants(context, &node, &handler)) {
+      return null();
+    }
   }
   funNode = &node->as<FunctionNode>();
 
   return funNode;
 }
 
 template <class ParseHandler, typename Unit>
 bool GeneralParser<ParseHandler, Unit>::functionFormalParametersAndBody(