// This file was autogenerated by binjs_generate_spidermonkey,
// please DO NOT EDIT BY HAND.
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * 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/binast/README.md.

#include "frontend/BinASTParser.h"

#include "mozilla/ArrayUtils.h"
#include "mozilla/Casting.h"
#include "mozilla/Maybe.h"
#include "mozilla/PodOperations.h"
#include "mozilla/Vector.h"

#include <type_traits>
#include <utility>

#include "frontend/BinAST-macros.h"
#include "frontend/BinASTTokenReaderContext.h"
#include "frontend/BinASTTokenReaderMultipart.h"
#include "frontend/FullParseHandler.h"
#include "frontend/FunctionSyntaxKind.h"  // FunctionSyntaxKind
#include "frontend/ParseNode.h"
#include "frontend/Parser.h"
#include "frontend/SharedContext.h"
#ifndef ENABLE_NEW_REGEXP
#  include "irregexp/RegExpParser.h"
#endif
#include "js/RegExpFlags.h"  //  JS::RegExpFlag, JS::RegExpFlags
#ifdef ENABLE_NEW_REGEXP
#  include "new-regexp/RegExpAPI.h"
#endif
#include "vm/GeneratorAndAsyncKind.h"  // js::GeneratorKind, js::FunctionAsyncKind
#include "vm/RegExpObject.h"

#include "frontend/ParseContext-inl.h"

using JS::RegExpFlag;
using JS::RegExpFlags;

