Bug 1424420 - Rename Parser to GeneralParser, and move all partially-specialized (by ParseHandler) functions in GeneralParser into a new Parser class inheriting from GeneralParser. This permits these functions to be specialized without having two identical definitions when CharT isn't solely char16_t. r=arai
authorJeff Walden <jwalden@mit.edu>
Fri, 08 Dec 2017 04:19:23 -0600
changeset 448016 9f31a702d5dea600cca630b3a5bdd755a27b2c3d
parent 448015 0559a4899c440b7c9a6666715cd3a1836fc4128f
child 448017 49a25966758216fc21e69948f26b0e7458f35782
push id8527
push userCallek@gmail.com
push dateThu, 11 Jan 2018 21:05:50 +0000
treeherdermozilla-beta@95342d212a7a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersarai
bugs1424420
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 1424420 - Rename Parser to GeneralParser, and move all partially-specialized (by ParseHandler) functions in GeneralParser into a new Parser class inheriting from GeneralParser. This permits these functions to be specialized without having two identical definitions when CharT isn't solely char16_t. r=arai
js/src/frontend/FoldConstants.cpp
js/src/frontend/FoldConstants.h
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/frontend/TokenStream.cpp
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -492,20 +492,21 @@ Boolish(ParseNode* pn)
       }
 
       default:
         return Unknown;
     }
 }
 
 static bool
-Fold(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler, char16_t>& parser);
+Fold(JSContext* cx, ParseNode** pnp, GeneralParser<FullParseHandler, char16_t>& parser);
 
 static bool
-FoldCondition(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser)
+FoldCondition(JSContext* cx, ParseNode** nodePtr,
+              GeneralParser<FullParseHandler, char16_t>& 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);
@@ -525,17 +526,17 @@ FoldCondition(JSContext* cx, ParseNode**
         }
         node->setArity(PN_NULLARY);
     }
 
     return true;
 }
 
 static bool
-FoldTypeOfExpr(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
+FoldTypeOfExpr(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_TYPEOFEXPR));
     MOZ_ASSERT(node->isArity(PN_UNARY));
 
     ParseNode*& expr = node->pn_kid;
     if (!Fold(cx, &expr, parser))
         return false;
 
@@ -560,17 +561,17 @@ FoldTypeOfExpr(JSContext* cx, ParseNode*
         node->setOp(JSOP_NOP);
         node->pn_atom = result;
     }
 
     return true;
 }
 
 static bool
-FoldDeleteExpr(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
+FoldDeleteExpr(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_DELETEEXPR));
     MOZ_ASSERT(node->isArity(PN_UNARY));
 
     ParseNode*& expr = node->pn_kid;
     if (!Fold(cx, &expr, parser))
         return false;
 
@@ -582,17 +583,18 @@ FoldDeleteExpr(JSContext* cx, ParseNode*
         node->setArity(PN_NULLARY);
         node->setOp(JSOP_TRUE);
     }
 
     return true;
 }
 
 static bool
-FoldDeleteElement(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
+FoldDeleteElement(JSContext* cx, ParseNode* node,
+                  GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_DELETEELEM));
     MOZ_ASSERT(node->isArity(PN_UNARY));
     MOZ_ASSERT(node->pn_kid->isKind(PNK_ELEM));
 
     ParseNode*& expr = node->pn_kid;
     if (!Fold(cx, &expr, parser))
         return false;
@@ -606,17 +608,18 @@ FoldDeleteElement(JSContext* cx, ParseNo
     MOZ_ASSERT(expr->isKind(PNK_ELEM) || expr->isKind(PNK_DOT));
     if (expr->isKind(PNK_DOT))
         node->setKind(PNK_DELETEPROP);
 
     return true;
 }
 
 static bool
-FoldDeleteProperty(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
+FoldDeleteProperty(JSContext* cx, ParseNode* node,
+                   GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_DELETEPROP));
     MOZ_ASSERT(node->isArity(PN_UNARY));
     MOZ_ASSERT(node->pn_kid->isKind(PNK_DOT));
 
     ParseNode*& expr = node->pn_kid;
 #ifdef DEBUG
     ParseNodeKind oldKind = expr->getKind();
@@ -627,17 +630,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, Parser<FullParseHandler, char16_t>& parser)
+FoldNot(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_NOT));
     MOZ_ASSERT(node->isArity(PN_UNARY));
 
     ParseNode*& expr = node->pn_kid;
     if (!FoldCondition(cx, &expr, parser))
         return false;
 
@@ -661,17 +664,18 @@ 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, Parser<FullParseHandler, char16_t>& parser)
+FoldUnaryArithmetic(JSContext* cx, ParseNode* node,
+                    GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_BITNOT) || node->isKind(PNK_POS) || node->isKind(PNK_NEG),
                "need a different method for this node kind");
     MOZ_ASSERT(node->isArity(PN_UNARY));
 
     ParseNode*& expr = node->pn_kid;
     if (!Fold(cx, &expr, parser))
         return false;
@@ -694,37 +698,38 @@ FoldUnaryArithmetic(JSContext* cx, Parse
         node->setArity(PN_NULLARY);
         node->pn_dval = d;
     }
 
     return true;
 }
 
 static bool
-FoldIncrementDecrement(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
+FoldIncrementDecrement(JSContext* cx, ParseNode* node,
+                       GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_PREINCREMENT) ||
                node->isKind(PNK_POSTINCREMENT) ||
                node->isKind(PNK_PREDECREMENT) ||
                node->isKind(PNK_POSTDECREMENT));
     MOZ_ASSERT(node->isArity(PN_UNARY));
 
     ParseNode*& target = node->pn_kid;
-    MOZ_ASSERT(parser.isValidSimpleAssignmentTarget(target, Parser<FullParseHandler, char16_t>::PermitAssignmentToFunctionCalls));
+    MOZ_ASSERT(parser.isValidSimpleAssignmentTarget(target, PermitAssignmentToFunctionCalls));
 
     if (!Fold(cx, &target, parser))
         return false;
 
-    MOZ_ASSERT(parser.isValidSimpleAssignmentTarget(target, Parser<FullParseHandler, char16_t>::PermitAssignmentToFunctionCalls));
+    MOZ_ASSERT(parser.isValidSimpleAssignmentTarget(target, PermitAssignmentToFunctionCalls));
 
     return true;
 }
 
 static bool
-FoldAndOr(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser)
+FoldAndOr(JSContext* cx, ParseNode** nodePtr, GeneralParser<FullParseHandler, char16_t>& parser)
 {
     ParseNode* node = *nodePtr;
 
     MOZ_ASSERT(node->isKind(PNK_AND) || node->isKind(PNK_OR));
     MOZ_ASSERT(node->isArity(PN_LIST));
 
     bool isOrNode = node->isKind(PNK_OR);
     ParseNode** elem = &node->pn_head;
@@ -796,17 +801,18 @@ FoldAndOr(JSContext* cx, ParseNode** nod
         node->setArity(PN_NULLARY);
         parser.freeTree(node);
     }
 
     return true;
 }
 
 static bool
-FoldConditional(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser)
+FoldConditional(JSContext* cx, ParseNode** nodePtr,
+                GeneralParser<FullParseHandler, char16_t>& 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;
@@ -865,17 +871,17 @@ FoldConditional(JSContext* cx, ParseNode
 
         parser.freeTree(discarded);
     } while (nextNode);
 
     return true;
 }
 
 static bool
-FoldIf(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser)
+FoldIf(JSContext* cx, ParseNode** nodePtr, GeneralParser<FullParseHandler, char16_t>& 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;
@@ -966,17 +972,17 @@ FoldIf(JSContext* cx, ParseNode** nodePt
             parser.freeTree(node);
         }
     } while (nextNode);
 
     return true;
 }
 
 static bool
-FoldFunction(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
+FoldFunction(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_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;
@@ -1028,28 +1034,29 @@ ComputeBinary(ParseNodeKind kind, double
     MOZ_ASSERT(kind == PNK_LSH || kind == PNK_RSH);
 
     int32_t i = ToInt32(left);
     uint32_t j = ToUint32(right) & 31;
     return int32_t((kind == PNK_LSH) ? uint32_t(i) << j : i >> j);
 }
 
 static bool
-FoldModule(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
+FoldModule(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_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, Parser<FullParseHandler, char16_t>& parser)
+FoldBinaryArithmetic(JSContext* cx, ParseNode* node,
+                     GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_SUB) ||
                node->isKind(PNK_STAR) ||
                node->isKind(PNK_LSH) ||
                node->isKind(PNK_RSH) ||
                node->isKind(PNK_URSH) ||
                node->isKind(PNK_DIV) ||
                node->isKind(PNK_MOD));
@@ -1110,17 +1117,18 @@ FoldBinaryArithmetic(JSContext* cx, Pars
             parser.freeTree(elem);
         }
     }
 
     return true;
 }
 
 static bool
-FoldExponentiation(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
+FoldExponentiation(JSContext* cx, ParseNode* node,
+                   GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_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) {
@@ -1153,17 +1161,17 @@ FoldExponentiation(JSContext* cx, ParseN
     node->setKind(PNK_NUMBER);
     node->setArity(PN_NULLARY);
     node->setOp(JSOP_DOUBLE);
     node->pn_dval = ecmaPow(d1, d2);
     return true;
 }
 
 static bool
-FoldList(JSContext* cx, ParseNode* list, Parser<FullParseHandler, char16_t>& parser)
+FoldList(JSContext* cx, ParseNode* list, GeneralParser<FullParseHandler, char16_t>& 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;
     }
@@ -1172,31 +1180,31 @@ FoldList(JSContext* cx, ParseNode* list,
     list->pn_tail = elem;
 
     list->checkListConsistency();
 
     return true;
 }
 
 static bool
-FoldReturn(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
+FoldReturn(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_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, Parser<FullParseHandler, char16_t>& parser)
+FoldTry(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_TRY));
     MOZ_ASSERT(node->isArity(PN_TERNARY));
 
     ParseNode*& statements = node->pn_kid1;
     if (!Fold(cx, &statements, parser))
         return false;
 
@@ -1209,17 +1217,17 @@ FoldTry(JSContext* cx, ParseNode* node, 
         if (!Fold(cx, &finally, parser))
             return false;
     }
 
     return true;
 }
 
 static bool
-FoldCatch(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
+FoldCatch(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_CATCH));
     MOZ_ASSERT(node->isArity(PN_BINARY));
 
     if (ParseNode*& declPattern = node->pn_left) {
         if (!Fold(cx, &declPattern, parser))
             return false;
     }
@@ -1228,17 +1236,17 @@ FoldCatch(JSContext* cx, ParseNode* node
         if (!Fold(cx, &statements, parser))
             return false;
     }
 
     return true;
 }
 
 static bool
-FoldClass(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
+FoldClass(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_CLASS));
     MOZ_ASSERT(node->isArity(PN_TERNARY));
 
     if (ParseNode*& classNames = node->pn_kid1) {
         if (!Fold(cx, &classNames, parser))
             return false;
     }
@@ -1248,17 +1256,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, Parser<FullParseHandler, char16_t>& parser)
+FoldElement(JSContext* cx, ParseNode** nodePtr, GeneralParser<FullParseHandler, char16_t>& parser)
 {
     ParseNode* node = *nodePtr;
 
     MOZ_ASSERT(node->isKind(PNK_ELEM));
     MOZ_ASSERT(node->isArity(PN_BINARY));
 
     ParseNode*& expr = node->pn_left;
     if (!Fold(cx, &expr, parser))
@@ -1316,17 +1324,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, Parser<FullParseHandler, char16_t>& parser)
+FoldAdd(JSContext* cx, ParseNode** nodePtr, GeneralParser<FullParseHandler, char16_t>& parser)
 {
     ParseNode* node = *nodePtr;
 
     MOZ_ASSERT(node->isKind(PNK_ADD));
     MOZ_ASSERT(node->isArity(PN_LIST));
     MOZ_ASSERT(node->pn_count >= 2);
 
     // Generically fold all operands first.
@@ -1466,17 +1474,17 @@ FoldAdd(JSContext* cx, ParseNode** nodeP
         node->setOp(JSOP_TRUE);
         parser.freeTree(node);
     }
 
     return true;
 }
 
 static bool
-FoldCall(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
+FoldCall(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_CALL) || node->isKind(PNK_SUPERCALL) ||
                node->isKind(PNK_TAGGED_TEMPLATE));
     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:
     //
@@ -1500,28 +1508,28 @@ 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, Parser<FullParseHandler, char16_t>& parser)
+FoldForInOrOf(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_FORIN) || node->isKind(PNK_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, Parser<FullParseHandler, char16_t>& parser)
+FoldForHead(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_FORHEAD));
     MOZ_ASSERT(node->isArity(PN_TERNARY));
 
     if (ParseNode*& init = node->pn_kid1) {
         if (!Fold(cx, &init, parser))
             return false;
     }
@@ -1540,46 +1548,47 @@ FoldForHead(JSContext* cx, ParseNode* no
         if (!Fold(cx, &update, parser))
             return false;
     }
 
     return true;
 }
 
 static bool
-FoldDottedProperty(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
+FoldDottedProperty(JSContext* cx, ParseNode* node,
+                   GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_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(PNK_DOT)) {
         MOZ_ASSERT((*nested)->isArity(PN_NAME));
         nested = &(*nested)->pn_expr;
     }
 
     return Fold(cx, nested, parser);
 }
 
 static bool
-FoldName(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
+FoldName(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_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, Parser<FullParseHandler, char16_t>& parser)
+Fold(JSContext* cx, ParseNode** pnp, GeneralParser<FullParseHandler, char16_t>& parser)
 {
     if (!CheckRecursionLimit(cx))
         return false;
 
     ParseNode* pn = *pnp;
 
     switch (pn->getKind()) {
       case PNK_NOP:
@@ -1859,22 +1868,23 @@ Fold(JSContext* cx, ParseNode** pnp, Par
     }
 
     MOZ_CRASH("shouldn't reach here");
     return false;
 }
 
 template<typename CharT>
 bool
-frontend::FoldConstants(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler, CharT>* parser)
+frontend::FoldConstants(JSContext* cx, ParseNode** pnp,
+                        GeneralParser<FullParseHandler, CharT>* 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,
-                        Parser<FullParseHandler, char16_t>* parser);
+                        GeneralParser<FullParseHandler, char16_t>* parser);
--- a/js/src/frontend/FoldConstants.h
+++ b/js/src/frontend/FoldConstants.h
@@ -8,39 +8,39 @@
 #define frontend_FoldConstants_h
 
 #include "frontend/SyntaxParseHandler.h"
 
 namespace js {
 namespace frontend {
 
 class FullParseHandler;
-template <class ParseHandler, typename CharT> class Parser;
+template <class ParseHandler, typename CharT> class GeneralParser;
 
 // 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, Parser<FullParseHandler, CharT>* parser);
+FoldConstants(JSContext* cx, ParseNode** pnp, GeneralParser<FullParseHandler, CharT>* parser);
 
 template<typename CharT>
 inline MOZ_MUST_USE bool
 FoldConstants(JSContext* cx, typename SyntaxParseHandler::Node* pnp,
-              Parser<SyntaxParseHandler, CharT>* parser)
+              GeneralParser<SyntaxParseHandler, CharT>* parser)
 {
     return true;
 }
 
 } /* namespace frontend */
 } /* namespace js */
 
 #endif /* frontend_FoldConstants_h */
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -16,16 +16,17 @@
  *
  * This parser attempts no error recovery.
  */
 
 #include "frontend/Parser.h"
 
 #include "mozilla/Range.h"
 #include "mozilla/Sprintf.h"
+#include "mozilla/TypeTraits.h"
 
 #include "jsapi.h"
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jsfun.h"
 #include "jsopcode.h"
 #include "jsscript.h"
 #include "jstypes.h"
@@ -584,96 +585,123 @@ FunctionBox::initWithEnclosingScope(Scop
         computeAllowSyntax(enclosingScope);
         computeThisBinding(enclosingScope);
     }
 
     computeInWith(enclosingScope);
 }
 
 template <class ParseHandler, typename CharT>
+inline typename GeneralParser<ParseHandler, CharT>::FinalParser*
+GeneralParser<ParseHandler, CharT>::asFinalParser()
+{
+    static_assert(mozilla::IsBaseOf<GeneralParser<ParseHandler, CharT>, FinalParser>::value,
+                  "inheritance relationship required by the static_cast<> below");
+
+    return static_cast<FinalParser*>(this);
+}
+
+template <class ParseHandler, typename CharT>
+inline const typename GeneralParser<ParseHandler, CharT>::FinalParser*
+GeneralParser<ParseHandler, CharT>::asFinalParser() const
+{
+    static_assert(mozilla::IsBaseOf<GeneralParser<ParseHandler, CharT>, FinalParser>::value,
+                  "inheritance relationship required by the static_cast<> below");
+
+    return static_cast<const FinalParser*>(this);
+}
+
+template<class ParseHandler, typename CharT>
+inline bool
+GeneralParser<ParseHandler, CharT>::abortIfSyntaxParser()
+{
+    return asFinalParser()->abortIfSyntaxParser();
+}
+
+template <class ParseHandler, typename CharT>
 void
-Parser<ParseHandler, CharT>::error(unsigned errorNumber, ...)
+GeneralParser<ParseHandler, CharT>::error(unsigned errorNumber, ...)
 {
     va_list args;
     va_start(args, errorNumber);
 
     ErrorMetadata metadata;
     if (tokenStream.computeErrorMetadata(&metadata, pos().begin))
         ReportCompileError(context, Move(metadata), nullptr, JSREPORT_ERROR, errorNumber, args);
 
     va_end(args);
 }
 
 template <class ParseHandler, typename CharT>
 void
-Parser<ParseHandler, CharT>::errorWithNotes(UniquePtr<JSErrorNotes> notes, unsigned errorNumber,
-                                            ...)
+GeneralParser<ParseHandler, CharT>::errorWithNotes(UniquePtr<JSErrorNotes> notes,
+                                                   unsigned errorNumber, ...)
 {
     va_list args;
     va_start(args, errorNumber);
 
     ErrorMetadata metadata;
     if (tokenStream.computeErrorMetadata(&metadata, pos().begin)) {
         ReportCompileError(context, Move(metadata), Move(notes), JSREPORT_ERROR, errorNumber,
                            args);
     }
 
     va_end(args);
 }
 
 template <class ParseHandler, typename CharT>
 void
-Parser<ParseHandler, CharT>::errorAt(uint32_t offset, unsigned errorNumber, ...)
+GeneralParser<ParseHandler, CharT>::errorAt(uint32_t offset, unsigned errorNumber, ...)
 {
     va_list args;
     va_start(args, errorNumber);
 
     ErrorMetadata metadata;
     if (tokenStream.computeErrorMetadata(&metadata, offset))
         ReportCompileError(context, Move(metadata), nullptr, JSREPORT_ERROR, errorNumber, args);
 
     va_end(args);
 }
 
 template <class ParseHandler, typename CharT>
 void
