Bug 1371105 - De-templatize parse handlers. (r=jorendorff)
authorShu-yu Guo <shu@rfrn.org>
Fri, 09 Jun 2017 17:44:21 -0700
changeset 411495 07fcbdb153e29d0fec4420aac2593150365bf7c3
parent 411494 6fdd4f87cc0ea91ee036b38fa1ce58f7eaea88ea
child 411496 f40ea9d14a1114c305108475053a5353fceb9304
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs1371105
milestone55.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 1371105 - De-templatize parse handlers. (r=jorendorff) The ParseHandlers were made to take a CharT because FullParseHandler held a pointer to a syntax parser for syntax parsing. This patch moves that pointer into Parser to detemplatize the handlers. It also collapses the |abortedSyntaxParse| flag, which was already on the Parser, and re-uses the new |syntaxParser| field in Parser to track if a syntax parse was aborted.
js/src/frontend/EitherParser.h
js/src/frontend/FoldConstants.h
js/src/frontend/FullParseHandler.h
js/src/frontend/ParseNode.cpp
js/src/frontend/ParseNode.h
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/frontend/SyntaxParseHandler.h
js/src/wasm/AsmJS.h
--- a/js/src/frontend/EitherParser.h
+++ b/js/src/frontend/EitherParser.h
@@ -19,22 +19,22 @@
 #include "mozilla/Variant.h"
 
 #include "frontend/Parser.h"
 #include "frontend/TokenStream.h"
 
 namespace js {
 namespace frontend {
 
-template<template<typename CharT> class ParseHandler>
+template<class ParseHandler>
 class EitherParser
 {
     const mozilla::Variant<Parser<ParseHandler, char16_t>* const> parser;
 
-    using Node = typename ParseHandler<char16_t>::Node;
+    using Node = typename ParseHandler::Node;
 
   public:
     template<class Parser>
     explicit EitherParser(Parser* parser) : parser(parser) {}
 
   private:
     struct TokenStreamMatcher
     {
--- a/js/src/frontend/FoldConstants.h
+++ b/js/src/frontend/FoldConstants.h
@@ -7,18 +7,18 @@
 #ifndef frontend_FoldConstants_h
 #define frontend_FoldConstants_h
 
 #include "frontend/SyntaxParseHandler.h"
 
 namespace js {
 namespace frontend {
 
-template <typename CharT> class FullParseHandler;
-template <template <typename CharT> class ParseHandler, typename CharT> class Parser;
+class FullParseHandler;
+template <class ParseHandler, typename CharT> class Parser;
 
 // 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.
 //
@@ -29,17 +29,17 @@ template <template <typename CharT> clas
 //    if (!FoldConstants(cx, &pn, parser))
 //        return false;
 template<typename CharT>
 MOZ_MUST_USE bool
 FoldConstants(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler, CharT>* parser);
 
 template<typename CharT>
 inline MOZ_MUST_USE bool
-FoldConstants(JSContext* cx, typename SyntaxParseHandler<CharT>::Node* pnp,
+FoldConstants(JSContext* cx, typename SyntaxParseHandler::Node* pnp,
               Parser<SyntaxParseHandler, CharT>* parser)
 {
     return true;
 }
 
 } /* namespace frontend */
 } /* namespace js */
 
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -13,24 +13,19 @@
 #include <string.h>
 
 #include "frontend/ParseNode.h"
 #include "frontend/SharedContext.h"
 
 namespace js {
 namespace frontend {
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-class Parser;
-
-template <typename CharT> class SyntaxParseHandler;
-
 // Parse handler used when generating a full parse tree for all code which the
 // parser encounters.
-class FullParseHandlerBase
+class FullParseHandler
 {
     ParseNodeAllocator allocator;
 
     ParseNode* allocParseNode(size_t size) {
         MOZ_ASSERT(size == sizeof(ParseNode));
         return static_cast<ParseNode*>(allocator.allocNode());
     }
 
@@ -78,17 +73,17 @@ class FullParseHandlerBase
         return node->isInParens() && (node->isKind(PNK_OBJECT) || node->isKind(PNK_ARRAY));
     }
 
     static bool isDestructuringPatternAnyParentheses(ParseNode* node) {
         return isUnparenthesizedDestructuringPattern(node) ||
                isParenthesizedDestructuringPattern(node);
     }
 
-    FullParseHandlerBase(JSContext* cx, LifoAlloc& alloc, LazyScript* lazyOuterFunction)
+    FullParseHandler(JSContext* cx, LifoAlloc& alloc, LazyScript* lazyOuterFunction)
       : allocator(cx, alloc),
         lazyOuterFunction_(cx, lazyOuterFunction),
         lazyInnerFunctionIndex(0),
         lazyClosedOverBindingIndex(0)
     {}
 
     static ParseNode* null() { return nullptr; }
 
@@ -934,31 +929,31 @@ class FullParseHandlerBase
     }
     JSAtom* nextLazyClosedOverBinding() {
         MOZ_ASSERT(lazyClosedOverBindingIndex < lazyOuterFunction()->numClosedOverBindings());
         return lazyOuterFunction()->closedOverBindings()[lazyClosedOverBindingIndex++];
     }
 };
 
 inline bool
-FullParseHandlerBase::addCatchBlock(ParseNode* catchList, ParseNode* lexicalScope,
-                                    ParseNode* catchName, ParseNode* catchGuard,
-                                    ParseNode* catchBody)
+FullParseHandler::addCatchBlock(ParseNode* catchList, ParseNode* lexicalScope,
+                                ParseNode* catchName, ParseNode* catchGuard,
+                                ParseNode* catchBody)
 {
     ParseNode* catchpn = newTernary(PNK_CATCH, catchName, catchGuard, catchBody);
     if (!catchpn)
         return false;
     catchList->append(lexicalScope);
     lexicalScope->setScopeBody(catchpn);
     return true;
 }
 
 inline bool
-FullParseHandlerBase::setLastFunctionFormalParameterDefault(ParseNode* funcpn,
-                                                            ParseNode* defaultValue)
+FullParseHandler::setLastFunctionFormalParameterDefault(ParseNode* funcpn,
+                                                        ParseNode* defaultValue)
 {
     ParseNode* arg = funcpn->pn_body->last();
     ParseNode* pn = newBinary(PNK_ASSIGN, arg, defaultValue, JSOP_NOP);
     if (!pn)
         return false;
 
     checkAndSetIsDirectRHSAnonFunction(defaultValue);
 
@@ -976,51 +971,22 @@ FullParseHandlerBase::setLastFunctionFor
         pnchild->pn_next = pn;
     }
     funcpn->pn_body->pn_tail = &pn->pn_next;
 
     return true;
 }
 
 inline bool
-FullParseHandlerBase::finishInitializerAssignment(ParseNode* pn, ParseNode* init)
+FullParseHandler::finishInitializerAssignment(ParseNode* pn, ParseNode* init)
 {
     pn->pn_expr = init;
     pn->setOp(JSOP_SETNAME);
 
     /* The declarator's position must include the initializer. */
     pn->pn_pos.end = init->pn_pos.end;
     return true;
 }
 
-template<typename CharT>
-class FullParseHandler : public FullParseHandlerBase
-{
-    using SyntaxParser = Parser<SyntaxParseHandler, CharT>;
-
-    /*
-     * If non-nullptr, points to a syntax parser which can be used for inner
-     * functions. Cleared if language features not handled by the syntax parser
-     * are encountered, in which case all future activity will use the full
-     * parser.
-     */
-    SyntaxParser* syntaxParser;
-
-  public:
-    FullParseHandler(JSContext* cx, LifoAlloc& alloc, SyntaxParser* syntaxParser,
-                     LazyScript* lazyOuterFunction)
-      : FullParseHandlerBase(cx, alloc, lazyOuterFunction),
-        syntaxParser(syntaxParser)
-    {}
-
-    SyntaxParser* getSyntaxParser() const {
-        return syntaxParser;
-    }
-
-    void disableSyntaxParser() {
-        syntaxParser = nullptr;
-    }
-};
-
 } // namespace frontend
 } // namespace js
 
 #endif /* frontend_FullParseHandler_h */
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -567,17 +567,17 @@ ParseNodeAllocator::allocNode()
     void* p = alloc.alloc(sizeof (ParseNode));
     if (!p)
         ReportOutOfMemory(cx);
     return p;
 }
 
 ParseNode*
 ParseNode::appendOrCreateList(ParseNodeKind kind, JSOp op, ParseNode* left, ParseNode* right,
-                              FullParseHandlerBase* handler, ParseContext* pc)
+                              FullParseHandler* handler, ParseContext* pc)
 {
     // The asm.js specification is written in ECMAScript grammar terms that
     // specify *only* a binary tree.  It's a royal pain to implement the asm.js
     // spec to act upon n-ary lists as created below.  So for asm.js, form a
     // binary tree of lists exactly as ECMAScript would by skipping the
     // following optimization.
     if (!pc->useAsmOrInsideUseAsm()) {
         // Left-associative trees of a given operator (e.g. |a + b + c|) are
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -11,17 +11,17 @@
 
 #include "builtin/ModuleObject.h"
 #include "frontend/TokenStream.h"
 
 namespace js {
 namespace frontend {
 
 class ParseContext;
-class FullParseHandlerBase;
+class FullParseHandler;
 class FunctionBox;
 class ObjectBox;
 
 #define FOR_EACH_PARSE_NODE_KIND(F) \
     F(NOP) \
     F(SEMI) \
     F(COMMA) \
     F(CONDITIONAL) \
@@ -605,17 +605,17 @@ class ParseNode
 
   public:
     /*
      * If |left| is a list of the given kind/left-associative op, append
      * |right| to it and return |left|.  Otherwise return a [left, right] list.
      */
     static ParseNode*
     appendOrCreateList(ParseNodeKind kind, JSOp op, ParseNode* left, ParseNode* right,
-                       FullParseHandlerBase* handler, ParseContext* pc);
+                       FullParseHandler* handler, ParseContext* pc);
 
     inline PropertyName* name() const;
     inline JSAtom* atom() const;
 
     ParseNode* expr() const {
         MOZ_ASSERT(pn_arity == PN_NAME || pn_arity == PN_CODE);
         return pn_expr;
     }
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -762,32 +762,76 @@ ParserBase::reportNoOffset(ParseReportKi
                                                            errorNumber, args);
         break;
     }
 
     va_end(args);
     return result;
 }
 
+#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()
 {
-    handler.disableSyntaxParser();
+    disableSyntaxParser();
     return true;
 }
 
 template <>
 inline bool
 Parser<SyntaxParseHandler, char16_t>::abortIfSyntaxParser()
 {
-    abortedSyntaxParse = true;
+    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,
                        LazyScript* lazyOuterFunction)
   : context(cx),
     alloc(alloc),
@@ -796,17 +840,16 @@ ParserBase::ParserBase(JSContext* cx, Li
     pc(nullptr),
     usedNames(usedNames),
     ss(nullptr),
     keepAtoms(cx),
     foldConstants(foldConstants),
 #ifdef DEBUG
     checkOptionsCalled(false),
 #endif
-    abortedSyntaxParse(false),
     isUnexpectedEOF_(false),
     awaitIsKeyword_(false)
 {
     cx->frontendCollectionPool().addActiveCompilation();
     tempPoolMark = alloc.mark();
 }
 
 ParserBase::~ParserBase()
@@ -818,47 +861,48 @@ ParserBase::~ParserBase()
      * Eagerly free the memory now (which otherwise won't be freed until the
      * next GC) to avoid unnecessary OOMs.
      */
     alloc.freeAllIfHugeAndUnused();
 
     context->frontendCollectionPool().removeActiveCompilation();
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+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,
-                                    Parser<SyntaxParseHandler, CharT>* syntaxParser,
+                                    SyntaxParser* syntaxParser,
                                     LazyScript* lazyOuterFunction)
   : ParserBase(cx, alloc, options, chars, length, foldConstants, usedNames, lazyOuterFunction),
     AutoGCRooter(cx, PARSER),
-    handler(cx, alloc, syntaxParser, lazyOuterFunction)
+    syntaxParser_(syntaxParser),
+    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)
-        handler.disableSyntaxParser();
-}
-
-template<template <typename CharT> class ParseHandler, typename CharT>
+        disableSyntaxParser();
+}
+
+template<class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::checkOptions()
 {
 #ifdef DEBUG
     checkOptionsCalled = true;
 #endif
 
     return tokenStream.checkOptions();
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 Parser<ParseHandler, CharT>::~Parser()
 {
     MOZ_ASSERT(checkOptionsCalled);
 }
 
 template <>
 void
 Parser<SyntaxParseHandler, char16_t>::setAwaitIsKeyword(bool isKeyword)
@@ -866,18 +910,18 @@ Parser<SyntaxParseHandler, char16_t>::se
     awaitIsKeyword_ = isKeyword;
 }
 
 template <>
 void
 Parser<FullParseHandler, char16_t>::setAwaitIsKeyword(bool isKeyword)
 {
     awaitIsKeyword_ = isKeyword;
-    if (Parser<SyntaxParseHandler, char16_t>* parser = handler.getSyntaxParser())
-        parser->setAwaitIsKeyword(isKeyword);
+    if (syntaxParser_)
+        syntaxParser_->setAwaitIsKeyword(isKeyword);
 }
 
 ObjectBox*
 ParserBase::newObjectBox(JSObject* obj)
 {
     MOZ_ASSERT(obj);
 
     /*
@@ -894,17 +938,17 @@ ParserBase::newObjectBox(JSObject* obj)
         return nullptr;
     }
 
     traceListHead = objbox;
 
     return objbox;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 FunctionBox*
 Parser<ParseHandler, CharT>::newFunctionBox(Node fn, JSFunction* fun, uint32_t toStringStart,
                                             Directives inheritedDirectives,
                                             GeneratorKind generatorKind, FunctionAsyncKind asyncKind)
 {
     MOZ_ASSERT(fun);
 
     /*
@@ -936,34 +980,34 @@ ModuleSharedContext::ModuleSharedContext
     module_(cx, module),
     enclosingScope_(cx, enclosingScope),
     bindings(cx),
     builder(builder)
 {
     thisBinding_ = ThisBinding::Module;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 void
 Parser<ParseHandler, CharT>::trace(JSTracer* trc)
 {
     ObjectBox::TraceList(trc, traceListHead);
 }
 
 void
 TraceParser(JSTracer* trc, AutoGCRooter* parser)
 {
     static_cast<Parser<FullParseHandler, char16_t>*>(parser)->trace(trc);
 }
 
 /*
  * Parse a top-level JS script.
  */
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::parse()
 {
     MOZ_ASSERT(checkOptionsCalled);
 
     Directives directives(options().strictOption);
     GlobalSharedContext globalsc(context, ScopeKind::Global,
                                  directives, options().extraWarningsOption);
     ParseContext globalpc(this, &globalsc, /* newDirectives = */ nullptr);
@@ -1026,17 +1070,17 @@ ParserBase::hasValidSimpleStrictParamete
     for (auto* name : pc->positionalFormalParameterNames()) {
         MOZ_ASSERT(name);
         if (!isValidStrictBinding(name->asPropertyName()))
             return false;
     }
     return true;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 void
 Parser<ParseHandler, CharT>::reportMissingClosing(unsigned errorNumber, unsigned noteNumber,
                                                   uint32_t openedPos)
 {
     auto notes = MakeUnique<JSErrorNotes>();
     if (!notes) {
         ReportOutOfMemory(pc->sc()->context);
         return;
@@ -1057,17 +1101,17 @@ Parser<ParseHandler, CharT>::reportMissi
                              noteNumber, lineNumber, columnNumber))
     {
         return;
     }
 
     errorWithNotes(Move(notes), errorNumber);
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 void
 Parser<ParseHandler, CharT>::reportRedeclaration(HandlePropertyName name, DeclarationKind prevKind,
                                                  TokenPos pos, uint32_t prevPos)
 {
     JSAutoByteString bytes;
     if (!AtomToPrintableString(context, name, &bytes))
         return;
 
@@ -1108,17 +1152,17 @@ Parser<ParseHandler, CharT>::reportRedec
 // function definition and the arguments specified by the Function
 // constructor.
 //
 // 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 <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::notePositionalFormalParameter(Node fn, HandlePropertyName name,
                                                            uint32_t beginPos,
                                                            bool disallowDuplicateParams,
                                                            bool* duplicatedParam)
 {
     if (AddDeclaredNamePtr p = pc->functionScope().lookupDeclaredNameForAdd(name)) {
         if (disallowDuplicateParams) {
@@ -1153,17 +1197,17 @@ Parser<ParseHandler, CharT>::notePositio
     Node paramNode = newName(name);
     if (!paramNode)
         return false;
 
     handler.addFunctionFormalParameter(fn, paramNode);
     return true;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 bool
 Parser<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;
@@ -1399,17 +1443,17 @@ ParseContext::annexBAppliesToLexicalFunc
         }
     }
 
     // If an early error would have occurred already, this function should not
     // exhibit Annex B.3.3 semantics.
     return !redeclaredKind;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 bool
 Parser<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
@@ -1423,17 +1467,17 @@ Parser<ParseHandler, CharT>::checkLexica
                 : JSMSG_LEXICAL_DECL_NOT_IN_BLOCK,
                 DeclarationKindString(kind));
         return false;
     }
 
     return true;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 bool
 Parser<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;
@@ -1593,17 +1637,17 @@ Parser<ParseHandler, CharT>::noteDeclare
         MOZ_CRASH("Synthesized Annex B vars should go through "
                   "tryDeclareVarForAnnexBLexicalFunction");
         break;
     }
 
     return true;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 bool
 Parser<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;
 
