Bug 1428863 - Pass a PerHandlerParser<ParseHandler> to FoldConstants rather than a GeneralParser<ParseHandler, CharT>, eliminating the need to duplicate constant-folding for one/two-byte parsing. r=arai
authorJeff Walden <jwalden@mit.edu>
Thu, 21 Dec 2017 15:43:55 -0500
changeset 454186 d01ca5c5f3d06aef7c40b59335302de83bfcdb62
parent 454185 e1f7574c4a92738748c10baa49cdf36942cae489
child 454187 2a4be20ecde238347d030c522b2e24741ce8a3d4
push id1648
push usermtabara@mozilla.com
push dateThu, 01 Mar 2018 12:45:47 +0000
treeherdermozilla-release@cbb9688c2eeb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersarai
bugs1428863
milestone59.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 1428863 - Pass a PerHandlerParser<ParseHandler> to FoldConstants rather than a GeneralParser<ParseHandler, CharT>, eliminating the need to duplicate constant-folding for one/two-byte parsing. r=arai
js/src/frontend/FoldConstants.cpp
js/src/frontend/FoldConstants.h
js/src/frontend/FullParseHandler.h
js/src/frontend/Parser.h
js/src/frontend/SyntaxParseHandler.h
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -493,21 +493,20 @@ Boolish(ParseNode* pn)
       }
 
       default:
         return Unknown;
     }
 }
 
 static bool
-Fold(JSContext* cx, ParseNode** pnp, GeneralParser<FullParseHandler, char16_t>& parser);
+Fold(JSContext* cx, ParseNode** pnp, PerHandlerParser<FullParseHandler>& parser);
 
 static bool
-FoldCondition(JSContext* cx, ParseNode** nodePtr,
-              GeneralParser<FullParseHandler, char16_t>& parser)
+FoldCondition(JSContext* cx, ParseNode** nodePtr, PerHandlerParser<FullParseHandler>& parser)
 {
     // Conditions fold like any other expression...
     if (!Fold(cx, nodePtr, parser))
         return false;
 
     // ...but then they sometimes can be further folded to constants.
     ParseNode* node = *nodePtr;
     Truthiness t = Boolish(node);
@@ -527,17 +526,17 @@ FoldCondition(JSContext* cx, ParseNode**
         }
         node->setArity(PN_NULLARY);
     }
 
     return true;
 }
 
 static bool
