Bug 1424394 - Split TokenStream in Parser across Parser and ParserBase. r=arai
authorJeff Walden <jwalden@mit.edu>
Tue, 05 Dec 2017 23:58:47 -0800
changeset 396260 0559a4899c440b7c9a6666715cd3a1836fc4128f
parent 396259 5d3190b2b1bd77bdf476fa9c075be1829d8447d9
child 396261 9f31a702d5dea600cca630b3a5bdd755a27b2c3d
push id33087
push userdluca@mozilla.com
push dateThu, 14 Dec 2017 09:54:55 +0000
treeherdermozilla-central@8062887ff0d9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersarai
bugs1424394
milestone59.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1424394 - Split TokenStream in Parser across Parser and ParserBase. r=arai
js/src/builtin/ModuleObject.cpp
js/src/builtin/ModuleObject.h
js/src/builtin/ReflectParse.cpp
js/src/devtools/rootAnalysis/annotations.js
js/src/frontend/BytecodeCompiler.cpp
js/src/frontend/EitherParser.h
js/src/frontend/FullParseHandler.h
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/frontend/SharedContext.h
js/src/frontend/TokenStream.cpp
js/src/frontend/TokenStream.h
js/src/irregexp/RegExpParser.cpp
js/src/irregexp/RegExpParser.h
js/src/jsapi.cpp
js/src/vm/RegExpObject.cpp
js/src/vm/RegExpObject.h
js/src/vm/SelfHosting.cpp
js/src/vm/StructuredClone.cpp
js/src/wasm/AsmJS.cpp
--- a/js/src/builtin/ModuleObject.cpp
+++ b/js/src/builtin/ModuleObject.cpp
@@ -1199,17 +1199,17 @@ GlobalObject::initModuleProto(JSContext*
 #undef DEFINE_GETTER_FUNCTIONS
 #undef DEFINE_STRING_ACCESSOR_METHOD
 #undef DEFINE_ARRAY_SLOT_ACCESSOR
 
 ///////////////////////////////////////////////////////////////////////////
 // ModuleBuilder
 
 ModuleBuilder::ModuleBuilder(JSContext* cx, HandleModuleObject module,
-                             const frontend::TokenStream& tokenStream)
+                             const frontend::TokenStreamAnyChars& tokenStream)
   : cx_(cx),
     module_(cx, module),
     tokenStream_(tokenStream),
     requestedModuleSpecifiers_(cx, AtomSet(cx)),
     requestedModules_(cx, RequestedModuleVector(cx)),
     importedBoundNames_(cx, AtomVector(cx)),
     importEntries_(cx, ImportEntryVector(cx)),
     exportEntries_(cx, ExportEntryVector(cx)),
--- a/js/src/builtin/ModuleObject.h
+++ b/js/src/builtin/ModuleObject.h
@@ -21,17 +21,17 @@
 
 namespace js {
 
 class ModuleEnvironmentObject;
 class ModuleObject;
 
 namespace frontend {
 class ParseNode;
-class TokenStream;
+class TokenStreamAnyChars;
 } /* namespace frontend */
 
 typedef Rooted<ModuleObject*> RootedModuleObject;
 typedef Handle<ModuleObject*> HandleModuleObject;
 typedef Rooted<ModuleEnvironmentObject*> RootedModuleEnvironmentObject;
 typedef Handle<ModuleEnvironmentObject*> HandleModuleEnvironmentObject;
 
 class ImportEntryObject : public NativeObject
@@ -345,17 +345,17 @@ class ModuleObject : public NativeObject
 };
 
 // Process a module's parse tree to collate the import and export data used when
 // creating a ModuleObject.
 class MOZ_STACK_CLASS ModuleBuilder
 {
   public:
     explicit ModuleBuilder(JSContext* cx, HandleModuleObject module,
-                           const frontend::TokenStream& tokenStream);
+                           const frontend::TokenStreamAnyChars& tokenStream);
     bool init();
 
     bool processImport(frontend::ParseNode* pn);
     bool processExport(frontend::ParseNode* pn);
     bool processExportFrom(frontend::ParseNode* pn);
 
     bool hasExportedName(JSAtom* name) const;
 
@@ -375,17 +375,17 @@ class MOZ_STACK_CLASS ModuleBuilder
     using RootedAtomVector = JS::Rooted<AtomVector>;
     using RootedImportEntryVector = JS::Rooted<ImportEntryVector>;
     using RootedExportEntryVector = JS::Rooted<ExportEntryVector>;
     using RootedRequestedModuleVector = JS::Rooted<RequestedModuleVector>;
     using RootedAtomSet = JS::Rooted<AtomSet>;
 
     JSContext* cx_;
     RootedModuleObject module_;
-    const frontend::TokenStream& tokenStream_;
+    const frontend::TokenStreamAnyChars& tokenStream_;
     RootedAtomSet requestedModuleSpecifiers_;
     RootedRequestedModuleVector requestedModules_;
     RootedAtomVector importedBoundNames_;
     RootedImportEntryVector importEntries_;
     RootedExportEntryVector exportEntries_;
     RootedExportEntryVector localExportEntries_;
     RootedExportEntryVector indirectExportEntries_;
     RootedExportEntryVector starExportEntries_;
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -1732,17 +1732,17 @@ class ASTSerializer
     {}
 
     bool init(HandleObject userobj) {
         return builder.init(userobj);
     }
 
     void setParser(Parser<FullParseHandler, char16_t>* p) {
         parser = p;
-        builder.setTokenStream(&p->tokenStream);
+        builder.setTokenStream(&p->anyChars);
     }
 
     bool program(ParseNode* pn, MutableHandleValue dst);
 };
 
 } /* anonymous namespace */
 
 AssignmentOperator
@@ -1909,17 +1909,17 @@ ASTSerializer::blockStatement(ParseNode*
     NodeVector stmts(cx);
     return statements(pn, stmts) &&
            builder.blockStatement(stmts, &pn->pn_pos, dst);
 }
 
 bool
 ASTSerializer::program(ParseNode* pn, MutableHandleValue dst)
 {
-    MOZ_ASSERT(parser->tokenStream.srcCoords.lineNum(pn->pn_pos.begin) == lineno);
+    MOZ_ASSERT(parser->anyChars.srcCoords.lineNum(pn->pn_pos.begin) == lineno);
 
     NodeVector stmts(cx);
     return statements(pn, stmts) &&
            builder.program(stmts, &pn->pn_pos, dst);
 }
 
 bool
 ASTSerializer::sourceElement(ParseNode* pn, MutableHandleValue dst)