@@ -1618,43 +1662,43 @@ Parser<ParseHandler, CharT>::noteUsedNam
     // optimization.
     ParseContext::Scope* scope = pc->innermostScope();
     if (pc->sc()->isGlobalContext() && scope == &pc->varScope())
         return true;
 
     return usedNames.noteUse(context, name, pc->scriptId(), scope->id());
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::hasUsedName(HandlePropertyName name)
 {
     if (UsedNamePtr p = usedNames.lookup(name))
         return p->value().isUsedInScript(pc->scriptId());
     return false;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 bool
 Parser<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
         // array.
         while (JSAtom* name = handler.nextLazyClosedOverBinding())
             scope.lookupDeclaredName(name)->value()->setClosedOver();
         return true;
     }
 
-    bool isSyntaxParser = mozilla::IsSame<ParseHandler<CharT>, SyntaxParseHandler<CharT>>::value;
+    bool isSyntaxParser = mozilla::IsSame<ParseHandler, SyntaxParseHandler>::value;
     uint32_t scriptId = pc->scriptId();
     uint32_t scopeId = scope.id();
     for (BindingIter bi = scope.bindings(pc); bi; bi++) {
         if (UsedNamePtr p = usedNames.lookup(bi.name())) {
             bool closedOver;
             p->value().noteBoundInScope(scriptId, scopeId, &closedOver);
             if (closedOver) {
                 bi.setClosedOver();
@@ -2057,17 +2101,17 @@ ParserBase::newLexicalScopeData(ParseCon
         PodCopy(cursor, consts.begin(), consts.length());
         bindings->length = numBindings;
     }
 
     return Some(bindings);
 }
 
 template <>
-SyntaxParseHandlerBase::Node
+SyntaxParseHandler::Node
 Parser<SyntaxParseHandler, char16_t>::finishLexicalScope(ParseContext::Scope& scope, Node body)
 {
     if (!propagateFreeNamesAndMarkClosedOverBindings(scope))
         return null();
     return body;
 }
 
 template <>
@@ -2288,32 +2332,32 @@ Parser<FullParseHandler, char16_t>::modu
     if (!bindings)
         return nullptr;
 
     modulesc->bindings = *bindings;
     return mn;
 }
 
 template <>
-SyntaxParseHandlerBase::Node
+SyntaxParseHandler::Node
 Parser<SyntaxParseHandler, char16_t>::moduleBody(ModuleSharedContext* modulesc)
 {
     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
-    return SyntaxParseHandlerBase::NodeFailure;
-}
-
-template <template <typename CharT> class ParseHandler, typename CharT>
+    return SyntaxParseHandler::NodeFailure;
+}
+
+template <class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::hasUsedFunctionSpecialName(HandlePropertyName name)
 {
     MOZ_ASSERT(name == context->names().arguments || name == context->names().dotThis);
     return hasUsedName(name) || pc->functionBox()->bindingsAccessedDynamically();
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 bool
 Parser<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;
 
@@ -2338,60 +2382,60 @@ Parser<ParseHandler, CharT>::declareFunc
             return false;
         }
         funbox->setHasThisBinding();
     }
 
     return true;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::newInternalDotName(HandlePropertyName name)
 {
     Node nameNode = newName(name);
     if (!nameNode)
         return null();
     if (!noteUsedName(name))
         return null();
     return nameNode;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::newThisName()
 {
     return newInternalDotName(context->names().dotThis);
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::newDotGeneratorName()
 {
     return newInternalDotName(context->names().dotGenerator);
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 bool
 Parser<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 <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::finishFunctionScopes(bool isStandaloneFunction)
 {
     FunctionBox* funbox = pc->functionBox();
 
     if (funbox->hasParameterExprs) {
         if (!propagateFreeNamesAndMarkClosedOverBindings(pc->functionScope()))
             return false;
@@ -2583,17 +2627,17 @@ Parser<FullParseHandler, char16_t>::stan
     }
 
     if (!FoldConstants(context, &fn, this))
         return null();
 
     return fn;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::declareFunctionArgumentsObject()
 {
     FunctionBox* funbox = pc->functionBox();
     ParseContext::Scope& funScope = pc->functionScope();
     ParseContext::Scope& varScope = pc->varScope();
 
     bool hasExtraBodyVarScope = &funScope != &varScope;
@@ -2660,18 +2704,18 @@ Parser<ParseHandler, CharT>::declareFunc
         // eagerly so the Debugger API may observe bindings.
         if (pc->sc()->hasDebuggerStatement())
             funbox->setDefinitelyNeedsArgsObj();
     }
 
     return true;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::functionBody(InHandling inHandling, YieldHandling yieldHandling,
                                           FunctionSyntaxKind kind, FunctionBodyType type)
 {
     MOZ_ASSERT(pc->isFunctionBox());
     MOZ_ASSERT(!pc->funHasReturnExpr && !pc->funHasReturnVoid);
 
 #ifdef DEBUG
     uint32_t startYieldOffset = pc->lastYieldOffset;
@@ -2847,17 +2891,17 @@ ParserBase::newFunction(HandleAtom atom,
     return fun;
 }
 
 /*
  * WARNING: Do not call this function directly.
  * Call either matchOrInsertSemicolonAfterExpression or
  * matchOrInsertSemicolonAfterNonExpression instead, depending on context.
  */
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::matchOrInsertSemicolonHelper(TokenStream::Modifier modifier)
 {
     TokenKind tt = TOK_EOF;
     if (!tokenStream.peekTokenSameLine(&tt, modifier))
         return false;
     if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
         /*
@@ -2885,31 +2929,31 @@ Parser<ParseHandler, CharT>::matchOrInse
     bool matched;
     if (!tokenStream.matchToken(&matched, TOK_SEMI, modifier))
         return false;
     if (!matched && modifier == TokenStream::None)
         tokenStream.addModifierException(TokenStream::OperandIsNone);
     return true;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::matchOrInsertSemicolonAfterExpression()
 {
     return matchOrInsertSemicolonHelper(TokenStream::None);
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::matchOrInsertSemicolonAfterNonExpression()
 {
     return matchOrInsertSemicolonHelper(TokenStream::Operand);
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 bool
 Parser<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.
@@ -2930,17 +2974,17 @@ Parser<ParseHandler, CharT>::leaveInnerF
     if (!outerpc->innerFunctionsForLazy.append(pc->functionBox()->function()))
         return false;
 
     PropagateTransitiveParseFlags(pc->functionBox(), outerpc->sc());
 
     return true;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 JSAtom*
 Parser<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);
@@ -2949,17 +2993,17 @@ Parser<ParseHandler, CharT>::prefixAcces
 
     RootedString str(context, ConcatStrings<CanGC>(context, prefix, propAtom));
     if (!str)
         return nullptr;
 
     return AtomizeString(context, str);
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::functionArguments(YieldHandling yieldHandling,
                                                FunctionSyntaxKind kind,
                                                Node funcpn)
 {
     FunctionBox* funbox = pc->functionBox();
 
     bool parenFreeArrow = false;
@@ -3272,17 +3316,17 @@ template <>
 bool
 Parser<SyntaxParseHandler, char16_t>::skipLazyInnerFunction(Node pn, uint32_t toStringStart,
                                                             FunctionSyntaxKind kind,
                                                             bool tryAnnexB)
 {
     MOZ_CRASH("Cannot skip lazy inner functions when syntax parsing");
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::addExprAndGetNextTemplStrToken(YieldHandling yieldHandling,
                                                             Node nodeList,
                                                             TokenKind* ttp)
 {
     Node pn = expr(InAllowed, yieldHandling, TripledotProhibited);
     if (!pn)
         return false;
@@ -3294,17 +3338,17 @@ Parser<ParseHandler, CharT>::addExprAndG
     if (tt != TOK_RC) {
         error(JSMSG_TEMPLSTR_UNTERM_EXPR);
         return false;
     }
 
     return tokenStream.getToken(ttp, TokenStream::TemplateTail);
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::taggedTemplate(YieldHandling yieldHandling, Node nodeList,
                                             TokenKind tt)
 {
     Node callSiteObjNode = handler.newCallSiteObject(pos().begin);
     if (!callSiteObjNode)
         return false;
     handler.addList(nodeList, callSiteObjNode);
@@ -3317,18 +3361,18 @@ Parser<ParseHandler, CharT>::taggedTempl
 
         if (!addExprAndGetNextTemplStrToken(yieldHandling, nodeList, &tt))
             return false;
     }
     handler.setEndPosition(nodeList, callSiteObjNode);
     return true;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::templateLiteral(YieldHandling yieldHandling)
 {
     Node pn = noSubstitutionUntaggedTemplate();
     if (!pn)
         return null();
 
     Node nodeList = handler.newList(PNK_TEMPLATE_STRING_LIST, pn);
     if (!nodeList)
@@ -3343,18 +3387,18 @@ Parser<ParseHandler, CharT>::templateLit
         if (!pn)
             return null();
 
         handler.addList(nodeList, pn);
     } while (tt == TOK_TEMPLATE_HEAD);
     return nodeList;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+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 */)
 {
@@ -3440,57 +3484,57 @@ Parser<FullParseHandler, char16_t>::tryS
     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() && generatorKind == NotGenerator && asyncKind == SyncFunction)
             break;
 
-        Parser<SyntaxParseHandler, char16_t>* parser = handler.getSyntaxParser();
-        if (!parser)
+        if (!syntaxParser_)
             break;
 
         UsedNameTracker::RewindToken token = usedNames.getRewindToken();
 
         // Move the syntax parser to the current position in the stream.
         TokenStream::Position position(keepAtoms);
         tokenStream.tell(&position);
-        if (!parser->tokenStream.seek(position, tokenStream))
+        if (!syntaxParser_->tokenStream.seek(position, tokenStream))
             return false;
 
         // Make a FunctionBox before we enter the syntax parser, because |pn|
         // 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 (!parser->innerFunction(SyntaxParseHandlerBase::NodeGeneric, pc, funbox, toStringStart,
-                                   inHandling, yieldHandling, kind,
-                                   inheritedDirectives, newDirectives))
+        if (!syntaxParser_->innerFunction(SyntaxParseHandler::NodeGeneric,
+                                          pc, funbox, toStringStart,
+                                          inHandling, yieldHandling, kind,
+                                          inheritedDirectives, newDirectives))
         {
-            if (parser->hadAbortedSyntaxParse()) {
+            if (syntaxParser_->hadAbortedSyntaxParse()) {
                 // Try again with a full parse. UsedNameTracker needs to be
                 // rewound to just before we tried the syntax parse for
                 // correctness.
-                parser->clearAbortedSyntaxParse();
+                syntaxParser_->clearAbortedSyntaxParse();
                 usedNames.rewind(token);
-                MOZ_ASSERT_IF(!parser->context->helperThread(),
-                              !parser->context->isExceptionPending());
+                MOZ_ASSERT_IF(!syntaxParser_->context->helperThread(),
+                              !syntaxParser_->context->isExceptionPending());
                 break;
             }
             return false;
         }
 
         // Advance this parser over tokens processed by the syntax parser.
-        parser->tokenStream.tell(&position);
-        if (!tokenStream.seek(position, parser->tokenStream))
+        syntaxParser_->tokenStream.tell(&position);
+        if (!tokenStream.seek(position, syntaxParser_->tokenStream))
             return false;
 
         // Update the end position of the parse node.
         pn->pn_pos.end = tokenStream.currentToken().pos.end;
 
         // Append possible Annex B function box only upon successfully parsing.
         if (tryAnnexB && !pc->innermostScope()->addPossibleAnnexBFunctionBox(pc, funbox))
             return false;
@@ -3516,17 +3560,17 @@ Parser<SyntaxParseHandler, char16_t>::tr
                                                                   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 <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::innerFunction(Node pn, ParseContext* outerpc, FunctionBox* funbox,
                                            uint32_t toStringStart,
                                            InHandling inHandling, YieldHandling yieldHandling,
                                            FunctionSyntaxKind kind, Directives inheritedDirectives,
                                            Directives* newDirectives)
 {
     // Note that it is possible for outerpc != this->pc, as we may be
@@ -3540,17 +3584,17 @@ Parser<ParseHandler, CharT>::innerFuncti
         return false;
 
     if (!functionFormalParametersAndBody(inHandling, yieldHandling, pn, kind))
         return false;
 
     return leaveInnerFunction(outerpc);
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+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,
@@ -3576,17 +3620,17 @@ Parser<ParseHandler, CharT>::innerFuncti
 
     // 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> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::appendToCallSiteObj(Node callSiteObj)
 {
     Node cookedNode = noSubstitutionTaggedTemplate();
     if (!cookedNode)
         return false;
 
     JSAtom* atom = tokenStream.getRawTemplateStringAtom();
@@ -3655,17 +3699,17 @@ Parser<FullParseHandler, char16_t>::stan
     }
 
     if (!FoldConstants(context, &pn, this))
         return null();
 
     return pn;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+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 */)
 {
     // Given a properly initialized parse context, try to parse an actual
@@ -3810,18 +3854,18 @@ Parser<ParseHandler, CharT>::functionFor
 
     handler.setEndPosition(body, pos().begin);
     handler.setEndPosition(pn, pos().end);
     handler.setFunctionBody(pn, body);
 
     return true;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::functionStmt(uint32_t toStringStart, YieldHandling yieldHandling,
                                           DefaultHandling defaultHandling,
                                           FunctionAsyncKind asyncKind)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
 
     // In sloppy mode, Annex B.3.2 allows labelled function declarations.
     // Otherwise it's a parse error.
@@ -3904,18 +3948,18 @@ Parser<ParseHandler, CharT>::functionStm
     // Scope::propagateAndMarkAnnexBFunctionBoxes.
     bool tryAnnexB = kind == DeclarationKind::SloppyLexicalFunction;
 
     YieldHandling newYieldHandling = GetYieldHandling(generatorKind);
     return functionDefinition(pn, toStringStart, InAllowed, newYieldHandling, name, Statement,
                               generatorKind, asyncKind, tryAnnexB);
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::functionExpr(uint32_t toStringStart, InvokedPrediction invoked,
                                           FunctionAsyncKind asyncKind)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
 
     AutoAwaitIsKeyword<Parser> awaitIsKeyword(this, asyncKind == AsyncFunction);
     GeneratorKind generatorKind = NotGenerator;
     TokenKind tt;
@@ -3988,17 +4032,17 @@ Parser<SyntaxParseHandler, char16_t>::as
     return false;
 }
 
 template <>
 bool
 Parser<FullParseHandler, char16_t>::asmJS(Node list)
 {
     // Disable syntax parsing in anything nested inside the asm.js module.
-    handler.disableSyntaxParser();
+    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.
     if (!pc->newDirectives || pc->newDirectives->asmJS())
         return true;
 
@@ -4039,17 +4083,17 @@ Parser<FullParseHandler, char16_t>::asmJ
  *   "use\x20loose"
  *   "use strict"
  * }
  *
  * 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 <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::maybeParseDirective(Node list, Node possibleDirective, bool* cont)
 {
     TokenPos directivePos;
     JSAtom* directive = handler.isStringExprStatement(possibleDirective, &directivePos);
 
     *cont = !!directive;
     if (!*cont)
@@ -4102,18 +4146,18 @@ Parser<ParseHandler, CharT>::maybeParseD
             if (pc->isFunctionBox())
                 return asmJS(list);
             return warningAt(directivePos.begin, JSMSG_USE_ASM_DIRECTIVE_FAIL);
         }
     }
     return true;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::statementList(YieldHandling yieldHandling)
 {
     if (!CheckRecursionLimit(context))
         return null();
 
     Node pn = handler.newStatementList(pos());
     if (!pn)
         return null();
@@ -4162,35 +4206,35 @@ Parser<ParseHandler, CharT>::statementLi
         }
 
         handler.addStatementToList(pn, next);
     }
 
     return pn;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<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(TOK_RP, JSMSG_PAREN_AFTER_COND);
 
     /* Check for (a = b) and warn about possible (a == b) mistype. */
     if (handler.isUnparenthesizedAssignment(pn)) {
         if (!extraWarning(JSMSG_EQUAL_AS_ASSIGN))
             return null();
     }
     return pn;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::matchLabel(YieldHandling yieldHandling,
                                         MutableHandle<PropertyName*> label)
 {
     TokenKind tt = TOK_EOF;
     if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
         return false;
 
@@ -4201,172 +4245,172 @@ Parser<ParseHandler, CharT>::matchLabel(
         if (!label)
             return false;
     } else {
         label.set(nullptr);
     }
     return true;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 Parser<ParseHandler, CharT>::PossibleError::PossibleError(Parser<ParseHandler, CharT>& parser)
   : parser_(parser)
 {}
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 typename Parser<ParseHandler, CharT>::PossibleError::Error&
 Parser<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 <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 void
 Parser<ParseHandler, CharT>::PossibleError::setResolved(ErrorKind kind)
 {
     error(kind).state_ = ErrorState::None;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::PossibleError::hasError(ErrorKind kind)
 {
     return error(kind).state_ == ErrorState::Pending;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::PossibleError::hasPendingDestructuringError()
 {
     return hasError(ErrorKind::Destructuring);
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 void
 Parser<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 <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 void
 Parser<ParseHandler, CharT>::PossibleError::setPendingDestructuringErrorAt(const TokenPos& pos,
                                                                            unsigned errorNumber)
 {
     setPending(ErrorKind::Destructuring, pos, errorNumber);
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 void
 Parser<ParseHandler, CharT>::PossibleError::setPendingDestructuringWarningAt(const TokenPos& pos,
                                                                              unsigned errorNumber)
 {
     setPending(ErrorKind::DestructuringWarning, pos, errorNumber);
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 void
 Parser<ParseHandler, CharT>::PossibleError::setPendingExpressionErrorAt(const TokenPos& pos,
                                                                         unsigned errorNumber)
 {
     setPending(ErrorKind::Expression, pos, errorNumber);
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::PossibleError::checkForError(ErrorKind kind)
 {
     if (!hasError(kind))
         return true;
 
     Error& err = error(kind);
     parser_.errorAt(err.offset_, err.errorNumber_);
     return false;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::PossibleError::checkForWarning(ErrorKind kind)
 {
     if (!hasError(kind))
         return true;
 
     Error& err = error(kind);
     return parser_.extraWarningAt(err.offset_, err.errorNumber_);
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 bool
 Parser<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 <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 bool
 Parser<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 <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 void
 Parser<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 <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 void
 Parser<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 <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::bindingInitializer(Node lhs, DeclarationKind kind,
                                                 YieldHandling yieldHandling)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_ASSIGN));
 
     if (kind == DeclarationKind::FormalParameter)
         pc->functionBox()->hasParameterExprs = true;
 
@@ -4381,33 +4425,33 @@ Parser<ParseHandler, CharT>::bindingInit
         return null();
 
     if (foldConstants && !FoldConstants(context, &assign, this))
         return null();
 
     return assign;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<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 <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<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);
@@ -4415,18 +4459,18 @@ Parser<ParseHandler, CharT>::bindingIden
     if (!TokenKindIsPossibleIdentifierName(tt)) {
         error(JSMSG_NO_VARIABLE_NAME);
         return null();
     }
 
     return bindingIdentifier(kind, yieldHandling);
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::objectBindingPattern(DeclarationKind kind,
                                                   YieldHandling yieldHandling)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
 
     if (!CheckRecursionLimit(context))
         return null();
 
@@ -4536,18 +4580,18 @@ Parser<ParseHandler, CharT>::objectBindi
     MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::None,
                                      reportMissingClosing(JSMSG_CURLY_AFTER_LIST,
                                                           JSMSG_CURLY_OPENED, begin));
 
     handler.setEndPosition(literal, pos().end);
     return literal;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::arrayBindingPattern(DeclarationKind kind, YieldHandling yieldHandling)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LB));
 
     if (!CheckRecursionLimit(context))
         return null();
 
     uint32_t begin = pos().begin;
@@ -4625,32 +4669,32 @@ Parser<ParseHandler, CharT>::arrayBindin
      MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RB, modifier,
                                       reportMissingClosing(JSMSG_BRACKET_AFTER_LIST,
                                                            JSMSG_BRACKET_OPENED, begin));
 
     handler.setEndPosition(literal, pos().end);
     return literal;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::destructuringDeclaration(DeclarationKind kind,
                                                       YieldHandling yieldHandling,
                                                       TokenKind tt)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(tt));
     MOZ_ASSERT(tt == TOK_LB || tt == TOK_LC);
 
     return tt == TOK_LB
            ? arrayBindingPattern(kind, yieldHandling)
            : objectBindingPattern(kind, yieldHandling);
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<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) {
@@ -4661,18 +4705,18 @@ Parser<ParseHandler, CharT>::destructuri
         if (pc->lastAwaitOffset != startAwaitOffset) {
             errorAt(pc->lastAwaitOffset, JSMSG_AWAIT_IN_DEFAULT);
             return null();
         }
     }
     return res;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::blockStatement(YieldHandling yieldHandling, unsigned errorNumber)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
     uint32_t openedPos = pos().begin;
 
     ParseContext::Statement stmt(pc, StatementKind::Block);
     ParseContext::Scope scope(this);
     if (!scope.init(pc))
@@ -4684,30 +4728,30 @@ Parser<ParseHandler, CharT>::blockStatem
 
     MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand,
                                      reportMissingClosing(errorNumber, JSMSG_CURLY_OPENED,
                                                           openedPos));
 
     return finishLexicalScope(scope, list);
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<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 <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::declarationPattern(Node decl, DeclarationKind declKind, TokenKind tt,
                                                 bool initialDeclaration,
                                                 YieldHandling yieldHandling,
                                                 ParseNodeKind* forHeadKind,
                                                 Node* forInOrOfExpression)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LB) ||
                tokenStream.isCurrentTokenType(TOK_LC));
