Bug 1508063 - Part 5: Move non-auto-generated part of BinASTParser into BinASTParserPerTokenizer. r=Yoric
authorTooru Fujisawa <arai_a@mac.com>
Thu, 29 Nov 2018 01:03:46 +0900
changeset 504951 44cd6ec4fbd5da73777883f4faf9fe822f7de513
parent 504950 02386132c1e9bfa3ef88b9ae7d967f90b6e1045e
child 504952 4201f7161e7a5323d7a9250f08c1d046aa85456d
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersYoric
bugs1508063
milestone65.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 1508063 - Part 5: Move non-auto-generated part of BinASTParser into BinASTParserPerTokenizer. r=Yoric
config/check_spidermonkey_style.py
js/src/frontend/BinASTParser.cpp
js/src/frontend/BinASTParser.h
js/src/frontend/BinASTParserPerTokenizer.cpp
js/src/frontend/BinASTParserPerTokenizer.h
js/src/frontend/BinSource.yaml
js/src/frontend/BytecodeCompiler.cpp
js/src/frontend/binsource/src/main.rs
js/src/fuzz-tests/testBinASTReader.cpp
js/src/jsapi-tests/testBinASTReader.cpp
js/src/shell/js.cpp
--- a/config/check_spidermonkey_style.py
+++ b/config/check_spidermonkey_style.py
@@ -108,17 +108,16 @@ included_inclnames_to_ignore = set([
     'unicode/uversion.h',       # ICU
     'vtune/VTuneWrapper.h'      # VTune
 ])
 
 # These files have additional constraints on where they are #included, so we
 # ignore #includes of them when checking #include ordering.
 oddly_ordered_inclnames = set([
     'ctypes/typedefs.h',        # Included multiple times in the body of ctypes/CTypes.h
-    'frontend/BinASTParser.h',  # Included in the body of frontend/BinSource.h
     # Included in the body of frontend/TokenStream.h
     'frontend/ReservedWordsGenerated.h',
     'gc/StatsPhasesGenerated.h',         # Included in the body of gc/Statistics.h
     'gc/StatsPhasesGenerated.cpp',       # Included in the body of gc/Statistics.cpp
     'psapi.h',                  # Must be included after "util/Windows.h" on Windows
     'machine/endian.h',         # Must be included after <sys/types.h> on BSD
     'winbase.h',                # Must precede other system headers(?)
     'windef.h'                  # Must precede other system headers(?)
--- a/js/src/frontend/BinASTParser.cpp
+++ b/js/src/frontend/BinASTParser.cpp
@@ -4,24 +4,25 @@
 * vim: set ts=8 sts=4 et sw=4 tw=99:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // To generate this file, see the documentation in
 // js/src/frontend/binsource/README.md.
 
+#include "frontend/BinASTParser.h"
+
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Casting.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/Move.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/Vector.h"
 
-#include "frontend/BinASTParserPerTokenizer.h"
 #include "frontend/BinSource-macros.h"
 #include "frontend/BinTokenReaderTester.h"
 #include "frontend/FullParseHandler.h"
 #include "frontend/ParseNode.h"
 #include "frontend/Parser.h"
 #include "frontend/SharedContext.h"
 
 #include "vm/RegExpObject.h"
@@ -2949,17 +2950,17 @@ BinASTParser<Tok>::parseInterfaceGetterC
     MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif // defined(DEBUG)
 
     BINJS_MOZ_TRY_DECL(isThisCaptured, tokenizer_->readBool());
     // TODO: Use this in BinASTParser::buildFunction.
     (void) isThisCaptured;
     MOZ_TRY(parseAssertedVarScope());
 
-    BINJS_TRY_DECL(params, new_<ListNode>(ParseNodeKind::ParamsBody, tokenizer_->pos(start)));
+    BINJS_TRY_DECL(params, this->template new_<ListNode>(ParseNodeKind::ParamsBody, tokenizer_->pos(start)));
     BINJS_MOZ_TRY_DECL(body, parseFunctionBody());
 
     *paramsOut = params;
     *bodyOut = body;
     auto result = Ok();
     return result;
 }
 
@@ -3489,17 +3490,17 @@ BinASTParser<Tok>::parseInterfaceSetterC
     BINJS_MOZ_TRY_DECL(isThisCaptured, tokenizer_->readBool());
     // TODO: Use this in BinASTParser::buildFunction.
     (void) isThisCaptured;
     Rooted<GCVector<JSAtom*>> positionalParams(cx_, GCVector<JSAtom*>(cx_));
     MOZ_TRY(parseAssertedParameterScope(
         &positionalParams));
 
     BINJS_MOZ_TRY_DECL(param, parseParameter());
-    BINJS_TRY_DECL(params, new_<ListNode>(ParseNodeKind::ParamsBody, param->pn_pos));
+    BINJS_TRY_DECL(params, this->template new_<ListNode>(ParseNodeKind::ParamsBody, param->pn_pos));
     factory_.addList(params, param);
     MOZ_TRY(checkPositionalParameterIndices(positionalParams, params));
     MOZ_TRY(parseAssertedVarScope());
 
     BINJS_MOZ_TRY_DECL(body, parseFunctionBody());
 
     *paramsOut = params;
     *bodyOut = body;
@@ -4511,17 +4512,17 @@ BinASTParser<Tok>::parseListOfOptionalSp
 template<typename Tok> JS::Result<ListNode*>
 BinASTParser<Tok>::parseListOfParameter()
 {
     uint32_t length;
     AutoList guard(*tokenizer_);
 
     const auto start = tokenizer_->offset();
     MOZ_TRY(tokenizer_->enterList(length, guard));
-    BINJS_TRY_DECL(result, new_<ListNode>(ParseNodeKind::ParamsBody, tokenizer_->pos(start)));
+    BINJS_TRY_DECL(result, this->template new_<ListNode>(ParseNodeKind::ParamsBody, tokenizer_->pos(start)));
 
     for (uint32_t i = 0; i < length; ++i) {
         BINJS_MOZ_TRY_DECL(item, parseParameter());
         factory_.addList(/* list = */ result, /* kid = */ item);
     }
 
     MOZ_TRY(guard.done());
     return result;
--- a/js/src/frontend/BinASTParser.h
+++ b/js/src/frontend/BinASTParser.h
@@ -4,243 +4,350 @@
 * vim: set ts=8 sts=4 et sw=4 tw=99:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // To generate this file, see the documentation in
 // js/src/frontend/binsource/README.md.
 
-// This file is meant to be included from the declaration
-// of class `BinASTParser`. The include may be public or private.
+#ifndef frontend_BinASTParser_h
+#define frontend_BinASTParser_h
+
+#include "mozilla/Maybe.h"
+
+#include "frontend/BCEParserHandle.h"
+#include "frontend/BinASTParserPerTokenizer.h"
+#include "frontend/BinToken.h"
+#include "frontend/BinTokenReaderMultipart.h"
+#include "frontend/BinTokenReaderTester.h"
+#include "frontend/FullParseHandler.h"
+#include "frontend/ParseContext.h"
+#include "frontend/ParseNode.h"
+#include "frontend/SharedContext.h"
+
+#include "js/CompileOptions.h"
+#include "js/GCHashTable.h"
+#include "js/GCVector.h"
+#include "js/Result.h"
+
+namespace js {
+namespace frontend {
+
+template<typename Tok>
+class BinASTParser : public BinASTParserPerTokenizer<Tok>
+{
+  public:
+    using Base = BinASTParserPerTokenizer<Tok>;
+
+    using Tokenizer = Tok;
 
+    using BinFields = typename Tokenizer::BinFields;
+    using AutoList = typename Tokenizer::AutoList;
+    using AutoTaggedTuple = typename Tokenizer::AutoTaggedTuple;
+    using AutoTuple = typename Tokenizer::AutoTuple;
+    using Chars = typename Tokenizer::Chars;
+
+  public:
+    // Auto-generated types.
+    using AssertedDeclaredKind = binast::AssertedDeclaredKind;
+    using BinaryOperator = binast::BinaryOperator;
+    using CompoundAssignmentOperator = binast::CompoundAssignmentOperator;
+    using UnaryOperator = binast::UnaryOperator;
+    using UpdateOperator = binast::UpdateOperator;
+    using VariableDeclarationKind = binast::VariableDeclarationKind;
+
+  public:
+    // BinASTParserPerTokenizer types.
+    using AssertedScopeKind = typename Base::AssertedScopeKind;
+
+  public:
+    BinASTParser(JSContext* cx, LifoAlloc& alloc, UsedNameTracker& usedNames,
+                 const JS::ReadOnlyCompileOptions& options,
+                 HandleScriptSourceObject sourceObject,
+                 Handle<LazyScript*> lazyScript = nullptr)
+        : BinASTParserPerTokenizer<Tok>(cx, alloc, usedNames, options, sourceObject, lazyScript)
+    {}
+    ~BinASTParser()
+    {}
+
+  protected:
+    // BinASTParserBase fields.
+    using Base::cx_;
+
+    using Base::alloc_;
+    using Base::usedNames_;
+
+    using Base::sourceObject_;
+    using Base::parseContext_;
+    using Base::factory_;
 
-// ----- Sums of interfaces (by lexicographical order)
-// Implementations are autogenerated
-// `ParseNode*` may never be nullptr
-JS::Result<Ok> parseAssertedMaybePositionalParameterName(
-    AssertedScopeKind scopeKind,
-    MutableHandle<GCVector<JSAtom*>> positionalParams);
-JS::Result<ParseNode*> parseAssignmentTarget();
-JS::Result<ParseNode*> parseBinding();
-JS::Result<ParseNode*> parseExpression();
-JS::Result<ParseNode*> parseExpressionOrSuper();
-JS::Result<ParseNode*> parseForInOfBindingOrAssignmentTarget();
-JS::Result<ParseNode*> parseObjectProperty();
-JS::Result<ParseNode*> parseParameter();
-JS::Result<ParseNode*> parseProgram();
-JS::Result<ParseNode*> parsePropertyName();
-JS::Result<ParseNode*> parseSimpleAssignmentTarget();
-JS::Result<ParseNode*> parseSpreadElementOrExpression();
-JS::Result<ParseNode*> parseStatement();
-JS::Result<Ok> parseSumAssertedMaybePositionalParameterName(const size_t start, const BinKind kind, const BinFields& fields,
-    AssertedScopeKind scopeKind,
-    MutableHandle<GCVector<JSAtom*>> positionalParams);
-JS::Result<ParseNode*> parseSumAssignmentTarget(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseSumBinding(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseSumExpression(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseSumExpressionOrSuper(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseSumForInOfBindingOrAssignmentTarget(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseSumObjectProperty(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseSumParameter(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseSumProgram(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseSumPropertyName(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseSumSimpleAssignmentTarget(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseSumSpreadElementOrExpression(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseSumStatement(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseSumVariableDeclarationOrExpression(const size_t start, const BinKind kind, const BinFields& fields);
+  protected:
+    // BinASTParserPerTokenizer types.
+    using AutoVariableDeclarationKind = typename Base::AutoVariableDeclarationKind;
+  protected:
+    // BinASTParserPerTokenizer fields.
+    using Base::tokenizer_;
+    using Base::variableDeclarationKind_;
+
+  protected:
+    // BinASTParserPerTokenizer methods.
+    using Base::raiseInvalidClosedVar;
+    using Base::raiseMissingVariableInAssertedScope;
+    using Base::raiseMissingDirectEvalInAssertedScope;
+    using Base::raiseInvalidKind;
+    using Base::raiseInvalidVariant;
+    using Base::raiseMissingField;
+    using Base::raiseEmpty;
+    using Base::raiseOOM;
+    using Base::raiseError;
+
+    using Base::makeEmptyFunctionNode;
+    using Base::buildFunction;
+    using Base::buildFunctionBox;
+    using Base::addScopeName;
+    using Base::captureFunctionName;
+
+    using Base::getDeclaredScope;
+    using Base::getBoundScope;
+    using Base::checkBinding;
+    using Base::checkPositionalParameterIndices;
+
+    using Base::checkFunctionLength;
+    using Base::checkClosedVars;
+
+    using Base::prependDirectivesToBody;
 
+    using Base::forceStrictIfNecessary;
+
+  public:
+
+    // ----- Sums of interfaces (by lexicographical order)
+    // `ParseNode*` may never be nullptr
+    JS::Result<Ok> parseAssertedMaybePositionalParameterName(
+        AssertedScopeKind scopeKind,
+        MutableHandle<GCVector<JSAtom*>> positionalParams);
+    JS::Result<ParseNode*> parseAssignmentTarget();
+    JS::Result<ParseNode*> parseBinding();
+    JS::Result<ParseNode*> parseExpression();
+    JS::Result<ParseNode*> parseExpressionOrSuper();
+    JS::Result<ParseNode*> parseForInOfBindingOrAssignmentTarget();
+    JS::Result<ParseNode*> parseObjectProperty();
+    JS::Result<ParseNode*> parseParameter();
+    JS::Result<ParseNode*> parseProgram();
+    JS::Result<ParseNode*> parsePropertyName();
+    JS::Result<ParseNode*> parseSimpleAssignmentTarget();
+    JS::Result<ParseNode*> parseSpreadElementOrExpression();
+    JS::Result<ParseNode*> parseStatement();
+    JS::Result<Ok> parseSumAssertedMaybePositionalParameterName(const size_t start, const BinKind kind, const BinFields& fields,
+        AssertedScopeKind scopeKind,
+        MutableHandle<GCVector<JSAtom*>> positionalParams);
+    JS::Result<ParseNode*> parseSumAssignmentTarget(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseSumBinding(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseSumExpression(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseSumExpressionOrSuper(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseSumForInOfBindingOrAssignmentTarget(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseSumObjectProperty(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseSumParameter(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseSumProgram(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseSumPropertyName(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseSumSimpleAssignmentTarget(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseSumSpreadElementOrExpression(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseSumStatement(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseSumVariableDeclarationOrExpression(const size_t start, const BinKind kind, const BinFields& fields);
 
-// ----- Interfaces (by lexicographical order)
-// Implementations are autogenerated
-// `ParseNode*` may never be nullptr
-JS::Result<Ok> parseAssertedBlockScope();
-JS::Result<Ok> parseAssertedBoundName(
-    AssertedScopeKind scopeKind);
-JS::Result<Ok> parseAssertedBoundNamesScope();
-JS::Result<Ok> parseAssertedDeclaredName(
-    AssertedScopeKind scopeKind);
-JS::Result<Ok> parseAssertedParameterScope(
-    MutableHandle<GCVector<JSAtom*>> positionalParams);
-JS::Result<Ok> parseAssertedScriptGlobalScope();
-JS::Result<Ok> parseAssertedVarScope();
-JS::Result<ParseNode*> parseBindingIdentifier();
-JS::Result<ParseNode*> parseBlock();
-JS::Result<LexicalScopeNode*> parseCatchClause();
-JS::Result<ParseNode*> parseDirective();
-JS::Result<ListNode*> parseFormalParameters();
-JS::Result<Ok> parseFunctionExpressionContents(
-    uint32_t funLength,
-    ListNode** paramsOut,
-    ListNode** bodyOut);
-JS::Result<Ok> parseFunctionOrMethodContents(
-    uint32_t funLength,
-    ListNode** paramsOut,
-    ListNode** bodyOut);
-JS::Result<Ok> parseGetterContents(
-    uint32_t funLength,
-    ListNode** paramsOut,
-    ListNode** bodyOut);
-JS::Result<ParseNode*> parseIdentifierExpression();
-JS::Result<Ok> parseSetterContents(
-    uint32_t funLength,
-    ListNode** paramsOut,
-    ListNode** bodyOut);
-JS::Result<CaseClause*> parseSwitchCase();
-JS::Result<ParseNode*> parseSwitchDefault();
-JS::Result<ParseNode*> parseVariableDeclarator();
-JS::Result<ParseNode*> parseInterfaceArrayAssignmentTarget(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceArrayBinding(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceArrayExpression(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<Ok> parseInterfaceAssertedBlockScope(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<Ok> parseInterfaceAssertedBoundName(const size_t start, const BinKind kind, const BinFields& fields,
-    AssertedScopeKind scopeKind);
-JS::Result<Ok> parseInterfaceAssertedBoundNamesScope(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<Ok> parseInterfaceAssertedDeclaredName(const size_t start, const BinKind kind, const BinFields& fields,
-    AssertedScopeKind scopeKind);
-JS::Result<Ok> parseInterfaceAssertedParameterScope(const size_t start, const BinKind kind, const BinFields& fields,
-    MutableHandle<GCVector<JSAtom*>> positionalParams);
-JS::Result<Ok> parseInterfaceAssertedPositionalParameterName(const size_t start, const BinKind kind, const BinFields& fields,
-    AssertedScopeKind scopeKind,
-    MutableHandle<GCVector<JSAtom*>> positionalParams);
-JS::Result<Ok> parseInterfaceAssertedScriptGlobalScope(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<Ok> parseInterfaceAssertedVarScope(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceAssignmentExpression(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceAssignmentTargetIdentifier(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceAwaitExpression(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceBinaryExpression(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceBindingIdentifier(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceBindingWithInitializer(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceBlock(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceBreakStatement(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceCallExpression(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<LexicalScopeNode*> parseInterfaceCatchClause(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceClassDeclaration(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceClassExpression(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceCompoundAssignmentExpression(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceComputedMemberAssignmentTarget(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceComputedMemberExpression(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceComputedPropertyName(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceConditionalExpression(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceContinueStatement(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceDataProperty(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceDebuggerStatement(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceDirective(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceDoWhileStatement(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceEagerArrowExpressionWithExpression(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceEagerArrowExpressionWithFunctionBody(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceEagerFunctionDeclaration(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceEagerFunctionExpression(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceEagerGetter(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceEagerMethod(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceEagerSetter(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceEmptyStatement(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceExpressionStatement(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceForInOfBinding(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceForInStatement(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceForOfStatement(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceForStatement(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ListNode*> parseInterfaceFormalParameters(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<Ok> parseInterfaceFunctionExpressionContents(const size_t start, const BinKind kind, const BinFields& fields,
-    uint32_t funLength,
-    ListNode** paramsOut,
-    ListNode** bodyOut);
-JS::Result<Ok> parseInterfaceFunctionOrMethodContents(const size_t start, const BinKind kind, const BinFields& fields,
-    uint32_t funLength,
-    ListNode** paramsOut,
-    ListNode** bodyOut);
-JS::Result<Ok> parseInterfaceGetterContents(const size_t start, const BinKind kind, const BinFields& fields,
-    uint32_t funLength,
-    ListNode** paramsOut,
-    ListNode** bodyOut);
-JS::Result<ParseNode*> parseInterfaceIdentifierExpression(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceIfStatement(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceLabelledStatement(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceLazyArrowExpressionWithExpression(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceLazyArrowExpressionWithFunctionBody(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceLazyFunctionDeclaration(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceLazyFunctionExpression(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceLazyGetter(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceLazyMethod(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceLazySetter(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceLiteralBooleanExpression(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceLiteralInfinityExpression(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceLiteralNullExpression(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceLiteralNumericExpression(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceLiteralPropertyName(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceLiteralRegExpExpression(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceLiteralStringExpression(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceModule(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceNewExpression(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceNewTargetExpression(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceObjectAssignmentTarget(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceObjectBinding(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceObjectExpression(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceReturnStatement(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceScript(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<Ok> parseInterfaceSetterContents(const size_t start, const BinKind kind, const BinFields& fields,
-    uint32_t funLength,
-    ListNode** paramsOut,
-    ListNode** bodyOut);
-JS::Result<ParseNode*> parseInterfaceShorthandProperty(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceSpreadElement(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceStaticMemberAssignmentTarget(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceStaticMemberExpression(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceSuper(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<CaseClause*> parseInterfaceSwitchCase(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceSwitchDefault(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceSwitchStatement(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceSwitchStatementWithDefault(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceTemplateExpression(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceThisExpression(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceThrowStatement(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceTryCatchStatement(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceTryFinallyStatement(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceUnaryExpression(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceUpdateExpression(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceVariableDeclaration(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceVariableDeclarator(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceWhileStatement(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceWithStatement(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceYieldExpression(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceYieldStarExpression(const size_t start, const BinKind kind, const BinFields& fields);
+    // ----- Interfaces (by lexicographical order)
+    // `ParseNode*` may never be nullptr
+    JS::Result<Ok> parseAssertedBlockScope();
+    JS::Result<Ok> parseAssertedBoundName(
+        AssertedScopeKind scopeKind);
+    JS::Result<Ok> parseAssertedBoundNamesScope();
+    JS::Result<Ok> parseAssertedDeclaredName(
+        AssertedScopeKind scopeKind);
+    JS::Result<Ok> parseAssertedParameterScope(
+        MutableHandle<GCVector<JSAtom*>> positionalParams);
+    JS::Result<Ok> parseAssertedScriptGlobalScope();
+    JS::Result<Ok> parseAssertedVarScope();
+    JS::Result<ParseNode*> parseBindingIdentifier();
+    JS::Result<ParseNode*> parseBlock();
+    JS::Result<LexicalScopeNode*> parseCatchClause();
+    JS::Result<ParseNode*> parseDirective();
+    JS::Result<ListNode*> parseFormalParameters();
+    JS::Result<Ok> parseFunctionExpressionContents(
+        uint32_t funLength,
+        ListNode** paramsOut,
+        ListNode** bodyOut);
+    JS::Result<Ok> parseFunctionOrMethodContents(
+        uint32_t funLength,
+        ListNode** paramsOut,
+        ListNode** bodyOut);
+    JS::Result<Ok> parseGetterContents(
+        uint32_t funLength,
+        ListNode** paramsOut,
+        ListNode** bodyOut);
+    JS::Result<ParseNode*> parseIdentifierExpression();
+    JS::Result<Ok> parseSetterContents(
+        uint32_t funLength,
+        ListNode** paramsOut,
+        ListNode** bodyOut);
+    JS::Result<CaseClause*> parseSwitchCase();
+    JS::Result<ParseNode*> parseSwitchDefault();
+    JS::Result<ParseNode*> parseVariableDeclarator();
+    JS::Result<ParseNode*> parseInterfaceArrayAssignmentTarget(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceArrayBinding(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceArrayExpression(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<Ok> parseInterfaceAssertedBlockScope(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<Ok> parseInterfaceAssertedBoundName(const size_t start, const BinKind kind, const BinFields& fields,
+        AssertedScopeKind scopeKind);
+    JS::Result<Ok> parseInterfaceAssertedBoundNamesScope(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<Ok> parseInterfaceAssertedDeclaredName(const size_t start, const BinKind kind, const BinFields& fields,
+        AssertedScopeKind scopeKind);
+    JS::Result<Ok> parseInterfaceAssertedParameterScope(const size_t start, const BinKind kind, const BinFields& fields,
+        MutableHandle<GCVector<JSAtom*>> positionalParams);
+    JS::Result<Ok> parseInterfaceAssertedPositionalParameterName(const size_t start, const BinKind kind, const BinFields& fields,
+        AssertedScopeKind scopeKind,
+        MutableHandle<GCVector<JSAtom*>> positionalParams);
+    JS::Result<Ok> parseInterfaceAssertedScriptGlobalScope(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<Ok> parseInterfaceAssertedVarScope(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceAssignmentExpression(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceAssignmentTargetIdentifier(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceAwaitExpression(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceBinaryExpression(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceBindingIdentifier(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceBindingWithInitializer(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceBlock(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceBreakStatement(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceCallExpression(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<LexicalScopeNode*> parseInterfaceCatchClause(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceClassDeclaration(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceClassExpression(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceCompoundAssignmentExpression(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceComputedMemberAssignmentTarget(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceComputedMemberExpression(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceComputedPropertyName(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceConditionalExpression(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceContinueStatement(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceDataProperty(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceDebuggerStatement(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceDirective(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceDoWhileStatement(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceEagerArrowExpressionWithExpression(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceEagerArrowExpressionWithFunctionBody(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceEagerFunctionDeclaration(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceEagerFunctionExpression(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceEagerGetter(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceEagerMethod(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceEagerSetter(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceEmptyStatement(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceExpressionStatement(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceForInOfBinding(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceForInStatement(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceForOfStatement(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceForStatement(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ListNode*> parseInterfaceFormalParameters(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<Ok> parseInterfaceFunctionExpressionContents(const size_t start, const BinKind kind, const BinFields& fields,
+        uint32_t funLength,
+        ListNode** paramsOut,
+        ListNode** bodyOut);
+    JS::Result<Ok> parseInterfaceFunctionOrMethodContents(const size_t start, const BinKind kind, const BinFields& fields,
+        uint32_t funLength,
+        ListNode** paramsOut,
+        ListNode** bodyOut);
+    JS::Result<Ok> parseInterfaceGetterContents(const size_t start, const BinKind kind, const BinFields& fields,
+        uint32_t funLength,
+        ListNode** paramsOut,
+        ListNode** bodyOut);
+    JS::Result<ParseNode*> parseInterfaceIdentifierExpression(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceIfStatement(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceLabelledStatement(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceLazyArrowExpressionWithExpression(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceLazyArrowExpressionWithFunctionBody(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceLazyFunctionDeclaration(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceLazyFunctionExpression(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceLazyGetter(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceLazyMethod(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceLazySetter(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceLiteralBooleanExpression(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceLiteralInfinityExpression(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceLiteralNullExpression(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceLiteralNumericExpression(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceLiteralPropertyName(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceLiteralRegExpExpression(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceLiteralStringExpression(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceModule(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceNewExpression(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceNewTargetExpression(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceObjectAssignmentTarget(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceObjectBinding(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceObjectExpression(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceReturnStatement(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceScript(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<Ok> parseInterfaceSetterContents(const size_t start, const BinKind kind, const BinFields& fields,
+        uint32_t funLength,
+        ListNode** paramsOut,
+        ListNode** bodyOut);
+    JS::Result<ParseNode*> parseInterfaceShorthandProperty(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceSpreadElement(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceStaticMemberAssignmentTarget(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceStaticMemberExpression(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceSuper(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<CaseClause*> parseInterfaceSwitchCase(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceSwitchDefault(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceSwitchStatement(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceSwitchStatementWithDefault(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceTemplateExpression(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceThisExpression(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceThrowStatement(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceTryCatchStatement(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceTryFinallyStatement(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceUnaryExpression(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceUpdateExpression(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceVariableDeclaration(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceVariableDeclarator(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceWhileStatement(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceWithStatement(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceYieldExpression(const size_t start, const BinKind kind, const BinFields& fields);
+    JS::Result<ParseNode*> parseInterfaceYieldStarExpression(const size_t start, const BinKind kind, const BinFields& fields);
 
-
-// ----- String enums (by lexicographical order)
-// Implementations are autogenerated
-JS::Result<typename BinASTParser<Tok>::AssertedDeclaredKind> parseAssertedDeclaredKind();
-JS::Result<typename BinASTParser<Tok>::BinaryOperator> parseBinaryOperator();
-JS::Result<typename BinASTParser<Tok>::CompoundAssignmentOperator> parseCompoundAssignmentOperator();
-JS::Result<typename BinASTParser<Tok>::UnaryOperator> parseUnaryOperator();
-JS::Result<typename BinASTParser<Tok>::UpdateOperator> parseUpdateOperator();
-JS::Result<typename BinASTParser<Tok>::VariableDeclarationKind> parseVariableDeclarationKind();
-
+    // ----- String enums (by lexicographical order)
+    JS::Result<typename BinASTParser<Tok>::AssertedDeclaredKind> parseAssertedDeclaredKind();
+    JS::Result<typename BinASTParser<Tok>::BinaryOperator> parseBinaryOperator();
+    JS::Result<typename BinASTParser<Tok>::CompoundAssignmentOperator> parseCompoundAssignmentOperator();
+    JS::Result<typename BinASTParser<Tok>::UnaryOperator> parseUnaryOperator();
+    JS::Result<typename BinASTParser<Tok>::UpdateOperator> parseUpdateOperator();
+    JS::Result<typename BinASTParser<Tok>::VariableDeclarationKind> parseVariableDeclarationKind();
 
-// ----- Lists (by lexicographical order)
-// Implementations are autogenerated
-JS::Result<ParseNode*> parseArguments();
-JS::Result<ListNode*> parseFunctionBody();
-JS::Result<Ok> parseListOfAssertedBoundName(
-    AssertedScopeKind scopeKind);
-JS::Result<Ok> parseListOfAssertedDeclaredName(
-    AssertedScopeKind scopeKind);
-JS::Result<Ok> parseListOfAssertedMaybePositionalParameterName(
-    AssertedScopeKind scopeKind,
-    MutableHandle<GCVector<JSAtom*>> positionalParams);
-JS::Result<ListNode*> parseListOfDirective();
-JS::Result<ListNode*> parseListOfObjectProperty();
-JS::Result<ListNode*> parseListOfOptionalSpreadElementOrExpression();
-JS::Result<ListNode*> parseListOfParameter();
-JS::Result<ListNode*> parseListOfStatement();
-JS::Result<ListNode*> parseListOfSwitchCase();
-JS::Result<ListNode*> parseListOfVariableDeclarator();
+    // ----- Lists (by lexicographical order)
+    JS::Result<ParseNode*> parseArguments();
+    JS::Result<ListNode*> parseFunctionBody();
+    JS::Result<Ok> parseListOfAssertedBoundName(
+        AssertedScopeKind scopeKind);
+    JS::Result<Ok> parseListOfAssertedDeclaredName(
+        AssertedScopeKind scopeKind);
+    JS::Result<Ok> parseListOfAssertedMaybePositionalParameterName(
+        AssertedScopeKind scopeKind,
+        MutableHandle<GCVector<JSAtom*>> positionalParams);
+    JS::Result<ListNode*> parseListOfDirective();
+    JS::Result<ListNode*> parseListOfObjectProperty();
+    JS::Result<ListNode*> parseListOfOptionalSpreadElementOrExpression();
+    JS::Result<ListNode*> parseListOfParameter();
+    JS::Result<ListNode*> parseListOfStatement();
+    JS::Result<ListNode*> parseListOfSwitchCase();
+    JS::Result<ListNode*> parseListOfVariableDeclarator();
 
+    // ----- Default values (by lexicographical order)
+    JS::Result<ParseNode*> parseOptionalBinding();
+    JS::Result<ParseNode*> parseOptionalBindingIdentifier();
+    JS::Result<LexicalScopeNode*> parseOptionalCatchClause();
+    JS::Result<ParseNode*> parseOptionalExpression();
+    JS::Result<ParseNode*> parseOptionalSpreadElementOrExpression();
+    JS::Result<ParseNode*> parseOptionalStatement();
+    JS::Result<ParseNode*> parseOptionalVariableDeclarationOrExpression();
 
-// ----- Default values (by lexicographical order)
-// Implementations are autogenerated
-JS::Result<ParseNode*> parseOptionalBinding();
-JS::Result<ParseNode*> parseOptionalBindingIdentifier();
-JS::Result<LexicalScopeNode*> parseOptionalCatchClause();
-JS::Result<ParseNode*> parseOptionalExpression();
-JS::Result<ParseNode*> parseOptionalSpreadElementOrExpression();
-JS::Result<ParseNode*> parseOptionalStatement();
-JS::Result<ParseNode*> parseOptionalVariableDeclarationOrExpression();
+};
+
+extern template class BinASTParser<BinTokenReaderMultipart>;
+extern template class BinASTParser<BinTokenReaderTester>;
 
+} // namespace frontend
+} // namespace js
+
+#endif // frontend_BinASTParser_h
--- a/js/src/frontend/BinASTParserPerTokenizer.cpp
+++ b/js/src/frontend/BinASTParserPerTokenizer.cpp
@@ -9,16 +9,17 @@
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Casting.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/Move.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/ScopeExit.h"
 #include "mozilla/Vector.h"
 
+#include "frontend/BinASTParser.h"
 #include "frontend/BinSource-macros.h"
 #include "frontend/BinTokenReaderTester.h"
 #include "frontend/FullParseHandler.h"
 #include "frontend/ParseNode.h"
 #include "frontend/Parser.h"
 #include "frontend/SharedContext.h"
 
 #include "js/Result.h"
@@ -75,36 +76,37 @@
 namespace js {
 namespace frontend {
 
 using UsedNamePtr = UsedNameTracker::UsedNameMap::Ptr;
 
 // ------------- Toplevel constructions
 
 template<typename Tok> JS::Result<ParseNode*>
-BinASTParser<Tok>::parse(GlobalSharedContext* globalsc, const Vector<uint8_t>& data,
-                         BinASTSourceMetadata** metadataPtr)
+BinASTParserPerTokenizer<Tok>::parse(GlobalSharedContext* globalsc, const Vector<uint8_t>& data,
+                                     BinASTSourceMetadata** metadataPtr)
 {
     return parse(globalsc, data.begin(), data.length(), metadataPtr);
 }
 
 template<typename Tok> JS::Result<ParseNode*>
-BinASTParser<Tok>::parse(GlobalSharedContext* globalsc, const uint8_t* start, const size_t length,
-                         BinASTSourceMetadata** metadataPtr)
+BinASTParserPerTokenizer<Tok>::parse(GlobalSharedContext* globalsc, const uint8_t* start,
+                                     const size_t length,
+                                     BinASTSourceMetadata** metadataPtr)
 {
     auto result = parseAux(globalsc, start, length, metadataPtr);
     poison(); // Make sure that the parser is never used again accidentally.
     return result;
 }
 
 
 template<typename Tok> JS::Result<ParseNode*>
-BinASTParser<Tok>::parseAux(GlobalSharedContext* globalsc,
-                            const uint8_t* start, const size_t length,
-                            BinASTSourceMetadata** metadataPtr)
+BinASTParserPerTokenizer<Tok>::parseAux(GlobalSharedContext* globalsc,
+                                        const uint8_t* start, const size_t length,
+                                        BinASTSourceMetadata** metadataPtr)
 {
     MOZ_ASSERT(globalsc);
 
     tokenizer_.emplace(cx_, this, start, length);
 
     BinParseContext globalpc(cx_, this, globalsc, /* newDirectives = */ nullptr);
     if (!globalpc.init()) {
         return cx_->alreadyReportedError();
@@ -113,34 +115,35 @@ BinASTParser<Tok>::parseAux(GlobalShared
     ParseContext::VarScope varScope(cx_, &globalpc, usedNames_);
     if (!varScope.init(&globalpc)) {
         return cx_->alreadyReportedError();
     }
 
     MOZ_TRY(tokenizer_->readHeader());
 
     ParseNode* result(nullptr);
-    MOZ_TRY_VAR(result, parseProgram());
+    MOZ_TRY_VAR(result, asFinalParser()->parseProgram());
 
     mozilla::Maybe<GlobalScope::Data*> bindings = NewGlobalScopeData(cx_, varScope, alloc_,
                                                                      parseContext_);
     if (!bindings) {
         return cx_->alreadyReportedError();
     }
     globalsc->bindings = *bindings;
 
     if (metadataPtr) {
         *metadataPtr = tokenizer_->takeMetadata();
     }
 
     return result; // Magic conversion to Ok.
 }
 
 template<typename Tok> JS::Result<ParseNode*>
-BinASTParser<Tok>::parseLazyFunction(ScriptSource* scriptSource, const size_t firstOffset)
+BinASTParserPerTokenizer<Tok>::parseLazyFunction(ScriptSource* scriptSource,
+                                                 const size_t firstOffset)
 {
     MOZ_ASSERT(lazyScript_);
     MOZ_ASSERT(scriptSource->length() > firstOffset);
 
     tokenizer_.emplace(cx_, this, scriptSource->binASTSource(), scriptSource->length());
 
     MOZ_TRY(tokenizer_->initFromScriptSource(scriptSource));
 
@@ -165,45 +168,45 @@ BinASTParser<Tok>::parseLazyFunction(Scr
     BINJS_TRY(funpc.init());
     parseContext_->functionScope().useAsVarScope(parseContext_);
     MOZ_ASSERT(parseContext_->isFunctionBox());
 
     ParseContext::Scope lexicalScope(cx_, parseContext_, usedNames_);
     BINJS_TRY(lexicalScope.init(parseContext_));
     ListNode* params;
     ListNode* tmpBody;
-    auto parseFunc = isExpr ? &BinASTParser::parseFunctionExpressionContents
-                            : &BinASTParser::parseFunctionOrMethodContents;
-    MOZ_TRY((this->*parseFunc)(func->nargs(), &params, &tmpBody));
+    auto parseFunc = isExpr ? &FinalParser::parseFunctionExpressionContents
+                            : &FinalParser::parseFunctionOrMethodContents;
+    MOZ_TRY((asFinalParser()->*parseFunc)(func->nargs(), &params, &tmpBody));
 
     BINJS_TRY_DECL(lexicalScopeData, NewLexicalScopeData(cx_, lexicalScope, alloc_, parseContext_));
     BINJS_TRY_DECL(body, factory_.newLexicalScope(*lexicalScopeData, tmpBody));
 
     auto binKind = isExpr ? BinKind::LazyFunctionExpression : BinKind::LazyFunctionDeclaration;
     return buildFunction(firstOffset, binKind, nullptr, params, body, funbox);
 }
 
 template<typename Tok> void
-BinASTParser<Tok>::forceStrictIfNecessary(SharedContext* sc, ListNode* directives)
+BinASTParserPerTokenizer<Tok>::forceStrictIfNecessary(SharedContext* sc, ListNode* directives)
 {
     JSAtom* useStrict = cx_->names().useStrict;
 
     for (const ParseNode* directive : directives->contents()) {
         if (directive->as<NameNode>().atom() == useStrict) {
             sc->strictScript = true;
             break;
         }
     }
 }
 
 template<typename Tok> JS::Result<FunctionBox*>
-BinASTParser<Tok>::buildFunctionBox(GeneratorKind generatorKind,
-    FunctionAsyncKind functionAsyncKind,
-    FunctionSyntaxKind syntax,
-    ParseNode* name)
+BinASTParserPerTokenizer<Tok>::buildFunctionBox(GeneratorKind generatorKind,
+                                                FunctionAsyncKind functionAsyncKind,
+                                                FunctionSyntaxKind syntax,
+                                                ParseNode* name)
 {
     MOZ_ASSERT_IF(!parseContext_, lazyScript_);
 
     RootedAtom atom(cx_);
 
     // Name might be any of {Identifier,ComputedPropertyName,LiteralPropertyName}
     if (name && name->is<NameNode>()) {
         atom = name->as<NameNode>().atom();
@@ -250,35 +253,37 @@ BinASTParser<Tok>::buildFunctionBox(Gene
         funbox->initWithEnclosingParseContext(parseContext_, syntax);
     } else {
         funbox->initFromLazyFunction();
     }
     return funbox;
 }
 
 template<typename Tok> JS::Result<CodeNode*>
-BinASTParser<Tok>::makeEmptyFunctionNode(const size_t start, const BinKind kind, FunctionBox* funbox)
+BinASTParserPerTokenizer<Tok>::makeEmptyFunctionNode(const size_t start, const BinKind kind,
+                                                     FunctionBox* funbox)
 {
     // LazyScript compilation requires basically none of the fields filled out.
     TokenPos pos = tokenizer_->pos(start);
     bool isStatement = kind == BinKind::EagerFunctionDeclaration ||
                        kind == BinKind::LazyFunctionDeclaration;
 
     BINJS_TRY_DECL(result, isStatement
                      ? factory_.newFunctionStatement(pos)
                      : factory_.newFunctionExpression(pos));
 
     factory_.setFunctionBox(result, funbox);
 
     return result;
 }
 
 template<typename Tok> JS::Result<ParseNode*>
-BinASTParser<Tok>::buildFunction(const size_t start, const BinKind kind, ParseNode* name,
-                                 ListNode* params, ParseNode* body, FunctionBox* funbox)
+BinASTParserPerTokenizer<Tok>::buildFunction(const size_t start, const BinKind kind,
+                                             ParseNode* name, ListNode* params, ParseNode* body,
+                                             FunctionBox* funbox)
 {
     // Set the argument count for building argument packets. Function.length is handled
     // by setting the appropriate funbox field during argument parsing.
     if (!lazyScript_ || lazyScript_->functionNonDelazifying() != funbox->function()) {
         funbox->function()->setArgCount(params ? uint16_t(params->count()) : 0);
     }
 
     // ParseNode represents the body as concatenated after the params.
@@ -366,19 +371,19 @@ BinASTParser<Tok>::buildFunction(const s
 
         funbox->namedLambdaBindings().set(*recursiveBinding);
     }
 
     return result;
 }
 
 template<typename Tok> JS::Result<Ok>
-BinASTParser<Tok>::addScopeName(AssertedScopeKind scopeKind, HandleAtom name,
-                                ParseContext::Scope* scope, DeclarationKind declKind,
-                                bool isCaptured, bool allowDuplicateName)
+BinASTParserPerTokenizer<Tok>::addScopeName(AssertedScopeKind scopeKind, HandleAtom name,
+                                            ParseContext::Scope* scope, DeclarationKind declKind,
+                                            bool isCaptured, bool allowDuplicateName)
 {
     auto ptr = scope->lookupDeclaredNameForAdd(name);
     if (ptr) {
         if (allowDuplicateName) {
             return Ok();
         }
         return raiseError("Variable redeclaration");
     }
@@ -391,32 +396,34 @@ BinASTParser<Tok>::addScopeName(Asserted
         MOZ_ASSERT(declaredPtr);
         declaredPtr->value()->setClosedOver();
     }
 
     return Ok();
 }
 
 template<typename Tok> void
-BinASTParser<Tok>::captureFunctionName()
+BinASTParserPerTokenizer<Tok>::captureFunctionName()
 {
     MOZ_ASSERT(parseContext_->isFunctionBox());
     MOZ_ASSERT(parseContext_->functionBox()->function()->isNamedLambda());
 
     RootedAtom funName(cx_, parseContext_->functionBox()->function()->explicitName());
     MOZ_ASSERT(funName);
 
     auto ptr = parseContext_->namedLambdaScope().lookupDeclaredName(funName);
     MOZ_ASSERT(ptr);
     ptr->value()->setClosedOver();
 }
 
 template<typename Tok> JS::Result<Ok>
-BinASTParser<Tok>::getDeclaredScope(AssertedScopeKind scopeKind, AssertedDeclaredKind kind,
-                                    ParseContext::Scope*& scope, DeclarationKind& declKind)
+BinASTParserPerTokenizer<Tok>::getDeclaredScope(AssertedScopeKind scopeKind,
+                                                AssertedDeclaredKind kind,
+                                                ParseContext::Scope*& scope,
+                                                DeclarationKind& declKind)
 {
     MOZ_ASSERT(scopeKind == AssertedScopeKind::Block ||
                scopeKind == AssertedScopeKind::Global ||
                scopeKind == AssertedScopeKind::Var);
     switch (kind) {
       case AssertedDeclaredKind::Var:
         if (scopeKind == AssertedScopeKind::Block) {
             return raiseError("AssertedBlockScope cannot contain 'var' binding");
@@ -433,18 +440,19 @@ BinASTParser<Tok>::getDeclaredScope(Asse
         scope = parseContext_->innermostScope();
         break;
     }
 
     return Ok();
 }
 
 template<typename Tok> JS::Result<Ok>
-BinASTParser<Tok>::getBoundScope(AssertedScopeKind scopeKind,
-                                 ParseContext::Scope*& scope, DeclarationKind& declKind)
+BinASTParserPerTokenizer<Tok>::getBoundScope(AssertedScopeKind scopeKind,
+                                             ParseContext::Scope*& scope,
+                                             DeclarationKind& declKind)
 {
     MOZ_ASSERT(scopeKind == AssertedScopeKind::Catch ||
                scopeKind == AssertedScopeKind::Parameter);
     switch (scopeKind) {
       case AssertedScopeKind::Catch:
         declKind = DeclarationKind::CatchParameter;
         scope = parseContext_->innermostScope();
         break;
@@ -457,17 +465,17 @@ BinASTParser<Tok>::getBoundScope(Asserte
         MOZ_ASSERT_UNREACHABLE("Unexpected AssertedScopeKind");
         break;
     }
 
     return Ok();
 }
 
 template<typename Tok> JS::Result<Ok>
-BinASTParser<Tok>::checkBinding(JSAtom* name)
+BinASTParserPerTokenizer<Tok>::checkBinding(JSAtom* name)
 {
     // Check that the variable appears in the corresponding scope.
     ParseContext::Scope& scope =
         variableDeclarationKind_ == VariableDeclarationKind::Var
         ? parseContext_->varScope()
         : *parseContext_->innermostScope();
 
     auto ptr = scope.lookupDeclaredName(name->asPropertyName());
@@ -476,18 +484,18 @@ BinASTParser<Tok>::checkBinding(JSAtom* 
     }
 
     return Ok();
 }
 
 // Binary AST (revision 8eab67e0c434929a66ff6abe99ff790bca087dda)
 // 3.1.5 CheckPositionalParameterIndices.
 template<typename Tok> JS::Result<Ok>
-BinASTParser<Tok>::checkPositionalParameterIndices(Handle<GCVector<JSAtom*>> positionalParams,
-                                                   ListNode* params)
+BinASTParserPerTokenizer<Tok>::checkPositionalParameterIndices(Handle<GCVector<JSAtom*>> positionalParams,
+                                                               ListNode* params)
 {
     // positionalParams should have the corresponding entry up to the last
     // positional parameter.
 
     // `positionalParams` corresponds to `expectedParams` parameter in the spec.
     // `params` corresponds to `parseTree` in 3.1.9 CheckAssertedScope, and
     // `positionalParamNames` parameter
 
@@ -563,197 +571,199 @@ BinASTParser<Tok>::checkPositionalParame
     }
 
     return Ok();
 }
 
 // Binary AST (revision 8eab67e0c434929a66ff6abe99ff790bca087dda)
 // 3.1.13 CheckFunctionLength.
 template<typename Tok> JS::Result<Ok>
-BinASTParser<Tok>::checkFunctionLength(uint32_t expectedLength)
+BinASTParserPerTokenizer<Tok>::checkFunctionLength(uint32_t expectedLength)
 {
     if (parseContext_->functionBox()->length != expectedLength) {
         return raiseError("Function length does't match");
     }
     return Ok();
 }
 
 template<typename Tok> JS::Result<Ok>
-BinASTParser<Tok>::checkClosedVars(ParseContext::Scope& scope)
+BinASTParserPerTokenizer<Tok>::checkClosedVars(ParseContext::Scope& scope)
 {
     for (ParseContext::Scope::BindingIter bi = scope.bindings(parseContext_); bi; bi++) {
         if (UsedNamePtr p = usedNames_.lookup(bi.name())) {
             bool closedOver;
             p->value().noteBoundInScope(parseContext_->scriptId(), scope.id(), &closedOver);
             if (closedOver && !bi.closedOver()) {
                 return raiseInvalidClosedVar(bi.name());
             }
         }
     }
 
     return Ok();
 }
 
 template<typename Tok> JS::Result<Ok>
-BinASTParser<Tok>::checkFunctionClosedVars()
+BinASTParserPerTokenizer<Tok>::checkFunctionClosedVars()
 {
     MOZ_ASSERT(parseContext_->isFunctionBox());
 
     MOZ_TRY(checkClosedVars(*parseContext_->innermostScope()));
     MOZ_TRY(checkClosedVars(parseContext_->functionScope()));
     if (parseContext_->functionBox()->function()->isNamedLambda()) {
         MOZ_TRY(checkClosedVars(parseContext_->namedLambdaScope()));
     }
 
     return Ok();
 }
 
 template<typename Tok> JS::Result<Ok>
-BinASTParser<Tok>::prependDirectivesToBody(ListNode* body, ListNode* directives)
+BinASTParserPerTokenizer<Tok>::prependDirectivesToBody(ListNode* body, ListNode* directives)
 {
     if (!directives) {
         return Ok();
     }
 
     if (directives->empty()) {
         return Ok();
     }
 
     MOZ_TRY(prependDirectivesImpl(body, directives->head()));
 
     return Ok();
 }
 
 template<typename Tok> JS::Result<Ok>
-BinASTParser<Tok>::prependDirectivesImpl(ListNode* body, ParseNode* directive)
+BinASTParserPerTokenizer<Tok>::prependDirectivesImpl(ListNode* body, ParseNode* directive)
 {
     BINJS_TRY(CheckRecursionLimit(cx_));
 
     // Prepend in the reverse order by using stack, so that the directives are
     // prepended in the original order.
     if (ParseNode* next = directive->pn_next) {
         MOZ_TRY(prependDirectivesImpl(body, next));
     }
 
     BINJS_TRY_DECL(statement, factory_.newExprStatement(directive, directive->pn_pos.end));
     body->prependAndUpdatePos(statement);
 
     return Ok();
 }
 
 template<typename Tok> mozilla::GenericErrorResult<JS::Error&>
-BinASTParser<Tok>::raiseInvalidClosedVar(JSAtom* name)
+BinASTParserPerTokenizer<Tok>::raiseInvalidClosedVar(JSAtom* name)
 {
     return raiseError("Captured variable was not declared as captured");
 }
 
 template<typename Tok> mozilla::GenericErrorResult<JS::Error&>
-BinASTParser<Tok>::raiseMissingVariableInAssertedScope(JSAtom* name)
+BinASTParserPerTokenizer<Tok>::raiseMissingVariableInAssertedScope(JSAtom* name)
 {
     // For the moment, we don't trust inputs sufficiently to put the name
     // in an error message.
     return raiseError("Missing variable in AssertedScope");
 }
 
 template<typename Tok> mozilla::GenericErrorResult<JS::Error&>
-BinASTParser<Tok>::raiseMissingDirectEvalInAssertedScope()
+BinASTParserPerTokenizer<Tok>::raiseMissingDirectEvalInAssertedScope()
 {
     return raiseError("Direct call to `eval` was not declared in AssertedScope");
 }
 
 template<typename Tok> mozilla::GenericErrorResult<JS::Error&>
-BinASTParser<Tok>::raiseInvalidKind(const char* superKind, const BinKind kind)
+BinASTParserPerTokenizer<Tok>::raiseInvalidKind(const char* superKind, const BinKind kind)
 {
     Sprinter out(cx_);
     BINJS_TRY(out.init());
     BINJS_TRY(out.printf("In %s, invalid kind %s", superKind, describeBinKind(kind)));
     return raiseError(out.string());
 }
 
 template<typename Tok> mozilla::GenericErrorResult<JS::Error&>
-BinASTParser<Tok>::raiseInvalidVariant(const char* kind, const BinVariant value)
+BinASTParserPerTokenizer<Tok>::raiseInvalidVariant(const char* kind, const BinVariant value)
 {
     Sprinter out(cx_);
     BINJS_TRY(out.init());
     BINJS_TRY(out.printf("In %s, invalid variant '%s'", kind, describeBinVariant(value)));
 
     return raiseError(out.string());
 }
 
 template<typename Tok> mozilla::GenericErrorResult<JS::Error&>
-BinASTParser<Tok>::raiseMissingField(const char* kind, const BinField field)
+BinASTParserPerTokenizer<Tok>::raiseMissingField(const char* kind, const BinField field)
 {
     Sprinter out(cx_);
     BINJS_TRY(out.init());
     BINJS_TRY(out.printf("In %s, missing field '%s'", kind, describeBinField(field)));
 
     return raiseError(out.string());
 }
 
 template<typename Tok> mozilla::GenericErrorResult<JS::Error&>
-BinASTParser<Tok>::raiseEmpty(const char* description)
+BinASTParserPerTokenizer<Tok>::raiseEmpty(const char* description)
 {
     Sprinter out(cx_);
     BINJS_TRY(out.init());
     BINJS_TRY(out.printf("Empty %s", description));
 
     return raiseError(out.string());
 }
 
 template<typename Tok> mozilla::GenericErrorResult<JS::Error&>
-BinASTParser<Tok>::raiseOOM()
+BinASTParserPerTokenizer<Tok>::raiseOOM()
 {
     return tokenizer_->raiseOOM();
 }
 
 template<typename Tok> mozilla::GenericErrorResult<JS::Error&>
-BinASTParser<Tok>::raiseError(BinKind kind, const char* description)
+BinASTParserPerTokenizer<Tok>::raiseError(BinKind kind, const char* description)
 {
     Sprinter out(cx_);
     BINJS_TRY(out.init());
     BINJS_TRY(out.printf("In %s, ", description));
     return tokenizer_->raiseError(out.string());
 }
 
 template<typename Tok> mozilla::GenericErrorResult<JS::Error&>
-BinASTParser<Tok>::raiseError(const char* description)
+BinASTParserPerTokenizer<Tok>::raiseError(const char* description)
 {
     return tokenizer_->raiseError(description);
 }
 
 template<typename Tok> void
-BinASTParser<Tok>::poison()
+BinASTParserPerTokenizer<Tok>::poison()
 {
     tokenizer_.reset();
 }
 
 template<typename Tok> void
-BinASTParser<Tok>::reportErrorNoOffsetVA(unsigned errorNumber, va_list args)
+BinASTParserPerTokenizer<Tok>::reportErrorNoOffsetVA(unsigned errorNumber, va_list args)
 {
     ErrorMetadata metadata;
     metadata.filename = getFilename();
     metadata.lineNumber = 0;
     metadata.columnNumber = offset();
     metadata.isMuted = options().mutedErrors();
     ReportCompileError(cx_, std::move(metadata), nullptr, JSREPORT_ERROR, errorNumber, args);
 }
 
 template<typename Tok> void
-BinASTParser<Tok>::errorAtVA(uint32_t offset, unsigned errorNumber, va_list* args)
+BinASTParserPerTokenizer<Tok>::errorAtVA(uint32_t offset, unsigned errorNumber, va_list* args)
 {
     ErrorMetadata metadata;
     metadata.filename = getFilename();
     metadata.lineNumber = 0;
     metadata.columnNumber = offset;
     metadata.isMuted = options().mutedErrors();
     ReportCompileError(cx_, std::move(metadata), nullptr, JSREPORT_ERROR, errorNumber, *args);
 }
 
 template<typename Tok> bool
-BinASTParser<Tok>::reportExtraWarningErrorNumberVA(UniquePtr<JSErrorNotes> notes, uint32_t offset, unsigned errorNumber, va_list* args)
+BinASTParserPerTokenizer<Tok>::reportExtraWarningErrorNumberVA(UniquePtr<JSErrorNotes> notes,
+                                                               uint32_t offset,
+                                                               unsigned errorNumber, va_list* args)
 {
     if (!options().extraWarningsOption) {
         return true;
     }
 
     ErrorMetadata metadata;
     metadata.filename = getFilename();
     metadata.lineNumber = 0;
@@ -771,24 +781,46 @@ BinASTParser<Tok>::reportExtraWarningErr
 void
 TraceBinParser(JSTracer* trc, JS::AutoGCRooter* parser)
 {
     static_cast<BinASTParserBase*>(parser)->trace(trc);
 }
 
 template<typename Tok>
 void
-BinASTParser<Tok>::doTrace(JSTracer* trc)
+BinASTParserPerTokenizer<Tok>::doTrace(JSTracer* trc)
 {
     if (tokenizer_) {
         tokenizer_->traceMetadata(trc);
     }
 }
 
+template<typename Tok>
+inline typename BinASTParserPerTokenizer<Tok>::FinalParser*
+BinASTParserPerTokenizer<Tok>::asFinalParser()
+{
+    // Same as GeneralParser::asFinalParser, verify the inheritance to
+    // make sure the static downcast works.
+    static_assert(mozilla::IsBaseOf<BinASTParserPerTokenizer<Tok>, FinalParser>::value,
+                  "inheritance relationship required by the static_cast<> below");
+
+    return static_cast<FinalParser*>(this);
+}
+
+template<typename Tok>
+inline const typename BinASTParserPerTokenizer<Tok>::FinalParser*
+BinASTParserPerTokenizer<Tok>::asFinalParser() const
+{
+    static_assert(mozilla::IsBaseOf<BinASTParserPerTokenizer<Tok>, FinalParser>::value,
+                  "inheritance relationship required by the static_cast<> below");
+
+    return static_cast<const FinalParser*>(this);
+}
+
 // Force class instantiation.
 // This ensures that the symbols are built, without having to export all our
 // code (and its baggage of #include and macros) in the header.
-template class BinASTParser<BinTokenReaderMultipart>;
-template class BinASTParser<BinTokenReaderTester>;
+template class BinASTParserPerTokenizer<BinTokenReaderMultipart>;
+template class BinASTParserPerTokenizer<BinTokenReaderTester>;
 
 } // namespace frontend
 } // namespace js
 
--- a/js/src/frontend/BinASTParserPerTokenizer.h
+++ b/js/src/frontend/BinASTParserPerTokenizer.h
@@ -30,73 +30,75 @@
 #include "js/CompileOptions.h"
 #include "js/GCHashTable.h"
 #include "js/GCVector.h"
 #include "js/Result.h"
 
 namespace js {
 namespace frontend {
 
+template<typename Tok>
+class BinASTParser;
+
 /**
  * The parser for a Binary AST.
  *
  * By design, this parser never needs to backtrack or look ahead. Errors are not
  * recoverable.
  */
 template<typename Tok>
-class BinASTParser : public BinASTParserBase, public ErrorReporter, public BCEParserHandle
+class BinASTParserPerTokenizer : public BinASTParserBase, public ErrorReporter, public BCEParserHandle
 {
   public:
     using Tokenizer = Tok;
 
     using AutoList = typename Tokenizer::AutoList;
     using AutoTaggedTuple = typename Tokenizer::AutoTaggedTuple;
     using AutoTuple = typename Tokenizer::AutoTuple;
     using BinFields = typename Tokenizer::BinFields;
     using Chars = typename Tokenizer::Chars;
 
   public:
     // Auto-generated types.
     using AssertedDeclaredKind = binast::AssertedDeclaredKind;
-    using BinaryOperator = binast::BinaryOperator;
-    using CompoundAssignmentOperator = binast::CompoundAssignmentOperator;
-    using UnaryOperator = binast::UnaryOperator;
-    using UpdateOperator = binast::UpdateOperator;
     using VariableDeclarationKind = binast::VariableDeclarationKind;
 
   public:
-    BinASTParser(JSContext* cx, LifoAlloc& alloc, UsedNameTracker& usedNames, const JS::ReadOnlyCompileOptions& options,
-                 HandleScriptSourceObject sourceObject, Handle<LazyScript*> lazyScript = nullptr)
+    BinASTParserPerTokenizer(JSContext* cx, LifoAlloc& alloc, UsedNameTracker& usedNames,
+                             const JS::ReadOnlyCompileOptions& options,
+                             HandleScriptSourceObject sourceObject,
+                             Handle<LazyScript*> lazyScript = nullptr)
         : BinASTParserBase(cx, alloc, usedNames, sourceObject, lazyScript)
         , options_(options)
         , variableDeclarationKind_(VariableDeclarationKind::Var)
     {
     }
-    ~BinASTParser()
+    ~BinASTParserPerTokenizer()
     {
     }
 
     /**
      * Parse a buffer, returning a node (which may be nullptr) in case of success
      * or Nothing() in case of error.
      *
-     * The instance of `ParseNode` MAY NOT survive the `BinASTParser`. Indeed,
-     * destruction of the `BinASTParser` will also destroy the `ParseNode`.
+     * The instance of `ParseNode` MAY NOT survive the
+     * `BinASTParserPerTokenizer`. Indeed, destruction of the
+     * `BinASTParserPerTokenizer` will also destroy the `ParseNode`.
      *
      * In case of error, the parser reports the JS error.
      */
     JS::Result<ParseNode*> parse(GlobalSharedContext* globalsc,
                                  const uint8_t* start, const size_t length,
                                  BinASTSourceMetadata** metadataPtr = nullptr);
     JS::Result<ParseNode*> parse(GlobalSharedContext* globalsc, const Vector<uint8_t>& data,
                                  BinASTSourceMetadata** metadataPtr = nullptr);
 
     JS::Result<ParseNode*> parseLazyFunction(ScriptSource* src, const size_t firstOffset);
 
-  private:
+  protected:
     MOZ_MUST_USE JS::Result<ParseNode*> parseAux(GlobalSharedContext* globalsc,
                                                  const uint8_t* start, const size_t length,
                                                  BinASTSourceMetadata** metadataPtr = nullptr);
 
     // --- Raise errors.
     //
     // These methods return a (failed) JS::Result for convenience.
 
@@ -123,19 +125,16 @@ class BinASTParser : public BinASTParser
     enum class AssertedScopeKind {
         Block,
         Catch,
         Global,
         Parameter,
         Var,
     };
 
-    // Auto-generated methods
-#include "frontend/BinASTParser.h"
-
     // --- Auxiliary parsing functions
 
     // Build a function object for a function-producing production. Called AFTER creating the scope.
     JS::Result<CodeNode*>
     makeEmptyFunctionNode(const size_t start, const BinKind kind, FunctionBox* funbox);
     JS::Result<ParseNode*>
     buildFunction(const size_t start, const BinKind kind, ParseNode* name, ListNode* params,
                   ParseNode* body, FunctionBox* funbox);
@@ -182,17 +181,17 @@ class BinASTParser : public BinASTParser
 
     MOZ_MUST_USE JS::Result<Ok> prependDirectivesImpl(ListNode* body,
         ParseNode* directive);
 
     // Optionally force a strict context without restarting the parse when we see a strict
     // directive.
     void forceStrictIfNecessary(SharedContext* sc, ListNode* directives);
 
-  private: // Implement ErrorReporter
+  protected: // Implement ErrorReporter
     const JS::ReadOnlyCompileOptions& options_;
 
     const JS::ReadOnlyCompileOptions& options() const override {
         return this->options_;
     }
 
     void doTrace(JSTracer* trc) final;
 
@@ -266,54 +265,64 @@ class BinASTParser : public BinASTParser
     }
     virtual void reportErrorNoOffsetVA(unsigned errorNumber, va_list args) override;
     virtual void errorAtVA(uint32_t offset, unsigned errorNumber, va_list* args) override;
     virtual bool reportExtraWarningErrorNumberVA(UniquePtr<JSErrorNotes> notes, uint32_t offset, unsigned errorNumber, va_list* args) override;
     virtual const char* getFilename() const override {
         return this->options_.filename();
     }
 
-  private: // Implement ErrorReporter
+  protected: // Implement ErrorReporter
     mozilla::Maybe<Tokenizer> tokenizer_;
     VariableDeclarationKind variableDeclarationKind_;
 
     friend class BinParseContext;
     friend class AutoVariableDeclarationKind;
 
     // Helper class: Restore field `variableDeclarationKind` upon leaving a scope.
     class MOZ_RAII AutoVariableDeclarationKind {
       public:
-        explicit AutoVariableDeclarationKind(BinASTParser<Tok>* parser
+        explicit AutoVariableDeclarationKind(BinASTParserPerTokenizer<Tok>* parser
                                              MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
             : parser_(parser)
             , kind(parser->variableDeclarationKind_)
         {
             MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         }
         ~AutoVariableDeclarationKind() {
             parser_->variableDeclarationKind_ = kind;
         }
         private:
-        BinASTParser<Tok>* parser_;
-        BinASTParser<Tok>::VariableDeclarationKind kind;
+        BinASTParserPerTokenizer<Tok>* parser_;
+        BinASTParserPerTokenizer<Tok>::VariableDeclarationKind kind;
         MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
     };
+
+  private:
+    // Some methods in this class require access to auto-generated methods in
+    // BinASTParser which derives this class.
+    // asFinalParser methods provide the access to BinASTParser class methods
+    // of this instance.
+    using FinalParser = BinASTParser<Tok>;
+
+    inline FinalParser* asFinalParser();
+    inline const FinalParser* asFinalParser() const;
 };
 
 class BinParseContext : public ParseContext
 {
   public:
     template<typename Tok>
-    BinParseContext(JSContext* cx, BinASTParser<Tok>* parser, SharedContext* sc,
+    BinParseContext(JSContext* cx, BinASTParserPerTokenizer<Tok>* parser, SharedContext* sc,
         Directives* newDirectives)
         : ParseContext(cx, parser->parseContext_, sc, *parser,
                        parser->usedNames_, newDirectives, /* isFull = */ true)
     { }
 };
 
 
-extern template class BinASTParser<BinTokenReaderMultipart>;
-extern template class BinASTParser<BinTokenReaderTester>;
+extern template class BinASTParserPerTokenizer<BinTokenReaderMultipart>;
+extern template class BinASTParserPerTokenizer<BinTokenReaderTester>;
 
 } // namespace frontend
 } // namespace js
 
 #endif // frontend_BinASTParserPerTokenizer_h
--- a/js/src/frontend/BinSource.yaml
+++ b/js/src/frontend/BinSource.yaml
@@ -22,24 +22,25 @@ cpp:
         * vim: set ts=8 sts=4 et sw=4 tw=99:
         * This Source Code Form is subject to the terms of the Mozilla Public
         * License, v. 2.0. If a copy of the MPL was not distributed with this
         * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
         // To generate this file, see the documentation in
         // js/src/frontend/binsource/README.md.
 
+        #include "frontend/BinASTParser.h"
+
         #include "mozilla/ArrayUtils.h"
         #include "mozilla/Casting.h"
         #include "mozilla/Maybe.h"
         #include "mozilla/Move.h"
         #include "mozilla/PodOperations.h"
         #include "mozilla/Vector.h"
 
-        #include "frontend/BinASTParserPerTokenizer.h"
         #include "frontend/BinSource-macros.h"
         #include "frontend/BinTokenReaderTester.h"
         #include "frontend/FullParseHandler.h"
         #include "frontend/ParseNode.h"
         #include "frontend/Parser.h"
         #include "frontend/SharedContext.h"
 
         #include "vm/RegExpObject.h"
@@ -65,30 +66,148 @@ cpp:
         template class BinASTParser<BinTokenReaderTester>;
 
         } // namespace frontend
         } // namespace js
 
 
 
 hpp:
-    # Rules for generating BinSource-class.h
+    # Rules for generating BinASTParser.h
     class:
         header: |
             /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
             * vim: set ts=8 sts=4 et sw=4 tw=99:
             * This Source Code Form is subject to the terms of the Mozilla Public
             * License, v. 2.0. If a copy of the MPL was not distributed with this
             * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
             // To generate this file, see the documentation in
             // js/src/frontend/binsource/README.md.
 
-            // This file is meant to be included from the declaration
-            // of class `BinASTParser`. The include may be public or private.
+            #ifndef frontend_BinASTParser_h
+            #define frontend_BinASTParser_h
+
+            #include "mozilla/Maybe.h"
+
+            #include "frontend/BCEParserHandle.h"
+            #include "frontend/BinASTParserPerTokenizer.h"
+            #include "frontend/BinToken.h"
+            #include "frontend/BinTokenReaderMultipart.h"
+            #include "frontend/BinTokenReaderTester.h"
+            #include "frontend/FullParseHandler.h"
+            #include "frontend/ParseContext.h"
+            #include "frontend/ParseNode.h"
+            #include "frontend/SharedContext.h"
+
+            #include "js/CompileOptions.h"
+            #include "js/GCHashTable.h"
+            #include "js/GCVector.h"
+            #include "js/Result.h"
+
+            namespace js {
+            namespace frontend {
+
+            template<typename Tok>
+            class BinASTParser : public BinASTParserPerTokenizer<Tok>
+            {
+              public:
+                using Base = BinASTParserPerTokenizer<Tok>;
+
+                using Tokenizer = Tok;
+
+                using BinFields = typename Tokenizer::BinFields;
+                using AutoList = typename Tokenizer::AutoList;
+                using AutoTaggedTuple = typename Tokenizer::AutoTaggedTuple;
+                using AutoTuple = typename Tokenizer::AutoTuple;
+                using Chars = typename Tokenizer::Chars;
+
+              public:
+                // Auto-generated types.
+                using AssertedDeclaredKind = binast::AssertedDeclaredKind;
+                using BinaryOperator = binast::BinaryOperator;
+                using CompoundAssignmentOperator = binast::CompoundAssignmentOperator;
+                using UnaryOperator = binast::UnaryOperator;
+                using UpdateOperator = binast::UpdateOperator;
+                using VariableDeclarationKind = binast::VariableDeclarationKind;
+
+              public:
+                // BinASTParserPerTokenizer types.
+                using AssertedScopeKind = typename Base::AssertedScopeKind;
+
+              public:
+                BinASTParser(JSContext* cx, LifoAlloc& alloc, UsedNameTracker& usedNames,
+                             const JS::ReadOnlyCompileOptions& options,
+                             HandleScriptSourceObject sourceObject,
+                             Handle<LazyScript*> lazyScript = nullptr)
+                    : BinASTParserPerTokenizer<Tok>(cx, alloc, usedNames, options, sourceObject, lazyScript)
+                {}
+                ~BinASTParser()
+                {}
+
+              protected:
+                // BinASTParserBase fields.
+                using Base::cx_;
+
+                using Base::alloc_;
+                using Base::usedNames_;
+
+                using Base::sourceObject_;
+                using Base::parseContext_;
+                using Base::factory_;
+
+              protected:
+                // BinASTParserPerTokenizer types.
+                using AutoVariableDeclarationKind = typename Base::AutoVariableDeclarationKind;
+              protected:
+                // BinASTParserPerTokenizer fields.
+                using Base::tokenizer_;
+                using Base::variableDeclarationKind_;
+
+              protected:
+                // BinASTParserPerTokenizer methods.
+                using Base::raiseInvalidClosedVar;
+                using Base::raiseMissingVariableInAssertedScope;
+                using Base::raiseMissingDirectEvalInAssertedScope;
+                using Base::raiseInvalidKind;
+                using Base::raiseInvalidVariant;
+                using Base::raiseMissingField;
+                using Base::raiseEmpty;
+                using Base::raiseOOM;
+                using Base::raiseError;
+
+                using Base::makeEmptyFunctionNode;
+                using Base::buildFunction;
+                using Base::buildFunctionBox;
+                using Base::addScopeName;
+                using Base::captureFunctionName;
+
+                using Base::getDeclaredScope;
+                using Base::getBoundScope;
+                using Base::checkBinding;
+                using Base::checkPositionalParameterIndices;
+
+                using Base::checkFunctionLength;
+                using Base::checkClosedVars;
+
+                using Base::prependDirectivesToBody;
+
+                using Base::forceStrictIfNecessary;
+
+              public:
+        footer: |
+            };
+
+            extern template class BinASTParser<BinTokenReaderMultipart>;
+            extern template class BinASTParser<BinTokenReaderTester>;
+
+            } // namespace frontend
+            } // namespace js
+
+            #endif // frontend_BinASTParser_h
 
     enums:
         header: |
             /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
             * vim: set ts=8 sts=4 et sw=4 tw=99:
             * This Source Code Form is subject to the terms of the Mozilla Public
             * License, v. 2.0. If a copy of the MPL was not distributed with this
             * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@@ -719,24 +838,24 @@ FunctionExpressionContents:
 FunctionOrMethodContents:
     inherits: FunctionExpressionContents
 
 GetterContents:
     inherits: FunctionExpressionContents
     fields:
         body:
             before: |
-                BINJS_TRY_DECL(params, new_<ListNode>(ParseNodeKind::ParamsBody, tokenizer_->pos(start)));
+                BINJS_TRY_DECL(params, this->template new_<ListNode>(ParseNodeKind::ParamsBody, tokenizer_->pos(start)));
 
 SetterContents:
     inherits: FunctionExpressionContents
     fields:
         param:
             after: |
-              BINJS_TRY_DECL(params, new_<ListNode>(ParseNodeKind::ParamsBody, param->pn_pos));
+              BINJS_TRY_DECL(params, this->template new_<ListNode>(ParseNodeKind::ParamsBody, param->pn_pos));
               factory_.addList(params, param);
               MOZ_TRY(checkPositionalParameterIndices(positionalParams, params));
 
 EagerFunctionExpression:
     init: |
         const auto syntax = FunctionSyntaxKind::Expression;
     fields:
         isGenerator:
@@ -1018,34 +1137,34 @@ ListOfOptionalSpreadElementOrExpression:
         } else {
             BINJS_TRY(factory_.addElision(result, tokenizer_->pos(start)));
         }
 
 ListOfParameter:
     type-ok:
         ListNode*
     init: |
-        BINJS_TRY_DECL(result, new_<ListNode>(ParseNodeKind::ParamsBody, tokenizer_->pos(start)));
+        BINJS_TRY_DECL(result, this->template new_<ListNode>(ParseNodeKind::ParamsBody, tokenizer_->pos(start)));
     append:
         factory_.addList(/* list = */ result, /* kid = */ item);
 
 ListOfStatement:
     type-ok:
         ListNode*
     init:
         BINJS_TRY_DECL(result, factory_.newStatementList(tokenizer_->pos(start)));
     append:
         factory_.addStatementToList(result, item);
 
 
 #ListOfSpreadElementOrExpression:
 #    type-ok:
 #        ListNode*
 #    init:
-#        BINJS_TRY_DECL(result, new_<ListNode>(ParseNodeKind::ParamsBody, tokenizer_->pos()));
+#        BINJS_TRY_DECL(result, this->template new_<ListNode>(ParseNodeKind::ParamsBody, tokenizer_->pos()));
 #    append:
 #        result->appendWithoutOrderAssumption(item);
 
 ListOfSwitchCase:
     type-ok:
         ListNode*
     init:
         BINJS_TRY_DECL(result, factory_.newStatementList(tokenizer_->pos(start)));
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -8,17 +8,17 @@
 
 #include "mozilla/Attributes.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/Utf8.h"
 
 #include "builtin/ModuleObject.h"
 #if defined(JS_BUILD_BINAST)
-# include "frontend/BinASTParserPerTokenizer.h"
+# include "frontend/BinASTParser.h"
 #endif // JS_BUILD_BINAST
 #include "frontend/BytecodeCompilation.h"
 #include "frontend/BytecodeEmitter.h"
 #include "frontend/EitherParser.h"
 #include "frontend/ErrorReporter.h"
 #include "frontend/FoldConstants.h"
 #include "frontend/ModuleSharedContext.h"
 #include "frontend/Parser.h"
--- a/js/src/frontend/binsource/src/main.rs
+++ b/js/src/frontend/binsource/src/main.rs
@@ -138,16 +138,20 @@ struct GlobalRules {
 
     /// Header to add at the end of the .cpp file.
     cpp_footer: Option<String>,
 
     /// Header to add at the start of the .hpp file.
     /// defining the class.
     hpp_class_header: Option<String>,
 
+    /// Footer to add at the end of the .hpp file
+    /// defining the class.
+    hpp_class_footer: Option<String>,
+
     /// Header to add at the start of the .hpp file.
     /// defining the enums.
     hpp_enums_header: Option<String>,
 
     /// Footer to add at the end of the .hpp file
     /// defining the enums.
     hpp_enums_footer: Option<String>,
 
@@ -179,16 +183,17 @@ impl GlobalRules {
         let mut parser_class_name = None;
         let mut parser_class_template = None;
         let mut parser_type_ok = None;
         let mut parser_default_value = None;
         let mut parser_list_append = None;
         let mut cpp_header = None;
         let mut cpp_footer = None;
         let mut hpp_class_header = None;
+        let mut hpp_class_footer = None;
         let mut hpp_enums_header = None;
         let mut hpp_enums_footer = None;
         let mut hpp_tokens_header = None;
         let mut hpp_tokens_footer = None;
         let mut hpp_tokens_kind_doc = None;
         let mut hpp_tokens_field_doc = None;
         let mut hpp_tokens_variants_doc = None;
         let mut per_node = HashMap::new();
@@ -216,16 +221,18 @@ impl GlobalRules {
                         .unwrap_or_else(|_| panic!("Rule cpp.header must be a string"));
                     update_rule(&mut cpp_footer, &node_entries["footer"])
                         .unwrap_or_else(|_| panic!("Rule cpp.footer must be a string"));
                     continue;
                 }
                 "hpp" => {
                     update_rule(&mut hpp_class_header, &node_entries["class"]["header"])
                         .unwrap_or_else(|_| panic!("Rule hpp.class.header must be a string"));
+                    update_rule(&mut hpp_class_footer, &node_entries["class"]["footer"])
+                        .unwrap_or_else(|_| panic!("Rule hpp.class.footer must be a string"));
                     update_rule(&mut hpp_enums_header, &node_entries["enums"]["header"])
                         .unwrap_or_else(|_| panic!("Rule hpp.enum.header must be a string"));
                     update_rule(&mut hpp_enums_footer, &node_entries["enums"]["footer"])
                         .unwrap_or_else(|_| panic!("Rule hpp.enum.footer must be a string"));
                     update_rule(&mut hpp_tokens_header, &node_entries["tokens"]["header"])
                         .unwrap_or_else(|_| panic!("Rule hpp.tokens.header must be a string"));
                     update_rule(&mut hpp_tokens_footer, &node_entries["tokens"]["footer"])
                         .unwrap_or_else(|_| panic!("Rule hpp.tokens.footer must be a string"));
@@ -407,16 +414,17 @@ impl GlobalRules {
             parser_type_ok: parser_type_ok
                 .expect("parser.type-ok should be specified"),
             parser_default_value: parser_default_value
                 .expect("parser.default-value should be specified"),
             parser_list_append,
             cpp_header,
             cpp_footer,
             hpp_class_header,
+            hpp_class_footer,
             hpp_enums_header,
             hpp_enums_footer,
             hpp_tokens_header,
             hpp_tokens_footer,
             hpp_tokens_kind_doc,
             hpp_tokens_field_doc,
             hpp_tokens_variants_doc,
             per_node,
@@ -1023,135 +1031,140 @@ enum class {name} {{
             buffer.push_str(&rendered);
         }
     }
 
     fn export_declare_sums_of_interface_methods(&self, buffer: &mut String) {
         let sums_of_interfaces = self.syntax.resolved_sums_of_interfaces_by_name()
             .iter()
             .sorted_by(|a, b| a.0.cmp(&b.0));
-        buffer.push_str("\n\n// ----- Sums of interfaces (by lexicographical order)\n");
-        buffer.push_str("// Implementations are autogenerated\n");
-        buffer.push_str("// `ParseNode*` may never be nullptr\n");
+        buffer.push_str("
+    // ----- Sums of interfaces (by lexicographical order)
+    // `ParseNode*` may never be nullptr
+");
         for &(ref name, _) in &sums_of_interfaces {
             if !self.refgraph.is_used(name.to_rc_string().clone()) {
                 continue;
             }
 
             let rules_for_this_sum = self.rules.get(name);
             let extra_params = rules_for_this_sum.extra_params;
             let rendered = self.get_method_signature(name, "", "",
                                                      &extra_params);
-            buffer.push_str(&rendered.reindent("")
+            buffer.push_str(&rendered.reindent("    ")
                             .newline_if_not_empty());
         }
         for (name, _) in sums_of_interfaces {
             let prefix = "Sum";
             if !self.refgraph.is_used(Rc::new(format!("{}{}", prefix, name))) {
                 continue;
             }
 
             let rules_for_this_sum = self.rules.get(name);
             let extra_params = rules_for_this_sum.extra_params;
             let rendered = self.get_method_signature(name, prefix,
                                                      INTERFACE_PARAMS,
                                                      &extra_params);
-            buffer.push_str(&rendered.reindent("")
+            buffer.push_str(&rendered.reindent("    ")
                             .newline_if_not_empty());
         }
     }
 
     fn export_declare_single_interface_methods(&self, buffer: &mut String) {
-        buffer.push_str("\n\n// ----- Interfaces (by lexicographical order)\n");
-        buffer.push_str("// Implementations are autogenerated\n");
-        buffer.push_str("// `ParseNode*` may never be nullptr\n");
+        buffer.push_str("
+    // ----- Interfaces (by lexicographical order)
+    // `ParseNode*` may never be nullptr
+");
         let interfaces_by_name = self.syntax.interfaces_by_name()
             .iter()
             .sorted_by(|a, b| str::cmp(a.0.to_str(), b.0.to_str()));
 
         let mut outer_parsers = Vec::with_capacity(interfaces_by_name.len());
         let mut inner_parsers = Vec::with_capacity(interfaces_by_name.len());
 
         for &(name, _) in &interfaces_by_name {
             let rules_for_this_interface = self.rules.get(name);
             let extra_params = rules_for_this_interface.extra_params;
 
             if self.refgraph.is_used(name.to_rc_string().clone()) {
                 let outer = self.get_method_signature(name, "", "", &extra_params);
-                outer_parsers.push(outer.reindent(""));
+                outer_parsers.push(outer.reindent("    "));
             }
 
             let inner_prefix = "Interface";
             if !self.refgraph.is_used(Rc::new(format!("{}{}", inner_prefix, name))) {
                 continue;
             }
             let inner = self.get_method_signature(name, inner_prefix,
                                                   INTERFACE_PARAMS,
                                                   &extra_params);
-            inner_parsers.push(inner.reindent(""));
+            inner_parsers.push(inner.reindent("    "));
         }
 
         for parser in outer_parsers.drain(..) {
             buffer.push_str(&parser);
             buffer.push_str("\n");
         }
 
         for parser in inner_parsers.drain(..) {
             buffer.push_str(&parser);
             buffer.push_str("\n");
         }
     }
 
     fn export_declare_string_enums_methods(&self, buffer: &mut String) {
-        buffer.push_str("\n\n// ----- String enums (by lexicographical order)\n");
-        buffer.push_str("// Implementations are autogenerated\n");
+        buffer.push_str("
+    // ----- String enums (by lexicographical order)
+");
         let string_enums_by_name = self.syntax.string_enums_by_name()
             .iter()
             .sorted_by(|a, b| str::cmp(a.0.to_str(), b.0.to_str()));
         for (kind, _) in string_enums_by_name {
             if !self.refgraph.is_used(kind.to_rc_string().clone()) {
                 continue;
             }
 
             let rendered = self.get_method_signature(kind, "", "", &None);
-            buffer.push_str(&rendered.reindent(""));
+            buffer.push_str(&rendered.reindent("    "));
             buffer.push_str("\n");
         }
     }
 
     fn export_declare_list_methods(&self, buffer: &mut String) {
-        buffer.push_str("\n\n// ----- Lists (by lexicographical order)\n");
-        buffer.push_str("// Implementations are autogenerated\n");
+        buffer.push_str("
+    // ----- Lists (by lexicographical order)
+");
         for parser in &self.list_parsers_to_generate {
             if !self.refgraph.is_used(parser.name.to_rc_string().clone()) {
                 continue;
             }
 
             let rules_for_this_node = self.rules.get(&parser.name);
             let extra_params = rules_for_this_node.extra_params;
             let rendered = self.get_method_signature(&parser.name, "", "",
                                                      &extra_params);
-            buffer.push_str(&rendered.reindent(""));
+            buffer.push_str(&rendered.reindent("    "));
             buffer.push_str("\n");
         }
     }
 
     fn export_declare_option_methods(&self, buffer: &mut String) {
-        buffer.push_str("\n\n// ----- Default values (by lexicographical order)\n");
-        buffer.push_str("// Implementations are autogenerated\n");
+        buffer.push_str("
+    // ----- Default values (by lexicographical order)
+");
         for parser in &self.option_parsers_to_generate {
             if !self.refgraph.is_used(parser.name.to_rc_string().clone()) {
                 continue;
             }
 
             let rules_for_this_node = self.rules.get(&parser.name);
             let extra_params = rules_for_this_node.extra_params;
             let rendered = self.get_method_signature(&parser.name, "", "",
                                                      &extra_params);
-            buffer.push_str(&rendered.reindent(""));
+            buffer.push_str(&rendered.reindent("    "));
             buffer.push_str("\n");
         }
     }
 
     fn generate_autogenerated_warning(&self) -> String {
         let warning = format!("// This file was autogenerated by binjs_generate_spidermonkey,
 // please DO NOT EDIT BY HAND.
 ");
@@ -1179,16 +1192,18 @@ enum class {name} {{
 
         self.export_declare_sums_of_interface_methods(&mut buffer);
         self.export_declare_single_interface_methods(&mut buffer);
         self.export_declare_string_enums_methods(&mut buffer);
         self.export_declare_list_methods(&mut buffer);
         self.export_declare_option_methods(&mut buffer);
 
         buffer.push_str("\n");
+        buffer.push_str(&self.rules.hpp_class_footer.reindent(""));
+        buffer.push_str("\n");
         buffer
     }
 
     fn to_spidermonkey_enum_hpp(&self) -> String {
         let mut buffer = String::new();
 
         buffer.push_str(&self.generate_autogenerated_warning());
 
--- a/js/src/fuzz-tests/testBinASTReader.cpp
+++ b/js/src/fuzz-tests/testBinASTReader.cpp
@@ -5,17 +5,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 
 #include "mozilla/ScopeExit.h"
 
 #include "jsapi.h"
 
-#include "frontend/BinASTParserPerTokenizer.h"
+#include "frontend/BinASTParser.h"
 #include "frontend/FullParseHandler.h"
 #include "frontend/ParseContext.h"
 #include "frontend/Parser.h"
 #include "fuzz-tests/tests.h"
 #include "js/CompileOptions.h"
 #include "vm/Interpreter.h"
 
 #include "vm/JSContext-inl.h"
--- a/js/src/jsapi-tests/testBinASTReader.cpp
+++ b/js/src/jsapi-tests/testBinASTReader.cpp
@@ -18,17 +18,17 @@
 #include <windows.h>
 
 #endif
 
 #include "mozilla/Maybe.h"
 
 #include "jsapi.h"
 
-#include "frontend/BinASTParserPerTokenizer.h"
+#include "frontend/BinASTParser.h"
 #include "frontend/FullParseHandler.h"
 #include "frontend/ParseContext.h"
 #include "frontend/Parser.h"
 #include "js/Vector.h"
 
 #include "jsapi-tests/tests.h"
 
 #include "vm/Interpreter.h"
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -63,17 +63,17 @@
 #endif
 #include "shellmoduleloader.out.h"
 
 #include "builtin/Array.h"
 #include "builtin/ModuleObject.h"
 #include "builtin/RegExp.h"
 #include "builtin/TestingFunctions.h"
 #if defined(JS_BUILD_BINAST)
-# include "frontend/BinASTParserPerTokenizer.h"
+# include "frontend/BinASTParser.h"
 #endif // defined(JS_BUILD_BINAST)
 #include "frontend/ModuleSharedContext.h"
 #include "frontend/Parser.h"
 #include "gc/PublicIterators.h"
 #include "jit/arm/Simulator-arm.h"
 #include "jit/InlinableNatives.h"
 #include "jit/Ion.h"
 #include "jit/JitcodeMap.h"