namespace js::frontend {

// Compare a bunch of `uint8_t` values (as returned by the tokenizer_) with
// a string literal (and ONLY a string literal).
template <typename Tok, size_t N>
bool operator==(const typename Tok::Chars& left, const char (&right)[N]) {
  return Tok::equals(left, right);
}

// ----- Sums of interfaces (autogenerated, by lexicographical order)
// Sums of sums are flattened.
/*
AssertedMaybePositionalParameterName ::= AssertedParameterName
    AssertedPositionalParameterName
    AssertedRestParameterName
*/
template <typename Tok>
JS::Result<Ok> BinASTParser<Tok>::parseAssertedMaybePositionalParameterName(
    AssertedScopeKind scopeKind,
    MutableHandle<GCVector<JSAtom*>> positionalParams,
    const ListContext& context) {
  BinASTKind kind = BinASTKind::_Uninitialized;
  AutoTaggedTuple guard(*tokenizer_);
  const auto start = tokenizer_->offset();

  guard.init();
  MOZ_TRY(tokenizer_->enterSum(kind, context));

  BINJS_MOZ_TRY_DECL(result,
                     parseSumAssertedMaybePositionalParameterName(
                         start, kind, scopeKind, positionalParams, context));

  MOZ_TRY(guard.done());
  return result;
}

template <typename Tok>
JS::Result<Ok> BinASTParser<Tok>::parseSumAssertedMaybePositionalParameterName(
    const size_t start, const BinASTKind kind, AssertedScopeKind scopeKind,
    MutableHandle<GCVector<JSAtom*>> positionalParams,
    const ListContext& context) {
  Ok result;
  switch (kind) {
    case BinASTKind::AssertedParameterName:
      return raiseError(
          "FIXME: Not implemented yet in this preview release "
          "(AssertedParameterName)");
    case BinASTKind::AssertedPositionalParameterName:
      MOZ_TRY_VAR(result, parseInterfaceAssertedPositionalParameterName(
                              start, scopeKind, positionalParams, context));
      break;
    case BinASTKind::AssertedRestParameterName:
      return raiseError(
          "FIXME: Not implemented yet in this preview release "
          "(AssertedRestParameterName)");
    default:
      if (isInvalidKindPossible()) {
        return raiseInvalidKind("AssertedMaybePositionalParameterName", kind);
      } else {
        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE(
            "invalid BinASTKind should not appear");
      }
  }
  return result;
}

/*
AssignmentTarget ::= ArrayAssignmentTarget
    AssignmentTargetIdentifier
    ComputedMemberAssignmentTarget
    ObjectAssignmentTarget
    StaticMemberAssignmentTarget
*/
template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseAssignmentTarget(
    const FieldContext& context) {
  BinASTKind kind = BinASTKind::_Uninitialized;
  AutoTaggedTuple guard(*tokenizer_);
  const auto start = tokenizer_->offset();

  guard.init();
  MOZ_TRY(tokenizer_->enterSum(kind, context));

  BINJS_MOZ_TRY_DECL(result, parseSumAssignmentTarget(start, kind, context));

  MOZ_TRY(guard.done());
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseSumAssignmentTarget(
    const size_t start, const BinASTKind kind, const FieldContext& context) {
  ParseNode* result;
  switch (kind) {
    case BinASTKind::ArrayAssignmentTarget:
      MOZ_TRY_VAR(result, parseInterfaceArrayAssignmentTarget(start, context));
      break;
    case BinASTKind::AssignmentTargetIdentifier:
      MOZ_TRY_VAR(result,
                  parseInterfaceAssignmentTargetIdentifier(start, context));
      break;
    case BinASTKind::ComputedMemberAssignmentTarget:
      MOZ_TRY_VAR(result,
                  parseInterfaceComputedMemberAssignmentTarget(start, context));
      break;
    case BinASTKind::ObjectAssignmentTarget:
      MOZ_TRY_VAR(result, parseInterfaceObjectAssignmentTarget(start, context));
      break;
    case BinASTKind::StaticMemberAssignmentTarget:
      MOZ_TRY_VAR(result,
                  parseInterfaceStaticMemberAssignmentTarget(start, context));
      break;
    default:
      if (isInvalidKindPossible()) {
        return raiseInvalidKind("AssignmentTarget", kind);
      } else {
        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE(
            "invalid BinASTKind should not appear");
      }
  }
  return result;
}

/*
AssignmentTargetOrForInOfBinding ::= ArrayAssignmentTarget
    AssignmentTargetIdentifier
    ComputedMemberAssignmentTarget
    ForInOfBinding
    ObjectAssignmentTarget
    StaticMemberAssignmentTarget
*/
template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseAssignmentTargetOrForInOfBinding(
    const FieldContext& context) {
  BinASTKind kind = BinASTKind::_Uninitialized;
  AutoTaggedTuple guard(*tokenizer_);
  const auto start = tokenizer_->offset();

  guard.init();
  MOZ_TRY(tokenizer_->enterSum(kind, context));

  BINJS_MOZ_TRY_DECL(
      result, parseSumAssignmentTargetOrForInOfBinding(start, kind, context));

  MOZ_TRY(guard.done());
  return result;
}

template <typename Tok>
JS::Result<ParseNode*>
BinASTParser<Tok>::parseSumAssignmentTargetOrForInOfBinding(
    const size_t start, const BinASTKind kind, const FieldContext& context) {
  ParseNode* result;
  switch (kind) {
    case BinASTKind::ArrayAssignmentTarget:
      MOZ_TRY_VAR(result, parseInterfaceArrayAssignmentTarget(start, context));
      break;
    case BinASTKind::AssignmentTargetIdentifier:
      MOZ_TRY_VAR(result,
                  parseInterfaceAssignmentTargetIdentifier(start, context));
      break;
    case BinASTKind::ComputedMemberAssignmentTarget:
      MOZ_TRY_VAR(result,
                  parseInterfaceComputedMemberAssignmentTarget(start, context));
      break;
    case BinASTKind::ForInOfBinding:
      MOZ_TRY_VAR(result, parseInterfaceForInOfBinding(start, context));
      break;
    case BinASTKind::ObjectAssignmentTarget:
      MOZ_TRY_VAR(result, parseInterfaceObjectAssignmentTarget(start, context));
      break;
    case BinASTKind::StaticMemberAssignmentTarget:
      MOZ_TRY_VAR(result,
                  parseInterfaceStaticMemberAssignmentTarget(start, context));
      break;
    default:
      if (isInvalidKindPossible()) {
        return raiseInvalidKind("AssignmentTargetOrForInOfBinding", kind);
      } else {
        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE(
            "invalid BinASTKind should not appear");
      }
  }
  return result;
}

/*
Binding ::= ArrayBinding
    BindingIdentifier
    ObjectBinding
*/
template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseBinding(
    const FieldContext& context) {
  BinASTKind kind = BinASTKind::_Uninitialized;
  AutoTaggedTuple guard(*tokenizer_);
  const auto start = tokenizer_->offset();

  guard.init();
  MOZ_TRY(tokenizer_->enterSum(kind, context));

  BINJS_MOZ_TRY_DECL(result, parseSumBinding(start, kind, context));

  MOZ_TRY(guard.done());
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseSumBinding(
    const size_t start, const BinASTKind kind, const FieldContext& context) {
  ParseNode* result;
  switch (kind) {
    case BinASTKind::ArrayBinding:
      MOZ_TRY_VAR(result, parseInterfaceArrayBinding(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::BindingIdentifier:
      MOZ_TRY_VAR(result, parseInterfaceBindingIdentifier(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::ObjectBinding:
      MOZ_TRY_VAR(result, parseInterfaceObjectBinding(
                              start, FieldOrListContext(context)));
      break;
    default:
      if (isInvalidKindPossible()) {
        return raiseInvalidKind("Binding", kind);
      } else {
        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE(
            "invalid BinASTKind should not appear");
      }
  }
  return result;
}

/*
Expression ::= ArrayExpression
    AssignmentExpression
    AwaitExpression
    BinaryExpression
    CallExpression
    ClassExpression
    CompoundAssignmentExpression
    ComputedMemberExpression
    ConditionalExpression
    EagerArrowExpressionWithExpression
    EagerArrowExpressionWithFunctionBody
    EagerFunctionExpression
    IdentifierExpression
    LazyArrowExpressionWithExpression
    LazyArrowExpressionWithFunctionBody
    LazyFunctionExpression
    LiteralBooleanExpression
    LiteralInfinityExpression
    LiteralNullExpression
    LiteralNumericExpression
    LiteralRegExpExpression
    LiteralStringExpression
    NewExpression
    NewTargetExpression
    ObjectExpression
    StaticMemberExpression
    TemplateExpression
    ThisExpression
    UnaryExpression
    UpdateExpression
    YieldExpression
    YieldStarExpression
*/
template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseExpression(
    const FieldContext& context) {
  BinASTKind kind = BinASTKind::_Uninitialized;
  AutoTaggedTuple guard(*tokenizer_);
  const auto start = tokenizer_->offset();

  guard.init();
  MOZ_TRY(tokenizer_->enterSum(kind, context));

  BINJS_MOZ_TRY_DECL(result, parseSumExpression(start, kind, context));

  MOZ_TRY(guard.done());
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseSumExpression(
    const size_t start, const BinASTKind kind, const FieldContext& context) {
  ParseNode* result;
  switch (kind) {
    case BinASTKind::ArrayExpression:
      MOZ_TRY_VAR(result, parseInterfaceArrayExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::AssignmentExpression:
      MOZ_TRY_VAR(result, parseInterfaceAssignmentExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::AwaitExpression:
      MOZ_TRY_VAR(result, parseInterfaceAwaitExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::BinaryExpression:
      MOZ_TRY_VAR(result, parseInterfaceBinaryExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::CallExpression:
      MOZ_TRY_VAR(result, parseInterfaceCallExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::ClassExpression:
      MOZ_TRY_VAR(result, parseInterfaceClassExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::CompoundAssignmentExpression:
      MOZ_TRY_VAR(result, parseInterfaceCompoundAssignmentExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::ComputedMemberExpression:
      MOZ_TRY_VAR(result, parseInterfaceComputedMemberExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::ConditionalExpression:
      MOZ_TRY_VAR(result, parseInterfaceConditionalExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::EagerArrowExpressionWithExpression:
      MOZ_TRY_VAR(result, parseInterfaceEagerArrowExpressionWithExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::EagerArrowExpressionWithFunctionBody:
      MOZ_TRY_VAR(result, parseInterfaceEagerArrowExpressionWithFunctionBody(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::EagerFunctionExpression:
      MOZ_TRY_VAR(result, parseInterfaceEagerFunctionExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::IdentifierExpression:
      MOZ_TRY_VAR(result, parseInterfaceIdentifierExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::LazyArrowExpressionWithExpression:
      MOZ_TRY_VAR(result, parseInterfaceLazyArrowExpressionWithExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::LazyArrowExpressionWithFunctionBody:
      MOZ_TRY_VAR(result, parseInterfaceLazyArrowExpressionWithFunctionBody(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::LazyFunctionExpression:
      MOZ_TRY_VAR(result, parseInterfaceLazyFunctionExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::LiteralBooleanExpression:
      MOZ_TRY_VAR(result, parseInterfaceLiteralBooleanExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::LiteralInfinityExpression:
      MOZ_TRY_VAR(result, parseInterfaceLiteralInfinityExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::LiteralNullExpression:
      MOZ_TRY_VAR(result, parseInterfaceLiteralNullExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::LiteralNumericExpression:
      MOZ_TRY_VAR(result, parseInterfaceLiteralNumericExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::LiteralRegExpExpression:
      MOZ_TRY_VAR(result, parseInterfaceLiteralRegExpExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::LiteralStringExpression:
      MOZ_TRY_VAR(result, parseInterfaceLiteralStringExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::NewExpression:
      MOZ_TRY_VAR(result, parseInterfaceNewExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::NewTargetExpression:
      MOZ_TRY_VAR(result, parseInterfaceNewTargetExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::ObjectExpression:
      MOZ_TRY_VAR(result, parseInterfaceObjectExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::StaticMemberExpression:
      MOZ_TRY_VAR(result, parseInterfaceStaticMemberExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::TemplateExpression:
      MOZ_TRY_VAR(result, parseInterfaceTemplateExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::ThisExpression:
      MOZ_TRY_VAR(result, parseInterfaceThisExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::UnaryExpression:
      MOZ_TRY_VAR(result, parseInterfaceUnaryExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::UpdateExpression:
      MOZ_TRY_VAR(result, parseInterfaceUpdateExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::YieldExpression:
      MOZ_TRY_VAR(result, parseInterfaceYieldExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::YieldStarExpression:
      MOZ_TRY_VAR(result, parseInterfaceYieldStarExpression(
                              start, FieldOrListContext(context)));
      break;
    default:
      if (isInvalidKindPossible()) {
        return raiseInvalidKind("Expression", kind);
      } else {
        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE(
            "invalid BinASTKind should not appear");
      }
  }
  return result;
}

/*
ExpressionOrSpreadElement ::= ArrayExpression
    AssignmentExpression
    AwaitExpression
    BinaryExpression
    CallExpression
    ClassExpression
    CompoundAssignmentExpression
    ComputedMemberExpression
    ConditionalExpression
    EagerArrowExpressionWithExpression
    EagerArrowExpressionWithFunctionBody
    EagerFunctionExpression
    IdentifierExpression
    LazyArrowExpressionWithExpression
    LazyArrowExpressionWithFunctionBody
    LazyFunctionExpression
    LiteralBooleanExpression
    LiteralInfinityExpression
    LiteralNullExpression
    LiteralNumericExpression
    LiteralRegExpExpression
    LiteralStringExpression
    NewExpression
    NewTargetExpression
    ObjectExpression
    SpreadElement
    StaticMemberExpression
    TemplateExpression
    ThisExpression
    UnaryExpression
    UpdateExpression
    YieldExpression
    YieldStarExpression
*/
template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseExpressionOrSpreadElement(
    const ListContext& context) {
  BinASTKind kind = BinASTKind::_Uninitialized;
  AutoTaggedTuple guard(*tokenizer_);
  const auto start = tokenizer_->offset();

  guard.init();
  MOZ_TRY(tokenizer_->enterSum(kind, context));

  BINJS_MOZ_TRY_DECL(result,
                     parseSumExpressionOrSpreadElement(start, kind, context));

  MOZ_TRY(guard.done());
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseSumExpressionOrSpreadElement(
    const size_t start, const BinASTKind kind, const ListContext& context) {
  ParseNode* result;
  switch (kind) {
    case BinASTKind::ArrayExpression:
      MOZ_TRY_VAR(result, parseInterfaceArrayExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::AssignmentExpression:
      MOZ_TRY_VAR(result, parseInterfaceAssignmentExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::AwaitExpression:
      MOZ_TRY_VAR(result, parseInterfaceAwaitExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::BinaryExpression:
      MOZ_TRY_VAR(result, parseInterfaceBinaryExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::CallExpression:
      MOZ_TRY_VAR(result, parseInterfaceCallExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::ClassExpression:
      MOZ_TRY_VAR(result, parseInterfaceClassExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::CompoundAssignmentExpression:
      MOZ_TRY_VAR(result, parseInterfaceCompoundAssignmentExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::ComputedMemberExpression:
      MOZ_TRY_VAR(result, parseInterfaceComputedMemberExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::ConditionalExpression:
      MOZ_TRY_VAR(result, parseInterfaceConditionalExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::EagerArrowExpressionWithExpression:
      MOZ_TRY_VAR(result, parseInterfaceEagerArrowExpressionWithExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::EagerArrowExpressionWithFunctionBody:
      MOZ_TRY_VAR(result, parseInterfaceEagerArrowExpressionWithFunctionBody(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::EagerFunctionExpression:
      MOZ_TRY_VAR(result, parseInterfaceEagerFunctionExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::IdentifierExpression:
      MOZ_TRY_VAR(result, parseInterfaceIdentifierExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::LazyArrowExpressionWithExpression:
      MOZ_TRY_VAR(result, parseInterfaceLazyArrowExpressionWithExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::LazyArrowExpressionWithFunctionBody:
      MOZ_TRY_VAR(result, parseInterfaceLazyArrowExpressionWithFunctionBody(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::LazyFunctionExpression:
      MOZ_TRY_VAR(result, parseInterfaceLazyFunctionExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::LiteralBooleanExpression:
      MOZ_TRY_VAR(result, parseInterfaceLiteralBooleanExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::LiteralInfinityExpression:
      MOZ_TRY_VAR(result, parseInterfaceLiteralInfinityExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::LiteralNullExpression:
      MOZ_TRY_VAR(result, parseInterfaceLiteralNullExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::LiteralNumericExpression:
      MOZ_TRY_VAR(result, parseInterfaceLiteralNumericExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::LiteralRegExpExpression:
      MOZ_TRY_VAR(result, parseInterfaceLiteralRegExpExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::LiteralStringExpression:
      MOZ_TRY_VAR(result, parseInterfaceLiteralStringExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::NewExpression:
      MOZ_TRY_VAR(result, parseInterfaceNewExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::NewTargetExpression:
      MOZ_TRY_VAR(result, parseInterfaceNewTargetExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::ObjectExpression:
      MOZ_TRY_VAR(result, parseInterfaceObjectExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::SpreadElement:
      MOZ_TRY_VAR(result, parseInterfaceSpreadElement(start, context));
      break;
    case BinASTKind::StaticMemberExpression:
      MOZ_TRY_VAR(result, parseInterfaceStaticMemberExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::TemplateExpression:
      MOZ_TRY_VAR(result, parseInterfaceTemplateExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::ThisExpression:
      MOZ_TRY_VAR(result, parseInterfaceThisExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::UnaryExpression:
      MOZ_TRY_VAR(result, parseInterfaceUnaryExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::UpdateExpression:
      MOZ_TRY_VAR(result, parseInterfaceUpdateExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::YieldExpression:
      MOZ_TRY_VAR(result, parseInterfaceYieldExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::YieldStarExpression:
      MOZ_TRY_VAR(result, parseInterfaceYieldStarExpression(
                              start, FieldOrListContext(context)));
      break;
    default:
      if (isInvalidKindPossible()) {
        return raiseInvalidKind("ExpressionOrSpreadElement", kind);
      } else {
        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE(
            "invalid BinASTKind should not appear");
      }
  }
  return result;
}

/*
ExpressionOrSuper ::= ArrayExpression
    AssignmentExpression
    AwaitExpression
    BinaryExpression
    CallExpression
    ClassExpression
    CompoundAssignmentExpression
    ComputedMemberExpression
    ConditionalExpression
    EagerArrowExpressionWithExpression
    EagerArrowExpressionWithFunctionBody
    EagerFunctionExpression
    IdentifierExpression
    LazyArrowExpressionWithExpression
    LazyArrowExpressionWithFunctionBody
    LazyFunctionExpression
    LiteralBooleanExpression
    LiteralInfinityExpression
    LiteralNullExpression
    LiteralNumericExpression
    LiteralRegExpExpression
    LiteralStringExpression
    NewExpression
    NewTargetExpression
    ObjectExpression
    StaticMemberExpression
    Super
    TemplateExpression
    ThisExpression
    UnaryExpression
    UpdateExpression
    YieldExpression
    YieldStarExpression
*/
template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseExpressionOrSuper(
    const FieldContext& context) {
  BinASTKind kind = BinASTKind::_Uninitialized;
  AutoTaggedTuple guard(*tokenizer_);
  const auto start = tokenizer_->offset();

  guard.init();
  MOZ_TRY(tokenizer_->enterSum(kind, context));

  BINJS_MOZ_TRY_DECL(result, parseSumExpressionOrSuper(start, kind, context));

  MOZ_TRY(guard.done());
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseSumExpressionOrSuper(
    const size_t start, const BinASTKind kind, const FieldContext& context) {
  ParseNode* result;
  switch (kind) {
    case BinASTKind::ArrayExpression:
      MOZ_TRY_VAR(result, parseInterfaceArrayExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::AssignmentExpression:
      MOZ_TRY_VAR(result, parseInterfaceAssignmentExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::AwaitExpression:
      MOZ_TRY_VAR(result, parseInterfaceAwaitExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::BinaryExpression:
      MOZ_TRY_VAR(result, parseInterfaceBinaryExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::CallExpression:
      MOZ_TRY_VAR(result, parseInterfaceCallExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::ClassExpression:
      MOZ_TRY_VAR(result, parseInterfaceClassExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::CompoundAssignmentExpression:
      MOZ_TRY_VAR(result, parseInterfaceCompoundAssignmentExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::ComputedMemberExpression:
      MOZ_TRY_VAR(result, parseInterfaceComputedMemberExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::ConditionalExpression:
      MOZ_TRY_VAR(result, parseInterfaceConditionalExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::EagerArrowExpressionWithExpression:
      MOZ_TRY_VAR(result, parseInterfaceEagerArrowExpressionWithExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::EagerArrowExpressionWithFunctionBody:
      MOZ_TRY_VAR(result, parseInterfaceEagerArrowExpressionWithFunctionBody(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::EagerFunctionExpression:
      MOZ_TRY_VAR(result, parseInterfaceEagerFunctionExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::IdentifierExpression:
      MOZ_TRY_VAR(result, parseInterfaceIdentifierExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::LazyArrowExpressionWithExpression:
      MOZ_TRY_VAR(result, parseInterfaceLazyArrowExpressionWithExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::LazyArrowExpressionWithFunctionBody:
      MOZ_TRY_VAR(result, parseInterfaceLazyArrowExpressionWithFunctionBody(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::LazyFunctionExpression:
      MOZ_TRY_VAR(result, parseInterfaceLazyFunctionExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::LiteralBooleanExpression:
      MOZ_TRY_VAR(result, parseInterfaceLiteralBooleanExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::LiteralInfinityExpression:
      MOZ_TRY_VAR(result, parseInterfaceLiteralInfinityExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::LiteralNullExpression:
      MOZ_TRY_VAR(result, parseInterfaceLiteralNullExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::LiteralNumericExpression:
      MOZ_TRY_VAR(result, parseInterfaceLiteralNumericExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::LiteralRegExpExpression:
      MOZ_TRY_VAR(result, parseInterfaceLiteralRegExpExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::LiteralStringExpression:
      MOZ_TRY_VAR(result, parseInterfaceLiteralStringExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::NewExpression:
      MOZ_TRY_VAR(result, parseInterfaceNewExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::NewTargetExpression:
      MOZ_TRY_VAR(result, parseInterfaceNewTargetExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::ObjectExpression:
      MOZ_TRY_VAR(result, parseInterfaceObjectExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::StaticMemberExpression:
      MOZ_TRY_VAR(result, parseInterfaceStaticMemberExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::Super:
      MOZ_TRY_VAR(result, parseInterfaceSuper(start, context));
      break;
    case BinASTKind::TemplateExpression:
      MOZ_TRY_VAR(result, parseInterfaceTemplateExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::ThisExpression:
      MOZ_TRY_VAR(result, parseInterfaceThisExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::UnaryExpression:
      MOZ_TRY_VAR(result, parseInterfaceUnaryExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::UpdateExpression:
      MOZ_TRY_VAR(result, parseInterfaceUpdateExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::YieldExpression:
      MOZ_TRY_VAR(result, parseInterfaceYieldExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::YieldStarExpression:
      MOZ_TRY_VAR(result, parseInterfaceYieldStarExpression(
                              start, FieldOrListContext(context)));
      break;
    default:
      if (isInvalidKindPossible()) {
        return raiseInvalidKind("ExpressionOrSuper", kind);
      } else {
        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE(
            "invalid BinASTKind should not appear");
      }
  }
  return result;
}

template <typename Tok>
JS::Result<ParseNode*>
BinASTParser<Tok>::parseSumExpressionOrVariableDeclaration(
    const size_t start, const BinASTKind kind, const FieldContext& context) {
  ParseNode* result;
  switch (kind) {
    case BinASTKind::ArrayExpression:
      MOZ_TRY_VAR(result, parseInterfaceArrayExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::AssignmentExpression:
      MOZ_TRY_VAR(result, parseInterfaceAssignmentExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::AwaitExpression:
      MOZ_TRY_VAR(result, parseInterfaceAwaitExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::BinaryExpression:
      MOZ_TRY_VAR(result, parseInterfaceBinaryExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::CallExpression:
      MOZ_TRY_VAR(result, parseInterfaceCallExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::ClassExpression:
      MOZ_TRY_VAR(result, parseInterfaceClassExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::CompoundAssignmentExpression:
      MOZ_TRY_VAR(result, parseInterfaceCompoundAssignmentExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::ComputedMemberExpression:
      MOZ_TRY_VAR(result, parseInterfaceComputedMemberExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::ConditionalExpression:
      MOZ_TRY_VAR(result, parseInterfaceConditionalExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::EagerArrowExpressionWithExpression:
      MOZ_TRY_VAR(result, parseInterfaceEagerArrowExpressionWithExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::EagerArrowExpressionWithFunctionBody:
      MOZ_TRY_VAR(result, parseInterfaceEagerArrowExpressionWithFunctionBody(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::EagerFunctionExpression:
      MOZ_TRY_VAR(result, parseInterfaceEagerFunctionExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::IdentifierExpression:
      MOZ_TRY_VAR(result, parseInterfaceIdentifierExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::LazyArrowExpressionWithExpression:
      MOZ_TRY_VAR(result, parseInterfaceLazyArrowExpressionWithExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::LazyArrowExpressionWithFunctionBody:
      MOZ_TRY_VAR(result, parseInterfaceLazyArrowExpressionWithFunctionBody(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::LazyFunctionExpression:
      MOZ_TRY_VAR(result, parseInterfaceLazyFunctionExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::LiteralBooleanExpression:
      MOZ_TRY_VAR(result, parseInterfaceLiteralBooleanExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::LiteralInfinityExpression:
      MOZ_TRY_VAR(result, parseInterfaceLiteralInfinityExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::LiteralNullExpression:
      MOZ_TRY_VAR(result, parseInterfaceLiteralNullExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::LiteralNumericExpression:
      MOZ_TRY_VAR(result, parseInterfaceLiteralNumericExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::LiteralRegExpExpression:
      MOZ_TRY_VAR(result, parseInterfaceLiteralRegExpExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::LiteralStringExpression:
      MOZ_TRY_VAR(result, parseInterfaceLiteralStringExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::NewExpression:
      MOZ_TRY_VAR(result, parseInterfaceNewExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::NewTargetExpression:
      MOZ_TRY_VAR(result, parseInterfaceNewTargetExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::ObjectExpression:
      MOZ_TRY_VAR(result, parseInterfaceObjectExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::StaticMemberExpression:
      MOZ_TRY_VAR(result, parseInterfaceStaticMemberExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::TemplateExpression:
      MOZ_TRY_VAR(result, parseInterfaceTemplateExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::ThisExpression:
      MOZ_TRY_VAR(result, parseInterfaceThisExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::UnaryExpression:
      MOZ_TRY_VAR(result, parseInterfaceUnaryExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::UpdateExpression:
      MOZ_TRY_VAR(result, parseInterfaceUpdateExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::VariableDeclaration:
      MOZ_TRY_VAR(result, parseInterfaceVariableDeclaration(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::YieldExpression:
      MOZ_TRY_VAR(result, parseInterfaceYieldExpression(
                              start, FieldOrListContext(context)));
      break;
    case BinASTKind::YieldStarExpression:
      MOZ_TRY_VAR(result, parseInterfaceYieldStarExpression(
                              start, FieldOrListContext(context)));
      break;
    default:
      if (isInvalidKindPossible()) {
        return raiseInvalidKind("ExpressionOrVariableDeclaration", kind);
      } else {
        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE(
            "invalid BinASTKind should not appear");
      }
  }
  return result;
}

/*
ObjectProperty ::= DataProperty
    EagerGetter
    EagerMethod
    EagerSetter
    LazyGetter
    LazyMethod
    LazySetter
    ShorthandProperty
*/
template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseObjectProperty(
    const ListContext& context) {
  BinASTKind kind = BinASTKind::_Uninitialized;
  AutoTaggedTuple guard(*tokenizer_);
  const auto start = tokenizer_->offset();

  guard.init();
  MOZ_TRY(tokenizer_->enterSum(kind, context));

  BINJS_MOZ_TRY_DECL(result, parseSumObjectProperty(start, kind, context));

  MOZ_TRY(guard.done());
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseSumObjectProperty(
    const size_t start, const BinASTKind kind, const ListContext& context) {
  ParseNode* result;
  switch (kind) {
    case BinASTKind::DataProperty:
      MOZ_TRY_VAR(result, parseInterfaceDataProperty(start, context));
      break;
    case BinASTKind::EagerGetter:
      MOZ_TRY_VAR(result, parseInterfaceEagerGetter(start, context));
      break;
    case BinASTKind::EagerMethod:
      MOZ_TRY_VAR(result, parseInterfaceEagerMethod(start, context));
      break;
    case BinASTKind::EagerSetter:
      MOZ_TRY_VAR(result, parseInterfaceEagerSetter(start, context));
      break;
    case BinASTKind::LazyGetter:
      MOZ_TRY_VAR(result, parseInterfaceLazyGetter(start, context));
      break;
    case BinASTKind::LazyMethod:
      MOZ_TRY_VAR(result, parseInterfaceLazyMethod(start, context));
      break;
    case BinASTKind::LazySetter:
      MOZ_TRY_VAR(result, parseInterfaceLazySetter(start, context));
      break;
    case BinASTKind::ShorthandProperty:
      MOZ_TRY_VAR(result, parseInterfaceShorthandProperty(start, context));
      break;
    default:
      if (isInvalidKindPossible()) {
        return raiseInvalidKind("ObjectProperty", kind);
      } else {
        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE(
            "invalid BinASTKind should not appear");
      }
  }
  return result;
}

/*
Parameter ::= ArrayBinding
    BindingIdentifier
    BindingWithInitializer
    ObjectBinding
*/
template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseParameter(
    const FieldOrListContext& context) {
  BinASTKind kind = BinASTKind::_Uninitialized;
  AutoTaggedTuple guard(*tokenizer_);
  const auto start = tokenizer_->offset();

  guard.init();
  MOZ_TRY(tokenizer_->enterSum(kind, context));

  BINJS_MOZ_TRY_DECL(result, parseSumParameter(start, kind, context));

  MOZ_TRY(guard.done());
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseSumParameter(
    const size_t start, const BinASTKind kind,
    const FieldOrListContext& context) {
  ParseNode* result;
  switch (kind) {
    case BinASTKind::ArrayBinding:
      MOZ_TRY_VAR(result, parseInterfaceArrayBinding(start, context));
      break;
    case BinASTKind::BindingIdentifier:
      MOZ_TRY_VAR(result, parseInterfaceBindingIdentifier(start, context));
      if (!pc_->positionalFormalParameterNames().append(
              result->template as<NameNode>().atom())) {
        return raiseOOM();
      }
      if (pc_->isFunctionBox()) {
        pc_->functionBox()->length++;
      }
      break;
    case BinASTKind::BindingWithInitializer:
      MOZ_TRY_VAR(result, parseInterfaceBindingWithInitializer(start, context));
      break;
    case BinASTKind::ObjectBinding:
      MOZ_TRY_VAR(result, parseInterfaceObjectBinding(start, context));
      break;
    default:
      if (isInvalidKindPossible()) {
        return raiseInvalidKind("Parameter", kind);
      } else {
        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE(
            "invalid BinASTKind should not appear");
      }
  }
  return result;
}

/*
Program ::= Module
    Script
*/
template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseProgram(
    const RootContext& context) {
  BinASTKind kind = BinASTKind::_Uninitialized;
  AutoTaggedTuple guard(*tokenizer_);
  const auto start = tokenizer_->offset();

  guard.init();
  MOZ_TRY(tokenizer_->enterSum(kind, context));

  BINJS_MOZ_TRY_DECL(result, parseSumProgram(start, kind, context));

  MOZ_TRY(guard.done());
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseSumProgram(
    const size_t start, const BinASTKind kind, const RootContext& context) {
  ParseNode* result;
  switch (kind) {
    case BinASTKind::Module:
      MOZ_TRY_VAR(result, parseInterfaceModule(start, context));
      break;
    case BinASTKind::Script:
      MOZ_TRY_VAR(result, parseInterfaceScript(start, context));
      break;
    default:
      if (isInvalidKindPossible()) {
        return raiseInvalidKind("Program", kind);
      } else {
        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE(
            "invalid BinASTKind should not appear");
      }
  }
  return result;
}

/*
PropertyName ::= ComputedPropertyName
    LiteralPropertyName
*/
template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parsePropertyName(
    const FieldContext& context) {
  BinASTKind kind = BinASTKind::_Uninitialized;
  AutoTaggedTuple guard(*tokenizer_);
  const auto start = tokenizer_->offset();

  guard.init();
  MOZ_TRY(tokenizer_->enterSum(kind, context));

  BINJS_MOZ_TRY_DECL(result, parseSumPropertyName(start, kind, context));

  MOZ_TRY(guard.done());
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseSumPropertyName(
    const size_t start, const BinASTKind kind, const FieldContext& context) {
  ParseNode* result;
  switch (kind) {
    case BinASTKind::ComputedPropertyName:
      MOZ_TRY_VAR(result, parseInterfaceComputedPropertyName(start, context));
      break;
    case BinASTKind::LiteralPropertyName:
      MOZ_TRY_VAR(result, parseInterfaceLiteralPropertyName(start, context));
      break;
    default:
      if (isInvalidKindPossible()) {
        return raiseInvalidKind("PropertyName", kind);
      } else {
        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE(
            "invalid BinASTKind should not appear");
      }
  }
  return result;
}

/*
SimpleAssignmentTarget ::= AssignmentTargetIdentifier
    ComputedMemberAssignmentTarget
    StaticMemberAssignmentTarget
*/
template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseSimpleAssignmentTarget(
    const FieldContext& context) {
  BinASTKind kind = BinASTKind::_Uninitialized;
  AutoTaggedTuple guard(*tokenizer_);
  const auto start = tokenizer_->offset();

  guard.init();
  MOZ_TRY(tokenizer_->enterSum(kind, context));

  BINJS_MOZ_TRY_DECL(result,
                     parseSumSimpleAssignmentTarget(start, kind, context));

  MOZ_TRY(guard.done());
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseSumSimpleAssignmentTarget(
    const size_t start, const BinASTKind kind, const FieldContext& context) {
  ParseNode* result;
  switch (kind) {
    case BinASTKind::AssignmentTargetIdentifier:
      MOZ_TRY_VAR(result,
                  parseInterfaceAssignmentTargetIdentifier(start, context));
      break;
    case BinASTKind::ComputedMemberAssignmentTarget:
      MOZ_TRY_VAR(result,
                  parseInterfaceComputedMemberAssignmentTarget(start, context));
      break;
    case BinASTKind::StaticMemberAssignmentTarget:
      MOZ_TRY_VAR(result,
                  parseInterfaceStaticMemberAssignmentTarget(start, context));
      break;
    default:
      if (isInvalidKindPossible()) {
        return raiseInvalidKind("SimpleAssignmentTarget", kind);
      } else {
        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE(
            "invalid BinASTKind should not appear");
      }
  }
  return result;
}

/*
Statement ::= Block
    BreakStatement
    ClassDeclaration
    ContinueStatement
    DebuggerStatement
    DoWhileStatement
    EagerFunctionDeclaration
    EmptyStatement
    ExpressionStatement
    ForInStatement
    ForOfStatement
    ForStatement
    IfStatement
    LabelledStatement
    LazyFunctionDeclaration
    ReturnStatement
    SwitchStatement
    SwitchStatementWithDefault
    ThrowStatement
    TryCatchStatement
    TryFinallyStatement
    VariableDeclaration
    WhileStatement
    WithStatement
*/
template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseStatement(
    const FieldOrListContext& context) {
  BinASTKind kind = BinASTKind::_Uninitialized;
  AutoTaggedTuple guard(*tokenizer_);
  const auto start = tokenizer_->offset();

  guard.init();
  MOZ_TRY(tokenizer_->enterSum(kind, context));

  BINJS_MOZ_TRY_DECL(result, parseSumStatement(start, kind, context));

  MOZ_TRY(guard.done());
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseSumStatement(
    const size_t start, const BinASTKind kind,
    const FieldOrListContext& context) {
  ParseNode* result;
  switch (kind) {
    case BinASTKind::Block:
      MOZ_TRY_VAR(result, parseInterfaceBlock(start, context));
      break;
    case BinASTKind::BreakStatement:
      MOZ_TRY_VAR(result, parseInterfaceBreakStatement(start, context));
      break;
    case BinASTKind::ClassDeclaration:
      MOZ_TRY_VAR(result, parseInterfaceClassDeclaration(start, context));
      break;
    case BinASTKind::ContinueStatement:
      MOZ_TRY_VAR(result, parseInterfaceContinueStatement(start, context));
      break;
    case BinASTKind::DebuggerStatement:
      MOZ_TRY_VAR(result, parseInterfaceDebuggerStatement(start, context));
      break;
    case BinASTKind::DoWhileStatement:
      MOZ_TRY_VAR(result, parseInterfaceDoWhileStatement(start, context));
      break;
    case BinASTKind::EagerFunctionDeclaration:
      MOZ_TRY_VAR(result,
                  parseInterfaceEagerFunctionDeclaration(start, context));
      break;
    case BinASTKind::EmptyStatement:
      MOZ_TRY_VAR(result, parseInterfaceEmptyStatement(start, context));
      break;
    case BinASTKind::ExpressionStatement:
      MOZ_TRY_VAR(result, parseInterfaceExpressionStatement(start, context));
      break;
    case BinASTKind::ForInStatement:
      MOZ_TRY_VAR(result, parseInterfaceForInStatement(start, context));
      break;
    case BinASTKind::ForOfStatement:
      MOZ_TRY_VAR(result, parseInterfaceForOfStatement(start, context));
      break;
    case BinASTKind::ForStatement:
      MOZ_TRY_VAR(result, parseInterfaceForStatement(start, context));
      break;
    case BinASTKind::IfStatement:
      MOZ_TRY_VAR(result, parseInterfaceIfStatement(start, context));
      break;
    case BinASTKind::LabelledStatement:
      MOZ_TRY_VAR(result, parseInterfaceLabelledStatement(start, context));
      break;
    case BinASTKind::LazyFunctionDeclaration:
      MOZ_TRY_VAR(result,
                  parseInterfaceLazyFunctionDeclaration(start, context));
      break;
    case BinASTKind::ReturnStatement:
      MOZ_TRY_VAR(result, parseInterfaceReturnStatement(start, context));
      break;
    case BinASTKind::SwitchStatement:
      MOZ_TRY_VAR(result, parseInterfaceSwitchStatement(start, context));
      break;
    case BinASTKind::SwitchStatementWithDefault:
      MOZ_TRY_VAR(result,
                  parseInterfaceSwitchStatementWithDefault(start, context));
      break;
    case BinASTKind::ThrowStatement:
      MOZ_TRY_VAR(result, parseInterfaceThrowStatement(start, context));
      break;
    case BinASTKind::TryCatchStatement:
      MOZ_TRY_VAR(result, parseInterfaceTryCatchStatement(start, context));
      break;
    case BinASTKind::TryFinallyStatement:
      MOZ_TRY_VAR(result, parseInterfaceTryFinallyStatement(start, context));
      break;
    case BinASTKind::VariableDeclaration:
      MOZ_TRY_VAR(result, parseInterfaceVariableDeclaration(start, context));
      break;
    case BinASTKind::WhileStatement:
      MOZ_TRY_VAR(result, parseInterfaceWhileStatement(start, context));
      break;
    case BinASTKind::WithStatement:
      MOZ_TRY_VAR(result, parseInterfaceWithStatement(start, context));
      break;
    default:
      if (isInvalidKindPossible()) {
        return raiseInvalidKind("Statement", kind);
      } else {
        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE(
            "invalid BinASTKind should not appear");
      }
  }
  return result;
}

// ----- Interfaces (autogenerated, by lexicographical order)
// When fields have a non-trivial type, implementation is deanonymized and
// delegated to another parser.
template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceArrayAssignmentTarget(
    const size_t start, const FieldContext& context) {
  return raiseError(
      "FIXME: Not implemented yet in this preview release "
      "(ArrayAssignmentTarget)");
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceArrayBinding(
    const size_t start, const FieldOrListContext& context) {
  return raiseError(
      "FIXME: Not implemented yet in this preview release (ArrayBinding)");
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceArrayExpression(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  BINJS_MOZ_TRY_DECL(elements,
                     parseListOfOptionalExpressionOrSpreadElement(FieldContext(
                         BinASTInterfaceAndField::ArrayExpression__Elements)));

  if (elements->empty()) {
    elements->setHasNonConstInitializer();
  }
  auto result = elements;
  return result;
}

/*
 interface AssertedBlockScope : Node {
    FrozenArray<AssertedDeclaredName> declaredNames;
    bool hasDirectEval;
 }
*/
template <typename Tok>
JS::Result<Ok> BinASTParser<Tok>::parseAssertedBlockScope(
    const FieldContext& context) {
  BinASTKind kind = BinASTKind::AssertedBlockScope;
  AutoTaggedTuple guard(*tokenizer_);

  guard.init();
  MOZ_TRY(tokenizer_->enterInterface(kind, context));
  const auto start = tokenizer_->offset();
  BINJS_MOZ_TRY_DECL(result, parseInterfaceAssertedBlockScope(start, context));
  MOZ_TRY(guard.done());

  return result;
}

template <typename Tok>
JS::Result<Ok> BinASTParser<Tok>::parseInterfaceAssertedBlockScope(
    const size_t start, const FieldContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));
  const auto scopeKind = AssertedScopeKind::Block;

  MOZ_TRY(parseListOfAssertedDeclaredName(
      scopeKind,
      FieldContext(
          BinASTInterfaceAndField::AssertedBlockScope__DeclaredNames)));

  BINJS_MOZ_TRY_DECL(
      hasDirectEval,
      tokenizer_->readBool(FieldContext(
          BinASTInterfaceAndField::AssertedBlockScope__HasDirectEval)));
  if (hasDirectEval) {
    pc_->sc()->setHasDirectEval();
    pc_->sc()->setBindingsAccessedDynamically();
  }
  if (hasDirectEval && pc_->isFunctionBox() && !pc_->sc()->strict()) {
    // In non-strict mode code, direct calls to eval can
    // add variables to the call object.
    pc_->functionBox()->setFunHasExtensibleScope();
  }
  auto result = Ok();
  return result;
}

/*
 interface AssertedBoundName : Node {
    [IdentifierName] string name;
    bool isCaptured;
 }
*/
template <typename Tok>
JS::Result<Ok> BinASTParser<Tok>::parseAssertedBoundName(
    AssertedScopeKind scopeKind, const ListContext& context) {
  BinASTKind kind = BinASTKind::AssertedBoundName;
  AutoTaggedTuple guard(*tokenizer_);

  guard.init();
  MOZ_TRY(tokenizer_->enterInterface(kind, context));
  const auto start = tokenizer_->offset();
  BINJS_MOZ_TRY_DECL(
      result, parseInterfaceAssertedBoundName(start, scopeKind, context));
  MOZ_TRY(guard.done());

  return result;
}

template <typename Tok>
JS::Result<Ok> BinASTParser<Tok>::parseInterfaceAssertedBoundName(
    const size_t start, AssertedScopeKind scopeKind,
    const ListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));
  const bool allowDuplicateName = false;

  RootedAtom name(cx_);
  MOZ_TRY_VAR(name, tokenizer_->readIdentifierName(FieldContext(
                        BinASTInterfaceAndField::AssertedBoundName__Name)));

  BINJS_MOZ_TRY_DECL(
      isCaptured, tokenizer_->readBool(FieldContext(
                      BinASTInterfaceAndField::AssertedBoundName__IsCaptured)));
  ParseContext::Scope* scope;
  DeclarationKind declKind;
  MOZ_TRY(getBoundScope(scopeKind, scope, declKind));
  MOZ_TRY(addScopeName(scopeKind, name, scope, declKind, isCaptured,
                       allowDuplicateName));
  auto result = Ok();
  return result;
}

/*
 interface AssertedBoundNamesScope : Node {
    FrozenArray<AssertedBoundName> boundNames;
    bool hasDirectEval;
 }
*/
template <typename Tok>
JS::Result<Ok> BinASTParser<Tok>::parseAssertedBoundNamesScope(
    const FieldContext& context) {
  BinASTKind kind = BinASTKind::AssertedBoundNamesScope;
  AutoTaggedTuple guard(*tokenizer_);

  guard.init();
  MOZ_TRY(tokenizer_->enterInterface(kind, context));
  const auto start = tokenizer_->offset();
  BINJS_MOZ_TRY_DECL(result,
                     parseInterfaceAssertedBoundNamesScope(start, context));
  MOZ_TRY(guard.done());

  return result;
}

template <typename Tok>
JS::Result<Ok> BinASTParser<Tok>::parseInterfaceAssertedBoundNamesScope(
    const size_t start, const FieldContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));
  const auto scopeKind = AssertedScopeKind::Catch;

  MOZ_TRY(parseListOfAssertedBoundName(
      scopeKind,
      FieldContext(
          BinASTInterfaceAndField::AssertedBoundNamesScope__BoundNames)));

  BINJS_MOZ_TRY_DECL(
      hasDirectEval,
      tokenizer_->readBool(FieldContext(
          BinASTInterfaceAndField::AssertedBoundNamesScope__HasDirectEval)));
  if (hasDirectEval) {
    pc_->sc()->setHasDirectEval();
    pc_->sc()->setBindingsAccessedDynamically();
  }
  if (hasDirectEval && pc_->isFunctionBox() && !pc_->sc()->strict()) {
    // In non-strict mode code, direct calls to eval can
    // add variables to the call object.
    pc_->functionBox()->setFunHasExtensibleScope();
  }
  auto result = Ok();
  return result;
}

/*
 interface AssertedDeclaredName : Node {
    [IdentifierName] string name;
    AssertedDeclaredKind kind;
    bool isCaptured;
 }
*/
template <typename Tok>
JS::Result<Ok> BinASTParser<Tok>::parseAssertedDeclaredName(
    AssertedScopeKind scopeKind, const ListContext& context) {
  BinASTKind kind = BinASTKind::AssertedDeclaredName;
  AutoTaggedTuple guard(*tokenizer_);

  guard.init();
  MOZ_TRY(tokenizer_->enterInterface(kind, context));
  const auto start = tokenizer_->offset();
  BINJS_MOZ_TRY_DECL(
      result, parseInterfaceAssertedDeclaredName(start, scopeKind, context));
  MOZ_TRY(guard.done());

  return result;
}

template <typename Tok>
JS::Result<Ok> BinASTParser<Tok>::parseInterfaceAssertedDeclaredName(
    const size_t start, AssertedScopeKind scopeKind,
    const ListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));
  const bool allowDuplicateName = false;

  RootedAtom name(cx_);
  MOZ_TRY_VAR(name, tokenizer_->readIdentifierName(FieldContext(
                        BinASTInterfaceAndField::AssertedDeclaredName__Name)));

  BINJS_MOZ_TRY_DECL(kind_,
                     parseAssertedDeclaredKind(FieldContext(
                         BinASTInterfaceAndField::AssertedDeclaredName__Kind)));
  if (kind_ == AssertedDeclaredKind::NonConstLexical) {
    return raiseError("Let is not supported in this preview release");
  }
  if (kind_ == AssertedDeclaredKind::ConstLexical) {
    return raiseError("Const is not supported in this preview release");
  }
  BINJS_MOZ_TRY_DECL(
      isCaptured,
      tokenizer_->readBool(FieldContext(
          BinASTInterfaceAndField::AssertedDeclaredName__IsCaptured)));
  ParseContext::Scope* scope;
  DeclarationKind declKind;
  MOZ_TRY(getDeclaredScope(scopeKind, kind_, scope, declKind));
  MOZ_TRY(addScopeName(scopeKind, name, scope, declKind, isCaptured,
                       allowDuplicateName));
  auto result = Ok();
  return result;
}

/*
 interface AssertedParameterScope : Node {
    FrozenArray<AssertedMaybePositionalParameterName> paramNames;
    bool hasDirectEval;
    bool isSimpleParameterList;
 }
*/
template <typename Tok>
JS::Result<Ok> BinASTParser<Tok>::parseAssertedParameterScope(
    MutableHandle<GCVector<JSAtom*>> positionalParams,
    const FieldContext& context) {
  BinASTKind kind = BinASTKind::AssertedParameterScope;
  AutoTaggedTuple guard(*tokenizer_);

  guard.init();
  MOZ_TRY(tokenizer_->enterInterface(kind, context));
  const auto start = tokenizer_->offset();
  BINJS_MOZ_TRY_DECL(result, parseInterfaceAssertedParameterScope(
                                 start, positionalParams, context));
  MOZ_TRY(guard.done());

  return result;
}

template <typename Tok>
JS::Result<Ok> BinASTParser<Tok>::parseInterfaceAssertedParameterScope(
    const size_t start, MutableHandle<GCVector<JSAtom*>> positionalParams,
    const FieldContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));
  const auto scopeKind = AssertedScopeKind::Parameter;

  MOZ_TRY(parseListOfAssertedMaybePositionalParameterName(
      scopeKind, positionalParams,
      FieldContext(
          BinASTInterfaceAndField::AssertedParameterScope__ParamNames)));

  BINJS_MOZ_TRY_DECL(
      hasDirectEval,
      tokenizer_->readBool(FieldContext(
          BinASTInterfaceAndField::AssertedParameterScope__HasDirectEval)));
  if (hasDirectEval) {
    pc_->sc()->setHasDirectEval();
    pc_->sc()->setBindingsAccessedDynamically();
  }
  BINJS_MOZ_TRY_DECL(isSimpleParameterList,
                     tokenizer_->readBool(FieldContext(
                         BinASTInterfaceAndField::
                             AssertedParameterScope__IsSimpleParameterList)));
  (void)isSimpleParameterList;
  if (hasDirectEval && pc_->isFunctionBox() && !pc_->sc()->strict()) {
    // In non-strict mode code, direct calls to eval can
    // add variables to the call object.
    pc_->functionBox()->setFunHasExtensibleScope();
  }
  auto result = Ok();
  return result;
}

template <typename Tok>
JS::Result<Ok> BinASTParser<Tok>::parseInterfaceAssertedPositionalParameterName(
    const size_t start, AssertedScopeKind scopeKind,
    MutableHandle<GCVector<JSAtom*>> positionalParams,
    const ListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));
  bool allowDuplicateName = !pc_->sc()->strict();

  BINJS_MOZ_TRY_DECL(
      index,
      tokenizer_->readUnsignedLong(FieldContext(
          BinASTInterfaceAndField::AssertedPositionalParameterName__Index)));

  RootedAtom name(cx_);
  MOZ_TRY_VAR(
      name,
      tokenizer_->readIdentifierName(FieldContext(
          BinASTInterfaceAndField::AssertedPositionalParameterName__Name)));
  // `positionalParams` vector can be shorter than the actual
  // parameter length. Resize on demand.
  // (see also ListOfAssertedMaybePositionalParameterName)
  size_t prevLength = positionalParams.get().length();
  if (index >= prevLength) {
    // This is implementation limit, which is not in the spec.
    if (index >= ARGNO_LIMIT - 1) {
      return raiseError("AssertedPositionalParameterName.index is too big");
    }
    size_t newLength = index + 1;
    BINJS_TRY(positionalParams.get().resize(newLength));
    for (uint32_t i = prevLength; i < newLength; i++) {
      positionalParams.get()[i] = nullptr;
    }
  }

  if (positionalParams.get()[index]) {
    return raiseError(
        "AssertedPositionalParameterName has duplicate entry for the same "
        "index");
  }
  positionalParams.get()[index] = name;
  BINJS_MOZ_TRY_DECL(isCaptured,
                     tokenizer_->readBool(FieldContext(
                         BinASTInterfaceAndField::
                             AssertedPositionalParameterName__IsCaptured)));
  ParseContext::Scope* scope;
  DeclarationKind declKind;
  MOZ_TRY(getBoundScope(scopeKind, scope, declKind));
  MOZ_TRY(addScopeName(scopeKind, name, scope, declKind, isCaptured,
                       allowDuplicateName));
  auto result = Ok();
  return result;
}

/*
 interface AssertedScriptGlobalScope : Node {
    FrozenArray<AssertedDeclaredName> declaredNames;
    bool hasDirectEval;
 }
*/
template <typename Tok>
JS::Result<Ok> BinASTParser<Tok>::parseAssertedScriptGlobalScope(
    const FieldContext& context) {
  BinASTKind kind = BinASTKind::AssertedScriptGlobalScope;
  AutoTaggedTuple guard(*tokenizer_);

  guard.init();
  MOZ_TRY(tokenizer_->enterInterface(kind, context));
  const auto start = tokenizer_->offset();
  BINJS_MOZ_TRY_DECL(result,
                     parseInterfaceAssertedScriptGlobalScope(start, context));
  MOZ_TRY(guard.done());

  return result;
}

template <typename Tok>
JS::Result<Ok> BinASTParser<Tok>::parseInterfaceAssertedScriptGlobalScope(
    const size_t start, const FieldContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));
  const auto scopeKind = AssertedScopeKind::Global;

  MOZ_TRY(parseListOfAssertedDeclaredName(
      scopeKind,
      FieldContext(
          BinASTInterfaceAndField::AssertedScriptGlobalScope__DeclaredNames)));

  BINJS_MOZ_TRY_DECL(
      hasDirectEval,
      tokenizer_->readBool(FieldContext(
          BinASTInterfaceAndField::AssertedScriptGlobalScope__HasDirectEval)));
  if (hasDirectEval) {
    pc_->sc()->setHasDirectEval();
    pc_->sc()->setBindingsAccessedDynamically();
  }
  if (hasDirectEval && pc_->isFunctionBox() && !pc_->sc()->strict()) {
    // In non-strict mode code, direct calls to eval can
    // add variables to the call object.
    pc_->functionBox()->setFunHasExtensibleScope();
  }
  auto result = Ok();
  return result;
}

/*
 interface AssertedVarScope : Node {
    FrozenArray<AssertedDeclaredName> declaredNames;
    bool hasDirectEval;
 }
*/
template <typename Tok>
JS::Result<Ok> BinASTParser<Tok>::parseAssertedVarScope(
    const FieldContext& context) {
  BinASTKind kind = BinASTKind::AssertedVarScope;
  AutoTaggedTuple guard(*tokenizer_);

  guard.init();
  MOZ_TRY(tokenizer_->enterInterface(kind, context));
  const auto start = tokenizer_->offset();
  BINJS_MOZ_TRY_DECL(result, parseInterfaceAssertedVarScope(start, context));
  MOZ_TRY(guard.done());

  return result;
}

template <typename Tok>
JS::Result<Ok> BinASTParser<Tok>::parseInterfaceAssertedVarScope(
    const size_t start, const FieldContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));
  const auto scopeKind = AssertedScopeKind::Var;

  MOZ_TRY(parseListOfAssertedDeclaredName(
      scopeKind,
      FieldContext(BinASTInterfaceAndField::AssertedVarScope__DeclaredNames)));

  BINJS_MOZ_TRY_DECL(
      hasDirectEval,
      tokenizer_->readBool(FieldContext(
          BinASTInterfaceAndField::AssertedVarScope__HasDirectEval)));
  if (hasDirectEval) {
    pc_->sc()->setHasDirectEval();
    pc_->sc()->setBindingsAccessedDynamically();
  }
  if (hasDirectEval && pc_->isFunctionBox() && !pc_->sc()->strict()) {
    // In non-strict mode code, direct calls to eval can
    // add variables to the call object.
    pc_->functionBox()->setFunHasExtensibleScope();
  }
  auto result = Ok();
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceAssignmentExpression(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  BINJS_MOZ_TRY_DECL(
      binding, parseAssignmentTarget(FieldContext(
                   BinASTInterfaceAndField::AssignmentExpression__Binding)));

  BINJS_MOZ_TRY_DECL(
      expression,
      parseExpression(FieldContext(
          BinASTInterfaceAndField::AssignmentExpression__Expression)));

  BINJS_TRY_DECL(result, handler_.newAssignment(ParseNodeKind::AssignExpr,
                                                binding, expression));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*>
BinASTParser<Tok>::parseInterfaceAssignmentTargetIdentifier(
    const size_t start, const FieldContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  RootedAtom name(cx_);
  MOZ_TRY_VAR(name,
              tokenizer_->readIdentifierName(FieldContext(
                  BinASTInterfaceAndField::AssignmentTargetIdentifier__Name)));

  BINJS_TRY(usedNames_.noteUse(cx_, name, pc_->scriptId(),
                               pc_->innermostScope()->id()));
  BINJS_TRY_DECL(result, handler_.newName(name->asPropertyName(),
                                          tokenizer_->pos(start), cx_));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceAwaitExpression(
    const size_t start, const FieldOrListContext& context) {
  return raiseError(
      "FIXME: Not implemented yet in this preview release (AwaitExpression)");
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceBinaryExpression(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  BINJS_MOZ_TRY_DECL(operator_,
                     parseBinaryOperator(FieldContext(
                         BinASTInterfaceAndField::BinaryExpression__Operator)));

  BINJS_MOZ_TRY_DECL(
      left, parseExpression(
                FieldContext(BinASTInterfaceAndField::BinaryExpression__Left)));

  BINJS_MOZ_TRY_DECL(right,
                     parseExpression(FieldContext(
                         BinASTInterfaceAndField::BinaryExpression__Right)));

  ParseNodeKind pnk;
  switch (operator_) {
    case BinaryOperator::Comma:
      pnk = ParseNodeKind::CommaExpr;
      break;
    case BinaryOperator::LogicalOr:
      pnk = ParseNodeKind::OrExpr;
      break;
    case BinaryOperator::LogicalAnd:
      pnk = ParseNodeKind::AndExpr;
      break;
    case BinaryOperator::BitOr:
      pnk = ParseNodeKind::BitOrExpr;
      break;
    case BinaryOperator::BitXor:
      pnk = ParseNodeKind::BitXorExpr;
      break;
    case BinaryOperator::BitAnd:
      pnk = ParseNodeKind::BitAndExpr;
      break;
    case BinaryOperator::Eq:
      pnk = ParseNodeKind::EqExpr;
      break;
    case BinaryOperator::Neq:
      pnk = ParseNodeKind::NeExpr;
      break;
    case BinaryOperator::StrictEq:
      pnk = ParseNodeKind::StrictEqExpr;
      break;
    case BinaryOperator::StrictNeq:
      pnk = ParseNodeKind::StrictNeExpr;
      break;
    case BinaryOperator::LessThan:
      pnk = ParseNodeKind::LtExpr;
      break;
    case BinaryOperator::LeqThan:
      pnk = ParseNodeKind::LeExpr;
      break;
    case BinaryOperator::GreaterThan:
      pnk = ParseNodeKind::GtExpr;
      break;
    case BinaryOperator::GeqThan:
      pnk = ParseNodeKind::GeExpr;
      break;
    case BinaryOperator::In:
      pnk = ParseNodeKind::InExpr;
      break;
    case BinaryOperator::Instanceof:
      pnk = ParseNodeKind::InstanceOfExpr;
      break;
    case BinaryOperator::Lsh:
      pnk = ParseNodeKind::LshExpr;
      break;
    case BinaryOperator::Rsh:
      pnk = ParseNodeKind::RshExpr;
      break;
    case BinaryOperator::Ursh:
      pnk = ParseNodeKind::UrshExpr;
      break;
    case BinaryOperator::Plus:
      pnk = ParseNodeKind::AddExpr;
      break;
    case BinaryOperator::Minus:
      pnk = ParseNodeKind::SubExpr;
      break;
    case BinaryOperator::Mul:
      pnk = ParseNodeKind::MulExpr;
      break;
    case BinaryOperator::Div:
      pnk = ParseNodeKind::DivExpr;
      break;
    case BinaryOperator::Mod:
      pnk = ParseNodeKind::ModExpr;
      break;
    case BinaryOperator::Pow:
      pnk = ParseNodeKind::PowExpr;
      break;
  }

  ParseNode* result;
  // ParseNodeKind::PowExpr is not left-associative
  if (left->isKind(pnk) && pnk != ParseNodeKind::PowExpr) {
    // Regroup left-associative operations into lists.
    left->template as<ListNode>().appendWithoutOrderAssumption(right);
    result = left;
  } else {
    BINJS_TRY_DECL(list, handler_.newList(pnk, tokenizer_->pos(start)));

    list->appendWithoutOrderAssumption(left);
    list->appendWithoutOrderAssumption(right);
    result = list;
  }
  return result;
}

/*
 interface BindingIdentifier : Node {
    [IdentifierName] string name;
 }
*/
template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseBindingIdentifier(
    const FieldOrListContext& context) {
  BinASTKind kind = BinASTKind::BindingIdentifier;
  AutoTaggedTuple guard(*tokenizer_);

  guard.init();
  MOZ_TRY(tokenizer_->enterInterface(kind, context));
  const auto start = tokenizer_->offset();
  BINJS_MOZ_TRY_DECL(result, parseInterfaceBindingIdentifier(start, context));
  MOZ_TRY(guard.done());

  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceBindingIdentifier(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  RootedAtom name(cx_);
  MOZ_TRY_VAR(name, tokenizer_->readIdentifierName(FieldContext(
                        BinASTInterfaceAndField::BindingIdentifier__Name)));

  BINJS_TRY_DECL(result, handler_.newName(name->asPropertyName(),
                                          tokenizer_->pos(start), cx_));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceBindingWithInitializer(
    const size_t start, const FieldOrListContext& context) {
  return raiseError(
      "FIXME: Not implemented yet in this preview release "
      "(BindingWithInitializer)");
}

/*
 interface Block : Node {
    AssertedBlockScope scope;
    FrozenArray<Statement> statements;
 }
*/
template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseBlock(
    const FieldOrListContext& context) {
  BinASTKind kind = BinASTKind::Block;
  AutoTaggedTuple guard(*tokenizer_);

  guard.init();
  MOZ_TRY(tokenizer_->enterInterface(kind, context));
  const auto start = tokenizer_->offset();
  BINJS_MOZ_TRY_DECL(result, parseInterfaceBlock(start, context));
  MOZ_TRY(guard.done());

  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceBlock(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));
  ParseContext::Statement stmt(pc_, StatementKind::Block);
  ParseContext::Scope currentScope(cx_, pc_, usedNames_);
  BINJS_TRY(currentScope.init(pc_));

  MOZ_TRY(parseAssertedBlockScope(
      FieldContext(BinASTInterfaceAndField::Block__Scope)));

  BINJS_MOZ_TRY_DECL(statements,
                     parseListOfStatement(FieldContext(
                         BinASTInterfaceAndField::Block__Statements)));

  MOZ_TRY(checkClosedVars(currentScope));
  BINJS_TRY_DECL(bindings, NewLexicalScopeData(cx_, currentScope, alloc_, pc_));
  BINJS_TRY_DECL(result, handler_.newLexicalScope(*bindings, statements));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceBreakStatement(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));
  RootedAtom label(cx_);
  MOZ_TRY_VAR(label, tokenizer_->readMaybeAtom(FieldContext(
                         BinASTInterfaceAndField::BreakStatement__Label)));

  if (label) {
    if (!IsIdentifier(label)) {
      return raiseError("Invalid identifier");
    }
  }

  auto validity =
      pc_->checkBreakStatement(label ? label->asPropertyName() : nullptr);
  if (validity.isErr()) {
    switch (validity.unwrapErr()) {
      case ParseContext::BreakStatementError::ToughBreak:
        this->error(JSMSG_TOUGH_BREAK);
        return cx_->alreadyReportedError();
      case ParseContext::BreakStatementError::LabelNotFound:
        this->error(JSMSG_LABEL_NOT_FOUND);
        return cx_->alreadyReportedError();
    }
  }

  BINJS_TRY_DECL(result, handler_.newBreakStatement(
                             label ? label->asPropertyName() : nullptr,
                             tokenizer_->pos(start)));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceCallExpression(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  BINJS_MOZ_TRY_DECL(callee,
                     parseExpressionOrSuper(FieldContext(
                         BinASTInterfaceAndField::CallExpression__Callee)));

  BINJS_MOZ_TRY_DECL(arguments,
                     parseArguments(FieldContext(
                         BinASTInterfaceAndField::CallExpression__Arguments)));

  auto op = JSOp::Call;

  // Try to optimize funcall and funapply at the bytecode level
  if (PropertyName* prop = handler_.maybeDottedProperty(callee)) {
    if (prop == cx_->names().apply) {
      op = JSOp::FunApply;
      if (pc_->isFunctionBox()) {
        pc_->functionBox()->usesApply = true;
      }
    } else if (prop == cx_->names().call) {
      op = JSOp::FunCall;
    }
  }

  // Check for direct calls to `eval`.
  if (handler_.isEvalName(callee, cx_)) {
    if (!pc_->varScope().lookupDeclaredNameForAdd(cx_->names().eval) &&
        !pc_->innermostScope()->lookupDeclaredNameForAdd(cx_->names().eval)) {
      // This is a direct call to `eval`.
      if (!pc_->sc()->hasDirectEval()) {
        return raiseMissingDirectEvalInAssertedScope();
      }

      op = pc_->sc()->strict() ? JSOp::StrictEval : JSOp::Eval;
    }
  }

  BINJS_TRY_DECL(result, handler_.newCall(callee, arguments, op));
  return result;
}

/*
 interface CatchClause : Node {
    AssertedBoundNamesScope bindingScope;
    Binding binding;
    Block body;
 }
*/
template <typename Tok>
JS::Result<LexicalScopeNode*> BinASTParser<Tok>::parseCatchClause(
    const FieldContext& context) {
  BinASTKind kind = BinASTKind::CatchClause;
  AutoTaggedTuple guard(*tokenizer_);

  guard.init();
  MOZ_TRY(tokenizer_->enterInterface(kind, context));
  const auto start = tokenizer_->offset();
  BINJS_MOZ_TRY_DECL(result, parseInterfaceCatchClause(start, context));
  MOZ_TRY(guard.done());

  return result;
}

template <typename Tok>
JS::Result<LexicalScopeNode*> BinASTParser<Tok>::parseInterfaceCatchClause(
    const size_t start, const FieldContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));
  ParseContext::Statement stmt(pc_, StatementKind::Catch);
  ParseContext::Scope currentScope(cx_, pc_, usedNames_);
  BINJS_TRY(currentScope.init(pc_));

  MOZ_TRY(parseAssertedBoundNamesScope(
      FieldContext(BinASTInterfaceAndField::CatchClause__BindingScope)));

  BINJS_MOZ_TRY_DECL(binding,
                     parseBinding(FieldContext(
                         BinASTInterfaceAndField::CatchClause__Binding)));
  if (!currentScope.lookupDeclaredName(
          binding->template as<NameNode>().atom())) {
    return raiseError("Missing catch variable in scope");
  }
  BINJS_MOZ_TRY_DECL(body, parseBlock(FieldOrListContext(FieldContext(
                               BinASTInterfaceAndField::CatchClause__Body))));

  MOZ_TRY(checkClosedVars(currentScope));
  BINJS_TRY_DECL(bindings, NewLexicalScopeData(cx_, currentScope, alloc_, pc_));
  BINJS_TRY_DECL(result, handler_.newLexicalScope(*bindings, body));
  BINJS_TRY(handler_.setupCatchScope(result, binding, body));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceClassDeclaration(
    const size_t start, const FieldOrListContext& context) {
  return raiseError(
      "FIXME: Not implemented yet in this preview release (ClassDeclaration)");
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceClassExpression(
    const size_t start, const FieldOrListContext& context) {
  return raiseError(
      "FIXME: Not implemented yet in this preview release (ClassExpression)");
}

template <typename Tok>
JS::Result<ParseNode*>
BinASTParser<Tok>::parseInterfaceCompoundAssignmentExpression(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  BINJS_MOZ_TRY_DECL(
      operator_,
      parseCompoundAssignmentOperator(FieldContext(
          BinASTInterfaceAndField::CompoundAssignmentExpression__Operator)));

  BINJS_MOZ_TRY_DECL(
      binding,
      parseSimpleAssignmentTarget(FieldContext(
          BinASTInterfaceAndField::CompoundAssignmentExpression__Binding)));

  BINJS_MOZ_TRY_DECL(
      expression,
      parseExpression(FieldContext(
          BinASTInterfaceAndField::CompoundAssignmentExpression__Expression)));

  ParseNodeKind pnk;
  switch (operator_) {
    case CompoundAssignmentOperator::PlusAssign:
      pnk = ParseNodeKind::AddAssignExpr;
      break;
    case CompoundAssignmentOperator::MinusAssign:
      pnk = ParseNodeKind::SubAssignExpr;
      break;
    case CompoundAssignmentOperator::MulAssign:
      pnk = ParseNodeKind::MulAssignExpr;
      break;
    case CompoundAssignmentOperator::DivAssign:
      pnk = ParseNodeKind::DivAssignExpr;
      break;
    case CompoundAssignmentOperator::ModAssign:
      pnk = ParseNodeKind::ModAssignExpr;
      break;
    case CompoundAssignmentOperator::PowAssign:
      pnk = ParseNodeKind::PowAssignExpr;
      break;
    case CompoundAssignmentOperator::LshAssign:
      pnk = ParseNodeKind::LshAssignExpr;
      break;
    case CompoundAssignmentOperator::RshAssign:
      pnk = ParseNodeKind::RshAssignExpr;
      break;
    case CompoundAssignmentOperator::UrshAssign:
      pnk = ParseNodeKind::UrshAssignExpr;
      break;
    case CompoundAssignmentOperator::BitOrAssign:
      pnk = ParseNodeKind::BitOrAssignExpr;
      break;
    case CompoundAssignmentOperator::BitXorAssign:
      pnk = ParseNodeKind::BitXorAssignExpr;
      break;
    case CompoundAssignmentOperator::BitAndAssign:
      pnk = ParseNodeKind::BitAndAssignExpr;
      break;
  }
  BINJS_TRY_DECL(result, handler_.newAssignment(pnk, binding, expression));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*>
BinASTParser<Tok>::parseInterfaceComputedMemberAssignmentTarget(
    const size_t start, const FieldContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  BINJS_MOZ_TRY_DECL(
      object,
      parseExpressionOrSuper(FieldContext(
          BinASTInterfaceAndField::ComputedMemberAssignmentTarget__Object)));

  BINJS_MOZ_TRY_DECL(expression,
                     parseExpression(FieldContext(
                         BinASTInterfaceAndField::
                             ComputedMemberAssignmentTarget__Expression)));

  BINJS_TRY_DECL(result, handler_.newPropertyByValue(object, expression,
                                                     tokenizer_->offset()));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*>
BinASTParser<Tok>::parseInterfaceComputedMemberExpression(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  BINJS_MOZ_TRY_DECL(
      object, parseExpressionOrSuper(FieldContext(
                  BinASTInterfaceAndField::ComputedMemberExpression__Object)));

  BINJS_MOZ_TRY_DECL(
      expression,
      parseExpression(FieldContext(
          BinASTInterfaceAndField::ComputedMemberExpression__Expression)));

  BINJS_TRY_DECL(result, handler_.newPropertyByValue(object, expression,
                                                     tokenizer_->offset()));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceComputedPropertyName(
    const size_t start, const FieldContext& context) {
  return raiseError(
      "FIXME: Not implemented yet in this preview release "
      "(ComputedPropertyName)");
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceConditionalExpression(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  BINJS_MOZ_TRY_DECL(
      test, parseExpression(FieldContext(
                BinASTInterfaceAndField::ConditionalExpression__Test)));

  BINJS_MOZ_TRY_DECL(
      consequent,
      parseExpression(FieldContext(
          BinASTInterfaceAndField::ConditionalExpression__Consequent)));

  BINJS_MOZ_TRY_DECL(
      alternate,
      parseExpression(FieldContext(
          BinASTInterfaceAndField::ConditionalExpression__Alternate)));

  BINJS_TRY_DECL(result, handler_.newConditional(test, consequent, alternate));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceContinueStatement(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));
  RootedAtom label(cx_);
  MOZ_TRY_VAR(label, tokenizer_->readMaybeAtom(FieldContext(
                         BinASTInterfaceAndField::ContinueStatement__Label)));

  if (label) {
    if (!IsIdentifier(label)) {
      return raiseError("ContinueStatement - Label MUST be an identifier");
    }
  }

  auto validity =
      pc_->checkContinueStatement(label ? label->asPropertyName() : nullptr);
  if (validity.isErr()) {
    switch (validity.unwrapErr()) {
      case ParseContext::ContinueStatementError::NotInALoop:
        this->error(JSMSG_BAD_CONTINUE);
        return cx_->alreadyReportedError();
      case ParseContext::ContinueStatementError::LabelNotFound:
        this->error(JSMSG_LABEL_NOT_FOUND);
        return cx_->alreadyReportedError();
    }
  }

  BINJS_TRY_DECL(result, handler_.newContinueStatement(
                             label ? label->asPropertyName() : nullptr,
                             tokenizer_->pos(start)));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceDataProperty(
    const size_t start, const ListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  BINJS_MOZ_TRY_DECL(name, parsePropertyName(FieldContext(
                               BinASTInterfaceAndField::DataProperty__Name)));

  BINJS_MOZ_TRY_DECL(expression,
                     parseExpression(FieldContext(
                         BinASTInterfaceAndField::DataProperty__Expression)));

  if (!handler_.isUsableAsObjectPropertyName(name)) {
    return raiseError("DataProperty key kind");
  }

  ParseNode* result;
  if (name->template is<NameNode>() &&
      name->template as<NameNode>().atom() == cx_->names().proto) {
    BINJS_TRY_VAR(result, handler_.newUnary(ParseNodeKind::MutateProto, start,
                                            expression));
  } else {
    BINJS_TRY_VAR(result, handler_.newObjectMethodOrPropertyDefinition(
                              name, expression, AccessorType::None));
  }
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceDebuggerStatement(
    const size_t start, const FieldOrListContext& context) {
  return raiseError(
      "FIXME: Not implemented yet in this preview release (DebuggerStatement)");
}

/*
 interface Directive : Node {
    string rawValue;
 }
*/
template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseDirective(
    const ListContext& context) {
  BinASTKind kind = BinASTKind::Directive;
  AutoTaggedTuple guard(*tokenizer_);

  guard.init();
  MOZ_TRY(tokenizer_->enterInterface(kind, context));
  const auto start = tokenizer_->offset();
  BINJS_MOZ_TRY_DECL(result, parseInterfaceDirective(start, context));
  MOZ_TRY(guard.done());

  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceDirective(
    const size_t start, const ListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  RootedAtom rawValue(cx_);
  MOZ_TRY_VAR(rawValue, tokenizer_->readAtom(FieldContext(
                            BinASTInterfaceAndField::Directive__RawValue)));

  TokenPos pos = tokenizer_->pos(start);
  BINJS_TRY_DECL(result, handler_.newStringLiteral(rawValue, pos));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceDoWhileStatement(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));
  ParseContext::Statement stmt(pc_, StatementKind::DoLoop);

  BINJS_MOZ_TRY_DECL(
      test, parseExpression(
                FieldContext(BinASTInterfaceAndField::DoWhileStatement__Test)));

  BINJS_MOZ_TRY_DECL(body,
                     parseStatement(FieldOrListContext(FieldContext(
                         BinASTInterfaceAndField::DoWhileStatement__Body))));

  BINJS_TRY_DECL(
      result, handler_.newDoWhileStatement(body, test, tokenizer_->pos(start)));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*>
BinASTParser<Tok>::parseInterfaceEagerArrowExpressionWithExpression(
    const size_t start, const FieldOrListContext& context) {
  return raiseError(
      "FIXME: Not implemented yet in this preview release "
      "(EagerArrowExpressionWithExpression)");
}

template <typename Tok>
JS::Result<ParseNode*>
BinASTParser<Tok>::parseInterfaceEagerArrowExpressionWithFunctionBody(
    const size_t start, const FieldOrListContext& context) {
  return raiseError(
      "FIXME: Not implemented yet in this preview release "
      "(EagerArrowExpressionWithFunctionBody)");
}

template <typename Tok>
JS::Result<ParseNode*>
BinASTParser<Tok>::parseInterfaceEagerFunctionDeclaration(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));
  const auto syntax = FunctionSyntaxKind::Statement;

  BINJS_MOZ_TRY_DECL(
      isAsync,
      tokenizer_->readBool(FieldContext(
          BinASTInterfaceAndField::EagerFunctionDeclaration__IsAsync)));
  if (isAsync) {
    return raiseError(
        "Async function is not supported in this preview release");
  }
  BINJS_MOZ_TRY_DECL(
      isGenerator,
      tokenizer_->readBool(FieldContext(
          BinASTInterfaceAndField::EagerFunctionDeclaration__IsGenerator)));
  if (isGenerator) {
    return raiseError("Generator is not supported in this preview release");
  }
  BINJS_MOZ_TRY_DECL(
      name, parseBindingIdentifier(FieldOrListContext(FieldContext(
                BinASTInterfaceAndField::EagerFunctionDeclaration__Name))));

  BINJS_MOZ_TRY_DECL(
      length, tokenizer_->readUnsignedLong(FieldContext(
                  BinASTInterfaceAndField::EagerFunctionDeclaration__Length)));

  BINJS_MOZ_TRY_DECL(
      directives,
      parseListOfDirective(FieldContext(
          BinASTInterfaceAndField::EagerFunctionDeclaration__Directives)));

  BINJS_MOZ_TRY_DECL(funbox,
                     buildFunctionBox(isGenerator ? GeneratorKind::Generator
                                                  : GeneratorKind::NotGenerator,
                                      isAsync ? FunctionAsyncKind::AsyncFunction
                                              : FunctionAsyncKind::SyncFunction,
                                      syntax,
                                      (syntax != FunctionSyntaxKind::Setter &&
                                       syntax != FunctionSyntaxKind::Getter)
                                          ? name
                                          : nullptr));

  forceStrictIfNecessary(funbox, directives);

  pc_->sc()->setHasInnerFunctions();

  // Push a new ParseContext. It will be used to parse `scope`, the arguments,
  // the function.
  BinASTParseContext funpc(cx_, this, funbox, /* newDirectives = */ nullptr);
  BINJS_TRY(funpc.init());
  pc_->functionScope().useAsVarScope(pc_);
  MOZ_ASSERT(pc_->isFunctionBox());

  ParseContext::Scope lexicalScope(cx_, pc_, usedNames_);
  BINJS_TRY(lexicalScope.init(pc_));
  ListNode* params;
  ListNode* body;
  MOZ_TRY(parseFunctionOrMethodContents(
      length, &params, &body,
      FieldOrRootContext(FieldContext(
          BinASTInterfaceAndField::EagerFunctionDeclaration__Contents))));
  MOZ_TRY(prependDirectivesToBody(body, directives));
  uint32_t nargs = params->count();

  BINJS_TRY_DECL(lexicalScopeData,
                 NewLexicalScopeData(cx_, lexicalScope, alloc_, pc_));
  BINJS_TRY_DECL(bodyScope, handler_.newLexicalScope(*lexicalScopeData, body));
  BINJS_MOZ_TRY_DECL(result, makeEmptyFunctionNode(start, syntax, funbox));
  MOZ_TRY(setFunctionParametersAndBody(result, params, bodyScope));
  MOZ_TRY(finishEagerFunction(funbox, nargs));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceEagerFunctionExpression(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));
  const auto syntax = FunctionSyntaxKind::Expression;

  BINJS_MOZ_TRY_DECL(
      isAsync, tokenizer_->readBool(FieldContext(
                   BinASTInterfaceAndField::EagerFunctionExpression__IsAsync)));
  if (isAsync) {
    return raiseError(
        "Async function is not supported in this preview release");
  }
  BINJS_MOZ_TRY_DECL(
      isGenerator,
      tokenizer_->readBool(FieldContext(
          BinASTInterfaceAndField::EagerFunctionExpression__IsGenerator)));
  if (isGenerator) {
    return raiseError("Generator is not supported in this preview release");
  }
  BINJS_MOZ_TRY_DECL(
      name, parseOptionalBindingIdentifier(FieldContext(
                BinASTInterfaceAndField::EagerFunctionExpression__Name)));

  BINJS_MOZ_TRY_DECL(
      length, tokenizer_->readUnsignedLong(FieldContext(
                  BinASTInterfaceAndField::EagerFunctionExpression__Length)));

  BINJS_MOZ_TRY_DECL(
      directives,
      parseListOfDirective(FieldContext(
          BinASTInterfaceAndField::EagerFunctionExpression__Directives)));

  BINJS_MOZ_TRY_DECL(funbox,
                     buildFunctionBox(isGenerator ? GeneratorKind::Generator
                                                  : GeneratorKind::NotGenerator,
                                      isAsync ? FunctionAsyncKind::AsyncFunction
                                              : FunctionAsyncKind::SyncFunction,
                                      syntax,
                                      (syntax != FunctionSyntaxKind::Setter &&
                                       syntax != FunctionSyntaxKind::Getter)
                                          ? name
                                          : nullptr));

  forceStrictIfNecessary(funbox, directives);

  pc_->sc()->setHasInnerFunctions();

  // Push a new ParseContext. It will be used to parse `scope`, the arguments,
  // the function.
  BinASTParseContext funpc(cx_, this, funbox, /* newDirectives = */ nullptr);
  BINJS_TRY(funpc.init());
  pc_->functionScope().useAsVarScope(pc_);
  MOZ_ASSERT(pc_->isFunctionBox());

  ParseContext::Scope lexicalScope(cx_, pc_, usedNames_);
  BINJS_TRY(lexicalScope.init(pc_));
  ListNode* params;
  ListNode* body;
  MOZ_TRY(parseFunctionExpressionContents(
      length, &params, &body,
      FieldOrRootContext(FieldContext(
          BinASTInterfaceAndField::EagerFunctionExpression__Contents))));
  MOZ_TRY(prependDirectivesToBody(body, directives));
  uint32_t nargs = params->count();

  BINJS_TRY_DECL(lexicalScopeData,
                 NewLexicalScopeData(cx_, lexicalScope, alloc_, pc_));
  BINJS_TRY_DECL(bodyScope, handler_.newLexicalScope(*lexicalScopeData, body));
  BINJS_MOZ_TRY_DECL(result, makeEmptyFunctionNode(start, syntax, funbox));
  MOZ_TRY(setFunctionParametersAndBody(result, params, bodyScope));
  MOZ_TRY(finishEagerFunction(funbox, nargs));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceEagerGetter(
    const size_t start, const ListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));
  const auto syntax = FunctionSyntaxKind::Setter;
  const bool isGenerator = false;
  const bool isAsync = false;
  const auto accessorType = AccessorType::Getter;
  const uint32_t length = 0;

  BINJS_MOZ_TRY_DECL(name, parsePropertyName(FieldContext(
                               BinASTInterfaceAndField::EagerGetter__Name)));

  BINJS_MOZ_TRY_DECL(directives,
                     parseListOfDirective(FieldContext(
                         BinASTInterfaceAndField::EagerGetter__Directives)));

  BINJS_MOZ_TRY_DECL(funbox,
                     buildFunctionBox(isGenerator ? GeneratorKind::Generator
                                                  : GeneratorKind::NotGenerator,
                                      isAsync ? FunctionAsyncKind::AsyncFunction
                                              : FunctionAsyncKind::SyncFunction,
                                      syntax,
                                      (syntax != FunctionSyntaxKind::Setter &&
                                       syntax != FunctionSyntaxKind::Getter)
                                          ? name
                                          : nullptr));

  forceStrictIfNecessary(funbox, directives);

  pc_->sc()->setHasInnerFunctions();

  // Push a new ParseContext. It will be used to parse `scope`, the arguments,
  // the function.
  BinASTParseContext funpc(cx_, this, funbox, /* newDirectives = */ nullptr);
  BINJS_TRY(funpc.init());
  pc_->functionScope().useAsVarScope(pc_);
  MOZ_ASSERT(pc_->isFunctionBox());

  ParseContext::Scope lexicalScope(cx_, pc_, usedNames_);
  BINJS_TRY(lexicalScope.init(pc_));
  ListNode* params;
  ListNode* body;
  MOZ_TRY(parseGetterContents(
      length, &params, &body,
      FieldContext(BinASTInterfaceAndField::EagerGetter__Contents)));
  MOZ_TRY(prependDirectivesToBody(body, directives));
  uint32_t nargs = params->count();

  BINJS_TRY_DECL(lexicalScopeData,
                 NewLexicalScopeData(cx_, lexicalScope, alloc_, pc_));
  BINJS_TRY_DECL(bodyScope, handler_.newLexicalScope(*lexicalScopeData, body));
  BINJS_MOZ_TRY_DECL(method, makeEmptyFunctionNode(start, syntax, funbox));
  MOZ_TRY(setFunctionParametersAndBody(method, params, bodyScope));
  BINJS_TRY_DECL(result, handler_.newObjectMethodOrPropertyDefinition(
                             name, method, accessorType));
  MOZ_TRY(finishEagerFunction(funbox, nargs));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceEagerMethod(
    const size_t start, const ListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));
  const auto syntax = FunctionSyntaxKind::Method;
  const auto accessorType = AccessorType::None;

  BINJS_MOZ_TRY_DECL(isAsync,
                     tokenizer_->readBool(FieldContext(
                         BinASTInterfaceAndField::EagerMethod__IsAsync)));
  if (isAsync) {
    return raiseError(
        "Async function is not supported in this preview release");
  }
  BINJS_MOZ_TRY_DECL(isGenerator,
                     tokenizer_->readBool(FieldContext(
                         BinASTInterfaceAndField::EagerMethod__IsGenerator)));
  if (isGenerator) {
    return raiseError("Generator is not supported in this preview release");
  }
  BINJS_MOZ_TRY_DECL(name, parsePropertyName(FieldContext(
                               BinASTInterfaceAndField::EagerMethod__Name)));

  BINJS_MOZ_TRY_DECL(
      length, tokenizer_->readUnsignedLong(
                  FieldContext(BinASTInterfaceAndField::EagerMethod__Length)));

  BINJS_MOZ_TRY_DECL(directives,
                     parseListOfDirective(FieldContext(
                         BinASTInterfaceAndField::EagerMethod__Directives)));

  BINJS_MOZ_TRY_DECL(funbox,
                     buildFunctionBox(isGenerator ? GeneratorKind::Generator
                                                  : GeneratorKind::NotGenerator,
                                      isAsync ? FunctionAsyncKind::AsyncFunction
                                              : FunctionAsyncKind::SyncFunction,
                                      syntax,
                                      (syntax != FunctionSyntaxKind::Setter &&
                                       syntax != FunctionSyntaxKind::Getter)
                                          ? name
                                          : nullptr));

  forceStrictIfNecessary(funbox, directives);

  pc_->sc()->setHasInnerFunctions();

  // Push a new ParseContext. It will be used to parse `scope`, the arguments,
  // the function.
  BinASTParseContext funpc(cx_, this, funbox, /* newDirectives = */ nullptr);
  BINJS_TRY(funpc.init());
  pc_->functionScope().useAsVarScope(pc_);
  MOZ_ASSERT(pc_->isFunctionBox());

  ParseContext::Scope lexicalScope(cx_, pc_, usedNames_);
  BINJS_TRY(lexicalScope.init(pc_));
  ListNode* params;
  ListNode* body;
  MOZ_TRY(parseFunctionOrMethodContents(
      length, &params, &body,
      FieldOrRootContext(
          FieldContext(BinASTInterfaceAndField::EagerMethod__Contents))));
  MOZ_TRY(prependDirectivesToBody(body, directives));
  uint32_t nargs = params->count();

  BINJS_TRY_DECL(lexicalScopeData,
                 NewLexicalScopeData(cx_, lexicalScope, alloc_, pc_));
  BINJS_TRY_DECL(bodyScope, handler_.newLexicalScope(*lexicalScopeData, body));
  BINJS_MOZ_TRY_DECL(method, makeEmptyFunctionNode(start, syntax, funbox));
  MOZ_TRY(setFunctionParametersAndBody(method, params, bodyScope));
  BINJS_TRY_DECL(result, handler_.newObjectMethodOrPropertyDefinition(
                             name, method, accessorType));
  MOZ_TRY(finishEagerFunction(funbox, nargs));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceEagerSetter(
    const size_t start, const ListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));
  const auto syntax = FunctionSyntaxKind::Setter;
  const bool isGenerator = false;
  const bool isAsync = false;
  const auto accessorType = AccessorType::Setter;

  BINJS_MOZ_TRY_DECL(name, parsePropertyName(FieldContext(
                               BinASTInterfaceAndField::EagerSetter__Name)));

  BINJS_MOZ_TRY_DECL(
      length, tokenizer_->readUnsignedLong(
                  FieldContext(BinASTInterfaceAndField::EagerSetter__Length)));

  BINJS_MOZ_TRY_DECL(directives,
                     parseListOfDirective(FieldContext(
                         BinASTInterfaceAndField::EagerSetter__Directives)));

  BINJS_MOZ_TRY_DECL(funbox,
                     buildFunctionBox(isGenerator ? GeneratorKind::Generator
                                                  : GeneratorKind::NotGenerator,
                                      isAsync ? FunctionAsyncKind::AsyncFunction
                                              : FunctionAsyncKind::SyncFunction,
                                      syntax,
                                      (syntax != FunctionSyntaxKind::Setter &&
                                       syntax != FunctionSyntaxKind::Getter)
                                          ? name
                                          : nullptr));

  forceStrictIfNecessary(funbox, directives);

  pc_->sc()->setHasInnerFunctions();

  // Push a new ParseContext. It will be used to parse `scope`, the arguments,
  // the function.
  BinASTParseContext funpc(cx_, this, funbox, /* newDirectives = */ nullptr);
  BINJS_TRY(funpc.init());
  pc_->functionScope().useAsVarScope(pc_);
  MOZ_ASSERT(pc_->isFunctionBox());

  ParseContext::Scope lexicalScope(cx_, pc_, usedNames_);
  BINJS_TRY(lexicalScope.init(pc_));
  ListNode* params;
  ListNode* body;
  MOZ_TRY(parseSetterContents(
      length, &params, &body,
      FieldContext(BinASTInterfaceAndField::EagerSetter__Contents)));
  MOZ_TRY(prependDirectivesToBody(body, directives));
  uint32_t nargs = params->count();

  BINJS_TRY_DECL(lexicalScopeData,
                 NewLexicalScopeData(cx_, lexicalScope, alloc_, pc_));
  BINJS_TRY_DECL(bodyScope, handler_.newLexicalScope(*lexicalScopeData, body));
  BINJS_MOZ_TRY_DECL(method, makeEmptyFunctionNode(start, syntax, funbox));
  MOZ_TRY(setFunctionParametersAndBody(method, params, bodyScope));
  BINJS_TRY_DECL(result, handler_.newObjectMethodOrPropertyDefinition(
                             name, method, accessorType));
  MOZ_TRY(finishEagerFunction(funbox, nargs));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceEmptyStatement(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  BINJS_TRY_DECL(result, handler_.newEmptyStatement(tokenizer_->pos(start)));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceExpressionStatement(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  BINJS_MOZ_TRY_DECL(
      expression,
      parseExpression(FieldContext(
          BinASTInterfaceAndField::ExpressionStatement__Expression)));

  BINJS_TRY_DECL(result, handler_.newExprStatement(expression));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceForInOfBinding(
    const size_t start, const FieldContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));
  AutoVariableDeclarationKind kindGuard(this);

  BINJS_MOZ_TRY_DECL(
      kind_, parseVariableDeclarationKind(
                 FieldContext(BinASTInterfaceAndField::ForInOfBinding__Kind)));

  BINJS_MOZ_TRY_DECL(binding,
                     parseBinding(FieldContext(
                         BinASTInterfaceAndField::ForInOfBinding__Binding)));

  // Restored by `kindGuard`.
  variableDeclarationKind_ = kind_;
  MOZ_TRY(
      checkBinding(binding->template as<NameNode>().atom()->asPropertyName()));
  ParseNodeKind pnk;
  switch (kind_) {
    case VariableDeclarationKind::Var:
      pnk = ParseNodeKind::VarStmt;
      break;
    case VariableDeclarationKind::Let:
      return raiseError("Let is not supported in this preview release");
    case VariableDeclarationKind::Const:
      return raiseError("Const is not supported in this preview release");
  }
  BINJS_TRY_DECL(result,
                 handler_.newDeclarationList(pnk, tokenizer_->pos(start)));
  handler_.addList(result, binding);
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceForInStatement(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));
  ParseContext::Statement stmt(pc_, StatementKind::ForInLoop);

  // Implicit scope around the `for`, used to store `for (let x in  ...)`
  // or `for (const x in ...)`-style declarations. Detail on the
  // declaration is stored as part of `scope`.
  ParseContext::Scope scope(cx_, pc_, usedNames_);
  BINJS_TRY(scope.init(pc_));

  BINJS_MOZ_TRY_DECL(left, parseAssignmentTargetOrForInOfBinding(FieldContext(
                               BinASTInterfaceAndField::ForInStatement__Left)));

  BINJS_MOZ_TRY_DECL(
      right, parseExpression(
                 FieldContext(BinASTInterfaceAndField::ForInStatement__Right)));

  BINJS_MOZ_TRY_DECL(
      body, parseStatement(FieldOrListContext(
                FieldContext(BinASTInterfaceAndField::ForInStatement__Body))));

  BINJS_TRY_DECL(forHead,
                 handler_.newForInOrOfHead(ParseNodeKind::ForIn, left, right,
                                           tokenizer_->pos(start)));
  ParseNode* result;
  BINJS_TRY_VAR(result, handler_.newForStatement(start, forHead, body,
                                                 /* iflags = */ 0));

  if (!scope.isEmpty()) {
    BINJS_TRY_DECL(bindings, NewLexicalScopeData(cx_, scope, alloc_, pc_));
    BINJS_TRY_VAR(result, handler_.newLexicalScope(*bindings, result));
  }
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceForOfStatement(
    const size_t start, const FieldOrListContext& context) {
  return raiseError(
      "FIXME: Not implemented yet in this preview release (ForOfStatement)");
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceForStatement(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));
  ParseContext::Statement stmt(pc_, StatementKind::ForLoop);

  // Implicit scope around the `for`, used to store `for (let x; ...; ...)`
  // or `for (const x; ...; ...)`-style declarations. Detail on the
  // declaration is stored as part of `BINJS_Scope`.
  ParseContext::Scope scope(cx_, pc_, usedNames_);
  BINJS_TRY(scope.init(pc_));

  BINJS_MOZ_TRY_DECL(
      init, parseOptionalExpressionOrVariableDeclaration(
                FieldContext(BinASTInterfaceAndField::ForStatement__Init)));

  BINJS_MOZ_TRY_DECL(test, parseOptionalExpression(FieldContext(
                               BinASTInterfaceAndField::ForStatement__Test)));

  BINJS_MOZ_TRY_DECL(
      update, parseOptionalExpression(
                  FieldContext(BinASTInterfaceAndField::ForStatement__Update)));

  BINJS_MOZ_TRY_DECL(body, parseStatement(FieldOrListContext(FieldContext(
                               BinASTInterfaceAndField::ForStatement__Body))));

  BINJS_TRY_DECL(
      forHead, handler_.newForHead(init, test, update, tokenizer_->pos(start)));
  ParseNode* result;
  BINJS_TRY_VAR(result, handler_.newForStatement(start, forHead, body,
                                                 /* iflags = */ 0));

  if (!scope.isEmpty()) {
    BINJS_TRY_DECL(bindings, NewLexicalScopeData(cx_, scope, alloc_, pc_));
    BINJS_TRY_VAR(result, handler_.newLexicalScope(*bindings, result));
  }
  return result;
}

/*
 interface FormalParameters : Node {
    FrozenArray<Parameter> items;
    Binding? rest;
 }
*/
template <typename Tok>
JS::Result<ListNode*> BinASTParser<Tok>::parseFormalParameters(
    const FieldContext& context) {
  BinASTKind kind = BinASTKind::FormalParameters;
  AutoTaggedTuple guard(*tokenizer_);

  guard.init();
  MOZ_TRY(tokenizer_->enterInterface(kind, context));
  const auto start = tokenizer_->offset();
  BINJS_MOZ_TRY_DECL(result, parseInterfaceFormalParameters(start, context));
  MOZ_TRY(guard.done());

  return result;
}

template <typename Tok>
JS::Result<ListNode*> BinASTParser<Tok>::parseInterfaceFormalParameters(
    const size_t start, const FieldContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  BINJS_MOZ_TRY_DECL(items,
                     parseListOfParameter(FieldContext(
                         BinASTInterfaceAndField::FormalParameters__Items)));

  BINJS_MOZ_TRY_DECL(
      rest, parseOptionalBinding(
                FieldContext(BinASTInterfaceAndField::FormalParameters__Rest)));

  auto result = items;
  if (rest) {
    return raiseError(
        "Rest parameter is not supported in this preview release");
  }
  return result;
}

/*
 interface FunctionExpressionContents : Node {
    bool isFunctionNameCaptured;
    bool isThisCaptured;
    AssertedParameterScope parameterScope;
    FormalParameters params;
    AssertedVarScope bodyScope;
    FunctionBody body;
 }
*/
template <typename Tok>
JS::Result<Ok> BinASTParser<Tok>::parseFunctionExpressionContents(
    uint32_t funLength, ListNode** paramsOut, ListNode** bodyOut,
    const FieldOrRootContext& context) {
  BinASTKind kind = BinASTKind::FunctionExpressionContents;
  AutoTaggedTuple guard(*tokenizer_);

  guard.init();
  MOZ_TRY(tokenizer_->enterInterface(kind, context));
  const auto start = tokenizer_->offset();
  BINJS_MOZ_TRY_DECL(result,
                     parseInterfaceFunctionExpressionContents(
                         start, funLength, paramsOut, bodyOut, context));
  MOZ_TRY(guard.done());

  return result;
}

template <typename Tok>
JS::Result<Ok> BinASTParser<Tok>::parseInterfaceFunctionExpressionContents(
    const size_t start, uint32_t funLength, ListNode** paramsOut,
    ListNode** bodyOut, const FieldOrRootContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  BINJS_MOZ_TRY_DECL(
      isFunctionNameCaptured,
      tokenizer_->readBool(FieldContext(
          BinASTInterfaceAndField::
              FunctionExpressionContents__IsFunctionNameCaptured)));
  // Per spec, isFunctionNameCaptured can be true for anonymous
  // function.  Check isFunctionNameCaptured only for named
  // function.
  if (pc_->functionBox()->isNamedLambda() && isFunctionNameCaptured) {
    captureFunctionName();
  }
  BINJS_MOZ_TRY_DECL(isThisCaptured,
                     tokenizer_->readBool(FieldContext(
                         BinASTInterfaceAndField::
                             FunctionExpressionContents__IsThisCaptured)));
  // TODO: Use this in BinASTParser::buildFunction.
  (void)isThisCaptured;
  Rooted<GCVector<JSAtom*>> positionalParams(cx_, GCVector<JSAtom*>(cx_));
  MOZ_TRY(parseAssertedParameterScope(
      &positionalParams,
      FieldContext(BinASTInterfaceAndField::
                       FunctionExpressionContents__ParameterScope)));

  BINJS_MOZ_TRY_DECL(
      params,
      parseFormalParameters(FieldContext(
          BinASTInterfaceAndField::FunctionExpressionContents__Params)));
  MOZ_TRY(checkFunctionLength(funLength));
  MOZ_TRY(checkPositionalParameterIndices(positionalParams, params));
  MOZ_TRY(parseAssertedVarScope(FieldContext(
      BinASTInterfaceAndField::FunctionExpressionContents__BodyScope)));

  BINJS_MOZ_TRY_DECL(
      body, parseFunctionBody(FieldContext(
                BinASTInterfaceAndField::FunctionExpressionContents__Body)));

  *paramsOut = params;
  *bodyOut = body;
  auto result = Ok();
  return result;
}

/*
 interface FunctionOrMethodContents : Node {
    bool isThisCaptured;
    AssertedParameterScope parameterScope;
    FormalParameters params;
    AssertedVarScope bodyScope;
    FunctionBody body;
 }
*/
template <typename Tok>
JS::Result<Ok> BinASTParser<Tok>::parseFunctionOrMethodContents(
    uint32_t funLength, ListNode** paramsOut, ListNode** bodyOut,
    const FieldOrRootContext& context) {
  BinASTKind kind = BinASTKind::FunctionOrMethodContents;
  AutoTaggedTuple guard(*tokenizer_);

  guard.init();
  MOZ_TRY(tokenizer_->enterInterface(kind, context));
  const auto start = tokenizer_->offset();
  BINJS_MOZ_TRY_DECL(result,
                     parseInterfaceFunctionOrMethodContents(
                         start, funLength, paramsOut, bodyOut, context));
  MOZ_TRY(guard.done());

  return result;
}

template <typename Tok>
JS::Result<Ok> BinASTParser<Tok>::parseInterfaceFunctionOrMethodContents(
    const size_t start, uint32_t funLength, ListNode** paramsOut,
    ListNode** bodyOut, const FieldOrRootContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  BINJS_MOZ_TRY_DECL(
      isThisCaptured,
      tokenizer_->readBool(FieldContext(
          BinASTInterfaceAndField::FunctionOrMethodContents__IsThisCaptured)));
  // TODO: Use this in BinASTParser::buildFunction.
  (void)isThisCaptured;
  Rooted<GCVector<JSAtom*>> positionalParams(cx_, GCVector<JSAtom*>(cx_));
  MOZ_TRY(parseAssertedParameterScope(
      &positionalParams,
      FieldContext(
          BinASTInterfaceAndField::FunctionOrMethodContents__ParameterScope)));

  BINJS_MOZ_TRY_DECL(
      params, parseFormalParameters(FieldContext(
                  BinASTInterfaceAndField::FunctionOrMethodContents__Params)));
  MOZ_TRY(checkFunctionLength(funLength));
  MOZ_TRY(checkPositionalParameterIndices(positionalParams, params));
  MOZ_TRY(parseAssertedVarScope(FieldContext(
      BinASTInterfaceAndField::FunctionOrMethodContents__BodyScope)));

  BINJS_MOZ_TRY_DECL(
      body, parseFunctionBody(FieldContext(
                BinASTInterfaceAndField::FunctionOrMethodContents__Body)));

  *paramsOut = params;
  *bodyOut = body;
  auto result = Ok();
  return result;
}

/*
 interface GetterContents : Node {
    bool isThisCaptured;
    AssertedVarScope bodyScope;
    FunctionBody body;
 }
*/
template <typename Tok>
JS::Result<Ok> BinASTParser<Tok>::parseGetterContents(
    uint32_t funLength, ListNode** paramsOut, ListNode** bodyOut,
    const FieldContext& context) {
  BinASTKind kind = BinASTKind::GetterContents;
  AutoTaggedTuple guard(*tokenizer_);

  guard.init();
  MOZ_TRY(tokenizer_->enterInterface(kind, context));
  const auto start = tokenizer_->offset();
  BINJS_MOZ_TRY_DECL(
      result, parseInterfaceGetterContents(start, funLength, paramsOut, bodyOut,
                                           context));
  MOZ_TRY(guard.done());

  return result;
}

template <typename Tok>
JS::Result<Ok> BinASTParser<Tok>::parseInterfaceGetterContents(
    const size_t start, uint32_t funLength, ListNode** paramsOut,
    ListNode** bodyOut, const FieldContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  BINJS_MOZ_TRY_DECL(
      isThisCaptured,
      tokenizer_->readBool(FieldContext(
          BinASTInterfaceAndField::GetterContents__IsThisCaptured)));
  // TODO: Use this in BinASTParser::buildFunction.
  (void)isThisCaptured;
  MOZ_TRY(parseAssertedVarScope(
      FieldContext(BinASTInterfaceAndField::GetterContents__BodyScope)));

  BINJS_TRY_DECL(params, handler_.newParamsBody(tokenizer_->pos(start)));
  BINJS_MOZ_TRY_DECL(body, parseFunctionBody(FieldContext(
                               BinASTInterfaceAndField::GetterContents__Body)));

  *paramsOut = params;
  *bodyOut = body;
  auto result = Ok();
  return result;
}

/*
 interface IdentifierExpression : Node {
    [IdentifierName] string name;
 }
*/
template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseIdentifierExpression(
    const FieldOrListContext& context) {
  BinASTKind kind = BinASTKind::IdentifierExpression;
  AutoTaggedTuple guard(*tokenizer_);

  guard.init();
  MOZ_TRY(tokenizer_->enterInterface(kind, context));
  const auto start = tokenizer_->offset();
  BINJS_MOZ_TRY_DECL(result,
                     parseInterfaceIdentifierExpression(start, context));
  MOZ_TRY(guard.done());

  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceIdentifierExpression(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  RootedAtom name(cx_);
  MOZ_TRY_VAR(name, tokenizer_->readIdentifierName(FieldContext(
                        BinASTInterfaceAndField::IdentifierExpression__Name)));

  BINJS_TRY(usedNames_.noteUse(cx_, name, pc_->scriptId(),
                               pc_->innermostScope()->id()));
  BINJS_TRY_DECL(result, handler_.newName(name->asPropertyName(),
                                          tokenizer_->pos(start), cx_));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceIfStatement(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  BINJS_MOZ_TRY_DECL(test, parseExpression(FieldContext(
                               BinASTInterfaceAndField::IfStatement__Test)));

  BINJS_MOZ_TRY_DECL(consequent,
                     parseStatement(FieldOrListContext(FieldContext(
                         BinASTInterfaceAndField::IfStatement__Consequent))));

  BINJS_MOZ_TRY_DECL(alternate,
                     parseOptionalStatement(FieldContext(
                         BinASTInterfaceAndField::IfStatement__Alternate)));

  BINJS_TRY_DECL(result,
                 handler_.newIfStatement(start, test, consequent, alternate));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceLabelledStatement(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  RootedAtom label(cx_);
  MOZ_TRY_VAR(label, tokenizer_->readAtom(FieldContext(
                         BinASTInterfaceAndField::LabelledStatement__Label)));
  if (!IsIdentifier(label)) {
    return raiseError("Invalid identifier");
  }
  ParseContext::LabelStatement stmt(pc_, label);
  BINJS_MOZ_TRY_DECL(body,
                     parseStatement(FieldOrListContext(FieldContext(
                         BinASTInterfaceAndField::LabelledStatement__Body))));

  BINJS_TRY_DECL(result, handler_.newLabeledStatement(label->asPropertyName(),
                                                      body, start));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*>
BinASTParser<Tok>::parseInterfaceLazyArrowExpressionWithExpression(
    const size_t start, const FieldOrListContext& context) {
  return raiseError(
      "FIXME: Not implemented yet in this preview release "
      "(LazyArrowExpressionWithExpression)");
}

template <typename Tok>
JS::Result<ParseNode*>
BinASTParser<Tok>::parseInterfaceLazyArrowExpressionWithFunctionBody(
    const size_t start, const FieldOrListContext& context) {
  return raiseError(
      "FIXME: Not implemented yet in this preview release "
      "(LazyArrowExpressionWithFunctionBody)");
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceLazyFunctionDeclaration(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));
  const auto syntax = FunctionSyntaxKind::Statement;

  BINJS_MOZ_TRY_DECL(
      isAsync, tokenizer_->readBool(FieldContext(
                   BinASTInterfaceAndField::LazyFunctionDeclaration__IsAsync)));
  if (isAsync) {
    return raiseError(
        "Async function is not supported in this preview release");
  }
  BINJS_MOZ_TRY_DECL(
      isGenerator,
      tokenizer_->readBool(FieldContext(
          BinASTInterfaceAndField::LazyFunctionDeclaration__IsGenerator)));
  if (isGenerator) {
    return raiseError("Generator is not supported in this preview release");
  }
  BINJS_MOZ_TRY_DECL(
      name, parseBindingIdentifier(FieldOrListContext(FieldContext(
                BinASTInterfaceAndField::LazyFunctionDeclaration__Name))));

  BINJS_MOZ_TRY_DECL(
      length, tokenizer_->readUnsignedLong(FieldContext(
                  BinASTInterfaceAndField::LazyFunctionDeclaration__Length)));

  BINJS_MOZ_TRY_DECL(
      directives,
      parseListOfDirective(FieldContext(
          BinASTInterfaceAndField::LazyFunctionDeclaration__Directives)));

  BINJS_MOZ_TRY_DECL(
      contentsSkip,
      tokenizer_->readSkippableSubTree(FieldContext(
          BinASTInterfaceAndField::LazyFunctionDeclaration__ContentsSkip)));
  // Don't parse the contents until we delazify.

  // TODO: This will become incorrect in the face of ES6 features.
  uint32_t nargs = length;

  BINJS_MOZ_TRY_DECL(funbox,
                     buildFunctionBox(isGenerator ? GeneratorKind::Generator
                                                  : GeneratorKind::NotGenerator,
                                      isAsync ? FunctionAsyncKind::AsyncFunction
                                              : FunctionAsyncKind::SyncFunction,
                                      syntax, name));

  forceStrictIfNecessary(funbox, directives);

  pc_->sc()->setHasInnerFunctions();

  BINJS_MOZ_TRY_DECL(result, makeEmptyFunctionNode(start, syntax, funbox));

  auto skipStart = contentsSkip.startOffset();
  auto skipEnd = skipStart + contentsSkip.length();
  MOZ_TRY(finishLazyFunction(funbox, nargs, skipStart, skipEnd));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceLazyFunctionExpression(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));
  const auto syntax = FunctionSyntaxKind::Expression;

  BINJS_MOZ_TRY_DECL(
      isAsync, tokenizer_->readBool(FieldContext(
                   BinASTInterfaceAndField::LazyFunctionExpression__IsAsync)));
  if (isAsync) {
    return raiseError(
        "Async function is not supported in this preview release");
  }
  BINJS_MOZ_TRY_DECL(
      isGenerator,
      tokenizer_->readBool(FieldContext(
          BinASTInterfaceAndField::LazyFunctionExpression__IsGenerator)));
  if (isGenerator) {
    return raiseError("Generator is not supported in this preview release");
  }
  BINJS_MOZ_TRY_DECL(
      name, parseOptionalBindingIdentifier(FieldContext(
                BinASTInterfaceAndField::LazyFunctionExpression__Name)));

  BINJS_MOZ_TRY_DECL(
      length, tokenizer_->readUnsignedLong(FieldContext(
                  BinASTInterfaceAndField::LazyFunctionExpression__Length)));

  BINJS_MOZ_TRY_DECL(
      directives,
      parseListOfDirective(FieldContext(
          BinASTInterfaceAndField::LazyFunctionExpression__Directives)));

  BINJS_MOZ_TRY_DECL(
      contentsSkip,
      tokenizer_->readSkippableSubTree(FieldContext(
          BinASTInterfaceAndField::LazyFunctionExpression__ContentsSkip)));
  // Don't parse the contents until we delazify.

  // TODO: This will become incorrect in the face of ES6 features.
  uint32_t nargs = length;

  BINJS_MOZ_TRY_DECL(funbox,
                     buildFunctionBox(isGenerator ? GeneratorKind::Generator
                                                  : GeneratorKind::NotGenerator,
                                      isAsync ? FunctionAsyncKind::AsyncFunction
                                              : FunctionAsyncKind::SyncFunction,
                                      syntax, name));

  forceStrictIfNecessary(funbox, directives);

  pc_->sc()->setHasInnerFunctions();

  BINJS_MOZ_TRY_DECL(result, makeEmptyFunctionNode(start, syntax, funbox));

  auto skipStart = contentsSkip.startOffset();
  auto skipEnd = skipStart + contentsSkip.length();
  MOZ_TRY(finishLazyFunction(funbox, nargs, skipStart, skipEnd));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceLazyGetter(
    const size_t start, const ListContext& context) {
  return raiseError(
      "FIXME: Not implemented yet in this preview release (LazyGetter)");
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceLazyMethod(
    const size_t start, const ListContext& context) {
  return raiseError(
      "FIXME: Not implemented yet in this preview release (LazyMethod)");
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceLazySetter(
    const size_t start, const ListContext& context) {
  return raiseError(
      "FIXME: Not implemented yet in this preview release (LazySetter)");
}

template <typename Tok>
JS::Result<ParseNode*>
BinASTParser<Tok>::parseInterfaceLiteralBooleanExpression(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  BINJS_MOZ_TRY_DECL(
      value, tokenizer_->readBool(FieldContext(
                 BinASTInterfaceAndField::LiteralBooleanExpression__Value)));

  BINJS_TRY_DECL(result,
                 handler_.newBooleanLiteral(value, tokenizer_->pos(start)));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*>
BinASTParser<Tok>::parseInterfaceLiteralInfinityExpression(
    const size_t start, const FieldOrListContext& context) {
  return raiseError(
      "FIXME: Not implemented yet in this preview release "
      "(LiteralInfinityExpression)");
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceLiteralNullExpression(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  BINJS_TRY_DECL(result, handler_.newNullLiteral(tokenizer_->pos(start)));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*>
BinASTParser<Tok>::parseInterfaceLiteralNumericExpression(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  BINJS_MOZ_TRY_DECL(
      value, tokenizer_->readDouble(FieldContext(
                 BinASTInterfaceAndField::LiteralNumericExpression__Value)));

  BINJS_TRY_DECL(result, handler_.newNumber(value, DecimalPoint::HasDecimal,
                                            tokenizer_->pos(start)));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceLiteralPropertyName(
    const size_t start, const FieldContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  RootedAtom value(cx_);
  MOZ_TRY_VAR(value, tokenizer_->readAtom(FieldContext(
                         BinASTInterfaceAndField::LiteralPropertyName__Value)));

  ParseNode* result;
  uint32_t index;
  if (value->isIndex(&index)) {
    BINJS_TRY_VAR(result,
                  handler_.newNumber(index, NoDecimal,
                                     TokenPos(start, tokenizer_->offset())));
  } else {
    BINJS_TRY_VAR(result, handler_.newObjectLiteralPropertyName(
                              value, tokenizer_->pos(start)));
  }
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceLiteralRegExpExpression(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  RootedAtom pattern(cx_);
  MOZ_TRY_VAR(pattern,
              tokenizer_->readAtom(FieldContext(
                  BinASTInterfaceAndField::LiteralRegExpExpression__Pattern)));
  RegExpFlags reflags = JS::RegExpFlag::NoFlags;
  auto flagsContext =
      FieldContext(BinASTInterfaceAndField::LiteralRegExpExpression__Flags);
  if constexpr (std::is_same_v<Tok, BinASTTokenReaderContext>) {
    // Hack: optimized `readChars` is not implemented for
    // `BinASTTokenReaderContext`.
    RootedAtom flags(cx_);
    MOZ_TRY_VAR(flags, tokenizer_->readAtom(flagsContext));
    if (!this->parseRegExpFlags(flags, &reflags)) {
      return raiseError("Invalid regexp flags");
    }
  } else {
    Chars flags(cx_);
    MOZ_TRY(tokenizer_->readChars(flags, flagsContext));
    if (!this->parseRegExpFlags(flags, &reflags)) {
      return raiseError("Invalid regexp flags");
    }
  }

  // Validate the RegExp pattern is valid.
  {
    JS::CompileOptions dummyOptions(cx_);
    DummyTokenStream dummyTokenStream(cx_, dummyOptions);

    LifoAllocScope allocScope(&cx_->tempLifoAlloc());
#ifdef ENABLE_NEW_REGEXP
    BINJS_TRY(
        irregexp::CheckPatternSyntax(cx_, dummyTokenStream, pattern, reflags));
#else
    BINJS_TRY(irregexp::ParsePatternSyntax(dummyTokenStream, allocScope.alloc(),
                                           pattern, reflags.unicode()));
#endif
  }

  RegExpIndex index(this->getCompilationInfo().regExpData.length());
  BINJS_TRY(this->getCompilationInfo().regExpData.emplaceBack());
  BINJS_TRY(
      this->getCompilationInfo().regExpData[index].init(cx_, pattern, reflags));

  return handler_.newRegExp(index, tokenizer_->pos(start));
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceLiteralStringExpression(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  RootedAtom value(cx_);
  MOZ_TRY_VAR(value,
              tokenizer_->readAtom(FieldContext(
                  BinASTInterfaceAndField::LiteralStringExpression__Value)));

  BINJS_TRY_DECL(result,
                 handler_.newStringLiteral(value, tokenizer_->pos(start)));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceModule(
    const size_t start, const RootContext& context) {
  return raiseError(
      "FIXME: Not implemented yet in this preview release (Module)");
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceNewExpression(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  BINJS_MOZ_TRY_DECL(callee,
                     parseExpression(FieldContext(
                         BinASTInterfaceAndField::NewExpression__Callee)));

  BINJS_MOZ_TRY_DECL(arguments,
                     parseArguments(FieldContext(
                         BinASTInterfaceAndField::NewExpression__Arguments)));

  BINJS_TRY_DECL(result,
                 handler_.newNewExpression(tokenizer_->pos(start).begin, callee,
                                           arguments, /* isSpread = */ false));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceNewTargetExpression(
    const size_t start, const FieldOrListContext& context) {
  return raiseError(
      "FIXME: Not implemented yet in this preview release "
      "(NewTargetExpression)");
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceObjectAssignmentTarget(
    const size_t start, const FieldContext& context) {
  return raiseError(
      "FIXME: Not implemented yet in this preview release "
      "(ObjectAssignmentTarget)");
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceObjectBinding(
    const size_t start, const FieldOrListContext& context) {
  return raiseError(
      "FIXME: Not implemented yet in this preview release (ObjectBinding)");
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceObjectExpression(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  BINJS_MOZ_TRY_DECL(
      properties, parseListOfObjectProperty(FieldContext(
                      BinASTInterfaceAndField::ObjectExpression__Properties)));

  auto result = properties;
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceReturnStatement(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));
  if (!pc_->isFunctionBox()) {
    // Return statements are permitted only inside functions.
    return raiseInvalidKind("Toplevel Statement", BinASTKind::ReturnStatement);
  }

  pc_->functionBox()->usesReturn = true;

  BINJS_MOZ_TRY_DECL(
      expression, parseOptionalExpression(FieldContext(
                      BinASTInterfaceAndField::ReturnStatement__Expression)));

  BINJS_TRY_DECL(
      result, handler_.newReturnStatement(expression, tokenizer_->pos(start)));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceScript(
    const size_t start, const RootContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  MOZ_TRY(parseAssertedScriptGlobalScope(
      FieldContext(BinASTInterfaceAndField::Script__Scope)));

  BINJS_MOZ_TRY_DECL(directives,
                     parseListOfDirective(FieldContext(
                         BinASTInterfaceAndField::Script__Directives)));
  forceStrictIfNecessary(pc_->sc(), directives);
  BINJS_MOZ_TRY_DECL(statements,
                     parseListOfStatement(FieldContext(
                         BinASTInterfaceAndField::Script__Statements)));

  MOZ_TRY(checkClosedVars(pc_->varScope()));
  MOZ_TRY(prependDirectivesToBody(/* body = */ statements, directives));
  auto result = statements;
  return result;
}

/*
 interface SetterContents : Node {
    bool isThisCaptured;
    AssertedParameterScope parameterScope;
    Parameter param;
    AssertedVarScope bodyScope;
    FunctionBody body;
 }
*/
template <typename Tok>
JS::Result<Ok> BinASTParser<Tok>::parseSetterContents(
    uint32_t funLength, ListNode** paramsOut, ListNode** bodyOut,
    const FieldContext& context) {
  BinASTKind kind = BinASTKind::SetterContents;
  AutoTaggedTuple guard(*tokenizer_);

  guard.init();
  MOZ_TRY(tokenizer_->enterInterface(kind, context));
  const auto start = tokenizer_->offset();
  BINJS_MOZ_TRY_DECL(
      result, parseInterfaceSetterContents(start, funLength, paramsOut, bodyOut,
                                           context));
  MOZ_TRY(guard.done());

  return result;
}

template <typename Tok>
JS::Result<Ok> BinASTParser<Tok>::parseInterfaceSetterContents(
    const size_t start, uint32_t funLength, ListNode** paramsOut,
    ListNode** bodyOut, const FieldContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  BINJS_MOZ_TRY_DECL(
      isThisCaptured,
      tokenizer_->readBool(FieldContext(
          BinASTInterfaceAndField::SetterContents__IsThisCaptured)));
  // TODO: Use this in BinASTParser::buildFunction.
  (void)isThisCaptured;
  Rooted<GCVector<JSAtom*>> positionalParams(cx_, GCVector<JSAtom*>(cx_));
  MOZ_TRY(parseAssertedParameterScope(
      &positionalParams,
      FieldContext(BinASTInterfaceAndField::SetterContents__ParameterScope)));

  BINJS_MOZ_TRY_DECL(param,
                     parseParameter(FieldOrListContext(FieldContext(
                         BinASTInterfaceAndField::SetterContents__Param))));
  BINJS_TRY_DECL(params, handler_.newParamsBody(param->pn_pos));
  handler_.addList(params, param);
  MOZ_TRY(checkPositionalParameterIndices(positionalParams, params));
  MOZ_TRY(parseAssertedVarScope(
      FieldContext(BinASTInterfaceAndField::SetterContents__BodyScope)));

  BINJS_MOZ_TRY_DECL(body, parseFunctionBody(FieldContext(
                               BinASTInterfaceAndField::SetterContents__Body)));

  *paramsOut = params;
  *bodyOut = body;
  auto result = Ok();
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceShorthandProperty(
    const size_t start, const ListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  BINJS_MOZ_TRY_DECL(name,
                     parseIdentifierExpression(FieldOrListContext(FieldContext(
                         BinASTInterfaceAndField::ShorthandProperty__Name))));

  MOZ_ASSERT(name->isKind(ParseNodeKind::Name));
  MOZ_ASSERT(!handler_.isUsableAsObjectPropertyName(name));
  BINJS_TRY_DECL(propName, handler_.newObjectLiteralPropertyName(
                               name->template as<NameNode>().name(),
                               tokenizer_->pos(start)));

  BINJS_TRY_DECL(result,
                 handler_.newShorthandPropertyDefinition(propName, name));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceSpreadElement(
    const size_t start, const ListContext& context) {
  return raiseError(
      "FIXME: Not implemented yet in this preview release (SpreadElement)");
}

template <typename Tok>
JS::Result<ParseNode*>
BinASTParser<Tok>::parseInterfaceStaticMemberAssignmentTarget(
    const size_t start, const FieldContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));
  size_t nameStart;

  BINJS_MOZ_TRY_DECL(
      object,
      parseExpressionOrSuper(FieldContext(
          BinASTInterfaceAndField::StaticMemberAssignmentTarget__Object)));

  RootedAtom property(cx_);
  {
    nameStart = tokenizer_->offset();
    MOZ_TRY_VAR(
        property,
        tokenizer_->readPropertyKey(FieldContext(
            BinASTInterfaceAndField::StaticMemberAssignmentTarget__Property)));
  }

  BINJS_TRY_DECL(name, handler_.newPropertyName(property->asPropertyName(),
                                                tokenizer_->pos(nameStart)));
  BINJS_TRY_DECL(result, handler_.newPropertyAccess(object, name));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceStaticMemberExpression(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));
  size_t nameStart;

  BINJS_MOZ_TRY_DECL(
      object, parseExpressionOrSuper(FieldContext(
                  BinASTInterfaceAndField::StaticMemberExpression__Object)));

  RootedAtom property(cx_);
  {
    nameStart = tokenizer_->offset();
    MOZ_TRY_VAR(
        property,
        tokenizer_->readPropertyKey(FieldContext(
            BinASTInterfaceAndField::StaticMemberExpression__Property)));
  }

  BINJS_TRY_DECL(name, handler_.newPropertyName(property->asPropertyName(),
                                                tokenizer_->pos(nameStart)));
  BINJS_TRY_DECL(result, handler_.newPropertyAccess(object, name));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceSuper(
    const size_t start, const FieldContext& context) {
  return raiseError(
      "FIXME: Not implemented yet in this preview release (Super)");
}

/*
 interface SwitchCase : Node {
    Expression test;
    FrozenArray<Statement> consequent;
 }
*/
template <typename Tok>
JS::Result<CaseClause*> BinASTParser<Tok>::parseSwitchCase(
    const ListContext& context) {
  BinASTKind kind = BinASTKind::SwitchCase;
  AutoTaggedTuple guard(*tokenizer_);

  guard.init();
  MOZ_TRY(tokenizer_->enterInterface(kind, context));
  const auto start = tokenizer_->offset();
  BINJS_MOZ_TRY_DECL(result, parseInterfaceSwitchCase(start, context));
  MOZ_TRY(guard.done());

  return result;
}

template <typename Tok>
JS::Result<CaseClause*> BinASTParser<Tok>::parseInterfaceSwitchCase(
    const size_t start, const ListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  BINJS_MOZ_TRY_DECL(
      test,
      parseExpression(FieldContext(BinASTInterfaceAndField::SwitchCase__Test)));

  BINJS_MOZ_TRY_DECL(consequent,
                     parseListOfStatement(FieldContext(
                         BinASTInterfaceAndField::SwitchCase__Consequent)));

  BINJS_TRY_DECL(result, handler_.newCaseOrDefault(start, test, consequent));
  return result;
}

/*
 interface SwitchDefault : Node {
    FrozenArray<Statement> consequent;
 }
*/
template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseSwitchDefault(
    const FieldContext& context) {
  BinASTKind kind = BinASTKind::SwitchDefault;
  AutoTaggedTuple guard(*tokenizer_);

  guard.init();
  MOZ_TRY(tokenizer_->enterInterface(kind, context));
  const auto start = tokenizer_->offset();
  BINJS_MOZ_TRY_DECL(result, parseInterfaceSwitchDefault(start, context));
  MOZ_TRY(guard.done());

  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceSwitchDefault(
    const size_t start, const FieldContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  BINJS_MOZ_TRY_DECL(consequent,
                     parseListOfStatement(FieldContext(
                         BinASTInterfaceAndField::SwitchDefault__Consequent)));

  BINJS_TRY_DECL(result, handler_.newCaseOrDefault(start, nullptr, consequent));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceSwitchStatement(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  BINJS_MOZ_TRY_DECL(
      discriminant,
      parseExpression(FieldContext(
          BinASTInterfaceAndField::SwitchStatement__Discriminant)));
  ParseContext::Statement stmt(pc_, StatementKind::Switch);
  BINJS_MOZ_TRY_DECL(cases,
                     parseListOfSwitchCase(FieldContext(
                         BinASTInterfaceAndField::SwitchStatement__Cases)));

  BINJS_TRY_DECL(scope, handler_.newLexicalScope(nullptr, cases));
  BINJS_TRY_DECL(result, handler_.newSwitchStatement(start, discriminant, scope,
                                                     /* hasDefault = */ false));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*>
BinASTParser<Tok>::parseInterfaceSwitchStatementWithDefault(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  BINJS_MOZ_TRY_DECL(
      discriminant,
      parseExpression(FieldContext(
          BinASTInterfaceAndField::SwitchStatementWithDefault__Discriminant)));
  ParseContext::Statement stmt(pc_, StatementKind::Switch);
  BINJS_MOZ_TRY_DECL(preDefaultCases,
                     parseListOfSwitchCase(FieldContext(
                         BinASTInterfaceAndField::
                             SwitchStatementWithDefault__PreDefaultCases)));

  BINJS_MOZ_TRY_DECL(
      defaultCase,
      parseSwitchDefault(FieldContext(
          BinASTInterfaceAndField::SwitchStatementWithDefault__DefaultCase)));

  BINJS_MOZ_TRY_DECL(postDefaultCases,
                     parseListOfSwitchCase(FieldContext(
                         BinASTInterfaceAndField::
                             SwitchStatementWithDefault__PostDefaultCases)));

  // Concatenate `preDefaultCase`, `defaultCase`, `postDefaultCase`
  auto cases = preDefaultCases;
  handler_.addList(cases, defaultCase);
  ParseNode* iter = postDefaultCases->head();
  while (iter) {
    ParseNode* next = iter->pn_next;
    handler_.addList(cases, iter);
    iter = next;
  }
  BINJS_TRY_DECL(scope, handler_.newLexicalScope(nullptr, cases));
  BINJS_TRY_DECL(result, handler_.newSwitchStatement(start, discriminant, scope,
                                                     /* hasDefault = */ true));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceTemplateExpression(
    const size_t start, const FieldOrListContext& context) {
  return raiseError(
      "FIXME: Not implemented yet in this preview release "
      "(TemplateExpression)");
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceThisExpression(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  if (pc_->isFunctionBox()) {
    pc_->functionBox()->usesThis = true;
  }

  TokenPos pos = tokenizer_->pos(start);
  ParseNode* thisName(nullptr);
  if (pc_->sc()->hasFunctionThisBinding()) {
    HandlePropertyName dotThis = cx_->names().dotThis;
    BINJS_TRY(usedNames_.noteUse(cx_, dotThis, pc_->scriptId(),
                                 pc_->innermostScope()->id()));
    BINJS_TRY_VAR(thisName, handler_.newName(dotThis, pos, cx_));
  }

  BINJS_TRY_DECL(result, handler_.newThisLiteral(pos, thisName));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceThrowStatement(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  BINJS_MOZ_TRY_DECL(expression,
                     parseExpression(FieldContext(
                         BinASTInterfaceAndField::ThrowStatement__Expression)));

  BINJS_TRY_DECL(
      result, handler_.newThrowStatement(expression, tokenizer_->pos(start)));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceTryCatchStatement(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  ParseNode* body;
  {
    ParseContext::Statement stmt(pc_, StatementKind::Try);
    ParseContext::Scope scope(cx_, pc_, usedNames_);
    BINJS_TRY(scope.init(pc_));
    MOZ_TRY_VAR(body, parseBlock(FieldOrListContext(FieldContext(
                          BinASTInterfaceAndField::TryCatchStatement__Body))));
  }

  BINJS_MOZ_TRY_DECL(
      catchClause,
      parseCatchClause(FieldContext(
          BinASTInterfaceAndField::TryCatchStatement__CatchClause)));

  BINJS_TRY_DECL(result,
                 handler_.newTryStatement(start, body, catchClause,
                                          /* finallyBlock = */ nullptr));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceTryFinallyStatement(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  ParseNode* body;
  {
    ParseContext::Statement stmt(pc_, StatementKind::Try);
    ParseContext::Scope scope(cx_, pc_, usedNames_);
    BINJS_TRY(scope.init(pc_));
    MOZ_TRY_VAR(body,
                parseBlock(FieldOrListContext(FieldContext(
                    BinASTInterfaceAndField::TryFinallyStatement__Body))));
  }

  BINJS_MOZ_TRY_DECL(
      catchClause,
      parseOptionalCatchClause(FieldContext(
          BinASTInterfaceAndField::TryFinallyStatement__CatchClause)));

  ParseNode* finalizer;
  {
    ParseContext::Statement stmt(pc_, StatementKind::Finally);
    ParseContext::Scope scope(cx_, pc_, usedNames_);
    BINJS_TRY(scope.init(pc_));
    MOZ_TRY_VAR(finalizer,
                parseBlock(FieldOrListContext(FieldContext(
                    BinASTInterfaceAndField::TryFinallyStatement__Finalizer))));
  }

  BINJS_TRY_DECL(result,
                 handler_.newTryStatement(start, body, catchClause, finalizer));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceUnaryExpression(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  BINJS_MOZ_TRY_DECL(operator_,
                     parseUnaryOperator(FieldContext(
                         BinASTInterfaceAndField::UnaryExpression__Operator)));

  BINJS_MOZ_TRY_DECL(operand,
                     parseExpression(FieldContext(
                         BinASTInterfaceAndField::UnaryExpression__Operand)));

  ParseNodeKind pnk;
  switch (operator_) {
    case UnaryOperator::Minus:
      pnk = ParseNodeKind::NegExpr;
      break;
    case UnaryOperator::Plus:
      pnk = ParseNodeKind::PosExpr;
      break;
    case UnaryOperator::Not:
      pnk = ParseNodeKind::NotExpr;
      break;
    case UnaryOperator::BitNot:
      pnk = ParseNodeKind::BitNotExpr;
      break;
    case UnaryOperator::Typeof: {
      if (operand->isKind(ParseNodeKind::Name)) {
        pnk = ParseNodeKind::TypeOfNameExpr;
      } else {
        pnk = ParseNodeKind::TypeOfExpr;
      }
      break;
    }
    case UnaryOperator::Void:
      pnk = ParseNodeKind::VoidExpr;
      break;
    case UnaryOperator::Delete: {
      switch (operand->getKind()) {
        case ParseNodeKind::Name:
          pnk = ParseNodeKind::DeleteNameExpr;
          BINJS_TRY(this->strictModeError(JSMSG_DEPRECATED_DELETE_OPERAND));
          pc_->sc()->setBindingsAccessedDynamically();
          break;
        case ParseNodeKind::DotExpr:
          pnk = ParseNodeKind::DeletePropExpr;
          break;
        case ParseNodeKind::ElemExpr:
          pnk = ParseNodeKind::DeleteElemExpr;
          break;
        default:
          pnk = ParseNodeKind::DeleteExpr;
      }
      break;
    }
  }
  BINJS_TRY_DECL(result, handler_.newUnary(pnk, start, operand));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceUpdateExpression(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  BINJS_MOZ_TRY_DECL(isPrefix,
                     tokenizer_->readBool(FieldContext(
                         BinASTInterfaceAndField::UpdateExpression__IsPrefix)));

  BINJS_MOZ_TRY_DECL(operator_,
                     parseUpdateOperator(FieldContext(
                         BinASTInterfaceAndField::UpdateExpression__Operator)));

  BINJS_MOZ_TRY_DECL(operand,
                     parseSimpleAssignmentTarget(FieldContext(
                         BinASTInterfaceAndField::UpdateExpression__Operand)));

  ParseNodeKind pnk;
  switch (operator_) {
    case UpdateOperator::Incr:
      pnk = isPrefix ? ParseNodeKind::PreIncrementExpr
                     : ParseNodeKind::PostIncrementExpr;
      break;
    case UpdateOperator::Decr:
      pnk = isPrefix ? ParseNodeKind::PreDecrementExpr
                     : ParseNodeKind::PostDecrementExpr;
      break;
  }
  BINJS_TRY_DECL(result, handler_.newUnary(pnk, start, operand));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceVariableDeclaration(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));
  AutoVariableDeclarationKind kindGuard(this);

  BINJS_MOZ_TRY_DECL(kind_,
                     parseVariableDeclarationKind(FieldContext(
                         BinASTInterfaceAndField::VariableDeclaration__Kind)));
  // Restored by `kindGuard`.
  variableDeclarationKind_ = kind_;
  ParseNodeKind declarationListKind;
  switch (kind_) {
    case VariableDeclarationKind::Var:
      declarationListKind = ParseNodeKind::VarStmt;
      break;
    case VariableDeclarationKind::Let:
      return raiseError("Let is not supported in this preview release");
    case VariableDeclarationKind::Const:
      return raiseError("Const is not supported in this preview release");
  }
  BINJS_MOZ_TRY_DECL(
      declarators,
      parseListOfVariableDeclarator(
          declarationListKind,
          FieldContext(
              BinASTInterfaceAndField::VariableDeclaration__Declarators)));

  // By specification, the list may not be empty.
  if (declarators->empty()) {
    return raiseEmpty("VariableDeclaration");
  }

  auto result = declarators;
  return result;
}

/*
 interface VariableDeclarator : Node {
    Binding binding;
    Expression? init;
 }
*/
template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseVariableDeclarator(
    const ListContext& context) {
  BinASTKind kind = BinASTKind::VariableDeclarator;
  AutoTaggedTuple guard(*tokenizer_);

  guard.init();
  MOZ_TRY(tokenizer_->enterInterface(kind, context));
  const auto start = tokenizer_->offset();
  BINJS_MOZ_TRY_DECL(result, parseInterfaceVariableDeclarator(start, context));
  MOZ_TRY(guard.done());

  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceVariableDeclarator(
    const size_t start, const ListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  BINJS_MOZ_TRY_DECL(
      binding, parseBinding(FieldContext(
                   BinASTInterfaceAndField::VariableDeclarator__Binding)));

  BINJS_MOZ_TRY_DECL(init,
                     parseOptionalExpression(FieldContext(
                         BinASTInterfaceAndField::VariableDeclarator__Init)));

  ParseNode* result;
  if (binding->isKind(ParseNodeKind::Name)) {
    // `var foo [= bar]``
    NameNode* bindingNameNode = &binding->template as<NameNode>();
    MOZ_TRY(checkBinding(bindingNameNode->atom()->asPropertyName()));
    if (init) {
      BINJS_TRY_VAR(
          result, handler_.finishInitializerAssignment(bindingNameNode, init));
    } else {
      result = bindingNameNode;
    }
  } else {
    // `var pattern = bar`
    if (!init) {
      // Here, `init` is required.
      return raiseMissingField("VariableDeclarator (with non-trivial pattern)",
                               BinASTField::Init);
    }

    MOZ_CRASH(
        "Unimplemented: AssertedScope check for BindingPattern variable "
        "declaration");
    BINJS_TRY_VAR(result, handler_.newAssignment(ParseNodeKind::AssignExpr,
                                                 binding, init));
  }
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceWhileStatement(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));
  ParseContext::Statement stmt(pc_, StatementKind::WhileLoop);

  BINJS_MOZ_TRY_DECL(test, parseExpression(FieldContext(
                               BinASTInterfaceAndField::WhileStatement__Test)));

  BINJS_MOZ_TRY_DECL(
      body, parseStatement(FieldOrListContext(
                FieldContext(BinASTInterfaceAndField::WhileStatement__Body))));

  BINJS_TRY_DECL(result, handler_.newWhileStatement(start, test, body));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceWithStatement(
    const size_t start, const FieldOrListContext& context) {
  BINJS_TRY(CheckRecursionLimit(cx_));

  BINJS_MOZ_TRY_DECL(object,
                     parseExpression(FieldContext(
                         BinASTInterfaceAndField::WithStatement__Object)));

  ParseContext::Statement stmt(pc_, StatementKind::With);
  BINJS_MOZ_TRY_DECL(body, parseStatement(FieldOrListContext(FieldContext(
                               BinASTInterfaceAndField::WithStatement__Body))));

  pc_->sc()->setBindingsAccessedDynamically();
  BINJS_TRY_DECL(result, handler_.newWithStatement(start, object, body));
  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceYieldExpression(
    const size_t start, const FieldOrListContext& context) {
  return raiseError(
      "FIXME: Not implemented yet in this preview release (YieldExpression)");
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseInterfaceYieldStarExpression(
    const size_t start, const FieldOrListContext& context) {
  return raiseError(
      "FIXME: Not implemented yet in this preview release "
      "(YieldStarExpression)");
}

// ----- String enums (autogenerated, by lexicographical order)
/*
enum AssertedDeclaredKind {
    "var",
    "non-const lexical",
    "const lexical"
};
*/
template <typename Tok>
JS::Result<typename BinASTParser<Tok>::AssertedDeclaredKind>
BinASTParser<Tok>::parseAssertedDeclaredKind(const FieldContext& context) {
  BINJS_MOZ_TRY_DECL(variant, tokenizer_->readVariant(context));

  switch (variant) {
    case BinASTVariant::AssertedDeclaredKindOrVariableDeclarationKindVar:
      return AssertedDeclaredKind::Var;
    case BinASTVariant::AssertedDeclaredKindNonConstLexical:
      return AssertedDeclaredKind::NonConstLexical;
    case BinASTVariant::AssertedDeclaredKindConstLexical:
      return AssertedDeclaredKind::ConstLexical;
    default:
      if (isInvalidVariantPossible()) {
        return raiseInvalidVariant("AssertedDeclaredKind", variant);
      } else {
        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE(
            "invalid BinASTVariant should not appear");
      }
  }
}

/*
enum BinaryOperator {
    ",",
    "||",
    "&&",
    "|",
    "^",
    "&",
    "==",
    "!=",
    "===",
    "!==",
    "<",
    "<=",
    ">",
    ">=",
    "in",
    "instanceof",
    "<<",
    ">>",
    ">>>",
    "+",
    "-",
    "*",
    "/",
    "%",
    "**"
};
*/
template <typename Tok>
JS::Result<typename BinASTParser<Tok>::BinaryOperator>
BinASTParser<Tok>::parseBinaryOperator(const FieldContext& context) {
  BINJS_MOZ_TRY_DECL(variant, tokenizer_->readVariant(context));

  switch (variant) {
    case BinASTVariant::BinaryOperatorComma:
      return BinaryOperator::Comma;
    case BinASTVariant::BinaryOperatorLogicalOr:
      return BinaryOperator::LogicalOr;
    case BinASTVariant::BinaryOperatorLogicalAnd:
      return BinaryOperator::LogicalAnd;
    case BinASTVariant::BinaryOperatorBitOr:
      return BinaryOperator::BitOr;
    case BinASTVariant::BinaryOperatorBitXor:
      return BinaryOperator::BitXor;
    case BinASTVariant::BinaryOperatorBitAnd:
      return BinaryOperator::BitAnd;
    case BinASTVariant::BinaryOperatorEq:
      return BinaryOperator::Eq;
    case BinASTVariant::BinaryOperatorNeq:
      return BinaryOperator::Neq;
    case BinASTVariant::BinaryOperatorStrictEq:
      return BinaryOperator::StrictEq;
    case BinASTVariant::BinaryOperatorStrictNeq:
      return BinaryOperator::StrictNeq;
    case BinASTVariant::BinaryOperatorLessThan:
      return BinaryOperator::LessThan;
    case BinASTVariant::BinaryOperatorLeqThan:
      return BinaryOperator::LeqThan;
    case BinASTVariant::BinaryOperatorGreaterThan:
      return BinaryOperator::GreaterThan;
    case BinASTVariant::BinaryOperatorGeqThan:
      return BinaryOperator::GeqThan;
    case BinASTVariant::BinaryOperatorIn:
      return BinaryOperator::In;
    case BinASTVariant::BinaryOperatorInstanceof:
      return BinaryOperator::Instanceof;
    case BinASTVariant::BinaryOperatorLsh:
      return BinaryOperator::Lsh;
    case BinASTVariant::BinaryOperatorRsh:
      return BinaryOperator::Rsh;
    case BinASTVariant::BinaryOperatorUrsh:
      return BinaryOperator::Ursh;
    case BinASTVariant::BinaryOperatorOrUnaryOperatorPlus:
      return BinaryOperator::Plus;
    case BinASTVariant::BinaryOperatorOrUnaryOperatorMinus:
      return BinaryOperator::Minus;
    case BinASTVariant::BinaryOperatorMul:
      return BinaryOperator::Mul;
    case BinASTVariant::BinaryOperatorDiv:
      return BinaryOperator::Div;
    case BinASTVariant::BinaryOperatorMod:
      return BinaryOperator::Mod;
    case BinASTVariant::BinaryOperatorPow:
      return BinaryOperator::Pow;
    default:
      if (isInvalidVariantPossible()) {
        return raiseInvalidVariant("BinaryOperator", variant);
      } else {
        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE(
            "invalid BinASTVariant should not appear");
      }
  }
}

/*
enum CompoundAssignmentOperator {
    "+=",
    "-=",
    "*=",
    "/=",
    "%=",
    "**=",
    "<<=",
    ">>=",
    ">>>=",
    "|=",
    "^=",
    "&="
};
*/
template <typename Tok>
JS::Result<typename BinASTParser<Tok>::CompoundAssignmentOperator>
BinASTParser<Tok>::parseCompoundAssignmentOperator(
    const FieldContext& context) {
  BINJS_MOZ_TRY_DECL(variant, tokenizer_->readVariant(context));

  switch (variant) {
    case BinASTVariant::CompoundAssignmentOperatorPlusAssign:
      return CompoundAssignmentOperator::PlusAssign;
    case BinASTVariant::CompoundAssignmentOperatorMinusAssign:
      return CompoundAssignmentOperator::MinusAssign;
    case BinASTVariant::CompoundAssignmentOperatorMulAssign:
      return CompoundAssignmentOperator::MulAssign;
    case BinASTVariant::CompoundAssignmentOperatorDivAssign:
      return CompoundAssignmentOperator::DivAssign;
    case BinASTVariant::CompoundAssignmentOperatorModAssign:
      return CompoundAssignmentOperator::ModAssign;
    case BinASTVariant::CompoundAssignmentOperatorPowAssign:
      return CompoundAssignmentOperator::PowAssign;
    case BinASTVariant::CompoundAssignmentOperatorLshAssign:
      return CompoundAssignmentOperator::LshAssign;
    case BinASTVariant::CompoundAssignmentOperatorRshAssign:
      return CompoundAssignmentOperator::RshAssign;
    case BinASTVariant::CompoundAssignmentOperatorUrshAssign:
      return CompoundAssignmentOperator::UrshAssign;
    case BinASTVariant::CompoundAssignmentOperatorBitOrAssign:
      return CompoundAssignmentOperator::BitOrAssign;
    case BinASTVariant::CompoundAssignmentOperatorBitXorAssign:
      return CompoundAssignmentOperator::BitXorAssign;
    case BinASTVariant::CompoundAssignmentOperatorBitAndAssign:
      return CompoundAssignmentOperator::BitAndAssign;
    default:
      if (isInvalidVariantPossible()) {
        return raiseInvalidVariant("CompoundAssignmentOperator", variant);
      } else {
        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE(
            "invalid BinASTVariant should not appear");
      }
  }
}

/*
enum UnaryOperator {
    "+",
    "-",
    "!",
    "~",
    "typeof",
    "void",
    "delete"
};
*/
template <typename Tok>
JS::Result<typename BinASTParser<Tok>::UnaryOperator>
BinASTParser<Tok>::parseUnaryOperator(const FieldContext& context) {
  BINJS_MOZ_TRY_DECL(variant, tokenizer_->readVariant(context));

  switch (variant) {
    case BinASTVariant::BinaryOperatorOrUnaryOperatorPlus:
      return UnaryOperator::Plus;
    case BinASTVariant::BinaryOperatorOrUnaryOperatorMinus:
      return UnaryOperator::Minus;
    case BinASTVariant::UnaryOperatorNot:
      return UnaryOperator::Not;
    case BinASTVariant::UnaryOperatorBitNot:
      return UnaryOperator::BitNot;
    case BinASTVariant::UnaryOperatorTypeof:
      return UnaryOperator::Typeof;
    case BinASTVariant::UnaryOperatorVoid:
      return UnaryOperator::Void;
    case BinASTVariant::UnaryOperatorDelete:
      return UnaryOperator::Delete;
    default:
      if (isInvalidVariantPossible()) {
        return raiseInvalidVariant("UnaryOperator", variant);
      } else {
        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE(
            "invalid BinASTVariant should not appear");
      }
  }
}

/*
enum UpdateOperator {
    "++",
    "--"
};
*/
template <typename Tok>
JS::Result<typename BinASTParser<Tok>::UpdateOperator>
BinASTParser<Tok>::parseUpdateOperator(const FieldContext& context) {
  BINJS_MOZ_TRY_DECL(variant, tokenizer_->readVariant(context));

  switch (variant) {
    case BinASTVariant::UpdateOperatorIncr:
      return UpdateOperator::Incr;
    case BinASTVariant::UpdateOperatorDecr:
      return UpdateOperator::Decr;
    default:
      if (isInvalidVariantPossible()) {
        return raiseInvalidVariant("UpdateOperator", variant);
      } else {
        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE(
            "invalid BinASTVariant should not appear");
      }
  }
}

/*
enum VariableDeclarationKind {
    "var",
    "let",
    "const"
};
*/
template <typename Tok>
JS::Result<typename BinASTParser<Tok>::VariableDeclarationKind>
BinASTParser<Tok>::parseVariableDeclarationKind(const FieldContext& context) {
  BINJS_MOZ_TRY_DECL(variant, tokenizer_->readVariant(context));

  switch (variant) {
    case BinASTVariant::AssertedDeclaredKindOrVariableDeclarationKindVar:
      return VariableDeclarationKind::Var;
    case BinASTVariant::VariableDeclarationKindLet:
      return VariableDeclarationKind::Let;
    case BinASTVariant::VariableDeclarationKindConst:
      return VariableDeclarationKind::Const;
    default:
      if (isInvalidVariantPossible()) {
        return raiseInvalidVariant("VariableDeclarationKind", variant);
      } else {
        MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE(
            "invalid BinASTVariant should not appear");
      }
  }
}

// ----- Lists (autogenerated, by lexicographical order)

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseArguments(
    const FieldContext& context) {
  uint32_t length;
  AutoList guard(*tokenizer_);

  const auto start = tokenizer_->offset();
  const auto childContext =
      ListContext(context.position_, BinASTList::Arguments);
  guard.init();
  MOZ_TRY(tokenizer_->enterList(length, childContext));
  BINJS_TRY_DECL(result, handler_.newList(ParseNodeKind::Arguments,
                                          tokenizer_->pos(start)));

  for (uint32_t i = 0; i < length; ++i) {
    BINJS_MOZ_TRY_DECL(item, parseExpressionOrSpreadElement(childContext));
    handler_.addList(/* list = */ result, /* kid = */ item);
  }

  MOZ_TRY(guard.done());
  return result;
}

template <typename Tok>
JS::Result<ListNode*> BinASTParser<Tok>::parseFunctionBody(
    const FieldContext& context) {
  uint32_t length;
  AutoList guard(*tokenizer_);

  const auto start = tokenizer_->offset();
  const auto childContext =
      ListContext(context.position_, BinASTList::ListOfStatement);
  guard.init();
  MOZ_TRY(tokenizer_->enterList(length, childContext));
  BINJS_TRY_DECL(result, handler_.newStatementList(tokenizer_->pos(start)));

  for (uint32_t i = 0; i < length; ++i) {
    BINJS_MOZ_TRY_DECL(item, parseStatement(FieldOrListContext(childContext)));
    handler_.addStatementToList(result, item);
  }

  MOZ_TRY(guard.done());
  return result;
}

template <typename Tok>
JS::Result<Ok> BinASTParser<Tok>::parseListOfAssertedBoundName(
    AssertedScopeKind scopeKind, const FieldContext& context) {
  uint32_t length;
  AutoList guard(*tokenizer_);

  const auto start = tokenizer_->offset();
  const auto childContext =
      ListContext(context.position_, BinASTList::ListOfAssertedBoundName);
  guard.init();
  MOZ_TRY(tokenizer_->enterList(length, childContext));
  (void)start;
  auto result = Ok();

  for (uint32_t i = 0; i < length; ++i) {
    MOZ_TRY(parseAssertedBoundName(scopeKind, childContext));
    // Nothing to do here.
  }

  MOZ_TRY(guard.done());
  return result;
}

template <typename Tok>
JS::Result<Ok> BinASTParser<Tok>::parseListOfAssertedDeclaredName(
    AssertedScopeKind scopeKind, const FieldContext& context) {
  uint32_t length;
  AutoList guard(*tokenizer_);

  const auto start = tokenizer_->offset();
  const auto childContext =
      ListContext(context.position_, BinASTList::ListOfAssertedDeclaredName);
  guard.init();
  MOZ_TRY(tokenizer_->enterList(length, childContext));
  (void)start;
  auto result = Ok();

  for (uint32_t i = 0; i < length; ++i) {
    MOZ_TRY(parseAssertedDeclaredName(scopeKind, childContext));
    // Nothing to do here.
  }

  MOZ_TRY(guard.done());
  return result;
}

template <typename Tok>
JS::Result<Ok>
BinASTParser<Tok>::parseListOfAssertedMaybePositionalParameterName(
    AssertedScopeKind scopeKind,
    MutableHandle<GCVector<JSAtom*>> positionalParams,
    const FieldContext& context) {
  uint32_t length;
  AutoList guard(*tokenizer_);

  const auto start = tokenizer_->offset();
  const auto childContext =
      ListContext(context.position_,
                  BinASTList::ListOfAssertedMaybePositionalParameterName);
  guard.init();
  MOZ_TRY(tokenizer_->enterList(length, childContext));
  (void)start;
  auto result = Ok();
  // This list contains also destructuring parameters, and the number of
  // list items can be greater than the actual parameters, or more than
  // ARGNO_LIMIT even if the number of parameters fits into ARGNO_LIMIT.
  // Also, the number of parameters can be greater than this list's length
  // if one of destructuring parameter is empty.
  //
  // We resize `positionalParams` vector on demand, to keep the vector
  // length match to the known maximum positional parameter index + 1.

  for (uint32_t i = 0; i < length; ++i) {
    MOZ_TRY(parseAssertedMaybePositionalParameterName(
        scopeKind, positionalParams, childContext));
    // Nothing to do here.
  }

  MOZ_TRY(guard.done());
  return result;
}

template <typename Tok>
JS::Result<ListNode*> BinASTParser<Tok>::parseListOfDirective(
    const FieldContext& context) {
  uint32_t length;
  AutoList guard(*tokenizer_);

  const auto start = tokenizer_->offset();
  const auto childContext =
      ListContext(context.position_, BinASTList::ListOfDirective);
  guard.init();
  MOZ_TRY(tokenizer_->enterList(length, childContext));
  BINJS_TRY_DECL(result, handler_.newStatementList(tokenizer_->pos(start)));

  for (uint32_t i = 0; i < length; ++i) {
    BINJS_MOZ_TRY_DECL(item, parseDirective(childContext));
    handler_.addStatementToList(result, item);
  }

  MOZ_TRY(guard.done());
  return result;
}

template <typename Tok>
JS::Result<ListNode*> BinASTParser<Tok>::parseListOfObjectProperty(
    const FieldContext& context) {
  uint32_t length;
  AutoList guard(*tokenizer_);

  const auto start = tokenizer_->offset();
  const auto childContext =
      ListContext(context.position_, BinASTList::ListOfObjectProperty);
  guard.init();
  MOZ_TRY(tokenizer_->enterList(length, childContext));
  BINJS_TRY_DECL(result, handler_.newObjectLiteral(start));

  for (uint32_t i = 0; i < length; ++i) {
    BINJS_MOZ_TRY_DECL(item, parseObjectProperty(childContext));
    if (!item->isConstant()) result->setHasNonConstInitializer();
    result->appendWithoutOrderAssumption(item);
  }

  MOZ_TRY(guard.done());
  return result;
}

template <typename Tok>
JS::Result<ListNode*>
BinASTParser<Tok>::parseListOfOptionalExpressionOrSpreadElement(
    const FieldContext& context) {
  uint32_t length;
  AutoList guard(*tokenizer_);

  const auto start = tokenizer_->offset();
  const auto childContext = ListContext(
      context.position_, BinASTList::ListOfOptionalExpressionOrSpreadElement);
  guard.init();
  MOZ_TRY(tokenizer_->enterList(length, childContext));
  BINJS_TRY_DECL(result, handler_.newArrayLiteral(start));

  for (uint32_t i = 0; i < length; ++i) {
    BINJS_MOZ_TRY_DECL(item,
                       parseOptionalExpressionOrSpreadElement(childContext));
    if (item) {
      handler_.addArrayElement(result, item);  // Infallible.
    } else {
      BINJS_TRY(handler_.addElision(result, tokenizer_->pos(start)));
    }
  }

  MOZ_TRY(guard.done());
  return result;
}

template <typename Tok>
JS::Result<ListNode*> BinASTParser<Tok>::parseListOfParameter(
    const FieldContext& context) {
  uint32_t length;
  AutoList guard(*tokenizer_);

  const auto start = tokenizer_->offset();
  const auto childContext =
      ListContext(context.position_, BinASTList::ListOfParameter);
  guard.init();
  MOZ_TRY(tokenizer_->enterList(length, childContext));
  BINJS_TRY_DECL(result, handler_.newParamsBody(tokenizer_->pos(start)));

  for (uint32_t i = 0; i < length; ++i) {
    BINJS_MOZ_TRY_DECL(item, parseParameter(FieldOrListContext(childContext)));
    handler_.addList(/* list = */ result, /* kid = */ item);
  }

  MOZ_TRY(guard.done());
  return result;
}

template <typename Tok>
JS::Result<ListNode*> BinASTParser<Tok>::parseListOfStatement(
    const FieldContext& context) {
  uint32_t length;
  AutoList guard(*tokenizer_);

  const auto start = tokenizer_->offset();
  const auto childContext =
      ListContext(context.position_, BinASTList::ListOfStatement);
  guard.init();
  MOZ_TRY(tokenizer_->enterList(length, childContext));
  BINJS_TRY_DECL(result, handler_.newStatementList(tokenizer_->pos(start)));

  for (uint32_t i = 0; i < length; ++i) {
    BINJS_MOZ_TRY_DECL(item, parseStatement(FieldOrListContext(childContext)));
    handler_.addStatementToList(result, item);
  }

  MOZ_TRY(guard.done());
  return result;
}

template <typename Tok>
JS::Result<ListNode*> BinASTParser<Tok>::parseListOfSwitchCase(
    const FieldContext& context) {
  uint32_t length;
  AutoList guard(*tokenizer_);

  const auto start = tokenizer_->offset();
  const auto childContext =
      ListContext(context.position_, BinASTList::ListOfSwitchCase);
  guard.init();
  MOZ_TRY(tokenizer_->enterList(length, childContext));
  BINJS_TRY_DECL(result, handler_.newStatementList(tokenizer_->pos(start)));

  for (uint32_t i = 0; i < length; ++i) {
    BINJS_MOZ_TRY_DECL(item, parseSwitchCase(childContext));
    handler_.addCaseStatementToList(result, item);
  }

  MOZ_TRY(guard.done());
  return result;
}

template <typename Tok>
JS::Result<ListNode*> BinASTParser<Tok>::parseListOfVariableDeclarator(
    ParseNodeKind declarationListKind, const FieldContext& context) {
  uint32_t length;
  AutoList guard(*tokenizer_);

  const auto start = tokenizer_->offset();
  const auto childContext =
      ListContext(context.position_, BinASTList::ListOfVariableDeclarator);
  guard.init();
  MOZ_TRY(tokenizer_->enterList(length, childContext));
  BINJS_TRY_DECL(result, handler_.newDeclarationList(declarationListKind,
                                                     tokenizer_->pos(start)));

  for (uint32_t i = 0; i < length; ++i) {
    BINJS_MOZ_TRY_DECL(item, parseVariableDeclarator(childContext));
    result->appendWithoutOrderAssumption(item);
  }

  MOZ_TRY(guard.done());
  return result;
}

// ----- Default values (by lexicographical order)
template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseOptionalBinding(
    const FieldContext& context) {
  BinASTKind kind = BinASTKind::_Uninitialized;
  AutoTaggedTuple guard(*tokenizer_);

  guard.init();
  MOZ_TRY(tokenizer_->enterSum(kind, context));
  ParseNode* result;
  if (kind == BinASTKind::_Null) {
    result = nullptr;
  } else {
    const auto start = tokenizer_->offset();
    MOZ_TRY_VAR(result, parseSumBinding(start, kind, context));
  }
  MOZ_TRY(guard.done());

  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseOptionalBindingIdentifier(
    const FieldContext& context) {
  BinASTKind kind = BinASTKind::_Uninitialized;
  AutoTaggedTuple guard(*tokenizer_);

  guard.init();
  MOZ_TRY(tokenizer_->enterOptionalInterface(kind, context));
  ParseNode* result;
  if (kind == BinASTKind::_Null) {
    result = nullptr;
  } else if (!isInvalidKindPossible() ||
             kind == BinASTKind::BindingIdentifier) {
    const auto start = tokenizer_->offset();
    MOZ_TRY_VAR(result, parseInterfaceBindingIdentifier(
                            start, FieldOrListContext(context)));
  } else {
    if (isInvalidKindPossible()) {
      return raiseInvalidKind("BindingIdentifier", kind);
    } else {
      MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE(
          "invalid BinASTKind should not appear");
    }
  }
  MOZ_TRY(guard.done());

  return result;
}

template <typename Tok>
JS::Result<LexicalScopeNode*> BinASTParser<Tok>::parseOptionalCatchClause(
    const FieldContext& context) {
  BinASTKind kind = BinASTKind::_Uninitialized;
  AutoTaggedTuple guard(*tokenizer_);

  guard.init();
  MOZ_TRY(tokenizer_->enterOptionalInterface(kind, context));
  LexicalScopeNode* result;
  if (kind == BinASTKind::_Null) {
    result = nullptr;
  } else if (!isInvalidKindPossible() || kind == BinASTKind::CatchClause) {
    const auto start = tokenizer_->offset();
    MOZ_TRY_VAR(result, parseInterfaceCatchClause(start, context));
  } else {
    if (isInvalidKindPossible()) {
      return raiseInvalidKind("CatchClause", kind);
    } else {
      MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE(
          "invalid BinASTKind should not appear");
    }
  }
  MOZ_TRY(guard.done());

  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseOptionalExpression(
    const FieldContext& context) {
  BinASTKind kind = BinASTKind::_Uninitialized;
  AutoTaggedTuple guard(*tokenizer_);

  guard.init();
  MOZ_TRY(tokenizer_->enterSum(kind, context));
  ParseNode* result;
  if (kind == BinASTKind::_Null) {
    result = nullptr;
  } else {
    const auto start = tokenizer_->offset();
    MOZ_TRY_VAR(result, parseSumExpression(start, kind, context));
  }
  MOZ_TRY(guard.done());

  return result;
}

template <typename Tok>
JS::Result<ParseNode*>
BinASTParser<Tok>::parseOptionalExpressionOrSpreadElement(
    const ListContext& context) {
  BinASTKind kind = BinASTKind::_Uninitialized;
  AutoTaggedTuple guard(*tokenizer_);

  guard.init();
  MOZ_TRY(tokenizer_->enterSum(kind, context));
  ParseNode* result;
  if (kind == BinASTKind::_Null) {
    result = nullptr;
  } else {
    const auto start = tokenizer_->offset();
    MOZ_TRY_VAR(result,
                parseSumExpressionOrSpreadElement(start, kind, context));
  }
  MOZ_TRY(guard.done());

  return result;
}

template <typename Tok>
JS::Result<ParseNode*>
BinASTParser<Tok>::parseOptionalExpressionOrVariableDeclaration(
    const FieldContext& context) {
  BinASTKind kind = BinASTKind::_Uninitialized;
  AutoTaggedTuple guard(*tokenizer_);

  guard.init();
  MOZ_TRY(tokenizer_->enterSum(kind, context));
  ParseNode* result;
  if (kind == BinASTKind::_Null) {
    result = nullptr;
  } else {
    const auto start = tokenizer_->offset();
    MOZ_TRY_VAR(result,
                parseSumExpressionOrVariableDeclaration(start, kind, context));
  }
  MOZ_TRY(guard.done());

  return result;
}

template <typename Tok>
JS::Result<ParseNode*> BinASTParser<Tok>::parseOptionalStatement(
    const FieldContext& context) {
  BinASTKind kind = BinASTKind::_Uninitialized;
  AutoTaggedTuple guard(*tokenizer_);

  guard.init();
  MOZ_TRY(tokenizer_->enterSum(kind, context));
  ParseNode* result;
  if (kind == BinASTKind::_Null) {
    result = nullptr;
  } else {
    const auto start = tokenizer_->offset();
    MOZ_TRY_VAR(result,
                parseSumStatement(start, kind, FieldOrListContext(context)));
  }
  MOZ_TRY(guard.done());

  return result;
}

// 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<BinASTTokenReaderContext>;
template class BinASTParser<BinASTTokenReaderMultipart>;

}  // namespace js::frontend