@@ -4764,17 +4808,17 @@ Parser<ParseHandler, CharT>::declaration
         if (!tokenStream.peekToken(&ignored))
             return null();
         tokenStream.addModifierException(TokenStream::OperandIsNone);
     }
 
     return handler.newBinary(PNK_ASSIGN, pattern, init);
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::initializerInNameDeclaration(Node decl, Node binding,
                                                           Handle<PropertyName*> name,
                                                           DeclarationKind declKind,
                                                           bool initialDeclaration,
                                                           YieldHandling yieldHandling,
                                                           ParseNodeKind* forHeadKind,
                                                           Node* forInOrOfExpression)
@@ -4851,18 +4895,18 @@ Parser<ParseHandler, CharT>::initializer
             // so we need an exception.
             tokenStream.addModifierException(TokenStream::OperandIsNone);
         }
     }
 
     return handler.finishInitializerAssignment(binding, initializer);
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::declarationName(Node decl, 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();
@@ -4933,18 +4977,18 @@ Parser<ParseHandler, CharT>::declaration
     // Note the declared name after knowing whether or not we are in a for-of
     // loop, due to special early error semantics in Annex B.3.5.
     if (!noteDeclaredName(name, declKind, namePos))
         return null();
 
     return binding;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::declarationList(YieldHandling yieldHandling,
                                              ParseNodeKind kind,
                                              ParseNodeKind* forHeadKind /* = nullptr */,
                                              Node* forInOrOfExpression /* = nullptr */)
 {
     MOZ_ASSERT(kind == PNK_VAR || kind == PNK_LET || kind == PNK_CONST);
 
     JSOp op;
@@ -4997,18 +5041,18 @@ Parser<ParseHandler, CharT>::declaration
 
         if (!tokenStream.matchToken(&matched, TOK_COMMA))
             return null();
     } while (matched);
 
     return decl;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<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
@@ -5244,21 +5288,21 @@ Parser<FullParseHandler, char16_t>::impo
         handler.newImportDeclaration(importSpecSet, moduleSpec, TokenPos(begin, pos().end));
     if (!node || !pc->sc()->asModuleContext()->builder.processImport(node))
         return null();
 
     return node;
 }
 
 template<>