@@ -3486,17 +3486,17 @@ reflect_parse(JSContext* cx, uint32_t ar
     } else {
         if (!GlobalObject::ensureModulePrototypesCreated(cx, cx->global()))
             return false;
 
         Rooted<ModuleObject*> module(cx, ModuleObject::create(cx));
         if (!module)
             return false;
 
-        ModuleBuilder builder(cx, module, parser.tokenStream);
+        ModuleBuilder builder(cx, module, parser.anyChars);
         if (!builder.init())
             return false;
 
         ModuleSharedContext modulesc(cx, module, &cx->global()->emptyGlobalScope(), builder);
         pn = parser.moduleBody(&modulesc);
         if (!pn)
             return false;
 
--- a/js/src/devtools/rootAnalysis/annotations.js
+++ b/js/src/devtools/rootAnalysis/annotations.js
@@ -327,16 +327,17 @@ function extraRootedPointers()
     return [
         'ModuleValidator',
         'JSErrorResult',
         'WrappableJSErrorResult',
 
         // These are not actually rooted, but are only used in the context of
         // AutoKeepAtoms.
         'js::frontend::TokenStream',
+        'js::frontend::TokenStreamAnyChars',
 
         'mozilla::ErrorResult',
         'mozilla::IgnoredErrorResult',
         'mozilla::dom::binding_detail::FastErrorResult',
     ];
 }
 
 function isRootedGCPointerTypeName(name)
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -273,17 +273,17 @@ BytecodeCompiler::emplaceEmitter(Maybe<B
 bool
 BytecodeCompiler::handleParseFailure(const Directives& newDirectives)
 {
     if (parser->hadAbortedSyntaxParse()) {
         // Hit some unrecoverable ambiguity during an inner syntax parse.
         // Syntax parsing has now been disabled in the parser, so retry
         // the parse.
         parser->clearAbortedSyntaxParse();
-    } else if (parser->tokenStream.hadError() || directives == newDirectives) {
+    } else if (parser->anyChars.hadError() || directives == newDirectives) {
         return false;
     }
 
     parser->tokenStream.seek(startPosition);
 
     // Assignment must be monotonic to prevent reparsing iloops
     MOZ_ASSERT_IF(directives.strict(), newDirectives.strict());
     MOZ_ASSERT_IF(directives.asmJS(), newDirectives.asmJS());
@@ -397,17 +397,17 @@ BytecodeCompiler::compileModule()
     if (!module)
         return nullptr;
 
     if (!createScript())
         return nullptr;
 
     module->init(script);
 
-    ModuleBuilder builder(cx, module, parser->tokenStream);
+    ModuleBuilder builder(cx, module, parser->anyChars);
     if (!builder.init())
         return nullptr;
 
     ModuleSharedContext modulesc(cx, module, enclosingScope, builder);
     ParseNode* pn = parser->moduleBody(&modulesc);
     if (!pn)
         return nullptr;
 
--- a/js/src/frontend/EitherParser.h
+++ b/js/src/frontend/EitherParser.h
@@ -149,17 +149,17 @@ struct TokenStreamReportExtraWarning
 };
 
 // Generic matchers.
 
 struct TokenStreamMatcher
 {
     template<class Parser>
     frontend::TokenStreamAnyChars& match(Parser* parser) {
-        return parser->tokenStream;
+        return parser->anyChars;
     }
 };
 
 struct ScriptSourceMatcher
 {
     template<class Parser>
     ScriptSource* match(Parser* parser) {
         return parser->ss;
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -11,16 +11,19 @@
 #include "mozilla/PodOperations.h"
 
 #include <string.h>
 
 #include "frontend/ParseNode.h"
 #include "frontend/SharedContext.h"
 
 namespace js {
+
+class RegExpObject;
+
 namespace frontend {
 
 // Parse handler used when generating a full parse tree for all code which the
 // parser encounters.
 class FullParseHandler
 {
     ParseNodeAllocator allocator;
 
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -655,35 +655,34 @@ bool
 Parser<ParseHandler, CharT>::warning(unsigned errorNumber, ...)
 {
     va_list args;
     va_start(args, errorNumber);
 
     ErrorMetadata metadata;
     bool result =
         tokenStream.computeErrorMetadata(&metadata, pos().begin) &&
-        tokenStream.compileWarning(Move(metadata), nullptr, JSREPORT_WARNING, errorNumber, args);
+        anyChars.compileWarning(Move(metadata), nullptr, JSREPORT_WARNING, errorNumber, args);
 
     va_end(args);
     return result;
 }
 
 template <class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::warningAt(uint32_t offset, unsigned errorNumber, ...)
 {
     va_list args;
     va_start(args, errorNumber);
 
     ErrorMetadata metadata;
     bool result = tokenStream.computeErrorMetadata(&metadata, offset);
     if (result) {
         result =
-            tokenStream.compileWarning(Move(metadata), nullptr, JSREPORT_WARNING, errorNumber,
-                                       args);
+            anyChars.compileWarning(Move(metadata), nullptr, JSREPORT_WARNING, errorNumber, args);
     }
 
     va_end(args);
     return result;
 }
 
 template <class ParseHandler, typename CharT>
 bool
@@ -745,34 +744,33 @@ Parser<ParseHandler, CharT>::strictModeE
 
 bool
 ParserBase::warningNoOffset(unsigned errorNumber, ...)
 {
     va_list args;
     va_start(args, errorNumber);
 
     ErrorMetadata metadata;
-    tokenStream.computeErrorMetadataNoOffset(&metadata);
+    anyChars.computeErrorMetadataNoOffset(&metadata);
 
     bool result =
-        tokenStream.compileWarning(Move(metadata), nullptr, JSREPORT_WARNING, errorNumber,
-                                   args);
+        anyChars.compileWarning(Move(metadata), nullptr, JSREPORT_WARNING, errorNumber, args);
 
     va_end(args);
     return result;
 }
 
 void
 ParserBase::errorNoOffset(unsigned errorNumber, ...)
 {
     va_list args;
     va_start(args, errorNumber);
 
     ErrorMetadata metadata;
-    tokenStream.computeErrorMetadataNoOffset(&metadata);
+    anyChars.computeErrorMetadataNoOffset(&metadata);
 
     ReportCompileError(context, Move(metadata), nullptr, JSREPORT_ERROR, errorNumber, args);
 
     va_end(args);
 }
 
 #define ABORTED_SYNTAX_PARSE_SENTINEL (SyntaxParser*)(0x1)
 
@@ -836,17 +834,17 @@ Parser<SyntaxParseHandler, char16_t>::ab
 
 ParserBase::ParserBase(JSContext* cx, LifoAlloc& alloc,
                        const ReadOnlyCompileOptions& options,
                        const char16_t* chars, size_t length,
                        bool foldConstants,
                        UsedNameTracker& usedNames)
   : context(cx),
     alloc(alloc),
-    tokenStream(cx, options, chars, length, thisForCtor()),
+    anyChars(cx, options, thisForCtor()),
     traceListHead(nullptr),
     pc(nullptr),
     usedNames(usedNames),
     ss(nullptr),
     keepAtoms(cx),
     foldConstants(foldConstants),
 #ifdef DEBUG
     checkOptionsCalled(false),
@@ -878,16 +876,17 @@ Parser<ParseHandler, CharT>::Parser(JSCo
                                     const CharT* chars, size_t length,
                                     bool foldConstants,
                                     UsedNameTracker& usedNames,
                                     SyntaxParser* syntaxParser,
                                     LazyScript* lazyOuterFunction)
   : ParserBase(cx, alloc, options, chars, length, foldConstants, usedNames),
     AutoGCRooter(cx, PARSER),
     syntaxParser_(syntaxParser),
+    tokenStream(cx, options, chars, length),
     handler(cx, alloc, lazyOuterFunction)
 {
     // The Mozilla specific JSOPTION_EXTRA_WARNINGS option adds extra warnings
     // which are not generated if functions are parsed lazily. Note that the
     // standard "use strict" does not inhibit lazy parsing.
     if (options.extraWarningsOption)
         disableSyntaxParser();
 }
@@ -895,17 +894,17 @@ Parser<ParseHandler, CharT>::Parser(JSCo
 template<class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::checkOptions()
 {
 #ifdef DEBUG
     checkOptionsCalled = true;
 #endif
 
-    return tokenStream.checkOptions();
+    return anyChars.checkOptions();
 }
 
 template <class ParseHandler, typename CharT>
 Parser<ParseHandler, CharT>::~Parser()
 {
     MOZ_ASSERT(checkOptionsCalled);
 }
 
@@ -1089,17 +1088,17 @@ Parser<ParseHandler, CharT>::reportMissi
 {
     auto notes = MakeUnique<JSErrorNotes>();
     if (!notes) {
         ReportOutOfMemory(pc->sc()->context);
         return;
     }
 
     uint32_t line, column;
-    tokenStream.srcCoords.lineNumAndColumnIndex(openedPos, &line, &column);
+    anyChars.srcCoords.lineNumAndColumnIndex(openedPos, &line, &column);
 
     const size_t MaxWidth = sizeof("4294967295");
     char columnNumber[MaxWidth];
     SprintfLiteral(columnNumber, "%" PRIu32, column);
     char lineNumber[MaxWidth];
     SprintfLiteral(lineNumber, "%" PRIu32, line);
 
     if (!notes->addNoteASCII(pc->sc()->context,
@@ -1129,17 +1128,17 @@ Parser<ParseHandler, CharT>::reportRedec
 
     auto notes = MakeUnique<JSErrorNotes>();
     if (!notes) {
         ReportOutOfMemory(pc->sc()->context);
         return;
     }
 
     uint32_t line, column;
-    tokenStream.srcCoords.lineNumAndColumnIndex(prevPos, &line, &column);
+    anyChars.srcCoords.lineNumAndColumnIndex(prevPos, &line, &column);
 
     const size_t MaxWidth = sizeof("4294967295");
     char columnNumber[MaxWidth];
     SprintfLiteral(columnNumber, "%" PRIu32, column);
     char lineNumber[MaxWidth];
     SprintfLiteral(lineNumber, "%" PRIu32, line);
 
     if (!notes->addNoteASCII(pc->sc()->context,
@@ -2561,20 +2560,20 @@ Parser<FullParseHandler, char16_t>::stan
     if (generatorKind == GeneratorKind::Generator) {
         MOZ_ASSERT(tt == TOK_MUL);
         if (!tokenStream.getToken(&tt))
             return null();
     }
 
     // Skip function name, if present.
     if (TokenKindIsPossibleIdentifierName(tt)) {
-        MOZ_ASSERT(tokenStream.currentName() == fun->explicitName());
+        MOZ_ASSERT(anyChars.currentName() == fun->explicitName());
     } else {
         MOZ_ASSERT(fun->explicitName() == nullptr);
-        tokenStream.ungetToken();
+        anyChars.ungetToken();
     }
 
     Node fn = handler.newFunctionStatement(pos());
     if (!fn)
         return null();
 
     ParseNode* argsbody = handler.newList(PNK_PARAMSBODY, pos());
     if (!argsbody)
@@ -2870,21 +2869,21 @@ Parser<ParseHandler, CharT>::matchOrInse
          *   await f();
          *        ^
          *        |
          *        tried to insert semicolon here
          *
          * Detect this situation and throw an understandable error.  Otherwise
          * we'd throw a confusing "unexpected token: (unexpected token)" error.
          */
-        if (!pc->isAsync() && tokenStream.currentToken().type == TOK_AWAIT) {
+        if (!pc->isAsync() && anyChars.currentToken().type == TOK_AWAIT) {
             error(JSMSG_AWAIT_OUTSIDE_ASYNC);
             return false;
         }
-        if (!yieldExpressionsSupported() && tokenStream.currentToken().type == TOK_YIELD) {
+        if (!yieldExpressionsSupported() && anyChars.currentToken().type == TOK_YIELD) {
             error(JSMSG_YIELD_OUTSIDE_GENERATOR);
             return false;
         }
 
         /* Advance the scanner for proper error location reporting. */
         tokenStream.consumeKnownToken(tt, TokenStream::Operand);
         error(JSMSG_UNEXPECTED_TOKEN_NO_EXPECT, TokenKindToDesc(tt));
         return false;
@@ -2964,23 +2963,23 @@ Parser<ParseHandler, CharT>::functionArg
     //         ^
     //
     //   function f(a) {}
     //             ^
     //
     // TokenStream::Operand for the following case:
     //   a => 1
     //   ^
-    TokenStream::Modifier firstTokenModifier = TokenStream::None;
+    Modifier firstTokenModifier = TokenStream::None;
 
     // Modifier for the the first token in each argument.
     // can be changed to TokenStream::None for the following case:
     //   async a => 1
     //         ^
-    TokenStream::Modifier argModifier = TokenStream::Operand;
+    Modifier argModifier = TokenStream::Operand;
     if (kind == Arrow) {
         TokenKind tt;
         // In async function, the first token after `async` is already gotten
         // with TokenStream::None.
         // In sync function, the first token is already gotten with
         // TokenStream::Operand.
         firstTokenModifier = funbox->isAsync() ? TokenStream::None : TokenStream::Operand;
         if (!tokenStream.peekToken(&tt, firstTokenModifier))
@@ -3000,17 +2999,17 @@ Parser<ParseHandler, CharT>::functionArg
             error(kind == Arrow ? JSMSG_BAD_ARROW_ARGS : JSMSG_PAREN_BEFORE_FORMAL);
             return false;
         }
 
         firstTokenPos = pos();
 
         // Record the start of function source (for FunctionToString). If we
         // are parenFreeArrow, we will set this below, after consuming the NAME.
-        funbox->setStart(tokenStream);
+        funbox->setStart(anyChars);
     } else {
         // When delazifying, we may not have a current token and pos() is
         // garbage. In that case, substitute the first token's position.
         if (!tokenStream.peekTokenPos(&firstTokenPos, firstTokenModifier))
             return false;
     }
 
     Node argsbody = handler.newList(PNK_PARAMSBODY, firstTokenPos);
@@ -3103,17 +3102,17 @@ Parser<ParseHandler, CharT>::functionArg
 
               default: {
                 if (!TokenKindIsPossibleIdentifier(tt)) {
                     error(JSMSG_MISSING_FORMAL);
                     return false;
                 }
 
                 if (parenFreeArrow)
-                    funbox->setStart(tokenStream);
+                    funbox->setStart(anyChars);
 
                 RootedPropertyName name(context, bindingIdentifier(yieldHandling));
                 if (!name)
                     return false;
 
                 if (!notePositionalFormalParameter(funcpn, name, pos().begin,
                                                    disallowDuplicateParams, &duplicatedParam))
                 {
@@ -3385,32 +3384,32 @@ Parser<ParseHandler, CharT>::functionDef
 
     // Speculatively parse using the directives of the parent parsing context.
     // If a directive is encountered (e.g., "use strict") that changes how the
     // function should have been parsed, we backup and reparse with the new set
     // of directives.
     Directives directives(pc);
     Directives newDirectives = directives;
 
-    TokenStream::Position start(keepAtoms);
+    typename TokenStream::Position start(keepAtoms);
     tokenStream.tell(&start);
 
     // Parse the inner function. The following is a loop as we may attempt to
     // reparse a function due to failed syntax parsing and encountering new
     // "use foo" directives.
     while (true) {
         if (trySyntaxParseInnerFunction(pn, fun, toStringStart, inHandling, yieldHandling, kind,
                                         generatorKind, asyncKind, tryAnnexB, directives,
                                         &newDirectives))
         {
             break;
         }
 
         // Return on error.
-        if (tokenStream.hadError() || directives == newDirectives)
+        if (anyChars.hadError() || directives == newDirectives)
             return null();
 
         // Assignment must be monotonic to prevent infinitely attempting to
         // reparse.
         MOZ_ASSERT_IF(directives.strict(), newDirectives.strict());
         MOZ_ASSERT_IF(directives.asmJS(), newDirectives.asmJS());
         directives = newDirectives;
 
@@ -3450,19 +3449,19 @@ Parser<FullParseHandler, char16_t>::tryS
         }
 
         if (!syntaxParser_)
             break;
 
         UsedNameTracker::RewindToken token = usedNames.getRewindToken();
 
         // Move the syntax parser to the current position in the stream.
-        TokenStream::Position position(keepAtoms);
+        typename TokenStream::Position position(keepAtoms);
         tokenStream.tell(&position);
-        if (!syntaxParser_->tokenStream.seek(position, tokenStream))
+        if (!syntaxParser_->tokenStream.seek(position, anyChars))
             return false;
 
         // Make a FunctionBox before we enter the syntax parser, because |pn|
         // still expects a FunctionBox to be attached to it during BCE, and
         // the syntax parser cannot attach one to it.
         FunctionBox* funbox = newFunctionBox(pn, fun, toStringStart, inheritedDirectives,
                                              generatorKind, asyncKind);
         if (!funbox)
@@ -3483,21 +3482,21 @@ Parser<FullParseHandler, char16_t>::tryS
                               !syntaxParser_->context->isExceptionPending());
                 break;
             }
             return false;
         }
 
         // Advance this parser over tokens processed by the syntax parser.
         syntaxParser_->tokenStream.tell(&position);
-        if (!tokenStream.seek(position, syntaxParser_->tokenStream))
+        if (!tokenStream.seek(position, syntaxParser_->anyChars))
             return false;
 
         // Update the end position of the parse node.
-        pn->pn_pos.end = tokenStream.currentToken().pos.end;
+        pn->pn_pos.end = anyChars.currentToken().pos.end;
 
         // Append possible Annex B function box only upon successfully parsing.
         if (tryAnnexB && !pc->innermostScope()->addPossibleAnnexBFunctionBox(pc, funbox))
             return false;
 
         return true;
     } while (false);
 
@@ -3619,23 +3618,23 @@ Parser<FullParseHandler, char16_t>::stan
     funbox->initFromLazyFunction();
 
     Directives newDirectives = directives;
     SourceParseContext funpc(this, funbox, &newDirectives);
     if (!funpc.init())
         return null();
 
     // Our tokenStream has no current token, so pn's position is garbage.
-    // Substitute the position of the first token in our source. If the function
-    // is a not-async arrow, use TokenStream::Operand to keep
+    // Substitute the position of the first token in our source.  If the
+    // function is a not-async arrow, use TokenStream::Operand to keep
     // verifyConsistentModifier from complaining (we will use
     // TokenStream::Operand in functionArguments).
-    TokenStream::Modifier modifier = (fun->isArrow() &&
-                                      asyncKind == FunctionAsyncKind::SyncFunction)
-                                     ? TokenStream::Operand : TokenStream::None;
+    Modifier modifier = (fun->isArrow() && asyncKind == FunctionAsyncKind::SyncFunction)
+                        ? TokenStream::Operand
+                        : TokenStream::None;
     if (!tokenStream.peekTokenPos(&pn->pn_pos, modifier))
         return null();
 
     YieldHandling yieldHandling = GetYieldHandling(generatorKind);
     FunctionSyntaxKind syntaxKind = Statement;
     if (fun->isClassConstructor())
         syntaxKind = ClassConstructor;
     else if (fun->isMethod())
@@ -3731,17 +3730,17 @@ Parser<ParseHandler, CharT>::functionFor
             if (!warnOnceAboutExprClosure())
                 return false;
 #else
             error(JSMSG_CURLY_BEFORE_BODY);
             return false;
 #endif
         }
 
-        tokenStream.ungetToken();
+        anyChars.ungetToken();
         bodyType = ExpressionBody;
         funbox->setIsExprBody();
     } else {
         openedPos = pos().begin;
     }
 
     // Arrow function parameters inherit yieldHandling from the enclosing
     // context, but the arrow body doesn't. E.g. in |(a = yield) => yield|,
@@ -3779,33 +3778,33 @@ Parser<ParseHandler, CharT>::functionFor
             // because of different context.
             // It should already be checked before this point.
             nameYieldHandling = YieldIsName;
         }
 
         // We already use the correct await-handling at this point, therefore
         // we don't need call AutoAwaitIsKeyword here.
 
-        uint32_t nameOffset = handler.getFunctionNameOffset(pn, tokenStream);
+        uint32_t nameOffset = handler.getFunctionNameOffset(pn, anyChars);
         if (!checkBindingIdentifier(propertyName, nameOffset, nameYieldHandling))
             return false;
     }
 
     if (bodyType == StatementListBody) {
         MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand,
                                          reportMissingClosing(JSMSG_CURLY_AFTER_BODY,
                                                               JSMSG_CURLY_OPENED, openedPos));
-        funbox->setEnd(tokenStream);
+        funbox->setEnd(anyChars);
     } else {
 #if !JS_HAS_EXPR_CLOSURES
         MOZ_ASSERT(kind == Arrow);
 #endif
-        if (tokenStream.hadError())
+        if (anyChars.hadError())
             return false;
-        funbox->setEnd(tokenStream);
+        funbox->setEnd(anyChars);
         if (kind == Statement && !matchOrInsertSemicolon())
             return false;
     }
 
     if (IsMethodDefinitionKind(kind) && pc->superScopeNeedsHomeObject())
         funbox->setNeedsHomeObject();
 
     if (!finishFunction(isStandaloneFunction))
@@ -3819,17 +3818,17 @@ Parser<ParseHandler, CharT>::functionFor
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::functionStmt(uint32_t toStringStart, YieldHandling yieldHandling,
                                           DefaultHandling defaultHandling,
                                           FunctionAsyncKind asyncKind)
 {
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_FUNCTION));
 
     // In sloppy mode, Annex B.3.2 allows labelled function declarations.
     // Otherwise it's a parse error.
     ParseContext::Statement* declaredInStmt = pc->innermostStatement();
     if (declaredInStmt && declaredInStmt->kind() == StatementKind::Label) {
         MOZ_ASSERT(!pc->sc()->strict(),
                    "labeled functions shouldn't be parsed in strict mode");
 
@@ -3858,17 +3857,17 @@ Parser<ParseHandler, CharT>::functionStm
 
     RootedPropertyName name(context);
     if (TokenKindIsPossibleIdentifier(tt)) {
         name = bindingIdentifier(yieldHandling);
         if (!name)
             return null();
     } else if (defaultHandling == AllowDefaultName) {
         name = context->names().default_;
-        tokenStream.ungetToken();
+        anyChars.ungetToken();
     } else {
         /* Unnamed function expressions are forbidden in statement context. */
         error(JSMSG_UNNAMED_FUNCTION_STMT);
         return null();
     }
 
     // Note the declared name and check for early errors.
     DeclarationKind kind;
@@ -3910,17 +3909,17 @@ Parser<ParseHandler, CharT>::functionStm
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::functionExpr(uint32_t toStringStart,
                                           ExpressionClosure expressionClosureHandling,
                                           InvokedPrediction invoked,
                                           FunctionAsyncKind asyncKind)
 {
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_FUNCTION));
 
     AutoAwaitIsKeyword<Parser> awaitIsKeyword(this, GetAwaitHandling(asyncKind));
     GeneratorKind generatorKind = GeneratorKind::NotGenerator;
     TokenKind tt;
     if (!tokenStream.getToken(&tt))
         return null();
 
     if (tt == TOK_MUL) {
@@ -3932,17 +3931,17 @@ Parser<ParseHandler, CharT>::functionExp
     YieldHandling yieldHandling = GetYieldHandling(generatorKind);
 
     RootedPropertyName name(context);
     if (TokenKindIsPossibleIdentifier(tt)) {
         name = bindingIdentifier(yieldHandling);
         if (!name)
             return null();
     } else {
-        tokenStream.ungetToken();
+        anyChars.ungetToken();
     }
 
     Node pn = handler.newFunctionExpression(pos());
     if (!pn)
         return null();
 
     if (invoked)
         pn = handler.setLikelyIIFE(pn);
@@ -4093,17 +4092,17 @@ Parser<ParseHandler, CharT>::maybeParseD
 
             // We're going to be in strict mode. Note that this scope explicitly
             // had "use strict";
             pc->sc()->setExplicitUseStrict();
             if (!pc->sc()->strict()) {
                 // We keep track of the one possible strict violation that could
                 // occur in the directive prologue -- octal escapes -- and
                 // complain now.
-                if (tokenStream.sawOctalEscape()) {
+                if (anyChars.sawOctalEscape()) {
                     error(JSMSG_DEPRECATED_OCTAL);
                     return false;
                 }
                 pc->sc()->strictScript = true;
             }
         } else if (directive == context->names().useAsm) {
             if (pc->isFunctionBox())
                 return asmJS(list);
@@ -4121,24 +4120,24 @@ Parser<ParseHandler, CharT>::statementLi
         return null();
 
     Node pn = handler.newStatementList(pos());
     if (!pn)
         return null();
 
     bool canHaveDirectives = pc->atBodyLevel();
     if (canHaveDirectives)
-        tokenStream.clearSawOctalEscape();
+        anyChars.clearSawOctalEscape();
     bool afterReturn = false;
     bool warnedAboutStatementsAfterReturn = false;
     uint32_t statementBegin = 0;
     for (;;) {
         TokenKind tt = TOK_EOF;
         if (!tokenStream.peekToken(&tt, TokenStream::Operand)) {
-            if (tokenStream.isEOF())
+            if (anyChars.isEOF())
                 isUnexpectedEOF_ = true;
             return null();
         }
         if (tt == TOK_EOF || tt == TOK_RC) {
             TokenPos pos;
             if (!tokenStream.peekTokenPos(&pos, TokenStream::Operand)) {
                 return null();
             }
@@ -4146,17 +4145,17 @@ Parser<ParseHandler, CharT>::statementLi
             break;
         }
         if (afterReturn) {
             if (!tokenStream.peekOffset(&statementBegin, TokenStream::Operand))
                 return null();
         }
         Node next = statementListItem(yieldHandling, canHaveDirectives);
         if (!next) {
-            if (tokenStream.isEOF())
+            if (anyChars.isEOF())
                 isUnexpectedEOF_ = true;
             return null();
         }
         if (!warnedAboutStatementsAfterReturn) {
             if (afterReturn) {
                 if (!handler.isStatementPermittedAfterReturnStatement(next)) {
                     if (!warningAt(statementBegin, JSMSG_STMT_AFTER_RETURN))
                         return null();
@@ -4374,17 +4373,17 @@ Parser<ParseHandler, CharT>::PossibleErr
     transferErrorTo(ErrorKind::Expression, other);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::bindingInitializer(Node lhs, DeclarationKind kind,
                                                 YieldHandling yieldHandling)
 {
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_ASSIGN));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_ASSIGN));
 
     if (kind == DeclarationKind::FormalParameter)
         pc->functionBox()->hasParameterExprs = true;
 
     Node rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
     if (!rhs)
         return null();
 
@@ -4434,34 +4433,34 @@ Parser<ParseHandler, CharT>::bindingIden
     return bindingIdentifier(kind, yieldHandling);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::objectBindingPattern(DeclarationKind kind,
                                                   YieldHandling yieldHandling)
 {
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LC));
 
     if (!CheckRecursionLimit(context))
         return null();
 
     uint32_t begin = pos().begin;
     Node literal = handler.newObjectLiteral(begin);
     if (!literal)
         return null();
 
     Maybe<DeclarationKind> declKind = Some(kind);
     RootedAtom propAtom(context);
     for (;;) {
         TokenKind tt;
         if (!tokenStream.peekToken(&tt))
             return null();
         if (tt == TOK_RC) {
-            tokenStream.addModifierException(TokenStream::OperandIsNone);
+            anyChars.addModifierException(TokenStream::OperandIsNone);
             break;
         }
 
         if (tt == TOK_TRIPLEDOT) {
             tokenStream.consumeKnownToken(TOK_TRIPLEDOT);
             uint32_t begin = pos().begin;
 
             TokenKind tt;
@@ -4475,17 +4474,17 @@ Parser<ParseHandler, CharT>::objectBindi
 
             Node inner = bindingIdentifier(kind, yieldHandling);
             if (!inner)
                 return null();
 
             if (!handler.addSpreadProperty(literal, begin, inner))
                 return null();
         } else {
-            TokenPos namePos = tokenStream.nextToken().pos;
+            TokenPos namePos = anyChars.nextToken().pos;
 
             PropertyType propType;
             Node propName = propertyName(yieldHandling, declKind, literal, &propType, &propAtom);
             if (!propName)
                 return null();
 
             if (propType == PropertyType::Normal) {
                 // Handle e.g., |var {p: x} = o| and |var {p: x=0} = o|.
@@ -4561,17 +4560,17 @@ Parser<ParseHandler, CharT>::objectBindi
     handler.setEndPosition(literal, pos().end);
     return literal;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::arrayBindingPattern(DeclarationKind kind, YieldHandling yieldHandling)
 {
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LB));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LB));
 
     if (!CheckRecursionLimit(context))
         return null();
 
     uint32_t begin = pos().begin;
     Node literal = handler.newArrayLiteral(begin);
     if (!literal)
         return null();
@@ -4583,18 +4582,18 @@ Parser<ParseHandler, CharT>::arrayBindin
              return null();
          }
 
          TokenKind tt;
          if (!tokenStream.getToken(&tt))
              return null();
 
          if (tt == TOK_RB) {
-             tokenStream.ungetToken();
-             tokenStream.addModifierException(TokenStream::OperandIsNone);
+             anyChars.ungetToken();
+             anyChars.addModifierException(TokenStream::OperandIsNone);
              break;
          }
 
          if (tt == TOK_COMMA) {
              if (!handler.addElision(literal, pos()))
                  return null();
          } else if (tt == TOK_TRIPLEDOT) {
              uint32_t begin = pos().begin;
@@ -4651,17 +4650,17 @@ Parser<ParseHandler, CharT>::arrayBindin
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::destructuringDeclaration(DeclarationKind kind,
                                                       YieldHandling yieldHandling,
                                                       TokenKind tt)
 {
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(tt));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(tt));
     MOZ_ASSERT(tt == TOK_LB || tt == TOK_LC);
 
     return tt == TOK_LB
            ? arrayBindingPattern(kind, yieldHandling)
            : objectBindingPattern(kind, yieldHandling);
 }
 
 template <class ParseHandler, typename CharT>
@@ -4685,17 +4684,17 @@ Parser<ParseHandler, CharT>::destructuri
     }
     return res;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::blockStatement(YieldHandling yieldHandling, unsigned errorNumber)
 {
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LC));
     uint32_t openedPos = pos().begin;
 
     ParseContext::Statement stmt(pc, StatementKind::Block);
     ParseContext::Scope scope(this);
     if (!scope.init(pc))
         return null();
 
     Node list = statementList(yieldHandling);
@@ -4724,18 +4723,18 @@ Parser<ParseHandler, CharT>::expressionA
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::declarationPattern(DeclarationKind declKind, TokenKind tt,
                                                 bool initialDeclaration,
                                                 YieldHandling yieldHandling,
                                                 ParseNodeKind* forHeadKind,
                                                 Node* forInOrOfExpression)
 {
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LB) ||
-               tokenStream.isCurrentTokenType(TOK_LC));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LB) ||
+               anyChars.isCurrentTokenType(TOK_LC));
 
     Node pattern = destructuringDeclaration(declKind, yieldHandling, tt);
     if (!pattern)
         return null();
 
     if (initialDeclaration && forHeadKind) {
         bool isForIn, isForOf;
         if (!matchInOrOf(&isForIn, &isForOf))
@@ -4778,17 +4777,17 @@ template <class ParseHandler, typename C
 bool
 Parser<ParseHandler, CharT>::initializerInNameDeclaration(Node binding,
                                                           DeclarationKind declKind,
                                                           bool initialDeclaration,
                                                           YieldHandling yieldHandling,
                                                           ParseNodeKind* forHeadKind,
                                                           Node* forInOrOfExpression)
 {
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_ASSIGN));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_ASSIGN));
 
     uint32_t initializerOffset;
     if (!tokenStream.peekOffset(&initializerOffset, TokenStream::Operand))
         return false;
 
     Node initializer = assignExpr(forHeadKind ? InProhibited : InAllowed,
                                   yieldHandling, TripledotProhibited);
     if (!initializer)