-Parser<ParseHandler, CharT>::errorWithNotesAt(UniquePtr<JSErrorNotes> notes, uint32_t offset,
-                                              unsigned errorNumber, ...)
+GeneralParser<ParseHandler, CharT>::errorWithNotesAt(UniquePtr<JSErrorNotes> notes,
+                                                     uint32_t offset, unsigned errorNumber, ...)
 {
     va_list args;
     va_start(args, errorNumber);
 
     ErrorMetadata metadata;
     if (tokenStream.computeErrorMetadata(&metadata, offset)) {
         ReportCompileError(context, Move(metadata), Move(notes), JSREPORT_ERROR, errorNumber,
                            args);
     }
 
     va_end(args);
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::warning(unsigned errorNumber, ...)
+GeneralParser<ParseHandler, CharT>::warning(unsigned errorNumber, ...)
 {
     va_list args;
     va_start(args, errorNumber);
 
     ErrorMetadata metadata;
     bool result =
         tokenStream.computeErrorMetadata(&metadata, pos().begin) &&
         anyChars.compileWarning(Move(metadata), nullptr, JSREPORT_WARNING, errorNumber, args);
 
     va_end(args);
     return result;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::warningAt(uint32_t offset, unsigned errorNumber, ...)
+GeneralParser<ParseHandler, CharT>::warningAt(uint32_t offset, unsigned errorNumber, ...)
 {
     va_list args;
     va_start(args, errorNumber);
 
     ErrorMetadata metadata;
     bool result = tokenStream.computeErrorMetadata(&metadata, offset);
     if (result) {
         result =
@@ -681,60 +709,60 @@ Parser<ParseHandler, CharT>::warningAt(u
     }
 
     va_end(args);
     return result;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::extraWarning(unsigned errorNumber, ...)
+GeneralParser<ParseHandler, CharT>::extraWarning(unsigned errorNumber, ...)
 {
     va_list args;
     va_start(args, errorNumber);
 
     bool result =
         tokenStream.reportExtraWarningErrorNumberVA(nullptr, pos().begin, errorNumber, &args);
 
     va_end(args);
     return result;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::extraWarningAt(uint32_t offset, unsigned errorNumber, ...)
+GeneralParser<ParseHandler, CharT>::extraWarningAt(uint32_t offset, unsigned errorNumber, ...)
 {
     va_list args;
     va_start(args, errorNumber);
 
     bool result =
         tokenStream.reportExtraWarningErrorNumberVA(nullptr, offset, errorNumber, &args);
 
     va_end(args);
     return result;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::strictModeError(unsigned errorNumber, ...)
+GeneralParser<ParseHandler, CharT>::strictModeError(unsigned errorNumber, ...)
 {
     va_list args;
     va_start(args, errorNumber);
 
     bool res =
         tokenStream.reportStrictModeErrorNumberVA(nullptr, pos().begin, pc->sc()->strict(),
                                                   errorNumber, &args);
 
     va_end(args);
     return res;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::strictModeErrorAt(uint32_t offset, unsigned errorNumber, ...)
+GeneralParser<ParseHandler, CharT>::strictModeErrorAt(uint32_t offset, unsigned errorNumber, ...)
 {
     va_list args;
     va_start(args, errorNumber);
 
     bool res =
         tokenStream.reportStrictModeErrorNumberVA(nullptr, offset, pc->sc()->strict(),
                                                   errorNumber, &args);
 
@@ -767,76 +795,16 @@ ParserBase::errorNoOffset(unsigned error
     ErrorMetadata metadata;
     anyChars.computeErrorMetadataNoOffset(&metadata);
 
     ReportCompileError(context, Move(metadata), nullptr, JSREPORT_ERROR, errorNumber, args);
 
     va_end(args);
 }
 
-#define ABORTED_SYNTAX_PARSE_SENTINEL (SyntaxParser*)(0x1)
-
-template <>
-bool
-Parser<FullParseHandler, char16_t>::hadAbortedSyntaxParse()
-{
-    return false;
-}
-
-template <>
-bool
-Parser<SyntaxParseHandler, char16_t>::hadAbortedSyntaxParse()
-{
-    return syntaxParser_ == ABORTED_SYNTAX_PARSE_SENTINEL;
-}
-
-template <>
-void
-Parser<FullParseHandler, char16_t>::clearAbortedSyntaxParse()
-{
-}
-
-template <>
-void
-Parser<SyntaxParseHandler, char16_t>::clearAbortedSyntaxParse()
-{
-    syntaxParser_ = nullptr;
-}
-
-template <>
-void
-Parser<FullParseHandler, char16_t>::disableSyntaxParser()
-{
-    syntaxParser_ = nullptr;
-}
-
-template <>
-void
-Parser<SyntaxParseHandler, char16_t>::disableSyntaxParser()
-{
-}
-
-template <>
-inline bool
-Parser<FullParseHandler, char16_t>::abortIfSyntaxParser()
-{
-    disableSyntaxParser();
-    return true;
-}
-
-template <>
-inline bool
-Parser<SyntaxParseHandler, char16_t>::abortIfSyntaxParser()
-{
-    syntaxParser_ = ABORTED_SYNTAX_PARSE_SENTINEL;
-    return false;
-}
-
-#undef ABORTED_SYNTAX_PARSE_SENTINEL
-
 ParserBase::ParserBase(JSContext* cx, LifoAlloc& alloc,
                        const ReadOnlyCompileOptions& options,
                        const char16_t* chars, size_t length,
                        bool foldConstants,
                        UsedNameTracker& usedNames)
   : context(cx),
     alloc(alloc),
     anyChars(cx, options, thisForCtor()),
@@ -866,69 +834,70 @@ ParserBase::~ParserBase()
      * next GC) to avoid unnecessary OOMs.
      */
     alloc.freeAllIfHugeAndUnused();
 
     context->frontendCollectionPool().removeActiveCompilation();
 }
 
 template <class ParseHandler, typename CharT>
-Parser<ParseHandler, CharT>::Parser(JSContext* cx, LifoAlloc& alloc,
-                                    const ReadOnlyCompileOptions& options,
-                                    const CharT* chars, size_t length,
-                                    bool foldConstants,
-                                    UsedNameTracker& usedNames,
-                                    SyntaxParser* syntaxParser,
-                                    LazyScript* lazyOuterFunction)
+GeneralParser<ParseHandler, CharT>::GeneralParser(JSContext* cx, LifoAlloc& alloc,
+                                                  const ReadOnlyCompileOptions& options,
+                                                  const CharT* chars, size_t length,
+                                                  bool foldConstants,
+                                                  UsedNameTracker& usedNames,
+                                                  SyntaxParser* syntaxParser,
+                                                  LazyScript* lazyOuterFunction)
   : ParserBase(cx, alloc, options, chars, length, foldConstants, usedNames),
     AutoGCRooter(cx, PARSER),
     syntaxParser_(syntaxParser),
     tokenStream(cx, options, chars, length),
     handler(cx, alloc, lazyOuterFunction)
-{
-    // The Mozilla specific JSOPTION_EXTRA_WARNINGS option adds extra warnings
-    // which are not generated if functions are parsed lazily. Note that the
-    // standard "use strict" does not inhibit lazy parsing.
-    if (options.extraWarningsOption)
-        disableSyntaxParser();
-}
+{}
 
 template<class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::checkOptions()
+GeneralParser<ParseHandler, CharT>::checkOptions()
 {
 #ifdef DEBUG
     checkOptionsCalled = true;
 #endif
 
     return anyChars.checkOptions();
 }
 
 template <class ParseHandler, typename CharT>
-Parser<ParseHandler, CharT>::~Parser()
+GeneralParser<ParseHandler, CharT>::~GeneralParser()
 {
     MOZ_ASSERT(checkOptionsCalled);
 }
 
-template <>
+template <typename CharT>
 void
-Parser<SyntaxParseHandler, char16_t>::setAwaitHandling(AwaitHandling awaitHandling)
-{
-    awaitHandling_ = awaitHandling;
-}
-
-template <>
+Parser<SyntaxParseHandler, CharT>::setAwaitHandling(AwaitHandling awaitHandling)
+{
+    this->awaitHandling_ = awaitHandling;
+}
+
+template <typename CharT>
 void
-Parser<FullParseHandler, char16_t>::setAwaitHandling(AwaitHandling awaitHandling)
-{
-    awaitHandling_ = awaitHandling;
+Parser<FullParseHandler, CharT>::setAwaitHandling(AwaitHandling awaitHandling)
+{
+    this->awaitHandling_ = awaitHandling;
     if (syntaxParser_)
         syntaxParser_->setAwaitHandling(awaitHandling);
 }
 
+template <class ParseHandler, typename CharT>
+inline void
+GeneralParser<ParseHandler, CharT>::setAwaitHandling(AwaitHandling awaitHandling)
+{
+    asFinalParser()->setAwaitHandling(awaitHandling);
+}
+
 ObjectBox*
 ParserBase::newObjectBox(JSObject* obj)
 {
     MOZ_ASSERT(obj);
 
     /*
      * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
      * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
@@ -945,20 +914,21 @@ ParserBase::newObjectBox(JSObject* obj)
 
     traceListHead = objbox;
 
     return objbox;
 }
 
 template <class ParseHandler, typename CharT>
 FunctionBox*
-Parser<ParseHandler, CharT>::newFunctionBox(Node fn, JSFunction* fun, uint32_t toStringStart,
-                                            Directives inheritedDirectives,
-                                            GeneratorKind generatorKind,
-                                            FunctionAsyncKind asyncKind)
+GeneralParser<ParseHandler, CharT>::newFunctionBox(Node fn, JSFunction* fun,
+                                                   uint32_t toStringStart,
+                                                   Directives inheritedDirectives,
+                                                   GeneratorKind generatorKind,
+                                                   FunctionAsyncKind asyncKind)
 {
     MOZ_ASSERT(fun);
 
     /*
      * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
      * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
      * arenas containing the entries must be alive until we are done with
      * scanning, parsing and code generation for the whole script or top-level
@@ -988,33 +958,33 @@ ModuleSharedContext::ModuleSharedContext
     bindings(cx),
     builder(builder)
 {
     thisBinding_ = ThisBinding::Module;
 }
 
 template <class ParseHandler, typename CharT>
 void
-Parser<ParseHandler, CharT>::trace(JSTracer* trc)
+GeneralParser<ParseHandler, CharT>::trace(JSTracer* trc)
 {
     ObjectBox::TraceList(trc, traceListHead);
 }
 
 void
 TraceParser(JSTracer* trc, AutoGCRooter* parser)
 {
-    static_cast<Parser<FullParseHandler, char16_t>*>(parser)->trace(trc);
+    static_cast<GeneralParser<FullParseHandler, char16_t>*>(parser)->trace(trc);
 }
 
 /*
  * Parse a top-level JS script.
  */
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::parse()
+GeneralParser<ParseHandler, CharT>::parse()
 {
     MOZ_ASSERT(checkOptionsCalled);
 
     Directives directives(options().strictOption);
     GlobalSharedContext globalsc(context, ScopeKind::Global,
                                  directives, options().extraWarningsOption);
     SourceParseContext globalpc(this, &globalsc, /* newDirectives = */ nullptr);
     if (!globalpc.init())
@@ -1078,18 +1048,18 @@ ParserBase::hasValidSimpleStrictParamete
         if (!isValidStrictBinding(name->asPropertyName()))
             return false;
     }
     return true;
 }
 
 template <class ParseHandler, typename CharT>
 void
-Parser<ParseHandler, CharT>::reportMissingClosing(unsigned errorNumber, unsigned noteNumber,
-                                                  uint32_t openedPos)
+GeneralParser<ParseHandler, CharT>::reportMissingClosing(unsigned errorNumber, unsigned noteNumber,
+                                                         uint32_t openedPos)
 {
     auto notes = MakeUnique<JSErrorNotes>();
     if (!notes) {
         ReportOutOfMemory(pc->sc()->context);
         return;
     }
 
     uint32_t line, column;
@@ -1109,18 +1079,19 @@ Parser<ParseHandler, CharT>::reportMissi
         return;
     }
 
     errorWithNotes(Move(notes), errorNumber);
 }
 
 template <class ParseHandler, typename CharT>
 void
-Parser<ParseHandler, CharT>::reportRedeclaration(HandlePropertyName name, DeclarationKind prevKind,
-                                                 TokenPos pos, uint32_t prevPos)
+GeneralParser<ParseHandler, CharT>::reportRedeclaration(HandlePropertyName name,
+                                                        DeclarationKind prevKind,
+                                                        TokenPos pos, uint32_t prevPos)
 {
     JSAutoByteString bytes;
     if (!AtomToPrintableString(context, name, &bytes))
         return;
 
     if (prevPos == DeclaredNameInfo::npos) {
         errorAt(pos.begin, JSMSG_REDECLARED_VAR, DeclarationKindString(prevKind), bytes.ptr());
         return;
@@ -1160,20 +1131,20 @@ Parser<ParseHandler, CharT>::reportRedec
 //
 // The 'disallowDuplicateParams' bool indicates whether the use of another
 // feature (destructuring or default arguments) disables duplicate arguments.
 // (ECMA-262 requires us to support duplicate parameter names, but, for newer
 // features, we consider the code to have "opted in" to higher standards and
 // forbid duplicates.)
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::notePositionalFormalParameter(Node fn, HandlePropertyName name,
-                                                           uint32_t beginPos,
-                                                           bool disallowDuplicateParams,
-                                                           bool* duplicatedParam)
+GeneralParser<ParseHandler, CharT>::notePositionalFormalParameter(Node fn, HandlePropertyName name,
+                                                                  uint32_t beginPos,
+                                                                  bool disallowDuplicateParams,
+                                                                  bool* duplicatedParam)
 {
     if (AddDeclaredNamePtr p = pc->functionScope().lookupDeclaredNameForAdd(name)) {
         if (disallowDuplicateParams) {
             error(JSMSG_BAD_DUP_ARGS);
             return false;
         }
 
         // Strict-mode disallows duplicate args. We may not know whether we are
@@ -1205,17 +1176,18 @@ Parser<ParseHandler, CharT>::notePositio
         return false;
 
     handler.addFunctionFormalParameter(fn, paramNode);
     return true;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::noteDestructuredPositionalFormalParameter(Node fn, Node destruct)
+GeneralParser<ParseHandler, CharT>::noteDestructuredPositionalFormalParameter(Node fn,
+                                                                              Node destruct)
 {
     // Append an empty name to the positional formals vector to keep track of
     // argument slots when making FunctionScope::Data.
     if (!pc->positionalFormalParameterNames().append(nullptr)) {
         ReportOutOfMemory(context);
         return false;
     }
 
@@ -1451,19 +1423,19 @@ ParseContext::annexBAppliesToLexicalFunc
 
     // If an early error would have occurred already, this function should not
     // exhibit Annex B.3.3 semantics.
     return !redeclaredKind;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::checkLexicalDeclarationDirectlyWithinBlock(ParseContext::Statement& stmt,
-                                                                        DeclarationKind kind,
-                                                                        TokenPos pos)
+GeneralParser<ParseHandler, CharT>::checkLexicalDeclarationDirectlyWithinBlock(ParseContext::Statement& stmt,
+                                                                               DeclarationKind kind,
+                                                                               TokenPos pos)
 {
     MOZ_ASSERT(DeclarationKindIsLexical(kind));
 
     // It is an early error to declare a lexical binding not directly
     // within a block.
     if (!StatementKindIsBraced(stmt.kind()) &&
         stmt.kind() != StatementKind::ForLoopLexicalHead)
     {
@@ -1475,18 +1447,18 @@ Parser<ParseHandler, CharT>::checkLexica
         return false;
     }
 
     return true;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::noteDeclaredName(HandlePropertyName name, DeclarationKind kind,
-                                              TokenPos pos)
+GeneralParser<ParseHandler, CharT>::noteDeclaredName(HandlePropertyName name, DeclarationKind kind,
+                                                     TokenPos pos)
 {
     // The asm.js validator does all its own symbol-table management so, as an
     // optimization, avoid doing any work here.
     if (pc->useAsmOrInsideUseAsm())
         return true;
 
     switch (kind) {
       case DeclarationKind::Var:
@@ -1646,17 +1618,17 @@ Parser<ParseHandler, CharT>::noteDeclare
         break;
     }
 
     return true;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::noteUsedName(HandlePropertyName name)
+GeneralParser<ParseHandler, CharT>::noteUsedName(HandlePropertyName name)
 {
     // If the we are delazifying, the LazyScript already has all the
     // closed-over info for bindings and there's no need to track used names.
     if (handler.canSkipLazyClosedOverBindings())
         return true;
 
     // The asm.js validator does all its own symbol-table management so, as an
     // optimization, avoid doing any work here.
@@ -1671,26 +1643,26 @@ Parser<ParseHandler, CharT>::noteUsedNam
     if (pc->sc()->isGlobalContext() && scope == &pc->varScope())
         return true;
 
     return usedNames.noteUse(context, name, pc->scriptId(), scope->id());
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::hasUsedName(HandlePropertyName name)
+GeneralParser<ParseHandler, CharT>::hasUsedName(HandlePropertyName name)
 {
     if (UsedNamePtr p = usedNames.lookup(name))
         return p->value().isUsedInScript(pc->scriptId());
     return false;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::propagateFreeNamesAndMarkClosedOverBindings(ParseContext::Scope& scope)
+GeneralParser<ParseHandler, CharT>::propagateFreeNamesAndMarkClosedOverBindings(ParseContext::Scope& scope)
 {
     // Now that we have all the declared names in the scope, check which
     // functions should exhibit Annex B semantics.
     if (!scope.propagateAndMarkAnnexBFunctionBoxes(pc))
         return false;
 
     if (handler.canSkipLazyClosedOverBindings()) {
         // Scopes are nullptr-delimited in the LazyScript closed over bindings
@@ -1722,19 +1694,19 @@ Parser<ParseHandler, CharT>::propagateFr
     if (isSyntaxParser && !pc->closedOverBindingsForLazy().append(nullptr)) {
         ReportOutOfMemory(context);
         return false;
     }
 
     return true;
 }
 
-template <>
+template <typename CharT>
 bool
-Parser<FullParseHandler, char16_t>::checkStatementsEOF()
+Parser<FullParseHandler, CharT>::checkStatementsEOF()
 {
     // This is designed to be paired with parsing a statement list at the top
     // level.
     //
     // The statementList() call breaks on TOK_RC, so make sure we've
     // reached EOF here.
     TokenKind tt;
     if (!tokenStream.peekToken(&tt, TokenStream::Operand))
@@ -2107,40 +2079,50 @@ ParserBase::newLexicalScopeData(ParseCon
         bindings->constStart = cursor - start;
         PodCopy(cursor, consts.begin(), consts.length());
         bindings->length = numBindings;
     }
 
     return Some(bindings);
 }
 
-template <>
+template <typename CharT>
 SyntaxParseHandler::Node
-Parser<SyntaxParseHandler, char16_t>::finishLexicalScope(ParseContext::Scope& scope, Node body)
+Parser<SyntaxParseHandler, CharT>::finishLexicalScope(ParseContext::Scope& scope, Node body)
 {
     if (!propagateFreeNamesAndMarkClosedOverBindings(scope))
         return null();
+
     return body;
 }
 
-template <>
+template <typename CharT>
 ParseNode*
-Parser<FullParseHandler, char16_t>::finishLexicalScope(ParseContext::Scope& scope, ParseNode* body)
+Parser<FullParseHandler, CharT>::finishLexicalScope(ParseContext::Scope& scope, ParseNode* body)
 {
     if (!propagateFreeNamesAndMarkClosedOverBindings(scope))
         return nullptr;
+
     Maybe<LexicalScope::Data*> bindings = newLexicalScopeData(scope);
     if (!bindings)
         return nullptr;
+
     return handler.newLexicalScope(*bindings, body);
 }
 
-template <>
+template <class ParseHandler, typename CharT>
+inline typename ParseHandler::Node
+GeneralParser<ParseHandler, CharT>::finishLexicalScope(ParseContext::Scope& scope, Node body)
+{
+    return asFinalParser()->finishLexicalScope(scope, body);
+}
+
+template <typename CharT>
 ParseNode*
-Parser<FullParseHandler, char16_t>::evalBody(EvalSharedContext* evalsc)
+Parser<FullParseHandler, CharT>::evalBody(EvalSharedContext* evalsc)
 {
     SourceParseContext evalpc(this, evalsc, /* newDirectives = */ nullptr);
     if (!evalpc.init())
         return nullptr;
 
     ParseContext::VarScope varScope(this);
     if (!varScope.init(pc))
         return nullptr;
@@ -2198,19 +2180,19 @@ Parser<FullParseHandler, char16_t>::eval
     Maybe<EvalScope::Data*> bindings = newEvalScopeData(pc->varScope());
     if (!bindings)
         return nullptr;
     evalsc->bindings = *bindings;
 
     return body;
 }
 
-template <>
+template <typename CharT>
 ParseNode*
-Parser<FullParseHandler, char16_t>::globalBody(GlobalSharedContext* globalsc)
+Parser<FullParseHandler, CharT>::globalBody(GlobalSharedContext* globalsc)
 {
     SourceParseContext globalpc(this, globalsc, /* newDirectives = */ nullptr);
     if (!globalpc.init())
         return nullptr;
 
     ParseContext::VarScope varScope(this);
     if (!varScope.init(pc))
         return nullptr;
@@ -2234,35 +2216,36 @@ Parser<FullParseHandler, char16_t>::glob
     Maybe<GlobalScope::Data*> bindings = newGlobalScopeData(pc->varScope());
     if (!bindings)
         return nullptr;
     globalsc->bindings = *bindings;
 
     return body;
 }
 
-template <>
+template <typename CharT>
 ParseNode*
-Parser<FullParseHandler, char16_t>::moduleBody(ModuleSharedContext* modulesc)
+Parser<FullParseHandler, CharT>::moduleBody(ModuleSharedContext* modulesc)
 {
     MOZ_ASSERT(checkOptionsCalled);
 
     SourceParseContext modulepc(this, modulesc, nullptr);
     if (!modulepc.init())
         return null();
 
     ParseContext::VarScope varScope(this);
     if (!varScope.init(pc))
         return nullptr;
 
     Node mn = handler.newModule(pos());
     if (!mn)
         return null();
 
-    AutoAwaitIsKeyword<Parser> awaitIsKeyword(this, AwaitIsModuleKeyword);
+    AutoAwaitIsKeyword<GeneralParser<FullParseHandler, CharT>> awaitIsKeyword(this,
+                                                                              AwaitIsModuleKeyword);
     ParseNode* pn = statementList(YieldIsName);
     if (!pn)
         return null();
 
     MOZ_ASSERT(pn->isKind(PNK_STATEMENTLIST));
     mn->pn_body = pn;
 
     TokenKind tt;
@@ -2303,35 +2286,35 @@ Parser<FullParseHandler, char16_t>::modu
     Maybe<ModuleScope::Data*> bindings = newModuleScopeData(modulepc.varScope());
     if (!bindings)
         return nullptr;
 
     modulesc->bindings = *bindings;
     return mn;
 }
 
-template <>
+template <typename CharT>
 SyntaxParseHandler::Node
-Parser<SyntaxParseHandler, char16_t>::moduleBody(ModuleSharedContext* modulesc)
+Parser<SyntaxParseHandler, CharT>::moduleBody(ModuleSharedContext* modulesc)
 {
     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
     return SyntaxParseHandler::NodeFailure;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::hasUsedFunctionSpecialName(HandlePropertyName name)
+GeneralParser<ParseHandler, CharT>::hasUsedFunctionSpecialName(HandlePropertyName name)
 {
     MOZ_ASSERT(name == context->names().arguments || name == context->names().dotThis);
     return hasUsedName(name) || pc->functionBox()->bindingsAccessedDynamically();
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::declareFunctionThis()
+GeneralParser<ParseHandler, CharT>::declareFunctionThis()
 {
     // The asm.js validator does all its own symbol-table management so, as an
     // optimization, avoid doing any work here.
     if (pc->useAsmOrInsideUseAsm())
         return true;
 
     // Derived class constructors emit JSOP_CHECKRETURN, which requires
     // '.this' to be bound.
@@ -2356,79 +2339,79 @@ Parser<ParseHandler, CharT>::declareFunc
         funbox->setHasThisBinding();
     }
 
     return true;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::newInternalDotName(HandlePropertyName name)
+GeneralParser<ParseHandler, CharT>::newInternalDotName(HandlePropertyName name)
 {
     Node nameNode = newName(name);
     if (!nameNode)
         return null();
     if (!noteUsedName(name))
         return null();
     return nameNode;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::newThisName()
+GeneralParser<ParseHandler, CharT>::newThisName()
 {
     return newInternalDotName(context->names().dotThis);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::newDotGeneratorName()
+GeneralParser<ParseHandler, CharT>::newDotGeneratorName()
 {
     return newInternalDotName(context->names().dotGenerator);
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::declareDotGeneratorName()
+GeneralParser<ParseHandler, CharT>::declareDotGeneratorName()
 {
     // The special '.generator' binding must be on the function scope, as
     // generators expect to find it on the CallObject.
     ParseContext::Scope& funScope = pc->functionScope();
     HandlePropertyName dotGenerator = context->names().dotGenerator;
     AddDeclaredNamePtr p = funScope.lookupDeclaredNameForAdd(dotGenerator);
     if (!p && !funScope.addDeclaredName(pc, p, dotGenerator, DeclarationKind::Var,
                                         DeclaredNameInfo::npos))
     {
         return false;
     }
     return true;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::finishFunctionScopes(bool isStandaloneFunction)
+GeneralParser<ParseHandler, CharT>::finishFunctionScopes(bool isStandaloneFunction)
 {
     FunctionBox* funbox = pc->functionBox();
 
     if (funbox->hasParameterExprs) {
         if (!propagateFreeNamesAndMarkClosedOverBindings(pc->functionScope()))
             return false;
     }
 
     if (funbox->function()->isNamedLambda() && !isStandaloneFunction) {
         if (!propagateFreeNamesAndMarkClosedOverBindings(pc->namedLambdaScope()))
             return false;
     }
 
     return true;
 }
 
-template <>
+template <typename CharT>
 bool
-Parser<FullParseHandler, char16_t>::finishFunction(bool isStandaloneFunction /* = false */)
+Parser<FullParseHandler, CharT>::finishFunction(bool isStandaloneFunction /* = false */)
 {
     if (!finishFunctionScopes(isStandaloneFunction))
         return false;
 
     FunctionBox* funbox = pc->functionBox();
     bool hasParameterExprs = funbox->hasParameterExprs;
 
     if (hasParameterExprs) {
@@ -2451,19 +2434,19 @@ Parser<FullParseHandler, char16_t>::fini
         if (!bindings)
             return false;
         funbox->namedLambdaBindings().set(*bindings);
     }
 
     return true;
 }
 
-template <>
+template <typename CharT>
 bool
-Parser<SyntaxParseHandler, char16_t>::finishFunction(bool isStandaloneFunction /* = false */)
+Parser<SyntaxParseHandler, CharT>::finishFunction(bool isStandaloneFunction /* = false */)
 {
     // The LazyScript for a lazily parsed function needs to know its set of
     // free variables and inner functions so that when it is fully parsed, we
     // can skip over any already syntax parsed inner functions and still
     // retain correct scope information.
 
     if (!finishFunctionScopes(isStandaloneFunction))
         return false;
@@ -2511,41 +2494,48 @@ Parser<SyntaxParseHandler, char16_t>::fi
     // Flags that need to copied back into the parser when we do the full
     // parse.
     PropagateTransitiveParseFlags(funbox, lazy);
 
     fun->initLazyScript(lazy);
     return true;
 }
 
+template <class ParseHandler, typename CharT>
+inline bool
+GeneralParser<ParseHandler, CharT>::finishFunction(bool isStandaloneFunction /* = false */)
+{
+    return asFinalParser()->finishFunction(isStandaloneFunction);
+}
+
 static YieldHandling
 GetYieldHandling(GeneratorKind generatorKind)
 {
     if (generatorKind == GeneratorKind::NotGenerator)
         return YieldIsName;
     return YieldIsKeyword;
 }
 
 static AwaitHandling
 GetAwaitHandling(FunctionAsyncKind asyncKind)
 {
     if (asyncKind == FunctionAsyncKind::SyncFunction)
         return AwaitIsName;
     return AwaitIsKeyword;
 }
 
-template <>
+template <typename CharT>
 ParseNode*
-Parser<FullParseHandler, char16_t>::standaloneFunction(HandleFunction fun,
-                                                       HandleScope enclosingScope,
-                                                       const Maybe<uint32_t>& parameterListEnd,
-                                                       GeneratorKind generatorKind,
-                                                       FunctionAsyncKind asyncKind,
-                                                       Directives inheritedDirectives,
-                                                       Directives* newDirectives)
+Parser<FullParseHandler, CharT>::standaloneFunction(HandleFunction fun,
+                                                    HandleScope enclosingScope,
+                                                    const Maybe<uint32_t>& parameterListEnd,
+                                                    GeneratorKind generatorKind,
+                                                    FunctionAsyncKind asyncKind,
+                                                    Directives inheritedDirectives,
+                                                    Directives* newDirectives)
 {
     MOZ_ASSERT(checkOptionsCalled);
 
     // Skip prelude.
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
     if (asyncKind == FunctionAsyncKind::AsyncFunction) {
@@ -2588,17 +2578,17 @@ Parser<FullParseHandler, char16_t>::stan
 
     SourceParseContext funpc(this, funbox, newDirectives);
     if (!funpc.init())
         return null();
     funpc.setIsStandaloneFunctionBody();
 
     YieldHandling yieldHandling = GetYieldHandling(generatorKind);
     AwaitHandling awaitHandling = GetAwaitHandling(asyncKind);
-    AutoAwaitIsKeyword<Parser> awaitIsKeyword(this, awaitHandling);
+    AutoAwaitIsKeyword<GeneralParser<FullParseHandler, CharT>> awaitIsKeyword(this, awaitHandling);
     if (!functionFormalParametersAndBody(InAllowed, yieldHandling, fn, Statement,
                                          parameterListEnd, /* isStandaloneFunction = */ true))
     {
         return null();
     }
 
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
@@ -2610,17 +2600,17 @@ Parser<FullParseHandler, char16_t>::stan
     if (!FoldConstants(context, &fn, this))
         return null();
 
     return fn;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::declareFunctionArgumentsObject()
+GeneralParser<ParseHandler, CharT>::declareFunctionArgumentsObject()
 {
     FunctionBox* funbox = pc->functionBox();
     ParseContext::Scope& funScope = pc->functionScope();
     ParseContext::Scope& varScope = pc->varScope();
 
     bool hasExtraBodyVarScope = &funScope != &varScope;
 
     // Time to implement the odd semantics of 'arguments'.
@@ -2687,18 +2677,19 @@ Parser<ParseHandler, CharT>::declareFunc
             funbox->setDefinitelyNeedsArgsObj();
     }
 
     return true;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::functionBody(InHandling inHandling, YieldHandling yieldHandling,
-                                          FunctionSyntaxKind kind, FunctionBodyType type)
+GeneralParser<ParseHandler, CharT>::functionBody(InHandling inHandling,
+                                                 YieldHandling yieldHandling,
+                                                 FunctionSyntaxKind kind, FunctionBodyType type)
 {
     MOZ_ASSERT(pc->isFunctionBox());
 
 #ifdef DEBUG
     uint32_t startYieldOffset = pc->lastYieldOffset;
 #endif
 
     Node pn;
@@ -2851,17 +2842,17 @@ ParserBase::newFunction(HandleAtom atom,
             fun->setExtendedSlot(HAS_SELFHOSTED_CANONICAL_NAME_SLOT, BooleanValue(false));
 #endif
     }
     return fun;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::matchOrInsertSemicolon()
+GeneralParser<ParseHandler, CharT>::matchOrInsertSemicolon()
 {
     TokenKind tt = TOK_EOF;
     if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
         return false;
     if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
         /*
          * When current token is `await` and it's outside of async function,
          * it's possibly intended to be an await expression.
@@ -2889,17 +2880,17 @@ Parser<ParseHandler, CharT>::matchOrInse
         return false;
     }
     bool matched;
     return tokenStream.matchToken(&matched, TOK_SEMI, TokenStream::Operand);
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::leaveInnerFunction(ParseContext* outerpc)
+GeneralParser<ParseHandler, CharT>::leaveInnerFunction(ParseContext* outerpc)
 {
     MOZ_ASSERT(pc != outerpc);
 
     // If the current function allows super.property but cannot have a home
     // object, i.e., it is an arrow function, we need to propagate the flag to
     // the outer ParseContext.
     if (pc->superScopeNeedsHomeObject()) {
         if (!pc->isArrowFunction())
@@ -2909,28 +2900,28 @@ Parser<ParseHandler, CharT>::leaveInnerF
     }
 
     // Lazy functions inner to another lazy function need to be remembered by
     // the inner function so that if the outer function is eventually parsed
     // we do not need any further parsing or processing of the inner function.
     //
     // Append the inner function here unconditionally; the vector is only used
     // if the Parser using outerpc is a syntax parsing. See
-    // Parser<SyntaxParseHandler>::finishFunction.
+    // GeneralParser<SyntaxParseHandler>::finishFunction.
     if (!outerpc->innerFunctionsForLazy.append(pc->functionBox()->function()))
         return false;
 
     PropagateTransitiveParseFlags(pc->functionBox(), outerpc->sc());
 
     return true;
 }
 
 template <class ParseHandler, typename CharT>
 JSAtom*
-Parser<ParseHandler, CharT>::prefixAccessorName(PropertyType propType, HandleAtom propAtom)
+GeneralParser<ParseHandler, CharT>::prefixAccessorName(PropertyType propType, HandleAtom propAtom)
 {
     RootedAtom prefix(context);
     if (propType == PropertyType::Setter || propType == PropertyType::SetterNoExpressionClosure) {
         prefix = context->names().setPrefix;
     } else {
         MOZ_ASSERT(propType == PropertyType::Getter || propType == PropertyType::GetterNoExpressionClosure);
         prefix = context->names().getPrefix;
     }
@@ -2939,19 +2930,19 @@ Parser<ParseHandler, CharT>::prefixAcces
     if (!str)
         return nullptr;
 
     return AtomizeString(context, str);
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::functionArguments(YieldHandling yieldHandling,
-                                               FunctionSyntaxKind kind,
-                                               Node funcpn)
+GeneralParser<ParseHandler, CharT>::functionArguments(YieldHandling yieldHandling,
+                                                      FunctionSyntaxKind kind,
+                                                      Node funcpn)
 {
     FunctionBox* funbox = pc->functionBox();
 
     bool parenFreeArrow = false;
     // Modifier for the following tokens.
     // TokenStream::None for the following cases:
     //   async a => 1
     //         ^
@@ -3217,20 +3208,20 @@ Parser<ParseHandler, CharT>::functionArg
     } else if (IsSetterKind(kind)) {
         error(JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", "");
         return false;
     }
 
     return true;
 }
 
-template <>
+template <typename CharT>
 bool
-Parser<FullParseHandler, char16_t>::skipLazyInnerFunction(ParseNode* pn, uint32_t toStringStart,
-                                                          FunctionSyntaxKind kind, bool tryAnnexB)
+Parser<FullParseHandler, CharT>::skipLazyInnerFunction(ParseNode* pn, uint32_t toStringStart,
+                                                       FunctionSyntaxKind kind, bool tryAnnexB)
 {
     // When a lazily-parsed function is called, we only fully parse (and emit)
     // that function, not any of its nested children. The initial syntax-only
     // parse recorded the free variables of nested functions and their extents,
     // so we can skip over them after accounting for their free variables.
 
     RootedFunction fun(context, handler.nextLazyInnerFunction());
     FunctionBox* funbox = newFunctionBox(pn, fun, toStringStart, Directives(/* strict = */ false),
@@ -3261,30 +3252,39 @@ Parser<FullParseHandler, char16_t>::skip
 
     // Append possible Annex B function box only upon successfully parsing.
     if (tryAnnexB && !pc->innermostScope()->addPossibleAnnexBFunctionBox(pc, funbox))
         return false;
 
     return true;
 }
 
-template <>
+template <typename CharT>
 bool
-Parser<SyntaxParseHandler, char16_t>::skipLazyInnerFunction(Node pn, uint32_t toStringStart,
-                                                            FunctionSyntaxKind kind,
-                                                            bool tryAnnexB)
+Parser<SyntaxParseHandler, CharT>::skipLazyInnerFunction(Node pn, uint32_t toStringStart,
+                                                         FunctionSyntaxKind kind,
+                                                         bool tryAnnexB)
 {
     MOZ_CRASH("Cannot skip lazy inner functions when syntax parsing");
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::addExprAndGetNextTemplStrToken(YieldHandling yieldHandling,
-                                                            Node nodeList,
-                                                            TokenKind* ttp)
+GeneralParser<ParseHandler, CharT>::skipLazyInnerFunction(Node node, uint32_t toStringStart,
+                                                          FunctionSyntaxKind kind,
+                                                          bool tryAnnexB)
+{
+    return asFinalParser()->skipLazyInnerFunction(node, toStringStart, kind, tryAnnexB);
+}
+
+template <class ParseHandler, typename CharT>
+bool
+GeneralParser<ParseHandler, CharT>::addExprAndGetNextTemplStrToken(YieldHandling yieldHandling,
+                                                                   Node nodeList,
+                                                                   TokenKind* ttp)
 {
     Node pn = expr(InAllowed, yieldHandling, TripledotProhibited);
     if (!pn)
         return false;
     handler.addList(nodeList, pn);
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
@@ -3294,18 +3294,18 @@ Parser<ParseHandler, CharT>::addExprAndG
         return false;
     }
 
     return tokenStream.getToken(ttp, TokenStream::TemplateTail);
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::taggedTemplate(YieldHandling yieldHandling, Node nodeList,
-                                            TokenKind tt)
+GeneralParser<ParseHandler, CharT>::taggedTemplate(YieldHandling yieldHandling, Node nodeList,
+                                                   TokenKind tt)
 {
     Node callSiteObjNode = handler.newCallSiteObject(pos().begin);
     if (!callSiteObjNode)
         return false;
     handler.addList(nodeList, callSiteObjNode);
 
     while (true) {
         if (!appendToCallSiteObj(callSiteObjNode))
@@ -3317,17 +3317,17 @@ Parser<ParseHandler, CharT>::taggedTempl
             return false;
     }
     handler.setEndPosition(nodeList, callSiteObjNode);
     return true;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::templateLiteral(YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::templateLiteral(YieldHandling yieldHandling)
 {
     Node pn = noSubstitutionUntaggedTemplate();
     if (!pn)
         return null();
 
     Node nodeList = handler.newList(PNK_TEMPLATE_STRING_LIST, pn);
     if (!nodeList)
         return null();
@@ -3343,22 +3343,22 @@ Parser<ParseHandler, CharT>::templateLit
 
         handler.addList(nodeList, pn);
     } while (tt == TOK_TEMPLATE_HEAD);
     return nodeList;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::functionDefinition(Node pn, uint32_t toStringStart,
-                                                InHandling inHandling, YieldHandling yieldHandling,
-                                                HandleAtom funName, FunctionSyntaxKind kind,
-                                                GeneratorKind generatorKind,
-                                                FunctionAsyncKind asyncKind,
-                                                bool tryAnnexB /* = false */)
+GeneralParser<ParseHandler, CharT>::functionDefinition(Node pn, uint32_t toStringStart,
+                                                       InHandling inHandling, YieldHandling yieldHandling,
+                                                       HandleAtom funName, FunctionSyntaxKind kind,
+                                                       GeneratorKind generatorKind,
+                                                       FunctionAsyncKind asyncKind,
+                                                       bool tryAnnexB /* = false */)
 {
     MOZ_ASSERT_IF(kind == Statement, funName);
 
     // When fully parsing a LazyScript, we do not fully reparse its inner
     // functions, which are also lazy. Instead, their free variables and
     // source extents are recorded and may be skipped.
     if (handler.canSkipLazyInnerFunctions()) {
         if (!skipLazyInnerFunction(pn, toStringStart, kind, tryAnnexB))
@@ -3417,28 +3417,28 @@ Parser<ParseHandler, CharT>::functionDef
 
         // functionFormalParametersAndBody may have already set pn->pn_body before failing.
         handler.setFunctionFormalParametersAndBody(pn, null());
     }
 
     return pn;
 }
 
-template <>
+template <typename CharT>
 bool
-Parser<FullParseHandler, char16_t>::trySyntaxParseInnerFunction(ParseNode* pn, HandleFunction fun,
-                                                                uint32_t toStringStart,
-                                                                InHandling inHandling,
-                                                                YieldHandling yieldHandling,
-                                                                FunctionSyntaxKind kind,
-                                                                GeneratorKind generatorKind,
-                                                                FunctionAsyncKind asyncKind,
-                                                                bool tryAnnexB,
-                                                                Directives inheritedDirectives,
-                                                                Directives* newDirectives)
+Parser<FullParseHandler, CharT>::trySyntaxParseInnerFunction(ParseNode* pn, HandleFunction fun,
+                                                             uint32_t toStringStart,
+                                                             InHandling inHandling,
+                                                             YieldHandling yieldHandling,
+                                                             FunctionSyntaxKind kind,
+                                                             GeneratorKind generatorKind,
+                                                             FunctionAsyncKind asyncKind,
+                                                             bool tryAnnexB,
+                                                             Directives inheritedDirectives,
+                                                             Directives* newDirectives)
 {
     // Try a syntax parse for this inner function.
     do {
         // If we're assuming this function is an IIFE, always perform a full
         // parse to avoid the overhead of a lazy syntax-only parse. Although
         // the prediction may be incorrect, IIFEs are common enough that it
         // pays off for lots of code.
         if (pn->isLikelyIIFE() &&
@@ -3463,19 +3463,19 @@ Parser<FullParseHandler, char16_t>::tryS
         // still expects a FunctionBox to be attached to it during BCE, and
         // the syntax parser cannot attach one to it.
         FunctionBox* funbox = newFunctionBox(pn, fun, toStringStart, inheritedDirectives,
                                              generatorKind, asyncKind);
         if (!funbox)
             return false;
         funbox->initWithEnclosingParseContext(pc, kind);
 
-        if (!syntaxParser_->innerFunction(SyntaxParseHandler::NodeGeneric,
-                                          pc, funbox, inHandling, yieldHandling, kind,
-                                          newDirectives))
+        if (!syntaxParser_->innerFunctionForFunctionBox(SyntaxParseHandler::NodeGeneric,
+                                                        pc, funbox, inHandling, yieldHandling,
+                                                        kind, newDirectives))
         {
             if (syntaxParser_->hadAbortedSyntaxParse()) {
                 // Try again with a full parse. UsedNameTracker needs to be
                 // rewound to just before we tried the syntax parse for
                 // correctness.
                 syntaxParser_->clearAbortedSyntaxParse();
                 usedNames.rewind(token);
                 MOZ_ASSERT_IF(!syntaxParser_->context->helperThread(),
@@ -3500,40 +3500,61 @@ Parser<FullParseHandler, char16_t>::tryS
         return true;
     } while (false);
 
     // We failed to do a syntax parse above, so do the full parse.
     return innerFunction(pn, pc, fun, toStringStart, inHandling, yieldHandling, kind,
                          generatorKind, asyncKind, tryAnnexB, inheritedDirectives, newDirectives);
 }
 
-template <>
+template <typename CharT>
 bool
-Parser<SyntaxParseHandler, char16_t>::trySyntaxParseInnerFunction(Node pn, HandleFunction fun,
-                                                                  uint32_t toStringStart,
-                                                                  InHandling inHandling,
-                                                                  YieldHandling yieldHandling,
-                                                                  FunctionSyntaxKind kind,
-                                                                  GeneratorKind generatorKind,
-                                                                  FunctionAsyncKind asyncKind,
-                                                                  bool tryAnnexB,
-                                                                  Directives inheritedDirectives,
-                                                                  Directives* newDirectives)
+Parser<SyntaxParseHandler, CharT>::trySyntaxParseInnerFunction(Node pn, HandleFunction fun,
+                                                               uint32_t toStringStart,
+                                                               InHandling inHandling,
+                                                               YieldHandling yieldHandling,
+                                                               FunctionSyntaxKind kind,
+                                                               GeneratorKind generatorKind,
+                                                               FunctionAsyncKind asyncKind,
+                                                               bool tryAnnexB,
+                                                               Directives inheritedDirectives,
+                                                               Directives* newDirectives)
 {
     // This is already a syntax parser, so just parse the inner function.
     return innerFunction(pn, pc, fun, toStringStart, inHandling, yieldHandling, kind,
                          generatorKind, asyncKind, tryAnnexB, inheritedDirectives, newDirectives);
 }
 
 template <class ParseHandler, typename CharT>
+inline bool
+GeneralParser<ParseHandler, CharT>::trySyntaxParseInnerFunction(Node node, HandleFunction fun,
+                                                                uint32_t toStringStart,
+                                                                InHandling inHandling,
+                                                                YieldHandling yieldHandling,
+                                                                FunctionSyntaxKind kind,
+                                                                GeneratorKind generatorKind,
+                                                                FunctionAsyncKind asyncKind,
+                                                                bool tryAnnexB,
+                                                                Directives inheritedDirectives,
+                                                                Directives* newDirectives)
+{
+    return asFinalParser()->trySyntaxParseInnerFunction(node, fun, toStringStart, inHandling,
+                                                        yieldHandling, kind, generatorKind,
+                                                        asyncKind, tryAnnexB, inheritedDirectives,
+                                                        newDirectives);
+}
+
+template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::innerFunction(Node pn, ParseContext* outerpc, FunctionBox* funbox,
-                                           InHandling inHandling,
-                                           YieldHandling yieldHandling, FunctionSyntaxKind kind,
-                                           Directives* newDirectives)
+GeneralParser<ParseHandler, CharT>::innerFunctionForFunctionBox(Node pn, ParseContext* outerpc,
+                                                                FunctionBox* funbox,
+                                                                InHandling inHandling,
+                                                                YieldHandling yieldHandling,
+                                                                FunctionSyntaxKind kind,
+                                                                Directives* newDirectives)
 {
     // Note that it is possible for outerpc != this->pc, as we may be
     // attempting to syntax parse an inner function from an outer full
     // parser. In that case, outerpc is a SourceParseContext from the full parser
     // instead of the current top of the stack of the syntax parser.
 
     // Push a new ParseContext.
     SourceParseContext funpc(this, funbox, newDirectives);
@@ -3543,71 +3564,71 @@ Parser<ParseHandler, CharT>::innerFuncti
     if (!functionFormalParametersAndBody(inHandling, yieldHandling, pn, kind))
         return false;
 
     return leaveInnerFunction(outerpc);
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::innerFunction(Node pn, ParseContext* outerpc, HandleFunction fun,
-                                           uint32_t toStringStart, InHandling inHandling,
-                                           YieldHandling yieldHandling, FunctionSyntaxKind kind,
-                                           GeneratorKind generatorKind,
-                                           FunctionAsyncKind asyncKind, bool tryAnnexB,
-                                           Directives inheritedDirectives,
-                                           Directives* newDirectives)
+GeneralParser<ParseHandler, CharT>::innerFunction(Node pn, ParseContext* outerpc,
+                                                  HandleFunction fun, uint32_t toStringStart,
+                                                  InHandling inHandling,
+                                                  YieldHandling yieldHandling,
+                                                  FunctionSyntaxKind kind,
+                                                  GeneratorKind generatorKind,
+                                                  FunctionAsyncKind asyncKind, bool tryAnnexB,
+                                                  Directives inheritedDirectives,
+                                                  Directives* newDirectives)
 {
     // Note that it is possible for outerpc != this->pc, as we may be
     // attempting to syntax parse an inner function from an outer full
     // parser. In that case, outerpc is a SourceParseContext from the full parser
     // instead of the current top of the stack of the syntax parser.
 
     FunctionBox* funbox = newFunctionBox(pn, fun, toStringStart, inheritedDirectives,
                                          generatorKind, asyncKind);
     if (!funbox)
         return false;
     funbox->initWithEnclosingParseContext(outerpc, kind);
 
-    if (!innerFunction(pn, outerpc, funbox, inHandling, yieldHandling, kind, newDirectives))
+    if (!innerFunctionForFunctionBox(pn, outerpc, funbox, inHandling, yieldHandling, kind, newDirectives))
         return false;
 
     // Append possible Annex B function box only upon successfully parsing.
     if (tryAnnexB && !pc->innermostScope()->addPossibleAnnexBFunctionBox(pc, funbox))
         return false;
 
     return true;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::appendToCallSiteObj(Node callSiteObj)
+GeneralParser<ParseHandler, CharT>::appendToCallSiteObj(Node callSiteObj)
 {
     Node cookedNode = noSubstitutionTaggedTemplate();
     if (!cookedNode)
         return false;
 
     JSAtom* atom = tokenStream.getRawTemplateStringAtom();
     if (!atom)
         return false;
     Node rawNode = handler.newTemplateStringLiteral(atom, pos());
     if (!rawNode)
         return false;
 
     handler.addToCallSiteObject(callSiteObj, rawNode, cookedNode);
     return true;
 }
 
-template <>
+template <typename CharT>
 ParseNode*
-Parser<FullParseHandler, char16_t>::standaloneLazyFunction(HandleFunction fun,
-                                                           uint32_t toStringStart,
-                                                           bool strict,
-                                                           GeneratorKind generatorKind,
-                                                           FunctionAsyncKind asyncKind)
+Parser<FullParseHandler, CharT>::standaloneLazyFunction(HandleFunction fun, uint32_t toStringStart,
+                                                        bool strict, GeneratorKind generatorKind,
+                                                        FunctionAsyncKind asyncKind)
 {
     MOZ_ASSERT(checkOptionsCalled);
 
     Node pn = handler.newFunctionStatement(pos());
     if (!pn)
         return null();
 
     Directives directives(strict);
@@ -3654,36 +3675,37 @@ Parser<FullParseHandler, char16_t>::stan
     if (!FoldConstants(context, &pn, this))
         return null();
 
     return pn;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::functionFormalParametersAndBody(InHandling inHandling,
-                                                             YieldHandling yieldHandling,
-                                                             Node pn, FunctionSyntaxKind kind,
-                                                             const Maybe<uint32_t>& parameterListEnd /* = Nothing() */,
-                                                             bool isStandaloneFunction /* = false */)
+GeneralParser<ParseHandler, CharT>::functionFormalParametersAndBody(InHandling inHandling,
+                                                                    YieldHandling yieldHandling,
+                                                                    Node pn,
+                                                                    FunctionSyntaxKind kind,
+                                                                    const Maybe<uint32_t>& parameterListEnd /* = Nothing() */,
+                                                                    bool isStandaloneFunction /* = false */)
 {
     // Given a properly initialized parse context, try to parse an actual
     // function without concern for conversion to strict mode, use of lazy
     // parsing and such.
 
     FunctionBox* funbox = pc->functionBox();
     RootedFunction fun(context, funbox->function());
 
     // See below for an explanation why arrow function parameters and arrow
     // function bodies are parsed with different yield/await settings.
     {
         AwaitHandling awaitHandling = funbox->isAsync() || (kind == Arrow && awaitIsKeyword())
                                       ? AwaitIsKeyword
                                       : AwaitIsName;
-        AutoAwaitIsKeyword<Parser> awaitIsKeyword(this, awaitHandling);
+        AutoAwaitIsKeyword<GeneralParser<ParseHandler, CharT>> awaitIsKeyword(this, awaitHandling);
         if (!functionArguments(yieldHandling, kind, pn))
             return false;
     }
 
     Maybe<ParseContext::VarScope> varScope;
     if (funbox->hasParameterExprs) {
         varScope.emplace(this);
         if (!varScope->init(pc))
@@ -3748,17 +3770,18 @@ Parser<ParseHandler, CharT>::functionFor
     // whether the arrow function is enclosed in a generator function or not.
     // Whereas the |yield| in the function body is always parsed as a name.
     // The same goes when parsing |await| in arrow functions.
     YieldHandling bodyYieldHandling = GetYieldHandling(pc->generatorKind());
     AwaitHandling bodyAwaitHandling = GetAwaitHandling(pc->asyncKind());
     bool inheritedStrict = pc->sc()->strict();
     Node body;
     {
-        AutoAwaitIsKeyword<Parser> awaitIsKeyword(this, bodyAwaitHandling);
+        AutoAwaitIsKeyword<GeneralParser<ParseHandler, CharT>> awaitIsKeyword(this,
+                                                                              bodyAwaitHandling);
         body = functionBody(inHandling, bodyYieldHandling, kind, bodyType);
         if (!body)
             return false;
     }
 
     // Revalidate the function name when we transitioned to strict mode.
     if ((kind == Statement || IsFunctionExpression(kind)) &&
         fun->explicitName() &&
@@ -3814,19 +3837,20 @@ Parser<ParseHandler, CharT>::functionFor
     handler.setEndPosition(pn, pos().end);
     handler.setFunctionBody(pn, body);
 
     return true;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::functionStmt(uint32_t toStringStart, YieldHandling yieldHandling,
-                                          DefaultHandling defaultHandling,
-                                          FunctionAsyncKind asyncKind)
+GeneralParser<ParseHandler, CharT>::functionStmt(uint32_t toStringStart,
+                                                 YieldHandling yieldHandling,
+                                                 DefaultHandling defaultHandling,
+                                                 FunctionAsyncKind asyncKind)
 {
     MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_FUNCTION));
 
     // In sloppy mode, Annex B.3.2 allows labelled function declarations.
     // Otherwise it's a parse error.
     ParseContext::Statement* declaredInStmt = pc->innermostStatement();
     if (declaredInStmt && declaredInStmt->kind() == StatementKind::Label) {
         MOZ_ASSERT(!pc->sc()->strict(),
@@ -3904,24 +3928,25 @@ Parser<ParseHandler, CharT>::functionStm
 
     YieldHandling newYieldHandling = GetYieldHandling(generatorKind);
     return functionDefinition(pn, toStringStart, InAllowed, newYieldHandling, name, Statement,
                               generatorKind, asyncKind, tryAnnexB);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::functionExpr(uint32_t toStringStart,
-                                          ExpressionClosure expressionClosureHandling,
-                                          InvokedPrediction invoked,
-                                          FunctionAsyncKind asyncKind)
+GeneralParser<ParseHandler, CharT>::functionExpr(uint32_t toStringStart,
+                                                 ExpressionClosure expressionClosureHandling,
+                                                 InvokedPrediction invoked,
+                                                 FunctionAsyncKind asyncKind)
 {
     MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_FUNCTION));
 
-    AutoAwaitIsKeyword<Parser> awaitIsKeyword(this, GetAwaitHandling(asyncKind));
+    AutoAwaitIsKeyword<GeneralParser<ParseHandler, CharT>> awaitIsKeyword(this,
+                                                                          GetAwaitHandling(asyncKind));
     GeneratorKind generatorKind = GeneratorKind::NotGenerator;
     TokenKind tt;
     if (!tokenStream.getToken(&tt))
         return null();
 
     if (tt == TOK_MUL) {
         generatorKind = GeneratorKind::Generator;
         if (!tokenStream.getToken(&tt))
@@ -3967,19 +3992,19 @@ IsEscapeFreeStringLiteral(const TokenPos
     /*
      * If the string's length in the source code is its length as a value,
      * accounting for the quotes, then it must not contain any escape
      * sequences or line continuations.
      */
     return pos.begin + str->length() + 2 == pos.end;
 }
 
-template <>
+template <typename CharT>
 bool
-Parser<SyntaxParseHandler, char16_t>::asmJS(Node list)
+Parser<SyntaxParseHandler, CharT>::asmJS(Node list)
 {
     // While asm.js could technically be validated and compiled during syntax
     // parsing, we have no guarantee that some later JS wouldn't abort the
     // syntax parse and cause us to re-parse (and re-compile) the asm.js module.
     // For simplicity, unconditionally abort the syntax parse when "use asm" is
     // encountered so that asm.js is always validated/compiled exactly once
     // during a full parse.
     JS_ALWAYS_FALSE(abortIfSyntaxParser());
@@ -3987,19 +4012,19 @@ Parser<SyntaxParseHandler, char16_t>::as
     // Record that the current script source constains some AsmJS, to disable
     // any incremental encoder, as AsmJS cannot be encoded with XDR at the
     // moment.
     if (ss)
         ss->setContainsAsmJS();
     return false;
 }
 
-template <>
+template <typename CharT>
 bool
-Parser<FullParseHandler, char16_t>::asmJS(Node list)
+Parser<FullParseHandler, CharT>::asmJS(Node list)
 {
     // Disable syntax parsing in anything nested inside the asm.js module.
     disableSyntaxParser();
 
     // We should be encountering the "use asm" directive for the first time; if
     // the directive is already, we must have failed asm.js validation and we're
     // reparsing. In that case, don't try to validate again. A non-null
     // newDirectives means we're not in a normal function.
@@ -4025,16 +4050,23 @@ Parser<FullParseHandler, char16_t>::asmJ
     if (!validated) {
         pc->newDirectives->setAsmJS();
         return false;
     }
 
     return true;
 }
 
+template <class ParseHandler, typename CharT>
+inline bool
+GeneralParser<ParseHandler, CharT>::asmJS(Node list)
+{
+    return asFinalParser()->asmJS(list);
+}
+
 /*
  * Recognize Directive Prologue members and directives. Assuming |pn| is a
  * candidate for membership in a directive prologue, recognize directives and
  * set |pc|'s flags accordingly. If |pn| is indeed part of a prologue, set its
  * |pn_prologue| flag.
  *
  * Note that the following is a strict mode function:
  *
@@ -4046,17 +4078,18 @@ Parser<FullParseHandler, char16_t>::asmJ
  * }
  *
  * That is, even though "use\x20loose" can never be a directive, now or in the
  * future (because of the hex escape), the Directive Prologue extends through it
  * to the "use strict" statement, which is indeed a directive.
  */
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::maybeParseDirective(Node list, Node possibleDirective, bool* cont)
+GeneralParser<ParseHandler, CharT>::maybeParseDirective(Node list, Node possibleDirective,
+                                                        bool* cont)
 {
     TokenPos directivePos;
     JSAtom* directive = handler.isStringExprStatement(possibleDirective, &directivePos);
 
     *cont = !!directive;
     if (!*cont)
         return true;
 
@@ -4109,17 +4142,17 @@ Parser<ParseHandler, CharT>::maybeParseD
             return warningAt(directivePos.begin, JSMSG_USE_ASM_DIRECTIVE_FAIL);
         }
     }
     return true;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::statementList(YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::statementList(YieldHandling yieldHandling)
 {
     if (!CheckRecursionLimit(context))
         return null();
 
     Node pn = handler.newStatementList(pos());
     if (!pn)
         return null();
 
@@ -4175,17 +4208,17 @@ Parser<ParseHandler, CharT>::statementLi
         handler.addStatementToList(pn, next);
     }
 
     return pn;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::condition(InHandling inHandling, YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::condition(InHandling inHandling, YieldHandling yieldHandling)
 {
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
 
     Node pn = exprInParens(inHandling, yieldHandling, TripledotProhibited);
     if (!pn)
         return null();
 
     MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_COND);
@@ -4195,18 +4228,18 @@ Parser<ParseHandler, CharT>::condition(I
         if (!extraWarning(JSMSG_EQUAL_AS_ASSIGN))
             return null();
     }
     return pn;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::matchLabel(YieldHandling yieldHandling,
-                                        MutableHandle<PropertyName*> label)
+GeneralParser<ParseHandler, CharT>::matchLabel(YieldHandling yieldHandling,
+                                               MutableHandle<PropertyName*> label)
 {
     TokenKind tt = TOK_EOF;
     if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
         return false;
 
     if (TokenKindIsPossibleIdentifier(tt)) {
         tokenStream.consumeKnownToken(tt, TokenStream::Operand);
 
@@ -4215,173 +4248,174 @@ Parser<ParseHandler, CharT>::matchLabel(
             return false;
     } else {
         label.set(nullptr);
     }
     return true;
 }
 
 template <class ParseHandler, typename CharT>
-Parser<ParseHandler, CharT>::PossibleError::PossibleError(Parser<ParseHandler, CharT>& parser)
+GeneralParser<ParseHandler, CharT>::PossibleError::PossibleError(GeneralParser<ParseHandler, CharT>& parser)
   : parser_(parser)
 {}
 
 template <class ParseHandler, typename CharT>
-typename Parser<ParseHandler, CharT>::PossibleError::Error&
-Parser<ParseHandler, CharT>::PossibleError::error(ErrorKind kind)
+typename GeneralParser<ParseHandler, CharT>::PossibleError::Error&
+GeneralParser<ParseHandler, CharT>::PossibleError::error(ErrorKind kind)
 {
     if (kind == ErrorKind::Expression)
         return exprError_;
     if (kind == ErrorKind::Destructuring)
         return destructuringError_;
     MOZ_ASSERT(kind == ErrorKind::DestructuringWarning);
     return destructuringWarning_;
 }
 
 template <class ParseHandler, typename CharT>
 void
-Parser<ParseHandler, CharT>::PossibleError::setResolved(ErrorKind kind)
+GeneralParser<ParseHandler, CharT>::PossibleError::setResolved(ErrorKind kind)
 {
     error(kind).state_ = ErrorState::None;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::PossibleError::hasError(ErrorKind kind)
+GeneralParser<ParseHandler, CharT>::PossibleError::hasError(ErrorKind kind)
 {
     return error(kind).state_ == ErrorState::Pending;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::PossibleError::hasPendingDestructuringError()
+GeneralParser<ParseHandler, CharT>::PossibleError::hasPendingDestructuringError()
 {
     return hasError(ErrorKind::Destructuring);
 }
 
 template <class ParseHandler, typename CharT>
 void
-Parser<ParseHandler, CharT>::PossibleError::setPending(ErrorKind kind, const TokenPos& pos,
-                                                       unsigned errorNumber)
+GeneralParser<ParseHandler, CharT>::PossibleError::setPending(ErrorKind kind, const TokenPos& pos,
+                                                              unsigned errorNumber)
 {
     // Don't overwrite a previously recorded error.
     if (hasError(kind))
         return;
 
     // If we report an error later, we'll do it from the position where we set
     // the state to pending.
     Error& err = error(kind);
     err.offset_ = pos.begin;
     err.errorNumber_ = errorNumber;
     err.state_ = ErrorState::Pending;
 }
 
 template <class ParseHandler, typename CharT>
 void
-Parser<ParseHandler, CharT>::PossibleError::setPendingDestructuringErrorAt(const TokenPos& pos,
-                                                                           unsigned errorNumber)
+GeneralParser<ParseHandler, CharT>::PossibleError::setPendingDestructuringErrorAt(const TokenPos& pos,
+                                                                                  unsigned errorNumber)
 {
     setPending(ErrorKind::Destructuring, pos, errorNumber);
 }
 
 template <class ParseHandler, typename CharT>
 void
-Parser<ParseHandler, CharT>::PossibleError::setPendingDestructuringWarningAt(const TokenPos& pos,
-                                                                             unsigned errorNumber)
+GeneralParser<ParseHandler, CharT>::PossibleError::setPendingDestructuringWarningAt(const TokenPos& pos,
+                                                                                    unsigned errorNumber)
 {
     setPending(ErrorKind::DestructuringWarning, pos, errorNumber);
 }
 
 template <class ParseHandler, typename CharT>
 void
-Parser<ParseHandler, CharT>::PossibleError::setPendingExpressionErrorAt(const TokenPos& pos,
-                                                                        unsigned errorNumber)
+GeneralParser<ParseHandler, CharT>::PossibleError::setPendingExpressionErrorAt(const TokenPos& pos,
+                                                                               unsigned errorNumber)
 {
     setPending(ErrorKind::Expression, pos, errorNumber);
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::PossibleError::checkForError(ErrorKind kind)
+GeneralParser<ParseHandler, CharT>::PossibleError::checkForError(ErrorKind kind)
 {
     if (!hasError(kind))
         return true;
 
     Error& err = error(kind);
     parser_.errorAt(err.offset_, err.errorNumber_);
     return false;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::PossibleError::checkForWarning(ErrorKind kind)
+GeneralParser<ParseHandler, CharT>::PossibleError::checkForWarning(ErrorKind kind)
 {
     if (!hasError(kind))
         return true;
 
     Error& err = error(kind);
     return parser_.extraWarningAt(err.offset_, err.errorNumber_);
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::PossibleError::checkForDestructuringErrorOrWarning()
+GeneralParser<ParseHandler, CharT>::PossibleError::checkForDestructuringErrorOrWarning()
 {
     // Clear pending expression error, because we're definitely not in an
     // expression context.
     setResolved(ErrorKind::Expression);
 
     // Report any pending destructuring error or warning.
     return checkForError(ErrorKind::Destructuring) &&
            checkForWarning(ErrorKind::DestructuringWarning);
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::PossibleError::checkForExpressionError()
+GeneralParser<ParseHandler, CharT>::PossibleError::checkForExpressionError()
 {
     // Clear pending destructuring error or warning, because we're definitely
     // not in a destructuring context.
     setResolved(ErrorKind::Destructuring);
     setResolved(ErrorKind::DestructuringWarning);
 
     // Report any pending expression error.
     return checkForError(ErrorKind::Expression);
 }
 
 template <class ParseHandler, typename CharT>
 void
-Parser<ParseHandler, CharT>::PossibleError::transferErrorTo(ErrorKind kind, PossibleError* other)
+GeneralParser<ParseHandler, CharT>::PossibleError::transferErrorTo(ErrorKind kind,
+                                                                   PossibleError* other)
 {
     if (hasError(kind) && !other->hasError(kind)) {
         Error& err = error(kind);
         Error& otherErr = other->error(kind);
         otherErr.offset_ = err.offset_;
         otherErr.errorNumber_ = err.errorNumber_;
         otherErr.state_ = err.state_;
     }
 }
 
 template <class ParseHandler, typename CharT>
 void
-Parser<ParseHandler, CharT>::PossibleError::transferErrorsTo(PossibleError* other)
+GeneralParser<ParseHandler, CharT>::PossibleError::transferErrorsTo(PossibleError* other)
 {
     MOZ_ASSERT(other);
     MOZ_ASSERT(this != other);
     MOZ_ASSERT(&parser_ == &other->parser_,
                "Can't transfer fields to an instance which belongs to a different parser");
 
     transferErrorTo(ErrorKind::Destructuring, other);
     transferErrorTo(ErrorKind::Expression, other);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::bindingInitializer(Node lhs, DeclarationKind kind,
-                                                YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::bindingInitializer(Node lhs, DeclarationKind kind,
+                                                       YieldHandling yieldHandling)
 {
     MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_ASSIGN));
 
     if (kind == DeclarationKind::FormalParameter)
         pc->functionBox()->hasParameterExprs = true;
 
     Node rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
     if (!rhs)
@@ -4396,33 +4430,35 @@ Parser<ParseHandler, CharT>::bindingInit
     if (foldConstants && !FoldConstants(context, &assign, this))
         return null();
 
     return assign;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::bindingIdentifier(DeclarationKind kind, YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::bindingIdentifier(DeclarationKind kind,
+                                                      YieldHandling yieldHandling)
 {
     RootedPropertyName name(context, bindingIdentifier(yieldHandling));
     if (!name)
         return null();
 
     Node binding = newName(name);
     if (!binding || !noteDeclaredName(name, kind, pos()))
         return null();
 
     return binding;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::bindingIdentifierOrPattern(DeclarationKind kind,
-                                                        YieldHandling yieldHandling, TokenKind tt)
+GeneralParser<ParseHandler, CharT>::bindingIdentifierOrPattern(DeclarationKind kind,
+                                                               YieldHandling yieldHandling,
+                                                               TokenKind tt)
 {
     if (tt == TOK_LB)
         return arrayBindingPattern(kind, yieldHandling);
 
     if (tt == TOK_LC)
         return objectBindingPattern(kind, yieldHandling);
 
     if (!TokenKindIsPossibleIdentifierName(tt)) {
@@ -4430,18 +4466,18 @@ Parser<ParseHandler, CharT>::bindingIden
         return null();
     }
 
     return bindingIdentifier(kind, yieldHandling);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::objectBindingPattern(DeclarationKind kind,
-                                                  YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::objectBindingPattern(DeclarationKind kind,
+                                                         YieldHandling yieldHandling)
 {
     MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LC));
 
     if (!CheckRecursionLimit(context))
         return null();
 
     uint32_t begin = pos().begin;
     Node literal = handler.newObjectLiteral(begin);
@@ -4558,17 +4594,18 @@ Parser<ParseHandler, CharT>::objectBindi
                                                           JSMSG_CURLY_OPENED, begin));
 
     handler.setEndPosition(literal, pos().end);
     return literal;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::arrayBindingPattern(DeclarationKind kind, YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::arrayBindingPattern(DeclarationKind kind,
+                                                        YieldHandling yieldHandling)
 {
     MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LB));
 
     if (!CheckRecursionLimit(context))
         return null();
 
     uint32_t begin = pos().begin;
     Node literal = handler.newArrayLiteral(begin);
@@ -4646,33 +4683,33 @@ Parser<ParseHandler, CharT>::arrayBindin
                                                            JSMSG_BRACKET_OPENED, begin));
 
     handler.setEndPosition(literal, pos().end);
     return literal;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::destructuringDeclaration(DeclarationKind kind,
-                                                      YieldHandling yieldHandling,
-                                                      TokenKind tt)
+GeneralParser<ParseHandler, CharT>::destructuringDeclaration(DeclarationKind kind,
+                                                             YieldHandling yieldHandling,
+                                                             TokenKind tt)
 {
     MOZ_ASSERT(anyChars.isCurrentTokenType(tt));
     MOZ_ASSERT(tt == TOK_LB || tt == TOK_LC);
 
     return tt == TOK_LB
            ? arrayBindingPattern(kind, yieldHandling)
            : objectBindingPattern(kind, yieldHandling);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::destructuringDeclarationWithoutYieldOrAwait(DeclarationKind kind,
-                                                                         YieldHandling yieldHandling,
-                                                                         TokenKind tt)
+GeneralParser<ParseHandler, CharT>::destructuringDeclarationWithoutYieldOrAwait(DeclarationKind kind,
+                                                                                YieldHandling yieldHandling,
+                                                                                TokenKind tt)
 {
     uint32_t startYieldOffset = pc->lastYieldOffset;
     uint32_t startAwaitOffset = pc->lastAwaitOffset;
     Node res = destructuringDeclaration(kind, yieldHandling, tt);
     if (res) {
         if (pc->lastYieldOffset != startYieldOffset) {
             errorAt(pc->lastYieldOffset, JSMSG_YIELD_IN_DEFAULT);
             return null();
@@ -4682,17 +4719,18 @@ Parser<ParseHandler, CharT>::destructuri
             return null();
         }
     }
     return res;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::blockStatement(YieldHandling yieldHandling, unsigned errorNumber)
+GeneralParser<ParseHandler, CharT>::blockStatement(YieldHandling yieldHandling,
+                                                   unsigned errorNumber)
 {
     MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LC));
     uint32_t openedPos = pos().begin;
 
     ParseContext::Statement stmt(pc, StatementKind::Block);
     ParseContext::Scope scope(this);
     if (!scope.init(pc))
         return null();
@@ -4705,33 +4743,33 @@ Parser<ParseHandler, CharT>::blockStatem
                                      reportMissingClosing(errorNumber, JSMSG_CURLY_OPENED,
                                                           openedPos));
 
     return finishLexicalScope(scope, list);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::expressionAfterForInOrOf(ParseNodeKind forHeadKind,
-                                                      YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::expressionAfterForInOrOf(ParseNodeKind forHeadKind,
+                                                             YieldHandling yieldHandling)
 {
     MOZ_ASSERT(forHeadKind == PNK_FORIN || forHeadKind == PNK_FOROF);
     Node pn = forHeadKind == PNK_FOROF
            ? assignExpr(InAllowed, yieldHandling, TripledotProhibited)
            : expr(InAllowed, yieldHandling, TripledotProhibited);
     return pn;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::declarationPattern(DeclarationKind declKind, TokenKind tt,
-                                                bool initialDeclaration,
-                                                YieldHandling yieldHandling,
-                                                ParseNodeKind* forHeadKind,
-                                                Node* forInOrOfExpression)
+GeneralParser<ParseHandler, CharT>::declarationPattern(DeclarationKind declKind, TokenKind tt,
+                                                       bool initialDeclaration,
+                                                       YieldHandling yieldHandling,
+                                                       ParseNodeKind* forHeadKind,
+                                                       Node* forInOrOfExpression)
 {
     MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LB) ||
                anyChars.isCurrentTokenType(TOK_LC));
 
     Node pattern = destructuringDeclaration(declKind, yieldHandling, tt);
     if (!pattern)
         return null();
 
@@ -4770,22 +4808,22 @@ Parser<ParseHandler, CharT>::declaration
 
     handler.checkAndSetIsDirectRHSAnonFunction(init);
 
     return handler.newAssignment(PNK_ASSIGN, pattern, init);
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::initializerInNameDeclaration(Node binding,
-                                                          DeclarationKind declKind,
-                                                          bool initialDeclaration,
-                                                          YieldHandling yieldHandling,
-                                                          ParseNodeKind* forHeadKind,
-                                                          Node* forInOrOfExpression)
+GeneralParser<ParseHandler, CharT>::initializerInNameDeclaration(Node binding,
+                                                                 DeclarationKind declKind,
+                                                                 bool initialDeclaration,
+                                                                 YieldHandling yieldHandling,
+                                                                 ParseNodeKind* forHeadKind,
+                                                                 Node* forInOrOfExpression)
 {
     MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_ASSIGN));
 
     uint32_t initializerOffset;
     if (!tokenStream.peekOffset(&initializerOffset, TokenStream::Operand))
         return false;
 
     Node initializer = assignExpr(forHeadKind ? InProhibited : InAllowed,
@@ -4831,19 +4869,21 @@ Parser<ParseHandler, CharT>::initializer
         }
     }
 
     return handler.finishInitializerAssignment(binding, initializer);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::declarationName(DeclarationKind declKind, TokenKind tt,
-                                             bool initialDeclaration, YieldHandling yieldHandling,
-                                             ParseNodeKind* forHeadKind, Node* forInOrOfExpression)
+GeneralParser<ParseHandler, CharT>::declarationName(DeclarationKind declKind, TokenKind tt,
+                                                    bool initialDeclaration,
+                                                    YieldHandling yieldHandling,
+                                                    ParseNodeKind* forHeadKind,
+                                                    Node* forInOrOfExpression)
 {
     // Anything other than possible identifier is an error.
     if (!TokenKindIsPossibleIdentifier(tt)) {
         error(JSMSG_NO_VARIABLE_NAME);
         return null();
     }
 
     RootedPropertyName name(context, bindingIdentifier(yieldHandling));
@@ -4911,20 +4951,20 @@ Parser<ParseHandler, CharT>::declaration
     if (!noteDeclaredName(name, declKind, namePos))
         return null();
 
     return binding;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::declarationList(YieldHandling yieldHandling,
-                                             ParseNodeKind kind,
-                                             ParseNodeKind* forHeadKind /* = nullptr */,
-                                             Node* forInOrOfExpression /* = nullptr */)
+GeneralParser<ParseHandler, CharT>::declarationList(YieldHandling yieldHandling,
+                                                    ParseNodeKind kind,
+                                                    ParseNodeKind* forHeadKind /* = nullptr */,
+                                                    Node* forInOrOfExpression /* = nullptr */)
 {
     MOZ_ASSERT(kind == PNK_VAR || kind == PNK_LET || kind == PNK_CONST);
 
     DeclarationKind declKind;
     switch (kind) {
       case PNK_VAR:
         declKind = DeclarationKind::Var;
         break;
@@ -4973,17 +5013,18 @@ Parser<ParseHandler, CharT>::declaration
             return null();
     } while (moreDeclarations);
 
     return decl;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::lexicalDeclaration(YieldHandling yieldHandling, DeclarationKind kind)
+GeneralParser<ParseHandler, CharT>::lexicalDeclaration(YieldHandling yieldHandling,
+                                                       DeclarationKind kind)
 {
     MOZ_ASSERT(kind == DeclarationKind::Const || kind == DeclarationKind::Let);
 
     /*
      * Parse body-level lets without a new block object. ES6 specs
      * that an execution environment's initial lexical environment
      * is the VariableEnvironment, i.e., body-level lets are in
      * the same environment record as vars.
@@ -4996,19 +5037,19 @@ Parser<ParseHandler, CharT>::lexicalDecl
     Node decl = declarationList(yieldHandling,
                                 kind == DeclarationKind::Const ? PNK_CONST : PNK_LET);
     if (!decl || !matchOrInsertSemicolon())
         return null();
 
     return decl;
 }
 
-template <>
+template <typename CharT>
 bool
-Parser<FullParseHandler, char16_t>::namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet)
+Parser<FullParseHandler, CharT>::namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet)
 {
     if (tt == TOK_LC) {
         while (true) {
             // Handle the forms |import {} from 'a'| and
             // |import { ..., } from 'a'| (where ... is non empty), by
             // escaping the loop early if the next token is }.
             if (!tokenStream.getToken(&tt))
                 return false;
@@ -5113,28 +5154,19 @@ Parser<FullParseHandler, char16_t>::name
             return false;
 
         handler.addList(importSpecSet, importSpec);
     }
 
     return true;
 }
 
-template<>
-bool
-Parser<SyntaxParseHandler, char16_t>::namedImportsOrNamespaceImport(TokenKind tt,
-                                                                    Node importSpecSet)
-{
-    MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
-    return false;
-}
-
-template<>
+template<typename CharT>
 ParseNode*
-Parser<FullParseHandler, char16_t>::importDeclaration()
+Parser<FullParseHandler, CharT>::importDeclaration()
 {
     MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_IMPORT));
 
     if (!pc->atModuleLevel()) {
         error(JSMSG_IMPORT_DECL_AT_TOP_LEVEL);
         return null();
     }
 
@@ -5217,151 +5249,206 @@ Parser<FullParseHandler, char16_t>::impo
     ParseNode* node =
         handler.newImportDeclaration(importSpecSet, moduleSpec, TokenPos(begin, pos().end));
     if (!node || !pc->sc()->asModuleContext()->builder.processImport(node))
         return null();
 
     return node;
 }
 
-template<>
-SyntaxParseHandler::Node
-Parser<SyntaxParseHandler, char16_t>::importDeclaration()
+template<typename CharT>
+inline SyntaxParseHandler::Node
+Parser<SyntaxParseHandler, CharT>::importDeclaration()
 {
     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
     return SyntaxParseHandler::NodeFailure;
 }
 
-template<>
+template <class ParseHandler, typename CharT>
+inline typename ParseHandler::Node
+GeneralParser<ParseHandler, CharT>::importDeclaration()
+{
+    return asFinalParser()->importDeclaration();
+}
+
+template<typename CharT>
 bool
-Parser<FullParseHandler, char16_t>::checkExportedName(JSAtom* exportName)
+Parser<FullParseHandler, CharT>::checkExportedName(JSAtom* exportName)
 {
     if (!pc->sc()->asModuleContext()->builder.hasExportedName(exportName))
         return true;
 
     JSAutoByteString str;
     if (!AtomToPrintableString(context, exportName, &str))
         return false;
 
     error(JSMSG_DUPLICATE_EXPORT_NAME, str.ptr());
     return false;
 }
 
-template<>
-bool
-Parser<SyntaxParseHandler, char16_t>::checkExportedName(JSAtom* exportName)
+template<typename CharT>
+inline bool
+Parser<SyntaxParseHandler, CharT>::checkExportedName(JSAtom* exportName)
 {
     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
     return false;
 }
 
-template<>
+template<class ParseHandler, typename CharT>
+inline bool
+GeneralParser<ParseHandler, CharT>::checkExportedName(JSAtom* exportName)
+{
+    return asFinalParser()->checkExportedName(exportName);
+}
+
+template<typename CharT>
 bool
-Parser<FullParseHandler, char16_t>::checkExportedNamesForDeclaration(ParseNode* node)
+Parser<FullParseHandler, CharT>::checkExportedNamesForDeclaration(ParseNode* node)
 {
     MOZ_ASSERT(node->isArity(PN_LIST));
     for (ParseNode* binding = node->pn_head; binding; binding = binding->pn_next) {
         if (binding->isKind(PNK_ASSIGN))
             binding = binding->pn_left;
         MOZ_ASSERT(binding->isKind(PNK_NAME));
         if (!checkExportedName(binding->pn_atom))
             return false;
     }
 
     return true;
 }
 
-template<>
-bool
-Parser<SyntaxParseHandler, char16_t>::checkExportedNamesForDeclaration(Node node)
+template<typename CharT>
+inline bool
+Parser<SyntaxParseHandler, CharT>::checkExportedNamesForDeclaration(Node node)
 {
     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
     return false;
 }
 
-template<>
-bool
-Parser<FullParseHandler, char16_t>::checkExportedNameForClause(ParseNode* node)
+template<class ParseHandler, typename CharT>
+inline bool
+GeneralParser<ParseHandler, CharT>::checkExportedNamesForDeclaration(Node node)
+{
+    return asFinalParser()->checkExportedNamesForDeclaration(node);
+}
+
+template<typename CharT>
+inline bool
+Parser<FullParseHandler, CharT>::checkExportedNameForClause(ParseNode* node)
 {
     return checkExportedName(node->pn_atom);
 }
 
-template<>
-bool
-Parser<SyntaxParseHandler, char16_t>::checkExportedNameForClause(Node node)
+template<typename CharT>
+inline bool
+Parser<SyntaxParseHandler, CharT>::checkExportedNameForClause(Node node)
 {
     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
     return false;
 }
 
-template<>
+template<class ParseHandler, typename CharT>
+inline bool
+GeneralParser<ParseHandler, CharT>::checkExportedNameForClause(Node node)
+{
+    return asFinalParser()->checkExportedNameForClause(node);
+}
+
+template<typename CharT>
 bool
-Parser<FullParseHandler, char16_t>::checkExportedNameForFunction(ParseNode* node)
+Parser<FullParseHandler, CharT>::checkExportedNameForFunction(ParseNode* node)
 {
     return checkExportedName(node->pn_funbox->function()->explicitName());
 }
 
-template<>
-bool
-Parser<SyntaxParseHandler, char16_t>::checkExportedNameForFunction(Node node)
+template<typename CharT>
+inline bool
+Parser<SyntaxParseHandler, CharT>::checkExportedNameForFunction(Node node)
 {
     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
     return false;
 }
 
-template<>
+template<class ParseHandler, typename CharT>
+inline bool
+GeneralParser<ParseHandler, CharT>::checkExportedNameForFunction(Node node)
+{
+    return asFinalParser()->checkExportedNameForFunction(node);
+}
+
+template<typename CharT>
 bool
-Parser<FullParseHandler, char16_t>::checkExportedNameForClass(ParseNode* node)
+Parser<FullParseHandler, CharT>::checkExportedNameForClass(ParseNode* node)
 {
     const ClassNode& cls = node->as<ClassNode>();
     MOZ_ASSERT(cls.names());
     return checkExportedName(cls.names()->innerBinding()->pn_atom);
 }
 
-template<>
+template<typename CharT>
+inline bool
+Parser<SyntaxParseHandler, CharT>::checkExportedNameForClass(Node node)
+{
+    MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
+    return false;
+}
+
+template<class ParseHandler, typename CharT>
+inline bool
+GeneralParser<ParseHandler, CharT>::checkExportedNameForClass(Node node)
+{
+    return asFinalParser()->checkExportedNameForClass(node);
+}
+template<typename CharT>
 bool
-Parser<SyntaxParseHandler, char16_t>::checkExportedNameForClass(Node node)
+Parser<FullParseHandler, CharT>::processExport(ParseNode* node)
+{
+    return pc->sc()->asModuleContext()->builder.processExport(node);
+}
+
+template<typename CharT>
+inline bool
+Parser<SyntaxParseHandler, CharT>::processExport(Node node)
 {
     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
     return false;
 }
 
-template<>
+template<class ParseHandler, typename CharT>
+inline bool
+GeneralParser<ParseHandler, CharT>::processExport(Node node)
+{
+    return asFinalParser()->processExport(node);
+}
+
+template<typename CharT>
 bool
-Parser<FullParseHandler, char16_t>::processExport(ParseNode* node)
-{
-    return pc->sc()->asModuleContext()->builder.processExport(node);
-}
-
-template<>
-bool
-Parser<SyntaxParseHandler, char16_t>::processExport(Node node)
+Parser<FullParseHandler, CharT>::processExportFrom(ParseNode* node)
+{
+    return pc->sc()->asModuleContext()->builder.processExportFrom(node);
+}
+
+template<typename CharT>
+inline bool
+Parser<SyntaxParseHandler, CharT>::processExportFrom(Node node)
 {
     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
     return false;
 }
 
-template<>
-bool
-Parser<FullParseHandler, char16_t>::processExportFrom(ParseNode* node)
-{
-    return pc->sc()->asModuleContext()->builder.processExportFrom(node);
-}
-
-template<>
-bool
-Parser<SyntaxParseHandler, char16_t>::processExportFrom(Node node)
-{
-    MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
-    return false;
+template<class ParseHandler, typename CharT>
+inline bool
+GeneralParser<ParseHandler, CharT>::processExportFrom(Node node)
+{
+    return asFinalParser()->processExportFrom(node);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::exportFrom(uint32_t begin, Node specList)
+GeneralParser<ParseHandler, CharT>::exportFrom(uint32_t begin, Node specList)
 {
     if (!abortIfSyntaxParser())
         return null();
 
     MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_FROM));
 
     if (!abortIfSyntaxParser())
         return null();
@@ -5382,17 +5469,17 @@ Parser<ParseHandler, CharT>::exportFrom(
     if (!processExportFrom(node))
         return null();
 
     return node;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::exportBatch(uint32_t begin)
+GeneralParser<ParseHandler, CharT>::exportBatch(uint32_t begin)
 {
     if (!abortIfSyntaxParser())
         return null();
 
     MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_MUL));
 
     Node kid = handler.newList(PNK_EXPORT_SPEC_LIST, pos());
     if (!kid)
@@ -5406,44 +5493,51 @@ Parser<ParseHandler, CharT>::exportBatch
 
     handler.addList(kid, exportSpec);
 
     MUST_MATCH_TOKEN(TOK_FROM, JSMSG_FROM_AFTER_EXPORT_STAR);
 
     return exportFrom(begin, kid);
 }
 
-template<>
+template<typename CharT>
 bool
-Parser<FullParseHandler, char16_t>::checkLocalExportNames(ParseNode* node)
+Parser<FullParseHandler, CharT>::checkLocalExportNames(ParseNode* node)
 {
     // ES 2017 draft 15.2.3.1.
     for (ParseNode* next = node->pn_head; next; next = next->pn_next) {
         ParseNode* name = next->pn_left;
         MOZ_ASSERT(name->isKind(PNK_NAME));
 
         RootedPropertyName ident(context, name->pn_atom->asPropertyName());
         if (!checkLocalExportName(ident, name->pn_pos.begin))
             return false;
     }
 
     return true;
 }
 
-template<>
+template<typename CharT>
 bool
-Parser<SyntaxParseHandler, char16_t>::checkLocalExportNames(Node node)
+Parser<SyntaxParseHandler, CharT>::checkLocalExportNames(Node node)
 {
     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
     return false;
 }
 
+template<class ParseHandler, typename CharT>
+inline bool
+GeneralParser<ParseHandler, CharT>::checkLocalExportNames(Node node)
+{
+    return asFinalParser()->checkLocalExportNames(node);
+}
+
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::exportClause(uint32_t begin)
+GeneralParser<ParseHandler, CharT>::exportClause(uint32_t begin)
 {
     if (!abortIfSyntaxParser())
         return null();
 
     MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LC));
 
     Node kid = handler.newList(PNK_EXPORT_SPEC_LIST, pos());
     if (!kid)
@@ -5533,17 +5627,17 @@ Parser<ParseHandler, CharT>::exportClaus
     if (!processExport(node))
         return null();
 
     return node;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::exportVariableStatement(uint32_t begin)
+GeneralParser<ParseHandler, CharT>::exportVariableStatement(uint32_t begin)
 {
     if (!abortIfSyntaxParser())
         return null();
 
     MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_VAR));
 
     Node kid = declarationList(YieldIsName, PNK_VAR);
     if (!kid)
@@ -5560,18 +5654,19 @@ Parser<ParseHandler, CharT>::exportVaria
     if (!processExport(node))
         return null();
 
     return node;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::exportFunctionDeclaration(uint32_t begin, uint32_t toStringStart,
-                                                       FunctionAsyncKind asyncKind /* = SyncFunction */)
+GeneralParser<ParseHandler, CharT>::exportFunctionDeclaration(uint32_t begin,
+                                                              uint32_t toStringStart,
+                                                              FunctionAsyncKind asyncKind /* = SyncFunction */)
 {
     if (!abortIfSyntaxParser())
         return null();
 
     MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_FUNCTION));
 
     Node kid = functionStmt(toStringStart, YieldIsName, NameRequired, asyncKind);
     if (!kid)
@@ -5587,17 +5682,17 @@ Parser<ParseHandler, CharT>::exportFunct
     if (!processExport(node))
         return null();
 
     return node;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::exportClassDeclaration(uint32_t begin)
+GeneralParser<ParseHandler, CharT>::exportClassDeclaration(uint32_t begin)
 {
     if (!abortIfSyntaxParser())
         return null();
 
     MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_CLASS));
 
     Node kid = classDefinition(YieldIsName, ClassStatement, NameRequired);
     if (!kid)
@@ -5613,17 +5708,17 @@ Parser<ParseHandler, CharT>::exportClass
     if (!processExport(node))
         return null();
 
     return node;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::exportLexicalDeclaration(uint32_t begin, DeclarationKind kind)
+GeneralParser<ParseHandler, CharT>::exportLexicalDeclaration(uint32_t begin, DeclarationKind kind)
 {
     if (!abortIfSyntaxParser())
         return null();
 
     MOZ_ASSERT(kind == DeclarationKind::Const || kind == DeclarationKind::Let);
     MOZ_ASSERT_IF(kind == DeclarationKind::Const, anyChars.isCurrentTokenType(TOK_CONST));
     MOZ_ASSERT_IF(kind == DeclarationKind::Let, anyChars.isCurrentTokenType(TOK_LET));
 
@@ -5640,19 +5735,19 @@ Parser<ParseHandler, CharT>::exportLexic
     if (!processExport(node))
         return null();
 
     return node;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::exportDefaultFunctionDeclaration(uint32_t begin,
-                                                              uint32_t toStringStart,
-                                                              FunctionAsyncKind asyncKind /* = SyncFunction */)
+GeneralParser<ParseHandler, CharT>::exportDefaultFunctionDeclaration(uint32_t begin,
+                                                                     uint32_t toStringStart,
+                                                                     FunctionAsyncKind asyncKind /* = SyncFunction */)
 {
     if (!abortIfSyntaxParser())
         return null();
 
     MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_FUNCTION));
 
     Node kid = functionStmt(toStringStart, YieldIsName, AllowDefaultName, asyncKind);
     if (!kid)
@@ -5665,17 +5760,17 @@ Parser<ParseHandler, CharT>::exportDefau
     if (!processExport(node))
         return null();
 
     return node;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::exportDefaultClassDeclaration(uint32_t begin)
+GeneralParser<ParseHandler, CharT>::exportDefaultClassDeclaration(uint32_t begin)
 {
     if (!abortIfSyntaxParser())
         return null();
 
     MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_CLASS));
 
     Node kid = classDefinition(YieldIsName, ClassStatement, AllowDefaultName);
     if (!kid)
@@ -5688,17 +5783,17 @@ Parser<ParseHandler, CharT>::exportDefau
     if (!processExport(node))
         return null();
 
     return node;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::exportDefaultAssignExpr(uint32_t begin)
+GeneralParser<ParseHandler, CharT>::exportDefaultAssignExpr(uint32_t begin)
 {
     if (!abortIfSyntaxParser())
         return null();
 
     HandlePropertyName name = context->names().default_;
     Node nameNode = newName(name);
     if (!nameNode)
         return null();
@@ -5721,17 +5816,17 @@ Parser<ParseHandler, CharT>::exportDefau
     if (!processExport(node))
         return null();
 
     return node;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::exportDefault(uint32_t begin)
+GeneralParser<ParseHandler, CharT>::exportDefault(uint32_t begin)
 {
     if (!abortIfSyntaxParser())
         return null();
 
     MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_DEFAULT));
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
@@ -5766,17 +5861,17 @@ Parser<ParseHandler, CharT>::exportDefau
       default:
         anyChars.ungetToken();
         return exportDefaultAssignExpr(begin);
     }
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::exportDeclaration()
+GeneralParser<ParseHandler, CharT>::exportDeclaration()
 {
     if (!abortIfSyntaxParser())
         return null();
 
     MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_EXPORT));
 
     if (!pc->atModuleLevel()) {
         error(JSMSG_EXPORT_DECL_AT_TOP_LEVEL);
@@ -5832,32 +5927,32 @@ Parser<ParseHandler, CharT>::exportDecla
       default:
         error(JSMSG_DECLARATION_AFTER_EXPORT);
         return null();
     }
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::expressionStatement(YieldHandling yieldHandling,
-                                                 InvokedPrediction invoked)
+GeneralParser<ParseHandler, CharT>::expressionStatement(YieldHandling yieldHandling,
+                                                        InvokedPrediction invoked)
 {
     anyChars.ungetToken();
     Node pnexpr = expr(InAllowed, yieldHandling, TripledotProhibited,
                        /* possibleError = */ nullptr, invoked);
     if (!pnexpr)
         return null();
     if (!matchOrInsertSemicolon())
         return null();
     return handler.newExprStatement(pnexpr, pos().end);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::consequentOrAlternative(YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::consequentOrAlternative(YieldHandling yieldHandling)
 {
     TokenKind next;
     if (!tokenStream.peekToken(&next, TokenStream::Operand))
         return null();
 
     // Annex B.3.4 says that unbraced FunctionDeclarations under if/else in
     // non-strict code act as if they were braced: |if (x) function f() {}|
     // parses as |if (x) { function f() {} }|.
@@ -5901,17 +5996,17 @@ Parser<ParseHandler, CharT>::consequentO
         return finishLexicalScope(scope, block);
     }
 
     return statement(yieldHandling);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::ifStatement(YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::ifStatement(YieldHandling yieldHandling)
 {
     Vector<Node, 4> condList(context), thenList(context);
     Vector<uint32_t, 4> posList(context);
     Node elseBranch;
 
     ParseContext::Statement stmt(pc, StatementKind::If);
 
     while (true) {
@@ -5960,17 +6055,17 @@ Parser<ParseHandler, CharT>::ifStatement
             return null();
     }
 
     return elseBranch;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::doWhileStatement(YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::doWhileStatement(YieldHandling yieldHandling)
 {
     uint32_t begin = pos().begin;
     ParseContext::Statement stmt(pc, StatementKind::DoLoop);
     Node body = statement(yieldHandling);
     if (!body)
         return null();
     MUST_MATCH_TOKEN_MOD(TOK_WHILE, TokenStream::Operand, JSMSG_WHILE_AFTER_DO);
     Node cond = condition(InAllowed, yieldHandling);
@@ -5986,53 +6081,52 @@ Parser<ParseHandler, CharT>::doWhileStat
     bool ignored;
     if (!tokenStream.matchToken(&ignored, TOK_SEMI, TokenStream::Operand))
         return null();
     return handler.newDoWhileStatement(body, cond, TokenPos(begin, pos().end));
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::whileStatement(YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::whileStatement(YieldHandling yieldHandling)
 {
     uint32_t begin = pos().begin;
     ParseContext::Statement stmt(pc, StatementKind::WhileLoop);
     Node cond = condition(InAllowed, yieldHandling);
     if (!cond)
         return null();
     Node body = statement(yieldHandling);
     if (!body)
         return null();
     return handler.newWhileStatement(begin, cond, body);
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::matchInOrOf(bool* isForInp, bool* isForOfp)
+GeneralParser<ParseHandler, CharT>::matchInOrOf(bool* isForInp, bool* isForOfp)
 {
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return false;
 
     *isForInp = tt == TOK_IN;
     *isForOfp = tt == TOK_OF;
     if (!*isForInp && !*isForOfp)
         anyChars.ungetToken();
 
     MOZ_ASSERT_IF(*isForInp || *isForOfp, *isForInp != *isForOfp);
     return true;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::forHeadStart(YieldHandling yieldHandling,
-                                          ParseNodeKind* forHeadKind,
-                                          Node* forInitialPart,
-                                          Maybe<ParseContext::Scope>& forLoopLexicalScope,
-                                          Node* forInOrOfExpression)
+GeneralParser<ParseHandler, CharT>::forHeadStart(YieldHandling yieldHandling,
+                                                 ParseNodeKind* forHeadKind, Node* forInitialPart,
+                                                 Maybe<ParseContext::Scope>& forLoopLexicalScope,
+                                                 Node* forInOrOfExpression)
 {
     MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LP));
 
     TokenKind tt;
     if (!tokenStream.peekToken(&tt, TokenStream::Operand))
         return null();
 
     // Super-duper easy case: |for (;| is a C-style for-loop with no init
@@ -6171,17 +6265,17 @@ Parser<ParseHandler, CharT>::forHeadStar
     // Finally, parse the iterated expression, making the for-loop's closing
     // ')' the next token.
     *forInOrOfExpression = expressionAfterForInOrOf(*forHeadKind, yieldHandling);
     return *forInOrOfExpression != null();
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::forStatement(YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::forStatement(YieldHandling yieldHandling)
 {
     MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_FOR));
 
     uint32_t begin = pos().begin;
 
     ParseContext::Statement stmt(pc, StatementKind::ForLoop);
 
     IteratorKind iterKind = IteratorKind::Sync;
@@ -6325,17 +6419,17 @@ Parser<ParseHandler, CharT>::forStatemen
     if (forLoopLexicalScope)
         return finishLexicalScope(*forLoopLexicalScope, forLoop);
 
     return forLoop;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::switchStatement(YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::switchStatement(YieldHandling yieldHandling)
 {
     MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_SWITCH));
     uint32_t begin = pos().begin;
 
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
 
     Node discriminant = exprInParens(InAllowed, yieldHandling, TripledotProhibited);
     if (!discriminant)
@@ -6432,17 +6526,17 @@ Parser<ParseHandler, CharT>::switchState
 
     handler.setEndPosition(caseList, pos().end);
 
     return handler.newSwitchStatement(begin, discriminant, caseList);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::continueStatement(YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::continueStatement(YieldHandling yieldHandling)
 {
     MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_CONTINUE));
     uint32_t begin = pos().begin;
 
     RootedPropertyName label(context);
     if (!matchLabel(yieldHandling, &label))
         return null();
 
@@ -6490,17 +6584,17 @@ Parser<ParseHandler, CharT>::continueSta
     if (!matchOrInsertSemicolon())
         return null();
 
     return handler.newContinueStatement(label, TokenPos(begin, pos().end));
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::breakStatement(YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::breakStatement(YieldHandling yieldHandling)
 {
     MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_BREAK));
     uint32_t begin = pos().begin;
 
     RootedPropertyName label(context);
     if (!matchLabel(yieldHandling, &label))
         return null();
 
@@ -6530,17 +6624,17 @@ Parser<ParseHandler, CharT>::breakStatem
     if (!matchOrInsertSemicolon())
         return null();
 
     return handler.newBreakStatement(label, TokenPos(begin, pos().end));
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::returnStatement(YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::returnStatement(YieldHandling yieldHandling)
 {
     MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_RETURN));
     uint32_t begin = pos().begin;
 
     MOZ_ASSERT(pc->isFunctionBox());
     pc->functionBox()->usesReturn = true;
 
     // Parse an optional operand.
@@ -6567,17 +6661,17 @@ Parser<ParseHandler, CharT>::returnState
     if (!matchOrInsertSemicolon())
         return null();
 
     return handler.newReturnStatement(exprNode, TokenPos(begin, pos().end));
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::yieldExpression(InHandling inHandling)
+GeneralParser<ParseHandler, CharT>::yieldExpression(InHandling inHandling)
 {
     MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_YIELD));
     uint32_t begin = pos().begin;
 
     MOZ_ASSERT(pc->isGenerator());
     MOZ_ASSERT(pc->isFunctionBox());
 
     pc->lastYieldOffset = begin;
@@ -6618,17 +6712,17 @@ Parser<ParseHandler, CharT>::yieldExpres
     }
     if (kind == PNK_YIELD_STAR)
         return handler.newYieldStarExpression(begin, exprNode);
     return handler.newYieldExpression(begin, exprNode);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::withStatement(YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::withStatement(YieldHandling yieldHandling)
 {
     MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_WITH));
     uint32_t begin = pos().begin;
 
     // Usually we want the constructs forbidden in strict mode code to be a
     // subset of those that ContextOptions::extraWarnings() warns about, and we
     // use strictModeError directly.  But while 'with' is forbidden in strict
     // mode code, it doesn't even merit a warning in non-strict code.  See
@@ -6656,17 +6750,17 @@ Parser<ParseHandler, CharT>::withStateme
 
     pc->sc()->setBindingsAccessedDynamically();
 
     return handler.newWithStatement(begin, objectExpr, innerBlock);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::labeledItem(YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::labeledItem(YieldHandling yieldHandling)
 {
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
 
     if (tt == TOK_FUNCTION) {
         TokenKind next;
         if (!tokenStream.peekToken(&next))
@@ -6691,17 +6785,17 @@ Parser<ParseHandler, CharT>::labeledItem
     }
 
     anyChars.ungetToken();
     return statement(yieldHandling);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::labeledStatement(YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::labeledStatement(YieldHandling yieldHandling)
 {
     RootedPropertyName label(context, labelIdentifier(yieldHandling));
     if (!label)
         return null();
 
     auto hasSameLabel = [&label](ParseContext::LabelStatement* stmt) {
         return stmt->label() == label;
     };
@@ -6721,17 +6815,17 @@ Parser<ParseHandler, CharT>::labeledStat
     if (!pn)
         return null();
 
     return handler.newLabeledStatement(label, pn, begin);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::throwStatement(YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::throwStatement(YieldHandling yieldHandling)
 {
     MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_THROW));
     uint32_t begin = pos().begin;
 
     /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */
     TokenKind tt = TOK_EOF;
     if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
         return null();
@@ -6751,17 +6845,17 @@ Parser<ParseHandler, CharT>::throwStatem
     if (!matchOrInsertSemicolon())
         return null();
 
     return handler.newThrowStatement(throwExpr, TokenPos(begin, pos().end));
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::tryStatement(YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::tryStatement(YieldHandling yieldHandling)
 {
     MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_TRY));
     uint32_t begin = pos().begin;
 
     /*
      * try nodes are ternary.
      * kid1 is the try statement
      * kid2 is the catch node list or null
@@ -6909,18 +7003,18 @@ Parser<ParseHandler, CharT>::tryStatemen
         return null();
     }
 
     return handler.newTryStatement(begin, innerBlock, catchScope, finallyBlock);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::catchBlockStatement(YieldHandling yieldHandling,
-                                                 ParseContext::Scope& catchParamScope)
+GeneralParser<ParseHandler, CharT>::catchBlockStatement(YieldHandling yieldHandling,
+                                                        ParseContext::Scope& catchParamScope)
 {
     uint32_t openedPos = pos().begin;
 
     ParseContext::Statement stmt(pc, StatementKind::Block);
 
     // ES 13.15.7 CatchClauseEvaluation
     //
     // Step 8 means that the body of a catch block always has an additional
@@ -6945,17 +7039,17 @@ Parser<ParseHandler, CharT>::catchBlockS
     // The catch parameter names are not bound in the body scope, so remove
     // them before generating bindings.
     scope.removeCatchParameters(pc, catchParamScope);
     return finishLexicalScope(scope, list);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::debuggerStatement()
+GeneralParser<ParseHandler, CharT>::debuggerStatement()
 {
     TokenPos p;
     p.begin = pos().begin;
     if (!matchOrInsertSemicolon())
         return null();
     p.end = pos().end;
 
     pc->sc()->setBindingsAccessedDynamically();
@@ -6984,19 +7078,19 @@ ToAccessorType(PropertyType propType)
         return AccessorType::None;
       default:
         MOZ_CRASH("unexpected property type");
     }
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::classDefinition(YieldHandling yieldHandling,
-                                             ClassContext classContext,
-                                             DefaultHandling defaultHandling)
+GeneralParser<ParseHandler, CharT>::classDefinition(YieldHandling yieldHandling,
+                                                    ClassContext classContext,
+                                                    DefaultHandling defaultHandling)
 {
     MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_CLASS));
 
     uint32_t classStartOffset = pos().begin;
     bool savedStrictness = setLocalStrictMode(true);
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt))
@@ -7213,17 +7307,17 @@ Parser<ParseHandler, CharT>::classDefini
     MOZ_ALWAYS_TRUE(setLocalStrictMode(savedStrictness));
 
     return handler.newClass(nameNode, classHeritage, methodsOrBlock,
                             TokenPos(classStartOffset, classEndOffset));
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::nextTokenContinuesLetDeclaration(TokenKind next)
+GeneralParser<ParseHandler, CharT>::nextTokenContinuesLetDeclaration(TokenKind next)
 {
     MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LET));
 
 #ifdef DEBUG
     TokenKind verify;
     MOZ_ALWAYS_TRUE(tokenStream.peekToken(&verify));
     MOZ_ASSERT(next == verify);
 #endif
@@ -7244,29 +7338,29 @@ Parser<ParseHandler, CharT>::nextTokenCo
     // parse tree with ASI applied.  No backsies!
 
     // Otherwise a let declaration must have a name.
     return TokenKindIsPossibleIdentifier(next);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::variableStatement(YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::variableStatement(YieldHandling yieldHandling)
 {
     Node vars = declarationList(yieldHandling, PNK_VAR);
     if (!vars)
         return null();
     if (!matchOrInsertSemicolon())
         return null();
     return vars;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::statement(YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::statement(YieldHandling yieldHandling)
 {
     MOZ_ASSERT(checkOptionsCalled);
 
     if (!CheckRecursionLimit(context))
         return null();
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
@@ -7473,18 +7567,18 @@ Parser<ParseHandler, CharT>::statement(Y
         return null();
 
       // NOTE: default case handled in the ExpressionStatement section.
     }
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::statementListItem(YieldHandling yieldHandling,
-                                               bool canHaveDirectives /* = false */)
+GeneralParser<ParseHandler, CharT>::statementListItem(YieldHandling yieldHandling,
+                                                      bool canHaveDirectives /* = false */)
 {
     MOZ_ASSERT(checkOptionsCalled);
 
     if (!CheckRecursionLimit(context))
         return null();
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
@@ -7666,20 +7760,20 @@ Parser<ParseHandler, CharT>::statementLi
         return null();
 
       // NOTE: default case handled in the ExpressionStatement section.
     }
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::expr(InHandling inHandling, YieldHandling yieldHandling,
-                                  TripledotHandling tripledotHandling,
-                                  PossibleError* possibleError /* = nullptr */,
-                                  InvokedPrediction invoked /* = PredictUninvoked */)
+GeneralParser<ParseHandler, CharT>::expr(InHandling inHandling, YieldHandling yieldHandling,
+                                         TripledotHandling tripledotHandling,
+                                         PossibleError* possibleError /* = nullptr */,
+                                         InvokedPrediction invoked /* = PredictUninvoked */)
 {
     Node pn = assignExpr(inHandling, yieldHandling, tripledotHandling,
                          possibleError, invoked);
     if (!pn)
         return null();
 
     bool matched;
     if (!tokenStream.matchToken(&matched, TOK_COMMA, TokenStream::Operand))
@@ -7791,21 +7885,21 @@ Precedence(ParseNodeKind pnk) {
 
     MOZ_ASSERT(pnk >= PNK_BINOP_FIRST);
     MOZ_ASSERT(pnk <= PNK_BINOP_LAST);
     return PrecedenceTable[pnk - PNK_BINOP_FIRST];
 }
 
 template <class ParseHandler, typename CharT>
 MOZ_ALWAYS_INLINE typename ParseHandler::Node
-Parser<ParseHandler, CharT>::orExpr(InHandling inHandling, YieldHandling yieldHandling,
-                                    TripledotHandling tripledotHandling,
-                                    ExpressionClosure expressionClosureHandling,
-                                    PossibleError* possibleError,
-                                    InvokedPrediction invoked /* = PredictUninvoked */)
+GeneralParser<ParseHandler, CharT>::orExpr(InHandling inHandling, YieldHandling yieldHandling,
+                                           TripledotHandling tripledotHandling,
+                                           ExpressionClosure expressionClosureHandling,
+                                           PossibleError* possibleError,
+                                           InvokedPrediction invoked /* = PredictUninvoked */)
 {
     // Shift-reduce parser for the binary operator part of the JS expression
     // syntax.
 
     // Conceptually there's just one stack, a stack of pairs (lhs, op).
     // It's implemented using two separate arrays, though.
     Node nodeStack[PRECEDENCE_CLASSES];
     ParseNodeKind kindStack[PRECEDENCE_CLASSES];
@@ -7879,21 +7973,21 @@ Parser<ParseHandler, CharT>::orExpr(InHa
     anyChars.addModifierException(TokenStream::OperandIsNone);
 
     MOZ_ASSERT(depth == 0);
     return pn;
 }
 
 template <class ParseHandler, typename CharT>
 MOZ_ALWAYS_INLINE typename ParseHandler::Node
-Parser<ParseHandler, CharT>::condExpr(InHandling inHandling, YieldHandling yieldHandling,
-                                      TripledotHandling tripledotHandling,
-                                      ExpressionClosure expressionClosureHandling,
-                                      PossibleError* possibleError,
-                                      InvokedPrediction invoked /* = PredictUninvoked */)
+GeneralParser<ParseHandler, CharT>::condExpr(InHandling inHandling, YieldHandling yieldHandling,
+                                             TripledotHandling tripledotHandling,
+                                             ExpressionClosure expressionClosureHandling,
+                                             PossibleError* possibleError,
+                                             InvokedPrediction invoked /* = PredictUninvoked */)
 {
     Node condition = orExpr(inHandling, yieldHandling, tripledotHandling,
                             expressionClosureHandling, possibleError, invoked);
     if (!condition)
         return null();
 
     if (handler.isExpressionClosure(condition))
         return condition;
@@ -7914,20 +8008,20 @@ Parser<ParseHandler, CharT>::condExpr(In
     if (!elseExpr)
         return null();
 
     return handler.newConditional(condition, thenExpr, elseExpr);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::assignExpr(InHandling inHandling, YieldHandling yieldHandling,
-                                        TripledotHandling tripledotHandling,
-                                        PossibleError* possibleError /* = nullptr */,
-                                        InvokedPrediction invoked /* = PredictUninvoked */)
+GeneralParser<ParseHandler, CharT>::assignExpr(InHandling inHandling, YieldHandling yieldHandling,
+                                               TripledotHandling tripledotHandling,
+                                               PossibleError* possibleError /* = nullptr */,
+                                               InvokedPrediction invoked /* = PredictUninvoked */)
 {
     if (!CheckRecursionLimit(context))
         return null();
 
     // It's very common at this point to have a "detectably simple" expression,
     // i.e. a name/number/string token followed by one of the following tokens
     // that obviously isn't part of an expression: , ; : ) ] }
     //
@@ -8142,18 +8236,18 @@ Parser<ParseHandler, CharT>::assignExpr(
     if (kind == PNK_ASSIGN)
         handler.checkAndSetIsDirectRHSAnonFunction(rhs);
 
     return handler.newAssignment(kind, lhs, rhs);
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::isValidSimpleAssignmentTarget(Node node,
-                                                           FunctionCallBehavior behavior /* = ForbidAssignmentToFunctionCalls */)
+GeneralParser<ParseHandler, CharT>::isValidSimpleAssignmentTarget(Node node,
+                                                                  FunctionCallBehavior behavior /* = ForbidAssignmentToFunctionCalls */)
 {
     // Note that this method implements *only* a boolean test.  Reporting an
     // error for the various syntaxes that fail this, and warning for the
     // various syntaxes that "pass" this but should not, occurs elsewhere.
 
     if (handler.isName(node)) {
         if (!pc->sc()->strict())
             return true;
@@ -8169,30 +8263,30 @@ Parser<ParseHandler, CharT>::isValidSimp
             return true;
     }
 
     return false;
 }
 
 template <class ParseHandler, typename CharT>
 const char*
-Parser<ParseHandler, CharT>::nameIsArgumentsOrEval(Node node)
+GeneralParser<ParseHandler, CharT>::nameIsArgumentsOrEval(Node node)
 {
     MOZ_ASSERT(handler.isName(node), "must only call this function on known names");
 
     if (handler.isEvalName(node, context))
         return js_eval_str;
     if (handler.isArgumentsName(node, context))
         return js_arguments_str;
     return nullptr;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::checkIncDecOperand(Node operand, uint32_t operandOffset)
+GeneralParser<ParseHandler, CharT>::checkIncDecOperand(Node operand, uint32_t operandOffset)
 {
     if (handler.isName(operand)) {
         if (const char* chars = nameIsArgumentsOrEval(operand)) {
             if (!strictModeErrorAt(operandOffset, JSMSG_BAD_STRICT_ASSIGN, chars))
                 return false;
         }
     } else if (handler.isPropertyAccess(operand)) {
         // Permitted: no additional testing/fixup needed.
@@ -8210,32 +8304,32 @@ Parser<ParseHandler, CharT>::checkIncDec
 
     MOZ_ASSERT(isValidSimpleAssignmentTarget(operand, PermitAssignmentToFunctionCalls),
                "inconsistent increment/decrement operand validation");
     return true;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kind,
-                                         uint32_t begin)
+GeneralParser<ParseHandler, CharT>::unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kind,
+                                                uint32_t begin)
 {
     Node kid = unaryExpr(yieldHandling, TripledotProhibited, ExpressionClosure::Forbidden);
     if (!kid)
         return null();
     return handler.newUnary(kind, begin, kid);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::unaryExpr(YieldHandling yieldHandling,
-                                       TripledotHandling tripledotHandling,
-                                       ExpressionClosure expressionClosureHandling,
-                                       PossibleError* possibleError /* = nullptr */,
-                                       InvokedPrediction invoked /* = PredictUninvoked */)
+GeneralParser<ParseHandler, CharT>::unaryExpr(YieldHandling yieldHandling,
+                                              TripledotHandling tripledotHandling,
+                                              ExpressionClosure expressionClosureHandling,
+                                              PossibleError* possibleError /* = nullptr */,
+                                              InvokedPrediction invoked /* = PredictUninvoked */)
 {
     if (!CheckRecursionLimit(context))
         return null();
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
     uint32_t begin = pos().begin;
@@ -8344,17 +8438,17 @@ Parser<ParseHandler, CharT>::unaryExpr(Y
                                  begin, expr);
       }
     }
 }
 
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::assignExprWithoutYieldOrAwait(YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::assignExprWithoutYieldOrAwait(YieldHandling yieldHandling)
 {
     uint32_t startYieldOffset = pc->lastYieldOffset;
     uint32_t startAwaitOffset = pc->lastAwaitOffset;
     Node res = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
     if (res) {
         if (pc->lastYieldOffset != startYieldOffset) {
             errorAt(pc->lastYieldOffset, JSMSG_YIELD_IN_DEFAULT);
             return null();
@@ -8364,19 +8458,19 @@ Parser<ParseHandler, CharT>::assignExprW
             return null();
         }
     }
     return res;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::argumentList(YieldHandling yieldHandling, Node listNode,
-                                          bool* isSpread,
-                                          PossibleError* possibleError /* = nullptr */)
+GeneralParser<ParseHandler, CharT>::argumentList(YieldHandling yieldHandling, Node listNode,
+                                                 bool* isSpread,
+                                                 PossibleError* possibleError /* = nullptr */)
 {
     bool matched;
     if (!tokenStream.matchToken(&matched, TOK_RP, TokenStream::Operand))
         return false;
     if (matched) {
         handler.setEndPosition(listNode, pos().end);
         return true;
     }
@@ -8419,32 +8513,32 @@ Parser<ParseHandler, CharT>::argumentLis
     MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_ARGS);
 
     handler.setEndPosition(listNode, pos().end);
     return true;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::checkAndMarkSuperScope()
+GeneralParser<ParseHandler, CharT>::checkAndMarkSuperScope()
 {
     if (!pc->sc()->allowSuperProperty())
         return false;
     pc->setSuperScopeNeedsHomeObject();
     return true;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::memberExpr(YieldHandling yieldHandling,
-                                        TripledotHandling tripledotHandling,
-                                        ExpressionClosure expressionClosureHandling,
-                                        TokenKind tt, bool allowCallSyntax /* = true */,
-                                        PossibleError* possibleError /* = nullptr */,
-                                        InvokedPrediction invoked /* = PredictUninvoked */)
+GeneralParser<ParseHandler, CharT>::memberExpr(YieldHandling yieldHandling,
+                                               TripledotHandling tripledotHandling,
+                                               ExpressionClosure expressionClosureHandling,
+                                               TokenKind tt, bool allowCallSyntax /* = true */,
+                                               PossibleError* possibleError /* = nullptr */,
+                                               InvokedPrediction invoked /* = PredictUninvoked */)
 {
     MOZ_ASSERT(anyChars.isCurrentTokenType(tt));
 
     Node lhs;
 
     if (!CheckRecursionLimit(context))
         return null();
 
@@ -8665,34 +8759,34 @@ Parser<ParseHandler, CharT>::memberExpr(
         return null();
     }
 
     return lhs;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::newName(PropertyName* name)
+GeneralParser<ParseHandler, CharT>::newName(PropertyName* name)
 {
     return newName(name, pos());
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::newName(PropertyName* name, TokenPos pos)
+GeneralParser<ParseHandler, CharT>::newName(PropertyName* name, TokenPos pos)
 {
     return handler.newName(name, pos, context);
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::checkLabelOrIdentifierReference(PropertyName* ident,
-                                                             uint32_t offset,
-                                                             YieldHandling yieldHandling,
-                                                             TokenKind hint /* = TOK_LIMIT */)
+GeneralParser<ParseHandler, CharT>::checkLabelOrIdentifierReference(PropertyName* ident,
+                                                                    uint32_t offset,
+                                                                    YieldHandling yieldHandling,
+                                                                    TokenKind hint /* = TOK_LIMIT */)
 {
     TokenKind tt;
     if (hint == TOK_LIMIT) {
         tt = ReservedWordTokenKind(ident);
     } else {
         MOZ_ASSERT(hint == ReservedWordTokenKind(ident), "hint doesn't match actual token kind");
         tt = hint;
     }
@@ -8748,20 +8842,19 @@ Parser<ParseHandler, CharT>::checkLabelO
         return false;
     }
     MOZ_ASSERT_UNREACHABLE("Unexpected reserved word kind.");
     return false;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::checkBindingIdentifier(PropertyName* ident,
-                                                    uint32_t offset,
-                                                    YieldHandling yieldHandling,
-                                                    TokenKind hint /* = TOK_LIMIT */)
+GeneralParser<ParseHandler, CharT>::checkBindingIdentifier(PropertyName* ident, uint32_t offset,
+                                                           YieldHandling yieldHandling,
+                                                           TokenKind hint /* = TOK_LIMIT */)
 {
     if (pc->sc()->needStrictChecks()) {
         if (ident == context->names().arguments) {
             if (!strictModeErrorAt(offset, JSMSG_BAD_STRICT_ASSIGN, "arguments"))
                 return false;
             return true;
         }
 
@@ -8772,17 +8865,17 @@ Parser<ParseHandler, CharT>::checkBindin
         }
     }
 
     return checkLabelOrIdentifierReference(ident, offset, yieldHandling, hint);
 }
 
 template <class ParseHandler, typename CharT>
 PropertyName*
-Parser<ParseHandler, CharT>::labelOrIdentifierReference(YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::labelOrIdentifierReference(YieldHandling yieldHandling)
 {
     // ES 2017 draft 12.1.1.
     //   StringValue of IdentifierName normalizes any Unicode escape sequences
     //   in IdentifierName hence such escapes cannot be used to write an
     //   Identifier whose code point sequence is the same as a ReservedWord.
     //
     // Use PropertyName* instead of TokenKind to reflect the normalization.
 
@@ -8794,115 +8887,122 @@ Parser<ParseHandler, CharT>::labelOrIden
     RootedPropertyName ident(context, anyChars.currentName());
     if (!checkLabelOrIdentifierReference(ident, pos().begin, yieldHandling, hint))
         return nullptr;
     return ident;
 }
 
 template <class ParseHandler, typename CharT>
 PropertyName*
-Parser<ParseHandler, CharT>::bindingIdentifier(YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::bindingIdentifier(YieldHandling yieldHandling)
 {
     TokenKind hint = !anyChars.currentNameHasEscapes()
                      ? anyChars.currentToken().type
                      : TOK_LIMIT;
     RootedPropertyName ident(context, anyChars.currentName());
     if (!checkBindingIdentifier(ident, pos().begin, yieldHandling, hint))
         return nullptr;
     return ident;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::identifierReference(Handle<PropertyName*> name)
+GeneralParser<ParseHandler, CharT>::identifierReference(Handle<PropertyName*> name)
 {
     Node pn = newName(name);
     if (!pn)
         return null();
 
     if (!noteUsedName(name))
         return null();
 
     return pn;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::stringLiteral()
+GeneralParser<ParseHandler, CharT>::stringLiteral()
 {
     return handler.newStringLiteral(anyChars.currentToken().atom(), pos());
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::noSubstitutionTaggedTemplate()
+GeneralParser<ParseHandler, CharT>::noSubstitutionTaggedTemplate()
 {
     if (anyChars.hasInvalidTemplateEscape()) {
         anyChars.clearInvalidTemplateEscape();
         return handler.newRawUndefinedLiteral(pos());
     }
 
     return handler.newTemplateStringLiteral(anyChars.currentToken().atom(), pos());
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::noSubstitutionUntaggedTemplate()
+GeneralParser<ParseHandler, CharT>::noSubstitutionUntaggedTemplate()
 {
     if (!tokenStream.checkForInvalidTemplateEscapeError())
         return null();
 
     return handler.newTemplateStringLiteral(anyChars.currentToken().atom(), pos());
 }
 
-template <>
+template <typename CharT>
 ParseNode*
-Parser<FullParseHandler, char16_t>::newRegExp()
+Parser<FullParseHandler, CharT>::newRegExp()
 {
     MOZ_ASSERT(!options().selfHostingMode);
 
     // Create the regexp and check its syntax.
     const char16_t* chars = tokenStream.getTokenbuf().begin();
     size_t length = tokenStream.getTokenbuf().length();
     RegExpFlag flags = anyChars.currentToken().regExpFlags();
 
     Rooted<RegExpObject*> reobj(context);
     reobj = RegExpObject::create(context, chars, length, flags, anyChars, alloc, TenuredObject);
     if (!reobj)
         return null();
 
     return handler.newRegExp(reobj, pos(), *this);
 }
 
-template <>
+template <typename CharT>
 SyntaxParseHandler::Node
-Parser<SyntaxParseHandler, char16_t>::newRegExp()
+Parser<SyntaxParseHandler, CharT>::newRegExp()
 {
     MOZ_ASSERT(!options().selfHostingMode);
 
     // Only check the regexp's syntax, but don't create a regexp object.
     const char16_t* chars = tokenStream.getTokenbuf().begin();
     size_t length = tokenStream.getTokenbuf().length();
     RegExpFlag flags = anyChars.currentToken().regExpFlags();
 
     mozilla::Range<const char16_t> source(chars, length);
     if (!js::irregexp::ParsePatternSyntax(anyChars, alloc, source, flags & UnicodeFlag))
         return null();
 
     return handler.newRegExp(SyntaxParseHandler::NodeGeneric, pos(), *this);
 }
 
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
+GeneralParser<ParseHandler, CharT>::newRegExp()
+{
+    return asFinalParser()->newRegExp();
+}
+
 // |exprPossibleError| is the PossibleError state within |expr|,
 // |possibleError| is the surrounding PossibleError state.
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::checkDestructuringAssignmentTarget(Node expr, TokenPos exprPos,
-                                                                PossibleError* exprPossibleError,
-                                                                PossibleError* possibleError,
-                                                                TargetBehavior behavior)
+GeneralParser<ParseHandler, CharT>::checkDestructuringAssignmentTarget(Node expr, TokenPos exprPos,
+                                                                       PossibleError* exprPossibleError,
+                                                                       PossibleError* possibleError,
+                                                                       TargetBehavior behavior)
 {
     // Report any pending expression error if we're definitely not in a
     // destructuring context or the possible destructuring target is a
     // property accessor.
     if (!possibleError || handler.isPropertyAccess(expr))
         return exprPossibleError->checkForExpressionError();
 
     // |expr| may end up as a destructuring assignment target, so we need to
@@ -8938,18 +9038,18 @@ Parser<ParseHandler, CharT>::checkDestru
         possibleError->setPendingDestructuringErrorAt(exprPos, JSMSG_BAD_DESTRUCT_TARGET);
     }
 
     return true;
 }
 
 template <class ParseHandler, typename CharT>
 void
-Parser<ParseHandler, CharT>::checkDestructuringAssignmentName(Node name, TokenPos namePos,
-                                                              PossibleError* possibleError)
+GeneralParser<ParseHandler, CharT>::checkDestructuringAssignmentName(Node name, TokenPos namePos,
+                                                                     PossibleError* possibleError)
 {
     MOZ_ASSERT(handler.isName(name));
 
     // Return early if a pending destructuring error is already present.
     if (possibleError->hasPendingDestructuringError())
         return;
 
     if (pc->sc()->needStrictChecks()) {
@@ -8974,19 +9074,19 @@ Parser<ParseHandler, CharT>::checkDestru
             }
             return;
         }
     }
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::checkDestructuringAssignmentElement(Node expr, TokenPos exprPos,
-                                                                 PossibleError* exprPossibleError,
-                                                                 PossibleError* possibleError)
+GeneralParser<ParseHandler, CharT>::checkDestructuringAssignmentElement(Node expr, TokenPos exprPos,
+                                                                        PossibleError* exprPossibleError,
+                                                                        PossibleError* possibleError)
 {
     // ES2018 draft rev 0719f44aab93215ed9a626b2f45bd34f36916834
     // 12.15.5 Destructuring Assignment
     //
     // AssignmentElement[Yield, Await]:
     //   DestructuringAssignmentTarget[?Yield, ?Await]
     //   DestructuringAssignmentTarget[?Yield, ?Await] Initializer[+In, ?Yield, ?Await]
 
@@ -9002,18 +9102,18 @@ Parser<ParseHandler, CharT>::checkDestru
         exprPossibleError->transferErrorsTo(possibleError);
         return true;
     }
     return checkDestructuringAssignmentTarget(expr, exprPos, exprPossibleError, possibleError);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::arrayInitializer(YieldHandling yieldHandling,
-                                              PossibleError* possibleError)
+GeneralParser<ParseHandler, CharT>::arrayInitializer(YieldHandling yieldHandling,
+                                                     PossibleError* possibleError)
 {
     MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LB));
 
     uint32_t begin = pos().begin;
     Node literal = handler.newArrayLiteral(begin);
     if (!literal)
         return null();
 
@@ -9114,19 +9214,21 @@ DoubleToAtom(JSContext* cx, double value
 {
     // This is safe because doubles can not be moved.
     Value tmp = DoubleValue(value);
     return ToAtom<CanGC>(cx, HandleValue::fromMarkedLocation(&tmp));
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::propertyName(YieldHandling yieldHandling,
-                                          const Maybe<DeclarationKind>& maybeDecl, Node propList,
-                                          PropertyType* propType, MutableHandleAtom propAtom)
+GeneralParser<ParseHandler, CharT>::propertyName(YieldHandling yieldHandling,
+                                                 const Maybe<DeclarationKind>& maybeDecl,
+                                                 Node propList,
+                                                 PropertyType* propType,
+                                                 MutableHandleAtom propAtom)
 {
     TokenKind ltok;
     if (!tokenStream.getToken(&ltok))
         return null();
 
     MOZ_ASSERT(ltok != TOK_RC, "caller should have handled TOK_RC");
 
     bool isGenerator = false;
@@ -9309,19 +9411,19 @@ Parser<ParseHandler, CharT>::propertyNam
     }
 
     error(JSMSG_COLON_AFTER_ID);
     return null();
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::computedPropertyName(YieldHandling yieldHandling,
-                                                  const Maybe<DeclarationKind>& maybeDecl,
-                                                  Node literal)
+GeneralParser<ParseHandler, CharT>::computedPropertyName(YieldHandling yieldHandling,
+                                                         const Maybe<DeclarationKind>& maybeDecl,
+                                                         Node literal)
 {
     MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LB));
 
     uint32_t begin = pos().begin;
 
     if (maybeDecl) {
         if (*maybeDecl == DeclarationKind::FormalParameter)
             pc->functionBox()->hasParameterExprs = true;
@@ -9334,18 +9436,18 @@ Parser<ParseHandler, CharT>::computedPro
         return null();
 
     MUST_MATCH_TOKEN_MOD(TOK_RB, TokenStream::Operand, JSMSG_COMP_PROP_UNTERM_EXPR);
     return handler.newComputedName(assignNode, begin, pos().end);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::objectLiteral(YieldHandling yieldHandling,
-                                           PossibleError* possibleError)
+GeneralParser<ParseHandler, CharT>::objectLiteral(YieldHandling yieldHandling,
+                                                  PossibleError* possibleError)
 {
     MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LC));
 
     uint32_t openedPos = pos().begin;
 
     Node literal = handler.newObjectLiteral(pos().begin);
     if (!literal)
         return null();
@@ -9560,18 +9662,18 @@ Parser<ParseHandler, CharT>::objectLiter
                                                           JSMSG_CURLY_OPENED, openedPos));
 
     handler.setEndPosition(literal, pos().end);
     return literal;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::methodDefinition(uint32_t toStringStart, PropertyType propType,
-                                              HandleAtom funName)
+GeneralParser<ParseHandler, CharT>::methodDefinition(uint32_t toStringStart, PropertyType propType,
+                                                     HandleAtom funName)
 {
     FunctionSyntaxKind kind;
     switch (propType) {
       case PropertyType::Getter:
         kind = Getter;
         break;
 
       case PropertyType::GetterNoExpressionClosure:
@@ -9622,17 +9724,17 @@ Parser<ParseHandler, CharT>::methodDefin
         return null();
 
     return functionDefinition(pn, toStringStart, InAllowed, yieldHandling, funName, kind,
                               generatorKind, asyncKind);
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::tryNewTarget(Node &newTarget)
+GeneralParser<ParseHandler, CharT>::tryNewTarget(Node &newTarget)
 {
     MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_NEW));
 
     newTarget = null();
 
     Node newHolder = handler.newPosHolder(pos());
     if (!newHolder)
         return false;
@@ -9666,21 +9768,21 @@ Parser<ParseHandler, CharT>::tryNewTarge
         return false;
 
     newTarget = handler.newNewTarget(newHolder, targetHolder);
     return !!newTarget;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::primaryExpr(YieldHandling yieldHandling,
-                                         TripledotHandling tripledotHandling,
-                                         ExpressionClosure expressionClosureHandling, TokenKind tt,
-                                         PossibleError* possibleError,
-                                         InvokedPrediction invoked /* = PredictUninvoked */)
+GeneralParser<ParseHandler, CharT>::primaryExpr(YieldHandling yieldHandling,
+                                                TripledotHandling tripledotHandling,
+                                                ExpressionClosure expressionClosureHandling,
+                                                TokenKind tt, PossibleError* possibleError,
+                                                InvokedPrediction invoked /* = PredictUninvoked */)
 {
     MOZ_ASSERT(anyChars.isCurrentTokenType(tt));
     if (!CheckRecursionLimit(context))
         return null();
 
     switch (tt) {
       case TOK_FUNCTION:
         return functionExpr(pos().begin, expressionClosureHandling, invoked,
@@ -9841,46 +9943,49 @@ Parser<ParseHandler, CharT>::primaryExpr
         // Return an arbitrary expression node. See case TOK_RP above.
         return handler.newNullLiteral(pos());
       }
     }
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::exprInParens(InHandling inHandling, YieldHandling yieldHandling,
-                                          TripledotHandling tripledotHandling,
-                                          PossibleError* possibleError /* = nullptr */)
+GeneralParser<ParseHandler, CharT>::exprInParens(InHandling inHandling,
+                                                 YieldHandling yieldHandling,
+                                                 TripledotHandling tripledotHandling,
+                                                 PossibleError* possibleError /* = nullptr */)
 {
     MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LP));
     return expr(inHandling, yieldHandling, tripledotHandling, possibleError, PredictInvoked);
 }
 
 void
 ParserBase::addTelemetry(DeprecatedLanguageExtension e)
 {
     if (context->helperThread())
         return;
     context->compartment()->addTelemetry(getFilename(), e);
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::warnOnceAboutExprClosure()
+GeneralParser<ParseHandler, CharT>::warnOnceAboutExprClosure()
 {
 #ifndef RELEASE_OR_BETA
     if (context->helperThread())
         return true;
 
     if (!context->compartment()->warnedAboutExprClosure) {
         if (!warning(JSMSG_DEPRECATED_EXPR_CLOSURE))
             return false;
         context->compartment()->warnedAboutExprClosure = true;
     }
 #endif
     return true;
 }
 
+template class GeneralParser<FullParseHandler, char16_t>;
+template class GeneralParser<SyntaxParseHandler, char16_t>;
 template class Parser<FullParseHandler, char16_t>;
 template class Parser<SyntaxParseHandler, char16_t>;
 
 } /* namespace frontend */
 } /* namespace js */
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -30,23 +30,25 @@
 namespace js {
 
 class ModuleObject;
 
 namespace frontend {
 
 class ParserBase;
 
-template <class ParseHandler, typename CharT> class Parser;
+template <class ParseHandler, typename CharT>
+class GeneralParser;
 
 class SourceParseContext: public ParseContext
 {
 public:
     template<typename ParseHandler, typename CharT>
-    SourceParseContext(Parser<ParseHandler, CharT>* prs, SharedContext* sc, Directives* newDirectives)
+    SourceParseContext(GeneralParser<ParseHandler, CharT>* prs, SharedContext* sc,
+                       Directives* newDirectives)
       : ParseContext(prs->context, prs->pc, sc, prs->anyChars, prs->usedNames, newDirectives,
                      mozilla::IsSame<ParseHandler, FullParseHandler>::value)
     { }
 };
 
 template <>
 inline bool
 ParseContext::Statement::is<ParseContext::LabelStatement>() const
@@ -107,16 +109,20 @@ enum class PropertyType {
 // we're in a function box -- easier and simpler than passing an extra
 // parameter everywhere.
 enum YieldHandling { YieldIsName, YieldIsKeyword };
 enum AwaitHandling : uint8_t { AwaitIsName, AwaitIsKeyword, AwaitIsModuleKeyword };
 enum InHandling { InAllowed, InProhibited };
 enum DefaultHandling { NameRequired, AllowDefaultName };
 enum TripledotHandling { TripledotAllowed, TripledotProhibited };
 
+enum FunctionCallBehavior {
+    PermitAssignmentToFunctionCalls,
+    ForbidAssignmentToFunctionCalls
+};
 
 template <class Parser>
 class AutoAwaitIsKeyword;
 
 class ParserBase : public StrictModeGetter
 {
   private:
     ParserBase* thisForCtor() { return this; }
@@ -157,16 +163,18 @@ class ParserBase : public StrictModeGett
 
     /* AwaitHandling */ uint8_t awaitHandling_:2;
 
   public:
     bool awaitIsKeyword() const {
       return awaitHandling_ != AwaitIsName;
     }
 
+    template<class> friend class AutoAwaitIsKeyword;
+
     ParserBase(JSContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options,
                const char16_t* chars, size_t length, bool foldConstants,
                UsedNameTracker& usedNames);
     ~ParserBase();
 
     const char* getFilename() const { return anyChars.getFilename(); }
     TokenPos pos() const { return anyChars.currentToken().pos; }
 
@@ -272,25 +280,33 @@ class ParserAnyCharsAccess
     using TokenStreamSpecific = typename Parser::TokenStream;
     using TokenStreamChars = typename TokenStreamSpecific::CharsBase;
 
     static inline TokenStreamAnyChars& anyChars(TokenStreamChars* ts);
     static inline const TokenStreamAnyChars& anyChars(const TokenStreamChars* ts);
 };
 
 template <class ParseHandler, typename CharT>
-class Parser final
+class Parser;
+
+template <class ParseHandler, typename CharT>
+class GeneralParser
   : public ParserBase,
     private JS::AutoGCRooter
 {
+  protected:
     using Modifier = TokenStreamShared::Modifier;
 
   private:
+    using FinalParser = Parser<ParseHandler, CharT>;
     using Node = typename ParseHandler::Node;
 
+    inline FinalParser* asFinalParser();
+    inline const FinalParser* asFinalParser() const;
+
     /*
      * A class for temporarily stashing errors while parsing continues.
      *
      * The ability to stash an error is useful for handling situations where we
      * aren't able to verify that an error has occurred until later in the parse.
      * For instance | ({x=1}) | is always parsed as an object literal with
      * a SyntaxError, however, in the case where it is followed by '=>' we rewind
      * and reparse it as a valid arrow function. Here a PossibleError would be
@@ -341,17 +357,17 @@ class Parser final
         struct Error {
             ErrorState state_ = ErrorState::None;
 
             // Error reporting fields.
             uint32_t offset_;
             unsigned errorNumber_;
         };
 
-        Parser<ParseHandler, CharT>& parser_;
+        GeneralParser<ParseHandler, CharT>& parser_;
         Error exprError_;
         Error destructuringError_;
         Error destructuringWarning_;
 
         // Returns the error report.
         Error& error(ErrorKind kind);
 
         // Return true if an error is pending without reporting.
@@ -371,17 +387,17 @@ class Parser final
         // If there is a pending warning, report it and return either false or
         // true depending on the werror option, otherwise return true.
         MOZ_MUST_USE bool checkForWarning(ErrorKind kind);
 
         // Transfer an existing error to another instance.
         void transferErrorTo(ErrorKind kind, PossibleError* other);
 
       public:
-        explicit PossibleError(Parser<ParseHandler, CharT>& parser);
+        explicit PossibleError(GeneralParser<ParseHandler, CharT>& parser);
 
         // Return true if a pending destructuring error is present.
         bool hasPendingDestructuringError();
 
         // Set a pending destructuring error. Only a single error may be set
         // per instance, i.e. subsequent calls to this method are ignored and
         // won't overwrite the existing pending error.
         void setPendingDestructuringErrorAt(const TokenPos& pos, unsigned errorNumber);
@@ -408,59 +424,51 @@ class Parser final
 
         // Pass pending errors between possible error instances. This is useful
         // for extending the lifetime of a pending error beyond the scope of
         // the PossibleError where it was initially set (keeping in mind that
         // PossibleError is a MOZ_STACK_CLASS).
         void transferErrorsTo(PossibleError* other);
     };
 
+  protected:
     // When ParseHandler is FullParseHandler:
     //
     //   If non-null, this field holds the syntax parser used to attempt lazy
     //   parsing of inner functions. If null, then lazy parsing is disabled.
     //
     // When ParseHandler is SyntaxParseHandler:
     //
     //   If non-null, this field must be a sentinel value signaling that the
     //   syntax parse was aborted. If null, then lazy parsing was aborted due
     //   to encountering unsupported language constructs.
     using SyntaxParser = Parser<SyntaxParseHandler, CharT>;
     SyntaxParser* syntaxParser_;
 
+  private:
+    inline bool abortIfSyntaxParser();
+
   public:
-    using TokenStream = TokenStreamSpecific<CharT, ParserAnyCharsAccess<Parser>>;
+    using TokenStream = TokenStreamSpecific<CharT, ParserAnyCharsAccess<GeneralParser>>;
     TokenStream tokenStream;
 
     /* State specific to the kind of parse being performed. */
     ParseHandler handler;
 
     void prepareNodeForMutation(Node node) { handler.prepareNodeForMutation(node); }
     void freeTree(Node node) { handler.freeTree(node); }
 
   public:
-    Parser(JSContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options,
-           const CharT* chars, size_t length, bool foldConstants, UsedNameTracker& usedNames,
-           SyntaxParser* syntaxParser, LazyScript* lazyOuterFunction);
-    ~Parser();
-
-    friend class AutoAwaitIsKeyword<Parser>;
-    void setAwaitHandling(AwaitHandling awaitHandling);
+    GeneralParser(JSContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options,
+                  const CharT* chars, size_t length, bool foldConstants,
+                  UsedNameTracker& usedNames, SyntaxParser* syntaxParser,
+                  LazyScript* lazyOuterFunction);
+    ~GeneralParser();
 
-    // If ParseHandler is SyntaxParseHandler, whether the last syntax parse was
-    // aborted due to unsupported language constructs.
-    //
-    // If ParseHandler is FullParseHandler, false.
-    bool hadAbortedSyntaxParse();
-
-    // If ParseHandler is SyntaxParseHandler, clear whether the last syntax
-    // parse was aborted.
-    //
-    // If ParseHandler is FullParseHandler, do nothing.
-    void clearAbortedSyntaxParse();
+    inline void setAwaitHandling(AwaitHandling awaitHandling);
 
     bool checkOptions();
 
     friend void js::frontend::TraceParser(JSTracer* trc, JS::AutoGCRooter* parser);
 
     /*
      * Parse a top-level JS script.
      */
@@ -511,109 +519,68 @@ class Parser final
 
     /*
      * If extra warnings are enabled, report the given warning at the given
      * offset.
      */
     MOZ_MUST_USE bool extraWarningAt(uint32_t offset, unsigned errorNumber, ...);
 
   private:
-    Parser* thisForCtor() { return this; }
+    GeneralParser* thisForCtor() { return this; }
 
-    Node stringLiteral();
     Node noSubstitutionTaggedTemplate();
     Node noSubstitutionUntaggedTemplate();
     Node templateLiteral(YieldHandling yieldHandling);
     bool taggedTemplate(YieldHandling yieldHandling, Node nodeList, TokenKind tt);
     bool appendToCallSiteObj(Node callSiteObj);
     bool addExprAndGetNextTemplStrToken(YieldHandling yieldHandling, Node nodeList,
                                         TokenKind* ttp);
-    bool checkStatementsEOF();
-
-    inline Node newName(PropertyName* name);
-    inline Node newName(PropertyName* name, TokenPos pos);
 
-    // If ParseHandler is SyntaxParseHandler, flag the current syntax parse as
-    // aborted due to unsupported language constructs and return
-    // false. Aborting the current syntax parse does not disable attempts to
-    // syntax parse future inner functions.
-    //
-    // If ParseHandler is FullParseHandler, disable syntax parsing of all
-    // future inner functions and return true.
-    inline bool abortIfSyntaxParser();
+    inline bool trySyntaxParseInnerFunction(Node node, HandleFunction fun, uint32_t toStringStart,
+                                            InHandling inHandling, YieldHandling yieldHandling,
+                                            FunctionSyntaxKind kind, GeneratorKind generatorKind,
+                                            FunctionAsyncKind asyncKind, bool tryAnnexB,
+                                            Directives inheritedDirectives,
+                                            Directives* newDirectives);
 
-    // If ParseHandler is SyntaxParseHandler, do nothing.
-    //
-    // If ParseHandler is FullParseHandler, disable syntax parsing of all
-    // future inner functions.
-    void disableSyntaxParser();
+    inline bool skipLazyInnerFunction(Node node, uint32_t toStringStart, FunctionSyntaxKind kind,
+                                      bool tryAnnexB);
 
   public:
     /* Public entry points for parsing. */
     Node statementListItem(YieldHandling yieldHandling, bool canHaveDirectives = false);
 
-    // Parse the body of an eval.
-    //
-    // Eval scripts are distinguished from global scripts in that in ES6, per
-    // 18.2.1.1 steps 9 and 10, all eval scripts are executed under a fresh
-    // lexical scope.
-    Node evalBody(EvalSharedContext* evalsc);
-
-    // Parse the body of a global script.
-    Node globalBody(GlobalSharedContext* globalsc);
-
-    // Parse a module.
-    Node moduleBody(ModuleSharedContext* modulesc);
-
-    // Parse a function, used for the Function, GeneratorFunction, and
-    // AsyncFunction constructors.
-    Node standaloneFunction(HandleFunction fun, HandleScope enclosingScope,
-                            const mozilla::Maybe<uint32_t>& parameterListEnd,
-                            GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
-                            Directives inheritedDirectives, Directives* newDirectives);
-
-    // Parse a function, given only its arguments and body. Used for lazily
-    // parsed functions.
-    Node standaloneLazyFunction(HandleFunction fun, uint32_t toStringStart, bool strict,
-                                GeneratorKind generatorKind, FunctionAsyncKind asyncKind);
-
     // Parse an inner function given an enclosing ParseContext and a
     // FunctionBox for the inner function.
-    bool innerFunction(Node pn, ParseContext* outerpc, FunctionBox* funbox,
-                       InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind,
-                       Directives* newDirectives);
+    bool innerFunctionForFunctionBox(Node pn, ParseContext* outerpc, FunctionBox* funbox,
+                                     InHandling inHandling, YieldHandling yieldHandling,
+                                     FunctionSyntaxKind kind, Directives* newDirectives);
 
     // Parse a function's formal parameters and its body assuming its function
     // ParseContext is already on the stack.
     bool functionFormalParametersAndBody(InHandling inHandling, YieldHandling yieldHandling,
                                          Node pn, FunctionSyntaxKind kind,
                                          const mozilla::Maybe<uint32_t>& parameterListEnd = mozilla::Nothing(),
                                          bool isStandaloneFunction = false);
 
-    // Match the current token against the BindingIdentifier production with
-    // the given Yield parameter.  If there is no match, report a syntax
-    // error.
-    PropertyName* bindingIdentifier(YieldHandling yieldHandling);
-
   private:
     /*
      * JS parsers, from lowest to highest precedence.
      *
      * Each parser must be called during the dynamic scope of a ParseContext
      * object, pointed to by this->pc.
      *
      * Each returns a parse node tree or null on error.
      */
     Node functionStmt(uint32_t toStringStart,
                       YieldHandling yieldHandling, DefaultHandling defaultHandling,
                       FunctionAsyncKind asyncKind = FunctionAsyncKind::SyncFunction);
     Node functionExpr(uint32_t toStringStart, ExpressionClosure expressionClosureHandling,
                       InvokedPrediction invoked, FunctionAsyncKind asyncKind);
 
-    Node statementList(YieldHandling yieldHandling);
     Node statement(YieldHandling yieldHandling);
     bool maybeParseDirective(Node list, Node pn, bool* cont);
 
     Node blockStatement(YieldHandling yieldHandling,
                         unsigned errorNumber = JSMSG_CURLY_IN_COMPOUND);
     Node doWhileStatement(YieldHandling yieldHandling);
     Node whileStatement(YieldHandling yieldHandling);
 
@@ -645,24 +612,23 @@ class Parser final
 
     // While on a |let| TOK_NAME token, examine |next|.  Indicate whether
     // |next|, the next token already gotten with modifier TokenStream::None,
     // continues a LexicalDeclaration.
     bool nextTokenContinuesLetDeclaration(TokenKind next);
 
     Node lexicalDeclaration(YieldHandling yieldHandling, DeclarationKind kind);
 
-    Node importDeclaration();
-
-    bool processExport(Node node);
-    bool processExportFrom(Node node);
+    inline Node importDeclaration();
+    inline bool processExport(Node node);
+    inline bool processExportFrom(Node node);
 
     Node exportFrom(uint32_t begin, Node specList);
     Node exportBatch(uint32_t begin);
-    bool checkLocalExportNames(Node node);
+    inline bool checkLocalExportNames(Node node);
     Node exportClause(uint32_t begin);
     Node exportFunctionDeclaration(uint32_t begin, uint32_t toStringStart,
                                    FunctionAsyncKind asyncKind = FunctionAsyncKind::SyncFunction);
     Node exportVariableStatement(uint32_t begin);
     Node exportClassDeclaration(uint32_t begin);
     Node exportLexicalDeclaration(uint32_t begin, DeclarationKind kind);
     Node exportDefaultFunctionDeclaration(uint32_t begin, uint32_t toStringStart,
                                           FunctionAsyncKind asyncKind = FunctionAsyncKind::SyncFunction);
@@ -783,94 +749,62 @@ class Parser final
 
     bool argumentList(YieldHandling yieldHandling, Node listNode, bool* isSpread,
                       PossibleError* possibleError = nullptr);
     Node destructuringDeclaration(DeclarationKind kind, YieldHandling yieldHandling,
                                   TokenKind tt);
     Node destructuringDeclarationWithoutYieldOrAwait(DeclarationKind kind, YieldHandling yieldHandling,
                                                      TokenKind tt);
 
-    bool namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet);
-    bool checkExportedName(JSAtom* exportName);
-    bool checkExportedNamesForDeclaration(Node node);
-    bool checkExportedNameForClause(Node node);
-    bool checkExportedNameForFunction(Node node);
-    bool checkExportedNameForClass(Node node);
+    inline bool checkExportedName(JSAtom* exportName);
+    inline bool checkExportedNamesForDeclaration(Node node);
+    inline bool checkExportedNameForFunction(Node node);
+    inline bool checkExportedNameForClass(Node node);
+    inline bool checkExportedNameForClause(Node node);
 
     enum ClassContext { ClassStatement, ClassExpression };
     Node classDefinition(YieldHandling yieldHandling, ClassContext classContext,
                          DefaultHandling defaultHandling);
 
-    bool checkLabelOrIdentifierReference(PropertyName* ident,
-                                         uint32_t offset,
-                                         YieldHandling yieldHandling,
-                                         TokenKind hint = TOK_LIMIT);
-
-    bool checkLocalExportName(PropertyName* ident, uint32_t offset) {
-        return checkLabelOrIdentifierReference(ident, offset, YieldIsName);
-    }
-
     bool checkBindingIdentifier(PropertyName* ident,
                                 uint32_t offset,
                                 YieldHandling yieldHandling,
                                 TokenKind hint = TOK_LIMIT);
 
     PropertyName* labelOrIdentifierReference(YieldHandling yieldHandling);
 
     PropertyName* labelIdentifier(YieldHandling yieldHandling) {
         return labelOrIdentifierReference(yieldHandling);
     }
 
     PropertyName* identifierReference(YieldHandling yieldHandling) {
         return labelOrIdentifierReference(yieldHandling);
     }
 
-    PropertyName* importedBinding() {
-        return bindingIdentifier(YieldIsName);
-    }
-
     Node identifierReference(Handle<PropertyName*> name);
 
     bool matchLabel(YieldHandling yieldHandling, MutableHandle<PropertyName*> label);
 
     // Indicate if the next token (tokenized as Operand) is |in| or |of|.  If
     // so, consume it.
     bool matchInOrOf(bool* isForInp, bool* isForOfp);
 
     bool hasUsedFunctionSpecialName(HandlePropertyName name);
     bool declareFunctionArgumentsObject();
     bool declareFunctionThis();
     Node newInternalDotName(HandlePropertyName name);
     Node newThisName();
     Node newDotGeneratorName();
     bool declareDotGeneratorName();
 
-    bool skipLazyInnerFunction(Node pn, uint32_t toStringStart, FunctionSyntaxKind kind,
-                               bool tryAnnexB);
-    bool innerFunction(Node pn, ParseContext* outerpc, HandleFunction fun, uint32_t toStringStart,
-                       InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind,
-                       GeneratorKind generatorKind, FunctionAsyncKind asyncKind, bool tryAnnexB,
-                       Directives inheritedDirectives, Directives* newDirectives);
-    bool trySyntaxParseInnerFunction(Node pn, HandleFunction fun, uint32_t toStringStart,
-                                     InHandling inHandling, YieldHandling yieldHandling,
-                                     FunctionSyntaxKind kind, GeneratorKind generatorKind,
-                                     FunctionAsyncKind asyncKind, bool tryAnnexB,
-                                     Directives inheritedDirectives, Directives* newDirectives);
-    bool finishFunctionScopes(bool isStandaloneFunction);
-    bool finishFunction(bool isStandaloneFunction = false);
+    inline bool finishFunction(bool isStandaloneFunction = false);
+
     bool leaveInnerFunction(ParseContext* outerpc);
 
-    bool matchOrInsertSemicolon();
-
   public:
-    enum FunctionCallBehavior {
-        PermitAssignmentToFunctionCalls,
-        ForbidAssignmentToFunctionCalls
-    };
-
     bool isValidSimpleAssignmentTarget(Node node,
                                        FunctionCallBehavior behavior = ForbidAssignmentToFunctionCalls);
 
   private:
     const char* nameIsArgumentsOrEval(Node node);
     bool checkIncDecOperand(Node operand, uint32_t operandOffset);
     bool checkStrictAssignment(Node lhs);
 
@@ -879,32 +813,28 @@ class Parser final
     void reportRedeclaration(HandlePropertyName name, DeclarationKind prevKind, TokenPos pos,
                              uint32_t prevPos);
     bool notePositionalFormalParameter(Node fn, HandlePropertyName name, uint32_t beginPos,
                                        bool disallowDuplicateParams, bool* duplicatedParam);
     bool noteDestructuredPositionalFormalParameter(Node fn, Node destruct);
 
     bool checkLexicalDeclarationDirectlyWithinBlock(ParseContext::Statement& stmt,
                                                     DeclarationKind kind, TokenPos pos);
-    bool noteDeclaredName(HandlePropertyName name, DeclarationKind kind, TokenPos pos);
     bool noteUsedName(HandlePropertyName name);
     bool hasUsedName(HandlePropertyName name);
 
-    // Required on Scope exit.
-    bool propagateFreeNamesAndMarkClosedOverBindings(ParseContext::Scope& scope);
-
-    Node finishLexicalScope(ParseContext::Scope& scope, Node body);
+    inline Node finishLexicalScope(ParseContext::Scope& scope, Node body);
 
     Node propertyName(YieldHandling yieldHandling,
                       const mozilla::Maybe<DeclarationKind>& maybeDecl, Node propList,
                       PropertyType* propType, MutableHandleAtom propAtom);
     Node computedPropertyName(YieldHandling yieldHandling,
                               const mozilla::Maybe<DeclarationKind>& maybeDecl, Node literal);
     Node arrayInitializer(YieldHandling yieldHandling, PossibleError* possibleError);
-    Node newRegExp();
+    inline Node newRegExp();
 
     Node objectLiteral(YieldHandling yieldHandling, PossibleError* possibleError);
 
     Node bindingInitializer(Node lhs, DeclarationKind kind, YieldHandling yieldHandling);
     Node bindingIdentifier(DeclarationKind kind, YieldHandling yieldHandling);
     Node bindingIdentifierOrPattern(DeclarationKind kind, YieldHandling yieldHandling,
                                     TokenKind tt);
     Node objectBindingPattern(DeclarationKind kind, YieldHandling yieldHandling);
@@ -923,20 +853,363 @@ class Parser final
     bool checkDestructuringAssignmentElement(Node expr, TokenPos exprPos,
                                              PossibleError* exprPossibleError,
                                              PossibleError* possibleError);
 
     Node newNumber(const Token& tok) {
         return handler.newNumber(tok.number(), tok.decimalPoint(), tok.pos);
     }
 
+  protected:
     static Node null() { return ParseHandler::null(); }
 
+    Node stringLiteral();
+
+    inline Node newName(PropertyName* name);
+    inline Node newName(PropertyName* name, TokenPos pos);
+
+    // Match the current token against the BindingIdentifier production with
+    // the given Yield parameter.  If there is no match, report a syntax
+    // error.
+    PropertyName* bindingIdentifier(YieldHandling yieldHandling);
+
+    bool checkLabelOrIdentifierReference(PropertyName* ident, uint32_t offset,
+                                         YieldHandling yieldHandling, TokenKind hint = TOK_LIMIT);
+
+    Node statementList(YieldHandling yieldHandling);
+
+    bool innerFunction(Node pn, ParseContext* outerpc, HandleFunction fun, uint32_t toStringStart,
+                       InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind,
+                       GeneratorKind generatorKind, FunctionAsyncKind asyncKind, bool tryAnnexB,
+                       Directives inheritedDirectives, Directives* newDirectives);
+
+    bool finishFunctionScopes(bool isStandaloneFunction);
+
+    bool matchOrInsertSemicolon();
+
+    bool noteDeclaredName(HandlePropertyName name, DeclarationKind kind, TokenPos pos);
+
+    // Required on Scope exit.
+    bool propagateFreeNamesAndMarkClosedOverBindings(ParseContext::Scope& scope);
+
+  private:
     JSAtom* prefixAccessorName(PropertyType propType, HandleAtom propAtom);
 
+    inline bool asmJS(Node list);
+};
+
+template <typename CharT>
+class Parser<SyntaxParseHandler, CharT> final
+  : public GeneralParser<SyntaxParseHandler, CharT>
+{
+    using Base = GeneralParser<SyntaxParseHandler, CharT>;
+    using Node = SyntaxParseHandler::Node;
+
+    using SyntaxParser = Parser<SyntaxParseHandler, CharT>;
+
+    // Numerous Base::* functions have bodies like
+    //
+    //   return asFinalParser()->func(...);
+    //
+    // and must be able to call functions here.  Add a friendship relationship
+    // so functions here can be hidden when appropriate.
+    friend class GeneralParser<SyntaxParseHandler, CharT>;
+
+  public:
+    using Base::Base;
+
+    // Inherited types, listed here to have non-dependent names.
+    using typename Base::Modifier;
+
+    // Inherited functions, listed here to have non-dependent names.
+
+  public:
+    using Base::anyChars;
+    using Base::context;
+    using Base::innerFunctionForFunctionBox;
+    using Base::tokenStream;
+
+  private:
+    using Base::alloc;
+#if DEBUG
+    using Base::checkOptionsCalled;
+#endif
+    using Base::error;
+    using Base::errorAt;
+    using Base::finishFunctionScopes;
+    using Base::functionFormalParametersAndBody;
+    using Base::handler;
+    using Base::innerFunction;
+    using Base::keepAtoms;
+    using Base::matchOrInsertSemicolon;
+    using Base::newFunctionBox;
+    using Base::newLexicalScopeData;
+    using Base::newModuleScopeData;
+    using Base::newName;
+    using Base::noteDeclaredName;
+    using Base::null;
+    using Base::options;
+    using Base::pc;
+    using Base::pos;
+    using Base::propagateFreeNamesAndMarkClosedOverBindings;
+    using Base::ss;
+    using Base::statementList;
+    using Base::stringLiteral;
+    using Base::syntaxParser_;
+    using Base::usedNames;
+
+  public:
+    // Functions with multiple overloads of different visibility.  We can't
+    // |using| the whole thing into existence because of the visibility
+    // distinction, so we instead must manually delegate the required overload.
+
+    PropertyName* bindingIdentifier(YieldHandling yieldHandling) {
+        return Base::bindingIdentifier(yieldHandling);
+    }
+
+    // Functions present in both Parser<ParseHandler, CharT> specializations.
+
+    inline void setAwaitHandling(AwaitHandling awaitHandling);
+
+    Node newRegExp();
+
+    // Parse a module.
+    Node moduleBody(ModuleSharedContext* modulesc);
+
+    inline Node importDeclaration();
+    inline bool processExport(Node node);
+    inline bool processExportFrom(Node node);
+    inline bool checkLocalExportNames(Node node);
+    inline bool checkExportedName(JSAtom* exportName);
+    inline bool checkExportedNamesForDeclaration(Node node);
+    inline bool checkExportedNameForFunction(Node node);
+    inline bool checkExportedNameForClass(Node node);
+    inline bool checkExportedNameForClause(Node node);
+
+    bool trySyntaxParseInnerFunction(Node node, HandleFunction fun, uint32_t toStringStart,
+                                     InHandling inHandling, YieldHandling yieldHandling,
+                                     FunctionSyntaxKind kind, GeneratorKind generatorKind,
+                                     FunctionAsyncKind asyncKind, bool tryAnnexB,
+                                     Directives inheritedDirectives, Directives* newDirectives);
+
+    bool skipLazyInnerFunction(Node node, uint32_t toStringStart, FunctionSyntaxKind kind,
+                               bool tryAnnexB);
+
+    Node finishLexicalScope(ParseContext::Scope& scope, Node body);
+
+    bool finishFunction(bool isStandaloneFunction = false);
+
+#define ABORTED_SYNTAX_PARSE_SENTINEL reinterpret_cast<SyntaxParser*>(0x1)
+
+    // Flag the current syntax parse as aborted due to unsupported language
+    // constructs and return false.  Aborting the current syntax parse does not
+    // disable attempts to syntax parse future inner functions.
+    bool abortIfSyntaxParser() {
+        syntaxParser_ = ABORTED_SYNTAX_PARSE_SENTINEL;
+        return false;
+    }
+
+    // Return whether the last syntax parse was aborted due to unsupported
+    // language constructs.
+    bool hadAbortedSyntaxParse() {
+        return syntaxParser_ == ABORTED_SYNTAX_PARSE_SENTINEL;
+    }
+
+    // Clear whether the last syntax parse was aborted.
+    void clearAbortedSyntaxParse() {
+        syntaxParser_ = nullptr;
+    }
+
+    // Disable syntax parsing of all future inner functions during this
+    // full-parse.
+    void disableSyntaxParser() {}
+
+#undef ABORTED_SYNTAX_PARSE_SENTINEL
+
+    bool asmJS(Node list);
+
+    // Functions present only in Parser<SyntaxParseHandler, CharT>.
+};
+
+template <typename CharT>
+class Parser<FullParseHandler, CharT> final
+  : public GeneralParser<FullParseHandler, CharT>
+{
+    using Base = GeneralParser<FullParseHandler, CharT>;
+    using Node = FullParseHandler::Node;
+
+    using SyntaxParser = Parser<SyntaxParseHandler, CharT>;
+
+    // Numerous Base::* functions have bodies like
+    //
+    //   return asFinalParser()->func(...);
+    //
+    // and must be able to call functions here.  Add a friendship relationship
+    // so functions here can be hidden when appropriate.
+    friend class GeneralParser<FullParseHandler, CharT>;
+
+  public:
+    Parser(JSContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options,
+           const CharT* chars, size_t length, bool foldConstants, UsedNameTracker& usedNames,
+           SyntaxParser* syntaxParser, LazyScript* lazyOuterFunction)
+      : Base(cx, alloc, options, chars, length, foldConstants, usedNames, syntaxParser,
+             lazyOuterFunction)
+    {
+        // The Mozilla specific JSOPTION_EXTRA_WARNINGS option adds extra warnings
+        // which are not generated if functions are parsed lazily. Note that the
+        // standard "use strict" does not inhibit lazy parsing.
+        if (options.extraWarningsOption)
+            disableSyntaxParser();
+    }
+
+    using Base::Base;
+
+    // Inherited types, listed here to have non-dependent names.
+    using typename Base::Modifier;
+
+    // Inherited functions, listed here to have non-dependent names.
+
+  public:
+    using Base::anyChars;
+    using Base::functionFormalParametersAndBody;
+    using Base::handler;
+    using Base::newFunctionBox;
+    using Base::options;
+    using Base::pc;
+    using Base::pos;
+    using Base::ss;
+    using Base::tokenStream;
+
+  private:
+    using Base::alloc;
+    using Base::checkLabelOrIdentifierReference;
+#if DEBUG
+    using Base::checkOptionsCalled;
+#endif
+    using Base::context;
+    using Base::error;
+    using Base::errorAt;
+    using Base::finishFunctionScopes;
+    using Base::innerFunction;
+    using Base::innerFunctionForFunctionBox;
+    using Base::keepAtoms;
+    using Base::matchOrInsertSemicolon;
+    using Base::newEvalScopeData;
+    using Base::newFunctionScopeData;
+    using Base::newGlobalScopeData;
+    using Base::newLexicalScopeData;
+    using Base::newModuleScopeData;
+    using Base::newName;
+    using Base::newVarScopeData;
+    using Base::noteDeclaredName;
+    using Base::null;
+    using Base::propagateFreeNamesAndMarkClosedOverBindings;
+    using Base::statementList;
+    using Base::stringLiteral;
+    using Base::syntaxParser_;
+    using Base::usedNames;
+
+  public:
+    // Functions with multiple overloads of different visibility.  We can't
+    // |using| the whole thing into existence because of the visibility
+    // distinction, so we instead must manually delegate the required overload.
+
+    PropertyName* bindingIdentifier(YieldHandling yieldHandling) {
+        return Base::bindingIdentifier(yieldHandling);
+    }
+
+    // Functions present in both Parser<ParseHandler, CharT> specializations.
+
+    friend class AutoAwaitIsKeyword<GeneralParser<SyntaxParseHandler, CharT>>;
+    inline void setAwaitHandling(AwaitHandling awaitHandling);
+
+    Node newRegExp();
+
+    // Parse a module.
+    Node moduleBody(ModuleSharedContext* modulesc);
+
+    Node importDeclaration();
+    bool processExport(Node node);
+    bool processExportFrom(Node node);
+    bool checkLocalExportNames(Node node);
+    bool checkExportedName(JSAtom* exportName);
+    bool checkExportedNamesForDeclaration(Node node);
+    bool checkExportedNameForFunction(Node node);
+    bool checkExportedNameForClass(Node node);
+    inline bool checkExportedNameForClause(Node node);
+
+    bool trySyntaxParseInnerFunction(Node pn, HandleFunction fun, uint32_t toStringStart,
+                                     InHandling inHandling, YieldHandling yieldHandling,
+                                     FunctionSyntaxKind kind, GeneratorKind generatorKind,
+                                     FunctionAsyncKind asyncKind, bool tryAnnexB,
+                                     Directives inheritedDirectives, Directives* newDirectives);
+
+    bool skipLazyInnerFunction(Node node, uint32_t toStringStart, FunctionSyntaxKind kind,
+                               bool tryAnnexB);
+
+    Node finishLexicalScope(ParseContext::Scope& scope, Node body);
+
+    bool finishFunction(bool isStandaloneFunction = false);
+
+    // Functions present only in Parser<FullParseHandler, CharT>.
+
+    // Parse the body of an eval.
+    //
+    // Eval scripts are distinguished from global scripts in that in ES6, per
+    // 18.2.1.1 steps 9 and 10, all eval scripts are executed under a fresh
+    // lexical scope.
+    Node evalBody(EvalSharedContext* evalsc);
+
+    // Parse a function, given only its arguments and body. Used for lazily
+    // parsed functions.
+    Node standaloneLazyFunction(HandleFunction fun, uint32_t toStringStart, bool strict,
+                                GeneratorKind generatorKind, FunctionAsyncKind asyncKind);
+
+    // Parse a function, used for the Function, GeneratorFunction, and
+    // AsyncFunction constructors.
+    Node standaloneFunction(HandleFunction fun, HandleScope enclosingScope,
+                            const mozilla::Maybe<uint32_t>& parameterListEnd,
+                            GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
+                            Directives inheritedDirectives, Directives* newDirectives);
+
+    bool abortIfSyntaxParser() {
+        disableSyntaxParser();
+        return true;
+    }
+
+    // Return whether the last syntax parse was aborted due to unsupported
+    // language constructs.
+    bool hadAbortedSyntaxParse() {
+        return false;
+    }
+
+    // Clear whether the last syntax parse was aborted.
+    void clearAbortedSyntaxParse() {}
+
+    // Disable syntax parsing of all future inner functions during this
+    // full-parse.
+    void disableSyntaxParser() {
+        syntaxParser_ = nullptr;
+    }
+
+    bool checkStatementsEOF();
+
+    // Parse the body of a global script.
+    Node globalBody(GlobalSharedContext* globalsc);
+
+    bool namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet);
+
+    PropertyName* importedBinding() {
+        return bindingIdentifier(YieldIsName);
+    }
+
+    bool checkLocalExportName(PropertyName* ident, uint32_t offset) {
+        return checkLabelOrIdentifierReference(ident, offset, YieldIsName);
+    }
+
     bool asmJS(Node list);
 };
 
 template<class Parser>
 /* static */ inline TokenStreamAnyChars&
 ParserAnyCharsAccess<Parser>::anyChars(TokenStreamChars* ts)
 {
     // The structure we're walking through looks like this:
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -2363,24 +2363,24 @@ TokenKindToString(TokenKind tt)
 #endif
 
 template class frontend::TokenStreamCharsBase<char16_t>;
 
 template class frontend::TokenStreamChars<char16_t, frontend::TokenStreamAnyCharsAccess>;
 template class frontend::TokenStreamSpecific<char16_t, frontend::TokenStreamAnyCharsAccess>;
 
 template class
-frontend::TokenStreamChars<char16_t, frontend::ParserAnyCharsAccess<frontend::Parser<frontend::FullParseHandler, char16_t>>>;
+frontend::TokenStreamChars<char16_t, frontend::ParserAnyCharsAccess<frontend::GeneralParser<frontend::FullParseHandler, char16_t>>>;
 template class
-frontend::TokenStreamChars<char16_t, frontend::ParserAnyCharsAccess<frontend::Parser<frontend::SyntaxParseHandler, char16_t>>>;
+frontend::TokenStreamChars<char16_t, frontend::ParserAnyCharsAccess<frontend::GeneralParser<frontend::SyntaxParseHandler, char16_t>>>;
 
 template class
-frontend::TokenStreamSpecific<char16_t, frontend::ParserAnyCharsAccess<frontend::Parser<frontend::FullParseHandler, char16_t>>>;
+frontend::TokenStreamSpecific<char16_t, frontend::ParserAnyCharsAccess<frontend::GeneralParser<frontend::FullParseHandler, char16_t>>>;
 template class
-frontend::TokenStreamSpecific<char16_t, frontend::ParserAnyCharsAccess<frontend::Parser<frontend::SyntaxParseHandler, char16_t>>>;
+frontend::TokenStreamSpecific<char16_t, frontend::ParserAnyCharsAccess<frontend::GeneralParser<frontend::SyntaxParseHandler, char16_t>>>;
 
 } // namespace frontend
 
 } // namespace js
 
 
 JS_FRIEND_API(int)
 js_fgets(char* buf, int size, FILE* file)