-SyntaxParseHandlerBase::Node
+SyntaxParseHandler::Node
 Parser<SyntaxParseHandler, char16_t>::importDeclaration()
 {
-    JS_ALWAYS_FALSE(abortIfSyntaxParser());
-    return SyntaxParseHandlerBase::NodeFailure;
+    MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
+    return SyntaxParseHandler::NodeFailure;
 }
 
 template<>
 bool
 Parser<FullParseHandler, char16_t>::checkExportedName(JSAtom* exportName)
 {
     if (!pc->sc()->asModuleContext()->builder.hasExportedName(exportName))
         return true;
@@ -5375,18 +5419,18 @@ Parser<FullParseHandler, char16_t>::proc
 template<>
 bool
 Parser<SyntaxParseHandler, char16_t>::processExportFrom(Node node)
 {
     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
     return false;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::exportFrom(uint32_t begin, Node specList)
 {
     if (!abortIfSyntaxParser())
         return null();
 
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FROM));
 
     if (!abortIfSyntaxParser())
@@ -5406,18 +5450,18 @@ Parser<ParseHandler, CharT>::exportFrom(
         return null();
 
     if (!processExportFrom(node))
         return null();
 
     return node;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::exportBatch(uint32_t begin)
 {
     if (!abortIfSyntaxParser())
         return null();
 
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_MUL));
 
     Node kid = handler.newList(PNK_EXPORT_SPEC_LIST, pos());
@@ -5457,18 +5501,18 @@ Parser<FullParseHandler, char16_t>::chec
 template<>
 bool
 Parser<SyntaxParseHandler, char16_t>::checkLocalExportNames(Node node)
 {
     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
     return false;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::exportClause(uint32_t begin)
 {
     if (!abortIfSyntaxParser())
         return null();
 
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
 
     Node kid = handler.newList(PNK_EXPORT_SPEC_LIST, pos());
@@ -5557,18 +5601,18 @@ Parser<ParseHandler, CharT>::exportClaus
         return null();
 
     if (!processExport(node))
         return null();
 
     return node;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::exportVariableStatement(uint32_t begin)
 {
     if (!abortIfSyntaxParser())
         return null();
 
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_VAR));
 
     Node kid = declarationList(YieldIsName, PNK_VAR);
@@ -5584,18 +5628,18 @@ Parser<ParseHandler, CharT>::exportVaria
         return null();
 
     if (!processExport(node))
         return null();
 
     return node;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::exportFunctionDeclaration(uint32_t begin, uint32_t toStringStart,
                                                        FunctionAsyncKind asyncKind /* = SyncFunction */)
 {
     if (!abortIfSyntaxParser())
         return null();
 
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
 
@@ -5611,18 +5655,18 @@ Parser<ParseHandler, CharT>::exportFunct
         return null();
 
     if (!processExport(node))
         return null();
 
     return node;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::exportClassDeclaration(uint32_t begin)
 {
     if (!abortIfSyntaxParser())
         return null();
 
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_CLASS));
 
     Node kid = classDefinition(YieldIsKeyword, ClassStatement, NameRequired);
@@ -5637,18 +5681,18 @@ Parser<ParseHandler, CharT>::exportClass
         return null();
 
     if (!processExport(node))
         return null();
 
     return node;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<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, tokenStream.isCurrentTokenType(TOK_CONST));
     MOZ_ASSERT_IF(kind == DeclarationKind::Let, tokenStream.isCurrentTokenType(TOK_LET));
@@ -5664,18 +5708,18 @@ Parser<ParseHandler, CharT>::exportLexic
         return null();
 
     if (!processExport(node))
         return null();
 
     return node;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::exportDefaultFunctionDeclaration(uint32_t begin,
                                                               uint32_t toStringStart,
                                                               FunctionAsyncKind asyncKind /* = SyncFunction */)
 {
     if (!abortIfSyntaxParser())
         return null();
 
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
@@ -5689,18 +5733,18 @@ Parser<ParseHandler, CharT>::exportDefau
         return null();
 
     if (!processExport(node))
         return null();
 
     return node;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::exportDefaultClassDeclaration(uint32_t begin)
 {
     if (!abortIfSyntaxParser())
         return null();
 
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_CLASS));
 
     Node kid = classDefinition(YieldIsKeyword, ClassStatement, AllowDefaultName);
@@ -5712,18 +5756,18 @@ Parser<ParseHandler, CharT>::exportDefau
         return null();
 
     if (!processExport(node))
         return null();
 
     return node;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::exportDefaultAssignExpr(uint32_t begin)
 {
     if (!abortIfSyntaxParser())
         return null();
 
     RootedPropertyName name(context, context->names().starDefaultStar);
     Node nameNode = newName(name);
     if (!nameNode)
@@ -5742,18 +5786,18 @@ Parser<ParseHandler, CharT>::exportDefau
         return null();
 
     if (!processExport(node))
         return null();
 
     return node;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::exportDefault(uint32_t begin)
 {
     if (!abortIfSyntaxParser())
         return null();
 
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_DEFAULT));
 
     TokenKind tt;
@@ -5786,18 +5830,18 @@ Parser<ParseHandler, CharT>::exportDefau
         return exportDefaultClassDeclaration(begin);
 
       default:
         tokenStream.ungetToken();
         return exportDefaultAssignExpr(begin);
     }
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::exportDeclaration()
 {
     if (!abortIfSyntaxParser())
         return null();
 
     MOZ_ASSERT(tokenStream.currentToken().type == TOK_EXPORT);
 
     if (!pc->atModuleLevel()) {
@@ -5851,33 +5895,33 @@ Parser<ParseHandler, CharT>::exportDecla
         return exportDefault(begin);
 
       default:
         error(JSMSG_DECLARATION_AFTER_EXPORT);
         return null();
     }
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::expressionStatement(YieldHandling yieldHandling,
                                                  InvokedPrediction invoked)
 {
     tokenStream.ungetToken();
     Node pnexpr = expr(InAllowed, yieldHandling, TripledotProhibited,
                        /* possibleError = */ nullptr, invoked);
     if (!pnexpr)
         return null();
     if (!matchOrInsertSemicolonAfterExpression())
         return null();
     return handler.newExprStatement(pnexpr, pos().end);
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<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() {}|
@@ -5920,18 +5964,18 @@ Parser<ParseHandler, CharT>::consequentO
 
         handler.addStatementToList(block, fun);
         return finishLexicalScope(scope, block);
     }
 
     return statement(yieldHandling);
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<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);
 