@@ -5017,17 +5016,17 @@ Parser<FullParseHandler, char16_t>::name
             if (tt == TOK_RC)
                 break;
 
             if (!TokenKindIsPossibleIdentifierName(tt)) {
                 error(JSMSG_NO_IMPORT_NAME);
                 return false;
             }
 
-            Rooted<PropertyName*> importName(context, tokenStream.currentName());
+            Rooted<PropertyName*> importName(context, anyChars.currentName());
             TokenPos importNamePos = pos();
 
             bool matched;
             if (!tokenStream.matchToken(&matched, TOK_AS))
                 return null();
 
             if (matched) {
                 TokenKind afterAs;
@@ -5127,17 +5126,17 @@ Parser<SyntaxParseHandler, char16_t>::na
     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
     return false;
 }
 
 template<>
 ParseNode*
 Parser<FullParseHandler, char16_t>::importDeclaration()
 {
-    MOZ_ASSERT(tokenStream.currentToken().type == TOK_IMPORT);
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_IMPORT));
 
     if (!pc->atModuleLevel()) {
         error(JSMSG_IMPORT_DECL_AT_TOP_LEVEL);
         return null();
     }
 
     uint32_t begin = pos().begin;
     TokenKind tt;
@@ -5357,17 +5356,17 @@ Parser<SyntaxParseHandler, char16_t>::pr
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::exportFrom(uint32_t begin, Node specList)
 {
     if (!abortIfSyntaxParser())
         return null();
 
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FROM));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_FROM));
 
     if (!abortIfSyntaxParser())
         return null();
 
     MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
 
     Node moduleSpec = stringLiteral();
     if (!moduleSpec)
@@ -5388,17 +5387,17 @@ Parser<ParseHandler, CharT>::exportFrom(
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::exportBatch(uint32_t begin)
 {
     if (!abortIfSyntaxParser())
         return null();
 
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_MUL));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_MUL));
 
     Node kid = handler.newList(PNK_EXPORT_SPEC_LIST, pos());
     if (!kid)
         return null();
 
     // Handle the form |export *| by adding a special export batch
     // specifier to the list.
     Node exportSpec = handler.newExportBatchSpec(pos());
@@ -5439,17 +5438,17 @@ Parser<SyntaxParseHandler, char16_t>::ch
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::exportClause(uint32_t begin)
 {
     if (!abortIfSyntaxParser())
         return null();
 
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LC));
 
     Node kid = handler.newList(PNK_EXPORT_SPEC_LIST, pos());
     if (!kid)
         return null();
 
     TokenKind tt;
     while (true) {
         // Handle the forms |export {}| and |export { ..., }| (where ... is non
@@ -5460,27 +5459,27 @@ Parser<ParseHandler, CharT>::exportClaus
         if (tt == TOK_RC)
             break;
 
         if (!TokenKindIsPossibleIdentifierName(tt)) {
             error(JSMSG_NO_BINDING_NAME);
             return null();
         }
 
-        Node bindingName = newName(tokenStream.currentName());
+        Node bindingName = newName(anyChars.currentName());
         if (!bindingName)
             return null();
 
         bool foundAs;
         if (!tokenStream.matchToken(&foundAs, TOK_AS))
             return null();
         if (foundAs)
             MUST_MATCH_TOKEN_FUNC(TokenKindIsPossibleIdentifierName, JSMSG_NO_EXPORT_NAME);
 
-        Node exportName = newName(tokenStream.currentName());
+        Node exportName = newName(anyChars.currentName());
         if (!exportName)
             return null();
 
         if (!checkExportedNameForClause(exportName))
             return null();
 
         Node exportSpec = handler.newExportSpec(bindingName, exportName);
         if (!exportSpec)
@@ -5539,17 +5538,17 @@ Parser<ParseHandler, CharT>::exportClaus
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::exportVariableStatement(uint32_t begin)
 {
     if (!abortIfSyntaxParser())
         return null();
 
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_VAR));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_VAR));
 
     Node kid = declarationList(YieldIsName, PNK_VAR);
     if (!kid)
         return null();
     if (!matchOrInsertSemicolon())
         return null();
     if (!checkExportedNamesForDeclaration(kid))
         return null();
@@ -5567,17 +5566,17 @@ Parser<ParseHandler, CharT>::exportVaria
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::exportFunctionDeclaration(uint32_t begin, uint32_t toStringStart,
                                                        FunctionAsyncKind asyncKind /* = SyncFunction */)
 {
     if (!abortIfSyntaxParser())
         return null();
 
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_FUNCTION));
 
     Node kid = functionStmt(toStringStart, YieldIsName, NameRequired, asyncKind);
     if (!kid)
         return null();
 
     if (!checkExportedNameForFunction(kid))
         return null();
 
@@ -5593,17 +5592,17 @@ Parser<ParseHandler, CharT>::exportFunct
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::exportClassDeclaration(uint32_t begin)
 {
     if (!abortIfSyntaxParser())
         return null();
 
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_CLASS));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_CLASS));
 
     Node kid = classDefinition(YieldIsName, ClassStatement, NameRequired);
     if (!kid)
         return null();
 
     if (!checkExportedNameForClass(kid))
         return null();
 
@@ -5620,18 +5619,18 @@ Parser<ParseHandler, CharT>::exportClass
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::exportLexicalDeclaration(uint32_t begin, DeclarationKind kind)
 {
     if (!abortIfSyntaxParser())
         return null();
 
     MOZ_ASSERT(kind == DeclarationKind::Const || kind == DeclarationKind::Let);
-    MOZ_ASSERT_IF(kind == DeclarationKind::Const, tokenStream.isCurrentTokenType(TOK_CONST));
-    MOZ_ASSERT_IF(kind == DeclarationKind::Let, tokenStream.isCurrentTokenType(TOK_LET));
+    MOZ_ASSERT_IF(kind == DeclarationKind::Const, anyChars.isCurrentTokenType(TOK_CONST));
+    MOZ_ASSERT_IF(kind == DeclarationKind::Let, anyChars.isCurrentTokenType(TOK_LET));
 
     Node kid = lexicalDeclaration(YieldIsName, kind);
     if (!kid)
         return null();
     if (!checkExportedNamesForDeclaration(kid))
         return null();
 
     Node node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end));
@@ -5648,17 +5647,17 @@ template <class ParseHandler, typename C
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::exportDefaultFunctionDeclaration(uint32_t begin,
                                                               uint32_t toStringStart,
                                                               FunctionAsyncKind asyncKind /* = SyncFunction */)
 {
     if (!abortIfSyntaxParser())
         return null();
 
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_FUNCTION));
 
     Node kid = functionStmt(toStringStart, YieldIsName, AllowDefaultName, asyncKind);
     if (!kid)
         return null();
 
     Node node = handler.newExportDefaultDeclaration(kid, null(), TokenPos(begin, pos().end));
     if (!node)
         return null();
@@ -5671,17 +5670,17 @@ Parser<ParseHandler, CharT>::exportDefau
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::exportDefaultClassDeclaration(uint32_t begin)
 {
     if (!abortIfSyntaxParser())
         return null();
 
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_CLASS));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_CLASS));
 
     Node kid = classDefinition(YieldIsName, ClassStatement, AllowDefaultName);
     if (!kid)
         return null();
 
     Node node = handler.newExportDefaultDeclaration(kid, null(), TokenPos(begin, pos().end));
     if (!node)
         return null();
@@ -5727,17 +5726,17 @@ Parser<ParseHandler, CharT>::exportDefau
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::exportDefault(uint32_t begin)
 {
     if (!abortIfSyntaxParser())
         return null();
 
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_DEFAULT));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_DEFAULT));
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
 
     if (!checkExportedName(context->names().default_))
         return null();
 
@@ -5752,37 +5751,37 @@ Parser<ParseHandler, CharT>::exportDefau
 
         if (nextSameLine == TOK_FUNCTION) {
             uint32_t toStringStart = pos().begin;
             tokenStream.consumeKnownToken(TOK_FUNCTION);
             return exportDefaultFunctionDeclaration(begin, toStringStart,
                                                     FunctionAsyncKind::AsyncFunction);
         }
 
-        tokenStream.ungetToken();
+        anyChars.ungetToken();
         return exportDefaultAssignExpr(begin);
       }
 
       case TOK_CLASS:
         return exportDefaultClassDeclaration(begin);
 
       default:
-        tokenStream.ungetToken();
+        anyChars.ungetToken();
         return exportDefaultAssignExpr(begin);
     }
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::exportDeclaration()
 {
     if (!abortIfSyntaxParser())
         return null();
 
-    MOZ_ASSERT(tokenStream.currentToken().type == TOK_EXPORT);
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_EXPORT));
 
     if (!pc->atModuleLevel()) {
         error(JSMSG_EXPORT_DECL_AT_TOP_LEVEL);
         return null();
     }
 
     uint32_t begin = pos().begin;
 
@@ -5836,17 +5835,17 @@ Parser<ParseHandler, CharT>::exportDecla
     }
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::expressionStatement(YieldHandling yieldHandling,
                                                  InvokedPrediction invoked)
 {
-    tokenStream.ungetToken();
+    anyChars.ungetToken();
     Node pnexpr = expr(InAllowed, yieldHandling, TripledotProhibited,
                        /* possibleError = */ nullptr, invoked);
     if (!pnexpr)
         return null();
     if (!matchOrInsertSemicolon())
         return null();
     return handler.newExprStatement(pnexpr, pos().end);
 }
@@ -6011,31 +6010,31 @@ Parser<ParseHandler, CharT>::matchInOrOf
 {
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return false;
 
     *isForInp = tt == TOK_IN;
     *isForOfp = tt == TOK_OF;
     if (!*isForInp && !*isForOfp)
-        tokenStream.ungetToken();
+        anyChars.ungetToken();
 
     MOZ_ASSERT_IF(*isForInp || *isForOfp, *isForInp != *isForOfp);
     return true;
 }
 
 template <class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::forHeadStart(YieldHandling yieldHandling,
                                           ParseNodeKind* forHeadKind,
                                           Node* forInitialPart,
                                           Maybe<ParseContext::Scope>& forLoopLexicalScope,
                                           Node* forInOrOfExpression)
 {
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LP));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LP));
 
     TokenKind tt;
     if (!tokenStream.peekToken(&tt, TokenStream::Operand))
         return null();
 
     // Super-duper easy case: |for (;| is a C-style for-loop with no init
     // component.
     if (tt == TOK_SEMI) {
@@ -6074,17 +6073,17 @@ Parser<ParseHandler, CharT>::forHeadStar
         tokenStream.consumeKnownToken(TOK_LET, TokenStream::Operand);
 
         TokenKind next;
         if (!tokenStream.peekToken(&next))
             return false;
 
         parsingLexicalDeclaration = nextTokenContinuesLetDeclaration(next);
         if (!parsingLexicalDeclaration) {
-            tokenStream.ungetToken();
+            anyChars.ungetToken();
             letIsIdentifier = true;
         }
     }
 
     if (parsingLexicalDeclaration) {
         forLoopLexicalScope.emplace(this);
         if (!forLoopLexicalScope->init(pc))
             return null();
@@ -6174,17 +6173,18 @@ Parser<ParseHandler, CharT>::forHeadStar
     *forInOrOfExpression = expressionAfterForInOrOf(*forHeadKind, yieldHandling);
     return *forInOrOfExpression != null();
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::forStatement(YieldHandling yieldHandling)
 {
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_FOR));
+
     uint32_t begin = pos().begin;
 
     ParseContext::Statement stmt(pc, StatementKind::ForLoop);
 
     IteratorKind iterKind = IteratorKind::Sync;
     unsigned iflags = 0;
 
     if (pc->isAsync()) {
@@ -6327,17 +6327,17 @@ Parser<ParseHandler, CharT>::forStatemen
 
     return forLoop;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::switchStatement(YieldHandling yieldHandling)
 {
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_SWITCH));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_SWITCH));
     uint32_t begin = pos().begin;
 
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
 
     Node discriminant = exprInParens(InAllowed, yieldHandling, TripledotProhibited);
     if (!discriminant)
         return null();
 
@@ -6434,17 +6434,17 @@ Parser<ParseHandler, CharT>::switchState
 
     return handler.newSwitchStatement(begin, discriminant, caseList);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::continueStatement(YieldHandling yieldHandling)
 {
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_CONTINUE));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_CONTINUE));
     uint32_t begin = pos().begin;
 
     RootedPropertyName label(context);
     if (!matchLabel(yieldHandling, &label))
         return null();
 
     // Labeled 'continue' statements target the nearest labeled loop
     // statements with the same label. Unlabeled 'continue' statements target
@@ -6492,17 +6492,17 @@ Parser<ParseHandler, CharT>::continueSta
 
     return handler.newContinueStatement(label, TokenPos(begin, pos().end));
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::breakStatement(YieldHandling yieldHandling)
 {
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_BREAK));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_BREAK));
     uint32_t begin = pos().begin;
 
     RootedPropertyName label(context);
     if (!matchLabel(yieldHandling, &label))
         return null();
 
     // Labeled 'break' statements target the nearest labeled statements (could
     // be any kind) with the same label. Unlabeled 'break' statements target
@@ -6532,17 +6532,17 @@ Parser<ParseHandler, CharT>::breakStatem
 
     return handler.newBreakStatement(label, TokenPos(begin, pos().end));
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::returnStatement(YieldHandling yieldHandling)
 {
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_RETURN));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_RETURN));
     uint32_t begin = pos().begin;
 
     MOZ_ASSERT(pc->isFunctionBox());
     pc->functionBox()->usesReturn = true;
 
     // Parse an optional operand.
     //
     // This is ugly, but we don't want to require a semicolon.
@@ -6569,17 +6569,17 @@ Parser<ParseHandler, CharT>::returnState
 
     return handler.newReturnStatement(exprNode, TokenPos(begin, pos().end));
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::yieldExpression(InHandling inHandling)
 {
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_YIELD));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_YIELD));
     uint32_t begin = pos().begin;
 
     MOZ_ASSERT(pc->isGenerator());
     MOZ_ASSERT(pc->isFunctionBox());
 
     pc->lastYieldOffset = begin;
 
     Node exprNode;
@@ -6600,17 +6600,17 @@ Parser<ParseHandler, CharT>::yieldExpres
       case TOK_RC:
       case TOK_RB:
       case TOK_RP:
       case TOK_COLON:
       case TOK_COMMA:
       case TOK_IN:
         // No value.
         exprNode = null();
-        tokenStream.addModifierException(TokenStream::NoneIsOperand);
+        anyChars.addModifierException(TokenStream::NoneIsOperand);
         break;
       case TOK_MUL:
         kind = PNK_YIELD_STAR;
         tokenStream.consumeKnownToken(TOK_MUL, TokenStream::Operand);
         MOZ_FALLTHROUGH;
       default:
         exprNode = assignExpr(inHandling, YieldIsKeyword, TripledotProhibited);
         if (!exprNode)
@@ -6620,17 +6620,17 @@ Parser<ParseHandler, CharT>::yieldExpres
         return handler.newYieldStarExpression(begin, exprNode);
     return handler.newYieldExpression(begin, exprNode);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::withStatement(YieldHandling yieldHandling)
 {
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_WITH));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_WITH));
     uint32_t begin = pos().begin;
 
     // Usually we want the constructs forbidden in strict mode code to be a
     // subset of those that ContextOptions::extraWarnings() warns about, and we
     // use strictModeError directly.  But while 'with' is forbidden in strict
     // mode code, it doesn't even merit a warning in non-strict code.  See
     // https://bugzilla.mozilla.org/show_bug.cgi?id=514576#c1.
     if (pc->sc()->strict()) {
@@ -6685,17 +6685,17 @@ Parser<ParseHandler, CharT>::labeledItem
         if (pc->sc()->strict()) {
             error(JSMSG_FUNCTION_LABEL);
             return null();
         }
 
         return functionStmt(pos().begin, yieldHandling, NameRequired);
     }
 