-FoldTypeOfExpr(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser)
+FoldTypeOfExpr(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
 {
     MOZ_ASSERT(node->isKind(ParseNodeKind::TypeOfExpr));
     MOZ_ASSERT(node->isArity(PN_UNARY));
 
     ParseNode*& expr = node->pn_kid;
     if (!Fold(cx, &expr, parser))
         return false;
 
@@ -562,17 +561,17 @@ FoldTypeOfExpr(JSContext* cx, ParseNode*
         node->setOp(JSOP_NOP);
         node->pn_atom = result;
     }
 
     return true;
 }
 
 static bool
-FoldDeleteExpr(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser)
+FoldDeleteExpr(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
 {
     MOZ_ASSERT(node->isKind(ParseNodeKind::DeleteExpr));
     MOZ_ASSERT(node->isArity(PN_UNARY));
 
     ParseNode*& expr = node->pn_kid;
     if (!Fold(cx, &expr, parser))
         return false;
 
@@ -584,18 +583,17 @@ FoldDeleteExpr(JSContext* cx, ParseNode*
         node->setArity(PN_NULLARY);
         node->setOp(JSOP_TRUE);
     }
 
     return true;
 }
 
 static bool
-FoldDeleteElement(JSContext* cx, ParseNode* node,
-                  GeneralParser<FullParseHandler, char16_t>& parser)
+FoldDeleteElement(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
 {
     MOZ_ASSERT(node->isKind(ParseNodeKind::DeleteElem));
     MOZ_ASSERT(node->isArity(PN_UNARY));
     MOZ_ASSERT(node->pn_kid->isKind(ParseNodeKind::Elem));
 
     ParseNode*& expr = node->pn_kid;
     if (!Fold(cx, &expr, parser))
         return false;
@@ -609,18 +607,17 @@ FoldDeleteElement(JSContext* cx, ParseNo
     MOZ_ASSERT(expr->isKind(ParseNodeKind::Elem) || expr->isKind(ParseNodeKind::Dot));
     if (expr->isKind(ParseNodeKind::Dot))
         node->setKind(ParseNodeKind::DeleteProp);
 
     return true;
 }
 
 static bool
-FoldDeleteProperty(JSContext* cx, ParseNode* node,
-                   GeneralParser<FullParseHandler, char16_t>& parser)
+FoldDeleteProperty(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
 {
     MOZ_ASSERT(node->isKind(ParseNodeKind::DeleteProp));
     MOZ_ASSERT(node->isArity(PN_UNARY));
     MOZ_ASSERT(node->pn_kid->isKind(ParseNodeKind::Dot));
 
     ParseNode*& expr = node->pn_kid;
 #ifdef DEBUG
     ParseNodeKind oldKind = expr->getKind();
@@ -631,17 +628,17 @@ FoldDeleteProperty(JSContext* cx, ParseN
 
     MOZ_ASSERT(expr->isKind(oldKind),
                "kind should have remained invariant under folding");
 
     return true;
 }
 
 static bool
-FoldNot(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser)
+FoldNot(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
 {
     MOZ_ASSERT(node->isKind(ParseNodeKind::Not));
     MOZ_ASSERT(node->isArity(PN_UNARY));
 
     ParseNode*& expr = node->pn_kid;
     if (!FoldCondition(cx, &expr, parser))
         return false;
 
@@ -665,18 +662,17 @@ FoldNot(JSContext* cx, ParseNode* node, 
         node->setArity(PN_NULLARY);
         node->setOp(newval ? JSOP_TRUE : JSOP_FALSE);
     }
 
     return true;
 }
 
 static bool
-FoldUnaryArithmetic(JSContext* cx, ParseNode* node,
-                    GeneralParser<FullParseHandler, char16_t>& parser)
+FoldUnaryArithmetic(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
 {
     MOZ_ASSERT(node->isKind(ParseNodeKind::BitNot) ||
                node->isKind(ParseNodeKind::Pos) ||
                node->isKind(ParseNodeKind::Neg),
                "need a different method for this node kind");
     MOZ_ASSERT(node->isArity(PN_UNARY));
 
     ParseNode*& expr = node->pn_kid;
@@ -704,18 +700,17 @@ FoldUnaryArithmetic(JSContext* cx, Parse
         node->setArity(PN_NULLARY);
         node->pn_dval = d;
     }
 
     return true;
 }
 
 static bool
-FoldIncrementDecrement(JSContext* cx, ParseNode* node,
-                       GeneralParser<FullParseHandler, char16_t>& parser)
+FoldIncrementDecrement(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
 {
     MOZ_ASSERT(node->isKind(ParseNodeKind::PreIncrement) ||
                node->isKind(ParseNodeKind::PostIncrement) ||
                node->isKind(ParseNodeKind::PreDecrement) ||
                node->isKind(ParseNodeKind::PostDecrement));
     MOZ_ASSERT(node->isArity(PN_UNARY));
 
     ParseNode*& target = node->pn_kid;
@@ -725,17 +720,17 @@ FoldIncrementDecrement(JSContext* cx, Pa
         return false;
 
     MOZ_ASSERT(parser.isValidSimpleAssignmentTarget(target, PermitAssignmentToFunctionCalls));
 
     return true;
 }
 
 static bool
-FoldAndOr(JSContext* cx, ParseNode** nodePtr, GeneralParser<FullParseHandler, char16_t>& parser)
+FoldAndOr(JSContext* cx, ParseNode** nodePtr, PerHandlerParser<FullParseHandler>& parser)
 {
     ParseNode* node = *nodePtr;
 
     MOZ_ASSERT(node->isKind(ParseNodeKind::And) || node->isKind(ParseNodeKind::Or));
     MOZ_ASSERT(node->isArity(PN_LIST));
 
     bool isOrNode = node->isKind(ParseNodeKind::Or);
     ParseNode** elem = &node->pn_head;
@@ -756,17 +751,17 @@ FoldAndOr(JSContext* cx, ParseNode** nod
         // 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) {
             ParseNode* afterNext;
             for (ParseNode* next = (*elem)->pn_next; next; next = afterNext) {
                 afterNext = next->pn_next;
-                parser.handler.freeTree(next);
+                parser.freeTree(next);
                 --node->pn_count;
             }
 
             // Terminate the original and/or list at the known-truthiness
             // node.
             (*elem)->pn_next = nullptr;
             elem = &(*elem)->pn_next;
             break;
@@ -776,17 +771,17 @@ FoldAndOr(JSContext* cx, ParseNode** nod
 
         // 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;
-            parser.handler.freeTree(elt);
+            parser.freeTree(elt);
             --node->pn_count;
         } else {
             // Otherwise this node is the result of the overall expression,
             // so leave it alone.  And we're done.
             elem = &(*elem)->pn_next;
             break;
         }
     } while (*elem);
@@ -807,18 +802,17 @@ FoldAndOr(JSContext* cx, ParseNode** nod
         node->setArity(PN_NULLARY);
         parser.freeTree(node);
     }
 
     return true;
 }
 
 static bool
-FoldConditional(JSContext* cx, ParseNode** nodePtr,
-                GeneralParser<FullParseHandler, char16_t>& parser)
+FoldConditional(JSContext* cx, ParseNode** nodePtr, PerHandlerParser<FullParseHandler>& parser)
 {
     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;
@@ -877,17 +871,17 @@ FoldConditional(JSContext* cx, ParseNode
 
         parser.freeTree(discarded);
     } while (nextNode);
 
     return true;
 }
 
 static bool
-FoldIf(JSContext* cx, ParseNode** nodePtr, GeneralParser<FullParseHandler, char16_t>& parser)
+FoldIf(JSContext* cx, ParseNode** nodePtr, PerHandlerParser<FullParseHandler>& parser)
 {
     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;
@@ -978,17 +972,17 @@ FoldIf(JSContext* cx, ParseNode** nodePt
             parser.freeTree(node);
         }
     } while (nextNode);
 
     return true;
 }
 
 static bool
-FoldFunction(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser)
+FoldFunction(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
 {
     MOZ_ASSERT(node->isKind(ParseNodeKind::Function));
     MOZ_ASSERT(node->isArity(PN_CODE));
 
     // 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->pn_funbox->useAsmOrInsideUseAsm())
         return true;
@@ -1040,29 +1034,28 @@ ComputeBinary(ParseNodeKind kind, double
     MOZ_ASSERT(kind == ParseNodeKind::Lsh || kind == ParseNodeKind::Rsh);
 
     int32_t i = ToInt32(left);
     uint32_t j = ToUint32(right) & 31;
     return int32_t((kind == ParseNodeKind::Lsh) ? uint32_t(i) << j : i >> j);
 }
 
 static bool
-FoldModule(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser)
+FoldModule(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
 {
     MOZ_ASSERT(node->isKind(ParseNodeKind::Module));
     MOZ_ASSERT(node->isArity(PN_CODE));
 
     ParseNode*& moduleBody = node->pn_body;
     MOZ_ASSERT(moduleBody);
     return Fold(cx, &moduleBody, parser);
 }
 
 static bool
-FoldBinaryArithmetic(JSContext* cx, ParseNode* node,
-                     GeneralParser<FullParseHandler, char16_t>& parser)
+FoldBinaryArithmetic(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
 {
     MOZ_ASSERT(node->isKind(ParseNodeKind::Sub) ||
                node->isKind(ParseNodeKind::Star) ||
                node->isKind(ParseNodeKind::Lsh) ||
                node->isKind(ParseNodeKind::Rsh) ||
                node->isKind(ParseNodeKind::Ursh) ||
                node->isKind(ParseNodeKind::Div) ||
                node->isKind(ParseNodeKind::Mod));
@@ -1123,18 +1116,17 @@ FoldBinaryArithmetic(JSContext* cx, Pars
             parser.freeTree(elem);
         }
     }
 
     return true;
 }
 
 static bool
-FoldExponentiation(JSContext* cx, ParseNode* node,
-                   GeneralParser<FullParseHandler, char16_t>& parser)
+FoldExponentiation(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
 {
     MOZ_ASSERT(node->isKind(ParseNodeKind::Pow));
     MOZ_ASSERT(node->isArity(PN_LIST));
     MOZ_ASSERT(node->pn_count >= 2);
 
     // Fold each operand, ideally into a number.
     ParseNode** listp = &node->pn_head;
     for (; *listp; listp = &(*listp)->pn_next) {
@@ -1167,17 +1159,17 @@ FoldExponentiation(JSContext* cx, ParseN
     node->setKind(ParseNodeKind::Number);
     node->setArity(PN_NULLARY);
     node->setOp(JSOP_DOUBLE);
     node->pn_dval = ecmaPow(d1, d2);
     return true;
 }
 
 static bool
-FoldList(JSContext* cx, ParseNode* list, GeneralParser<FullParseHandler, char16_t>& parser)
+FoldList(JSContext* cx, ParseNode* list, PerHandlerParser<FullParseHandler>& parser)
 {
     MOZ_ASSERT(list->isArity(PN_LIST));
 
     ParseNode** elem = &list->pn_head;
     for (; *elem; elem = &(*elem)->pn_next) {
         if (!Fold(cx, elem, parser))
             return false;
     }
@@ -1186,31 +1178,31 @@ FoldList(JSContext* cx, ParseNode* list,
     list->pn_tail = elem;
 
     list->checkListConsistency();
 
     return true;
 }
 
 static bool
-FoldReturn(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser)
+FoldReturn(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
 {
     MOZ_ASSERT(node->isKind(ParseNodeKind::Return));
     MOZ_ASSERT(node->isArity(PN_UNARY));
 
     if (ParseNode*& expr = node->pn_kid) {
         if (!Fold(cx, &expr, parser))
             return false;
     }
 
     return true;
 }
 
 static bool
-FoldTry(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser)
+FoldTry(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
 {
     MOZ_ASSERT(node->isKind(ParseNodeKind::Try));
     MOZ_ASSERT(node->isArity(PN_TERNARY));
 
     ParseNode*& statements = node->pn_kid1;
     if (!Fold(cx, &statements, parser))
         return false;
 
@@ -1223,17 +1215,17 @@ FoldTry(JSContext* cx, ParseNode* node, 
         if (!Fold(cx, &finally, parser))
             return false;
     }
 
     return true;
 }
 
 static bool
-FoldCatch(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser)
+FoldCatch(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
 {
     MOZ_ASSERT(node->isKind(ParseNodeKind::Catch));
     MOZ_ASSERT(node->isArity(PN_BINARY));
 
     if (ParseNode*& declPattern = node->pn_left) {
         if (!Fold(cx, &declPattern, parser))
             return false;
     }
@@ -1242,17 +1234,17 @@ FoldCatch(JSContext* cx, ParseNode* node
         if (!Fold(cx, &statements, parser))
             return false;
     }
 
     return true;
 }
 
 static bool
-FoldClass(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser)
+FoldClass(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
 {
     MOZ_ASSERT(node->isKind(ParseNodeKind::Class));
     MOZ_ASSERT(node->isArity(PN_TERNARY));
 
     if (ParseNode*& classNames = node->pn_kid1) {
         if (!Fold(cx, &classNames, parser))
             return false;
     }
@@ -1262,17 +1254,17 @@ FoldClass(JSContext* cx, ParseNode* node
             return false;
     }
 
     ParseNode*& body = node->pn_kid3;
     return Fold(cx, &body, parser);
 }
 
 static bool
-FoldElement(JSContext* cx, ParseNode** nodePtr, GeneralParser<FullParseHandler, char16_t>& parser)
+FoldElement(JSContext* cx, ParseNode** nodePtr, PerHandlerParser<FullParseHandler>& parser)
 {
     ParseNode* node = *nodePtr;
 
     MOZ_ASSERT(node->isKind(ParseNodeKind::Elem));
     MOZ_ASSERT(node->isArity(PN_BINARY));
 
     ParseNode*& expr = node->pn_left;
     if (!Fold(cx, &expr, parser))
@@ -1310,17 +1302,17 @@ FoldElement(JSContext* cx, ParseNode** n
     }
 
     // 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.
-    ParseNode* dottedAccess = parser.handler.newPropertyAccess(expr, name, node->pn_pos.end);
+    ParseNode* dottedAccess = parser.newPropertyAccess(expr, name, node->pn_pos.end);
     if (!dottedAccess)
         return false;
     dottedAccess->setInParens(node->isInParens());
     ReplaceNode(nodePtr, dottedAccess);
 
     // If we've replaced |expr["prop"]| with |expr.prop|, we can now free the
     // |"prop"| and |expr["prop"]| nodes -- but not the |expr| node that we're
     // now using as a sub-node of |dottedAccess|.  Munge |expr["prop"]| into a
@@ -1330,17 +1322,17 @@ FoldElement(JSContext* cx, ParseNode** n
     node->setArity(PN_UNARY);
     node->pn_kid = key;
     parser.freeTree(node);
 
     return true;
 }
 
 static bool
-FoldAdd(JSContext* cx, ParseNode** nodePtr, GeneralParser<FullParseHandler, char16_t>& parser)
+FoldAdd(JSContext* cx, ParseNode** nodePtr, PerHandlerParser<FullParseHandler>& parser)
 {
     ParseNode* node = *nodePtr;
 
     MOZ_ASSERT(node->isKind(ParseNodeKind::Add));
     MOZ_ASSERT(node->isArity(PN_LIST));
     MOZ_ASSERT(node->pn_count >= 2);
 
     // Generically fold all operands first.
@@ -1480,17 +1472,17 @@ FoldAdd(JSContext* cx, ParseNode** nodeP
         node->setOp(JSOP_TRUE);
         parser.freeTree(node);
     }
 
     return true;
 }
 
 static bool
-FoldCall(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser)
+FoldCall(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
 {
     MOZ_ASSERT(node->isKind(ParseNodeKind::Call) ||
                node->isKind(ParseNodeKind::SuperCall) ||
                node->isKind(ParseNodeKind::TaggedTemplate));
     MOZ_ASSERT(node->isArity(PN_LIST));
 
     // Don't fold a parenthesized callable component in an invocation, as this
     // might cause a different |this| value to be used, changing semantics:
@@ -1515,29 +1507,29 @@ FoldCall(JSContext* cx, ParseNode* node,
     // If the last node in the list was replaced, pn_tail points into the wrong node.
     node->pn_tail = listp;
 
     node->checkListConsistency();
     return true;
 }
 
 static bool
-FoldForInOrOf(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser)
+FoldForInOrOf(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
 {
     MOZ_ASSERT(node->isKind(ParseNodeKind::ForIn) ||
                node->isKind(ParseNodeKind::ForOf));
     MOZ_ASSERT(node->isArity(PN_TERNARY));
     MOZ_ASSERT(!node->pn_kid2);
 
     return Fold(cx, &node->pn_kid1, parser) &&
            Fold(cx, &node->pn_kid3, parser);
 }
 
 static bool
-FoldForHead(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser)
+FoldForHead(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
 {
     MOZ_ASSERT(node->isKind(ParseNodeKind::ForHead));
     MOZ_ASSERT(node->isArity(PN_TERNARY));
 
     if (ParseNode*& init = node->pn_kid1) {
         if (!Fold(cx, &init, parser))
             return false;
     }
@@ -1556,47 +1548,46 @@ FoldForHead(JSContext* cx, ParseNode* no
         if (!Fold(cx, &update, parser))
             return false;
     }
 
     return true;
 }
 
 static bool
-FoldDottedProperty(JSContext* cx, ParseNode* node,
-                   GeneralParser<FullParseHandler, char16_t>& parser)
+FoldDottedProperty(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
 {
     MOZ_ASSERT(node->isKind(ParseNodeKind::Dot));
     MOZ_ASSERT(node->isArity(PN_NAME));
 
     // Iterate through a long chain of dotted property accesses to find the
     // most-nested non-dotted property node, then fold that.
     ParseNode** nested = &node->pn_expr;
     while ((*nested)->isKind(ParseNodeKind::Dot)) {
         MOZ_ASSERT((*nested)->isArity(PN_NAME));
         nested = &(*nested)->pn_expr;
     }
 
     return Fold(cx, nested, parser);
 }
 
 static bool
-FoldName(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser)
+FoldName(JSContext* cx, ParseNode* node, PerHandlerParser<FullParseHandler>& parser)
 {
     MOZ_ASSERT(node->isKind(ParseNodeKind::Name));
     MOZ_ASSERT(node->isArity(PN_NAME));
 
     if (!node->pn_expr)
         return true;
 
     return Fold(cx, &node->pn_expr, parser);
 }
 
 bool
-Fold(JSContext* cx, ParseNode** pnp, GeneralParser<FullParseHandler, char16_t>& parser)
+Fold(JSContext* cx, ParseNode** pnp, PerHandlerParser<FullParseHandler>& parser)
 {
     if (!CheckRecursionLimit(cx))
         return false;
 
     ParseNode* pn = *pnp;
 
     switch (pn->getKind()) {
       case ParseNodeKind::Nop:
@@ -1874,25 +1865,19 @@ Fold(JSContext* cx, ParseNode** pnp, Gen
       case ParseNodeKind::Limit: // invalid sentinel value
         MOZ_CRASH("invalid node kind");
     }
 
     MOZ_CRASH("shouldn't reach here");
     return false;
 }
 
-template<typename CharT>
 bool
-frontend::FoldConstants(JSContext* cx, ParseNode** pnp,
-                        GeneralParser<FullParseHandler, CharT>* parser)
+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;
 
     AutoTraceLog traceLog(TraceLoggerForCurrentThread(cx), TraceLogger_BytecodeFoldConstants);
     return Fold(cx, pnp, *parser);
 }
-
-template bool
-frontend::FoldConstants(JSContext* cx, ParseNode** pnp,
-                        GeneralParser<FullParseHandler, char16_t>* parser);
--- a/js/src/frontend/FoldConstants.h
+++ b/js/src/frontend/FoldConstants.h
@@ -8,39 +8,37 @@
 #define frontend_FoldConstants_h
 
 #include "frontend/SyntaxParseHandler.h"
 
 namespace js {
 namespace frontend {
 
 class FullParseHandler;
-template <class ParseHandler, typename CharT> class GeneralParser;
+template <class ParseHandler> class PerHandlerParser;
 
 // Perform constant folding on the given AST. For example, the program
 // `print(2 + 2)` would become `print(4)`.
 //
 // pnp is the address of a pointer variable that points to the root node of the
 // AST. On success, *pnp points to the root node of the new tree, which may be
 // the same node (unchanged or modified in place) or a new node.
 //
 // Usage:
 //    pn = parser->statement();
 //    if (!pn)
 //        return false;
 //    if (!FoldConstants(cx, &pn, parser))
 //        return false;
-template<typename CharT>
-MOZ_MUST_USE bool
-FoldConstants(JSContext* cx, ParseNode** pnp, GeneralParser<FullParseHandler, CharT>* parser);
+extern MOZ_MUST_USE bool
+FoldConstants(JSContext* cx, ParseNode** pnp, PerHandlerParser<FullParseHandler>* parser);
 
-template<typename CharT>
 inline MOZ_MUST_USE bool
 FoldConstants(JSContext* cx, typename SyntaxParseHandler::Node* pnp,
-              GeneralParser<SyntaxParseHandler, CharT>* parser)
+              PerHandlerParser<SyntaxParseHandler>* parser)
 {
     return true;
 }
 
 } /* namespace frontend */
 } /* namespace js */
 
 #endif /* frontend_FoldConstants_h */
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -635,18 +635,18 @@ class FullParseHandler
         TokenPos pos(begin, (finallyBlock ? finallyBlock : catchScope)->pn_pos.end);
         return new_<TernaryNode>(ParseNodeKind::Try, body, catchScope, finallyBlock, pos);
     }
 
     ParseNode* newDebuggerStatement(const TokenPos& pos) {
         return new_<DebuggerStatement>(pos);
     }
 
-    ParseNode* newPropertyAccess(ParseNode* pn, PropertyName* name, uint32_t end) {
-        return new_<PropertyAccess>(pn, name, pn->pn_pos.begin, end);
+    ParseNode* newPropertyAccess(ParseNode* expr, PropertyName* key, uint32_t end) {
+        return new_<PropertyAccess>(expr, key, expr->pn_pos.begin, end);
     }
 
     ParseNode* newPropertyByValue(ParseNode* lhs, ParseNode* index, uint32_t end) {
         return new_<PropertyByValue>(lhs, index, lhs->pn_pos.begin, end);
     }
 
     bool setupCatchScope(ParseNode* lexicalScope, ParseNode* catchName, ParseNode* catchBody) {
         ParseNode* catchpn;
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -538,19 +538,26 @@ class PerHandlerParser
 
     // If ParseHandler is SyntaxParseHandler:
     //   Clear whether the last syntax parse was aborted.
     // If ParseHandler is FullParseHandler:
     //   Do nothing.
     inline void clearAbortedSyntaxParse();
 
   public:
+    void prepareNodeForMutation(Node node) { handler.prepareNodeForMutation(node); }
+    void freeTree(Node node) { handler.freeTree(node); }
+
     bool isValidSimpleAssignmentTarget(Node node,
                                        FunctionCallBehavior behavior = ForbidAssignmentToFunctionCalls);
 
+    Node newPropertyAccess(Node expr, PropertyName* key, uint32_t end) {
+        return handler.newPropertyAccess(expr, key, end);
+    }
+
     FunctionBox* newFunctionBox(Node fn, JSFunction* fun, uint32_t toStringStart,
                                 Directives directives, GeneratorKind generatorKind,
                                 FunctionAsyncKind asyncKind);
 };
 
 #define ABORTED_SYNTAX_PARSE_SENTINEL reinterpret_cast<void*>(0x1)
 
 template<>
@@ -858,19 +865,16 @@ class GeneralParser
     void setSyntaxParser(SyntaxParser* syntaxParser) {
         internalSyntaxParser_ = syntaxParser;
     }
 
   public:
     using TokenStream = TokenStreamSpecific<CharT, ParserAnyCharsAccess<GeneralParser>>;
     TokenStream tokenStream;
 
-    void prepareNodeForMutation(Node node) { handler.prepareNodeForMutation(node); }
-    void freeTree(Node node) { handler.freeTree(node); }
-
   public:
     GeneralParser(JSContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options,
                   const CharT* chars, size_t length, bool foldConstants,
                   UsedNameTracker& usedNames, SyntaxParser* syntaxParser,
                   LazyScript* lazyOuterFunction);
 
     inline void setAwaitHandling(AwaitHandling awaitHandling);
 
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -326,18 +326,18 @@ class SyntaxParseHandler
     }
 
     Node newThrowStatement(Node expr, const TokenPos& pos) { return NodeThrow; }
     Node newTryStatement(uint32_t begin, Node body, Node catchScope, Node finallyBlock) {
         return NodeGeneric;
     }
     Node newDebuggerStatement(const TokenPos& pos) { return NodeGeneric; }
 
-    Node newPropertyAccess(Node pn, PropertyName* name, uint32_t end) {
-        lastAtom = name;
+    Node newPropertyAccess(Node expr, PropertyName* key, uint32_t end) {
+        lastAtom = key;
         return NodeDottedProperty;
     }
 
     Node newPropertyByValue(Node pn, Node kid, uint32_t end) { return NodeElement; }
 
     MOZ_MUST_USE bool setupCatchScope(Node letBlock, Node catchName, Node catchBody) {
         return true;
     }