@@ -5979,18 +6023,18 @@ Parser<ParseHandler, CharT>::ifStatement
         elseBranch = handler.newIfStatement(posList[i], condList[i], thenList[i], elseBranch);
         if (!elseBranch)
             return null();
     }
 
     return elseBranch;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<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);
@@ -6005,49 +6049,49 @@ Parser<ParseHandler, CharT>::doWhileStat
     //   https://bugs.ecmascript.org/show_bug.cgi?id=157
     // To parse |do {} while (true) false| correctly, use Operand.
     bool ignored;
     if (!tokenStream.matchToken(&ignored, TOK_SEMI, TokenStream::Operand))
         return null();
     return handler.newDoWhileStatement(body, cond, TokenPos(begin, pos().end));
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<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 <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::matchInOrOf(bool* isForInp, bool* isForOfp)
 {
     TokenKind tt;
     if (!tokenStream.getToken(&tt))
         return false;
 
     *isForInp = tt == TOK_IN;
     *isForOfp = tt == TOK_OF;
     if (!*isForInp && !*isForOfp)
         tokenStream.ungetToken();
 
     MOZ_ASSERT_IF(*isForInp || *isForOfp, *isForInp != *isForOfp);
     return true;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::forHeadStart(YieldHandling yieldHandling,
                                    IteratorKind iterKind,
                                           ParseNodeKind* forHeadKind,
                                           Node* forInitialPart,
                                           Maybe<ParseContext::Scope>& forLoopLexicalScope,
                                           Node* forInOrOfExpression)
 {
@@ -6194,18 +6238,18 @@ Parser<ParseHandler, CharT>::forHeadStar
         return false;
 
     // Finally, parse the iterated expression, making the for-loop's closing
     // ')' the next token.
     *forInOrOfExpression = expressionAfterForInOrOf(*forHeadKind, yieldHandling);
     return *forInOrOfExpression != null();
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::forStatement(YieldHandling yieldHandling)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
     uint32_t begin = pos().begin;
 
     ParseContext::Statement stmt(pc, StatementKind::ForLoop);
 
     bool isForEach = false;
@@ -6374,18 +6418,18 @@ Parser<ParseHandler, CharT>::forStatemen
         return null();
 
     if (forLoopLexicalScope)
         return finishLexicalScope(*forLoopLexicalScope, forLoop);
 
     return forLoop;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::switchStatement(YieldHandling yieldHandling)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_SWITCH));
     uint32_t begin = pos().begin;
 
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
 
     Node discriminant = exprInParens(InAllowed, yieldHandling, TripledotProhibited);
@@ -6481,18 +6525,18 @@ Parser<ParseHandler, CharT>::switchState
     if (!caseList)
         return null();
 
     handler.setEndPosition(caseList, pos().end);
 
     return handler.newSwitchStatement(begin, discriminant, caseList);
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::continueStatement(YieldHandling yieldHandling)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_CONTINUE));
     uint32_t begin = pos().begin;
 
     RootedPropertyName label(context);
     if (!matchLabel(yieldHandling, &label))
         return null();
@@ -6539,18 +6583,18 @@ Parser<ParseHandler, CharT>::continueSta
     }
 
     if (!matchOrInsertSemicolonAfterNonExpression())
         return null();
 
     return handler.newContinueStatement(label, TokenPos(begin, pos().end));
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::breakStatement(YieldHandling yieldHandling)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_BREAK));
     uint32_t begin = pos().begin;
 
     RootedPropertyName label(context);
     if (!matchLabel(yieldHandling, &label))
         return null();
@@ -6579,18 +6623,18 @@ Parser<ParseHandler, CharT>::breakStatem
     }
 
     if (!matchOrInsertSemicolonAfterNonExpression())
         return null();
 
     return handler.newBreakStatement(label, TokenPos(begin, pos().end));
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::returnStatement(YieldHandling yieldHandling)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_RETURN));
     uint32_t begin = pos().begin;
 
     MOZ_ASSERT(pc->isFunctionBox());
     pc->functionBox()->usesReturn = true;
 