-    tokenStream.ungetToken();
+    anyChars.ungetToken();
     return statement(yieldHandling);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::labeledStatement(YieldHandling yieldHandling)
 {
     RootedPropertyName label(context, labelIdentifier(yieldHandling));
@@ -6723,17 +6723,17 @@ Parser<ParseHandler, CharT>::labeledStat
 
     return handler.newLabeledStatement(label, pn, begin);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::throwStatement(YieldHandling yieldHandling)
 {
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_THROW));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_THROW));
     uint32_t begin = pos().begin;
 
     /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */
     TokenKind tt = TOK_EOF;
     if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
         return null();
     if (tt == TOK_EOF || tt == TOK_SEMI || tt == TOK_RC) {
         error(JSMSG_MISSING_EXPR_AFTER_THROW);
@@ -6753,17 +6753,17 @@ Parser<ParseHandler, CharT>::throwStatem
 
     return handler.newThrowStatement(throwExpr, TokenPos(begin, pos().end));
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::tryStatement(YieldHandling yieldHandling)
 {
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_TRY));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_TRY));
     uint32_t begin = pos().begin;
 
     /*
      * try nodes are ternary.
      * kid1 is the try statement
      * kid2 is the catch node list or null
      * kid3 is the finally statement
      *
@@ -6897,17 +6897,17 @@ Parser<ParseHandler, CharT>::tryStatemen
         finallyBlock = finishLexicalScope(scope, finallyBlock);
         if (!finallyBlock)
             return null();
 
         MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand,
                                          reportMissingClosing(JSMSG_CURLY_AFTER_FINALLY,
                                                               JSMSG_CURLY_OPENED, openedPos));
     } else {
-        tokenStream.ungetToken();
+        anyChars.ungetToken();
     }
     if (!catchScope && !finallyBlock) {
         error(JSMSG_CATCH_OR_FINALLY);
         return null();
     }
 
     return handler.newTryStatement(begin, innerBlock, catchScope, finallyBlock);
 }
@@ -6988,42 +6988,42 @@ ToAccessorType(PropertyType propType)
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::classDefinition(YieldHandling yieldHandling,
                                              ClassContext classContext,
                                              DefaultHandling defaultHandling)
 {
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_CLASS));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_CLASS));
 
     uint32_t classStartOffset = pos().begin;
     bool savedStrictness = setLocalStrictMode(true);
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt))
         return null();
 
     RootedPropertyName name(context);
     if (TokenKindIsPossibleIdentifier(tt)) {
         name = bindingIdentifier(yieldHandling);
         if (!name)
             return null();
     } else if (classContext == ClassStatement) {
         if (defaultHandling == AllowDefaultName) {
             name = context->names().default_;
-            tokenStream.ungetToken();
+            anyChars.ungetToken();
         } else {
             // Class statements must have a bound name
             error(JSMSG_UNNAMED_CLASS_STMT);
             return null();
         }
     } else {
         // Make sure to put it back, whatever it was
-        tokenStream.ungetToken();
+        anyChars.ungetToken();
     }
 
     // Push a ParseContext::ClassStatement to keep track of the constructor
     // funbox.
     ParseContext::ClassStatement classStmt(pc);
 
     RootedAtom propAtom(context);
 
@@ -7081,19 +7081,19 @@ Parser<ParseHandler, CharT>::classDefini
                 tokenStream.consumeKnownToken(tt);
                 error(JSMSG_UNEXPECTED_TOKEN, "property name", TokenKindToDesc(tt));
                 return null();
             }
 
             if (tt != TOK_LP)
                 isStatic = true;
             else
-                tokenStream.ungetToken();
+                anyChars.ungetToken();
         } else {
-            tokenStream.ungetToken();
+            anyChars.ungetToken();
         }
 
         uint32_t nameOffset;
         if (!tokenStream.peekOffset(&nameOffset))
             return null();
 
         PropertyType propType;
         Node propName = propertyName(yieldHandling, declKind, classMethods, &propType, &propAtom);
@@ -7129,28 +7129,28 @@ Parser<ParseHandler, CharT>::classDefini
             errorAt(nameOffset, JSMSG_BAD_METHOD_DEF);
             return null();
         }
 
         RootedAtom funName(context);
         switch (propType) {
           case PropertyType::GetterNoExpressionClosure:
           case PropertyType::SetterNoExpressionClosure:
-            if (!tokenStream.isCurrentTokenType(TOK_RB)) {
+            if (!anyChars.isCurrentTokenType(TOK_RB)) {
                 funName = prefixAccessorName(propType, propAtom);
                 if (!funName)
                     return null();
             }
             break;
           case PropertyType::Constructor:
           case PropertyType::DerivedConstructor:
             funName = name;
             break;
           default:
-            if (!tokenStream.isCurrentTokenType(TOK_RB))
+            if (!anyChars.isCurrentTokenType(TOK_RB))
                 funName = propAtom;
         }
 
         // Calling toString on constructors need to return the source text for
         // the entire class. The end offset is unknown at this point in
         // parsing and will be amended when class parsing finishes below.
         Node fn = methodDefinition(isConstructor ? classStartOffset : nameOffset,
                                    propType, funName);
@@ -7215,17 +7215,17 @@ Parser<ParseHandler, CharT>::classDefini
     return handler.newClass(nameNode, classHeritage, methodsOrBlock,
                             TokenPos(classStartOffset, classEndOffset));
 }
 
 template <class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::nextTokenContinuesLetDeclaration(TokenKind next)
 {
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LET));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LET));
 
 #ifdef DEBUG
     TokenKind verify;
     MOZ_ALWAYS_TRUE(tokenStream.peekToken(&verify));
     MOZ_ASSERT(next == verify);
 #endif
 
     // Destructuring continues a let declaration.
@@ -7285,17 +7285,17 @@ Parser<ParseHandler, CharT>::statement(Y
       case TOK_SEMI:
         return handler.newEmptyStatement(pos());
 
       // ExpressionStatement[?Yield].
 
       case TOK_YIELD: {
         // Don't use a ternary operator here due to obscure linker issues
         // around using static consts in the arms of a ternary.
-        TokenStream::Modifier modifier;
+        Modifier modifier;
         if (yieldExpressionsSupported())
             modifier = TokenStream::Operand;
         else
             modifier = TokenStream::None;
 
         TokenKind next;
         if (!tokenStream.peekToken(&next, modifier))
             return null();
@@ -7503,28 +7503,28 @@ Parser<ParseHandler, CharT>::statementLi
       case TOK_SEMI:
         return handler.newEmptyStatement(pos());
 
       // ExpressionStatement[?Yield].
       //
       // These should probably be handled by a single ExpressionStatement
       // function in a default, not split up this way.
       case TOK_STRING:
-        if (!canHaveDirectives && tokenStream.currentToken().atom() == context->names().useAsm) {
+        if (!canHaveDirectives && anyChars.currentToken().atom() == context->names().useAsm) {
             if (!abortIfSyntaxParser())
                 return null();
             if (!warning(JSMSG_USE_ASM_DIRECTIVE_FAIL))
                 return null();
         }
         return expressionStatement(yieldHandling);
 
       case TOK_YIELD: {
         // Don't use a ternary operator here due to obscure linker issues
         // around using static consts in the arms of a ternary.
-        TokenStream::Modifier modifier;
+        Modifier modifier;
         if (yieldExpressionsSupported())
             modifier = TokenStream::Operand;
         else
             modifier = TokenStream::None;
 
         TokenKind next;
         if (!tokenStream.peekToken(&next, modifier))
             return null();
@@ -7706,17 +7706,17 @@ Parser<ParseHandler, CharT>::expr(InHand
 
                 if (!tokenStream.peekToken(&tt))
                     return null();
                 if (tt != TOK_ARROW) {
                     error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(TOK_RP));
                     return null();
                 }
 
-                tokenStream.ungetToken();  // put back right paren
+                anyChars.ungetToken();  // put back right paren
                 break;
             }
         }
 
         // Additional calls to assignExpr should not reuse the possibleError
         // which had been passed into the function. Otherwise we would lose
         // information needed to determine whether or not we're dealing with
         // a non-recoverable situation.
@@ -7870,18 +7870,18 @@ Parser<ParseHandler, CharT>::orExpr(InHa
         kindStack[depth] = pnk;
         depth++;
         MOZ_ASSERT(depth <= PRECEDENCE_CLASSES);
     }
 
     // When the next token is no longer a binary operator, it's potentially the
     // start of an expression.  Add a modifier exception so that the next token
     // modifier can be Operand.
-    tokenStream.ungetToken();
-    tokenStream.addModifierException(TokenStream::OperandIsNone);
+    anyChars.ungetToken();
+    anyChars.addModifierException(TokenStream::OperandIsNone);
 
     MOZ_ASSERT(depth == 0);
     return pn;
 }
 
 template <class ParseHandler, typename CharT>
 MOZ_ALWAYS_INLINE typename ParseHandler::Node
 Parser<ParseHandler, CharT>::condExpr(InHandling inHandling, YieldHandling yieldHandling,
@@ -7960,17 +7960,17 @@ Parser<ParseHandler, CharT>::assignExpr(
             return identifierReference(name);
         }
     }
 
     if (firstToken == TOK_NUMBER) {
         if (!tokenStream.nextTokenEndsExpr(&endsExpr))
             return null();
         if (endsExpr)
-            return newNumber(tokenStream.currentToken());
+            return newNumber(anyChars.currentToken());
     }
 
     if (firstToken == TOK_STRING) {
         if (!tokenStream.nextTokenEndsExpr(&endsExpr))
             return null();
         if (endsExpr)
             return stringLiteral();
     }
@@ -7983,21 +7983,21 @@ Parser<ParseHandler, CharT>::assignExpr(
         TokenKind nextSameLine = TOK_EOF;
         if (!tokenStream.peekTokenSameLine(&nextSameLine))
             return null();
 
         if (TokenKindIsPossibleIdentifier(nextSameLine))
             maybeAsyncArrow = true;
     }
 
-    tokenStream.ungetToken();
+    anyChars.ungetToken();
 
     // Save the tokenizer state in case we find an arrow function and have to
     // rewind.
-    TokenStream::Position start(keepAtoms);
+    typename TokenStream::Position start(keepAtoms);
     tokenStream.tell(&start);
 
     PossibleError possibleErrorInner(*this);
     Node lhs;
     TokenKind tokenAfterLHS;
     bool isArrow;
     if (maybeAsyncArrow) {
         tokenStream.consumeKnownToken(TOK_ASYNC, TokenStream::Operand);
@@ -8038,34 +8038,34 @@ Parser<ParseHandler, CharT>::assignExpr(
 
     if (isArrow) {
         tokenStream.seek(start);
 
         TokenKind next;
         if (!tokenStream.getToken(&next, TokenStream::Operand))
             return null();
         uint32_t toStringStart = pos().begin;
-        tokenStream.ungetToken();
+        anyChars.ungetToken();
 
         FunctionAsyncKind asyncKind = FunctionAsyncKind::SyncFunction;
 
         if (next == TOK_ASYNC) {
             tokenStream.consumeKnownToken(next, TokenStream::Operand);
 
             TokenKind nextSameLine = TOK_EOF;
             if (!tokenStream.peekTokenSameLine(&nextSameLine))
                 return null();
 
             // The AsyncArrowFunction production are
             //   async [no LineTerminator here] AsyncArrowBindingIdentifier ...
             //   async [no LineTerminator here] ArrowFormalParameters ...
             if (TokenKindIsPossibleIdentifier(nextSameLine) || nextSameLine == TOK_LP)
                 asyncKind = FunctionAsyncKind::AsyncFunction;
             else
-                tokenStream.ungetToken();
+                anyChars.ungetToken();
         }
 
         Node pn = handler.newArrowFunction(pos());
         if (!pn)
             return null();
 
         return functionDefinition(pn, toStringStart, inHandling, yieldHandling, nullptr, Arrow,
                                   GeneratorKind::NotGenerator, asyncKind);
@@ -8085,25 +8085,25 @@ Parser<ParseHandler, CharT>::assignExpr(
       case TOK_RSHASSIGN:    kind = PNK_RSHASSIGN;    break;
       case TOK_URSHASSIGN:   kind = PNK_URSHASSIGN;   break;
       case TOK_MULASSIGN:    kind = PNK_MULASSIGN;    break;
       case TOK_DIVASSIGN:    kind = PNK_DIVASSIGN;    break;
       case TOK_MODASSIGN:    kind = PNK_MODASSIGN;    break;
       case TOK_POWASSIGN:    kind = PNK_POWASSIGN;    break;
 
       default:
-        MOZ_ASSERT(!tokenStream.isCurrentTokenAssignment());
+        MOZ_ASSERT(!anyChars.isCurrentTokenAssignment());
         if (!possibleError) {
             if (!possibleErrorInner.checkForExpressionError())
                 return null();
         } else {
             possibleErrorInner.transferErrorsTo(possibleError);
         }
 
-        tokenStream.ungetToken();
+        anyChars.ungetToken();
         return lhs;
     }
 
     // Verify the left-hand side expression doesn't have a forbidden form.
     if (handler.isUnparenthesizedDestructuringPattern(lhs)) {
         if (kind != PNK_ASSIGN) {
             error(JSMSG_BAD_DESTRUCT_ASS);
             return null();
@@ -8436,17 +8436,17 @@ template <class ParseHandler, typename C
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::memberExpr(YieldHandling yieldHandling,
                                         TripledotHandling tripledotHandling,
                                         ExpressionClosure expressionClosureHandling,
                                         TokenKind tt, bool allowCallSyntax /* = true */,
                                         PossibleError* possibleError /* = nullptr */,
                                         InvokedPrediction invoked /* = PredictUninvoked */)
 {
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(tt));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(tt));
 
     Node lhs;
 
     if (!CheckRecursionLimit(context))
         return null();
 
     /* Check for new expression first. */
     if (tt == TOK_NEW) {
@@ -8454,17 +8454,17 @@ Parser<ParseHandler, CharT>::memberExpr(
         // Make sure this wasn't a |new.target| in disguise.
         Node newTarget;
         if (!tryNewTarget(newTarget))
             return null();
         if (newTarget) {
             lhs = newTarget;
         } else {
             // Gotten by tryNewTarget
-            tt = tokenStream.currentToken().type;
+            tt = anyChars.currentToken().type;
             Node ctorExpr = memberExpr(yieldHandling, TripledotProhibited,
                                        ExpressionClosure::Forbidden, tt,
                                        /* allowCallSyntax = */ false,
                                        /* possibleError = */ nullptr, PredictInvoked);
             if (!ctorExpr)
                 return null();
 
             lhs = handler.newNewExpression(newBegin, ctorExpr);
@@ -8494,30 +8494,30 @@ Parser<ParseHandler, CharT>::memberExpr(
                           possibleError, invoked);
         if (!lhs)
             return null();
 
         if (handler.isExpressionClosure(lhs))
             return lhs;
     }
 
-    MOZ_ASSERT_IF(handler.isSuperBase(lhs), tokenStream.isCurrentTokenType(TOK_SUPER));
+    MOZ_ASSERT_IF(handler.isSuperBase(lhs), anyChars.isCurrentTokenType(TOK_SUPER));
 
     while (true) {
         if (!tokenStream.getToken(&tt))
             return null();
         if (tt == TOK_EOF)
             break;
 
         Node nextMember;
         if (tt == TOK_DOT) {
             if (!tokenStream.getToken(&tt))
                 return null();
             if (TokenKindIsPossibleIdentifierName(tt)) {
-                PropertyName* field = tokenStream.currentName();
+                PropertyName* field = anyChars.currentName();
                 if (handler.isSuperBase(lhs) && !checkAndMarkSuperScope()) {
                     error(JSMSG_BAD_SUPERPROP, "property");
                     return null();
                 }
                 nextMember = handler.newPropertyAccess(lhs, field, pos().end);
                 if (!nextMember)
                     return null();
             } else {
@@ -8646,17 +8646,17 @@ Parser<ParseHandler, CharT>::memberExpr(
                     }
                 } else {
                     if (!taggedTemplate(yieldHandling, nextMember, tt))
                         return null();
                 }
                 handler.setOp(nextMember, op);
             }
         } else {
-            tokenStream.ungetToken();
+            anyChars.ungetToken();
             if (handler.isSuperBase(lhs))
                 break;
             return lhs;
         }
 
         lhs = nextMember;
     }
 
@@ -8783,33 +8783,33 @@ Parser<ParseHandler, CharT>::labelOrIden
     //   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.
 
     // Unless the name contains escapes, we can reuse the current TokenKind
     // to determine if the name is a restricted identifier.
-    TokenKind hint = !tokenStream.currentNameHasEscapes()
-                     ? tokenStream.currentToken().type
+    TokenKind hint = !anyChars.currentNameHasEscapes()
+                     ? anyChars.currentToken().type
                      : TOK_LIMIT;
-    RootedPropertyName ident(context, tokenStream.currentName());
+    RootedPropertyName ident(context, anyChars.currentName());
     if (!checkLabelOrIdentifierReference(ident, pos().begin, yieldHandling, hint))
         return nullptr;
     return ident;
 }
 
 template <class ParseHandler, typename CharT>
 PropertyName*
 Parser<ParseHandler, CharT>::bindingIdentifier(YieldHandling yieldHandling)
 {
-    TokenKind hint = !tokenStream.currentNameHasEscapes()
-                     ? tokenStream.currentToken().type
+    TokenKind hint = !anyChars.currentNameHasEscapes()
+                     ? anyChars.currentToken().type
                      : TOK_LIMIT;
-    RootedPropertyName ident(context, tokenStream.currentName());
+    RootedPropertyName ident(context, anyChars.currentName());
     if (!checkBindingIdentifier(ident, pos().begin, yieldHandling, hint))
         return nullptr;
     return ident;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::identifierReference(Handle<PropertyName*> name)
@@ -8823,74 +8823,73 @@ Parser<ParseHandler, CharT>::identifierR
 
     return pn;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::stringLiteral()
 {
-    return handler.newStringLiteral(tokenStream.currentToken().atom(), pos());
+    return handler.newStringLiteral(anyChars.currentToken().atom(), pos());
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::noSubstitutionTaggedTemplate()
 {
-    if (tokenStream.hasInvalidTemplateEscape()) {
-        tokenStream.clearInvalidTemplateEscape();
+    if (anyChars.hasInvalidTemplateEscape()) {
+        anyChars.clearInvalidTemplateEscape();
         return handler.newRawUndefinedLiteral(pos());
     }
 
-    return handler.newTemplateStringLiteral(tokenStream.currentToken().atom(), pos());
+    return handler.newTemplateStringLiteral(anyChars.currentToken().atom(), pos());
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::noSubstitutionUntaggedTemplate()
 {
     if (!tokenStream.checkForInvalidTemplateEscapeError())
         return null();
 
-    return handler.newTemplateStringLiteral(tokenStream.currentToken().atom(), pos());
+    return handler.newTemplateStringLiteral(anyChars.currentToken().atom(), pos());
 }
 
 template <>
 ParseNode*
 Parser<FullParseHandler, char16_t>::newRegExp()
 {
     MOZ_ASSERT(!options().selfHostingMode);
 
     // Create the regexp and check its syntax.
     const char16_t* chars = tokenStream.getTokenbuf().begin();
     size_t length = tokenStream.getTokenbuf().length();
-    RegExpFlag flags = tokenStream.currentToken().regExpFlags();
+    RegExpFlag flags = anyChars.currentToken().regExpFlags();
 
     Rooted<RegExpObject*> reobj(context);
-    reobj = RegExpObject::create(context, chars, length, flags, nullptr, &tokenStream, alloc,
-                                 TenuredObject);
+    reobj = RegExpObject::create(context, chars, length, flags, anyChars, alloc, TenuredObject);
     if (!reobj)
         return null();
 
     return handler.newRegExp(reobj, pos(), *this);
 }
 
 template <>
 SyntaxParseHandler::Node
 Parser<SyntaxParseHandler, char16_t>::newRegExp()
 {
     MOZ_ASSERT(!options().selfHostingMode);
 
     // Only check the regexp's syntax, but don't create a regexp object.
     const char16_t* chars = tokenStream.getTokenbuf().begin();
     size_t length = tokenStream.getTokenbuf().length();
-    RegExpFlag flags = tokenStream.currentToken().regExpFlags();
+    RegExpFlag flags = anyChars.currentToken().regExpFlags();
 
     mozilla::Range<const char16_t> source(chars, length);
-    if (!js::irregexp::ParsePatternSyntax(tokenStream, alloc, source, flags & UnicodeFlag))
+    if (!js::irregexp::ParsePatternSyntax(anyChars, alloc, source, flags & UnicodeFlag))
         return null();
 
     return handler.newRegExp(SyntaxParseHandler::NodeGeneric, pos(), *this);
 }
 
 // |exprPossibleError| is the PossibleError state within |expr|,
 // |possibleError| is the surrounding PossibleError state.
 template <class ParseHandler, typename CharT>
@@ -9006,17 +9005,17 @@ Parser<ParseHandler, CharT>::checkDestru
     return checkDestructuringAssignmentTarget(expr, exprPos, exprPossibleError, possibleError);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::arrayInitializer(YieldHandling yieldHandling,
                                               PossibleError* possibleError)
 {
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LB));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LB));
 
     uint32_t begin = pos().begin;
     Node literal = handler.newArrayLiteral(begin);
     if (!literal)
         return null();
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
@@ -9024,17 +9023,17 @@ Parser<ParseHandler, CharT>::arrayInitia
 
     if (tt == TOK_RB) {
         /*
          * Mark empty arrays as non-constant, since we cannot easily
          * determine their type.
          */
         handler.setListFlag(literal, PNX_NONCONST);
     } else {
-        tokenStream.ungetToken();
+        anyChars.ungetToken();
 
         for (uint32_t index = 0; ; index++) {
             if (index >= NativeObject::MAX_DENSE_ELEMENTS_COUNT) {
                 error(JSMSG_ARRAY_INIT_TOO_BIG);
                 return null();
             }
 
             TokenKind tt;
@@ -9168,26 +9167,26 @@ Parser<ParseHandler, CharT>::propertyNam
         if (!tokenStream.getToken(&ltok))
             return null();
     }
 
     propAtom.set(nullptr);
     Node propName;
     switch (ltok) {
       case TOK_NUMBER:
-        propAtom.set(DoubleToAtom(context, tokenStream.currentToken().number()));
+        propAtom.set(DoubleToAtom(context, anyChars.currentToken().number()));
         if (!propAtom.get())
             return null();
-        propName = newNumber(tokenStream.currentToken());
+        propName = newNumber(anyChars.currentToken());
         if (!propName)
             return null();
         break;
 
       case TOK_STRING: {
-        propAtom.set(tokenStream.currentToken().atom());
+        propAtom.set(anyChars.currentToken().atom());
         uint32_t index;
         if (propAtom->isIndex(&index)) {
             propName = handler.newNumber(index, NoDecimal, pos());
             if (!propName)
                 return null();
             break;
         }
         propName = stringLiteral();
@@ -9203,17 +9202,17 @@ Parser<ParseHandler, CharT>::propertyNam
         break;
 
       default: {
         if (!TokenKindIsPossibleIdentifierName(ltok)) {
             error(JSMSG_UNEXPECTED_TOKEN, "property name", TokenKindToDesc(ltok));
             return null();
         }
 
-        propAtom.set(tokenStream.currentName());
+        propAtom.set(anyChars.currentName());
         // Do not look for accessor syntax on generator or async methods.
         if (isGenerator || isAsync || !(ltok == TOK_GET || ltok == TOK_SET)) {
             propName = handler.newObjectLiteralPropertyName(propAtom, pos());
             if (!propName)
                 return null();
             break;
         }
 
@@ -9222,40 +9221,40 @@ Parser<ParseHandler, CharT>::propertyNam
         // We have parsed |get| or |set|. Look for an accessor property
         // name next.
         TokenKind tt;
         if (!tokenStream.peekToken(&tt))
             return null();
         if (TokenKindIsPossibleIdentifierName(tt)) {
             tokenStream.consumeKnownToken(tt);
 
-            propAtom.set(tokenStream.currentName());
+            propAtom.set(anyChars.currentName());
             return handler.newObjectLiteralPropertyName(propAtom, pos());
         }
         if (tt == TOK_STRING) {
             tokenStream.consumeKnownToken(TOK_STRING);
 
-            propAtom.set(tokenStream.currentToken().atom());
+            propAtom.set(anyChars.currentToken().atom());
 
             uint32_t index;
             if (propAtom->isIndex(&index)) {
                 propAtom.set(DoubleToAtom(context, index));
                 if (!propAtom.get())
                     return null();
                 return handler.newNumber(index, NoDecimal, pos());
             }
             return stringLiteral();
         }
         if (tt == TOK_NUMBER) {
             tokenStream.consumeKnownToken(TOK_NUMBER);
 
-            propAtom.set(DoubleToAtom(context, tokenStream.currentToken().number()));
+            propAtom.set(DoubleToAtom(context, anyChars.currentToken().number()));
             if (!propAtom.get())
                 return null();
-            return newNumber(tokenStream.currentToken());
+            return newNumber(anyChars.currentToken());
         }
         if (tt == TOK_LB) {
             tokenStream.consumeKnownToken(TOK_LB);
 
             return computedPropertyName(yieldHandling, maybeDecl, propList);
         }
 
         // Not an accessor property after all.
@@ -9282,26 +9281,26 @@ Parser<ParseHandler, CharT>::propertyNam
     if (TokenKindIsPossibleIdentifierName(ltok) &&
         (tt == TOK_COMMA || tt == TOK_RC || tt == TOK_ASSIGN))
     {
         if (isGenerator || isAsync) {
             error(JSMSG_BAD_PROP_ID);
             return null();
         }
 
-        tokenStream.ungetToken();
-        tokenStream.addModifierException(TokenStream::OperandIsNone);
+        anyChars.ungetToken();
+        anyChars.addModifierException(TokenStream::OperandIsNone);
         *propType = tt == TOK_ASSIGN
                     ? PropertyType::CoverInitializedName
                     : PropertyType::Shorthand;
         return propName;
     }
 
     if (tt == TOK_LP) {
-        tokenStream.ungetToken();
+        anyChars.ungetToken();
 
         if (isGenerator && isAsync)
             *propType = PropertyType::AsyncGeneratorMethod;
         else if (isGenerator)
             *propType = PropertyType::GeneratorMethod;
         else if (isAsync)
             *propType = PropertyType::AsyncMethod;
         else
@@ -9314,17 +9313,17 @@ Parser<ParseHandler, CharT>::propertyNam
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::computedPropertyName(YieldHandling yieldHandling,
                                                   const Maybe<DeclarationKind>& maybeDecl,
                                                   Node literal)
 {
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LB));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LB));
 
     uint32_t begin = pos().begin;
 
     if (maybeDecl) {
         if (*maybeDecl == DeclarationKind::FormalParameter)
             pc->functionBox()->hasParameterExprs = true;
     } else {
         handler.setListFlag(literal, PNX_NONCONST);
@@ -9338,34 +9337,34 @@ Parser<ParseHandler, CharT>::computedPro
     return handler.newComputedName(assignNode, begin, pos().end);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::objectLiteral(YieldHandling yieldHandling,
                                            PossibleError* possibleError)
 {
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LC));
 
     uint32_t openedPos = pos().begin;
 
     Node literal = handler.newObjectLiteral(pos().begin);
     if (!literal)
         return null();
 
     bool seenPrototypeMutation = false;
     bool seenCoverInitializedName = false;
     Maybe<DeclarationKind> declKind = Nothing();
     RootedAtom propAtom(context);
     for (;;) {
         TokenKind tt;
         if (!tokenStream.peekToken(&tt))
             return null();
         if (tt == TOK_RC) {
-            tokenStream.addModifierException(TokenStream::OperandIsNone);
+            anyChars.addModifierException(TokenStream::OperandIsNone);
             break;
         }
 
         if (tt == TOK_TRIPLEDOT) {
             tokenStream.consumeKnownToken(TOK_TRIPLEDOT);
             uint32_t begin = pos().begin;
 
             TokenPos innerPos;
@@ -9381,17 +9380,17 @@ Parser<ParseHandler, CharT>::objectLiter
                                                     possibleError,
                                                     TargetBehavior::ForbidAssignmentPattern))
             {
                 return null();
             }
             if (!handler.addSpreadProperty(literal, begin, inner))
                 return null();
         } else {
-            TokenPos namePos = tokenStream.nextToken().pos;
+            TokenPos namePos = anyChars.nextToken().pos;
 
             PropertyType propType;
             Node propName = propertyName(yieldHandling, declKind, literal, &propType, &propAtom);
             if (!propName)
                 return null();
 
             if (propType == PropertyType::Normal) {
                 TokenPos exprPos;
@@ -9515,17 +9514,17 @@ Parser<ParseHandler, CharT>::objectLiter
                 Node propExpr = handler.newAssignment(PNK_ASSIGN, lhs, rhs);
                 if (!propExpr)
                     return null();
 
                 if (!handler.addPropertyDefinition(literal, propName, propExpr))
                     return null();
             } else {
                 RootedAtom funName(context);
-                if (!tokenStream.isCurrentTokenType(TOK_RB)) {
+                if (!anyChars.isCurrentTokenType(TOK_RB)) {
                     funName = propAtom;
 
                     if (propType == PropertyType::Getter || propType == PropertyType::Setter) {
                         funName = prefixAccessorName(propType, propAtom);
                         if (!funName)
                             return null();
                     }
                 }
@@ -9625,17 +9624,17 @@ Parser<ParseHandler, CharT>::methodDefin
     return functionDefinition(pn, toStringStart, InAllowed, yieldHandling, funName, kind,
                               generatorKind, asyncKind);
 }
 
 template <class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::tryNewTarget(Node &newTarget)
 {
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_NEW));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_NEW));
 
     newTarget = null();
 
     Node newHolder = handler.newPosHolder(pos());
     if (!newHolder)
         return false;
 
     uint32_t begin = pos().begin;
@@ -9673,17 +9672,17 @@ Parser<ParseHandler, CharT>::tryNewTarge
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::primaryExpr(YieldHandling yieldHandling,
                                          TripledotHandling tripledotHandling,
                                          ExpressionClosure expressionClosureHandling, TokenKind tt,
                                          PossibleError* possibleError,
                                          InvokedPrediction invoked /* = PredictUninvoked */)
 {
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(tt));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(tt));
     if (!CheckRecursionLimit(context))
         return null();
 
     switch (tt) {
       case TOK_FUNCTION:
         return functionExpr(pos().begin, expressionClosureHandling, invoked,
                             FunctionAsyncKind::SyncFunction);
 
@@ -9761,17 +9760,17 @@ Parser<ParseHandler, CharT>::primaryExpr
 
         return identifierReference(name);
       }
 
       case TOK_REGEXP:
         return newRegExp();
 
       case TOK_NUMBER:
-        return newNumber(tokenStream.currentToken());
+        return newNumber(anyChars.currentToken());
 
       case TOK_TRUE:
         return handler.newBooleanLiteral(true, pos());
       case TOK_FALSE:
         return handler.newBooleanLiteral(false, pos());
       case TOK_THIS: {
         if (pc->isFunctionBox())
             pc->functionBox()->usesThis = true;
@@ -9832,31 +9831,31 @@ Parser<ParseHandler, CharT>::primaryExpr
             return null();
         if (next != TOK_ARROW) {
             // Advance the scanner for proper error location reporting.
             tokenStream.consumeKnownToken(next);
             error(JSMSG_UNEXPECTED_TOKEN, "'=>' after argument list", TokenKindToDesc(next));
             return null();
         }
 
-        tokenStream.ungetToken();  // put back right paren
+        anyChars.ungetToken();  // put back right paren
 
         // Return an arbitrary expression node. See case TOK_RP above.
         return handler.newNullLiteral(pos());
       }
     }
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::exprInParens(InHandling inHandling, YieldHandling yieldHandling,
                                           TripledotHandling tripledotHandling,
                                           PossibleError* possibleError /* = nullptr */)
 {
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LP));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LP));
     return expr(inHandling, yieldHandling, tripledotHandling, possibleError, PredictInvoked);
 }
 
 void
 ParserBase::addTelemetry(DeprecatedLanguageExtension e)
 {
     if (context->helperThread())
         return;
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -20,35 +20,35 @@
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/FullParseHandler.h"
 #include "frontend/LanguageExtensions.h"
 #include "frontend/NameAnalysisTypes.h"
 #include "frontend/NameCollections.h"
 #include "frontend/ParseContext.h"
 #include "frontend/SharedContext.h"
 #include "frontend/SyntaxParseHandler.h"
+#include "frontend/TokenStream.h"
 
 namespace js {
 
 class ModuleObject;
 
 namespace frontend {
 
 class ParserBase;
 
 template <class ParseHandler, typename CharT> class Parser;
 
 class SourceParseContext: public ParseContext
 {
 public:
     template<typename ParseHandler, typename CharT>
     SourceParseContext(Parser<ParseHandler, CharT>* prs, SharedContext* sc, Directives* newDirectives)
-        : ParseContext(prs->context, prs->pc, sc, prs->tokenStream,
-            prs->usedNames, newDirectives, mozilla::IsSame<ParseHandler,
-            FullParseHandler>::value)
+      : ParseContext(prs->context, prs->pc, sc, prs->anyChars, prs->usedNames, newDirectives,
+                     mozilla::IsSame<ParseHandler, FullParseHandler>::value)
     { }
 };
 
 template <>
 inline bool
 ParseContext::Statement::is<ParseContext::LabelStatement>() const
 {
     return kind_ == StatementKind::Label;
@@ -121,17 +121,17 @@ class ParserBase : public StrictModeGett
   private:
     ParserBase* thisForCtor() { return this; }
 
   public:
     JSContext* const context;
 
     LifoAlloc& alloc;
 
-    TokenStream tokenStream;
+    TokenStreamAnyChars anyChars;
     LifoAlloc::Mark tempPoolMark;
 
     /* list of parsed objects for GC tracing */
     ObjectBox* traceListHead;
 
     /* innermost parse context (stack-allocated) */
     ParseContext* pc;
 
@@ -162,32 +162,32 @@ class ParserBase : public StrictModeGett
       return awaitHandling_ != AwaitIsName;
     }
 
     ParserBase(JSContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options,
                const char16_t* chars, size_t length, bool foldConstants,
                UsedNameTracker& usedNames);
     ~ParserBase();
 
-    const char* getFilename() const { return tokenStream.getFilename(); }
-    TokenPos pos() const { return tokenStream.currentToken().pos; }
+    const char* getFilename() const { return anyChars.getFilename(); }
+    TokenPos pos() const { return anyChars.currentToken().pos; }
 
     // Determine whether |yield| is a valid name in the current context.
     bool yieldExpressionsSupported() const {
         return pc->isGenerator();
     }
 
     virtual bool strictMode() { return pc->sc()->strict(); }
     bool setLocalStrictMode(bool strict) {
-        MOZ_ASSERT(tokenStream.debugHasNoLookahead());
+        MOZ_ASSERT(anyChars.debugHasNoLookahead());
         return pc->sc()->setLocalStrictMode(strict);
     }
 
     const ReadOnlyCompileOptions& options() const {
-        return tokenStream.options();
+        return anyChars.options();
     }
 
     bool isUnexpectedEOF() const { return isUnexpectedEOF_; }
 
     MOZ_MUST_USE bool warningNoOffset(unsigned errorNumber, ...);
     void errorNoOffset(unsigned errorNumber, ...);
 
     bool isValidStrictBinding(PropertyName* name);
@@ -260,19 +260,34 @@ inline
 ParseContext::VarScope::VarScope(ParserBase* parser)
   : Scope(parser)
 {
     useAsVarScope(parser->pc);
 }
 
 enum class ExpressionClosure { Allowed, Forbidden };
 
+template<class Parser>
+class ParserAnyCharsAccess
+{
+  public:
+    using TokenStreamSpecific = typename Parser::TokenStream;
+    using TokenStreamChars = typename TokenStreamSpecific::CharsBase;
+
+    static inline TokenStreamAnyChars& anyChars(TokenStreamChars* ts);
+    static inline const TokenStreamAnyChars& anyChars(const TokenStreamChars* ts);
+};
+
 template <class ParseHandler, typename CharT>
-class Parser final : public ParserBase, private JS::AutoGCRooter
+class Parser final
+  : public ParserBase,
+    private JS::AutoGCRooter
 {
+    using Modifier = TokenStreamShared::Modifier;
+
   private:
     using Node = typename ParseHandler::Node;
 
     /*
      * A class for temporarily stashing errors while parsing continues.
      *
      * The ability to stash an error is useful for handling situations where we
      * aren't able to verify that an error has occurred until later in the parse.
@@ -407,16 +422,19 @@ class Parser final : public ParserBase, 
     //
     //   If non-null, this field must be a sentinel value signaling that the
     //   syntax parse was aborted. If null, then lazy parsing was aborted due
     //   to encountering unsupported language constructs.
     using SyntaxParser = Parser<SyntaxParseHandler, CharT>;
     SyntaxParser* syntaxParser_;
 
   public:
+    using TokenStream = TokenStreamSpecific<CharT, ParserAnyCharsAccess<Parser>>;
+    TokenStream tokenStream;
+
     /* State specific to the kind of parse being performed. */
     ParseHandler handler;
 
     void prepareNodeForMutation(Node node) { handler.prepareNodeForMutation(node); }
     void freeTree(Node node) { handler.freeTree(node); }
 
   public:
     Parser(JSContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options,
@@ -578,24 +596,16 @@ class Parser final : public ParserBase, 
   private:
     /*
      * JS parsers, from lowest to highest precedence.
      *
      * Each parser must be called during the dynamic scope of a ParseContext
      * object, pointed to by this->pc.
      *
      * Each returns a parse node tree or null on error.
-     *
-     * Parsers whose name has a '1' suffix leave the TokenStream state
-     * pointing to the token one past the end of the parsed fragment.  For a
-     * number of the parsers this is convenient and avoids a lot of
-     * unnecessary ungetting and regetting of tokens.
-     *
-     * Some parsers have two versions:  an always-inlined version (with an 'i'
-     * suffix) and a never-inlined version (with an 'n' suffix).
      */
     Node functionStmt(uint32_t toStringStart,
                       YieldHandling yieldHandling, DefaultHandling defaultHandling,
                       FunctionAsyncKind asyncKind = FunctionAsyncKind::SyncFunction);
     Node functionExpr(uint32_t toStringStart, ExpressionClosure expressionClosureHandling,
                       InvokedPrediction invoked, FunctionAsyncKind asyncKind);
 
     Node statementList(YieldHandling yieldHandling);
@@ -920,16 +930,70 @@ class Parser final : public ParserBase, 
 
     static Node null() { return ParseHandler::null(); }
 
     JSAtom* prefixAccessorName(PropertyType propType, HandleAtom propAtom);
 
     bool asmJS(Node list);
 };
 
+template<class Parser>
+/* static */ inline TokenStreamAnyChars&
+ParserAnyCharsAccess<Parser>::anyChars(TokenStreamChars* ts)
+{
+    // The structure we're walking through looks like this:
+    //
+    //   struct ParserBase
+    //   {
+    //       ...;
+    //       TokenStreamAnyChars anyChars;
+    //       ...;
+    //   };
+    //   struct Parser : ParserBase
+    //   {
+    //       ...;
+    //       TokenStreamSpecific tokenStream;
+    //       ...;
+    //   };
+    //
+    // We're passed a TokenStreamChars* corresponding to a base class of
+    // Parser::tokenStream.  We cast that pointer to a TokenStreamSpecific*,
+    // then translate that to the enclosing Parser*, then return the |anyChars|
+    // member within.
+
+    auto* tss = static_cast<TokenStreamSpecific*>(ts);
+
+    auto tssAddr = reinterpret_cast<uintptr_t>(tss);
+
+    using ActualTokenStreamType = decltype(static_cast<Parser*>(nullptr)->tokenStream);
+    static_assert(mozilla::IsSame<ActualTokenStreamType, TokenStreamSpecific>::value,
+                                  "Parser::tokenStream must have type TokenStreamSpecific");
+
+    uintptr_t parserAddr = tssAddr - offsetof(Parser, tokenStream);
+
+    return reinterpret_cast<Parser*>(parserAddr)->anyChars;
+}
+
+template<class Parser>
+/* static */ inline const TokenStreamAnyChars&
+ParserAnyCharsAccess<Parser>::anyChars(const typename Parser::TokenStream::CharsBase* ts)
+{
+    const auto* tss = static_cast<const TokenStreamSpecific*>(ts);
+
+    auto tssAddr = reinterpret_cast<uintptr_t>(tss);
+
+    using ActualTokenStreamType = decltype(static_cast<Parser*>(nullptr)->tokenStream);
+    static_assert(mozilla::IsSame<ActualTokenStreamType, TokenStreamSpecific>::value,
+                                  "Parser::tokenStream must have type TokenStreamSpecific");
+
+    uintptr_t parserAddr = tssAddr - offsetof(Parser, tokenStream);
+
+    return reinterpret_cast<const Parser*>(parserAddr)->anyChars;
+}
+
 template <class Parser>
 class MOZ_STACK_CLASS AutoAwaitIsKeyword
 {
   private:
     Parser* parser_;
     AwaitHandling oldAwaitHandling_;
 
   public:
--- a/js/src/frontend/SharedContext.h
+++ b/js/src/frontend/SharedContext.h
@@ -517,31 +517,31 @@ class FunctionBox : public ObjectBox, pu
     // validated as asm.js. Note: if asm.js validation fails, this will be false
     // while the function is being reparsed. This flag can be used to disable
     // certain parsing features that are necessary in general, but unnecessary
     // for validated asm.js.
     bool useAsmOrInsideUseAsm() const {
         return useAsm;
     }
 
-    void setStart(const TokenStream& tokenStream) {
-        uint32_t offset = tokenStream.currentToken().pos.begin;
-        setStart(tokenStream, offset);
+    void setStart(const TokenStreamAnyChars& anyChars) {
+        uint32_t offset = anyChars.currentToken().pos.begin;
+        setStart(anyChars, offset);
     }
 
-    void setStart(const TokenStream& tokenStream, uint32_t offset) {
+    void setStart(const TokenStreamAnyChars& anyChars, uint32_t offset) {
         bufStart = offset;
-        tokenStream.srcCoords.lineNumAndColumnIndex(offset, &startLine, &startColumn);
+        anyChars.srcCoords.lineNumAndColumnIndex(offset, &startLine, &startColumn);
     }
 
-    void setEnd(const TokenStream& tokenStream) {
+    void setEnd(const TokenStreamAnyChars& anyChars) {
         // For all functions except class constructors, the buffer and
         // toString ending positions are the same. Class constructors override
         // the toString ending position with the end of the class definition.
-        uint32_t offset = tokenStream.currentToken().pos.end;
+        uint32_t offset = anyChars.currentToken().pos.end;
         bufEnd = offset;
         toStringEnd = offset;
     }
 
     void trace(JSTracer* trc) override;
 };
 
 inline FunctionBox*
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -19,16 +19,17 @@
 
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jscompartment.h"
 #include "jsexn.h"
 #include "jsnum.h"
 
 #include "frontend/BytecodeCompiler.h"
+#include "frontend/Parser.h"
 #include "frontend/ReservedWords.h"
 #include "js/CharacterEncoding.h"
 #include "js/UniquePtr.h"
 #include "vm/HelperThreads.h"
 #include "vm/StringBuffer.h"
 #include "vm/Unicode.h"
 
 using mozilla::ArrayLength;
@@ -704,19 +705,19 @@ TokenStreamSpecific<CharT, AnyCharsAcces
     anyChars.tokens[anyChars.cursor] = pos.currentToken;
     for (unsigned i = 0; i < anyChars.lookahead; i++)
         anyChars.tokens[(anyChars.cursor + 1 + i) & ntokensMask] = pos.lookaheadTokens[i];
 }
 
 template<typename CharT, class AnyCharsAccess>
 bool
 TokenStreamSpecific<CharT, AnyCharsAccess>::seek(const Position& pos,
-                                                 const TokenStreamSpecific& other)
+                                                 const TokenStreamAnyChars& other)
 {
-    if (!anyCharsAccess().srcCoords.fill(other.anyCharsAccess().srcCoords))
+    if (!anyCharsAccess().srcCoords.fill(other.srcCoords))
         return false;
 
     seek(pos);
     return true;
 }
 
 template<typename CharT, class AnyCharsAccess>
 bool
@@ -2357,19 +2358,30 @@ TokenKindToString(TokenKind tt)
       case TOK_LIMIT: break;
     }
 
     return "<bad TokenKind>";
 }
 #endif
 
 template class frontend::TokenStreamCharsBase<char16_t>;
+
 template class frontend::TokenStreamChars<char16_t, frontend::TokenStreamAnyCharsAccess>;
 template class frontend::TokenStreamSpecific<char16_t, frontend::TokenStreamAnyCharsAccess>;
 
+template class
+frontend::TokenStreamChars<char16_t, frontend::ParserAnyCharsAccess<frontend::Parser<frontend::FullParseHandler, char16_t>>>;
+template class
+frontend::TokenStreamChars<char16_t, frontend::ParserAnyCharsAccess<frontend::Parser<frontend::SyntaxParseHandler, char16_t>>>;
+
+template class
+frontend::TokenStreamSpecific<char16_t, frontend::ParserAnyCharsAccess<frontend::Parser<frontend::FullParseHandler, char16_t>>>;
+template class
+frontend::TokenStreamSpecific<char16_t, frontend::ParserAnyCharsAccess<frontend::Parser<frontend::SyntaxParseHandler, char16_t>>>;
+
 } // namespace frontend
 
 } // namespace js
 
 
 JS_FRIEND_API(int)
 js_fgets(char* buf, int size, FILE* file)
 {
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -383,44 +383,46 @@ ReservedWordToCharZ(TokenKind tt);
 // This class is a tiny back-channel from TokenStream to the strict mode flag
 // that avoids exposing the rest of SharedContext to TokenStream.
 //
 class StrictModeGetter {
   public:
     virtual bool strictMode() = 0;
 };
 
+struct TokenStreamFlags
+{
+    bool isEOF:1;           // Hit end of file.
+    bool isDirtyLine:1;     // Non-whitespace since start of line.
+    bool sawOctalEscape:1;  // Saw an octal character escape.
+    bool hadError:1;        // Hit a syntax error, at start or during a
+                            // token.
+
+    TokenStreamFlags()
+      : isEOF(), isDirtyLine(), sawOctalEscape(), hadError()
+    {}
+};
+
+
 /**
  * TokenStream types and constants that are used in both TokenStreamAnyChars
  * and TokenStreamSpecific.  Do not add any non-static data members to this
  * class!
  */
 class TokenStreamShared
 {
   protected:
     static constexpr size_t ntokens = 4; // 1 current + 2 lookahead, rounded
                                          // to power of 2 to avoid divmod by 3
 
-    static constexpr unsigned maxLookahead = 2;
     static constexpr unsigned ntokensMask = ntokens - 1;
 
-    struct Flags
-    {
-        bool isEOF:1;           // Hit end of file.
-        bool isDirtyLine:1;     // Non-whitespace since start of line.
-        bool sawOctalEscape:1;  // Saw an octal character escape.
-        bool hadError:1;        // Hit a syntax error, at start or during a
-                                // token.
+  public:
+    static constexpr unsigned maxLookahead = 2;
 
-        Flags()
-          : isEOF(), isDirtyLine(), sawOctalEscape(), hadError()
-        {}
-    };
-
-  public:
     static constexpr uint32_t NoOffset = UINT32_MAX;
 
     using Modifier = Token::Modifier;
     static constexpr Modifier None = Token::None;
     static constexpr Modifier Operand = Token::Operand;
     static constexpr Modifier TemplateTail = Token::TemplateTail;
 
     using ModifierException = Token::ModifierException;
@@ -716,16 +718,23 @@ class TokenStreamAnyChars
   public:
     const Token& nextToken() const {
         MOZ_ASSERT(hasLookahead());
         return tokens[(cursor + 1) & ntokensMask];
     }
 
     bool hasLookahead() const { return lookahead > 0; }
 
+    // Push the last scanned token back into the stream.
+    void ungetToken() {
+        MOZ_ASSERT(lookahead < maxLookahead);
+        lookahead++;
+        cursor = (cursor - 1) & ntokensMask;
+    }
+
   public:
     MOZ_MUST_USE bool compileWarning(ErrorMetadata&& metadata, UniquePtr<JSErrorNotes> notes,
                                      unsigned flags, unsigned errorNumber, va_list args);
 
     // Compute error metadata for an error at no offset.
     void computeErrorMetadataNoOffset(ErrorMetadata* err);
 
   public:
@@ -750,17 +759,17 @@ class TokenStreamAnyChars
   protected:
     // Options used for parsing/tokenizing.
     const ReadOnlyCompileOptions& options_;
 
     Token               tokens[ntokens];    // circular token buffer
     unsigned            cursor;             // index of last parsed token
     unsigned            lookahead;          // count of lookahead tokens
     unsigned            lineno;             // current line number
-    Flags               flags;              // flags -- see above
+    TokenStreamFlags    flags;              // flags -- see above
     size_t              linebase;           // start of current line
     size_t              prevLinebase;       // start of previous line;  size_t(-1) if on the first line
     const char*         filename_;          // input filename or null
     UniqueTwoByteChars  displayURL_;        // the user's requested source URL or null
     UniqueTwoByteChars  sourceMapURL_;      // source map's filename or null
     uint8_t             isExprEnding[TOK_LIMIT];// which tokens definitely terminate exprs?
     JSContext* const    cx;
     bool                mutedErrors;
@@ -893,16 +902,46 @@ class TokenStreamCharsBase
         const CharT* limit_;
 
         /** Next char to get. */
         const CharT* ptr;
     };
 
     MOZ_MUST_USE bool appendMultiUnitCodepointToTokenbuf(uint32_t codepoint);
 
+    class MOZ_STACK_CLASS Position
+    {
+      public:
+        // The JS_HAZ_ROOTED is permissible below because: 1) the only field in
+        // Position that can keep GC things alive is Token, 2) the only GC
+        // things Token can keep alive are atoms, and 3) the AutoKeepAtoms&
+        // passed to the constructor here represents that collection of atoms
+        // is disabled while atoms in Tokens in this Position are alive.  DON'T
+        // ADD NON-ATOM GC THING POINTERS HERE!  They would create a rooting
+        // hazard that JS_HAZ_ROOTED will cause to be ignored.
+        explicit Position(AutoKeepAtoms&) { }
+
+      private:
+        Position(const Position&) = delete;
+
+        // Technically this should only friend TokenStreamSpecific instantiated
+        // with CharT (letting the AnyCharsAccess parameter vary), but C++
+        // doesn't allow partial friend specialization.
+        template<typename, class> friend class TokenStreamSpecific;
+
+        const CharT* buf;
+        TokenStreamFlags flags;
+        unsigned lineno;
+        size_t linebase;
+        size_t prevLinebase;
+        Token currentToken;
+        unsigned lookahead;
+        Token lookaheadTokens[TokenStreamShared::maxLookahead];
+    } JS_HAZ_ROOTED;
+
   protected:
     /** User input buffer. */
     TokenBuf userbuf;
 
     /** Current token string buffer. */
     CharBuffer tokenbuf;
 };
 
@@ -981,20 +1020,20 @@ class TokenStreamChars<char16_t, AnyChar
 // The methods seek() and tell() allow to rescan from a previous visited
 // location of the buffer.
 //
 template<typename CharT, class AnyCharsAccess>
 class MOZ_STACK_CLASS TokenStreamSpecific
   : public TokenStreamChars<CharT, AnyCharsAccess>,
     public TokenStreamShared
 {
+  public:
     using CharsBase = TokenStreamChars<CharT, AnyCharsAccess>;
     using CharsSharedBase = TokenStreamCharsBase<CharT>;
 
-  public:
     // Anything inherited through a base class whose type depends upon this
     // class's template parameters can only be accessed through a dependent
     // name: prefixed with |this|, by explicit qualification, and so on.  (This
     // is so that references to inherited fields are statically distinguishable
     // from references to names outside of the class.)  This is tedious and
     // onerous.
     //
     // As an alternative, we directly add every one of these functions to this
@@ -1010,16 +1049,18 @@ class MOZ_STACK_CLASS TokenStreamSpecifi
 
     using typename CharsSharedBase::TokenBuf;
 
     using CharsSharedBase::appendMultiUnitCodepointToTokenbuf;
 
     using CharsSharedBase::userbuf;
     using CharsSharedBase::tokenbuf;
 
+    using typename CharsSharedBase::Position;
+
   public:
     TokenStreamSpecific(JSContext* cx, const ReadOnlyCompileOptions& options,
                         const CharT* base, size_t length);
 
     TokenStreamAnyChars& anyCharsAccess() {
         return CharsBase::anyChars();
     }
 
@@ -1144,46 +1185,37 @@ class MOZ_STACK_CLASS TokenStreamSpecifi
             verifyConsistentModifier(modifier, anyChars.currentToken());
             *ttp = tt;
             return true;
         }
 
         return getTokenInternal(ttp, modifier);
     }
 
