Bug 1351107 - Add a |typename CharT| template parameter to Parser, SyntaxParseHandler, and FullParseHandler, *without* otherwise changing functionality. r=arai
authorJeff Walden <jwalden@mit.edu>
Tue, 04 Apr 2017 11:27:23 -0700
changeset 354363 0d069bcc20b070ee61805c51585c06690914bf25
parent 354362 5649e25c4c42665c9cc2698438e0110b5f14e20b
child 354364 3ccc9c855766304cc5126237bed675caad8d3be5
push id31695
push userkwierso@gmail.com
push dateSat, 22 Apr 2017 00:30:52 +0000
treeherdermozilla-central@9550eedc0bd8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersarai
bugs1351107
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 1351107 - Add a |typename CharT| template parameter to Parser, SyntaxParseHandler, and FullParseHandler, *without* otherwise changing functionality. r=arai
js/src/builtin/ReflectParse.cpp
js/src/frontend/BytecodeCompiler.cpp
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/BytecodeEmitter.h
js/src/frontend/FoldConstants.cpp
js/src/frontend/FoldConstants.h
js/src/frontend/FullParseHandler.h
js/src/frontend/ParseNode.cpp
js/src/frontend/ParseNode.h
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/frontend/SyntaxParseHandler.h
js/src/jsapi.cpp
js/src/shell/js.cpp
js/src/vm/Debugger.cpp
js/src/wasm/AsmJS.h
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -1721,17 +1721,17 @@ namespace {
 /*
  * Serialization of parse nodes to JavaScript objects.
  *
  * All serialization methods take a non-nullable ParseNode pointer.
  */
 class ASTSerializer
 {
     JSContext*          cx;
-    Parser<FullParseHandler>* parser;
+    Parser<FullParseHandler, char16_t>* parser;
     NodeBuilder         builder;
     DebugOnly<uint32_t> lineno;
 
     Value unrootedAtomContents(JSAtom* atom) {
         return StringValue(atom ? atom : cx->names().empty);
     }
 
     BinaryOperator binop(ParseNodeKind kind, JSOp op);
@@ -1827,17 +1827,17 @@ class ASTSerializer
         , lineno(ln)
 #endif
     {}
 
     bool init(HandleObject userobj) {
         return builder.init(userobj);
     }
 
-    void setParser(Parser<FullParseHandler>* p) {
+    void setParser(Parser<FullParseHandler, char16_t>* p) {
         parser = p;
         builder.setTokenStream(&p->tokenStream);
     }
 
     bool program(ParseNode* pn, MutableHandleValue dst);
 };
 
 } /* anonymous namespace */
@@ -3702,19 +3702,20 @@ reflect_parse(JSContext* cx, uint32_t ar
     CompileOptions options(cx);
     options.setFileAndLine(filename, lineno);
     options.setCanLazilyParse(false);
     options.allowHTMLComments = target == ParseTarget::Script;
     mozilla::Range<const char16_t> chars = linearChars.twoByteRange();
     UsedNameTracker usedNames(cx);
     if (!usedNames.init())
         return false;
-    Parser<FullParseHandler> parser(cx, cx->tempLifoAlloc(), options, chars.begin().get(),
-                                    chars.length(), /* foldConstants = */ false, usedNames,
-                                    nullptr, nullptr);
+    Parser<FullParseHandler, char16_t> parser(cx, cx->tempLifoAlloc(), options,
+                                              chars.begin().get(), chars.length(),
+                                              /* foldConstants = */ false, usedNames, nullptr,
+                                              nullptr);
     if (!parser.checkOptions())
         return false;
 
     serialize.setParser(&parser);
 
     ParseNode* pn;
     if (target == ParseTarget::Script) {
         pn = parser.parse();
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -79,18 +79,18 @@ class MOZ_STACK_CLASS BytecodeCompiler
     SourceBufferHolder& sourceBuffer;
 
     RootedScope enclosingScope;
 
     RootedScriptSource sourceObject;
     ScriptSource* scriptSource;
 
     Maybe<UsedNameTracker> usedNames;
-    Maybe<Parser<SyntaxParseHandler>> syntaxParser;
-    Maybe<Parser<FullParseHandler>> parser;
+    Maybe<Parser<SyntaxParseHandler, char16_t>> syntaxParser;
+    Maybe<Parser<FullParseHandler, char16_t>> parser;
 
     Directives directives;
     TokenStream::Position startPosition;
 
     RootedScript script;
 };
 
 AutoFrontendTraceLog::AutoFrontendTraceLog(JSContext* cx, const TraceLoggerTextId id,
@@ -231,18 +231,17 @@ bool
 BytecodeCompiler::createParser()
 {
     usedNames.emplace(cx);
     if (!usedNames->init())
         return false;
 
     if (canLazilyParse()) {
         syntaxParser.emplace(cx, alloc, options, sourceBuffer.get(), sourceBuffer.length(),
-                             /* foldConstants = */ false, *usedNames,
-                             (Parser<SyntaxParseHandler>*) nullptr, (LazyScript*) nullptr);
+                             /* foldConstants = */ false, *usedNames, nullptr, nullptr);
 
         if (!syntaxParser->checkOptions())
             return false;
     }
 
     parser.emplace(cx, alloc, options, sourceBuffer.get(), sourceBuffer.length(),
                    /* foldConstants = */ true, *usedNames, syntaxParser.ptrOr(nullptr), nullptr);
     parser->ss = scriptSource;
@@ -668,18 +667,19 @@ frontend::CompileLazyFunction(JSContext*
             ? JS_TELEMETRY_PRIVILEGED_PARSER_COMPILE_LAZY_AFTER_MS
             : JS_TELEMETRY_WEB_PARSER_COMPILE_LAZY_AFTER_MS;
         cx->runtime()->addTelemetry(HISTOGRAM, delta.ToMilliseconds());
     }
 
     UsedNameTracker usedNames(cx);
     if (!usedNames.init())
         return false;
-    Parser<FullParseHandler> parser(cx, cx->tempLifoAlloc(), options, chars, length,
-                                    /* foldConstants = */ true, usedNames, nullptr, lazy);
+    Parser<FullParseHandler, char16_t> parser(cx, cx->tempLifoAlloc(), options, chars, length,
+                                              /* foldConstants = */ true, usedNames, nullptr,
+                                              lazy);
     if (!parser.checkOptions())
         return false;
 
     Rooted<JSFunction*> fun(cx, lazy->functionNonDelazifying());
     MOZ_ASSERT(!lazy->isLegacyGenerator());
     ParseNode* pn = parser.standaloneLazyFunction(fun, lazy->strict(), lazy->generatorKind(),
                                                   lazy->asyncKind());
     if (!pn)
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -2123,17 +2123,17 @@ class ForOfLoopControl : public LoopCont
                 return false;
         }
 
         return true;
     }
 };
 
 BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent,
-                                 Parser<FullParseHandler>* parser, SharedContext* sc,
+                                 Parser<FullParseHandler, char16_t>* parser, SharedContext* sc,
                                  HandleScript script, Handle<LazyScript*> lazyScript,
                                  uint32_t lineNum, EmitterMode emitterMode)
   : sc(sc),
     cx(sc->context),
     parent(parent),
     script(cx, script),
     lazyScript(cx, lazyScript),
     prologue(cx, lineNum),