@@ -6633,18 +6677,18 @@ Parser<ParseHandler, CharT>::returnState
     if (pc->isLegacyGenerator() && exprNode) {
         errorAt(begin, JSMSG_BAD_GENERATOR_RETURN);
         return null();
     }
 
     return pn;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::yieldExpression(InHandling inHandling)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_YIELD));
     uint32_t begin = pos().begin;
 
     switch (pc->generatorKind()) {
       case StarGenerator:
       {
@@ -6768,18 +6812,18 @@ Parser<ParseHandler, CharT>::yieldExpres
 
         return handler.newYieldExpression(begin, exprNode);
       }
     }
 
     MOZ_CRASH("yieldExpr");
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::withStatement(YieldHandling yieldHandling)
 {
     MOZ_ASSERT(tokenStream.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
@@ -6804,18 +6848,18 @@ Parser<ParseHandler, CharT>::withStateme
             return null();
     }
 
     pc->sc()->setBindingsAccessedDynamically();
 
     return handler.newWithStatement(begin, objectExpr, innerBlock);
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::labeledItem(YieldHandling yieldHandling)
 {
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
 
     if (tt == TOK_FUNCTION) {
         TokenKind next;
@@ -6839,18 +6883,18 @@ Parser<ParseHandler, CharT>::labeledItem
 
         return functionStmt(pos().begin, yieldHandling, NameRequired);
     }
 
     tokenStream.ungetToken();
     return statement(yieldHandling);
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::labeledStatement(YieldHandling yieldHandling)
 {
     RootedPropertyName label(context, labelIdentifier(yieldHandling));
     if (!label)
         return null();
 
     auto hasSameLabel = [&label](ParseContext::LabelStatement* stmt) {
         return stmt->label() == label;
@@ -6869,18 +6913,18 @@ Parser<ParseHandler, CharT>::labeledStat
     ParseContext::LabelStatement stmt(pc, label);
     Node pn = labeledItem(yieldHandling);
     if (!pn)
         return null();
 
     return handler.newLabeledStatement(label, pn, begin);
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::throwStatement(YieldHandling yieldHandling)
 {
     MOZ_ASSERT(tokenStream.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))
@@ -6899,18 +6943,18 @@ Parser<ParseHandler, CharT>::throwStatem
         return null();
 
     if (!matchOrInsertSemicolonAfterExpression())
         return null();
 
     return handler.newThrowStatement(throwExpr, TokenPos(begin, pos().end));
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::tryStatement(YieldHandling yieldHandling)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_TRY));
     uint32_t begin = pos().begin;
 
     /*
      * try nodes are ternary.
      * kid1 is the try statement
@@ -7086,18 +7130,18 @@ Parser<ParseHandler, CharT>::tryStatemen
     if (!catchList && !finallyBlock) {
         error(JSMSG_CATCH_OR_FINALLY);
         return null();
     }
 
     return handler.newTryStatement(begin, innerBlock, catchList, finallyBlock);
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::catchBlockStatement(YieldHandling yieldHandling,
                                                  ParseContext::Scope& catchParamScope)
 {
     uint32_t openedPos = pos().begin;
 
     ParseContext::Statement stmt(pc, StatementKind::Block);
 
     // ES 13.15.7 CatchClauseEvaluation
@@ -7122,18 +7166,18 @@ Parser<ParseHandler, CharT>::catchBlockS
                                                           JSMSG_CURLY_OPENED, openedPos));
 
     // 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 <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::debuggerStatement()
 {
     TokenPos p;
     p.begin = pos().begin;
     if (!matchOrInsertSemicolonAfterNonExpression())
         return null();
     p.end = pos().end;
 
@@ -7161,18 +7205,18 @@ JSOpFromPropertyType(PropertyType propTy
       case PropertyType::Constructor:
       case PropertyType::DerivedConstructor:
         return JSOP_INITPROP;
       default:
         MOZ_CRASH("unexpected property type");
     }
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::classDefinition(YieldHandling yieldHandling,
                                              ClassContext classContext,
                                              DefaultHandling defaultHandling)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_CLASS));
 
     uint32_t classStartOffset = pos().begin;
     bool savedStrictness = setLocalStrictMode(true);
@@ -7389,17 +7433,17 @@ Parser<ParseHandler, CharT>::classDefini
     }
 
     MOZ_ALWAYS_TRUE(setLocalStrictMode(savedStrictness));
 
     return handler.newClass(nameNode, classHeritage, methodsOrBlock,
                             TokenPos(classStartOffset, classEndOffset));
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::nextTokenContinuesLetDeclaration(TokenKind next,
                                                               YieldHandling yieldHandling)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LET));
 
 #ifdef DEBUG
     TokenKind verify;
@@ -7440,30 +7484,30 @@ Parser<ParseHandler, CharT>::nextTokenCo
         // parse tree with ASI applied.  No backsies!
         return true;
     }
 
     // Otherwise not a let declaration.
     return false;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::variableStatement(YieldHandling yieldHandling)
 {
     Node vars = declarationList(yieldHandling, PNK_VAR);
     if (!vars)
         return null();
     if (!matchOrInsertSemicolonAfterExpression())
         return null();
     return vars;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::statement(YieldHandling yieldHandling)
 {
     MOZ_ASSERT(checkOptionsCalled);
 
     if (!CheckRecursionLimit(context))
         return null();
 
     TokenKind tt;
@@ -7669,18 +7713,18 @@ Parser<ParseHandler, CharT>::statement(Y
       case TOK_FINALLY:
         error(JSMSG_FINALLY_WITHOUT_TRY);
         return null();
 
       // NOTE: default case handled in the ExpressionStatement section.
     }
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::statementListItem(YieldHandling yieldHandling,
                                                bool canHaveDirectives /* = false */)
 {
     MOZ_ASSERT(checkOptionsCalled);
 
     if (!CheckRecursionLimit(context))
         return null();
 
@@ -7861,18 +7905,18 @@ Parser<ParseHandler, CharT>::statementLi
       case TOK_FINALLY:
         error(JSMSG_FINALLY_WITHOUT_TRY);
         return null();
 
       // NOTE: default case handled in the ExpressionStatement section.
     }
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<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)
@@ -8021,18 +8065,18 @@ Precedence(ParseNodeKind pnk) {
     if (pnk == PNK_LIMIT)
         return 0;
 
     MOZ_ASSERT(pnk >= PNK_BINOP_FIRST);
     MOZ_ASSERT(pnk <= PNK_BINOP_LAST);
     return PrecedenceTable[pnk - PNK_BINOP_FIRST];
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-MOZ_ALWAYS_INLINE typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+MOZ_ALWAYS_INLINE typename ParseHandler::Node
 Parser<ParseHandler, CharT>::orExpr1(InHandling inHandling, YieldHandling yieldHandling,
                                      TripledotHandling tripledotHandling,
                                      PossibleError* possibleError,
                                      InvokedPrediction invoked /* = PredictUninvoked */)
 {
     // Shift-reduce parser for the binary operator part of the JS expression
     // syntax.
 
@@ -8097,18 +8141,18 @@ Parser<ParseHandler, CharT>::orExpr1(InH
         depth++;
         MOZ_ASSERT(depth <= PRECEDENCE_CLASSES);
     }
 
     MOZ_ASSERT(depth == 0);
     return pn;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-MOZ_ALWAYS_INLINE typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+MOZ_ALWAYS_INLINE typename ParseHandler::Node
 Parser<ParseHandler, CharT>::condExpr1(InHandling inHandling, YieldHandling yieldHandling,
                                        TripledotHandling tripledotHandling,
                                        PossibleError* possibleError,
                                        InvokedPrediction invoked /* = PredictUninvoked */)
 {
     Node condition = orExpr1(inHandling, yieldHandling, tripledotHandling, possibleError, invoked);
 
     if (!condition || !tokenStream.isCurrentTokenType(TOK_HOOK))
@@ -8126,18 +8170,18 @@ Parser<ParseHandler, CharT>::condExpr1(I
 
     // Advance to the next token; the caller is responsible for interpreting it.
     TokenKind ignored;
     if (!tokenStream.getToken(&ignored))
         return null();
     return handler.newConditional(condition, thenExpr, elseExpr);
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::assignExpr(InHandling inHandling, YieldHandling yieldHandling,
                                         TripledotHandling tripledotHandling,
                                         PossibleError* possibleError /* = nullptr */,
                                         InvokedPrediction invoked /* = PredictUninvoked */)
 {
     if (!CheckRecursionLimit(context))
         return null();
 
@@ -8393,17 +8437,17 @@ Parser<ParseHandler, CharT>::assignExpr(
         return null();
 
     if (kind == PNK_ASSIGN)
         handler.checkAndSetIsDirectRHSAnonFunction(rhs);
 
     return handler.newAssignment(kind, lhs, rhs, op);
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 bool
 Parser<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.
 
@@ -8420,17 +8464,17 @@ Parser<ParseHandler, CharT>::isValidSimp
     if (behavior == PermitAssignmentToFunctionCalls) {
         if (handler.isFunctionCall(node))
             return true;
     }
 
     return false;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::checkIncDecOperand(Node operand, uint32_t operandOffset)
 {
     if (handler.isNameAnyParentheses(operand)) {
         if (const char* chars = handler.nameIsArgumentsEvalAnyParentheses(operand, context)) {
             if (!strictModeErrorAt(operandOffset, JSMSG_BAD_STRICT_ASSIGN, chars))
                 return false;
         }
@@ -8448,29 +8492,29 @@ Parser<ParseHandler, CharT>::checkIncDec
         return false;
     }
 
     MOZ_ASSERT(isValidSimpleAssignmentTarget(operand, PermitAssignmentToFunctionCalls),
                "inconsistent increment/decrement operand validation");
     return true;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kind, JSOp op,
                                          uint32_t begin)
 {
     Node kid = unaryExpr(yieldHandling, TripledotProhibited);
     if (!kid)
         return null();
     return handler.newUnary(kind, op, begin, kid);
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::unaryExpr(YieldHandling yieldHandling,
                                        TripledotHandling tripledotHandling,
                                        PossibleError* possibleError /* = nullptr */,
                                        InvokedPrediction invoked /* = PredictUninvoked */)
 {
     if (!CheckRecursionLimit(context))
         return null();
 
@@ -8588,18 +8632,18 @@ Parser<ParseHandler, CharT>::unaryExpr(Y
  *     [for (V of OBJ) if (COND) EXPR]  // ES6-era array comprehension
  *     (for (V of OBJ) if (COND) EXPR)  // ES6-era generator expression
  *
  * (These flavors are called "ES6-era" because they were in ES6 draft
  * specifications for a while. Shortly after this syntax was implemented in SM,
  * TC39 decided to drop it.)
  */
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::generatorComprehensionLambda(unsigned begin)
 {
     Node genfn = handler.newFunctionExpression(pos());
     if (!genfn)
         return null();
 
     ParseContext* outerpc = pc;
 
@@ -8678,18 +8722,18 @@ Parser<ParseHandler, CharT>::generatorCo
     // need to propagate the closed-over variable set to the inner
     // lazyscript.
     if (!handler.setComprehensionLambdaBody(genfn, body))
         return null();
 
     return genfn;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::comprehensionFor(GeneratorKind comprehensionKind)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
 
     uint32_t begin = pos().begin;
 
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
 
@@ -8751,18 +8795,18 @@ Parser<ParseHandler, CharT>::comprehensi
 
     Node head = handler.newForInOrOfHead(PNK_FOROF, lexicalScope, rhs, headPos);
     if (!head)
         return null();
 
     return handler.newComprehensionFor(begin, head, tail);
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::comprehensionIf(GeneratorKind comprehensionKind)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_IF));
 
     uint32_t begin = pos().begin;
 
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
     Node cond = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited);
@@ -8778,18 +8822,18 @@ Parser<ParseHandler, CharT>::comprehensi
 
     Node then = comprehensionTail(comprehensionKind);
     if (!then)
         return null();
 
     return handler.newIfStatement(begin, cond, then, null());
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::comprehensionTail(GeneratorKind comprehensionKind)
 {
     if (!CheckRecursionLimit(context))
         return null();
 
     bool matched;
     if (!tokenStream.matchToken(&matched, TOK_FOR, TokenStream::Operand))
         return null();
@@ -8816,18 +8860,18 @@ Parser<ParseHandler, CharT>::comprehensi
         return null();
     yieldExpr = handler.parenthesize(yieldExpr);
 
     return handler.newExprStatement(yieldExpr, pos().end);
 }
 
 // Parse an ES6-era generator or array comprehension, starting at the first
 // `for`. The caller is responsible for matching the ending TOK_RP or TOK_RB.
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::comprehension(GeneratorKind comprehensionKind)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
 
     uint32_t startYieldOffset = pc->lastYieldOffset;
 
     Node body = comprehensionFor(comprehensionKind);
     if (!body)
@@ -8836,18 +8880,18 @@ Parser<ParseHandler, CharT>::comprehensi
     if (comprehensionKind != NotGenerator && pc->lastYieldOffset != startYieldOffset) {
         errorAt(pc->lastYieldOffset, JSMSG_BAD_GENEXP_BODY, js_yield_str);
         return null();
     }
 
     return body;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::arrayComprehension(uint32_t begin)
 {
     Node inner = comprehension(NotGenerator);
     if (!inner)
         return null();
 
     MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ARRAY_COMPREHENSION);
 
@@ -8856,18 +8900,18 @@ Parser<ParseHandler, CharT>::arrayCompre
         return null();
 
     handler.setBeginPosition(comp, begin);
     handler.setEndPosition(comp, pos().end);
 
     return comp;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::generatorComprehension(uint32_t begin)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
 
     // We have no problem parsing generator comprehensions inside lazy
     // functions, but the bytecode emitter currently can't handle them that way,
     // because when it goes to emit the code for the inner generator function,
     // it expects outer functions to have non-lazy scripts.
@@ -8882,18 +8926,18 @@ Parser<ParseHandler, CharT>::generatorCo
     if (!result)
         return null();
     handler.setBeginPosition(result, begin);
     handler.setEndPosition(result, pos().end);
 
     return result;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<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);
@@ -8902,17 +8946,17 @@ Parser<ParseHandler, CharT>::assignExprW
         if (pc->lastAwaitOffset != startAwaitOffset) {
             errorAt(pc->lastAwaitOffset, JSMSG_AWAIT_IN_DEFAULT);
             return null();
         }
     }
     return res;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::argumentList(YieldHandling yieldHandling, Node listNode,
                                           bool* isSpread,
                                           PossibleError* possibleError /* = nullptr */)
 {
     bool matched;
     if (!tokenStream.matchToken(&matched, TOK_RP, TokenStream::Operand))
         return false;
@@ -8959,28 +9003,28 @@ Parser<ParseHandler, CharT>::argumentLis
     }
 
     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_ARGS);
 
     handler.setEndPosition(listNode, pos().end);
     return true;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::checkAndMarkSuperScope()
 {
     if (!pc->sc()->allowSuperProperty())
         return false;
     pc->setSuperScopeNeedsHomeObject();
     return true;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::memberExpr(YieldHandling yieldHandling,
                                         TripledotHandling tripledotHandling,
                                         TokenKind tt, bool allowCallSyntax /* = true */,
                                         PossibleError* possibleError /* = nullptr */,
                                         InvokedPrediction invoked /* = PredictUninvoked */)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(tt));
 
@@ -9199,31 +9243,31 @@ Parser<ParseHandler, CharT>::memberExpr(
     if (handler.isSuperBase(lhs)) {
         error(JSMSG_BAD_SUPER);
         return null();
     }
 
     return lhs;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::newName(PropertyName* name)
 {
     return newName(name, pos());
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::newName(PropertyName* name, TokenPos pos)
 {
     return handler.newName(name, pos, context);
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::checkLabelOrIdentifierReference(PropertyName* ident,
                                                              uint32_t offset,
                                                              YieldHandling yieldHandling,
                                                              TokenKind hint /* = TOK_LIMIT */)
 {
     TokenKind tt;
     if (hint == TOK_LIMIT) {
@@ -9282,17 +9326,17 @@ Parser<ParseHandler, CharT>::checkLabelO
     if (TokenKindIsFutureReservedWord(tt)) {
         errorAt(offset, JSMSG_RESERVED_ID, ReservedWordToCharZ(tt));
         return false;
     }
     MOZ_ASSERT_UNREACHABLE("Unexpected reserved word kind.");
     return false;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::checkBindingIdentifier(PropertyName* ident,
                                                     uint32_t offset,
                                                     YieldHandling yieldHandling,
                                                     TokenKind hint /* = TOK_LIMIT */)
 {
     if (pc->sc()->needStrictChecks()) {
         if (ident == context->names().arguments) {
@@ -9306,17 +9350,17 @@ Parser<ParseHandler, CharT>::checkBindin
                 return false;
             return true;
         }
     }
 
     return checkLabelOrIdentifierReference(ident, offset, yieldHandling, hint);
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 PropertyName*
 Parser<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.
     //
@@ -9328,64 +9372,64 @@ Parser<ParseHandler, CharT>::labelOrIden
                      ? tokenStream.currentToken().type
                      : TOK_LIMIT;
     RootedPropertyName ident(context, tokenStream.currentName());
     if (!checkLabelOrIdentifierReference(ident, pos().begin, yieldHandling, hint))
         return nullptr;
     return ident;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 PropertyName*
 Parser<ParseHandler, CharT>::bindingIdentifier(YieldHandling yieldHandling)
 {
     TokenKind hint = !tokenStream.currentNameHasEscapes()
                      ? tokenStream.currentToken().type
                      : TOK_LIMIT;
     RootedPropertyName ident(context, tokenStream.currentName());
     if (!checkBindingIdentifier(ident, pos().begin, yieldHandling, hint))
         return nullptr;
     return ident;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::identifierReference(Handle<PropertyName*> name)
 {
     Node pn = newName(name);
     if (!pn)
         return null();
 
     if (!noteUsedName(name))
         return null();
 
     return pn;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::stringLiteral()
 {
     return handler.newStringLiteral(tokenStream.currentToken().atom(), pos());
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::noSubstitutionTaggedTemplate()
 {
     if (tokenStream.hasInvalidTemplateEscape()) {
         tokenStream.clearInvalidTemplateEscape();
         return handler.newRawUndefinedLiteral(pos());
     }
 
     return handler.newTemplateStringLiteral(tokenStream.currentToken().atom(), pos());
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::noSubstitutionUntaggedTemplate()
 {
     if (!tokenStream.checkForInvalidTemplateEscapeError())
         return null();
 
     return handler.newTemplateStringLiteral(tokenStream.currentToken().atom(), pos());
 }
 
@@ -9405,34 +9449,34 @@ Parser<FullParseHandler, char16_t>::newR
                                  TenuredObject);
     if (!reobj)
         return null();
 
     return handler.newRegExp(reobj, pos(), *this);
 }
 
 template <>
-SyntaxParseHandlerBase::Node
+SyntaxParseHandler::Node
 Parser<SyntaxParseHandler, char16_t>::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 = tokenStream.currentToken().regExpFlags();
 
     mozilla::Range<const char16_t> source(chars, length);
     if (!js::irregexp::ParsePatternSyntax(tokenStream, alloc, source, flags & UnicodeFlag))
         return null();
 
-    return handler.newRegExp(SyntaxParseHandlerBase::NodeGeneric, pos(), *this);
-}
-
-template <template <typename CharT> class ParseHandler, typename CharT>
+    return handler.newRegExp(SyntaxParseHandler::NodeGeneric, pos(), *this);
+}
+
+template <class ParseHandler, typename CharT>
 void
 Parser<ParseHandler, CharT>::checkDestructuringAssignmentTarget(Node expr, TokenPos exprPos,
                                                                 PossibleError* possibleError)
 {
     // Return early if a pending destructuring error is already present.
     if (possibleError->hasPendingDestructuringError())
         return;
 
@@ -9471,17 +9515,17 @@ Parser<ParseHandler, CharT>::checkDestru
         // parenthesized, nested patterns.
         if (handler.isParenthesizedDestructuringPattern(expr))
             possibleError->setPendingDestructuringErrorAt(exprPos, JSMSG_BAD_DESTRUCT_PARENS);
         else
             possibleError->setPendingDestructuringErrorAt(exprPos, JSMSG_BAD_DESTRUCT_TARGET);
     }
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 void
 Parser<ParseHandler, CharT>::checkDestructuringAssignmentElement(Node expr, TokenPos exprPos,
                                                                  PossibleError* possibleError)
 {
     // ES2018 draft rev 0719f44aab93215ed9a626b2f45bd34f36916834
     // 12.15.5 Destructuring Assignment
     //
     // AssignmentElement[Yield, Await]:
@@ -9490,18 +9534,18 @@ Parser<ParseHandler, CharT>::checkDestru
 
     // If |expr| is an assignment element with an initializer expression, its
     // destructuring assignment target was already validated in assignExpr().
     // Otherwise we need to check that |expr| is a valid destructuring target.
     if (!handler.isUnparenthesizedAssignment(expr))
         checkDestructuringAssignmentTarget(expr, exprPos, possibleError);
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::arrayInitializer(YieldHandling yieldHandling,
                                               PossibleError* possibleError)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LB));
 
     uint32_t begin = pos().begin;
     Node literal = handler.newArrayLiteral(begin);
     if (!literal)
@@ -9599,18 +9643,18 @@ Parser<ParseHandler, CharT>::arrayInitia
 static JSAtom*
 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 <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::propertyName(YieldHandling yieldHandling,
                                           const Maybe<DeclarationKind>& maybeDecl, Node propList,
                                           PropertyType* propType, MutableHandleAtom propAtom)
 {
     TokenKind ltok;
     if (!tokenStream.getToken(&ltok))
         return null();
 
@@ -9797,18 +9841,18 @@ Parser<ParseHandler, CharT>::propertyNam
             *propType = PropertyType::Method;
         return propName;
     }
 
     error(JSMSG_COLON_AFTER_ID);
     return null();
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::computedPropertyName(YieldHandling yieldHandling,
                                                   const Maybe<DeclarationKind>& maybeDecl,
                                                   Node literal)
 {
     uint32_t begin = pos().begin;
 
     if (maybeDecl) {
         if (*maybeDecl == DeclarationKind::FormalParameter)
@@ -9820,18 +9864,18 @@ Parser<ParseHandler, CharT>::computedPro
     Node assignNode = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
     if (!assignNode)
         return null();
 
     MUST_MATCH_TOKEN(TOK_RB, JSMSG_COMP_PROP_UNTERM_EXPR);
     return handler.newComputedName(assignNode, begin, pos().end);
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::objectLiteral(YieldHandling yieldHandling,
                                            PossibleError* possibleError)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
 
     uint32_t openedPos = pos().begin;
 
     Node literal = handler.newObjectLiteral(pos().begin);
@@ -10035,18 +10079,18 @@ Parser<ParseHandler, CharT>::objectLiter
     MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::None,
                                      reportMissingClosing(JSMSG_CURLY_AFTER_LIST,
                                                           JSMSG_CURLY_OPENED, openedPos));
 
     handler.setEndPosition(literal, pos().end);
     return literal;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::methodDefinition(uint32_t toStringStart, PropertyType propType,
                                               HandleAtom funName)
 {
     FunctionSyntaxKind kind;
     switch (propType) {
       case PropertyType::Getter:
         kind = Getter;
         break;
@@ -10097,17 +10141,17 @@ Parser<ParseHandler, CharT>::methodDefin
     Node pn = handler.newFunctionExpression(pos());
     if (!pn)
         return null();
 
     return functionDefinition(pn, toStringStart, InAllowed, yieldHandling, funName, kind,
                               generatorKind, asyncKind);
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::tryNewTarget(Node &newTarget)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_NEW));
 
     newTarget = null();
 
     Node newHolder = handler.newPosHolder(pos());
@@ -10141,18 +10185,18 @@ Parser<ParseHandler, CharT>::tryNewTarge
     Node targetHolder = handler.newPosHolder(pos());
     if (!targetHolder)
         return false;
 
     newTarget = handler.newNewTarget(newHolder, targetHolder);
     return !!newTarget;
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::primaryExpr(YieldHandling yieldHandling,
                                          TripledotHandling tripledotHandling, TokenKind tt,
                                          PossibleError* possibleError,
                                          InvokedPrediction invoked /* = PredictUninvoked */)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(tt));
     if (!CheckRecursionLimit(context))
         return null();
@@ -10319,18 +10363,18 @@ Parser<ParseHandler, CharT>::primaryExpr
         tokenStream.ungetToken();  // put back right paren
 
         // Return an arbitrary expression node. See case TOK_RP above.
         return handler.newNullLiteral(pos());
       }
     }
 }
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-typename ParseHandler<CharT>::Node
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 Parser<ParseHandler, CharT>::exprInParens(InHandling inHandling, YieldHandling yieldHandling,
                                           TripledotHandling tripledotHandling,
                                           PossibleError* possibleError /* = nullptr */)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LP));
     return expr(inHandling, yieldHandling, tripledotHandling, possibleError, PredictInvoked);
 }
 
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -28,17 +28,17 @@
 namespace js {
 
 class ModuleObject;
 
 namespace frontend {
 
 class ParserBase;
 
-template <template <typename CharT> class ParseHandler, typename CharT> class Parser;
+template <class ParseHandler, typename CharT> class Parser;
 
 /*
  * The struct ParseContext stores information about the current parsing context,
  * which is part of the parser state (see the field Parser::pc). The current
  * parsing context is either the global context, or the function currently being
  * parsed. When the parser encounters a function definition, it creates a new
  * ParseContext, makes it the new current context.
  */
@@ -347,21 +347,21 @@ class ParseContext : public Nestable<Par
 
     // Set when parsing a function and it has 'return <expr>;'
     bool funHasReturnExpr;
 
     // Set when parsing a function and it has 'return;'
     bool funHasReturnVoid;
 
   public:
-    template <template <typename CharT> class ParseHandler, typename CharT>
+    template <class ParseHandler, typename CharT>
     ParseContext(Parser<ParseHandler, CharT>* prs, SharedContext* sc, Directives* newDirectives)
       : Nestable<ParseContext>(&prs->pc),
         traceLog_(sc->context,
-                  mozilla::IsSame<ParseHandler<CharT>, FullParseHandler<CharT>>::value
+                  mozilla::IsSame<ParseHandler, FullParseHandler>::value
                   ? TraceLogger_ParsingFull
                   : TraceLogger_ParsingSyntax,
                   prs->tokenStream),
         sc_(sc),
         tokenStream_(prs->tokenStream),
         innermostStatement_(nullptr),
         innermostScope_(nullptr),
         varScope_(nullptr),
@@ -800,23 +800,16 @@ class ParserBase : public StrictModeGett
     const bool          foldConstants:1;
 
   protected:
 #if DEBUG
     /* Our fallible 'checkOptions' member function has been called. */
     bool checkOptionsCalled:1;
 #endif
 
-    /*
-     * Not all language constructs can be handled during syntax parsing. If it
-     * is not known whether the parse succeeds or fails, this bit is set and
-     * the parse will return false.
-     */
-    bool abortedSyntaxParse:1;
-
     /* Unexpected end of input, i.e. TOK_EOF not at top-level. */
     bool isUnexpectedEOF_:1;
 
     bool awaitIsKeyword_:1;
 
   public:
     bool awaitIsKeyword() const {
       return awaitIsKeyword_;
@@ -855,23 +848,16 @@ class ParserBase : public StrictModeGett
         MOZ_ASSERT(tokenStream.debugHasNoLookahead());
         return pc->sc()->setLocalStrictMode(strict);
     }
 
     const ReadOnlyCompileOptions& options() const {
         return tokenStream.options();
     }
 
-    bool hadAbortedSyntaxParse() {
-        return abortedSyntaxParse;
-    }
-    void clearAbortedSyntaxParse() {
-        abortedSyntaxParse = false;
-    }
-
     bool isUnexpectedEOF() const { return isUnexpectedEOF_; }
 
     bool reportNoOffset(ParseReportKind kind, bool strict, unsigned errorNumber, ...);
 
     /* Report the given error at the current offset. */
     void error(unsigned errorNumber, ...);
     void errorWithNotes(UniquePtr<JSErrorNotes> notes, unsigned errorNumber, ...);
 
@@ -983,21 +969,21 @@ ParseContext::Scope::Scope(ParserBase* p
 
 inline
 ParseContext::VarScope::VarScope(ParserBase* parser)
   : Scope(parser)
 {
     useAsVarScope(parser->pc);
 }
 
-template <template<typename CharT> class ParseHandler, typename CharT>
+template <class ParseHandler, typename CharT>
 class Parser final : public ParserBase, private JS::AutoGCRooter
 {
   private:
-    using Node = typename ParseHandler<CharT>::Node;
+    using Node = typename ParseHandler::Node;
 
     /*
      * 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
@@ -1116,32 +1102,57 @@ class Parser final : public ParserBase, 
 
         // 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);
     };
 
+    // 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_;
+
   public:
     /* State specific to the kind of parse being performed. */
-    ParseHandler<CharT> handler;
+    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,
-           Parser<SyntaxParseHandler, CharT>* syntaxParser, LazyScript* lazyOuterFunction);
+           SyntaxParser* syntaxParser, LazyScript* lazyOuterFunction);
     ~Parser();
 
     friend class AutoAwaitIsKeyword<Parser>;
     void setAwaitIsKeyword(bool isKeyword);
 
+    // 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();
+
     bool checkOptions();
 
     friend void js::frontend::TraceParser(JSTracer* trc, JS::AutoGCRooter* parser);
 
     /*
      * Parse a top-level JS script.
      */
     Node parse();
@@ -1163,25 +1174,35 @@ class Parser final : public ParserBase, 
     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();
 
+    // If ParseHandler is SyntaxParseHandler, do nothing.
+    //
+    // If ParseHandler is FullParseHandler, disable syntax parsing of all
+    // future inner functions.
+    void disableSyntaxParser();
+
   public:
     /* Public entry points for parsing. */
-    Node statement(YieldHandling yieldHandling);
     Node statementListItem(YieldHandling yieldHandling, bool canHaveDirectives = false);
 
-    bool maybeParseDirective(Node list, Node pn, bool* cont);
-
     // 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.
@@ -1240,16 +1261,18 @@ class Parser final : public ParserBase, 
      */
     Node functionStmt(uint32_t toStringStart,
                       YieldHandling yieldHandling, DefaultHandling defaultHandling,
                       FunctionAsyncKind asyncKind = SyncFunction);
     Node functionExpr(uint32_t toStringStart, InvokedPrediction invoked = PredictUninvoked,
                       FunctionAsyncKind asyncKind = SyncFunction);
 
     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);
 
     Node forStatement(YieldHandling yieldHandling);
     bool forHeadStart(YieldHandling yieldHandling,
@@ -1559,17 +1582,17 @@ class Parser final : public ParserBase, 
                                             PossibleError* possibleError);
     void checkDestructuringAssignmentElement(Node expr, TokenPos exprPos,
                                              PossibleError* possibleError);
 
     Node newNumber(const Token& tok) {
         return handler.newNumber(tok.number(), tok.decimalPoint(), tok.pos);
     }
 
-    static Node null() { return ParseHandler<CharT>::null(); }
+    static Node null() { return ParseHandler::null(); }
 
     JSAtom* prefixAccessorName(PropertyType propType, HandleAtom propAtom);
 
     bool asmJS(Node list);
 };
 
 template <class Parser>
 class MOZ_STACK_CLASS AutoAwaitIsKeyword
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -15,30 +15,27 @@
 #include "jscntxt.h"
 
 #include "frontend/ParseNode.h"
 
 namespace js {
 
 namespace frontend {
 
-template <template <typename CharT> class ParseHandler, typename CharT>
-class Parser;
-
 // Parse handler used when processing the syntax in a block of code, to generate
 // the minimal information which is required to detect syntax errors and allow
 // bytecode to be emitted for outer functions.
 //
 // When parsing, we start at the top level with a full parse, and when possible
 // only check the syntax for inner functions, so that they can be lazily parsed
 // into bytecode when/if they first run. Checking the syntax of a function is
 // several times faster than doing a full parse/emit, and lazy parsing improves
 // both performance and memory usage significantly when pages contain large
 // amounts of code that never executes (which happens often).
-class SyntaxParseHandlerBase
+class SyntaxParseHandler
 {
     // Remember the last encountered name or string literal during syntax parses.
     JSAtom* lastAtom;
     TokenPos lastStringPos;
 
   public:
     enum Node {
         NodeFailure = 0,
@@ -167,17 +164,17 @@ class SyntaxParseHandlerBase
     }
 
     static bool isDestructuringPatternAnyParentheses(Node node) {
         return isUnparenthesizedDestructuringPattern(node) ||
                 isParenthesizedDestructuringPattern(node);
     }
 
   public:
-    SyntaxParseHandlerBase(JSContext* cx, LifoAlloc& alloc, LazyScript* lazyOuterFunction)
+    SyntaxParseHandler(JSContext* cx, LifoAlloc& alloc, LazyScript* lazyOuterFunction)
       : lastAtom(nullptr)
     {}
 
     static Node null() { return NodeFailure; }
 
     void prepareNodeForMutation(Node node) {}
     void freeTree(Node node) {}
 
@@ -621,41 +618,18 @@ class SyntaxParseHandlerBase
 
     bool canSkipLazyInnerFunctions() {
         return false;
     }
     bool canSkipLazyClosedOverBindings() {
         return false;
     }
     JSAtom* nextLazyClosedOverBinding() {
-        MOZ_CRASH("SyntaxParseHandlerBase::canSkipLazyClosedOverBindings must return false");
+        MOZ_CRASH("SyntaxParseHandler::canSkipLazyClosedOverBindings must return false");
     }
 
     void adjustGetToSet(Node node) {}
-
-    void disableSyntaxParser() {
-    }
-};
-
-template<typename CharT>
-class SyntaxParseHandler : public SyntaxParseHandlerBase
-{
-  public:
-    // Using frontend::SyntaxParseHandler versus SyntaxParseHandler shouldn't
-    // be necessary per C++11 [temp.local]p1: in template argument lists inside
-    // a template class, the template class name refers to the template (i.e.
-    // SyntaxParseHandler) and not to the particular instantiation of the
-    // template class (i.e. SyntaxParseHandler<CharT>).
-    //
-    // Unfortunately, some versions of clang and MSVC are buggy in this regard.
-    // So we refer to SyntaxParseHandler with a qualified name.
-    SyntaxParseHandler(JSContext* cx, LifoAlloc& alloc,
-                       Parser<frontend::SyntaxParseHandler, CharT>* syntaxParser,
-                       LazyScript* lazyOuterFunction)
-      : SyntaxParseHandlerBase(cx, alloc, lazyOuterFunction)
-    {}
-
 };
 
 } // namespace frontend
 } // namespace js
 
 #endif /* frontend_SyntaxParseHandler_h */
--- a/js/src/wasm/AsmJS.h
+++ b/js/src/wasm/AsmJS.h
@@ -23,18 +23,18 @@
 
 namespace js {
 
 namespace frontend {
 
 class ParseContext;
 class ParseNode;
 
-template <template <typename CharT> class ParseHandler, typename CharT> class Parser;
-template <typename CharT> class FullParseHandler;
+template <class ParseHandler, typename CharT> class Parser;
+class FullParseHandler;
 
 }
 
 using AsmJSParser = frontend::Parser<frontend::FullParseHandler, char16_t>;
 
 // This function takes over parsing of a function starting with "use asm". The
 // return value indicates whether an error was reported which the caller should
 // propagate. If no error was reported, the function may still fail to validate