-    // Push the last scanned token back into the stream.
-    void ungetToken() {
-        TokenStreamAnyChars& anyChars = anyCharsAccess();
-
-        MOZ_ASSERT(anyChars.lookahead < maxLookahead);
-        anyChars.lookahead++;
-        anyChars.cursor = (anyChars.cursor - 1) & ntokensMask;
-    }
-
     MOZ_MUST_USE bool peekToken(TokenKind* ttp, Modifier modifier = None) {
         TokenStreamAnyChars& anyChars = anyCharsAccess();
         if (anyChars.lookahead > 0) {
             MOZ_ASSERT(!anyChars.flags.hadError);
             verifyConsistentModifier(modifier, anyChars.nextToken());
             *ttp = anyChars.nextToken().type;
             return true;
         }
         if (!getTokenInternal(ttp, modifier))
             return false;
-        ungetToken();
+        anyChars.ungetToken();
         return true;
     }
 
     MOZ_MUST_USE bool peekTokenPos(TokenPos* posp, Modifier modifier = None) {
         TokenStreamAnyChars& anyChars = anyCharsAccess();
         if (anyChars.lookahead == 0) {
             TokenKind tt;
             if (!getTokenInternal(&tt, modifier))
                 return false;
-            ungetToken();
+            anyChars.ungetToken();
             MOZ_ASSERT(anyChars.hasLookahead());
         } else {
             MOZ_ASSERT(!anyChars.flags.hadError);
             verifyConsistentModifier(modifier, anyChars.nextToken());
         }
         *posp = anyChars.nextToken().pos;
         return true;
     }