@@ -2163,17 +2163,17 @@ BytecodeEmitter::BytecodeEmitter(Bytecod
     emittingRunOnceLambda(false),
     emitterMode(emitterMode),
     functionBodyEndPosSet(false)
 {
     MOZ_ASSERT_IF(emitterMode == LazyFunction, lazyScript);
 }
 
 BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent,
-                                 Parser<FullParseHandler>* parser, SharedContext* sc,
+                                 Parser<FullParseHandler, char16_t>* parser, SharedContext* sc,
                                  HandleScript script, Handle<LazyScript*> lazyScript,
                                  TokenPos bodyPosition, EmitterMode emitterMode)
     : BytecodeEmitter(parent, parser, sc, script, lazyScript,
                       parser->tokenStream.srcCoords.lineNum(bodyPosition.begin),
                       emitterMode)
 {
     setFunctionBodyEndPos(bodyPosition);
 }
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -18,20 +18,20 @@
 #include "frontend/Parser.h"
 #include "frontend/SharedContext.h"
 #include "frontend/SourceNotes.h"
 #include "vm/Interpreter.h"
 
 namespace js {
 namespace frontend {
 
-class FullParseHandler;
+template <typename CharT> class FullParseHandler;
 class ObjectBox;
 class ParseNode;
-template <typename ParseHandler> class Parser;
+template <template <typename CharT> class ParseHandler, typename CharT> class Parser;
 class SharedContext;
 class TokenStream;
 
 class CGConstList {
     Vector<Value> list;
   public:
     explicit CGConstList(JSContext* cx) : list(cx) {}
     MOZ_MUST_USE bool append(const Value& v) {
@@ -201,17 +201,17 @@ struct MOZ_STACK_CLASS BytecodeEmitter
 
         EmitSection(JSContext* cx, uint32_t lineNum)
           : code(cx), notes(cx), lastNoteOffset(0), currentLine(lineNum), lastColumn(0),
             lastTarget{ -1 - ptrdiff_t(JSOP_JUMPTARGET_LENGTH) }
         {}
     };
     EmitSection prologue, main, *current;
 
-    Parser<FullParseHandler>* const parser;
+    Parser<FullParseHandler, char16_t>* const parser;
 
     PooledMapPtr<AtomIndexMap> atomIndices; /* literals indexed for mapping */
     unsigned        firstLine;      /* first line, for JSScript::initFromEmitter */
 
     uint32_t        maxFixedSlots;  /* maximum number of fixed frame slots so far */
     uint32_t        maxStackDepth;  /* maximum number of expression stack slots so far */
 
     int32_t         stackDepth;     /* current stack depth in script frame */
@@ -275,24 +275,24 @@ struct MOZ_STACK_CLASS BytecodeEmitter
     bool functionBodyEndPosSet;
 
     /*
      * Note that BytecodeEmitters are magic: they own the arena "top-of-stack"
      * space above their tempMark points. This means that you cannot alloc from
      * tempLifoAlloc and save the pointer beyond the next BytecodeEmitter
      * destruction.
      */
-    BytecodeEmitter(BytecodeEmitter* parent, Parser<FullParseHandler>* parser, SharedContext* sc,
-                    HandleScript script, Handle<LazyScript*> lazyScript, uint32_t lineNum,
-                    EmitterMode emitterMode = Normal);
+    BytecodeEmitter(BytecodeEmitter* parent, Parser<FullParseHandler, char16_t>* parser,
+                    SharedContext* sc, HandleScript script, Handle<LazyScript*> lazyScript,
+                    uint32_t lineNum, EmitterMode emitterMode = Normal);
 
     // An alternate constructor that uses a TokenPos for the starting
     // line and that sets functionBodyEndPos as well.
-    BytecodeEmitter(BytecodeEmitter* parent, Parser<FullParseHandler>* parser, SharedContext* sc,
-                    HandleScript script, Handle<LazyScript*> lazyScript,
+    BytecodeEmitter(BytecodeEmitter* parent, Parser<FullParseHandler, char16_t>* parser,
+                    SharedContext* sc, HandleScript script, Handle<LazyScript*> lazyScript,
                     TokenPos bodyPosition, EmitterMode emitterMode = Normal);
 
     MOZ_MUST_USE bool init();
 
     template <typename Predicate /* (NestableControl*) -> bool */>
     NestableControl* findInnermostNestableControl(Predicate predicate) const;
 
     template <typename T>
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -514,20 +514,21 @@ Boolish(ParseNode* pn)
       }
 
       default:
         return Unknown;
     }
 }
 
 static bool
-Fold(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bool inGenexpLambda);
+Fold(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler, char16_t>& parser,
+     bool inGenexpLambda);
 
 static bool
-FoldCondition(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler>& parser,
+FoldCondition(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser,
               bool inGenexpLambda)
 {
     // Conditions fold like any other expression...
     if (!Fold(cx, nodePtr, parser, inGenexpLambda))
         return false;
 
     // ...but then they sometimes can be further folded to constants.
     ParseNode* node = *nodePtr;
@@ -548,17 +549,17 @@ FoldCondition(JSContext* cx, ParseNode**
         }
         node->setArity(PN_NULLARY);
     }
 
     return true;
 }
 
 static bool
-FoldTypeOfExpr(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
+FoldTypeOfExpr(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
                bool inGenexpLambda)
 {
     MOZ_ASSERT(node->isKind(PNK_TYPEOFEXPR));
     MOZ_ASSERT(node->isArity(PN_UNARY));
 
     ParseNode*& expr = node->pn_kid;
     if (!Fold(cx, &expr, parser, inGenexpLambda))
         return false;
@@ -584,17 +585,17 @@ FoldTypeOfExpr(JSContext* cx, ParseNode*
         node->setOp(JSOP_NOP);
         node->pn_atom = result;
     }
 
     return true;
 }
 
 static bool
-FoldDeleteExpr(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
+FoldDeleteExpr(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
                bool inGenexpLambda)
 {
     MOZ_ASSERT(node->isKind(PNK_DELETEEXPR));
     MOZ_ASSERT(node->isArity(PN_UNARY));
 
     ParseNode*& expr = node->pn_kid;
     if (!Fold(cx, &expr, parser, inGenexpLambda))
         return false;
@@ -607,17 +608,17 @@ FoldDeleteExpr(JSContext* cx, ParseNode*
         node->setArity(PN_NULLARY);
         node->setOp(JSOP_TRUE);
     }
 
     return true;
 }
 
 static bool
-FoldDeleteElement(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
+FoldDeleteElement(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
                   bool inGenexpLambda)
 {
     MOZ_ASSERT(node->isKind(PNK_DELETEELEM));
     MOZ_ASSERT(node->isArity(PN_UNARY));
     MOZ_ASSERT(node->pn_kid->isKind(PNK_ELEM));
 
     ParseNode*& expr = node->pn_kid;
     if (!Fold(cx, &expr, parser, inGenexpLambda))
@@ -632,17 +633,17 @@ FoldDeleteElement(JSContext* cx, ParseNo
     MOZ_ASSERT(expr->isKind(PNK_ELEM) || expr->isKind(PNK_DOT));
     if (expr->isKind(PNK_DOT))
         node->setKind(PNK_DELETEPROP);
 
     return true;
 }
 
 static bool
-FoldDeleteProperty(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
+FoldDeleteProperty(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
                    bool inGenexpLambda)
 {
     MOZ_ASSERT(node->isKind(PNK_DELETEPROP));
     MOZ_ASSERT(node->isArity(PN_UNARY));
     MOZ_ASSERT(node->pn_kid->isKind(PNK_DOT));
 
     ParseNode*& expr = node->pn_kid;
 #ifdef DEBUG
@@ -654,17 +655,17 @@ FoldDeleteProperty(JSContext* cx, ParseN
 
     MOZ_ASSERT(expr->isKind(oldKind),
                "kind should have remained invariant under folding");
 
     return true;
 }
 
 static bool
-FoldNot(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
+FoldNot(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
         bool inGenexpLambda)
 {
     MOZ_ASSERT(node->isKind(PNK_NOT));
     MOZ_ASSERT(node->isArity(PN_UNARY));
 
     ParseNode*& expr = node->pn_kid;
     if (!FoldCondition(cx, &expr, parser, inGenexpLambda))
         return false;
@@ -689,17 +690,17 @@ FoldNot(JSContext* cx, ParseNode* node, 
         node->setArity(PN_NULLARY);
         node->setOp(newval ? JSOP_TRUE : JSOP_FALSE);
     }
 
     return true;
 }
 
 static bool
-FoldUnaryArithmetic(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
+FoldUnaryArithmetic(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
                     bool inGenexpLambda)
 {
     MOZ_ASSERT(node->isKind(PNK_BITNOT) || node->isKind(PNK_POS) || node->isKind(PNK_NEG),
                "need a different method for this node kind");
     MOZ_ASSERT(node->isArity(PN_UNARY));
 
     ParseNode*& expr = node->pn_kid;
     if (!Fold(cx, &expr, parser, inGenexpLambda))
@@ -723,38 +724,38 @@ FoldUnaryArithmetic(JSContext* cx, Parse
         node->setArity(PN_NULLARY);
         node->pn_dval = d;
     }
 
     return true;
 }
 
 static bool
-FoldIncrementDecrement(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
+FoldIncrementDecrement(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
                        bool inGenexpLambda)
 {
     MOZ_ASSERT(node->isKind(PNK_PREINCREMENT) ||
                node->isKind(PNK_POSTINCREMENT) ||
                node->isKind(PNK_PREDECREMENT) ||
                node->isKind(PNK_POSTDECREMENT));
     MOZ_ASSERT(node->isArity(PN_UNARY));
 
     ParseNode*& target = node->pn_kid;
-    MOZ_ASSERT(parser.isValidSimpleAssignmentTarget(target, Parser<FullParseHandler>::PermitAssignmentToFunctionCalls));
+    MOZ_ASSERT(parser.isValidSimpleAssignmentTarget(target, Parser<FullParseHandler, char16_t>::PermitAssignmentToFunctionCalls));
 
     if (!Fold(cx, &target, parser, inGenexpLambda))
         return false;
 
-    MOZ_ASSERT(parser.isValidSimpleAssignmentTarget(target, Parser<FullParseHandler>::PermitAssignmentToFunctionCalls));
+    MOZ_ASSERT(parser.isValidSimpleAssignmentTarget(target, Parser<FullParseHandler, char16_t>::PermitAssignmentToFunctionCalls));
 
     return true;
 }
 
 static bool
-FoldAndOr(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler>& parser,
+FoldAndOr(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser,
           bool inGenexpLambda)
 {
     ParseNode* node = *nodePtr;
 
     MOZ_ASSERT(node->isKind(PNK_AND) || node->isKind(PNK_OR));
     MOZ_ASSERT(node->isArity(PN_LIST));
 
     bool isOrNode = node->isKind(PNK_OR);
@@ -827,17 +828,17 @@ FoldAndOr(JSContext* cx, ParseNode** nod
         node->setArity(PN_NULLARY);
         parser.freeTree(node);
     }
 
     return true;
 }
 
 static bool
-FoldConditional(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler>& parser,
+FoldConditional(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser,
                 bool inGenexpLambda)
 {
     ParseNode** nextNode = nodePtr;
 
     do {
         // |nextNode| on entry points to the C?T:F expression to be folded.
         // Reset it to exit the loop in the common case where F isn't another
         // ?: expression.
@@ -897,17 +898,17 @@ FoldConditional(JSContext* cx, ParseNode
 
         parser.freeTree(discarded);
     } while (nextNode);
 
     return true;
 }
 
 static bool
-FoldIf(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler>& parser,
+FoldIf(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser,
        bool inGenexpLambda)
 {
     ParseNode** nextNode = nodePtr;
 
     do {
         // |nextNode| on entry points to the initial |if| to be folded.  Reset
         // it to exit the loop when the |else| arm isn't another |if|.
         nodePtr = nextNode;
@@ -1001,17 +1002,17 @@ FoldIf(JSContext* cx, ParseNode** nodePt
             parser.freeTree(node);
         }
     } while (nextNode);
 
     return true;
 }
 
 static bool
-FoldFunction(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
+FoldFunction(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
              bool inGenexpLambda)
 {
     MOZ_ASSERT(node->isKind(PNK_FUNCTION));
     MOZ_ASSERT(node->isArity(PN_CODE));
 
     // Don't constant-fold inside "use asm" code, as this could create a parse
     // tree that doesn't type-check as asm.js.
     if (node->pn_funbox->useAsmOrInsideUseAsm())
@@ -1064,28 +1065,28 @@ ComputeBinary(ParseNodeKind kind, double
     MOZ_ASSERT(kind == PNK_LSH || kind == PNK_RSH);
 
     int32_t i = ToInt32(left);
     uint32_t j = ToUint32(right) & 31;
     return int32_t((kind == PNK_LSH) ? uint32_t(i) << j : i >> j);
 }
 
 static bool
-FoldModule(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser)
+FoldModule(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_MODULE));
     MOZ_ASSERT(node->isArity(PN_CODE));
 
     ParseNode*& moduleBody = node->pn_body;
     MOZ_ASSERT(moduleBody);
     return Fold(cx, &moduleBody, parser, false);
 }
 
 static bool
-FoldBinaryArithmetic(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
+FoldBinaryArithmetic(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
                      bool inGenexpLambda)
 {
     MOZ_ASSERT(node->isKind(PNK_SUB) ||
                node->isKind(PNK_STAR) ||
                node->isKind(PNK_LSH) ||
                node->isKind(PNK_RSH) ||
                node->isKind(PNK_URSH) ||
                node->isKind(PNK_DIV) ||
@@ -1147,17 +1148,17 @@ FoldBinaryArithmetic(JSContext* cx, Pars
             parser.freeTree(elem);
         }
     }
 
     return true;
 }
 
 static bool
-FoldExponentiation(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
+FoldExponentiation(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
                    bool inGenexpLambda)
 {
     MOZ_ASSERT(node->isKind(PNK_POW));
     MOZ_ASSERT(node->isArity(PN_LIST));
     MOZ_ASSERT(node->pn_count >= 2);
 
     // Fold each operand, ideally into a number.
     ParseNode** listp = &node->pn_head;
@@ -1191,17 +1192,17 @@ FoldExponentiation(JSContext* cx, ParseN
     node->setKind(PNK_NUMBER);
     node->setArity(PN_NULLARY);
     node->setOp(JSOP_DOUBLE);
     node->pn_dval = ecmaPow(d1, d2);
     return true;
 }
 
 static bool
-FoldList(JSContext* cx, ParseNode* list, Parser<FullParseHandler>& parser,
+FoldList(JSContext* cx, ParseNode* list, Parser<FullParseHandler, char16_t>& parser,
          bool inGenexpLambda)
 {
     MOZ_ASSERT(list->isArity(PN_LIST));
 
     ParseNode** elem = &list->pn_head;
     for (; *elem; elem = &(*elem)->pn_next) {
         if (!Fold(cx, elem, parser, inGenexpLambda))
             return false;
@@ -1211,32 +1212,32 @@ FoldList(JSContext* cx, ParseNode* list,
     list->pn_tail = elem;
 
     list->checkListConsistency();
 
     return true;
 }
 
 static bool
-FoldReturn(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
+FoldReturn(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
            bool inGenexpLambda)
 {
     MOZ_ASSERT(node->isKind(PNK_RETURN));
     MOZ_ASSERT(node->isArity(PN_UNARY));
 
     if (ParseNode*& expr = node->pn_kid) {
         if (!Fold(cx, &expr, parser, inGenexpLambda))
             return false;
     }
 
     return true;
 }
 
 static bool
-FoldTry(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
+FoldTry(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
         bool inGenexpLambda)
 {
     MOZ_ASSERT(node->isKind(PNK_TRY));
     MOZ_ASSERT(node->isArity(PN_TERNARY));
 
     ParseNode*& statements = node->pn_kid1;
     if (!Fold(cx, &statements, parser, inGenexpLambda))
         return false;
@@ -1250,17 +1251,17 @@ FoldTry(JSContext* cx, ParseNode* node, 
         if (!Fold(cx, &finally, parser, inGenexpLambda))
             return false;
     }
 
     return true;
 }
 
 static bool
-FoldCatch(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
+FoldCatch(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
           bool inGenexpLambda)
 {
     MOZ_ASSERT(node->isKind(PNK_CATCH));
     MOZ_ASSERT(node->isArity(PN_TERNARY));
 
     ParseNode*& declPattern = node->pn_kid1;
     if (!Fold(cx, &declPattern, parser, inGenexpLambda))
         return false;
@@ -1274,17 +1275,17 @@ FoldCatch(JSContext* cx, ParseNode* node
         if (!Fold(cx, &statements, parser, inGenexpLambda))
             return false;
     }
 
     return true;
 }
 
 static bool
-FoldClass(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
+FoldClass(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
           bool inGenexpLambda)
 {
     MOZ_ASSERT(node->isKind(PNK_CLASS));
     MOZ_ASSERT(node->isArity(PN_TERNARY));
 
     if (ParseNode*& classNames = node->pn_kid1) {
         if (!Fold(cx, &classNames, parser, inGenexpLambda))
             return false;
@@ -1295,17 +1296,17 @@ FoldClass(JSContext* cx, ParseNode* node
             return false;
     }
 
     ParseNode*& body = node->pn_kid3;
     return Fold(cx, &body, parser, inGenexpLambda);
 }
 
 static bool
-FoldElement(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler>& parser,
+FoldElement(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser,
             bool inGenexpLambda)
 {
     ParseNode* node = *nodePtr;
 
     MOZ_ASSERT(node->isKind(PNK_ELEM));
     MOZ_ASSERT(node->isArity(PN_BINARY));
 
     ParseNode*& expr = node->pn_left;
@@ -1371,17 +1372,17 @@ FoldElement(JSContext* cx, ParseNode** n
     node->setArity(PN_UNARY);
     node->pn_kid = key;
     parser.freeTree(node);
 
     return true;
 }
 
 static bool
-FoldAdd(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler>& parser,
+FoldAdd(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser,
         bool inGenexpLambda)
 {
     ParseNode* node = *nodePtr;
 
     MOZ_ASSERT(node->isKind(PNK_ADD));
     MOZ_ASSERT(node->isArity(PN_LIST));
     MOZ_ASSERT(node->pn_count >= 2);
 
@@ -1522,17 +1523,17 @@ FoldAdd(JSContext* cx, ParseNode** nodeP
         node->setOp(JSOP_TRUE);
         parser.freeTree(node);
     }
 
     return true;
 }
 
 static bool
-FoldCall(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
+FoldCall(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
          bool inGenexpLambda)
 {
     MOZ_ASSERT(node->isKind(PNK_CALL) || node->isKind(PNK_SUPERCALL) ||
                node->isKind(PNK_TAGGED_TEMPLATE));
     MOZ_ASSERT(node->isArity(PN_LIST));
 
     // Don't fold a parenthesized callable component in an invocation, as this
     // might cause a different |this| value to be used, changing semantics:
@@ -1557,29 +1558,29 @@ FoldCall(JSContext* cx, ParseNode* node,
     // If the last node in the list was replaced, pn_tail points into the wrong node.
     node->pn_tail = listp;
 
     node->checkListConsistency();
     return true;
 }
 
 static bool
-FoldForInOrOf(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
+FoldForInOrOf(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
               bool inGenexpLambda)
 {
     MOZ_ASSERT(node->isKind(PNK_FORIN) || node->isKind(PNK_FOROF));
     MOZ_ASSERT(node->isArity(PN_TERNARY));
     MOZ_ASSERT(!node->pn_kid2);
 
     return Fold(cx, &node->pn_kid1, parser, inGenexpLambda) &&
            Fold(cx, &node->pn_kid3, parser, inGenexpLambda);
 }
 
 static bool
-FoldForHead(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
+FoldForHead(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
             bool inGenexpLambda)
 {
     MOZ_ASSERT(node->isKind(PNK_FORHEAD));
     MOZ_ASSERT(node->isArity(PN_TERNARY));
 
     if (ParseNode*& init = node->pn_kid1) {
         if (!Fold(cx, &init, parser, inGenexpLambda))
             return false;
@@ -1599,17 +1600,17 @@ FoldForHead(JSContext* cx, ParseNode* no
         if (!Fold(cx, &update, parser, inGenexpLambda))
             return false;
     }
 
     return true;
 }
 
 static bool
-FoldDottedProperty(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
+FoldDottedProperty(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
                    bool inGenexpLambda)
 {
     MOZ_ASSERT(node->isKind(PNK_DOT));
     MOZ_ASSERT(node->isArity(PN_NAME));
 
     // Iterate through a long chain of dotted property accesses to find the
     // most-nested non-dotted property node, then fold that.
     ParseNode** nested = &node->pn_expr;
@@ -1617,30 +1618,31 @@ FoldDottedProperty(JSContext* cx, ParseN
         MOZ_ASSERT((*nested)->isArity(PN_NAME));
         nested = &(*nested)->pn_expr;
     }
 
     return Fold(cx, nested, parser, inGenexpLambda);
 }
 
 static bool
-FoldName(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
+FoldName(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
          bool inGenexpLambda)
 {
     MOZ_ASSERT(node->isKind(PNK_NAME));
     MOZ_ASSERT(node->isArity(PN_NAME));
 
     if (!node->pn_expr)
         return true;
 
     return Fold(cx, &node->pn_expr, parser, inGenexpLambda);
 }
 
 bool
-Fold(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bool inGenexpLambda)
+Fold(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler, char16_t>& parser,
+     bool inGenexpLambda)
 {
     if (!CheckRecursionLimit(cx))
         return false;
 
     ParseNode* pn = *pnp;
 
     switch (pn->getKind()) {
       case PNK_NOP:
@@ -1920,19 +1922,24 @@ Fold(JSContext* cx, ParseNode** pnp, Par
       case PNK_LIMIT: // invalid sentinel value
         MOZ_CRASH("invalid node kind");
     }
 
     MOZ_CRASH("shouldn't reach here");
     return false;
 }
 
+template<typename CharT>
 bool
-frontend::FoldConstants(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler>* parser)
+frontend::FoldConstants(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler, CharT>* parser)
 {
     // Don't constant-fold inside "use asm" code, as this could create a parse
     // tree that doesn't type-check as asm.js.
     if (parser->pc->useAsmOrInsideUseAsm())
         return true;
 
     AutoTraceLog traceLog(TraceLoggerForCurrentThread(cx), TraceLogger_BytecodeFoldConstants);
     return Fold(cx, pnp, *parser, false);
 }
+
+template bool
+frontend::FoldConstants(JSContext* cx, ParseNode** pnp,
+                        Parser<FullParseHandler, char16_t>* parser);
--- a/js/src/frontend/FoldConstants.h
+++ b/js/src/frontend/FoldConstants.h
@@ -7,35 +7,40 @@
 #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;
+
 // Perform constant folding on the given AST. For example, the program
 // `print(2 + 2)` would become `print(4)`.
 //
 // pnp is the address of a pointer variable that points to the root node of the
 // AST. On success, *pnp points to the root node of the new tree, which may be
 // the same node (unchanged or modified in place) or a new node.
 //
 // Usage:
 //    pn = parser->statement();
 //    if (!pn)
 //        return false;
 //    if (!FoldConstants(cx, &pn, parser))
 //        return false;
+template<typename CharT>
 MOZ_MUST_USE bool
-FoldConstants(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler>* parser);
+FoldConstants(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler, CharT>* parser);
 
+template<typename CharT>
 inline MOZ_MUST_USE bool
-FoldConstants(JSContext* cx, SyntaxParseHandler::Node* pnp,
-              Parser<SyntaxParseHandler>* parser)
+FoldConstants(JSContext* cx, typename SyntaxParseHandler<CharT>::Node* pnp,
+              Parser<SyntaxParseHandler, CharT>* parser)
 {
     return true;
 }
 
 } /* namespace frontend */
 } /* namespace js */
 
 #endif /* frontend_FoldConstants_h */
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -13,24 +13,24 @@
 #include <string.h>
 
 #include "frontend/ParseNode.h"
 #include "frontend/SharedContext.h"
 
 namespace js {
 namespace frontend {
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 class Parser;
 
-class SyntaxParseHandler;
+template <typename CharT> class SyntaxParseHandler;
 
 // Parse handler used when generating a full parse tree for all code which the
 // parser encounters.
-class FullParseHandler
+class FullParseHandlerBase
 {
     ParseNodeAllocator allocator;
 
     ParseNode* allocParseNode(size_t size) {
         MOZ_ASSERT(size == sizeof(ParseNode));
         return static_cast<ParseNode*>(allocator.allocNode());
     }
 
@@ -47,25 +47,16 @@ class FullParseHandler
      * was previously lazily parsed, that lazy function and the current index
      * into its inner functions. We do not want to reparse the inner functions.
      */
     const Rooted<LazyScript*> lazyOuterFunction_;
     size_t lazyInnerFunctionIndex;
     size_t lazyClosedOverBindingIndex;
 
   public:
-
-    /*
-     * 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.
-     */
-    Parser<SyntaxParseHandler>* syntaxParser;
-
     /* new_ methods for creating parse nodes. These report OOM on context. */
     JS_DECLARE_NEW_METHODS(new_, allocParseNode, inline)
 
     typedef ParseNode* Node;
 
     bool isPropertyAccess(ParseNode* node) {
         return node->isKind(PNK_DOT) || node->isKind(PNK_ELEM);
     }
@@ -87,23 +78,21 @@ class FullParseHandler
         return node->isInParens() && (node->isKind(PNK_OBJECT) || node->isKind(PNK_ARRAY));
     }
 
     static bool isDestructuringPatternAnyParentheses(ParseNode* node) {
         return isUnparenthesizedDestructuringPattern(node) ||
                isParenthesizedDestructuringPattern(node);
     }
 
-    FullParseHandler(JSContext* cx, LifoAlloc& alloc, Parser<SyntaxParseHandler>* syntaxParser,
-                     LazyScript* lazyOuterFunction)
+    FullParseHandlerBase(JSContext* cx, LifoAlloc& alloc, LazyScript* lazyOuterFunction)
       : allocator(cx, alloc),
         lazyOuterFunction_(cx, lazyOuterFunction),
         lazyInnerFunctionIndex(0),
-        lazyClosedOverBindingIndex(0),
-        syntaxParser(syntaxParser)
+        lazyClosedOverBindingIndex(0)
     {}
 
     static ParseNode* null() { return nullptr; }
 
     ParseNode* freeTree(ParseNode* pn) { return allocator.freeTree(pn); }
     void prepareNodeForMutation(ParseNode* pn) { return allocator.prepareNodeForMutation(pn); }
 
     ParseNode* newName(PropertyName* name, const TokenPos& pos, JSContext* cx)
@@ -910,20 +899,16 @@ class FullParseHandler
         }
         return nullptr;
     }
 
     void adjustGetToSet(ParseNode* node) {
         node->setOp(node->isOp(JSOP_GETLOCAL) ? JSOP_SETLOCAL : JSOP_SETNAME);
     }
 
-    void disableSyntaxParser() {
-        syntaxParser = nullptr;
-    }
-
     bool canSkipLazyInnerFunctions() {
         return !!lazyOuterFunction_;
     }
     bool canSkipLazyClosedOverBindings() {
         return !!lazyOuterFunction_;
     }
     LazyScript* lazyOuterFunction() {
         return lazyOuterFunction_;
@@ -934,29 +919,31 @@ class FullParseHandler
     }
     JSAtom* nextLazyClosedOverBinding() {
         MOZ_ASSERT(lazyClosedOverBindingIndex < lazyOuterFunction()->numClosedOverBindings());
         return lazyOuterFunction()->closedOverBindings()[lazyClosedOverBindingIndex++];
     }
 };
 
 inline bool
-FullParseHandler::addCatchBlock(ParseNode* catchList, ParseNode* lexicalScope,
-                                ParseNode* catchName, ParseNode* catchGuard, ParseNode* catchBody)
+FullParseHandlerBase::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
-FullParseHandler::setLastFunctionFormalParameterDefault(ParseNode* funcpn, ParseNode* defaultValue)
+FullParseHandlerBase::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);
 
@@ -974,22 +961,51 @@ FullParseHandler::setLastFunctionFormalP
         pnchild->pn_next = pn;
     }
     funcpn->pn_body->pn_tail = &pn->pn_next;
 
     return true;
 }
 
 inline bool
-FullParseHandler::finishInitializerAssignment(ParseNode* pn, ParseNode* init)
+FullParseHandlerBase::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,
-                              FullParseHandler* handler, ParseContext* pc)
+                              FullParseHandlerBase* 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 FullParseHandler;
+class FullParseHandlerBase;
 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,
-                       FullParseHandler* handler, ParseContext* pc);
+                       FullParseHandlerBase* 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,26 +762,26 @@ ParserBase::reportNoOffset(ParseReportKi
         break;
     }
 
     va_end(args);
     return result;
 }
 
 template <>
-bool
-Parser<FullParseHandler>::abortIfSyntaxParser()
+inline bool
+Parser<FullParseHandler, char16_t>::abortIfSyntaxParser()
 {
     handler.disableSyntaxParser();
     return true;
 }
 
 template <>
-bool
-Parser<SyntaxParseHandler>::abortIfSyntaxParser()
+inline bool
+Parser<SyntaxParseHandler, char16_t>::abortIfSyntaxParser()
 {
     abortedSyntaxParse = true;
     return false;
 }
 
 ParserBase::ParserBase(JSContext* cx, LifoAlloc& alloc,
                        const ReadOnlyCompileOptions& options,
                        const char16_t* chars, size_t length,
@@ -817,65 +817,65 @@ 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 <typename ParseHandler>
-Parser<ParseHandler>::Parser(JSContext* cx, LifoAlloc& alloc,
-                             const ReadOnlyCompileOptions& options,
-                             const char16_t* chars, size_t length,
-                             bool foldConstants,
-                             UsedNameTracker& usedNames,
-                             Parser<SyntaxParseHandler>* syntaxParser,
-                             LazyScript* lazyOuterFunction)
+template <template <typename CharT> 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,
+                                    LazyScript* lazyOuterFunction)
   : ParserBase(cx, alloc, options, chars, length, foldConstants, usedNames, lazyOuterFunction),
     AutoGCRooter(cx, PARSER),
     handler(cx, alloc, syntaxParser, lazyOuterFunction)
 {
     // The Mozilla specific JSOPTION_EXTRA_WARNINGS option adds extra warnings
     // which are not generated if functions are parsed lazily. Note that the
     // standard "use strict" does not inhibit lazy parsing.
     if (options.extraWarningsOption)
         handler.disableSyntaxParser();
 }
 
-template<typename ParseHandler>
+template<template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::checkOptions()
+Parser<ParseHandler, CharT>::checkOptions()
 {
 #ifdef DEBUG
     checkOptionsCalled = true;
 #endif
 
     return tokenStream.checkOptions();
 }
 
-template <typename ParseHandler>
-Parser<ParseHandler>::~Parser()
+template <template <typename CharT> class ParseHandler, typename CharT>
+Parser<ParseHandler, CharT>::~Parser()
 {
     MOZ_ASSERT(checkOptionsCalled);
 }
 
 template <>
 void
-Parser<SyntaxParseHandler>::setAwaitIsKeyword(bool isKeyword)
+Parser<SyntaxParseHandler, char16_t>::setAwaitIsKeyword(bool isKeyword)
 {
     awaitIsKeyword_ = isKeyword;
 }
 
 template <>
 void
-Parser<FullParseHandler>::setAwaitIsKeyword(bool isKeyword)
+Parser<FullParseHandler, char16_t>::setAwaitIsKeyword(bool isKeyword)
 {
     awaitIsKeyword_ = isKeyword;
-    if (Parser<SyntaxParseHandler>* parser = handler.syntaxParser)
+    if (Parser<SyntaxParseHandler, char16_t>* parser = handler.getSyntaxParser())
         parser->setAwaitIsKeyword(isKeyword);
 }
 
 ObjectBox*
 ParserBase::newObjectBox(JSObject* obj)
 {
     MOZ_ASSERT(obj);
 
@@ -893,22 +893,22 @@ ParserBase::newObjectBox(JSObject* obj)
         return nullptr;
     }
 
     traceListHead = objbox;
 
     return objbox;
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 FunctionBox*
-Parser<ParseHandler>::newFunctionBox(Node fn, JSFunction* fun, uint32_t toStringStart,
-                                     Directives inheritedDirectives,
-                                     GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
-                                     bool tryAnnexB)
+Parser<ParseHandler, CharT>::newFunctionBox(Node fn, JSFunction* fun, uint32_t toStringStart,
+                                            Directives inheritedDirectives,
+                                            GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
+                                            bool tryAnnexB)
 {
     MOZ_ASSERT(fun);
     MOZ_ASSERT_IF(tryAnnexB, !pc->sc()->strict());
 
     /*
      * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
      * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
      * arenas containing the entries must be alive until we are done with
@@ -940,35 +940,35 @@ ModuleSharedContext::ModuleSharedContext
     module_(cx, module),
     enclosingScope_(cx, enclosingScope),
     bindings(cx),
     builder(builder)
 {
     thisBinding_ = ThisBinding::Module;
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 void
-Parser<ParseHandler>::trace(JSTracer* trc)
+Parser<ParseHandler, CharT>::trace(JSTracer* trc)
 {
     ObjectBox::TraceList(trc, traceListHead);
 }
 
 void
 TraceParser(JSTracer* trc, AutoGCRooter* parser)
 {
-    static_cast<Parser<FullParseHandler>*>(parser)->trace(trc);
+    static_cast<Parser<FullParseHandler, char16_t>*>(parser)->trace(trc);
 }
 
 /*
  * Parse a top-level JS script.
  */
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::parse()
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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);
     if (!globalpc.init())
@@ -1030,20 +1030,20 @@ ParserBase::hasValidSimpleStrictParamete
     for (auto* name : pc->positionalFormalParameterNames()) {
         MOZ_ASSERT(name);
         if (!isValidStrictBinding(name->asPropertyName()))
             return false;
     }
     return true;
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 void
-Parser<ParseHandler>::reportMissingClosing(unsigned errorNumber, unsigned noteNumber,
-                                           uint32_t openedPos)
+Parser<ParseHandler, CharT>::reportMissingClosing(unsigned errorNumber, unsigned noteNumber,
+                                                  uint32_t openedPos)
 {
     auto notes = MakeUnique<JSErrorNotes>();
     if (!notes) {
         ReportOutOfMemory(pc->sc()->context);
         return;
     }
 
     uint32_t line, column;
@@ -1061,20 +1061,20 @@ Parser<ParseHandler>::reportMissingClosi
                              noteNumber, lineNumber, columnNumber))
     {
         return;
     }
 
     errorWithNotes(Move(notes), errorNumber);
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 void
-Parser<ParseHandler>::reportRedeclaration(HandlePropertyName name, DeclarationKind prevKind,
-                                          TokenPos pos, uint32_t prevPos)
+Parser<ParseHandler, CharT>::reportRedeclaration(HandlePropertyName name, DeclarationKind prevKind,
+                                                 TokenPos pos, uint32_t prevPos)
 {
     JSAutoByteString bytes;
     if (!AtomToPrintableString(context, name, &bytes))
         return;
 
     if (prevPos == DeclaredNameInfo::npos) {
         errorAt(pos.begin, JSMSG_REDECLARED_VAR, DeclarationKindString(prevKind), bytes.ptr());
         return;
@@ -1112,22 +1112,22 @@ Parser<ParseHandler>::reportRedeclaratio
 // 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 <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::notePositionalFormalParameter(Node fn, HandlePropertyName name,
-                                                    uint32_t beginPos,
-                                                    bool disallowDuplicateParams,
-                                                    bool* duplicatedParam)
+Parser<ParseHandler, CharT>::notePositionalFormalParameter(Node fn, HandlePropertyName name,
+                                                           uint32_t beginPos,
+                                                           bool disallowDuplicateParams,
+                                                           bool* duplicatedParam)
 {
     if (AddDeclaredNamePtr p = pc->functionScope().lookupDeclaredNameForAdd(name)) {
         if (disallowDuplicateParams) {
             error(JSMSG_BAD_DUP_ARGS);
             return false;
         }
 
         // Strict-mode disallows duplicate args. We may not know whether we are
@@ -1157,19 +1157,19 @@ Parser<ParseHandler>::notePositionalForm
     Node paramNode = newName(name);
     if (!paramNode)
         return false;
 
     handler.addFunctionFormalParameter(fn, paramNode);
     return true;
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::noteDestructuredPositionalFormalParameter(Node fn, Node destruct)
+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;
     }
 
@@ -1181,19 +1181,19 @@ static bool
 DeclarationKindIsVar(DeclarationKind kind)
 {
     return kind == DeclarationKind::Var ||
            kind == DeclarationKind::BodyLevelFunction ||
            kind == DeclarationKind::VarForAnnexBLexicalFunction ||
            kind == DeclarationKind::ForOfVar;
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 Maybe<DeclarationKind>
-Parser<ParseHandler>::isVarRedeclaredInEval(HandlePropertyName name, DeclarationKind kind)
+Parser<ParseHandler, CharT>::isVarRedeclaredInEval(HandlePropertyName name, DeclarationKind kind)
 {
     MOZ_ASSERT(DeclarationKindIsVar(kind));
     MOZ_ASSERT(pc->sc()->isEvalContext());
 
     // In the case of eval, we also need to check enclosing VM scopes to see
     // if the var declaration is allowed in the context.
     //
     // This check is necessary in addition to
@@ -1242,20 +1242,22 @@ Parser<ParseHandler>::isVarRedeclaredInE
 
 static bool
 DeclarationKindIsParameter(DeclarationKind kind)
 {
     return kind == DeclarationKind::PositionalFormalParameter ||
            kind == DeclarationKind::FormalParameter;
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::tryDeclareVar(HandlePropertyName name, DeclarationKind kind, uint32_t beginPos,
-                                    Maybe<DeclarationKind>* redeclaredKind, uint32_t* prevPos)
+Parser<ParseHandler, CharT>::tryDeclareVar(HandlePropertyName name, DeclarationKind kind,
+                                           uint32_t beginPos,
+                                           Maybe<DeclarationKind>* redeclaredKind,
+                                           uint32_t* prevPos)
 {
     MOZ_ASSERT(DeclarationKindIsVar(kind));
 
     // It is an early error if a 'var' declaration appears inside a
     // scope contour that has a lexical declaration of the same name. For
     // example, the following are early errors:
     //
     //   { let x; var x; }
@@ -1345,20 +1347,21 @@ Parser<ParseHandler>::tryDeclareVar(Hand
         *redeclaredKind = isVarRedeclaredInEval(name, kind);
         // We don't have position information at runtime.
         *prevPos = DeclaredNameInfo::npos;
     }
 
     return true;
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::tryDeclareVarForAnnexBLexicalFunction(HandlePropertyName name,
-                                                            uint32_t beginPos, bool* tryAnnexB)
+Parser<ParseHandler, CharT>::tryDeclareVarForAnnexBLexicalFunction(HandlePropertyName name,
+                                                                   uint32_t beginPos,
+                                                                   bool* tryAnnexB)
 {
     Maybe<DeclarationKind> redeclaredKind;
     uint32_t unused;
     if (!tryDeclareVar(name, DeclarationKind::VarForAnnexBLexicalFunction, beginPos,
                        &redeclaredKind, &unused))
     {
         return false;
     }
@@ -1389,21 +1392,21 @@ Parser<ParseHandler>::tryDeclareVarForAn
         ParseContext::Scope::removeVarForAnnexBLexicalFunction(pc, name);
     } else {
         *tryAnnexB = true;
     }
 
     return true;
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::checkLexicalDeclarationDirectlyWithinBlock(ParseContext::Statement& stmt,
-                                                                 DeclarationKind kind,
-                                                                 TokenPos pos)
+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
     // within a block.
     if (!StatementKindIsBraced(stmt.kind()) &&
         stmt.kind() != StatementKind::ForLoopLexicalHead)
     {
@@ -1413,20 +1416,20 @@ Parser<ParseHandler>::checkLexicalDeclar
                 : JSMSG_LEXICAL_DECL_NOT_IN_BLOCK,
                 DeclarationKindString(kind));
         return false;
     }
 
     return true;
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::noteDeclaredName(HandlePropertyName name, DeclarationKind kind,
-                                       TokenPos pos)
+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;
 
     switch (kind) {
       case DeclarationKind::Var:
@@ -1585,19 +1588,19 @@ Parser<ParseHandler>::noteDeclaredName(H
         MOZ_CRASH("Synthesized Annex B vars should go through "
                   "tryDeclareVarForAnnexBLexicalFunction");
         break;
     }
 
     return true;
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::noteUsedName(HandlePropertyName name)
+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;
 
     // The asm.js validator does all its own symbol-table management so, as an
     // optimization, avoid doing any work here.
@@ -1610,38 +1613,38 @@ Parser<ParseHandler>::noteUsedName(Handl
     // 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 <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::hasUsedName(HandlePropertyName name)
+Parser<ParseHandler, CharT>::hasUsedName(HandlePropertyName name)
 {
     if (UsedNamePtr p = usedNames.lookup(name))
         return p->value().isUsedInScript(pc->scriptId());
     return false;
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::propagateFreeNamesAndMarkClosedOverBindings(ParseContext::Scope& scope)
+Parser<ParseHandler, CharT>::propagateFreeNamesAndMarkClosedOverBindings(ParseContext::Scope& scope)
 {
     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, SyntaxParseHandler>::value;
+    bool isSyntaxParser = mozilla::IsSame<ParseHandler<CharT>, SyntaxParseHandler<CharT>>::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();
@@ -1660,17 +1663,17 @@ Parser<ParseHandler>::propagateFreeNames
         return false;
     }
 
     return true;
 }
 
 template <>
 bool
-Parser<FullParseHandler>::checkStatementsEOF()
+Parser<FullParseHandler, char16_t>::checkStatementsEOF()
 {
     // This is designed to be paired with parsing a statement list at the top
     // level.
     //
     // The statementList() call breaks on TOK_RC, so make sure we've
     // reached EOF here.
     TokenKind tt;
     if (!tokenStream.peekToken(&tt, TokenStream::Operand))
@@ -1691,19 +1694,18 @@ NewEmptyBindingData(JSContext* cx, LifoA
     if (!bindings) {
         ReportOutOfMemory(cx);
         return nullptr;
     }
     PodZero(bindings);
     return bindings;
 }
 
-template <>
 Maybe<GlobalScope::Data*>
-Parser<FullParseHandler>::newGlobalScopeData(ParseContext::Scope& scope)
+ParserBase::newGlobalScopeData(ParseContext::Scope& scope)
 {
     Vector<BindingName> funs(context);
     Vector<BindingName> vars(context);
     Vector<BindingName> lets(context);
     Vector<BindingName> consts(context);
 
     bool allBindingsClosedOver = pc->sc()->allBindingsClosedOver();
     for (BindingIter bi = scope.bindings(pc); bi; bi++) {
@@ -1757,19 +1759,18 @@ Parser<FullParseHandler>::newGlobalScope
         bindings->constStart = cursor - start;
         PodCopy(cursor, consts.begin(), consts.length());
         bindings->length = numBindings;
     }
 
     return Some(bindings);
 }
 
-template <>
 Maybe<ModuleScope::Data*>
-Parser<FullParseHandler>::newModuleScopeData(ParseContext::Scope& scope)
+ParserBase::newModuleScopeData(ParseContext::Scope& scope)
 {
     Vector<BindingName> imports(context);
     Vector<BindingName> vars(context);
     Vector<BindingName> lets(context);
     Vector<BindingName> consts(context);
 
     bool allBindingsClosedOver = pc->sc()->allBindingsClosedOver();
     for (BindingIter bi = scope.bindings(pc); bi; bi++) {
@@ -1824,19 +1825,18 @@ Parser<FullParseHandler>::newModuleScope
         bindings->constStart = cursor - start;
         PodCopy(cursor, consts.begin(), consts.length());
         bindings->length = numBindings;
     }
 
     return Some(bindings);
 }
 
-template <>
 Maybe<EvalScope::Data*>
-Parser<FullParseHandler>::newEvalScopeData(ParseContext::Scope& scope)
+ParserBase::newEvalScopeData(ParseContext::Scope& scope)
 {
     Vector<BindingName> funs(context);
     Vector<BindingName> vars(context);
 
     for (BindingIter bi = scope.bindings(pc); bi; bi++) {
         // Eval scopes only contain 'var' bindings. Make all bindings aliased
         // for now.
         MOZ_ASSERT(bi.kind() == BindingKind::Var);
@@ -1869,19 +1869,18 @@ Parser<FullParseHandler>::newEvalScopeDa
         bindings->varStart = cursor - start;
         PodCopy(cursor, vars.begin(), vars.length());
         bindings->length = numBindings;
     }
 
     return Some(bindings);
 }
 
-template <>
 Maybe<FunctionScope::Data*>
-Parser<FullParseHandler>::newFunctionScopeData(ParseContext::Scope& scope, bool hasParameterExprs)
+ParserBase::newFunctionScopeData(ParseContext::Scope& scope, bool hasParameterExprs)
 {
     Vector<BindingName> positionalFormals(context);
     Vector<BindingName> formals(context);
     Vector<BindingName> vars(context);
 
     bool allBindingsClosedOver = pc->sc()->allBindingsClosedOver();
     bool hasDuplicateParams = pc->functionBox()->hasDuplicateParameters;
 
@@ -1964,19 +1963,18 @@ Parser<FullParseHandler>::newFunctionSco
         bindings->varStart = cursor - start;
         PodCopy(cursor, vars.begin(), vars.length());
         bindings->length = numBindings;
     }
 
     return Some(bindings);
 }
 
-template <>
 Maybe<VarScope::Data*>
-Parser<FullParseHandler>::newVarScopeData(ParseContext::Scope& scope)
+ParserBase::newVarScopeData(ParseContext::Scope& scope)
 {
     Vector<BindingName> vars(context);
 
     bool allBindingsClosedOver = pc->sc()->allBindingsClosedOver();
 
     for (BindingIter bi = scope.bindings(pc); bi; bi++) {
         BindingName binding(bi.name(), allBindingsClosedOver || bi.closedOver());
         if (!vars.append(binding))
@@ -1997,19 +1995,18 @@ Parser<FullParseHandler>::newVarScopeDat
 
         PodCopy(cursor, vars.begin(), vars.length());
         bindings->length = numBindings;
     }
 
     return Some(bindings);
 }
 
-template <>
 Maybe<LexicalScope::Data*>
-Parser<FullParseHandler>::newLexicalScopeData(ParseContext::Scope& scope)
+ParserBase::newLexicalScopeData(ParseContext::Scope& scope)
 {
     Vector<BindingName> lets(context);
     Vector<BindingName> consts(context);
 
     // Unlike other scopes with bindings which are body-level, it is unknown
     // if pc->sc()->allBindingsClosedOver() is correct at the time of
     // finishing parsing a lexical scope.
     //
@@ -2050,27 +2047,27 @@ Parser<FullParseHandler>::newLexicalScop
         PodCopy(cursor, consts.begin(), consts.length());
         bindings->length = numBindings;
     }
 
     return Some(bindings);
 }
 
 template <>
-SyntaxParseHandler::Node
-Parser<SyntaxParseHandler>::finishLexicalScope(ParseContext::Scope& scope, Node body)
+SyntaxParseHandlerBase::Node
+Parser<SyntaxParseHandler, char16_t>::finishLexicalScope(ParseContext::Scope& scope, Node body)
 {
     if (!propagateFreeNamesAndMarkClosedOverBindings(scope))
         return null();
     return body;
 }
 
 template <>
 ParseNode*
-Parser<FullParseHandler>::finishLexicalScope(ParseContext::Scope& scope, ParseNode* body)
+Parser<FullParseHandler, char16_t>::finishLexicalScope(ParseContext::Scope& scope, ParseNode* body)
 {
     if (!propagateFreeNamesAndMarkClosedOverBindings(scope))
         return nullptr;
     Maybe<LexicalScope::Data*> bindings = newLexicalScopeData(scope);
     if (!bindings)
         return nullptr;
     return handler.newLexicalScope(*bindings, body);
 }
@@ -2093,17 +2090,17 @@ IsArgumentsUsedInLegacyGenerator(JSConte
         }
     }
 
     return false;
 }
 
 template <>
 ParseNode*
-Parser<FullParseHandler>::evalBody(EvalSharedContext* evalsc)
+Parser<FullParseHandler, char16_t>::evalBody(EvalSharedContext* evalsc)
 {
     ParseContext evalpc(this, evalsc, /* newDirectives = */ nullptr);
     if (!evalpc.init())
         return nullptr;
 
     ParseContext::VarScope varScope(this);
     if (!varScope.init(pc))
         return nullptr;
@@ -2167,17 +2164,17 @@ Parser<FullParseHandler>::evalBody(EvalS
         return nullptr;
     evalsc->bindings = *bindings;
 
     return body;
 }
 
 template <>
 ParseNode*
-Parser<FullParseHandler>::globalBody(GlobalSharedContext* globalsc)
+Parser<FullParseHandler, char16_t>::globalBody(GlobalSharedContext* globalsc)
 {
     ParseContext globalpc(this, globalsc, /* newDirectives = */ nullptr);
     if (!globalpc.init())
         return nullptr;
 
     ParseContext::VarScope varScope(this);
     if (!varScope.init(pc))
         return nullptr;
@@ -2197,33 +2194,33 @@ Parser<FullParseHandler>::globalBody(Glo
         return nullptr;
     globalsc->bindings = *bindings;
 
     return body;
 }
 
 template <>
 ParseNode*
-Parser<FullParseHandler>::moduleBody(ModuleSharedContext* modulesc)
+Parser<FullParseHandler, char16_t>::moduleBody(ModuleSharedContext* modulesc)
 {
     MOZ_ASSERT(checkOptionsCalled);
 
     ParseContext modulepc(this, modulesc, nullptr);
     if (!modulepc.init())
         return null();
 
     ParseContext::VarScope varScope(this);
     if (!varScope.init(pc))
         return nullptr;
 
     Node mn = handler.newModule(pos());
     if (!mn)
         return null();
 
-    AutoAwaitIsKeyword<FullParseHandler> awaitIsKeyword(this, true);
+    AutoAwaitIsKeyword<Parser> awaitIsKeyword(this, true);
     ParseNode* pn = statementList(YieldIsKeyword);
     if (!pn)
         return null();
 
     MOZ_ASSERT(pn->isKind(PNK_STATEMENTLIST));
     mn->pn_body = pn;
 
     TokenKind tt;
@@ -2266,34 +2263,34 @@ Parser<FullParseHandler>::moduleBody(Mod
     if (!bindings)
         return nullptr;
 
     modulesc->bindings = *bindings;
     return mn;
 }
 
 template <>
-SyntaxParseHandler::Node
-Parser<SyntaxParseHandler>::moduleBody(ModuleSharedContext* modulesc)
+SyntaxParseHandlerBase::Node
+Parser<SyntaxParseHandler, char16_t>::moduleBody(ModuleSharedContext* modulesc)
 {
     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
-    return SyntaxParseHandler::NodeFailure;
-}
-
-template <typename ParseHandler>
+    return SyntaxParseHandlerBase::NodeFailure;
+}
+
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::hasUsedFunctionSpecialName(HandlePropertyName name)
+Parser<ParseHandler, CharT>::hasUsedFunctionSpecialName(HandlePropertyName name)
 {
     MOZ_ASSERT(name == context->names().arguments || name == context->names().dotThis);
     return hasUsedName(name) || pc->functionBox()->bindingsAccessedDynamically();
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::declareFunctionThis()
+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;
 
     // Derived class constructors emit JSOP_CHECKRETURN, which requires
     // '.this' to be bound.
@@ -2316,62 +2313,62 @@ Parser<ParseHandler>::declareFunctionThi
             return false;
         }
         funbox->setHasThisBinding();
     }
 
     return true;
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::newInternalDotName(HandlePropertyName name)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::newInternalDotName(HandlePropertyName name)
 {
     Node nameNode = newName(name);
     if (!nameNode)
         return null();
     if (!noteUsedName(name))
         return null();
     return nameNode;
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::newThisName()
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::newThisName()
 {
     return newInternalDotName(context->names().dotThis);
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::newDotGeneratorName()
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::newDotGeneratorName()
 {
     return newInternalDotName(context->names().dotGenerator);
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::declareDotGeneratorName()
+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 <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::finishFunctionScopes(bool isStandaloneFunction)
+Parser<ParseHandler, CharT>::finishFunctionScopes(bool isStandaloneFunction)
 {
     FunctionBox* funbox = pc->functionBox();
 
     if (funbox->hasParameterExprs) {
         if (!propagateFreeNamesAndMarkClosedOverBindings(pc->functionScope()))
             return false;
     }
 
@@ -2380,17 +2377,17 @@ Parser<ParseHandler>::finishFunctionScop
             return false;
     }
 
     return true;
 }
 
 template <>
 bool
-Parser<FullParseHandler>::finishFunction(bool isStandaloneFunction /* = false */)
+Parser<FullParseHandler, char16_t>::finishFunction(bool isStandaloneFunction /* = false */)
 {
     if (!finishFunctionScopes(isStandaloneFunction))
         return false;
 
     FunctionBox* funbox = pc->functionBox();
     bool hasParameterExprs = funbox->hasParameterExprs;
 
     if (hasParameterExprs) {
@@ -2415,17 +2412,17 @@ Parser<FullParseHandler>::finishFunction
         funbox->namedLambdaBindings().set(*bindings);
     }
 
     return true;
 }
 
 template <>
 bool
-Parser<SyntaxParseHandler>::finishFunction(bool isStandaloneFunction /* = false */)
+Parser<SyntaxParseHandler, char16_t>::finishFunction(bool isStandaloneFunction /* = false */)
 {
     // The LazyScript for a lazily parsed function needs to know its set of
     // free variables and inner functions so that when it is fully parsed, we
     // can skip over any already syntax parsed inner functions and still
     // retain correct scope information.
 
     if (!finishFunctionScopes(isStandaloneFunction))
         return false;
@@ -2483,23 +2480,23 @@ GetYieldHandling(GeneratorKind generator
 {
     if (generatorKind == NotGenerator)
         return YieldIsName;
     return YieldIsKeyword;
 }
 
 template <>
 ParseNode*
-Parser<FullParseHandler>::standaloneFunction(HandleFunction fun,
-                                             HandleScope enclosingScope,
-                                             const Maybe<uint32_t>& parameterListEnd,
-                                             GeneratorKind generatorKind,
-                                             FunctionAsyncKind asyncKind,
-                                             Directives inheritedDirectives,
-                                             Directives* newDirectives)
+Parser<FullParseHandler, char16_t>::standaloneFunction(HandleFunction fun,
+                                                       HandleScope enclosingScope,
+                                                       const Maybe<uint32_t>& parameterListEnd,
+                                                       GeneratorKind generatorKind,
+                                                       FunctionAsyncKind asyncKind,
+                                                       Directives inheritedDirectives,
+                                                       Directives* newDirectives)
 {
     MOZ_ASSERT(checkOptionsCalled);
 
     // Skip prelude.
     TokenKind tt;
     if (!tokenStream.getToken(&tt))
         return null();
     if (asyncKind == AsyncFunction) {
@@ -2541,17 +2538,17 @@ Parser<FullParseHandler>::standaloneFunc
     funbox->initStandaloneFunction(enclosingScope);
 
     ParseContext funpc(this, funbox, newDirectives);
     if (!funpc.init())
         return null();
     funpc.setIsStandaloneFunctionBody();
 
     YieldHandling yieldHandling = GetYieldHandling(generatorKind);
-    AutoAwaitIsKeyword<FullParseHandler> awaitIsKeyword(this, asyncKind == AsyncFunction);
+    AutoAwaitIsKeyword<Parser> awaitIsKeyword(this, asyncKind == AsyncFunction);
     if (!functionFormalParametersAndBody(InAllowed, yieldHandling, fn, Statement,
                                          parameterListEnd, /* isStandaloneFunction = */ true))
     {
         return null();
     }
 
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
@@ -2561,19 +2558,19 @@ Parser<FullParseHandler>::standaloneFunc
     }
 
     if (!FoldConstants(context, &fn, this))
         return null();
 
     return fn;
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::declareFunctionArgumentsObject()
+Parser<ParseHandler, CharT>::declareFunctionArgumentsObject()
 {
     FunctionBox* funbox = pc->functionBox();
     ParseContext::Scope& funScope = pc->functionScope();
     ParseContext::Scope& varScope = pc->varScope();
 
     bool hasExtraBodyVarScope = &funScope != &varScope;
 
     // Time to implement the odd semantics of 'arguments'.
@@ -2638,20 +2635,20 @@ Parser<ParseHandler>::declareFunctionArg
         // eagerly so the Debugger API may observe bindings.
         if (pc->sc()->hasDebuggerStatement())
             funbox->setDefinitelyNeedsArgsObj();
     }
 
     return true;
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::functionBody(InHandling inHandling, YieldHandling yieldHandling,
-                                   FunctionSyntaxKind kind, FunctionBodyType type)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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;
 #endif
 
@@ -2825,19 +2822,19 @@ ParserBase::newFunction(HandleAtom atom,
     return fun;
 }
 
 /*
  * WARNING: Do not call this function directly.
  * Call either matchOrInsertSemicolonAfterExpression or
  * matchOrInsertSemicolonAfterNonExpression instead, depending on context.
  */
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::matchOrInsertSemicolonHelper(TokenStream::Modifier modifier)
+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) {
         /*
          * When current token is `await` and it's outside of async function,
          * it's possibly intended to be an await expression.
@@ -2863,33 +2860,33 @@ Parser<ParseHandler>::matchOrInsertSemic
     bool matched;
     if (!tokenStream.matchToken(&matched, TOK_SEMI, modifier))
         return false;
     if (!matched && modifier == TokenStream::None)
         tokenStream.addModifierException(TokenStream::OperandIsNone);
     return true;
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::matchOrInsertSemicolonAfterExpression()
+Parser<ParseHandler, CharT>::matchOrInsertSemicolonAfterExpression()
 {
     return matchOrInsertSemicolonHelper(TokenStream::None);
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::matchOrInsertSemicolonAfterNonExpression()
+Parser<ParseHandler, CharT>::matchOrInsertSemicolonAfterNonExpression()
 {
     return matchOrInsertSemicolonHelper(TokenStream::Operand);
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::leaveInnerFunction(ParseContext* outerpc)
+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.
     if (pc->superScopeNeedsHomeObject()) {
         if (!pc->isArrowFunction())
@@ -2908,39 +2905,40 @@ Parser<ParseHandler>::leaveInnerFunction
     if (!outerpc->innerFunctionsForLazy.append(pc->functionBox()->function()))
         return false;
 
     PropagateTransitiveParseFlags(pc->functionBox(), outerpc->sc());
 
     return true;
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 JSAtom*
-Parser<ParseHandler>::prefixAccessorName(PropertyType propType, HandleAtom propAtom)
+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);
         prefix = context->names().getPrefix;
     }
 
     RootedString str(context, ConcatStrings<CanGC>(context, prefix, propAtom));
     if (!str)
         return nullptr;
 
     return AtomizeString(context, str);
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyntaxKind kind,
-                                        Node funcpn)
+Parser<ParseHandler, CharT>::functionArguments(YieldHandling yieldHandling,
+                                               FunctionSyntaxKind kind,
+                                               Node funcpn)
 {
     FunctionBox* funbox = pc->functionBox();
 
     bool parenFreeArrow = false;
     // Modifier for the following tokens.
     // TokenStream::None for the following cases:
     //   async a => 1
     //         ^
@@ -3191,18 +3189,18 @@ Parser<ParseHandler>::functionArguments(
         return false;
     }
 
     return true;
 }
 
 template <>
 bool
-Parser<FullParseHandler>::skipLazyInnerFunction(ParseNode* pn, uint32_t toStringStart,
-                                                FunctionSyntaxKind kind, bool tryAnnexB)
+Parser<FullParseHandler, char16_t>::skipLazyInnerFunction(ParseNode* pn, uint32_t toStringStart,
+                                                          FunctionSyntaxKind kind, bool tryAnnexB)
 {
     // When a lazily-parsed function is called, we only fully parse (and emit)
     // that function, not any of its nested children. The initial syntax-only
     // parse recorded the free variables of nested functions and their extents,
     // so we can skip over them after accounting for their free variables.
 
     RootedFunction fun(context, handler.nextLazyInnerFunction());
     MOZ_ASSERT(!fun->isLegacyGenerator());
@@ -3238,26 +3236,28 @@ Parser<FullParseHandler>::skipLazyInnerF
     }
 #endif
 
     return true;
 }
 
 template <>
 bool
-Parser<SyntaxParseHandler>::skipLazyInnerFunction(Node pn, uint32_t toStringStart,
-                                                  FunctionSyntaxKind kind, bool tryAnnexB)
+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 <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::addExprAndGetNextTemplStrToken(YieldHandling yieldHandling, Node nodeList,
-                                                     TokenKind* ttp)
+Parser<ParseHandler, CharT>::addExprAndGetNextTemplStrToken(YieldHandling yieldHandling,
+                                                            Node nodeList,
+                                                            TokenKind* ttp)
 {
     Node pn = expr(InAllowed, yieldHandling, TripledotProhibited);
     if (!pn)
         return false;
     handler.addList(nodeList, pn);
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt))
@@ -3265,19 +3265,20 @@ Parser<ParseHandler>::addExprAndGetNextT
     if (tt != TOK_RC) {
         error(JSMSG_TEMPLSTR_UNTERM_EXPR);
         return false;
     }
 
     return tokenStream.getToken(ttp, TokenStream::TemplateTail);
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::taggedTemplate(YieldHandling yieldHandling, Node nodeList, TokenKind tt)
+Parser<ParseHandler, CharT>::taggedTemplate(YieldHandling yieldHandling, Node nodeList,
+                                            TokenKind tt)
 {
     Node callSiteObjNode = handler.newCallSiteObject(pos().begin);
     if (!callSiteObjNode)
         return false;
     handler.addList(nodeList, callSiteObjNode);
 
     while (true) {
         if (!appendToCallSiteObj(callSiteObjNode))
@@ -3287,19 +3288,19 @@ Parser<ParseHandler>::taggedTemplate(Yie
 
         if (!addExprAndGetNextTemplStrToken(yieldHandling, nodeList, &tt))
             return false;
     }
     handler.setEndPosition(nodeList, callSiteObjNode);
     return true;
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::templateLiteral(YieldHandling yieldHandling)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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)
         return null();
@@ -3313,24 +3314,25 @@ Parser<ParseHandler>::templateLiteral(Yi
         if (!pn)
             return null();
 
         handler.addList(nodeList, pn);
     } while (tt == TOK_TEMPLATE_HEAD);
     return nodeList;
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::functionDefinition(Node pn, uint32_t toStringStart,
-                                         InHandling inHandling,
-                                         YieldHandling yieldHandling, HandleAtom funName,
-                                         FunctionSyntaxKind kind,
-                                         GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
-                                         bool tryAnnexB /* = false */)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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 */)
 {
     MOZ_ASSERT_IF(kind == Statement, funName);
 
     // When fully parsing a LazyScript, we do not fully reparse its inner
     // functions, which are also lazy. Instead, their free variables and
     // source extents are recorded and may be skipped.
     if (handler.canSkipLazyInnerFunctions()) {
         if (!skipLazyInnerFunction(pn, toStringStart, kind, tryAnnexB))
@@ -3389,37 +3391,37 @@ Parser<ParseHandler>::functionDefinition
         handler.setFunctionFormalParametersAndBody(pn, null());
     }
 
     return pn;
 }
 
 template <>
 bool
-Parser<FullParseHandler>::trySyntaxParseInnerFunction(ParseNode* pn, HandleFunction fun,
-                                                      uint32_t toStringStart,
-                                                      InHandling inHandling,
-                                                      YieldHandling yieldHandling,
-                                                      FunctionSyntaxKind kind,
-                                                      GeneratorKind generatorKind,
-                                                      FunctionAsyncKind asyncKind,
-                                                      bool tryAnnexB,
-                                                      Directives inheritedDirectives,
-                                                      Directives* newDirectives)
+Parser<FullParseHandler, char16_t>::trySyntaxParseInnerFunction(ParseNode* pn, HandleFunction fun,
+                                                                uint32_t toStringStart,
+                                                                InHandling inHandling,
+                                                                YieldHandling yieldHandling,
+                                                                FunctionSyntaxKind kind,
+                                                                GeneratorKind generatorKind,
+                                                                FunctionAsyncKind asyncKind,
+                                                                bool tryAnnexB,
+                                                                Directives inheritedDirectives,
+                                                                Directives* newDirectives)
 {
     // Try a syntax parse for this inner function.
     do {
         // If we're assuming this function is an IIFE, always perform a full
         // parse to avoid the overhead of a lazy syntax-only parse. Although
         // the prediction may be incorrect, IIFEs are common enough that it
         // pays off for lots of code.
         if (pn->isLikelyIIFE() && generatorKind == NotGenerator && asyncKind == SyncFunction)
             break;
 
-        Parser<SyntaxParseHandler>* parser = handler.syntaxParser;
+        Parser<SyntaxParseHandler, char16_t>* parser = handler.getSyntaxParser();
         if (!parser)
             break;
 
         UsedNameTracker::RewindToken token = usedNames.getRewindToken();
 
         // Move the syntax parser to the current position in the stream.
         TokenStream::Position position(keepAtoms);
         tokenStream.tell(&position);
@@ -3430,17 +3432,17 @@ Parser<FullParseHandler>::trySyntaxParse
         // 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, tryAnnexB);
         if (!funbox)
             return false;
         funbox->initWithEnclosingParseContext(pc, kind);
 
-        if (!parser->innerFunction(SyntaxParseHandler::NodeGeneric, pc, funbox, toStringStart,
+        if (!parser->innerFunction(SyntaxParseHandlerBase::NodeGeneric, pc, funbox, toStringStart,
                                    inHandling, yieldHandling, kind,
                                    inheritedDirectives, newDirectives))
         {
             if (parser->hadAbortedSyntaxParse()) {
                 // Try again with a full parse. UsedNameTracker needs to be
                 // rewound to just before we tried the syntax parse for
                 // correctness.
                 parser->clearAbortedSyntaxParse();
@@ -3464,39 +3466,39 @@ Parser<FullParseHandler>::trySyntaxParse
 
     // We failed to do a syntax parse above, so do the full parse.
     return innerFunction(pn, pc, fun, toStringStart, inHandling, yieldHandling, kind,
                          generatorKind, asyncKind, tryAnnexB, inheritedDirectives, newDirectives);
 }
 
 template <>
 bool
-Parser<SyntaxParseHandler>::trySyntaxParseInnerFunction(Node pn, HandleFunction fun,
-                                                        uint32_t toStringStart,
-                                                        InHandling inHandling,
-                                                        YieldHandling yieldHandling,
-                                                        FunctionSyntaxKind kind,
-                                                        GeneratorKind generatorKind,
-                                                        FunctionAsyncKind asyncKind,
-                                                        bool tryAnnexB,
-                                                        Directives inheritedDirectives,
-                                                        Directives* newDirectives)
+Parser<SyntaxParseHandler, char16_t>::trySyntaxParseInnerFunction(Node pn, HandleFunction fun,
+                                                                  uint32_t toStringStart,
+                                                                  InHandling inHandling,
+                                                                  YieldHandling yieldHandling,
+                                                                  FunctionSyntaxKind kind,
+                                                                  GeneratorKind generatorKind,
+                                                                  FunctionAsyncKind asyncKind,
+                                                                  bool tryAnnexB,
+                                                                  Directives inheritedDirectives,
+                                                                  Directives* newDirectives)
 {
     // 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 <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::innerFunction(Node pn, ParseContext* outerpc, FunctionBox* funbox,
-                                    uint32_t toStringStart,
-                                    InHandling inHandling, YieldHandling yieldHandling,
-                                    FunctionSyntaxKind kind, Directives inheritedDirectives,
-                                    Directives* newDirectives)
+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
     // attempting to syntax parse an inner function from an outer full
     // parser. In that case, outerpc is a ParseContext from the full parser
     // instead of the current top of the stack of the syntax parser.
 
     // Push a new ParseContext.
     ParseContext funpc(this, funbox, newDirectives);
@@ -3504,44 +3506,46 @@ Parser<ParseHandler>::innerFunction(Node
         return false;
 
     if (!functionFormalParametersAndBody(inHandling, yieldHandling, pn, kind))
         return false;
 
     return leaveInnerFunction(outerpc);
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::innerFunction(Node pn, ParseContext* outerpc, HandleFunction fun,
-                                    uint32_t toStringStart,
-                                    InHandling inHandling, YieldHandling yieldHandling,
-                                    FunctionSyntaxKind kind,
-                                    GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
-                                    bool tryAnnexB,
-                                    Directives inheritedDirectives, Directives* newDirectives)
+Parser<ParseHandler, CharT>::innerFunction(Node pn, ParseContext* outerpc, HandleFunction fun,
+                                           uint32_t toStringStart,
+                                           InHandling inHandling, YieldHandling yieldHandling,
+                                           FunctionSyntaxKind kind,
+                                           GeneratorKind generatorKind,
+                                           FunctionAsyncKind asyncKind,
+                                           bool tryAnnexB,
+                                           Directives inheritedDirectives,
+                                           Directives* newDirectives)
 {
     // Note that it is possible for outerpc != this->pc, as we may be
     // attempting to syntax parse an inner function from an outer full
     // parser. In that case, outerpc is a ParseContext from the full parser
     // instead of the current top of the stack of the syntax parser.
 
     FunctionBox* funbox = newFunctionBox(pn, fun, toStringStart, inheritedDirectives,
                                          generatorKind, asyncKind, tryAnnexB);
     if (!funbox)
         return false;
     funbox->initWithEnclosingParseContext(outerpc, kind);
 
     return innerFunction(pn, outerpc, funbox, toStringStart, inHandling, yieldHandling, kind,
                          inheritedDirectives, newDirectives);
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::appendToCallSiteObj(Node callSiteObj)
+Parser<ParseHandler, CharT>::appendToCallSiteObj(Node callSiteObj)
 {
     Node cookedNode = noSubstitutionTaggedTemplate();
     if (!cookedNode)
         return false;
 
     JSAtom* atom = tokenStream.getRawTemplateStringAtom();
     if (!atom)
         return false;
@@ -3550,19 +3554,19 @@ Parser<ParseHandler>::appendToCallSiteOb
         return false;
 
     handler.addToCallSiteObject(callSiteObj, rawNode, cookedNode);
     return true;
 }
 
 template <>
 ParseNode*
-Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, bool strict,
-                                                 GeneratorKind generatorKind,
-                                                 FunctionAsyncKind asyncKind)
+Parser<FullParseHandler, char16_t>::standaloneLazyFunction(HandleFunction fun, bool strict,
+                                                           GeneratorKind generatorKind,
+                                                           FunctionAsyncKind asyncKind)
 {
     MOZ_ASSERT(checkOptionsCalled);
 
     Node pn = handler.newFunctionStatement(pos());
     if (!pn)
         return null();
 
     Directives directives(strict);
@@ -3606,36 +3610,36 @@ Parser<FullParseHandler>::standaloneLazy
     }
 
     if (!FoldConstants(context, &pn, this))
         return null();
 
     return pn;
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling,
-                                                      YieldHandling yieldHandling,
-                                                      Node pn, FunctionSyntaxKind kind,
-                                                      const Maybe<uint32_t>& parameterListEnd /* = Nothing() */,
-                                                      bool isStandaloneFunction /* = false */)
+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
     // function without concern for conversion to strict mode, use of lazy
     // parsing and such.
 
     FunctionBox* funbox = pc->functionBox();
     RootedFunction fun(context, funbox->function());
 
     // See below for an explanation why arrow function parameters and arrow
     // function bodies are parsed with different yield/await settings.
     {
         bool asyncOrArrowInAsync = funbox->isAsync() || (kind == Arrow && awaitIsKeyword());
-        AutoAwaitIsKeyword<ParseHandler> awaitIsKeyword(this, asyncOrArrowInAsync);
+        AutoAwaitIsKeyword<Parser> awaitIsKeyword(this, asyncOrArrowInAsync);
         if (!functionArguments(yieldHandling, kind, pn))
             return false;
     }
 
     Maybe<ParseContext::VarScope> varScope;
     if (funbox->hasParameterExprs) {
         varScope.emplace(this);
         if (!varScope->init(pc))
@@ -3698,17 +3702,17 @@ Parser<ParseHandler>::functionFormalPara
     // context, but the arrow body doesn't. E.g. in |(a = yield) => yield|,
     // |yield| in the parameters is either a name or keyword, depending on
     // whether the arrow function is enclosed in a generator function or not.
     // Whereas the |yield| in the function body is always parsed as a name.
     // The same goes when parsing |await| in arrow functions.
     YieldHandling bodyYieldHandling = GetYieldHandling(pc->generatorKind());
     Node body;
     {
-        AutoAwaitIsKeyword<ParseHandler> awaitIsKeyword(this, funbox->isAsync());
+        AutoAwaitIsKeyword<Parser> awaitIsKeyword(this, funbox->isAsync());
         body = functionBody(inHandling, bodyYieldHandling, kind, bodyType);
         if (!body)
             return false;
     }
 
     if ((kind == Statement || kind == Expression) && fun->explicitName()) {
         RootedPropertyName propertyName(context, fun->explicitName()->asPropertyName());
         YieldHandling nameYieldHandling;
@@ -3754,20 +3758,21 @@ Parser<ParseHandler>::functionFormalPara
 
     handler.setEndPosition(body, pos().begin);
     handler.setEndPosition(pn, pos().end);
     handler.setFunctionBody(pn, body);
 
     return true;
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::functionStmt(uint32_t toStringStart, YieldHandling yieldHandling,
-                                   DefaultHandling defaultHandling, FunctionAsyncKind asyncKind)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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.
     ParseContext::Statement* declaredInStmt = pc->innermostStatement();
     if (declaredInStmt && declaredInStmt->kind() == StatementKind::Label) {
         MOZ_ASSERT(!pc->sc()->strict(),
@@ -3846,24 +3851,24 @@ Parser<ParseHandler>::functionStmt(uint3
     if (!pn)
         return null();
 
     YieldHandling newYieldHandling = GetYieldHandling(generatorKind);
     return functionDefinition(pn, toStringStart, InAllowed, newYieldHandling, name, Statement,
                               generatorKind, asyncKind, tryAnnexB);
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::functionExpr(uint32_t toStringStart, InvokedPrediction invoked,
-                                   FunctionAsyncKind asyncKind)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::functionExpr(uint32_t toStringStart, InvokedPrediction invoked,
+                                          FunctionAsyncKind asyncKind)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
 
-    AutoAwaitIsKeyword<ParseHandler> awaitIsKeyword(this, asyncKind == AsyncFunction);
+    AutoAwaitIsKeyword<Parser> awaitIsKeyword(this, asyncKind == AsyncFunction);
     GeneratorKind generatorKind = NotGenerator;
     TokenKind tt;
     if (!tokenStream.getToken(&tt))
         return null();
 
     if (tt == TOK_MUL) {
         if (!asyncIterationSupported()) {
             if (asyncKind != SyncFunction) {
@@ -3913,31 +3918,31 @@ IsEscapeFreeStringLiteral(const TokenPos
      * accounting for the quotes, then it must not contain any escape
      * sequences or line continuations.
      */
     return pos.begin + str->length() + 2 == pos.end;
 }
 
 template <>
 bool
-Parser<SyntaxParseHandler>::asmJS(Node list)
+Parser<SyntaxParseHandler, char16_t>::asmJS(Node list)
 {
     // While asm.js could technically be validated and compiled during syntax
     // parsing, we have no guarantee that some later JS wouldn't abort the
     // syntax parse and cause us to re-parse (and re-compile) the asm.js module.
     // For simplicity, unconditionally abort the syntax parse when "use asm" is
     // encountered so that asm.js is always validated/compiled exactly once
     // during a full parse.
     JS_ALWAYS_FALSE(abortIfSyntaxParser());
     return false;
 }
 
 template <>
 bool
-Parser<FullParseHandler>::asmJS(Node list)
+Parser<FullParseHandler, char16_t>::asmJS(Node list)
 {
     // Disable syntax parsing in anything nested inside the asm.js module.
     handler.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.
@@ -3981,19 +3986,19 @@ Parser<FullParseHandler>::asmJS(Node lis
  *   "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 <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::maybeParseDirective(Node list, Node possibleDirective, bool* cont)
+Parser<ParseHandler, CharT>::maybeParseDirective(Node list, Node possibleDirective, bool* cont)
 {
     TokenPos directivePos;
     JSAtom* directive = handler.isStringExprStatement(possibleDirective, &directivePos);
 
     *cont = !!directive;
     if (!*cont)
         return true;
 
@@ -4044,19 +4049,19 @@ Parser<ParseHandler>::maybeParseDirectiv
             if (pc->isFunctionBox())
                 return asmJS(list);
             return warningAt(directivePos.begin, JSMSG_USE_ASM_DIRECTIVE_FAIL);
         }
     }
     return true;
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::statementList(YieldHandling yieldHandling)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::statementList(YieldHandling yieldHandling)
 {
     if (!CheckRecursionLimit(context))
         return null();
 
     Node pn = handler.newStatementList(pos());
     if (!pn)
         return null();
 
@@ -4104,37 +4109,38 @@ Parser<ParseHandler>::statementList(Yiel
         }
 
         handler.addStatementToList(pn, next);
     }
 
     return pn;
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::condition(InHandling inHandling, YieldHandling yieldHandling)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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 <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::matchLabel(YieldHandling yieldHandling, MutableHandle<PropertyName*> label)
+Parser<ParseHandler, CharT>::matchLabel(YieldHandling yieldHandling,
+                                        MutableHandle<PropertyName*> label)
 {
     TokenKind tt = TOK_EOF;
     if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
         return false;
 
     if (TokenKindIsPossibleIdentifier(tt)) {
         tokenStream.consumeKnownToken(tt, TokenStream::Operand);
 
@@ -4142,144 +4148,144 @@ Parser<ParseHandler>::matchLabel(YieldHa
         if (!label)
             return false;
     } else {
         label.set(nullptr);
     }
     return true;
 }
 
-template <typename ParseHandler>
-Parser<ParseHandler>::PossibleError::PossibleError(Parser<ParseHandler>& parser)
+template <template <typename CharT> class ParseHandler, typename CharT>
+Parser<ParseHandler, CharT>::PossibleError::PossibleError(Parser<ParseHandler, CharT>& parser)
   : parser_(parser)
 {}
 
-template <typename ParseHandler>
-typename Parser<ParseHandler>::PossibleError::Error&
-Parser<ParseHandler>::PossibleError::error(ErrorKind kind)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename Parser<ParseHandler, CharT>::PossibleError::Error&
+Parser<ParseHandler, CharT>::PossibleError::error(ErrorKind kind)
 {
     if (kind == ErrorKind::Expression)
         return exprError_;
     MOZ_ASSERT(kind == ErrorKind::Destructuring);
     return destructuringError_;
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 void
-Parser<ParseHandler>::PossibleError::setResolved(ErrorKind kind)
+Parser<ParseHandler, CharT>::PossibleError::setResolved(ErrorKind kind)
 {
     error(kind).state_ = ErrorState::None;
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::PossibleError::hasError(ErrorKind kind)
+Parser<ParseHandler, CharT>::PossibleError::hasError(ErrorKind kind)
 {
     return error(kind).state_ == ErrorState::Pending;
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 void
-Parser<ParseHandler>::PossibleError::setPending(ErrorKind kind, const TokenPos& pos,
-                                                unsigned errorNumber)
+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 <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 void
-Parser<ParseHandler>::PossibleError::setPendingDestructuringErrorAt(const TokenPos& pos,
-                                                                    unsigned errorNumber)
+Parser<ParseHandler, CharT>::PossibleError::setPendingDestructuringErrorAt(const TokenPos& pos,
+                                                                           unsigned errorNumber)
 {
     setPending(ErrorKind::Destructuring, pos, errorNumber);
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 void
-Parser<ParseHandler>::PossibleError::setPendingExpressionErrorAt(const TokenPos& pos,
-                                                                 unsigned errorNumber)
+Parser<ParseHandler, CharT>::PossibleError::setPendingExpressionErrorAt(const TokenPos& pos,
+                                                                        unsigned errorNumber)
 {
     setPending(ErrorKind::Expression, pos, errorNumber);
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::PossibleError::checkForError(ErrorKind kind)
+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 <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::PossibleError::checkForDestructuringError()
+Parser<ParseHandler, CharT>::PossibleError::checkForDestructuringError()
 {
     // Clear pending expression error, because we're definitely not in an
     // expression context.
     setResolved(ErrorKind::Expression);
 
     // Report any pending destructuring error.
     return checkForError(ErrorKind::Destructuring);
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::PossibleError::checkForExpressionError()
+Parser<ParseHandler, CharT>::PossibleError::checkForExpressionError()
 {
     // Clear pending destructuring error, because we're definitely not in a
     // destructuring context.
     setResolved(ErrorKind::Destructuring);
 
     // Report any pending expression error.
     return checkForError(ErrorKind::Expression);
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 void
-Parser<ParseHandler>::PossibleError::transferErrorTo(ErrorKind kind, PossibleError* other)
+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 <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 void
-Parser<ParseHandler>::PossibleError::transferErrorsTo(PossibleError* other)
+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 <>
 bool
-Parser<FullParseHandler>::checkDestructuringName(ParseNode* expr,
-                                                 const Maybe<DeclarationKind>& maybeDecl)
+Parser<FullParseHandler, char16_t>::checkDestructuringName(ParseNode* expr,
+                                                           const Maybe<DeclarationKind>& maybeDecl)
 {
     MOZ_ASSERT(!handler.isUnparenthesizedDestructuringPattern(expr));
 
     // Parentheses are forbidden around destructuring *patterns* (but allowed
     // around names).  Use our nicer error message for parenthesized, nested
     // patterns.
     if (handler.isParenthesizedDestructuringPattern(expr)) {
         errorAt(expr->pn_pos.begin, JSMSG_BAD_DESTRUCT_PARENS);
@@ -4317,24 +4323,33 @@ Parser<FullParseHandler>::checkDestructu
         return true;
 
     errorAt(expr->pn_pos.begin, JSMSG_BAD_DESTRUCT_TARGET);
     return false;
 }
 
 template <>
 bool
-Parser<FullParseHandler>::checkDestructuringPattern(ParseNode* pattern,
-                                                    const Maybe<DeclarationKind>& maybeDecl,
-                                                    PossibleError* possibleError /* = nullptr */);
+Parser<FullParseHandler, char16_t>::checkDestructuringPattern(ParseNode* pattern,
+                                                              const Maybe<DeclarationKind>& maybeDecl,
+                                                              PossibleError* possibleError /* = nullptr */);
 
 template <>
 bool
-Parser<FullParseHandler>::checkDestructuringObject(ParseNode* objectPattern,
-                                                   const Maybe<DeclarationKind>& maybeDecl)
+Parser<SyntaxParseHandler, char16_t>::checkDestructuringPattern(Node pattern,
+                                                                const Maybe<DeclarationKind>& maybeDecl,
+                                                                PossibleError* possibleError /* = nullptr */)
+{
+    return abortIfSyntaxParser();
+}
+
+template <>
+bool
+Parser<FullParseHandler, char16_t>::checkDestructuringObject(ParseNode* objectPattern,
+                                                             const Maybe<DeclarationKind>& maybeDecl)
 {
     MOZ_ASSERT(objectPattern->isKind(PNK_OBJECT));
 
     for (ParseNode* member = objectPattern->pn_head; member; member = member->pn_next) {
         ParseNode* target;
         if (member->isKind(PNK_MUTATEPROTO)) {
             target = member->pn_kid;
         } else {
@@ -4358,18 +4373,18 @@ Parser<FullParseHandler>::checkDestructu
         }
     }
 
     return true;
 }
 
 template <>
 bool
-Parser<FullParseHandler>::checkDestructuringArray(ParseNode* arrayPattern,
-                                                  const Maybe<DeclarationKind>& maybeDecl)
+Parser<FullParseHandler, char16_t>::checkDestructuringArray(ParseNode* arrayPattern,
+                                                            const Maybe<DeclarationKind>& maybeDecl)
 {
     MOZ_ASSERT(arrayPattern->isKind(PNK_ARRAY));
 
     for (ParseNode* element = arrayPattern->pn_head; element; element = element->pn_next) {
         if (element->isKind(PNK_ELISION))
             continue;
 
         ParseNode* target;
@@ -4381,17 +4396,17 @@ Parser<FullParseHandler>::checkDestructu
             target = element->pn_kid;
         } else if (handler.isUnparenthesizedAssignment(element)) {
             target = element->pn_left;
         } else {
             target = element;
         }
 
         if (handler.isUnparenthesizedDestructuringPattern(target)) {
-            if (!checkDestructuringPattern(target, maybeDecl))
+            if (!this->checkDestructuringPattern(target, maybeDecl))
                 return false;
         } else {
             if (!checkDestructuringName(target, maybeDecl))
                 return false;
         }
     }
 
     return true;
@@ -4428,19 +4443,19 @@ Parser<FullParseHandler>::checkDestructu
  * pc->inDestructuringDecl set, which directs primaryExpr to leave
  * whatever name nodes it creates unconnected.  Then, here in
  * checkDestructuringPattern, we require the pattern's property value
  * positions to be simple names, and define them as appropriate to the
  * context.
  */
 template <>
 bool
-Parser<FullParseHandler>::checkDestructuringPattern(ParseNode* pattern,
-                                                    const Maybe<DeclarationKind>& maybeDecl,
-                                                    PossibleError* possibleError /* = nullptr */)
+Parser<FullParseHandler, char16_t>::checkDestructuringPattern(ParseNode* pattern,
+                                                              const Maybe<DeclarationKind>& maybeDecl,
+                                                              PossibleError* possibleError /* = nullptr */)
 {
     if (pattern->isKind(PNK_ARRAYCOMP)) {
         errorAt(pattern->pn_pos.begin, JSMSG_ARRAY_COMP_LEFTSIDE);
         return false;
     }
 
     bool isDestructuring = pattern->isKind(PNK_ARRAY)
                            ? checkDestructuringArray(pattern, maybeDecl)
@@ -4448,29 +4463,21 @@ Parser<FullParseHandler>::checkDestructu
 
     // Report any pending destructuring error.
     if (isDestructuring && possibleError && !possibleError->checkForDestructuringError())
         return false;
 
     return isDestructuring;
 }
 
-template <>
-bool
-Parser<SyntaxParseHandler>::checkDestructuringPattern(Node pattern,
-                                                      const Maybe<DeclarationKind>& maybeDecl,
-                                                      PossibleError* possibleError /* = nullptr */)
-{
-    return abortIfSyntaxParser();
-}
-
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::destructuringDeclaration(DeclarationKind kind, YieldHandling yieldHandling,
-                                               TokenKind tt)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::destructuringDeclaration(DeclarationKind kind,
+                                                      YieldHandling yieldHandling,
+                                                      TokenKind tt)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(tt));
     MOZ_ASSERT(tt == TOK_LB || tt == TOK_LC);
 
     PossibleError possibleError(*this);
     Node pattern;
     {
         pc->inDestructuringDecl = Some(kind);
@@ -4479,21 +4486,21 @@ Parser<ParseHandler>::destructuringDecla
     }
 
     if (!pattern || !checkDestructuringPattern(pattern, Some(kind), &possibleError))
         return null();
 
     return pattern;
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::destructuringDeclarationWithoutYieldOrAwait(DeclarationKind kind,
-                                                                  YieldHandling yieldHandling,
-                                                                  TokenKind tt)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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) {
         if (pc->lastYieldOffset != startYieldOffset) {
             errorAt(pc->lastYieldOffset, JSMSG_YIELD_IN_DEFAULT);
             return null();
@@ -4501,19 +4508,19 @@ Parser<ParseHandler>::destructuringDecla
         if (pc->lastAwaitOffset != startAwaitOffset) {
             errorAt(pc->lastAwaitOffset, JSMSG_AWAIT_IN_DEFAULT);
             return null();
         }
     }
     return res;
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::blockStatement(YieldHandling yieldHandling, unsigned errorNumber)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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))
         return null();
@@ -4524,33 +4531,35 @@ Parser<ParseHandler>::blockStatement(Yie
 
     MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand,
                                      reportMissingClosing(errorNumber, JSMSG_CURLY_OPENED,
                                                           openedPos));
 
     return finishLexicalScope(scope, list);
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::expressionAfterForInOrOf(ParseNodeKind forHeadKind,
-                                               YieldHandling yieldHandling)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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 <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::declarationPattern(Node decl, DeclarationKind declKind, TokenKind tt,
-                                         bool initialDeclaration, YieldHandling yieldHandling,
-                                         ParseNodeKind* forHeadKind, Node* forInOrOfExpression)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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));
 
     Node pattern = destructuringDeclaration(declKind, yieldHandling, tt);
     if (!pattern)
         return null();
 
@@ -4596,25 +4605,25 @@ Parser<ParseHandler>::declarationPattern
         // For all other declarations, through ASI's infinite majesty, a next
         // token on a new line would begin an expression.
         tokenStream.addModifierException(TokenStream::OperandIsNone);
     }
 
     return handler.newBinary(PNK_ASSIGN, pattern, init);
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::initializerInNameDeclaration(Node decl, Node binding,
-                                                   Handle<PropertyName*> name,
-                                                   DeclarationKind declKind,
-                                                   bool initialDeclaration,
-                                                   YieldHandling yieldHandling,
-                                                   ParseNodeKind* forHeadKind,
-                                                   Node* forInOrOfExpression)
+Parser<ParseHandler, CharT>::initializerInNameDeclaration(Node decl, Node binding,
+                                                          Handle<PropertyName*> name,
+                                                          DeclarationKind declKind,
+                                                          bool initialDeclaration,
+                                                          YieldHandling yieldHandling,
+                                                          ParseNodeKind* forHeadKind,
+                                                          Node* forInOrOfExpression)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_ASSIGN));
 
     uint32_t initializerOffset;
     if (!tokenStream.peekOffset(&initializerOffset, TokenStream::Operand))
         return false;
 
     Node initializer = assignExpr(forHeadKind ? InProhibited : InAllowed,
@@ -4683,21 +4692,21 @@ Parser<ParseHandler>::initializerInNameD
             // so we need an exception.
             tokenStream.addModifierException(TokenStream::OperandIsNone);
         }
     }
 
     return handler.finishInitializerAssignment(binding, initializer);
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::declarationName(Node decl, DeclarationKind declKind, TokenKind tt,
-                                      bool initialDeclaration, YieldHandling yieldHandling,
-                                      ParseNodeKind* forHeadKind, Node* forInOrOfExpression)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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();
     }
 
     RootedPropertyName name(context, bindingIdentifier(yieldHandling));
@@ -4765,22 +4774,22 @@ Parser<ParseHandler>::declarationName(No
     // 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 <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::declarationList(YieldHandling yieldHandling,
-                                      ParseNodeKind kind,
-                                      ParseNodeKind* forHeadKind /* = nullptr */,
-                                      Node* forInOrOfExpression /* = nullptr */)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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;
     DeclarationKind declKind;
     switch (kind) {
       case PNK_VAR:
         op = JSOP_DEFVAR;
@@ -4829,19 +4838,19 @@ Parser<ParseHandler>::declarationList(Yi
 
         if (!tokenStream.matchToken(&matched, TOK_COMMA))
             return null();
     } while (matched);
 
     return decl;
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::lexicalDeclaration(YieldHandling yieldHandling, DeclarationKind kind)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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
      * the same environment record as vars.
@@ -4856,17 +4865,17 @@ Parser<ParseHandler>::lexicalDeclaration
     if (!decl || !matchOrInsertSemicolonAfterExpression())
         return null();
 
     return decl;
 }
 
 template <>
 bool
-Parser<FullParseHandler>::namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet)
+Parser<FullParseHandler, char16_t>::namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet)
 {
     if (tt == TOK_LC) {
         while (true) {
             // Handle the forms |import {} from 'a'| and
             // |import { ..., } from 'a'| (where ... is non empty), by
             // escaping the loop early if the next token is }.
             if (!tokenStream.getToken(&tt))
                 return false;
@@ -4973,25 +4982,26 @@ Parser<FullParseHandler>::namedImportsOr
         handler.addList(importSpecSet, importSpec);
     }
 
     return true;
 }
 
 template<>
 bool
-Parser<SyntaxParseHandler>::namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet)
+Parser<SyntaxParseHandler, char16_t>::namedImportsOrNamespaceImport(TokenKind tt,
+                                                                    Node importSpecSet)
 {
     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
     return false;
 }
 
 template<>
 ParseNode*
-Parser<FullParseHandler>::importDeclaration()
+Parser<FullParseHandler, char16_t>::importDeclaration()
 {
     MOZ_ASSERT(tokenStream.currentToken().type == TOK_IMPORT);
 
     if (!pc->atModuleLevel()) {
         error(JSMSG_IMPORT_DECL_AT_TOP_LEVEL);
         return null();
     }
 
@@ -5075,150 +5085,150 @@ Parser<FullParseHandler>::importDeclarat
         handler.newImportDeclaration(importSpecSet, moduleSpec, TokenPos(begin, pos().end));
     if (!node || !pc->sc()->asModuleContext()->builder.processImport(node))
         return null();
 
     return node;
 }
 
 template<>
-SyntaxParseHandler::Node
-Parser<SyntaxParseHandler>::importDeclaration()
+SyntaxParseHandlerBase::Node
+Parser<SyntaxParseHandler, char16_t>::importDeclaration()
 {
     JS_ALWAYS_FALSE(abortIfSyntaxParser());
-    return SyntaxParseHandler::NodeFailure;
+    return SyntaxParseHandlerBase::NodeFailure;
 }
 
 template<>
 bool
-Parser<FullParseHandler>::checkExportedName(JSAtom* exportName)
+Parser<FullParseHandler, char16_t>::checkExportedName(JSAtom* exportName)
 {
     if (!pc->sc()->asModuleContext()->builder.hasExportedName(exportName))
         return true;
 
     JSAutoByteString str;
     if (!AtomToPrintableString(context, exportName, &str))
         return false;
 
     error(JSMSG_DUPLICATE_EXPORT_NAME, str.ptr());
     return false;
 }
 
 template<>
 bool
-Parser<SyntaxParseHandler>::checkExportedName(JSAtom* exportName)
+Parser<SyntaxParseHandler, char16_t>::checkExportedName(JSAtom* exportName)
 {
     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
     return false;
 }
 
 template<>
 bool
-Parser<FullParseHandler>::checkExportedNamesForDeclaration(ParseNode* node)
+Parser<FullParseHandler, char16_t>::checkExportedNamesForDeclaration(ParseNode* node)
 {
     MOZ_ASSERT(node->isArity(PN_LIST));
     for (ParseNode* binding = node->pn_head; binding; binding = binding->pn_next) {
         if (binding->isKind(PNK_ASSIGN))
             binding = binding->pn_left;
         MOZ_ASSERT(binding->isKind(PNK_NAME));
         if (!checkExportedName(binding->pn_atom))
             return false;
     }
 
     return true;
 }
 
 template<>
 bool
-Parser<SyntaxParseHandler>::checkExportedNamesForDeclaration(Node node)
+Parser<SyntaxParseHandler, char16_t>::checkExportedNamesForDeclaration(Node node)
 {
     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
     return false;
 }
 
 template<>
 bool
-Parser<FullParseHandler>::checkExportedNameForClause(ParseNode* node)
+Parser<FullParseHandler, char16_t>::checkExportedNameForClause(ParseNode* node)
 {
     return checkExportedName(node->pn_atom);
 }
 
 template<>
 bool
-Parser<SyntaxParseHandler>::checkExportedNameForClause(Node node)
+Parser<SyntaxParseHandler, char16_t>::checkExportedNameForClause(Node node)
 {
     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
     return false;
 }
 
 template<>
 bool
-Parser<FullParseHandler>::checkExportedNameForFunction(ParseNode* node)
+Parser<FullParseHandler, char16_t>::checkExportedNameForFunction(ParseNode* node)
 {
     return checkExportedName(node->pn_funbox->function()->explicitName());
 }
 
 template<>
 bool
-Parser<SyntaxParseHandler>::checkExportedNameForFunction(Node node)
+Parser<SyntaxParseHandler, char16_t>::checkExportedNameForFunction(Node node)
 {
     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
     return false;
 }
 
 template<>
 bool
-Parser<FullParseHandler>::checkExportedNameForClass(ParseNode* node)
+Parser<FullParseHandler, char16_t>::checkExportedNameForClass(ParseNode* node)
 {
     const ClassNode& cls = node->as<ClassNode>();
     MOZ_ASSERT(cls.names());
     return checkExportedName(cls.names()->innerBinding()->pn_atom);
 }
 
 template<>
 bool
-Parser<SyntaxParseHandler>::checkExportedNameForClass(Node node)
+Parser<SyntaxParseHandler, char16_t>::checkExportedNameForClass(Node node)
 {
     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
     return false;
 }
 
 template<>
 bool
-Parser<FullParseHandler>::processExport(ParseNode* node)
+Parser<FullParseHandler, char16_t>::processExport(ParseNode* node)
 {
     return pc->sc()->asModuleContext()->builder.processExport(node);
 }
 
 template<>
 bool
-Parser<SyntaxParseHandler>::processExport(Node node)
+Parser<SyntaxParseHandler, char16_t>::processExport(Node node)
 {
     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
     return false;
 }
 
 template<>
 bool
-Parser<FullParseHandler>::processExportFrom(ParseNode* node)
+Parser<FullParseHandler, char16_t>::processExportFrom(ParseNode* node)
 {
     return pc->sc()->asModuleContext()->builder.processExportFrom(node);
 }
 
 template<>
 bool
-Parser<SyntaxParseHandler>::processExportFrom(Node node)
+Parser<SyntaxParseHandler, char16_t>::processExportFrom(Node node)
 {
     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
     return false;
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::exportFrom(uint32_t begin, Node specList)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::exportFrom(uint32_t begin, Node specList)
 {
     if (!abortIfSyntaxParser())
         return null();
 
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FROM));
 
     if (!abortIfSyntaxParser())
         return null();
@@ -5237,19 +5247,19 @@ Parser<ParseHandler>::exportFrom(uint32_
         return null();
 
     if (!processExportFrom(node))
         return null();
 
     return node;
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::exportBatch(uint32_t begin)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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());
     if (!kid)
@@ -5265,42 +5275,42 @@ Parser<ParseHandler>::exportBatch(uint32
 
     MUST_MATCH_TOKEN(TOK_FROM, JSMSG_FROM_AFTER_EXPORT_STAR);
 
     return exportFrom(begin, kid);
 }
 
 template<>
 bool
-Parser<FullParseHandler>::checkLocalExportNames(ParseNode* node)
+Parser<FullParseHandler, char16_t>::checkLocalExportNames(ParseNode* node)
 {
     // ES 2017 draft 15.2.3.1.
     for (ParseNode* next = node->pn_head; next; next = next->pn_next) {
         ParseNode* name = next->pn_left;
         MOZ_ASSERT(name->isKind(PNK_NAME));
 
         RootedPropertyName ident(context, name->pn_atom->asPropertyName());
         if (!checkLocalExportName(ident, name->pn_pos.begin))
             return false;
     }
 
     return true;
 }
 
 template<>
 bool
-Parser<SyntaxParseHandler>::checkLocalExportNames(Node node)
+Parser<SyntaxParseHandler, char16_t>::checkLocalExportNames(Node node)
 {
     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
     return false;
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::exportClause(uint32_t begin)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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());
     if (!kid)
@@ -5388,19 +5398,19 @@ Parser<ParseHandler>::exportClause(uint3
         return null();
 
     if (!processExport(node))
         return null();
 
     return node;
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::exportVariableStatement(uint32_t begin)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::exportVariableStatement(uint32_t begin)
 {
     if (!abortIfSyntaxParser())
         return null();
 
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_VAR));
 
     Node kid = declarationList(YieldIsName, PNK_VAR);
     if (!kid)
@@ -5415,20 +5425,20 @@ Parser<ParseHandler>::exportVariableStat
         return null();
 
     if (!processExport(node))
         return null();
 
     return node;
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::exportFunctionDeclaration(uint32_t begin, uint32_t toStringStart,
-                                                FunctionAsyncKind asyncKind /* = SyncFunction */)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::exportFunctionDeclaration(uint32_t begin, uint32_t toStringStart,
+                                                       FunctionAsyncKind asyncKind /* = SyncFunction */)
 {
     if (!abortIfSyntaxParser())
         return null();
 
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
 
     Node kid = functionStmt(toStringStart, YieldIsKeyword, NameRequired, asyncKind);
     if (!kid)
@@ -5442,19 +5452,19 @@ Parser<ParseHandler>::exportFunctionDecl
         return null();
 
     if (!processExport(node))
         return null();
 
     return node;
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::exportClassDeclaration(uint32_t begin)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::exportClassDeclaration(uint32_t begin)
 {
     if (!abortIfSyntaxParser())
         return null();
 
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_CLASS));
 
     Node kid = classDefinition(YieldIsKeyword, ClassStatement, NameRequired);
     if (!kid)
@@ -5468,19 +5478,19 @@ Parser<ParseHandler>::exportClassDeclara
         return null();
 
     if (!processExport(node))
         return null();
 
     return node;
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::exportLexicalDeclaration(uint32_t begin, DeclarationKind kind)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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));
 
@@ -5495,21 +5505,21 @@ Parser<ParseHandler>::exportLexicalDecla
         return null();
 
     if (!processExport(node))
         return null();
 
     return node;
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::exportDefaultFunctionDeclaration(uint32_t begin, uint32_t toStringStart,
-                                                       FunctionAsyncKind asyncKind
-                                                       /* = SyncFunction */)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::exportDefaultFunctionDeclaration(uint32_t begin,
+                                                              uint32_t toStringStart,
+                                                              FunctionAsyncKind asyncKind /* = SyncFunction */)
 {
     if (!abortIfSyntaxParser())
         return null();
 
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
 
     Node kid = functionStmt(toStringStart, YieldIsKeyword, AllowDefaultName, asyncKind);
     if (!kid)
@@ -5520,19 +5530,19 @@ Parser<ParseHandler>::exportDefaultFunct
         return null();
 
     if (!processExport(node))
         return null();
 
     return node;
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::exportDefaultClassDeclaration(uint32_t begin)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::exportDefaultClassDeclaration(uint32_t begin)
 {
     if (!abortIfSyntaxParser())
         return null();
 
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_CLASS));
 
     Node kid = classDefinition(YieldIsKeyword, ClassStatement, AllowDefaultName);
     if (!kid)
@@ -5543,19 +5553,19 @@ Parser<ParseHandler>::exportDefaultClass
         return null();
 
     if (!processExport(node))
         return null();
 
     return node;
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::exportDefaultAssignExpr(uint32_t begin)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::exportDefaultAssignExpr(uint32_t begin)
 {
     if (!abortIfSyntaxParser())
         return null();
 
     RootedPropertyName name(context, context->names().starDefaultStar);
     Node nameNode = newName(name);
     if (!nameNode)
         return null();
@@ -5573,19 +5583,19 @@ Parser<ParseHandler>::exportDefaultAssig
         return null();
 
     if (!processExport(node))
         return null();
 
     return node;
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::exportDefault(uint32_t begin)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::exportDefault(uint32_t begin)
 {
     if (!abortIfSyntaxParser())
         return null();
 
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_DEFAULT));
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
@@ -5617,19 +5627,19 @@ Parser<ParseHandler>::exportDefault(uint
         return exportDefaultClassDeclaration(begin);
 
       default:
         tokenStream.ungetToken();
         return exportDefaultAssignExpr(begin);
     }
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::exportDeclaration()
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::exportDeclaration()
 {
     if (!abortIfSyntaxParser())
         return null();
 
     MOZ_ASSERT(tokenStream.currentToken().type == TOK_EXPORT);
 
     if (!pc->atModuleLevel()) {
         error(JSMSG_EXPORT_DECL_AT_TOP_LEVEL);
@@ -5682,33 +5692,34 @@ Parser<ParseHandler>::exportDeclaration(
         return exportDefault(begin);
 
       default:
         error(JSMSG_DECLARATION_AFTER_EXPORT);
         return null();
     }
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::expressionStatement(YieldHandling yieldHandling, InvokedPrediction invoked)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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 <class ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::consequentOrAlternative(YieldHandling yieldHandling)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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() {}|
     // parses as |if (x) { function f() {} }|.
@@ -5774,19 +5785,19 @@ Parser<ParseHandler>::consequentOrAltern
 
         handler.addStatementToList(block, fun);
         return finishLexicalScope(scope, block);
     }
 
     return statement(yieldHandling);
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::ifStatement(YieldHandling yieldHandling)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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);
 
     while (true) {
@@ -5833,19 +5844,19 @@ Parser<ParseHandler>::ifStatement(YieldH
         elseBranch = handler.newIfStatement(posList[i], condList[i], thenList[i], elseBranch);
         if (!elseBranch)
             return null();
     }
 
     return elseBranch;
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::doWhileStatement(YieldHandling yieldHandling)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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);
     Node cond = condition(InAllowed, yieldHandling);
@@ -5859,56 +5870,56 @@ Parser<ParseHandler>::doWhileStatement(Y
     //   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 <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::whileStatement(YieldHandling yieldHandling)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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 <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::matchInOrOf(bool* isForInp, bool* isForOfp)
+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 <class ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::forHeadStart(YieldHandling yieldHandling,
+Parser<ParseHandler, CharT>::forHeadStart(YieldHandling yieldHandling,
                                    IteratorKind iterKind,
-                                   ParseNodeKind* forHeadKind,
-                                   Node* forInitialPart,
-                                   Maybe<ParseContext::Scope>& forLoopLexicalScope,
-                                   Node* forInOrOfExpression)
+                                          ParseNodeKind* forHeadKind,
+                                          Node* forInitialPart,
+                                          Maybe<ParseContext::Scope>& forLoopLexicalScope,
+                                          Node* forInOrOfExpression)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LP));
 
     TokenKind tt;
     if (!tokenStream.peekToken(&tt, TokenStream::Operand))
         return null();
 
     // Super-duper easy case: |for (;| is a C-style for-loop with no init
@@ -6048,19 +6059,19 @@ Parser<ParseHandler>::forHeadStart(Yield
         return false;
 
     // Finally, parse the iterated expression, making the for-loop's closing
     // ')' the next token.
     *forInOrOfExpression = expressionAfterForInOrOf(*forHeadKind, yieldHandling);
     return *forInOrOfExpression != null();
 }
 
-template <class ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::forStatement(YieldHandling yieldHandling)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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;
     IteratorKind iterKind = IteratorKind::Sync;
@@ -6228,19 +6239,19 @@ Parser<ParseHandler>::forStatement(Yield
         return null();
 
     if (forLoopLexicalScope)
         return finishLexicalScope(*forLoopLexicalScope, forLoop);
 
     return forLoop;
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::switchStatement(YieldHandling yieldHandling)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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);
     if (!discriminant)
@@ -6335,19 +6346,19 @@ Parser<ParseHandler>::switchStatement(Yi
     if (!caseList)
         return null();
 
     handler.setEndPosition(caseList, pos().end);
 
     return handler.newSwitchStatement(begin, discriminant, caseList);
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::continueStatement(YieldHandling yieldHandling)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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();
 
@@ -6393,19 +6404,19 @@ Parser<ParseHandler>::continueStatement(
     }
 
     if (!matchOrInsertSemicolonAfterNonExpression())
         return null();
 
     return handler.newContinueStatement(label, TokenPos(begin, pos().end));
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::breakStatement(YieldHandling yieldHandling)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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();
 
@@ -6433,19 +6444,19 @@ Parser<ParseHandler>::breakStatement(Yie
     }
 
     if (!matchOrInsertSemicolonAfterNonExpression())
         return null();
 
     return handler.newBreakStatement(label, TokenPos(begin, pos().end));
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::returnStatement(YieldHandling yieldHandling)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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;
 
     // Parse an optional operand.
@@ -6487,19 +6498,19 @@ Parser<ParseHandler>::returnStatement(Yi
     if (pc->isLegacyGenerator() && exprNode) {
         errorAt(begin, JSMSG_BAD_GENERATOR_RETURN);
         return null();
     }
 
     return pn;
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::yieldExpression(InHandling inHandling)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::yieldExpression(InHandling inHandling)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_YIELD));
     uint32_t begin = pos().begin;
 
     switch (pc->generatorKind()) {
       case StarGenerator:
       {
         MOZ_ASSERT(pc->isFunctionBox());
@@ -6622,19 +6633,19 @@ Parser<ParseHandler>::yieldExpression(In
 
         return handler.newYieldExpression(begin, exprNode);
       }
     }
 
     MOZ_CRASH("yieldExpr");
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::withStatement(YieldHandling yieldHandling)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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
     // mode code, it doesn't even merit a warning in non-strict code.  See
@@ -6658,19 +6669,19 @@ Parser<ParseHandler>::withStatement(Yiel
             return null();
     }
 
     pc->sc()->setBindingsAccessedDynamically();
 
     return handler.newWithStatement(begin, objectExpr, innerBlock);
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::labeledItem(YieldHandling yieldHandling)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::labeledItem(YieldHandling yieldHandling)
 {
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
 
     if (tt == TOK_FUNCTION) {
         TokenKind next;
         if (!tokenStream.peekToken(&next))
@@ -6693,19 +6704,19 @@ Parser<ParseHandler>::labeledItem(YieldH
 
         return functionStmt(pos().begin, yieldHandling, NameRequired);
     }
 
     tokenStream.ungetToken();
     return statement(yieldHandling);
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::labeledStatement(YieldHandling yieldHandling)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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;
     };
@@ -6723,19 +6734,19 @@ Parser<ParseHandler>::labeledStatement(Y
     ParseContext::LabelStatement stmt(pc, label);
     Node pn = labeledItem(yieldHandling);
     if (!pn)
         return null();
 
     return handler.newLabeledStatement(label, pn, begin);
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::throwStatement(YieldHandling yieldHandling)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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))
         return null();
@@ -6753,19 +6764,19 @@ Parser<ParseHandler>::throwStatement(Yie
         return null();
 
     if (!matchOrInsertSemicolonAfterExpression())
         return null();
 
     return handler.newThrowStatement(throwExpr, TokenPos(begin, pos().end));
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::tryStatement(YieldHandling yieldHandling)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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
      * kid2 is the catch node list or null
@@ -6944,20 +6955,20 @@ Parser<ParseHandler>::tryStatement(Yield
     if (!catchList && !finallyBlock) {
         error(JSMSG_CATCH_OR_FINALLY);
         return null();
     }
 
     return handler.newTryStatement(begin, innerBlock, catchList, finallyBlock);
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::catchBlockStatement(YieldHandling yieldHandling,
-                                          ParseContext::Scope& catchParamScope)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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
     //
     // Step 8 means that the body of a catch block always has an additional
@@ -6980,19 +6991,19 @@ Parser<ParseHandler>::catchBlockStatemen
                                                           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 <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::debuggerStatement()
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::debuggerStatement()
 {
     TokenPos p;
     p.begin = pos().begin;
     if (!matchOrInsertSemicolonAfterNonExpression())
         return null();
     p.end = pos().end;
 
     pc->sc()->setBindingsAccessedDynamically();
@@ -7019,21 +7030,21 @@ JSOpFromPropertyType(PropertyType propTy
       case PropertyType::Constructor:
       case PropertyType::DerivedConstructor:
         return JSOP_INITPROP;
       default:
         MOZ_CRASH("unexpected property type");
     }
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
-                                      ClassContext classContext,
-                                      DefaultHandling defaultHandling)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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);
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt))
@@ -7246,19 +7257,20 @@ Parser<ParseHandler>::classDefinition(Yi
     }
 
     MOZ_ALWAYS_TRUE(setLocalStrictMode(savedStrictness));
 
     return handler.newClass(nameNode, classHeritage, methodsOrBlock,
                             TokenPos(classStartOffset, classEndOffset));
 }
 
-template <class ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::nextTokenContinuesLetDeclaration(TokenKind next, YieldHandling yieldHandling)
+Parser<ParseHandler, CharT>::nextTokenContinuesLetDeclaration(TokenKind next,
+                                                              YieldHandling yieldHandling)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LET));
 
 #ifdef DEBUG
     TokenKind verify;
     MOZ_ALWAYS_TRUE(tokenStream.peekToken(&verify));
     MOZ_ASSERT(next == verify);
 #endif
@@ -7296,31 +7308,31 @@ Parser<ParseHandler>::nextTokenContinues
         // parse tree with ASI applied.  No backsies!
         return true;
     }
 
     // Otherwise not a let declaration.
     return false;
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::variableStatement(YieldHandling yieldHandling)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::variableStatement(YieldHandling yieldHandling)
 {
     Node vars = declarationList(yieldHandling, PNK_VAR);
     if (!vars)
         return null();
     if (!matchOrInsertSemicolonAfterExpression())
         return null();
     return vars;
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::statement(YieldHandling yieldHandling)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::statement(YieldHandling yieldHandling)
 {
     MOZ_ASSERT(checkOptionsCalled);
 
     if (!CheckRecursionLimit(context))
         return null();
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
@@ -7507,20 +7519,20 @@ Parser<ParseHandler>::statement(YieldHan
       case TOK_FINALLY:
         error(JSMSG_FINALLY_WITHOUT_TRY);
         return null();
 
       // NOTE: default case handled in the ExpressionStatement section.
     }
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling,
-                                        bool canHaveDirectives /* = false */)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::statementListItem(YieldHandling yieldHandling,
+                                               bool canHaveDirectives /* = false */)
 {
     MOZ_ASSERT(checkOptionsCalled);
 
     if (!CheckRecursionLimit(context))
         return null();
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
@@ -7699,22 +7711,22 @@ Parser<ParseHandler>::statementListItem(
       case TOK_FINALLY:
         error(JSMSG_FINALLY_WITHOUT_TRY);
         return null();
 
       // NOTE: default case handled in the ExpressionStatement section.
     }
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::expr(InHandling inHandling, YieldHandling yieldHandling,
-                           TripledotHandling tripledotHandling,
-                           PossibleError* possibleError /* = nullptr */,
-                           InvokedPrediction invoked /* = PredictUninvoked */)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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)
         return null();
 
     bool matched;
     if (!tokenStream.matchToken(&matched, TOK_COMMA))
@@ -7859,22 +7871,22 @@ 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 <typename ParseHandler>
-MOZ_ALWAYS_INLINE typename ParseHandler::Node
-Parser<ParseHandler>::orExpr1(InHandling inHandling, YieldHandling yieldHandling,
-                              TripledotHandling tripledotHandling,
-                              PossibleError* possibleError,
-                              InvokedPrediction invoked /* = PredictUninvoked */)
+template <template <typename CharT> class ParseHandler, typename CharT>
+MOZ_ALWAYS_INLINE typename ParseHandler<CharT>::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.
 
     // Conceptually there's just one stack, a stack of pairs (lhs, op).
     // It's implemented using two separate arrays, though.
     Node nodeStack[PRECEDENCE_CLASSES];
     ParseNodeKind kindStack[PRECEDENCE_CLASSES];
@@ -7935,22 +7947,22 @@ Parser<ParseHandler>::orExpr1(InHandling
         depth++;
         MOZ_ASSERT(depth <= PRECEDENCE_CLASSES);
     }
 
     MOZ_ASSERT(depth == 0);
     return pn;
 }
 
-template <typename ParseHandler>
-MOZ_ALWAYS_INLINE typename ParseHandler::Node
-Parser<ParseHandler>::condExpr1(InHandling inHandling, YieldHandling yieldHandling,
-                                TripledotHandling tripledotHandling,
-                                PossibleError* possibleError,
-                                InvokedPrediction invoked /* = PredictUninvoked */)
+template <template <typename CharT> class ParseHandler, typename CharT>
+MOZ_ALWAYS_INLINE typename ParseHandler<CharT>::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))
         return condition;
 
     Node thenExpr = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
     if (!thenExpr)
@@ -7984,22 +7996,22 @@ class AutoClearInDestructuringDecl
             pc->functionBox()->hasParameterExprs = true;
     }
 
     ~AutoClearInDestructuringDecl() {
         pc_->inDestructuringDecl = saved_;
     }
 };
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandling,
-                                 TripledotHandling tripledotHandling,
-                                 PossibleError* possibleError /* = nullptr */,
-                                 InvokedPrediction invoked /* = PredictUninvoked */)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::assignExpr(InHandling inHandling, YieldHandling yieldHandling,
+                                        TripledotHandling tripledotHandling,
+                                        PossibleError* possibleError /* = nullptr */,
+                                        InvokedPrediction invoked /* = PredictUninvoked */)
 {
     if (!CheckRecursionLimit(context))
         return null();
 
     // It's very common at this point to have a "detectably simple" expression,
     // i.e. a name/number/string token followed by one of the following tokens
     // that obviously isn't part of an expression: , ; : ) ] }
     //
@@ -8252,20 +8264,20 @@ Parser<ParseHandler>::assignExpr(InHandl
     }
 
     if (kind == PNK_ASSIGN)
         handler.checkAndSetIsDirectRHSAnonFunction(rhs);
 
     return handler.newAssignment(kind, lhs, rhs, op);
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::isValidSimpleAssignmentTarget(Node node,
-                                                    FunctionCallBehavior behavior /* = ForbidAssignmentToFunctionCalls */)
+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.
 
     if (handler.isNameAnyParentheses(node)) {
         if (!pc->sc()->strict())
             return true;
@@ -8279,19 +8291,19 @@ Parser<ParseHandler>::isValidSimpleAssig
     if (behavior == PermitAssignmentToFunctionCalls) {
         if (handler.isFunctionCall(node))
             return true;
     }
 
     return false;
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::checkIncDecOperand(Node operand, uint32_t operandOffset)
+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;
         }
     } else if (handler.isPropertyAccess(operand)) {
         // Permitted: no additional testing/fixup needed.
@@ -8307,32 +8319,33 @@ Parser<ParseHandler>::checkIncDecOperand
         return false;
     }
 
     MOZ_ASSERT(isValidSimpleAssignmentTarget(operand, PermitAssignmentToFunctionCalls),
                "inconsistent increment/decrement operand validation");
     return true;
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kind, JSOp op,
-                                  uint32_t begin)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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 <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::unaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
-                                PossibleError* possibleError /* = nullptr */,
-                                InvokedPrediction invoked /* = PredictUninvoked */)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::unaryExpr(YieldHandling yieldHandling,
+                                       TripledotHandling tripledotHandling,
+                                       PossibleError* possibleError /* = nullptr */,
+                                       InvokedPrediction invoked /* = PredictUninvoked */)
 {
     if (!CheckRecursionLimit(context))
         return null();
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
     uint32_t begin = pos().begin;
@@ -8446,19 +8459,19 @@ Parser<ParseHandler>::unaryExpr(YieldHan
  *     [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 <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::generatorComprehensionLambda(unsigned begin)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::generatorComprehensionLambda(unsigned begin)
 {
     Node genfn = handler.newFunctionExpression(pos());
     if (!genfn)
         return null();
 
     ParseContext* outerpc = pc;
 
     // If we are off thread, the generator meta-objects have
@@ -8536,19 +8549,19 @@ Parser<ParseHandler>::generatorComprehen
     // need to propagate the closed-over variable set to the inner
     // lazyscript.
     if (!handler.setComprehensionLambdaBody(genfn, body))
         return null();
 
     return genfn;
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::comprehensionFor(GeneratorKind comprehensionKind)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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);
 
     // FIXME: Destructuring binding (bug 980828).
@@ -8609,19 +8622,19 @@ Parser<ParseHandler>::comprehensionFor(G
 
     Node head = handler.newForInOrOfHead(PNK_FOROF, lexicalScope, rhs, headPos);
     if (!head)
         return null();
 
     return handler.newComprehensionFor(begin, head, tail);
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::comprehensionIf(GeneratorKind comprehensionKind)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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);
     if (!cond)
@@ -8636,19 +8649,19 @@ Parser<ParseHandler>::comprehensionIf(Ge
 
     Node then = comprehensionTail(comprehensionKind);
     if (!then)
         return null();
 
     return handler.newIfStatement(begin, cond, then, null());
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::comprehensionTail(GeneratorKind comprehensionKind)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::comprehensionTail(GeneratorKind comprehensionKind)
 {
     if (!CheckRecursionLimit(context))
         return null();
 
     bool matched;
     if (!tokenStream.matchToken(&matched, TOK_FOR, TokenStream::Operand))
         return null();
     if (matched)
@@ -8674,19 +8687,19 @@ Parser<ParseHandler>::comprehensionTail(
         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 <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::comprehension(GeneratorKind comprehensionKind)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::comprehension(GeneratorKind comprehensionKind)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
 
     uint32_t startYieldOffset = pc->lastYieldOffset;
 
     Node body = comprehensionFor(comprehensionKind);
     if (!body)
         return null();
@@ -8694,19 +8707,19 @@ Parser<ParseHandler>::comprehension(Gene
     if (comprehensionKind != NotGenerator && pc->lastYieldOffset != startYieldOffset) {
         errorAt(pc->lastYieldOffset, JSMSG_BAD_GENEXP_BODY, js_yield_str);
         return null();
     }
 
     return body;
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::arrayComprehension(uint32_t begin)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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);
 
     Node comp = handler.newList(PNK_ARRAYCOMP, inner);
@@ -8714,19 +8727,19 @@ Parser<ParseHandler>::arrayComprehension
         return null();
 
     handler.setBeginPosition(comp, begin);
     handler.setEndPosition(comp, pos().end);
 
     return comp;
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::generatorComprehension(uint32_t begin)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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.
     if (!abortIfSyntaxParser())
@@ -8740,19 +8753,19 @@ Parser<ParseHandler>::generatorComprehen
     if (!result)
         return null();
     handler.setBeginPosition(result, begin);
     handler.setEndPosition(result, pos().end);
 
     return result;
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::assignExprWithoutYieldOrAwait(YieldHandling yieldHandling)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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);
             return null();
@@ -8760,20 +8773,21 @@ Parser<ParseHandler>::assignExprWithoutY
         if (pc->lastAwaitOffset != startAwaitOffset) {
             errorAt(pc->lastAwaitOffset, JSMSG_AWAIT_IN_DEFAULT);
             return null();
         }
     }
     return res;
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::argumentList(YieldHandling yieldHandling, Node listNode, bool* isSpread,
-                                   PossibleError* possibleError /* = nullptr */)
+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;
     if (matched) {
         handler.setEndPosition(listNode, pos().end);
         return true;
     }
@@ -8816,32 +8830,33 @@ Parser<ParseHandler>::argumentList(Yield
     }
 
     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_ARGS);
 
     handler.setEndPosition(listNode, pos().end);
     return true;
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::checkAndMarkSuperScope()
+Parser<ParseHandler, CharT>::checkAndMarkSuperScope()
 {
     if (!pc->sc()->allowSuperProperty())
         return false;
     pc->setSuperScopeNeedsHomeObject();
     return true;
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
-                                 TokenKind tt, bool allowCallSyntax /* = true */,
-                                 PossibleError* possibleError /* = nullptr */,
-                                 InvokedPrediction invoked /* = PredictUninvoked */)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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));
 
     Node lhs;
 
     if (!CheckRecursionLimit(context))
         return null();
 
@@ -9055,36 +9070,36 @@ Parser<ParseHandler>::memberExpr(YieldHa
     if (handler.isSuperBase(lhs)) {
         error(JSMSG_BAD_SUPER);
         return null();
     }
 
     return lhs;
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::newName(PropertyName* name)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::newName(PropertyName* name)
 {
     return newName(name, pos());
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::newName(PropertyName* name, TokenPos pos)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::newName(PropertyName* name, TokenPos pos)
 {
     return handler.newName(name, pos, context);
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::checkLabelOrIdentifierReference(PropertyName* ident,
-                                                      uint32_t offset,
-                                                      YieldHandling yieldHandling,
-                                                      TokenKind hint /* = TOK_LIMIT */)
+Parser<ParseHandler, CharT>::checkLabelOrIdentifierReference(PropertyName* ident,
+                                                             uint32_t offset,
+                                                             YieldHandling yieldHandling,
+                                                             TokenKind hint /* = TOK_LIMIT */)
 {
     TokenKind tt;
     if (hint == TOK_LIMIT) {
         tt = ReservedWordTokenKind(ident);
     } else {
         MOZ_ASSERT(hint == ReservedWordTokenKind(ident), "hint doesn't match actual token kind");
         tt = hint;
     }
@@ -9138,22 +9153,22 @@ Parser<ParseHandler>::checkLabelOrIdenti
     if (TokenKindIsFutureReservedWord(tt)) {
         errorAt(offset, JSMSG_RESERVED_ID, ReservedWordToCharZ(tt));
         return false;
     }
     MOZ_ASSERT_UNREACHABLE("Unexpected reserved word kind.");
     return false;
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::checkBindingIdentifier(PropertyName* ident,
-                                             uint32_t offset,
-                                             YieldHandling yieldHandling,
-                                             TokenKind hint /* = TOK_LIMIT */)
+Parser<ParseHandler, CharT>::checkBindingIdentifier(PropertyName* ident,
+                                                    uint32_t offset,
+                                                    YieldHandling yieldHandling,
+                                                    TokenKind hint /* = TOK_LIMIT */)
 {
     if (pc->sc()->needStrictChecks()) {
         if (ident == context->names().arguments) {
             if (!strictModeErrorAt(offset, JSMSG_BAD_STRICT_ASSIGN, "arguments"))
                 return false;
             return true;
         }
 
@@ -9162,19 +9177,19 @@ Parser<ParseHandler>::checkBindingIdenti
                 return false;
             return true;
         }
     }
 
     return checkLabelOrIdentifierReference(ident, offset, yieldHandling, hint);
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 PropertyName*
-Parser<ParseHandler>::labelOrIdentifierReference(YieldHandling yieldHandling)
+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.
     //
     // Use PropertyName* instead of TokenKind to reflect the normalization.
 
@@ -9184,93 +9199,94 @@ Parser<ParseHandler>::labelOrIdentifierR
                      ? tokenStream.currentToken().type
                      : TOK_LIMIT;
     RootedPropertyName ident(context, tokenStream.currentName());
     if (!checkLabelOrIdentifierReference(ident, pos().begin, yieldHandling, hint))
         return nullptr;
     return ident;
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 PropertyName*
-Parser<ParseHandler>::bindingIdentifier(YieldHandling yieldHandling)
+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 <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::identifierReference(Handle<PropertyName*> name)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::identifierReference(Handle<PropertyName*> name)
 {
     Node pn = newName(name);
     if (!pn)
         return null();
 
     if (!pc->inDestructuringDecl && !noteUsedName(name))
         return null();
 
     return pn;
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::stringLiteral()
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::stringLiteral()
 {
     return handler.newStringLiteral(tokenStream.currentToken().atom(), pos());
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::noSubstitutionTaggedTemplate()
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::noSubstitutionTaggedTemplate()
 {
     if (tokenStream.hasInvalidTemplateEscape()) {
         tokenStream.clearInvalidTemplateEscape();
         return handler.newRawUndefinedLiteral(pos());
     }
 
     return handler.newTemplateStringLiteral(tokenStream.currentToken().atom(), pos());
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::noSubstitutionUntaggedTemplate()
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::noSubstitutionUntaggedTemplate()
 {
     if (!tokenStream.checkForInvalidTemplateEscapeError())
         return null();
 
     return handler.newTemplateStringLiteral(tokenStream.currentToken().atom(), pos());
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::newRegExp()
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::newRegExp()
 {
     MOZ_ASSERT(!options().selfHostingMode);
     // Create the regexp even when doing a syntax parse, to check the regexp's syntax.
     const char16_t* chars = tokenStream.getTokenbuf().begin();
     size_t length = tokenStream.getTokenbuf().length();
     RegExpFlag flags = tokenStream.currentToken().regExpFlags();
 
     Rooted<RegExpObject*> reobj(context);
     reobj = RegExpObject::create(context, chars, length, flags, &tokenStream, alloc);
     if (!reobj)
         return null();
 
     return handler.newRegExp(reobj, pos(), *this);
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::arrayInitializer(YieldHandling yieldHandling, PossibleError* possibleError)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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)
         return null();
 
@@ -9353,20 +9369,20 @@ Parser<ParseHandler>::arrayInitializer(Y
 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 <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList,
-                                   PropertyType* propType, MutableHandleAtom propAtom)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::propertyName(YieldHandling yieldHandling, Node propList,
+                                          PropertyType* propType, MutableHandleAtom propAtom)
 {
     TokenKind ltok;
     if (!tokenStream.getToken(&ltok))
         return null();
 
     MOZ_ASSERT(ltok != TOK_RC, "caller should have handled TOK_RC");
 
     bool isGenerator = false;
@@ -9550,19 +9566,19 @@ Parser<ParseHandler>::propertyName(Yield
             *propType = PropertyType::Method;
         return propName;
     }
 
     error(JSMSG_COLON_AFTER_ID);
     return null();
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::computedPropertyName(YieldHandling yieldHandling, Node literal)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::computedPropertyName(YieldHandling yieldHandling, Node literal)
 {
     uint32_t begin = pos().begin;
 
     Node assignNode;
     {
         // Turn off the inDestructuringDecl flag when parsing computed property
         // names. In short, when parsing 'let {[x + y]: z} = obj;', noteUsedName()
         // should be called on x and y, but not on z. See the comment on
@@ -9576,19 +9592,20 @@ Parser<ParseHandler>::computedPropertyNa
     MUST_MATCH_TOKEN(TOK_RB, JSMSG_COMP_PROP_UNTERM_EXPR);
     Node propname = handler.newComputedName(assignNode, begin, pos().end);
     if (!propname)
         return null();
     handler.setListFlag(literal, PNX_NONCONST);
     return propname;
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError* possibleError)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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);
     if (!literal)
         return null();
@@ -9755,20 +9772,20 @@ Parser<ParseHandler>::objectLiteral(Yiel
             return null();
         }
     }
 
     handler.setEndPosition(literal, pos().end);
     return literal;
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::methodDefinition(uint32_t toStringStart, PropertyType propType,
-                                       HandleAtom funName)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::Node
+Parser<ParseHandler, CharT>::methodDefinition(uint32_t toStringStart, PropertyType propType,
+                                              HandleAtom funName)
 {
     FunctionSyntaxKind kind;
     switch (propType) {
       case PropertyType::Getter:
         kind = Getter;
         break;
 
       case PropertyType::GetterNoExpressionClosure:
@@ -9817,19 +9834,19 @@ Parser<ParseHandler>::methodDefinition(u
     Node pn = handler.newFunctionExpression(pos());
     if (!pn)
         return null();
 
     return functionDefinition(pn, toStringStart, InAllowed, yieldHandling, funName, kind,
                               generatorKind, asyncKind);
 }
 
-template <typename ParseHandler>
+template <template <typename CharT> class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler>::tryNewTarget(Node &newTarget)
+Parser<ParseHandler, CharT>::tryNewTarget(Node &newTarget)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_NEW));
 
     newTarget = null();
 
     Node newHolder = handler.newPosHolder(pos());
     if (!newHolder)
         return false;
@@ -9861,21 +9878,22 @@ Parser<ParseHandler>::tryNewTarget(Node 
     Node targetHolder = handler.newPosHolder(pos());
     if (!targetHolder)
         return false;
 
     newTarget = handler.newNewTarget(newHolder, targetHolder);
     return !!newTarget;
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
-                                  TokenKind tt, PossibleError* possibleError,
-                                  InvokedPrediction invoked /* = PredictUninvoked */)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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();
 
     switch (tt) {
       case TOK_FUNCTION:
         return functionExpr(pos().begin, invoked);
@@ -10038,21 +10056,21 @@ Parser<ParseHandler>::primaryExpr(YieldH
         tokenStream.ungetToken();  // put back right paren
 
         // Return an arbitrary expression node. See case TOK_RP above.
         return handler.newNullLiteral(pos());
       }
     }
 }
 
-template <typename ParseHandler>
-typename ParseHandler::Node
-Parser<ParseHandler>::exprInParens(InHandling inHandling, YieldHandling yieldHandling,
-                                   TripledotHandling tripledotHandling,
-                                   PossibleError* possibleError /* = nullptr */)
+template <template <typename CharT> class ParseHandler, typename CharT>
+typename ParseHandler<CharT>::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);
 }
 
 void
 ParserBase::addTelemetry(JSCompartment::DeprecatedLanguageExtension e)
 {
@@ -10086,13 +10104,13 @@ ParserBase::warnOnceAboutForEach()
     if (!context->compartment()->warnedAboutForEach) {
         if (!warning(JSMSG_DEPRECATED_FOR_EACH))
             return false;
         context->compartment()->warnedAboutForEach = true;
     }
     return true;
 }
 
-template class Parser<FullParseHandler>;
-template class Parser<SyntaxParseHandler>;
+template class Parser<FullParseHandler, char16_t>;
+template class Parser<SyntaxParseHandler, char16_t>;
 
 } /* namespace frontend */
 } /* namespace js */
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -25,16 +25,20 @@
 #include "frontend/SyntaxParseHandler.h"
 
 namespace js {
 
 class ModuleObject;
 
 namespace frontend {
 
+class ParserBase;
+
+template <template <typename CharT> 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.
  */
 class ParseContext : public Nestable<ParseContext>
@@ -123,22 +127,17 @@ class ParseContext : public Nestable<Par
         }
 
       public:
         using DeclaredNamePtr = DeclaredNameMap::Ptr;
         using AddDeclaredNamePtr = DeclaredNameMap::AddPtr;
 
         using Nestable<Scope>::enclosing;
 
-        template <typename ParseHandler>
-        explicit Scope(Parser<ParseHandler>* parser)
-          : Nestable<Scope>(&parser->pc->innermostScope_),
-            declared_(parser->context->frontendCollectionPool()),
-            id_(parser->usedNames.nextScopeId())
-        { }
+        explicit inline Scope(ParserBase* parser);
 
         void dump(ParseContext* pc);
 
         uint32_t id() const {
             return id_;
         }
 
         MOZ_MUST_USE bool init(ParseContext* pc) {
@@ -254,22 +253,17 @@ class ParseContext : public Nestable<Par
         };
 
         inline BindingIter bindings(ParseContext* pc);
     };
 
     class VarScope : public Scope
     {
       public:
-        template <typename ParseHandler>
-        explicit VarScope(Parser<ParseHandler>* parser)
-          : Scope(parser)
-        {
-            useAsVarScope(parser->pc);
-        }
+        explicit inline VarScope(ParserBase* parser);
     };
 
   private:
     // Trace logging of parsing time.
     AutoFrontendTraceLog traceLog_;
 
     // Context shared between parsing and bytecode generation.
     SharedContext* sc_;
@@ -359,21 +353,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 <typename ParseHandler>
-    ParseContext(Parser<ParseHandler>* prs, SharedContext* sc, Directives* newDirectives)
+    template <template <typename CharT> class ParseHandler, typename CharT>
+    ParseContext(Parser<ParseHandler, CharT>* prs, SharedContext* sc, Directives* newDirectives)
       : Nestable<ParseContext>(&prs->pc),
         traceLog_(sc->context,
-                  mozilla::IsSame<ParseHandler, FullParseHandler>::value
+                  mozilla::IsSame<ParseHandler<CharT>, FullParseHandler<CharT>>::value
                   ? TraceLogger_ParsingFull
                   : TraceLogger_ParsingSyntax,
                   prs->tokenStream),
         sc_(sc),
         tokenStream_(prs->tokenStream),
         innermostStatement_(nullptr),
         innermostScope_(nullptr),
         varScope_(nullptr),
@@ -763,17 +757,17 @@ class UsedNameTracker
         map_.clear();
         RewindToken token;
         token.scriptId = 0;
         token.scopeId = 0;
         rewind(token);
     }
 };
 
-template <typename ParseHandler>
+template <class Parser>
 class AutoAwaitIsKeyword;
 
 class ParserBase : public StrictModeGetter
 {
   private:
     ParserBase* thisForCtor() { return this; }
 
   public:
@@ -951,26 +945,48 @@ class ParserBase : public StrictModeGett
     }
     void release(Mark m) {
         alloc.release(m.mark);
         traceListHead = m.traceListHead;
     }
 
     ObjectBox* newObjectBox(JSObject* obj);
 
+    mozilla::Maybe<GlobalScope::Data*> newGlobalScopeData(ParseContext::Scope& scope);
+    mozilla::Maybe<ModuleScope::Data*> newModuleScopeData(ParseContext::Scope& scope);
+    mozilla::Maybe<EvalScope::Data*> newEvalScopeData(ParseContext::Scope& scope);
+    mozilla::Maybe<FunctionScope::Data*> newFunctionScopeData(ParseContext::Scope& scope,
+                                                              bool hasParameterExprs);
+    mozilla::Maybe<VarScope::Data*> newVarScopeData(ParseContext::Scope& scope);
+    mozilla::Maybe<LexicalScope::Data*> newLexicalScopeData(ParseContext::Scope& scope);
+
   protected:
     enum InvokedPrediction { PredictUninvoked = false, PredictInvoked = true };
     enum ForInitLocation { InForInit, NotInForInit };
 };
 
-template <typename ParseHandler>
+inline
+ParseContext::Scope::Scope(ParserBase* parser)
+  : Nestable<Scope>(&parser->pc->innermostScope_),
+    declared_(parser->context->frontendCollectionPool()),
+    id_(parser->usedNames.nextScopeId())
+{ }
+
+inline
+ParseContext::VarScope::VarScope(ParserBase* parser)
+  : Scope(parser)
+{
+    useAsVarScope(parser->pc);
+}
+
+template <template<typename CharT> class ParseHandler, typename CharT>
 class Parser final : public ParserBase, private JS::AutoGCRooter
 {
   private:
-    using Node = typename ParseHandler::Node;
+    using Node = typename ParseHandler<CharT>::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
@@ -1022,17 +1038,17 @@ class Parser final : public ParserBase, 
         struct Error {
             ErrorState state_ = ErrorState::None;
 
             // Error reporting fields.
             uint32_t offset_;
             unsigned errorNumber_;
         };
 
-        Parser<ParseHandler>& parser_;
+        Parser<ParseHandler, CharT>& parser_;
         Error exprError_;
         Error destructuringError_;
 
         // Returns the error report.
         Error& error(ErrorKind kind);
 
         // Return true if an error is pending without reporting
         bool hasError(ErrorKind kind);
@@ -1047,17 +1063,17 @@ class Parser final : public ParserBase, 
         // If there is a pending error, report it and return false, otherwise
         // return true.
         bool checkForError(ErrorKind kind);
 
         // Transfer an existing error to another instance.
         void transferErrorTo(ErrorKind kind, PossibleError* other);
 
       public:
-        explicit PossibleError(Parser<ParseHandler>& parser);
+        explicit PossibleError(Parser<ParseHandler, CharT>& parser);
 
         // Set a pending destructuring error. Only a single error may be set
         // per instance, i.e. subsequent calls to this method are ignored and
         // won't overwrite the existing pending error.
         void setPendingDestructuringErrorAt(const TokenPos& pos, unsigned errorNumber);
 
         // Set a pending expression error. Only a single error may be set per
         // instance, i.e. subsequent calls to this method are ignored and won't
@@ -1076,28 +1092,28 @@ class Parser final : public ParserBase, 
         // 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);
     };
 
   public:
     /* State specific to the kind of parse being performed. */
-    ParseHandler handler;
+    ParseHandler<CharT> 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 char16_t* chars, size_t length, bool foldConstants, UsedNameTracker& usedNames,
-           Parser<SyntaxParseHandler>* syntaxParser, LazyScript* lazyOuterFunction);
+           const CharT* chars, size_t length, bool foldConstants, UsedNameTracker& usedNames,
+           Parser<SyntaxParseHandler, CharT>* syntaxParser, LazyScript* lazyOuterFunction);
     ~Parser();
 
-    friend class AutoAwaitIsKeyword<ParseHandler>;
+    friend class AutoAwaitIsKeyword<Parser>;
     void setAwaitIsKeyword(bool isKeyword);
 
     bool checkOptions();
 
     friend void js::frontend::TraceParser(JSTracer* trc, JS::AutoGCRooter* parser);
 
     /*
      * Parse a top-level JS script.
@@ -1495,23 +1511,16 @@ class Parser final : public ParserBase, 
                                                     DeclarationKind kind, TokenPos pos);
     bool noteDeclaredName(HandlePropertyName name, DeclarationKind kind, TokenPos pos);
     bool noteUsedName(HandlePropertyName name);
     bool hasUsedName(HandlePropertyName name);
 
     // Required on Scope exit.
     bool propagateFreeNamesAndMarkClosedOverBindings(ParseContext::Scope& scope);
 
-    mozilla::Maybe<GlobalScope::Data*> newGlobalScopeData(ParseContext::Scope& scope);
-    mozilla::Maybe<ModuleScope::Data*> newModuleScopeData(ParseContext::Scope& scope);
-    mozilla::Maybe<EvalScope::Data*> newEvalScopeData(ParseContext::Scope& scope);
-    mozilla::Maybe<FunctionScope::Data*> newFunctionScopeData(ParseContext::Scope& scope,
-                                                              bool hasParameterExprs);
-    mozilla::Maybe<VarScope::Data*> newVarScopeData(ParseContext::Scope& scope);
-    mozilla::Maybe<LexicalScope::Data*> newLexicalScopeData(ParseContext::Scope& scope);
     Node finishLexicalScope(ParseContext::Scope& scope, Node body);
 
     Node propertyName(YieldHandling yieldHandling, Node propList,
                       PropertyType* propType, MutableHandleAtom propAtom);
     Node computedPropertyName(YieldHandling yieldHandling, Node literal);
     Node arrayInitializer(YieldHandling yieldHandling, PossibleError* possibleError);
     Node newRegExp();
 
@@ -1528,32 +1537,32 @@ class Parser final : public ParserBase, 
     bool checkDestructuringArray(Node arrayPattern, const mozilla::Maybe<DeclarationKind>& maybeDecl);
     bool checkDestructuringObject(Node objectPattern, const mozilla::Maybe<DeclarationKind>& maybeDecl);
     bool checkDestructuringName(Node expr, const mozilla::Maybe<DeclarationKind>& maybeDecl);
 
     Node newNumber(const Token& tok) {
         return handler.newNumber(tok.number(), tok.decimalPoint(), tok.pos);
     }
 
-    static Node null() { return ParseHandler::null(); }
+    static Node null() { return ParseHandler<CharT>::null(); }
 
     JSAtom* prefixAccessorName(PropertyType propType, HandleAtom propAtom);
 
     bool asmJS(Node list);
 };
 
-template <typename ParseHandler>
+template <class Parser>
 class MOZ_STACK_CLASS AutoAwaitIsKeyword
 {
   private:
-    Parser<ParseHandler>* parser_;
+    Parser* parser_;
     bool oldAwaitIsKeyword_;
 
   public:
-    AutoAwaitIsKeyword(Parser<ParseHandler>* parser, bool awaitIsKeyword) {
+    AutoAwaitIsKeyword(Parser* parser, bool awaitIsKeyword) {
         parser_ = parser;
         oldAwaitIsKeyword_ = parser_->awaitIsKeyword_;
         parser_->setAwaitIsKeyword(awaitIsKeyword);
     }
 
     ~AutoAwaitIsKeyword() {
         parser_->setAwaitIsKeyword(oldAwaitIsKeyword_);
     }
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -2,39 +2,43 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef frontend_SyntaxParseHandler_h
 #define frontend_SyntaxParseHandler_h
 
+#include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 
 #include <string.h>
 
+#include "jscntxt.h"
+
 #include "frontend/ParseNode.h"
 
 namespace js {
+
 namespace frontend {
 
-template <typename ParseHandler>
+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 SyntaxParseHandler
+class SyntaxParseHandlerBase
 {
     // Remember the last encountered name or string literal during syntax parses.
     JSAtom* lastAtom;
     TokenPos lastStringPos;
 
   public:
     enum Node {
         NodeFailure = 0,
@@ -163,18 +167,17 @@ class SyntaxParseHandler
     }
 
     static bool isDestructuringPatternAnyParentheses(Node node) {
         return isUnparenthesizedDestructuringPattern(node) ||
                 isParenthesizedDestructuringPattern(node);
     }
 
   public:
-    SyntaxParseHandler(JSContext* cx, LifoAlloc& alloc, Parser<SyntaxParseHandler>* syntaxParser,
-                       LazyScript* lazyOuterFunction)
+    SyntaxParseHandlerBase(JSContext* cx, LifoAlloc& alloc, LazyScript* lazyOuterFunction)
       : lastAtom(nullptr)
     {}
 
     static Node null() { return NodeFailure; }
 
     void prepareNodeForMutation(Node node) {}
     void freeTree(Node node) {}
 
@@ -613,21 +616,41 @@ class SyntaxParseHandler
 
     bool canSkipLazyInnerFunctions() {
         return false;
     }
     bool canSkipLazyClosedOverBindings() {
         return false;
     }
     JSAtom* nextLazyClosedOverBinding() {
-        MOZ_CRASH("SyntaxParseHandler::canSkipLazyClosedOverBindings must return false");
+        MOZ_CRASH("SyntaxParseHandlerBase::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/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4253,20 +4253,20 @@ JS_BufferIsCompilableUnit(JSContext* cx,
     // Return true on any out-of-memory error or non-EOF-related syntax error, so our
     // caller doesn't try to collect more buffered source.
     bool result = true;
 
     CompileOptions options(cx);
     frontend::UsedNameTracker usedNames(cx);
     if (!usedNames.init())
         return false;
-    frontend::Parser<frontend::FullParseHandler> parser(cx, cx->tempLifoAlloc(),
-                                                        options, chars, length,
-                                                        /* foldConstants = */ true,
-                                                        usedNames, nullptr, nullptr);
+    frontend::Parser<frontend::FullParseHandler, char16_t> parser(cx, cx->tempLifoAlloc(),
+                                                                  options, chars, length,
+                                                                  /* foldConstants = */ true,
+                                                                  usedNames, nullptr, nullptr);
     JS::WarningReporter older = JS::SetWarningReporter(cx, nullptr);
     if (!parser.checkOptions() || !parser.parse()) {
         // We ran into an error. If it was because we ran out of source, we
         // return false so our caller knows to try to collect more buffered
         // source.
         if (parser.isUnexpectedEOF())
             result = false;
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -4407,18 +4407,19 @@ Parse(JSContext* cx, unsigned argc, Valu
     const char16_t* chars = stableChars.twoByteRange().begin().get();
 
     CompileOptions options(cx);
     options.setIntroductionType("js shell parse")
            .setFileAndLine("<string>", 1);
     UsedNameTracker usedNames(cx);
     if (!usedNames.init())
         return false;
-    Parser<FullParseHandler> parser(cx, cx->tempLifoAlloc(), options, chars, length,
-                                    /* foldConstants = */ true, usedNames, nullptr, nullptr);
+    Parser<FullParseHandler, char16_t> parser(cx, cx->tempLifoAlloc(), options, chars, length,
+                                              /* foldConstants = */ true, usedNames, nullptr,
+                                              nullptr);
     if (!parser.checkOptions())
         return false;
 
     ParseNode* pn = parser.parse();
     if (!pn)
         return false;
 #ifdef DEBUG
     DumpParseTree(pn);
@@ -4457,19 +4458,19 @@ SyntaxParse(JSContext* cx, unsigned argc
     if (!stableChars.initTwoByte(cx, scriptContents))
         return false;
 
     const char16_t* chars = stableChars.twoByteRange().begin().get();
     size_t length = scriptContents->length();
     UsedNameTracker usedNames(cx);
     if (!usedNames.init())
         return false;
-    Parser<frontend::SyntaxParseHandler> parser(cx, cx->tempLifoAlloc(),
-                                                options, chars, length, false,
-                                                usedNames, nullptr, nullptr);
+    Parser<frontend::SyntaxParseHandler, char16_t> parser(cx, cx->tempLifoAlloc(),
+                                                          options, chars, length, false,
+                                                          usedNames, nullptr, nullptr);
     if (!parser.checkOptions())
         return false;
 
     bool succeeded = parser.parse();
     if (cx->isExceptionPending())
         return false;
 
     if (!succeeded && !parser.hadAbortedSyntaxParse()) {
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -5297,20 +5297,21 @@ Debugger::isCompilableUnit(JSContext* cx
         return false;
 
     bool result = true;
 
     CompileOptions options(cx);
     frontend::UsedNameTracker usedNames(cx);
     if (!usedNames.init())
         return false;
-    frontend::Parser<frontend::FullParseHandler> parser(cx, cx->tempLifoAlloc(),
-                                                        options, chars.twoByteChars(),
-                                                        length, /* foldConstants = */ true,
-                                                        usedNames, nullptr, nullptr);
+    frontend::Parser<frontend::FullParseHandler, char16_t> parser(cx, cx->tempLifoAlloc(),
+                                                                  options, chars.twoByteChars(),
+                                                                  length,
+                                                                  /* foldConstants = */ true,
+                                                                  usedNames, nullptr, nullptr);
     JS::WarningReporter older = JS::SetWarningReporter(cx, nullptr);
     if (!parser.checkOptions() || !parser.parse()) {
         // We ran into an error. If it was because we ran out of memory we report
         // it in the usual way.
         if (cx->isThrowingOutOfMemory()) {
             JS::SetWarningReporter(cx, older);
             return false;
         }
--- a/js/src/wasm/AsmJS.h
+++ b/js/src/wasm/AsmJS.h
@@ -19,22 +19,26 @@
 #ifndef asmjs_asmjs_h
 #define asmjs_asmjs_h
 
 #include "NamespaceImports.h"
 
 namespace js {
 
 namespace frontend {
-    template <typename ParseHandler> class Parser;
-    class ParseContext;
-    class FullParseHandler;
-    class ParseNode;
+
+class ParseContext;
+class ParseNode;
+
+template <template <typename CharT> class ParseHandler, typename CharT> class Parser;
+template <typename CharT> class FullParseHandler;
+
 }
-typedef frontend::Parser<frontend::FullParseHandler> AsmJSParser;
+
+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
 // as asm.js. In this case, the parser.tokenStream has been advanced an
 // indeterminate amount and the entire function should be reparsed from the
 // beginning.