@@ -1233,34 +1265,34 @@ class MOZ_STACK_CLASS TokenStreamSpecifi
         // - The next token starts on the same line, but lookahead==2 and there
         //   is a newline between the next token and the one after that.
         // The following test is somewhat expensive but gets these cases (and
         // all others) right.
         TokenKind tmp;
         if (!getToken(&tmp, modifier))
             return false;
         const Token& next = anyChars.currentToken();
-        ungetToken();
+        anyChars.ungetToken();
 
         const auto& srcCoords = anyChars.srcCoords;
         *ttp = srcCoords.lineNum(curr.pos.end) == srcCoords.lineNum(next.pos.begin)
              ? next.type
              : TOK_EOL;
         return true;
     }
 
     // Get the next token from the stream if its kind is |tt|.
     MOZ_MUST_USE bool matchToken(bool* matchedp, TokenKind tt, Modifier modifier = None) {
         TokenKind token;
         if (!getToken(&token, modifier))
             return false;
         if (token == tt) {
             *matchedp = true;
         } else {
-            ungetToken();
+            anyCharsAccess().ungetToken();
             *matchedp = false;
         }
         return true;
     }
 
     void consumeKnownToken(TokenKind tt, Modifier modifier = None) {
         bool matched;
         MOZ_ASSERT(anyCharsAccess().hasLookahead());
@@ -1279,44 +1311,21 @@ class MOZ_STACK_CLASS TokenStreamSpecifi
             // Expression without ever invoking Parser::orExpr().  But we need
             // that function's side effect of adding this modifier exception,
             // so we have to do it manually here.
             anyCharsAccess().addModifierException(OperandIsNone);
         }
         return true;
     }
 
-    class MOZ_STACK_CLASS Position {
-      public:
-        // The JS_HAZ_ROOTED is permissible below because: 1) the only field in
-        // Position that can keep GC things alive is Token, 2) the only GC
-        // things Token can keep alive are atoms, and 3) the AutoKeepAtoms&
-        // passed to the constructor here represents that collection of atoms
-        // is disabled while atoms in Tokens in this Position are alive.  DON'T
-        // ADD NON-ATOM GC THING POINTERS HERE!  They would create a rooting
-        // hazard that JS_HAZ_ROOTED will cause to be ignored.
-        explicit Position(AutoKeepAtoms&) { }
+    MOZ_MUST_USE bool advance(size_t position);
 
-      private:
-        Position(const Position&) = delete;
-        friend class TokenStreamSpecific;
-        const CharT* buf;
-        Flags flags;
-        unsigned lineno;
-        size_t linebase;
-        size_t prevLinebase;
-        Token currentToken;
-        unsigned lookahead;
-        Token lookaheadTokens[maxLookahead];
-    } JS_HAZ_ROOTED;
-
-    MOZ_MUST_USE bool advance(size_t position);
     void tell(Position*);
     void seek(const Position& pos);
-    MOZ_MUST_USE bool seek(const Position& pos, const TokenStreamSpecific& other);
+    MOZ_MUST_USE bool seek(const Position& pos, const TokenStreamAnyChars& other);
 
     const CharT* rawCharPtrAt(size_t offset) const {
         return userbuf.rawCharPtrAt(offset);
     }
 
     const CharT* rawLimit() const {
         return userbuf.limit();
     }
--- a/js/src/irregexp/RegExpParser.cpp
+++ b/js/src/irregexp/RegExpParser.cpp
@@ -232,17 +232,17 @@ RegExpBuilder::AddQuantifierToAtom(int m
     last_added_ = ADD_TERM;
 #endif
 }
 
 // ----------------------------------------------------------------------------
 // RegExpParser
 
 template <typename CharT>
-RegExpParser<CharT>::RegExpParser(frontend::TokenStream& ts, LifoAlloc* alloc,
+RegExpParser<CharT>::RegExpParser(frontend::TokenStreamAnyChars& ts, LifoAlloc* alloc,
                                   const CharT* chars, const CharT* end, bool multiline_mode,
                                   bool unicode, bool ignore_case)
   : ts(ts),
     alloc(alloc),
     captures_(nullptr),
     start_(chars),
     next_pos_(start_),
     end_(end),
@@ -1898,17 +1898,18 @@ RegExpParser<CharT>::ParseDisjunction()
     }
 }
 
 template class irregexp::RegExpParser<Latin1Char>;
 template class irregexp::RegExpParser<char16_t>;
 
 template <typename CharT>
 static bool
-ParsePattern(frontend::TokenStream& ts, LifoAlloc& alloc, const CharT* chars, size_t length,
+ParsePattern(frontend::TokenStreamAnyChars& ts, LifoAlloc& alloc,
+             const CharT* chars, size_t length,
              bool multiline, bool match_only, bool unicode, bool ignore_case,
              bool global, bool sticky, RegExpCompileData* data)
 {
     // We shouldn't strip pattern for exec, or test with global/sticky,
     // to reflect correct match position and lastIndex.
     if (match_only && !global && !sticky) {
         // Try to strip a leading '.*' from the RegExp, but only if it is not
         // followed by a '?' (which will affect how the .* is parsed). This
@@ -1937,47 +1938,47 @@ ParsePattern(frontend::TokenStream& ts, 
 
     data->simple = parser.simple();
     data->contains_anchor = parser.contains_anchor();
     data->capture_count = parser.captures_started();
     return true;
 }
 
 bool
-irregexp::ParsePattern(frontend::TokenStream& ts, LifoAlloc& alloc, JSAtom* str,
+irregexp::ParsePattern(frontend::TokenStreamAnyChars& ts, LifoAlloc& alloc, JSAtom* str,
                        bool multiline, bool match_only, bool unicode, bool ignore_case,
                        bool global, bool sticky, RegExpCompileData* data)
 {
     JS::AutoCheckCannotGC nogc;
     return str->hasLatin1Chars()
            ? ::ParsePattern(ts, alloc, str->latin1Chars(nogc), str->length(),
                             multiline, match_only, unicode, ignore_case, global, sticky, data)
            : ::ParsePattern(ts, alloc, str->twoByteChars(nogc), str->length(),
                             multiline, match_only, unicode, ignore_case, global, sticky, data);
 }
 
 template <typename CharT>
 static bool
-ParsePatternSyntax(frontend::TokenStream& ts, LifoAlloc& alloc, const CharT* chars, size_t length,
-                   bool unicode)
+ParsePatternSyntax(frontend::TokenStreamAnyChars& ts, LifoAlloc& alloc,
+                   const CharT* chars, size_t length, bool unicode)
 {
     LifoAllocScope scope(&alloc);
 
     RegExpParser<CharT> parser(ts, &alloc, chars, chars + length, false, unicode, false);
     return parser.ParsePattern() != nullptr;
 }
 
 bool
-irregexp::ParsePatternSyntax(frontend::TokenStream& ts, LifoAlloc& alloc, JSAtom* str,
+irregexp::ParsePatternSyntax(frontend::TokenStreamAnyChars& ts, LifoAlloc& alloc, JSAtom* str,
                              bool unicode)
 {
     JS::AutoCheckCannotGC nogc;
     return str->hasLatin1Chars()
            ? ::ParsePatternSyntax(ts, alloc, str->latin1Chars(nogc), str->length(), unicode)
            : ::ParsePatternSyntax(ts, alloc, str->twoByteChars(nogc), str->length(), unicode);
 }
 
 bool
-irregexp::ParsePatternSyntax(frontend::TokenStream& ts, LifoAlloc& alloc,
+irregexp::ParsePatternSyntax(frontend::TokenStreamAnyChars& ts, LifoAlloc& alloc,
                              const mozilla::Range<const char16_t> chars, bool unicode)
 {
     return ::ParsePatternSyntax(ts, alloc, chars.begin().get(), chars.length(), unicode);
 }
--- a/js/src/irregexp/RegExpParser.h
+++ b/js/src/irregexp/RegExpParser.h
@@ -35,32 +35,32 @@
 
 #include <stdarg.h>
 
 #include "irregexp/RegExpAST.h"
 
 namespace js {
 
 namespace frontend {
-    class TokenStream;
+    class TokenStreamAnyChars;
 }
 
 namespace irregexp {
 
-bool
-ParsePattern(frontend::TokenStream& ts, LifoAlloc& alloc, JSAtom* str,
+extern bool
+ParsePattern(frontend::TokenStreamAnyChars& ts, LifoAlloc& alloc, JSAtom* str,
              bool multiline, bool match_only, bool unicode, bool ignore_case,
              bool global, bool sticky, RegExpCompileData* data);
 
-bool
-ParsePatternSyntax(frontend::TokenStream& ts, LifoAlloc& alloc, JSAtom* str,
+extern bool
+ParsePatternSyntax(frontend::TokenStreamAnyChars& ts, LifoAlloc& alloc, JSAtom* str,
                    bool unicode);
 
-bool
-ParsePatternSyntax(frontend::TokenStream& ts, LifoAlloc& alloc,
+extern bool
+ParsePatternSyntax(frontend::TokenStreamAnyChars& ts, LifoAlloc& alloc,
                    const mozilla::Range<const char16_t> chars, bool unicode);
 
 // A BufferedVector is an automatically growing list, just like (and backed
 // by) a Vector, that is optimized for the case of adding and removing
 // a single element. The last element added is stored outside the backing list,
 // and if no more than one element is ever added, the ZoneList isn't even
 // allocated.
 // Elements must not be nullptr pointers.
@@ -179,17 +179,17 @@ class RegExpBuilder
 
 // Characters parsed by RegExpParser can be either char16_t or kEndMarker.
 typedef uint32_t widechar;
 
 template <typename CharT>
 class RegExpParser
 {
   public:
-    RegExpParser(frontend::TokenStream& ts, LifoAlloc* alloc,
+    RegExpParser(frontend::TokenStreamAnyChars& ts, LifoAlloc* alloc,
                  const CharT* chars, const CharT* end, bool multiline_mode, bool unicode,
                  bool ignore_case);
 
     RegExpTree* ParsePattern();
     RegExpTree* ParseDisjunction();
     RegExpTree* ParseCharacterClass();
 
     // Parses a {...,...} quantifier and stores the range in the given
@@ -297,17 +297,17 @@ class RegExpParser
     bool has_next() { return next_pos_ < end_; }
     widechar Next() {
         if (has_next())
             return *next_pos_;
         return kEndMarker;
     }
     void ScanForCaptures();
 
-    frontend::TokenStream& ts;
+    frontend::TokenStreamAnyChars& ts;
     LifoAlloc* alloc;
     RegExpCaptureVector* captures_;
     const CharT* const start_;
     const CharT* next_pos_;
     const CharT* end_;
     widechar current_;
     // The capture count is only valid after we have scanned for captures.
     int capture_count_;
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -6595,33 +6595,32 @@ JS_ObjectIsDate(JSContext* cx, HandleObj
 /*
  * Regular Expressions.
  */
 JS_PUBLIC_API(JSObject*)
 JS_NewRegExpObject(JSContext* cx, const char* bytes, size_t length, unsigned flags)
 {
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
+
     ScopedJSFreePtr<char16_t> chars(InflateString(cx, bytes, length));
     if (!chars)
         return nullptr;
 
-    RegExpObject* reobj = RegExpObject::create(cx, chars, length, RegExpFlag(flags),
-                                               nullptr, nullptr, cx->tempLifoAlloc(),
-                                               GenericObject);
-    return reobj;
+    return RegExpObject::create(cx, chars.get(), length, RegExpFlag(flags), cx->tempLifoAlloc(),
+                                GenericObject);
 }
 
 JS_PUBLIC_API(JSObject*)
 JS_NewUCRegExpObject(JSContext* cx, const char16_t* chars, size_t length, unsigned flags)
 {
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
-    return RegExpObject::create(cx, chars, length, RegExpFlag(flags),
-                                nullptr, nullptr, cx->tempLifoAlloc(),
+
+    return RegExpObject::create(cx, chars, length, RegExpFlag(flags), cx->tempLifoAlloc(),
                                 GenericObject);
 }
 
 JS_PUBLIC_API(bool)
 JS_SetRegExpInput(JSContext* cx, HandleObject obj, HandleString input)
 {
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -218,47 +218,81 @@ const Class RegExpObject::class_ = {
 
 const Class RegExpObject::protoClass_ = {
     js_Object_str,
     JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp),
     JS_NULL_CLASS_OPS,
     &RegExpObjectClassSpec
 };
 
+template<typename CharT>
 RegExpObject*
+RegExpObject::create(JSContext* cx, const CharT* chars, size_t length, RegExpFlag flags,
+                     frontend::TokenStreamAnyChars& tokenStream, LifoAlloc& alloc,
+                     NewObjectKind newKind)
+{
+    static_assert(mozilla::IsSame<CharT, char16_t>::value,
+                  "this code may need updating if/when CharT encodes UTF-8");
+
+    RootedAtom source(cx, AtomizeChars(cx, chars, length));
+    if (!source)
+        return nullptr;
+
+    return create(cx, source, flags, tokenStream, alloc, newKind);
+}
+
+template RegExpObject*
 RegExpObject::create(JSContext* cx, const char16_t* chars, size_t length, RegExpFlag flags,
-                     const ReadOnlyCompileOptions* options, TokenStream* tokenStream,
+                     frontend::TokenStreamAnyChars& tokenStream, LifoAlloc& alloc,
+                     NewObjectKind newKind);
+
+template<typename CharT>
+RegExpObject*
+RegExpObject::create(JSContext* cx, const CharT* chars, size_t length, RegExpFlag flags,
                      LifoAlloc& alloc, NewObjectKind newKind)
 {
+    static_assert(mozilla::IsSame<CharT, char16_t>::value,
+                  "this code may need updating if/when CharT encodes UTF-8");
+
     RootedAtom source(cx, AtomizeChars(cx, chars, length));
     if (!source)
         return nullptr;
 
-    return create(cx, source, flags, options, tokenStream, alloc, newKind);
+    return create(cx, source, flags, alloc, newKind);
 }
 
+template RegExpObject*
+RegExpObject::create(JSContext* cx, const char16_t* chars, size_t length, RegExpFlag flags,
+                     LifoAlloc& alloc, NewObjectKind newKind);
+
 RegExpObject*
 RegExpObject::create(JSContext* cx, HandleAtom source, RegExpFlag flags,
-                     const ReadOnlyCompileOptions* options, TokenStream* tokenStream,
+                     frontend::TokenStreamAnyChars& tokenStream,
                      LifoAlloc& alloc, NewObjectKind newKind)
 {
-    Maybe<CompileOptions> dummyOptions;
-    if (!tokenStream && !options) {
-        dummyOptions.emplace(cx);
-        options = dummyOptions.ptr();
-    }
-    Maybe<TokenStream> dummyTokenStream;
-    if (!tokenStream) {
-        dummyTokenStream.emplace(cx, *options,
-                                   (const char16_t*) nullptr, 0,
-                                   (frontend::StrictModeGetter*) nullptr);
-        tokenStream = dummyTokenStream.ptr();
-    }
+    if (!irregexp::ParsePatternSyntax(tokenStream, alloc, source, flags & UnicodeFlag))
+        return nullptr;
+
+    Rooted<RegExpObject*> regexp(cx, RegExpAlloc(cx, newKind));
+    if (!regexp)
+        return nullptr;
+
+    regexp->initAndZeroLastIndex(source, flags, cx);
 
-    if (!irregexp::ParsePatternSyntax(*tokenStream, alloc, source, flags & UnicodeFlag))
+    return regexp;
+}
+
+RegExpObject*
+RegExpObject::create(JSContext* cx, HandleAtom source, RegExpFlag flags, LifoAlloc& alloc,
+                     NewObjectKind newKind)
+{
+    CompileOptions dummyOptions(cx);
+    TokenStream dummyTokenStream(cx, dummyOptions, (const char16_t*) nullptr, 0, nullptr);
+
+    if (!irregexp::ParsePatternSyntax(dummyTokenStream, alloc, source, flags & UnicodeFlag))
         return nullptr;
 
     Rooted<RegExpObject*> regexp(cx, RegExpAlloc(cx, newKind));
     if (!regexp)
         return nullptr;
 
     regexp->initAndZeroLastIndex(source, flags, cx);
 
@@ -1430,23 +1464,18 @@ js::XDRScriptRegExpObject(XDRState<mode>
         MOZ_ASSERT(objp);
         RegExpObject& reobj = *objp;
         source = reobj.getSource();
         flagsword = reobj.getFlags();
     }
     if (!XDRAtom(xdr, &source) || !xdr->codeUint32(&flagsword))
         return false;
     if (mode == XDR_DECODE) {
-        RegExpFlag flags = RegExpFlag(flagsword);
-        const ReadOnlyCompileOptions* options = nullptr;
-        if (xdr->hasOptions())
-            options = &xdr->options();
-        RegExpObject* reobj = RegExpObject::create(xdr->cx(), source, flags,
-                                                   options, nullptr, xdr->lifoAlloc(),
-                                                   TenuredObject);
+        RegExpObject* reobj = RegExpObject::create(xdr->cx(), source, RegExpFlag(flagsword),
+                                                   xdr->lifoAlloc(), TenuredObject);
         if (!reobj)
             return false;
 
         objp.set(reobj);
     }
     return true;
 }
 
@@ -1459,19 +1488,17 @@ js::XDRScriptRegExpObject(XDRState<XDR_D
 JSObject*
 js::CloneScriptRegExpObject(JSContext* cx, RegExpObject& reobj)
 {
     /* NB: Keep this in sync with XDRScriptRegExpObject. */
 
     RootedAtom source(cx, reobj.getSource());
     cx->markAtom(source);
 
-    return RegExpObject::create(cx, source, reobj.getFlags(),
-                                nullptr, nullptr, cx->tempLifoAlloc(),
-                                TenuredObject);
+    return RegExpObject::create(cx, source, reobj.getFlags(), cx->tempLifoAlloc(), TenuredObject);
 }
 
 JS_FRIEND_API(RegExpShared*)
 js::RegExpToSharedNonInline(JSContext* cx, HandleObject obj)
 {
     return RegExpToShared(cx, obj);
 }
 
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -38,17 +38,17 @@ namespace JS { struct Zone; }
  *     Owns all RegExpShared instances in a zone.
  */
 namespace js {
 
 struct MatchPair;
 class MatchPairs;
 class RegExpStatics;
 
-namespace frontend { class TokenStream; }
+namespace frontend { class TokenStreamAnyChars; }
 
 extern RegExpObject*
 RegExpAlloc(JSContext* cx, NewObjectKind newKind, HandleObject proto = nullptr);
 
 extern JSObject*
 CloneRegExpObject(JSContext* cx, Handle<RegExpObject*> regex);
 
 class RegExpObject : public NativeObject
@@ -66,25 +66,33 @@ class RegExpObject : public NativeObject
 
     static const Class class_;
     static const Class protoClass_;
 
     // The maximum number of pairs a MatchResult can have, without having to
     // allocate a bigger MatchResult.
     static const size_t MaxPairCount = 14;
 
+    template<typename CharT>
     static RegExpObject*
-    create(JSContext* cx, const char16_t* chars, size_t length, RegExpFlag flags,
-           const ReadOnlyCompileOptions* options, frontend::TokenStream* ts, LifoAlloc& alloc,
+    create(JSContext* cx, const CharT* chars, size_t length, RegExpFlag flags, LifoAlloc& alloc,
+           NewObjectKind newKind);
+
+    template<typename CharT>
+    static RegExpObject*
+    create(JSContext* cx, const CharT* chars, size_t length, RegExpFlag flags,
+           frontend::TokenStreamAnyChars& ts, LifoAlloc& alloc, NewObjectKind kind);
+
+    static RegExpObject*
+    create(JSContext* cx, HandleAtom atom, RegExpFlag flags, LifoAlloc& alloc,
            NewObjectKind newKind);
 
     static RegExpObject*
-    create(JSContext* cx, HandleAtom atom, RegExpFlag flags,
-           const ReadOnlyCompileOptions* options, frontend::TokenStream* ts, LifoAlloc& alloc,
-           NewObjectKind newKind);
+    create(JSContext* cx, HandleAtom atom, RegExpFlag flags, frontend::TokenStreamAnyChars& ts,
+           LifoAlloc& alloc, NewObjectKind newKind);
 
     /*
      * Compute the initial shape to associate with fresh RegExp objects,
      * encoding their initial properties. Return the shape after
      * changing |obj|'s last property to it.
      */
     static Shape*
     assignInitialShape(JSContext* cx, Handle<RegExpObject*> obj);
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -2977,18 +2977,17 @@ CloneObject(JSContext* cx, HandleNativeO
             }
         } else {
             clone = CloneSelfHostingIntrinsic(cx, selfHostedFunction);
         }
     } else if (selfHostedObject->is<RegExpObject>()) {
         RegExpObject& reobj = selfHostedObject->as<RegExpObject>();
         RootedAtom source(cx, reobj.getSource());
         MOZ_ASSERT(source->isPermanentAtom());
-        clone = RegExpObject::create(cx, source, reobj.getFlags(),
-                                     nullptr, nullptr, cx->tempLifoAlloc(),
+        clone = RegExpObject::create(cx, source, reobj.getFlags(), cx->tempLifoAlloc(),
                                      TenuredObject);
     } else if (selfHostedObject->is<DateObject>()) {
         clone = JS::NewDateObject(cx, selfHostedObject->as<DateObject>().clippedTime());
     } else if (selfHostedObject->is<BooleanObject>()) {
         clone = BooleanObject::create(cx, selfHostedObject->as<BooleanObject>().unbox());
     } else if (selfHostedObject->is<NumberObject>()) {
         clone = NumberObject::create(cx, selfHostedObject->as<NumberObject>().unbox());
     } else if (selfHostedObject->is<StringObject>()) {
--- a/js/src/vm/StructuredClone.cpp
+++ b/js/src/vm/StructuredClone.cpp
@@ -2202,17 +2202,17 @@ JSStructuredCloneReader::startRead(Mutab
         JSString* str = readString(stringData);
         if (!str)
             return false;
 
         RootedAtom atom(context(), AtomizeString(context(), str));
         if (!atom)
             return false;
 
-        RegExpObject* reobj = RegExpObject::create(context(), atom, flags, nullptr, nullptr,
+        RegExpObject* reobj = RegExpObject::create(context(), atom, flags,
                                                    context()->tempLifoAlloc(), GenericObject);
         if (!reobj)
             return false;
         vp.setObject(*reobj);
         break;
       }
 
       case SCTAG_ARRAY_OBJECT:
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -756,32 +756,32 @@ static inline ParseNode*
 NextNonEmptyStatement(ParseNode* pn)
 {
     return SkipEmptyStatements(pn->pn_next);
 }
 
 static bool
 GetToken(AsmJSParser& parser, TokenKind* tkp)
 {
-    TokenStream& ts = parser.tokenStream;
+    auto& ts = parser.tokenStream;
     TokenKind tk;
     while (true) {
         if (!ts.getToken(&tk, TokenStream::Operand))
             return false;
         if (tk != TOK_SEMI)
             break;
     }
     *tkp = tk;
     return true;
 }
 
 static bool
 PeekToken(AsmJSParser& parser, TokenKind* tkp)
 {
-    TokenStream& ts = parser.tokenStream;
+    auto& ts = parser.tokenStream;
     TokenKind tk;
     while (true) {
         if (!ts.peekToken(&tk, TokenStream::Operand))
             return false;
         if (tk != TOK_SEMI)
             break;
         ts.consumeKnownToken(TOK_SEMI, TokenStream::Operand);
     }
@@ -1784,33 +1784,34 @@ class MOZ_STACK_CLASS ModuleValidator
             ReportOverRecursed(cx_);
     }
 
   private:
     void typeFailure(uint32_t offset, ...) {
         va_list args;
         va_start(args, offset);
 
-        TokenStream& ts = tokenStream();
+        auto& ts = tokenStream();
         ErrorMetadata metadata;
         if (ts.computeErrorMetadata(&metadata, offset)) {
-            if (ts.options().throwOnAsmJSValidationFailureOption) {
+            if (ts.anyCharsAccess().options().throwOnAsmJSValidationFailureOption) {
                 ReportCompileError(cx_, Move(metadata), nullptr, JSREPORT_ERROR,
                                    JSMSG_USE_ASM_TYPE_FAIL, args);
             } else {
                 // asm.js type failure is indicated by calling one of the fail*
                 // functions below.  These functions always return false to
                 // halt asm.js parsing.  Whether normal parsing is attempted as
                 // fallback, depends whether an exception is also set.
                 //
                 // If warning succeeds, no exception is set.  If warning fails,
                 // an exception is set and execution will halt.  Thus it's safe
                 // and correct to ignore the return value here.
-                Unused << ts.compileWarning(Move(metadata), nullptr, JSREPORT_WARNING,
-                                            JSMSG_USE_ASM_TYPE_FAIL, args);
+                Unused << ts.anyCharsAccess().compileWarning(Move(metadata), nullptr,
+                                                             JSREPORT_WARNING,
+                                                             JSMSG_USE_ASM_TYPE_FAIL, args);
             }
         }
 
         va_end(args);
     }
 
   public:
     bool init() {
@@ -1897,18 +1898,25 @@ class MOZ_STACK_CLASS ModuleValidator
     }
 
     JSContext* cx() const                    { return cx_; }
     PropertyName* moduleFunctionName() const { return moduleFunctionName_; }
     PropertyName* globalArgumentName() const { return globalArgumentName_; }
     PropertyName* importArgumentName() const { return importArgumentName_; }
     PropertyName* bufferArgumentName() const { return bufferArgumentName_; }
     const ModuleEnvironment& env()           { return env_; }
-    AsmJSParser& parser() const              { return parser_; }
-    TokenStream& tokenStream() const         { return parser_.tokenStream; }
+
+    AsmJSParser& parser() const { return parser_; }
+
+    auto tokenStream() const
+      -> decltype(parser_.tokenStream)&
+    {
+        return parser_.tokenStream;
+    }
+
     RootedFunction& dummyFunction()          { return dummyFunction_; }
     bool supportsSimd() const                { return cx_->jitSupportsSimd(); }
     bool atomicsPresent() const              { return atomicsPresent_; }
     uint32_t minMemoryLength() const         { return env_.minMemoryLength; }
 
     void initModuleFunctionName(PropertyName* name) {
         MOZ_ASSERT(!moduleFunctionName_);
         moduleFunctionName_ = name;
@@ -2296,17 +2304,17 @@ class MOZ_STACK_CLASS ModuleValidator
         MOZ_ASSERT(errorOffset_ == UINT32_MAX);
         MOZ_ASSERT(str);
         errorOffset_ = offset;
         errorString_ = DuplicateString(str);
         return false;
     }
 
     bool failCurrentOffset(const char* str) {
-        return failOffset(tokenStream().currentToken().pos.begin, str);
+        return failOffset(tokenStream().anyCharsAccess().currentToken().pos.begin, str);
     }
 
     bool fail(ParseNode* pn, const char* str) {
         return failOffset(pn->pn_pos.begin, str);
     }
 
     bool failfVAOffset(uint32_t offset, const char* fmt, va_list ap) MOZ_FORMAT_PRINTF(3, 0) {
         MOZ_ASSERT(!hasAlreadyFailed());
@@ -2438,17 +2446,17 @@ class MOZ_STACK_CLASS ModuleValidator
         if (!asmJSMetadata_->asmJSFuncNames.resize(funcImportMap_.count()))
             return nullptr;
         for (const Func& func : funcDefs_) {
             CacheableChars funcName = StringToNewUTF8CharsZ(cx_, *func.name());
             if (!funcName || !asmJSMetadata_->asmJSFuncNames.emplaceBack(Move(funcName)))
                 return nullptr;
         }
 
-        uint32_t endBeforeCurly = tokenStream().currentToken().pos.end;
+        uint32_t endBeforeCurly = tokenStream().anyCharsAccess().currentToken().pos.end;
         asmJSMetadata_->srcLength = endBeforeCurly - asmJSMetadata_->srcStart;
 
         TokenPos pos;
         JS_ALWAYS_TRUE(tokenStream().peekTokenPos(&pos, TokenStream::Operand));
         uint32_t endAfterCurly = pos.end;
         asmJSMetadata_->srcLengthWithRightBrace = endAfterCurly - asmJSMetadata_->srcStart;
 
         ScriptedCaller scriptedCaller;
@@ -3278,25 +3286,32 @@ class MOZ_STACK_CLASS FunctionValidator
             return encoder().writeOp(MozOp::B32x4Const) &&
                    encoder().writeFixedI32x4(lit.simdValue().asInt32x4());
           case NumLit::OutOfRangeInt:
             break;
         }
         MOZ_CRASH("unexpected literal type");
     }
     MOZ_MUST_USE bool writeCall(ParseNode* pn, Op op) {
-        return encoder().writeOp(op) &&
-               callSiteLineNums_.append(m().tokenStream().srcCoords.lineNum(pn->pn_pos.begin));
+        if (!encoder().writeOp(op))
+            return false;
+
+        TokenStreamAnyChars& anyChars = m().tokenStream().anyCharsAccess();
+        return callSiteLineNums_.append(anyChars.srcCoords.lineNum(pn->pn_pos.begin));
     }
     MOZ_MUST_USE bool writeCall(ParseNode* pn, MozOp op) {
-        return encoder().writeOp(op) &&
-               callSiteLineNums_.append(m().tokenStream().srcCoords.lineNum(pn->pn_pos.begin));
+        if (!encoder().writeOp(op))
+            return false;
+
+        TokenStreamAnyChars& anyChars = m().tokenStream().anyCharsAccess();
+        return callSiteLineNums_.append(anyChars.srcCoords.lineNum(pn->pn_pos.begin));
     }
     MOZ_MUST_USE bool prepareCall(ParseNode* pn) {
-        return callSiteLineNums_.append(m().tokenStream().srcCoords.lineNum(pn->pn_pos.begin));
+        TokenStreamAnyChars& anyChars = m().tokenStream().anyCharsAccess();
+        return callSiteLineNums_.append(anyChars.srcCoords.lineNum(pn->pn_pos.begin));
     }
     MOZ_MUST_USE bool writeSimdOp(SimdType simdType, SimdOperation simdOp) {
         MozOp op = SimdToOp(simdType, simdOp);
         if (op == MozOp::Limit)
             return true;
         return encoder().writeOp(op);
     }
 };
@@ -3821,25 +3836,25 @@ CheckModuleGlobal(ModuleValidator& m, Pa
         return CheckGlobalDotImport(m, var->name(), initNode);
 
     return m.fail(initNode, "unsupported import expression");
 }
 
 static bool
 CheckModuleProcessingDirectives(ModuleValidator& m)
 {
-    TokenStream& ts = m.parser().tokenStream;
+    auto& ts = m.parser().tokenStream;
     while (true) {
         bool matched;
         if (!ts.matchToken(&matched, TOK_STRING, TokenStream::Operand))
             return false;
         if (!matched)
             return true;
 
-        if (!IsIgnoredDirectiveName(m.cx(), ts.currentToken().atom()))
+        if (!IsIgnoredDirectiveName(m.cx(), ts.anyCharsAccess().currentToken().atom()))
             return m.failCurrentOffset("unsupported processing directive");
 
         TokenKind tt;
         if (!ts.getToken(&tt))
             return false;
         if (tt != TOK_SEMI)
             return m.failCurrentOffset("expected semicolon after string literal");
     }
@@ -7124,21 +7139,23 @@ CheckStatement(FunctionValidator& f, Par
     }
 
     return f.fail(stmt, "unexpected statement kind");
 }
 
 static bool
 ParseFunction(ModuleValidator& m, ParseNode** fnOut, unsigned* line)
 {
-    TokenStream& tokenStream = m.tokenStream();
+    auto& tokenStream = m.tokenStream();
 
     tokenStream.consumeKnownToken(TOK_FUNCTION, TokenStream::Operand);
-    uint32_t toStringStart = tokenStream.currentToken().pos.begin;
-    *line = tokenStream.srcCoords.lineNum(tokenStream.currentToken().pos.end);
+
+    auto& anyChars = tokenStream.anyCharsAccess();
+    uint32_t toStringStart = anyChars.currentToken().pos.begin;
+    *line = anyChars.srcCoords.lineNum(anyChars.currentToken().pos.end);
 
     TokenKind tk;
     if (!tokenStream.getToken(&tk, TokenStream::Operand))
         return false;
     if (tk == TOK_MUL)
         return m.failCurrentOffset("unexpected generator function");
     if (!TokenKindIsPossibleIdentifier(tk))
         return false;  // The regular parser will throw a SyntaxError, no need to m.fail.
@@ -7165,23 +7182,23 @@ ParseFunction(ModuleValidator& m, ParseN
     funbox->initWithEnclosingParseContext(outerpc, frontend::Statement);
 
     Directives newDirectives = directives;
     SourceParseContext funpc(&m.parser(), funbox, &newDirectives);
     if (!funpc.init())
         return false;
 
     if (!m.parser().functionFormalParametersAndBody(InAllowed, YieldIsName, fn, Statement)) {
-        if (tokenStream.hadError() || directives == newDirectives)
+        if (anyChars.hadError() || directives == newDirectives)
             return false;
 
         return m.fail(fn, "encountered new directive in function");
     }
 
-    MOZ_ASSERT(!tokenStream.hadError());
+    MOZ_ASSERT(!anyChars.hadError());
     MOZ_ASSERT(directives == newDirectives);
 
     *fnOut = fn;
     return true;
 }
 
 static bool
 CheckFunction(ModuleValidator& m)
@@ -7386,23 +7403,23 @@ CheckModuleExportObject(ModuleValidator&
 }
 
 static bool
 CheckModuleReturn(ModuleValidator& m)
 {
     TokenKind tk;
     if (!GetToken(m.parser(), &tk))
         return false;
-    TokenStream& ts = m.parser().tokenStream;
+    auto& ts = m.parser().tokenStream;
     if (tk != TOK_RETURN) {
         return m.failCurrentOffset((tk == TOK_RC || tk == TOK_EOF)
                                    ? "expecting return statement"
                                    : "invalid asm.js. statement");
     }
-    ts.ungetToken();
+    ts.anyCharsAccess().ungetToken();
 
     ParseNode* returnStmt = m.parser().statementListItem(YieldIsName);
     if (!returnStmt)
         return false;
 
     ParseNode* returnExpr = ReturnExpr(returnStmt);
     if (!returnExpr)
         return m.fail(returnStmt, "export statement must return something");
@@ -7423,17 +7440,17 @@ CheckModuleEnd(ModuleValidator &m)
 {
     TokenKind tk;
     if (!GetToken(m.parser(), &tk))
         return false;
 
     if (tk != TOK_EOF && tk != TOK_RC)
         return m.failCurrentOffset("top-level export (return) must be the last statement");
 
-    m.parser().tokenStream.ungetToken();
+    m.parser().tokenStream.anyCharsAccess().ungetToken();
     return true;
 }
 
 static SharedModule
 CheckModule(JSContext* cx, AsmJSParser& parser, ParseNode* stmtList, unsigned* time)
 {
     int64_t before = PRMJ_Now();