// Copyright 2012 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_PARSING_PARSER_BASE_H_
#define V8_PARSING_PARSER_BASE_H_

#include <stdint.h>

#include <utility>
#include <vector>

#include "src/ast/ast-source-ranges.h"
#include "src/ast/ast.h"
#include "src/ast/scopes.h"
#include "src/base/flags.h"
#include "src/base/hashmap.h"
#include "src/base/v8-fallthrough.h"
#include "src/codegen/bailout-reason.h"
#include "src/common/globals.h"
#include "src/common/message-template.h"
#include "src/logging/log.h"
#include "src/logging/runtime-call-stats-scope.h"
#include "src/objects/function-kind.h"
#include "src/parsing/expression-scope.h"
#include "src/parsing/func-name-inferrer.h"
#include "src/parsing/parse-info.h"
#include "src/parsing/scanner.h"
#include "src/parsing/token.h"
#include "src/regexp/regexp.h"
#include "src/utils/pointer-with-payload.h"
#include "src/zone/zone-chunk-list.h"

namespace v8 {
namespace internal {

class PreParserIdentifier;

enum FunctionNameValidity {
  kFunctionNameIsStrictReserved,
  kSkipFunctionNameCheck,
  kFunctionNameValidityUnknown
};

enum AllowLabelledFunctionStatement {
  kAllowLabelledFunctionStatement,
  kDisallowLabelledFunctionStatement,
};

enum ParsingArrowHeadFlag { kCertainlyNotArrowHead, kMaybeArrowHead };

enum class ParseFunctionFlag : uint8_t {
  kIsNormal = 0,
  kIsGenerator = 1 << 0,
  kIsAsync = 1 << 1
};

using ParseFunctionFlags = base::Flags<ParseFunctionFlag>;

struct FormalParametersBase {
  explicit FormalParametersBase(DeclarationScope* scope) : scope(scope) {}

  int num_parameters() const {
    // Don't include the rest parameter into the function's formal parameter
    // count (esp. the SharedFunctionInfo::internal_formal_parameter_count,
    // which says whether we need to create an arguments adaptor frame).
    return arity - has_rest;
  }

  void UpdateArityAndFunctionLength(bool is_optional, bool is_rest) {
    if (!is_optional && !is_rest && function_length == arity) {
      ++function_length;
    }
    ++arity;
  }

  DeclarationScope* scope;
  bool has_rest = false;
  bool is_simple = true;
  int function_length = 0;
  int arity = 0;
};

// Stack-allocated scope to collect source ranges from the parser.
class V8_NODISCARD SourceRangeScope final {
 public:
  SourceRangeScope(const Scanner* scanner, SourceRange* range)
      : scanner_(scanner), range_(range) {
    range_->start = scanner->peek_location().beg_pos;
    DCHECK_NE(range_->start, kNoSourcePosition);
    DCHECK_EQ(range_->end, kNoSourcePosition);
  }

  ~SourceRangeScope() {
    DCHECK_EQ(kNoSourcePosition, range_->end);
    range_->end = scanner_->location().end_pos;
    DCHECK_NE(range_->end, kNoSourcePosition);
  }

 private:
  const Scanner* scanner_;
  SourceRange* range_;

  DISALLOW_IMPLICIT_CONSTRUCTORS(SourceRangeScope);
};

// ----------------------------------------------------------------------------
// The RETURN_IF_PARSE_ERROR macro is a convenient macro to enforce error
// handling for functions that may fail (by returning if there was an parser
// error).
//
// Usage:
//     foo = ParseFoo(); // may fail
//     RETURN_IF_PARSE_ERROR
//
//     SAFE_USE(foo);

#define RETURN_IF_PARSE_ERROR \
  if (has_error()) return impl()->NullStatement();

// Common base class template shared between parser and pre-parser.
// The Impl parameter is the actual class of the parser/pre-parser,
// following the Curiously Recurring Template Pattern (CRTP).
// The structure of the parser objects is roughly the following:
//
//   // A structure template containing type definitions, needed to
//   // avoid a cyclic dependency.
//   template <typename Impl>
//   struct ParserTypes;
//
//   // The parser base object, which should just implement pure
//   // parser behavior.  The Impl parameter is the actual derived
//   // class (according to CRTP), which implements impure parser
//   // behavior.
//   template <typename Impl>
//   class ParserBase { ... };
//
//   // And then, for each parser variant (e.g., parser, preparser, etc):
//   class Parser;
//
//   template <>
//   class ParserTypes<Parser> { ... };
//
//   class Parser : public ParserBase<Parser> { ... };
//
// The parser base object implements pure parsing, according to the
// language grammar.  Different parser implementations may exhibit
// different parser-driven behavior that is not considered as pure
// parsing, e.g., early error detection and reporting, AST generation, etc.

// The ParserTypes structure encapsulates the differences in the
// types used in parsing methods.  E.g., Parser methods use Expression*
// and PreParser methods use PreParserExpression.  For any given parser
// implementation class Impl, it is expected to contain the following typedefs:
//
// template <>
// struct ParserTypes<Impl> {
//   // Synonyms for ParserBase<Impl> and Impl, respectively.
//   typedef Base;
//   typedef Impl;
//   // Return types for traversing functions.
//   typedef Identifier;
//   typedef Expression;
//   typedef FunctionLiteral;
//   typedef ObjectLiteralProperty;
//   typedef ClassLiteralProperty;
//   typedef ExpressionList;
//   typedef ObjectPropertyList;
//   typedef ClassPropertyList;
//   typedef FormalParameters;
//   typedef Statement;
//   typedef StatementList;
//   typedef Block;
//   typedef BreakableStatement;
//   typedef ForStatement;
//   typedef IterationStatement;
//   // For constructing objects returned by the traversing functions.
//   typedef Factory;
//   // For other implementation-specific tasks.
//   typedef Target;
//   typedef TargetScope;
// };

template <typename Impl>
struct ParserTypes;

enum class ParsePropertyKind : uint8_t {
  kAccessorGetter,
  kAccessorSetter,
  kValue,
  kShorthand,
  kAssign,
  kMethod,
  kClassField,
  kShorthandOrClassField,
  kSpread,
  kNotSet
};

template <typename Impl>
class ParserBase {
 public:
  // Shorten type names defined by ParserTypes<Impl>.
  using Types = ParserTypes<Impl>;
  using ExpressionScope = typename v8::internal::ExpressionScope<Types>;
  using ExpressionParsingScope =
      typename v8::internal::ExpressionParsingScope<Types>;
  using AccumulationScope = typename v8::internal::AccumulationScope<Types>;
  using ArrowHeadParsingScope =
      typename v8::internal::ArrowHeadParsingScope<Types>;
  using VariableDeclarationParsingScope =
      typename v8::internal::VariableDeclarationParsingScope<Types>;
  using ParameterDeclarationParsingScope =
      typename v8::internal::ParameterDeclarationParsingScope<Types>;

  // Return types for traversing functions.
  using BlockT = typename Types::Block;
  using BreakableStatementT = typename Types::BreakableStatement;
  using ClassLiteralPropertyT = typename Types::ClassLiteralProperty;
  using ClassPropertyListT = typename Types::ClassPropertyList;
  using ClassStaticElementListT = typename Types::ClassStaticElementList;
  using ExpressionT = typename Types::Expression;
  using ExpressionListT = typename Types::ExpressionList;
  using FormalParametersT = typename Types::FormalParameters;
  using ForStatementT = typename Types::ForStatement;
  using FunctionLiteralT = typename Types::FunctionLiteral;
  using IdentifierT = typename Types::Identifier;
  using IterationStatementT = typename Types::IterationStatement;
  using ObjectLiteralPropertyT = typename Types::ObjectLiteralProperty;
  using ObjectPropertyListT = typename Types::ObjectPropertyList;
  using StatementT = typename Types::Statement;
  using StatementListT = typename Types::StatementList;
  using SuspendExpressionT = typename Types::Suspend;
  // For constructing objects returned by the traversing functions.
  using FactoryT = typename Types::Factory;
  // Other implementation-specific tasks.
  using FuncNameInferrer = typename Types::FuncNameInferrer;
  using FuncNameInferrerState = typename Types::FuncNameInferrer::State;
  using SourceRange = typename Types::SourceRange;
  using SourceRangeScope = typename Types::SourceRangeScope;

  // All implementation-specific methods must be called through this.
  Impl* impl() { return static_cast<Impl*>(this); }
  const Impl* impl() const { return static_cast<const Impl*>(this); }

  ParserBase(Zone* zone, Scanner* scanner, uintptr_t stack_limit,
             AstValueFactory* ast_value_factory,
             PendingCompilationErrorHandler* pending_error_handler,
             RuntimeCallStats* runtime_call_stats, Logger* logger,
             UnoptimizedCompileFlags flags, bool parsing_on_main_thread)
      : scope_(nullptr),
        original_scope_(nullptr),
        function_state_(nullptr),
        fni_(ast_value_factory),
        ast_value_factory_(ast_value_factory),
        ast_node_factory_(ast_value_factory, zone),
        runtime_call_stats_(runtime_call_stats),
        logger_(logger),
        parsing_on_main_thread_(parsing_on_main_thread),
        stack_limit_(stack_limit),
        pending_error_handler_(pending_error_handler),
        zone_(zone),
        expression_scope_(nullptr),
        scanner_(scanner),
        flags_(flags),
        function_literal_id_(0),
        default_eager_compile_hint_(FunctionLiteral::kShouldLazyCompile) {
    pointer_buffer_.reserve(32);
    variable_buffer_.reserve(32);
  }

  const UnoptimizedCompileFlags& flags() const { return flags_; }

  bool allow_eval_cache() const { return allow_eval_cache_; }
  void set_allow_eval_cache(bool allow) { allow_eval_cache_ = allow; }

  V8_INLINE bool has_error() const { return scanner()->has_parser_error(); }

  uintptr_t stack_limit() const { return stack_limit_; }

  void set_stack_limit(uintptr_t stack_limit) { stack_limit_ = stack_limit; }

  void set_default_eager_compile_hint(
      FunctionLiteral::EagerCompileHint eager_compile_hint) {
    default_eager_compile_hint_ = eager_compile_hint;
  }

  FunctionLiteral::EagerCompileHint default_eager_compile_hint() const {
    return default_eager_compile_hint_;
  }

  int loop_nesting_depth() const {
    return function_state_->loop_nesting_depth();
  }
  int GetNextFunctionLiteralId() { return ++function_literal_id_; }
  int GetLastFunctionLiteralId() const { return function_literal_id_; }

  void SkipFunctionLiterals(int delta) { function_literal_id_ += delta; }

  void ResetFunctionLiteralId() { function_literal_id_ = 0; }

  // The Zone where the parsing outputs are stored.
  Zone* main_zone() const { return ast_value_factory()->single_parse_zone(); }

  // The current Zone, which might be the main zone or a temporary Zone.
  Zone* zone() const { return zone_; }

 protected:
  friend class v8::internal::ExpressionScope<ParserTypes<Impl>>;
  friend class v8::internal::ExpressionParsingScope<ParserTypes<Impl>>;
  friend class v8::internal::ArrowHeadParsingScope<ParserTypes<Impl>>;

  enum VariableDeclarationContext {
    kStatementListItem,
    kStatement,
    kForStatement
  };

  class ClassLiteralChecker;

  // ---------------------------------------------------------------------------
  // BlockState and FunctionState implement the parser's scope stack.
  // The parser's current scope is in scope_. BlockState and FunctionState
  // constructors push on the scope stack and the destructors pop. They are also
  // used to hold the parser's per-funcion state.
  class BlockState {
   public:
    BlockState(Scope** scope_stack, Scope* scope)
        : scope_stack_(scope_stack), outer_scope_(*scope_stack) {
      *scope_stack_ = scope;
    }

    BlockState(Zone* zone, Scope** scope_stack)
        : BlockState(scope_stack,
                     zone->New<Scope>(zone, *scope_stack, BLOCK_SCOPE)) {}

    ~BlockState() { *scope_stack_ = outer_scope_; }

   private:
    Scope** const scope_stack_;
    Scope* const outer_scope_;
  };

  // ---------------------------------------------------------------------------
  // Target is a support class to facilitate manipulation of the
  // Parser's target_stack_ (the stack of potential 'break' and
  // 'continue' statement targets). Upon construction, a new target is
  // added; it is removed upon destruction.

  // |labels| is a list of all labels that can be used as a target for break.
  // |own_labels| is a list of all labels that an iteration statement is
  // directly prefixed with, i.e. all the labels that a continue statement in
  // the body can use to continue this iteration statement. This is always a
  // subset of |labels|.
  //
  // Example: "l1: { l2: if (b) l3: l4: for (;;) s }"
  // labels() of the Block will be l1.
  // labels() of the ForStatement will be l2, l3, l4.
  // own_labels() of the ForStatement will be l3, l4.
  class Target {
   public:
    enum TargetType { TARGET_FOR_ANONYMOUS, TARGET_FOR_NAMED_ONLY };

    Target(ParserBase* parser, BreakableStatementT statement,
           ZonePtrList<const AstRawString>* labels,
           ZonePtrList<const AstRawString>* own_labels, TargetType target_type)
        : stack_(parser->function_state_->target_stack_address()),
          statement_(statement),
          labels_(labels),
          own_labels_(own_labels),
          target_type_(target_type),
          previous_(*stack_) {
      DCHECK_IMPLIES(Impl::IsIterationStatement(statement_),
                     target_type == Target::TARGET_FOR_ANONYMOUS);
      DCHECK_IMPLIES(!Impl::IsIterationStatement(statement_),
                     own_labels == nullptr);
      *stack_ = this;
    }

    ~Target() { *stack_ = previous_; }

    const Target* previous() const { return previous_; }
    const BreakableStatementT statement() const { return statement_; }
    const ZonePtrList<const AstRawString>* labels() const { return labels_; }
    const ZonePtrList<const AstRawString>* own_labels() const {
      return own_labels_;
    }
    bool is_iteration() const { return Impl::IsIterationStatement(statement_); }
    bool is_target_for_anonymous() const {
      return target_type_ == TARGET_FOR_ANONYMOUS;
    }

   private:
    Target** const stack_;
    const BreakableStatementT statement_;
    const ZonePtrList<const AstRawString>* const labels_;
    const ZonePtrList<const AstRawString>* const own_labels_;
    const TargetType target_type_;
    Target* const previous_;
  };

  Target* target_stack() { return *function_state_->target_stack_address(); }

  BreakableStatementT LookupBreakTarget(IdentifierT label) {
    bool anonymous = impl()->IsNull(label);
    for (const Target* t = target_stack(); t != nullptr; t = t->previous()) {
      if ((anonymous && t->is_target_for_anonymous()) ||
          (!anonymous &&
           ContainsLabel(t->labels(),
                         impl()->GetRawNameFromIdentifier(label)))) {
        return t->statement();
      }
    }
    return impl()->NullStatement();
  }

  IterationStatementT LookupContinueTarget(IdentifierT label) {
    bool anonymous = impl()->IsNull(label);
    for (const Target* t = target_stack(); t != nullptr; t = t->previous()) {
      if (!t->is_iteration()) continue;

      DCHECK(t->is_target_for_anonymous());
      if (anonymous || ContainsLabel(t->own_labels(),
                                     impl()->GetRawNameFromIdentifier(label))) {
        return impl()->AsIterationStatement(t->statement());
      }
    }
    return impl()->NullStatement();
  }

  class FunctionState final : public BlockState {
   public:
    FunctionState(FunctionState** function_state_stack, Scope** scope_stack,
                  DeclarationScope* scope);
    ~FunctionState();

    DeclarationScope* scope() const { return scope_->AsDeclarationScope(); }

    void AddProperty() { expected_property_count_++; }
    int expected_property_count() { return expected_property_count_; }

    void DisableOptimization(BailoutReason reason) {
      dont_optimize_reason_ = reason;
    }
    BailoutReason dont_optimize_reason() { return dont_optimize_reason_; }

    void AddSuspend() { suspend_count_++; }
    int suspend_count() const { return suspend_count_; }
    bool CanSuspend() const { return suspend_count_ > 0; }

    FunctionKind kind() const { return scope()->function_kind(); }

    bool next_function_is_likely_called() const {
      return next_function_is_likely_called_;
    }

    bool previous_function_was_likely_called() const {
      return previous_function_was_likely_called_;
    }

    void set_next_function_is_likely_called() {
      next_function_is_likely_called_ = !FLAG_max_lazy;
    }

    void RecordFunctionOrEvalCall() { contains_function_or_eval_ = true; }
    bool contains_function_or_eval() const {
      return contains_function_or_eval_;
    }

    class V8_NODISCARD FunctionOrEvalRecordingScope {
     public:
      explicit FunctionOrEvalRecordingScope(FunctionState* state)
          : state_and_prev_value_(state, state->contains_function_or_eval_) {
        state->contains_function_or_eval_ = false;
      }
      ~FunctionOrEvalRecordingScope() {
        bool found = state_and_prev_value_->contains_function_or_eval_;
        if (!found) {
          state_and_prev_value_->contains_function_or_eval_ =
              state_and_prev_value_.GetPayload();
        }
      }

     private:
      PointerWithPayload<FunctionState, bool, 1> state_and_prev_value_;
    };

    class V8_NODISCARD LoopScope final {
     public:
      explicit LoopScope(FunctionState* function_state)
          : function_state_(function_state) {
        function_state_->loop_nesting_depth_++;
      }

      ~LoopScope() { function_state_->loop_nesting_depth_--; }

     private:
      FunctionState* function_state_;
    };

    int loop_nesting_depth() const { return loop_nesting_depth_; }

    Target** target_stack_address() { return &target_stack_; }

   private:
    // Properties count estimation.
    int expected_property_count_;

    // How many suspends are needed for this function.
    int suspend_count_;

    // How deeply nested we currently are in this function.
    int loop_nesting_depth_ = 0;

    FunctionState** function_state_stack_;
    FunctionState* outer_function_state_;
    DeclarationScope* scope_;
    Target* target_stack_ = nullptr;  // for break, continue statements

    // A reason, if any, why this function should not be optimized.
    BailoutReason dont_optimize_reason_;

    // Record whether the next (=== immediately following) function literal is
    // preceded by a parenthesis / exclamation mark. Also record the previous
    // state.
    // These are managed by the FunctionState constructor; the caller may only
    // call set_next_function_is_likely_called.
    bool next_function_is_likely_called_;
    bool previous_function_was_likely_called_;

    // Track if a function or eval occurs within this FunctionState
    bool contains_function_or_eval_;

    friend Impl;
  };

  struct DeclarationDescriptor {
    VariableMode mode;
    VariableKind kind;
    int declaration_pos;
    int initialization_pos;
  };

  struct DeclarationParsingResult {
    struct Declaration {
      Declaration(ExpressionT pattern, ExpressionT initializer)
          : pattern(pattern), initializer(initializer) {
        DCHECK_IMPLIES(Impl::IsNull(pattern), Impl::IsNull(initializer));
      }

      ExpressionT pattern;
      ExpressionT initializer;
      int value_beg_pos = kNoSourcePosition;
    };

    DeclarationParsingResult()
        : first_initializer_loc(Scanner::Location::invalid()),
          bindings_loc(Scanner::Location::invalid()) {}

    DeclarationDescriptor descriptor;
    std::vector<Declaration> declarations;
    Scanner::Location first_initializer_loc;
    Scanner::Location bindings_loc;
  };

  struct CatchInfo {
   public:
    explicit CatchInfo(ParserBase* parser)
        : pattern(parser->impl()->NullExpression()),
          variable(nullptr),
          scope(nullptr) {}
    ExpressionT pattern;
    Variable* variable;
    Scope* scope;
  };

  struct ForInfo {
   public:
    explicit ForInfo(ParserBase* parser)
        : bound_names(1, parser->zone()),
          mode(ForEachStatement::ENUMERATE),
          position(kNoSourcePosition),
          parsing_result() {}
    ZonePtrList<const AstRawString> bound_names;
    ForEachStatement::VisitMode mode;
    int position;
    DeclarationParsingResult parsing_result;
  };

  struct ClassInfo {
   public:
    explicit ClassInfo(ParserBase* parser)
        : extends(parser->impl()->NullExpression()),
          public_members(parser->impl()->NewClassPropertyList(4)),
          private_members(parser->impl()->NewClassPropertyList(4)),
          static_elements(parser->impl()->NewClassStaticElementList(4)),
          instance_fields(parser->impl()->NewClassPropertyList(4)),
          constructor(parser->impl()->NullExpression()),
          has_seen_constructor(false),
          has_static_computed_names(false),
          has_static_elements(false),
          has_static_private_methods(false),
          has_static_blocks(false),
          has_instance_members(false),
          requires_brand(false),
          is_anonymous(false),
          has_private_methods(false),
          static_elements_scope(nullptr),
          instance_members_scope(nullptr),
          computed_field_count(0) {}
    ExpressionT extends;
    ClassPropertyListT public_members;
    ClassPropertyListT private_members;
    ClassStaticElementListT static_elements;
    ClassPropertyListT instance_fields;
    FunctionLiteralT constructor;

    bool has_seen_constructor;
    bool has_static_computed_names;
    bool has_static_elements;
    bool has_static_private_methods;
    bool has_static_blocks;
    bool has_instance_members;
    bool requires_brand;
    bool is_anonymous;
    bool has_private_methods;
    DeclarationScope* static_elements_scope;
    DeclarationScope* instance_members_scope;
    int computed_field_count;
    Variable* home_object_variable = nullptr;
    Variable* static_home_object_variable = nullptr;
  };

  enum class PropertyPosition { kObjectLiteral, kClassLiteral };
  struct ParsePropertyInfo {
   public:
    explicit ParsePropertyInfo(ParserBase* parser,
                               AccumulationScope* accumulation_scope = nullptr)
        : accumulation_scope(accumulation_scope),
          name(parser->impl()->NullIdentifier()),
          position(PropertyPosition::kClassLiteral),
          function_flags(ParseFunctionFlag::kIsNormal),
          kind(ParsePropertyKind::kNotSet),
          is_computed_name(false),
          is_private(false),
          is_static(false),
          is_rest(false) {}

    bool ParsePropertyKindFromToken(Token::Value token) {
      // This returns true, setting the property kind, iff the given token is
      // one which must occur after a property name, indicating that the
      // previous token was in fact a name and not a modifier (like the "get" in
      // "get x").
      switch (token) {
        case Token::COLON:
          kind = ParsePropertyKind::kValue;
          return true;
        case Token::COMMA:
          kind = ParsePropertyKind::kShorthand;
          return true;
        case Token::RBRACE:
          kind = ParsePropertyKind::kShorthandOrClassField;
          return true;
        case Token::ASSIGN:
          kind = ParsePropertyKind::kAssign;
          return true;
        case Token::LPAREN:
          kind = ParsePropertyKind::kMethod;
          return true;
        case Token::MUL:
        case Token::SEMICOLON:
          kind = ParsePropertyKind::kClassField;
          return true;
        default:
          break;
      }
      return false;
    }

    AccumulationScope* accumulation_scope;
    IdentifierT name;
    PropertyPosition position;
    ParseFunctionFlags function_flags;
    ParsePropertyKind kind;
    bool is_computed_name;
    bool is_private;
    bool is_static;
    bool is_rest;
  };

  void DeclareLabel(ZonePtrList<const AstRawString>** labels,
                    ZonePtrList<const AstRawString>** own_labels,
                    const AstRawString* label) {
    if (ContainsLabel(*labels, label) || TargetStackContainsLabel(label)) {
      ReportMessage(MessageTemplate::kLabelRedeclaration, label);
      return;
    }

    // Add {label} to both {labels} and {own_labels}.
    if (*labels == nullptr) {
      DCHECK_NULL(*own_labels);
      *labels =
          zone()->template New<ZonePtrList<const AstRawString>>(1, zone());
      *own_labels =
          zone()->template New<ZonePtrList<const AstRawString>>(1, zone());
    } else {
      if (*own_labels == nullptr) {
        *own_labels =
            zone()->template New<ZonePtrList<const AstRawString>>(1, zone());
      }
    }
    (*labels)->Add(label, zone());
    (*own_labels)->Add(label, zone());
  }

  bool ContainsLabel(const ZonePtrList<const AstRawString>* labels,
                     const AstRawString* label) {
    DCHECK_NOT_NULL(label);
    if (labels != nullptr) {
      for (int i = labels->length(); i-- > 0;) {
        if (labels->at(i) == label) return true;
      }
    }
    return false;
  }

  bool TargetStackContainsLabel(const AstRawString* label) {
    for (const Target* t = target_stack(); t != nullptr; t = t->previous()) {
      if (ContainsLabel(t->labels(), label)) return true;
    }
    return false;
  }

  ClassLiteralProperty::Kind ClassPropertyKindFor(ParsePropertyKind kind) {
    switch (kind) {
      case ParsePropertyKind::kAccessorGetter:
        return ClassLiteralProperty::GETTER;
      case ParsePropertyKind::kAccessorSetter:
        return ClassLiteralProperty::SETTER;
      case ParsePropertyKind::kMethod:
        return ClassLiteralProperty::METHOD;
      case ParsePropertyKind::kClassField:
        return ClassLiteralProperty::FIELD;
      default:
        // Only returns for deterministic kinds
        UNREACHABLE();
    }
  }

  VariableMode GetVariableMode(ClassLiteralProperty::Kind kind) {
    switch (kind) {
      case ClassLiteralProperty::Kind::FIELD:
        return VariableMode::kConst;
      case ClassLiteralProperty::Kind::METHOD:
        return VariableMode::kPrivateMethod;
      case ClassLiteralProperty::Kind::GETTER:
        return VariableMode::kPrivateGetterOnly;
      case ClassLiteralProperty::Kind::SETTER:
        return VariableMode::kPrivateSetterOnly;
    }
  }

  const AstRawString* ClassFieldVariableName(AstValueFactory* ast_value_factory,
                                             int index) {
    std::string name = ".class-field-" + std::to_string(index);
    return ast_value_factory->GetOneByteString(name.c_str());
  }

  DeclarationScope* NewScriptScope(REPLMode repl_mode) const {
    return zone()->template New<DeclarationScope>(zone(), ast_value_factory(),
                                                  repl_mode);
  }

  DeclarationScope* NewVarblockScope() const {
    return zone()->template New<DeclarationScope>(zone(), scope(), BLOCK_SCOPE);
  }

  ModuleScope* NewModuleScope(DeclarationScope* parent) const {
    return zone()->template New<ModuleScope>(parent, ast_value_factory());
  }

  DeclarationScope* NewEvalScope(Scope* parent) const {
    return zone()->template New<DeclarationScope>(zone(), parent, EVAL_SCOPE);
  }

  ClassScope* NewClassScope(Scope* parent, bool is_anonymous) const {
    return zone()->template New<ClassScope>(zone(), parent, is_anonymous);
  }

  Scope* NewBlockScopeForObjectLiteral() {
    Scope* scope = NewScope(BLOCK_SCOPE);
    scope->set_is_block_scope_for_object_literal();
    return scope;
  }

  Scope* NewScope(ScopeType scope_type) const {
    return NewScopeWithParent(scope(), scope_type);
  }

  // This constructor should only be used when absolutely necessary. Most scopes
  // should automatically use scope() as parent, and be fine with
  // NewScope(ScopeType) above.
  Scope* NewScopeWithParent(Scope* parent, ScopeType scope_type) const {
    // Must always use the specific constructors for the blocklisted scope
    // types.
    DCHECK_NE(FUNCTION_SCOPE, scope_type);
    DCHECK_NE(SCRIPT_SCOPE, scope_type);
    DCHECK_NE(MODULE_SCOPE, scope_type);
    DCHECK_NOT_NULL(parent);
    return zone()->template New<Scope>(zone(), parent, scope_type);
  }

  // Creates a function scope that always allocates in zone(). The function
  // scope itself is either allocated in zone() or in target_zone if one is
  // passed in.
  DeclarationScope* NewFunctionScope(FunctionKind kind,
                                     Zone* parse_zone = nullptr) const {
    DCHECK(ast_value_factory());
    if (parse_zone == nullptr) parse_zone = zone();
    DeclarationScope* result = zone()->template New<DeclarationScope>(
        parse_zone, scope(), FUNCTION_SCOPE, kind);

    // Record presence of an inner function scope
    function_state_->RecordFunctionOrEvalCall();

    // TODO(verwaest): Move into the DeclarationScope constructor.
    if (!IsArrowFunction(kind)) {
      result->DeclareDefaultFunctionVariables(ast_value_factory());
    }
    return result;
  }

  V8_INLINE DeclarationScope* GetDeclarationScope() const {
    return scope()->GetDeclarationScope();
  }
  V8_INLINE DeclarationScope* GetClosureScope() const {
    return scope()->GetClosureScope();
  }

  VariableProxy* NewRawVariable(const AstRawString* name, int pos) {
    return factory()->ast_node_factory()->NewVariableProxy(
        name, NORMAL_VARIABLE, pos);
  }

  VariableProxy* NewUnresolved(const AstRawString* name) {
    return scope()->NewUnresolved(factory()->ast_node_factory(), name,
                                  scanner()->location().beg_pos);
  }

  VariableProxy* NewUnresolved(const AstRawString* name, int begin_pos,
                               VariableKind kind = NORMAL_VARIABLE) {
    return scope()->NewUnresolved(factory()->ast_node_factory(), name,
                                  begin_pos, kind);
  }

  Scanner* scanner() const { return scanner_; }
  AstValueFactory* ast_value_factory() const { return ast_value_factory_; }
  int position() const { return scanner_->location().beg_pos; }
  int peek_position() const { return scanner_->peek_location().beg_pos; }
  int end_position() const { return scanner_->location().end_pos; }
  int peek_end_position() const { return scanner_->peek_location().end_pos; }
  bool stack_overflow() const {
    return pending_error_handler()->stack_overflow();
  }
  void set_stack_overflow() {
    scanner_->set_parser_error();
    pending_error_handler()->set_stack_overflow();
  }
  void CheckStackOverflow() {
    // Any further calls to Next or peek will return the illegal token.
    if (GetCurrentStackPosition() < stack_limit_) set_stack_overflow();
  }

  V8_INLINE Token::Value peek() { return scanner()->peek(); }

  // Returns the position past the following semicolon (if it exists), and the
  // position past the end of the current token otherwise.
  int PositionAfterSemicolon() {
    return (peek() == Token::SEMICOLON) ? peek_end_position() : end_position();
  }

  V8_INLINE Token::Value PeekAhead() { return scanner()->PeekAhead(); }

  V8_INLINE Token::Value Next() { return scanner()->Next(); }

  V8_INLINE void Consume(Token::Value token) {
    Token::Value next = scanner()->Next();
    USE(next);
    USE(token);
    DCHECK_IMPLIES(!has_error(), next == token);
  }

  V8_INLINE bool Check(Token::Value token) {
    Token::Value next = scanner()->peek();
    if (next == token) {
      Consume(next);
      return true;
    }
    return false;
  }

  void Expect(Token::Value token) {
    Token::Value next = Next();
    if (V8_UNLIKELY(next != token)) {
      ReportUnexpectedToken(next);
    }
  }

  void ExpectSemicolon() {
    // Check for automatic semicolon insertion according to
    // the rules given in ECMA-262, section 7.9, page 21.
    Token::Value tok = peek();
    if (V8_LIKELY(tok == Token::SEMICOLON)) {
      Next();
      return;
    }
    if (V8_LIKELY(scanner()->HasLineTerminatorBeforeNext() ||
                  Token::IsAutoSemicolon(tok))) {
      return;
    }

    if (scanner()->current_token() == Token::AWAIT && !is_async_function()) {
      if (flags().parsing_while_debugging() == ParsingWhileDebugging::kYes) {
        ReportMessageAt(scanner()->location(),
                        MessageTemplate::kAwaitNotInDebugEvaluate);
      } else {
        ReportMessageAt(scanner()->location(),
                        MessageTemplate::kAwaitNotInAsyncContext);
      }
      return;
    }

    ReportUnexpectedToken(Next());
  }

  bool peek_any_identifier() { return Token::IsAnyIdentifier(peek()); }

  bool PeekContextualKeyword(const AstRawString* name) {
    return peek() == Token::IDENTIFIER &&
           !scanner()->next_literal_contains_escapes() &&
           scanner()->NextSymbol(ast_value_factory()) == name;
  }

  bool CheckContextualKeyword(const AstRawString* name) {
    if (PeekContextualKeyword(name)) {
      Consume(Token::IDENTIFIER);
      return true;
    }
    return false;
  }

  void ExpectContextualKeyword(const AstRawString* name,
                               const char* fullname = nullptr, int pos = -1) {
    Expect(Token::IDENTIFIER);
    if (V8_UNLIKELY(scanner()->CurrentSymbol(ast_value_factory()) != name)) {
      ReportUnexpectedToken(scanner()->current_token());
    }
    if (V8_UNLIKELY(scanner()->literal_contains_escapes())) {
      const char* full = fullname == nullptr
                             ? reinterpret_cast<const char*>(name->raw_data())
                             : fullname;
      int start = pos == -1 ? position() : pos;
      impl()->ReportMessageAt(Scanner::Location(start, end_position()),
                              MessageTemplate::kInvalidEscapedMetaProperty,
                              full);
    }
  }

  bool CheckInOrOf(ForEachStatement::VisitMode* visit_mode) {
    if (Check(Token::IN)) {
      *visit_mode = ForEachStatement::ENUMERATE;
      return true;
    } else if (CheckContextualKeyword(ast_value_factory()->of_string())) {
      *visit_mode = ForEachStatement::ITERATE;
      return true;
    }
    return false;
  }

  bool PeekInOrOf() {
    return peek() == Token::IN ||
           PeekContextualKeyword(ast_value_factory()->of_string());
  }

  // Checks whether an octal literal was last seen between beg_pos and end_pos.
  // Only called for strict mode strings.
  void CheckStrictOctalLiteral(int beg_pos, int end_pos) {
    Scanner::Location octal = scanner()->octal_position();
    if (octal.IsValid() && beg_pos <= octal.beg_pos &&
        octal.end_pos <= end_pos) {
      MessageTemplate message = scanner()->octal_message();
      DCHECK_NE(message, MessageTemplate::kNone);
      impl()->ReportMessageAt(octal, message);
      scanner()->clear_octal_position();
      if (message == MessageTemplate::kStrictDecimalWithLeadingZero) {
        impl()->CountUsage(v8::Isolate::kDecimalWithLeadingZeroInStrictMode);
      }
    }
  }

  // Checks if an octal literal or an invalid hex or unicode escape sequence
  // appears in the current template literal token. In the presence of such,
  // either returns false or reports an error, depending on should_throw.
  // Otherwise returns true.
  inline bool CheckTemplateEscapes(bool should_throw) {
    DCHECK(Token::IsTemplate(scanner()->current_token()));
    if (!scanner()->has_invalid_template_escape()) return true;

    // Handle error case(s)
    if (should_throw) {
      impl()->ReportMessageAt(scanner()->invalid_template_escape_location(),
                              scanner()->invalid_template_escape_message());
    }
    scanner()->clear_invalid_template_escape_message();
    return should_throw;
  }

  ExpressionT ParsePossibleDestructuringSubPattern(AccumulationScope* scope);
  void ClassifyParameter(IdentifierT parameter, int beg_pos, int end_pos);
  void ClassifyArrowParameter(AccumulationScope* accumulation_scope,
                              int position, ExpressionT parameter);

  // Checking the name of a function literal. This has to be done after parsing
  // the function, since the function can declare itself strict.
  void CheckFunctionName(LanguageMode language_mode, IdentifierT function_name,
                         FunctionNameValidity function_name_validity,
                         const Scanner::Location& function_name_loc) {
    if (impl()->IsNull(function_name)) return;
    if (function_name_validity == kSkipFunctionNameCheck) return;
    // The function name needs to be checked in strict mode.
    if (is_sloppy(language_mode)) return;

    if (impl()->IsEvalOrArguments(function_name)) {
      impl()->ReportMessageAt(function_name_loc,
                              MessageTemplate::kStrictEvalArguments);
      return;
    }
    if (function_name_validity == kFunctionNameIsStrictReserved) {
      impl()->ReportMessageAt(function_name_loc,
                              MessageTemplate::kUnexpectedStrictReserved);
      return;
    }
  }

  typename Types::Factory* factory() { return &ast_node_factory_; }

  DeclarationScope* GetReceiverScope() const {
    return scope()->GetReceiverScope();
  }
  LanguageMode language_mode() { return scope()->language_mode(); }
  void RaiseLanguageMode(LanguageMode mode) {
    LanguageMode old = scope()->language_mode();
    impl()->SetLanguageMode(scope(), old > mode ? old : mode);
  }
  bool is_generator() const {
    return IsGeneratorFunction(function_state_->kind());
  }
  bool is_async_function() const {
    return IsAsyncFunction(function_state_->kind());
  }
  bool is_async_generator() const {
    return IsAsyncGeneratorFunction(function_state_->kind());
  }
  bool is_resumable() const {
    return IsResumableFunction(function_state_->kind());
  }
  bool is_await_allowed() const {
    return is_async_function() || IsModule(function_state_->kind());
  }
  bool is_await_as_identifier_disallowed() {
    return flags().is_module() ||
           IsAwaitAsIdentifierDisallowed(function_state_->kind());
  }
  const PendingCompilationErrorHandler* pending_error_handler() const {
    return pending_error_handler_;
  }
  PendingCompilationErrorHandler* pending_error_handler() {
    return pending_error_handler_;
  }

  // Report syntax errors.
  template <typename... Ts>
  V8_NOINLINE void ReportMessage(MessageTemplate message, const Ts&... args) {
    ReportMessageAt(scanner()->location(), message, args...);
  }

  template <typename... Ts>
  V8_NOINLINE void ReportMessageAt(Scanner::Location source_location,
                                   MessageTemplate message, const Ts&... args) {
    impl()->pending_error_handler()->ReportMessageAt(
        source_location.beg_pos, source_location.end_pos, message, args...);
    scanner()->set_parser_error();
  }

  V8_NOINLINE void ReportMessageAt(Scanner::Location source_location,
                                   MessageTemplate message,
                                   const PreParserIdentifier& arg0) {
    ReportMessageAt(source_location, message,
                    impl()->PreParserIdentifierToAstRawString(arg0));
  }

  V8_NOINLINE void ReportUnexpectedToken(Token::Value token);

  void ValidateFormalParameters(LanguageMode language_mode,
                                const FormalParametersT& parameters,
                                bool allow_duplicates) {
    if (!allow_duplicates) parameters.ValidateDuplicate(impl());
    if (is_strict(language_mode)) parameters.ValidateStrictMode(impl());
  }

  // Needs to be called if the reference needs to be available from the current
  // point. It causes the receiver to be context allocated if necessary.
  // Returns the receiver variable that we're referencing.
  V8_INLINE Variable* UseThis() {
    DeclarationScope* closure_scope = scope()->GetClosureScope();
    DeclarationScope* receiver_scope = closure_scope->GetReceiverScope();
    Variable* var = receiver_scope->receiver();
    var->set_is_used();
    if (closure_scope == receiver_scope) {
      // It's possible that we're parsing the head of an arrow function, in
      // which case we haven't realized yet that closure_scope !=
      // receiver_scope. Mark through the ExpressionScope for now.
      expression_scope()->RecordThisUse();
    } else {
      closure_scope->set_has_this_reference();
      var->ForceContextAllocation();
    }
    return var;
  }

  V8_INLINE IdentifierT ParseAndClassifyIdentifier(Token::Value token);

  // Similar logic to ParseAndClassifyIdentifier but the identifier is
  // already parsed in prop_info. Returns false if this is an invalid
  // identifier or an invalid use of the "arguments" keyword.
  V8_INLINE bool ClassifyPropertyIdentifier(Token::Value token,
                                            ParsePropertyInfo* prop_info);
  // Parses an identifier or a strict mode future reserved word. Allows passing
  // in function_kind for the case of parsing the identifier in a function
  // expression, where the relevant "function_kind" bit is of the function being
  // parsed, not the containing function.
  V8_INLINE IdentifierT ParseIdentifier(FunctionKind function_kind);
  V8_INLINE IdentifierT ParseIdentifier() {
    return ParseIdentifier(function_state_->kind());
  }
  // Same as above but additionally disallows 'eval' and 'arguments' in strict
  // mode.
  IdentifierT ParseNonRestrictedIdentifier();

  // This method should be used to ambiguously parse property names that can
  // become destructuring identifiers.
  V8_INLINE IdentifierT ParsePropertyName();

  ExpressionT ParsePropertyOrPrivatePropertyName();

  const AstRawString* GetNextSymbolForRegExpLiteral() const {
    return scanner()->NextSymbol(ast_value_factory());
  }
  bool ValidateRegExpLiteral(const AstRawString* pattern, RegExpFlags flags,
                             RegExpError* regexp_error);
  ExpressionT ParseRegExpLiteral();

  ExpressionT ParseBindingPattern();
  ExpressionT ParsePrimaryExpression();

  // Use when parsing an expression that is known to not be a pattern or part of
  // a pattern.
  V8_INLINE ExpressionT ParseExpression();
  V8_INLINE ExpressionT ParseAssignmentExpression();

  // These methods do not wrap the parsing of the expression inside a new
  // expression_scope; they use the outer expression_scope instead. They should
  // be used whenever we're parsing something with the "cover" grammar that
  // recognizes both patterns and non-patterns (which roughly corresponds to
  // what's inside the parentheses generated by the symbol
  // "CoverParenthesizedExpressionAndArrowParameterList" in the ES 2017
  // specification).
  ExpressionT ParseExpressionCoverGrammar();
  ExpressionT ParseAssignmentExpressionCoverGrammar();

  ExpressionT ParseArrowParametersWithRest(ExpressionListT* list,
                                           AccumulationScope* scope,
                                           int seen_variables);

  ExpressionT ParseArrayLiteral();

  inline static bool IsAccessor(ParsePropertyKind kind) {
    return base::IsInRange(kind, ParsePropertyKind::kAccessorGetter,
                           ParsePropertyKind::kAccessorSetter);
  }

  ExpressionT ParseProperty(ParsePropertyInfo* prop_info);
  ExpressionT ParseObjectLiteral();
  ClassLiteralPropertyT ParseClassPropertyDefinition(
      ClassInfo* class_info, ParsePropertyInfo* prop_info, bool has_extends);
  void CheckClassFieldName(IdentifierT name, bool is_static);
  void CheckClassMethodName(IdentifierT name, ParsePropertyKind type,
                            ParseFunctionFlags flags, bool is_static,
                            bool* has_seen_constructor);
  ExpressionT ParseMemberInitializer(ClassInfo* class_info, int beg_pos,
                                     bool is_static);
  BlockT ParseClassStaticBlock(ClassInfo* class_info);
  ObjectLiteralPropertyT ParseObjectPropertyDefinition(
      ParsePropertyInfo* prop_info, bool* has_seen_proto);
  void ParseArguments(
      ExpressionListT* args, bool* has_spread,
      ParsingArrowHeadFlag maybe_arrow = kCertainlyNotArrowHead);

  ExpressionT ParseYieldExpression();
  V8_INLINE ExpressionT ParseConditionalExpression();
  ExpressionT ParseConditionalContinuation(ExpressionT expression, int pos);
  ExpressionT ParseLogicalExpression();
  ExpressionT ParseCoalesceExpression(ExpressionT expression);
  ExpressionT ParseBinaryContinuation(ExpressionT x, int prec, int prec1);
  V8_INLINE ExpressionT ParseBinaryExpression(int prec);
  ExpressionT ParseUnaryOrPrefixExpression();
  ExpressionT ParseAwaitExpression();
  V8_INLINE ExpressionT ParseUnaryExpression();
  V8_INLINE ExpressionT ParsePostfixExpression();
  V8_NOINLINE ExpressionT ParsePostfixContinuation(ExpressionT expression,
                                                   int lhs_beg_pos);
  V8_INLINE ExpressionT ParseLeftHandSideExpression();
  ExpressionT ParseLeftHandSideContinuation(ExpressionT expression);
  ExpressionT ParseMemberWithPresentNewPrefixesExpression();
  ExpressionT ParseFunctionExpression();
  V8_INLINE ExpressionT ParseMemberExpression();
  V8_INLINE ExpressionT
  ParseMemberExpressionContinuation(ExpressionT expression) {
    if (!Token::IsMember(peek())) return expression;
    return DoParseMemberExpressionContinuation(expression);
  }
  ExpressionT DoParseMemberExpressionContinuation(ExpressionT expression);

  ExpressionT ParseArrowFunctionLiteral(const FormalParametersT& parameters);
  void ParseAsyncFunctionBody(Scope* scope, StatementListT* body);
  ExpressionT ParseAsyncFunctionLiteral();
  ExpressionT ParseClassExpression(Scope* outer_scope);
  ExpressionT ParseClassLiteral(Scope* outer_scope, IdentifierT name,
                                Scanner::Location class_name_location,
                                bool name_is_strict_reserved,
                                int class_token_pos);

  ExpressionT ParseTemplateLiteral(ExpressionT tag, int start, bool tagged);
  ExpressionT ParseSuperExpression();
  ExpressionT ParseImportExpressions();
  ExpressionT ParseNewTargetExpression();

  V8_INLINE void ParseFormalParameter(FormalParametersT* parameters);
  void ParseFormalParameterList(FormalParametersT* parameters);
  void CheckArityRestrictions(int param_count, FunctionKind function_type,
                              bool has_rest, int formals_start_pos,
                              int formals_end_pos);

  void ParseVariableDeclarations(VariableDeclarationContext var_context,
                                 DeclarationParsingResult* parsing_result,
                                 ZonePtrList<const AstRawString>* names);
  StatementT ParseAsyncFunctionDeclaration(
      ZonePtrList<const AstRawString>* names, bool default_export);
  StatementT ParseFunctionDeclaration();
  StatementT ParseHoistableDeclaration(ZonePtrList<const AstRawString>* names,
                                       bool default_export);
  StatementT ParseHoistableDeclaration(int pos, ParseFunctionFlags flags,
                                       ZonePtrList<const AstRawString>* names,
                                       bool default_export);
  StatementT ParseClassDeclaration(ZonePtrList<const AstRawString>* names,
                                   bool default_export);
  StatementT ParseNativeDeclaration();

  // Whether we're parsing a single-expression arrow function or something else.
  enum class FunctionBodyType { kExpression, kBlock };
  // Consumes the ending }.
  void ParseFunctionBody(StatementListT* body, IdentifierT function_name,
                         int pos, const FormalParametersT& parameters,
                         FunctionKind kind,
                         FunctionSyntaxKind function_syntax_kind,
                         FunctionBodyType body_type);

  // Check if the scope has conflicting var/let declarations from different
  // scopes. This covers for example
  //
  // function f() { { { var x; } let x; } }
  // function g() { { var x; let x; } }
  //
  // The var declarations are hoisted to the function scope, but originate from
  // a scope where the name has also been let bound or the var declaration is
  // hoisted over such a scope.
  void CheckConflictingVarDeclarations(DeclarationScope* scope) {
    if (has_error()) return;
    bool allowed_catch_binding_var_redeclaration = false;
    Declaration* decl = scope->CheckConflictingVarDeclarations(
        &allowed_catch_binding_var_redeclaration);
    if (allowed_catch_binding_var_redeclaration) {
      impl()->CountUsage(v8::Isolate::kVarRedeclaredCatchBinding);
    }
    if (decl != nullptr) {
      // In ES6, conflicting variable bindings are early errors.
      const AstRawString* name = decl->var()->raw_name();
      int position = decl->position();
      Scanner::Location location =
          position == kNoSourcePosition
              ? Scanner::Location::invalid()
              : Scanner::Location(position, position + 1);
      impl()->ReportMessageAt(location, MessageTemplate::kVarRedeclaration,
                              name);
    }
  }

  // TODO(nikolaos, marja): The first argument should not really be passed
  // by value. The method is expected to add the parsed statements to the
  // list. This works because in the case of the parser, StatementListT is
  // a pointer whereas the preparser does not really modify the body.
  V8_INLINE void ParseStatementList(StatementListT* body,
                                    Token::Value end_token);
  StatementT ParseStatementListItem();

  StatementT ParseStatement(ZonePtrList<const AstRawString>* labels,
                            ZonePtrList<const AstRawString>* own_labels) {
    return ParseStatement(labels, own_labels,
                          kDisallowLabelledFunctionStatement);
  }
  StatementT ParseStatement(ZonePtrList<const AstRawString>* labels,
                            ZonePtrList<const AstRawString>* own_labels,
                            AllowLabelledFunctionStatement allow_function);
  BlockT ParseBlock(ZonePtrList<const AstRawString>* labels,
                    Scope* block_scope);
  BlockT ParseBlock(ZonePtrList<const AstRawString>* labels);

  // Parse a SubStatement in strict mode, or with an extra block scope in
  // sloppy mode to handle
  // ES#sec-functiondeclarations-in-ifstatement-statement-clauses
  StatementT ParseScopedStatement(ZonePtrList<const AstRawString>* labels);

  StatementT ParseVariableStatement(VariableDeclarationContext var_context,
                                    ZonePtrList<const AstRawString>* names);

  // Magical syntax support.
  ExpressionT ParseV8Intrinsic();

  StatementT ParseDebuggerStatement();

  StatementT ParseExpressionOrLabelledStatement(
      ZonePtrList<const AstRawString>* labels,
      ZonePtrList<const AstRawString>* own_labels,
      AllowLabelledFunctionStatement allow_function);
  StatementT ParseIfStatement(ZonePtrList<const AstRawString>* labels);
  StatementT ParseContinueStatement();
  StatementT ParseBreakStatement(ZonePtrList<const AstRawString>* labels);
  StatementT ParseReturnStatement();
  StatementT ParseWithStatement(ZonePtrList<const AstRawString>* labels);
  StatementT ParseDoWhileStatement(ZonePtrList<const AstRawString>* labels,
                                   ZonePtrList<const AstRawString>* own_labels);
  StatementT ParseWhileStatement(ZonePtrList<const AstRawString>* labels,
                                 ZonePtrList<const AstRawString>* own_labels);
  StatementT ParseThrowStatement();
  StatementT ParseSwitchStatement(ZonePtrList<const AstRawString>* labels);
  V8_INLINE StatementT ParseTryStatement();
  StatementT ParseForStatement(ZonePtrList<const AstRawString>* labels,
                               ZonePtrList<const AstRawString>* own_labels);
  StatementT ParseForEachStatementWithDeclarations(
      int stmt_pos, ForInfo* for_info, ZonePtrList<const AstRawString>* labels,
      ZonePtrList<const AstRawString>* own_labels, Scope* inner_block_scope);
  StatementT ParseForEachStatementWithoutDeclarations(
      int stmt_pos, ExpressionT expression, int lhs_beg_pos, int lhs_end_pos,
      ForInfo* for_info, ZonePtrList<const AstRawString>* labels,
      ZonePtrList<const AstRawString>* own_labels);

  // Parse a C-style for loop: 'for (<init>; <cond>; <next>) { ... }'
  // "for (<init>;" is assumed to have been parser already.
  ForStatementT ParseStandardForLoop(
      int stmt_pos, ZonePtrList<const AstRawString>* labels,
      ZonePtrList<const AstRawString>* own_labels, ExpressionT* cond,
      StatementT* next, StatementT* body);
  // Same as the above, but handles those cases where <init> is a
  // lexical variable declaration.
  StatementT ParseStandardForLoopWithLexicalDeclarations(
      int stmt_pos, StatementT init, ForInfo* for_info,
      ZonePtrList<const AstRawString>* labels,
      ZonePtrList<const AstRawString>* own_labels);
  StatementT ParseForAwaitStatement(
      ZonePtrList<const AstRawString>* labels,
      ZonePtrList<const AstRawString>* own_labels);

  V8_INLINE bool IsLet(const AstRawString* identifier) const {
    return identifier == ast_value_factory()->let_string();
  }

  bool IsNextLetKeyword();

  // Checks if the expression is a valid reference expression (e.g., on the
  // left-hand side of assignments). Although ruled out by ECMA as early errors,
  // we allow calls for web compatibility and rewrite them to a runtime throw.
  // Modern language features can be exempted from this hack by passing
  // early_error = true.
  ExpressionT RewriteInvalidReferenceExpression(ExpressionT expression,
                                                int beg_pos, int end_pos,
                                                MessageTemplate message,
                                                bool early_error);

  bool IsValidReferenceExpression(ExpressionT expression);

  bool IsAssignableIdentifier(ExpressionT expression) {
    if (!impl()->IsIdentifier(expression)) return false;
    if (is_strict(language_mode()) &&
        impl()->IsEvalOrArguments(impl()->AsIdentifier(expression))) {
      return false;
    }
    return true;
  }

  enum SubFunctionKind { kFunction, kNonStaticMethod, kStaticMethod };

  FunctionKind FunctionKindForImpl(SubFunctionKind sub_function_kind,
                                   ParseFunctionFlags flags) {
    static const FunctionKind kFunctionKinds[][2][2] = {
        {
            // SubFunctionKind::kNormalFunction
            {// is_generator=false
             FunctionKind::kNormalFunction, FunctionKind::kAsyncFunction},
            {// is_generator=true
             FunctionKind::kGeneratorFunction,
             FunctionKind::kAsyncGeneratorFunction},
        },
        {
            // SubFunctionKind::kNonStaticMethod
            {// is_generator=false
             FunctionKind::kConciseMethod, FunctionKind::kAsyncConciseMethod},
            {// is_generator=true
             FunctionKind::kConciseGeneratorMethod,
             FunctionKind::kAsyncConciseGeneratorMethod},
        },
        {
            // SubFunctionKind::kStaticMethod
            {// is_generator=false
             FunctionKind::kStaticConciseMethod,
             FunctionKind::kStaticAsyncConciseMethod},
            {// is_generator=true
             FunctionKind::kStaticConciseGeneratorMethod,
             FunctionKind::kStaticAsyncConciseGeneratorMethod},
        }};
    return kFunctionKinds[sub_function_kind]
                         [(flags & ParseFunctionFlag::kIsGenerator) != 0]
                         [(flags & ParseFunctionFlag::kIsAsync) != 0];
  }

  inline FunctionKind FunctionKindFor(ParseFunctionFlags flags) {
    return FunctionKindForImpl(SubFunctionKind::kFunction, flags);
  }

  inline FunctionKind MethodKindFor(bool is_static, ParseFunctionFlags flags) {
    return FunctionKindForImpl(is_static ? SubFunctionKind::kStaticMethod
                                         : SubFunctionKind::kNonStaticMethod,
                               flags);
  }

  // Keep track of eval() calls since they disable all local variable
  // optimizations. This checks if expression is an eval call, and if yes,
  // forwards the information to scope.
  Call::PossiblyEval CheckPossibleEvalCall(ExpressionT expression,
                                           bool is_optional_call,
                                           Scope* scope) {
    if (impl()->IsIdentifier(expression) &&
        impl()->IsEval(impl()->AsIdentifier(expression)) && !is_optional_call) {
      function_state_->RecordFunctionOrEvalCall();
      scope->RecordEvalCall();

      return Call::IS_POSSIBLY_EVAL;
    }
    return Call::NOT_EVAL;
  }

  // Convenience method which determines the type of return statement to emit
  // depending on the current function type.
  inline StatementT BuildReturnStatement(
      ExpressionT expr, int pos,
      int end_pos = ReturnStatement::kFunctionLiteralReturnPosition) {
    if (impl()->IsNull(expr)) {
      expr = factory()->NewUndefinedLiteral(kNoSourcePosition);
    } else if (is_async_generator()) {
      // In async generators, if there is an explicit operand to the return
      // statement, await the operand.
      expr = factory()->NewAwait(expr, kNoSourcePosition);
      function_state_->AddSuspend();
    }
    if (is_async_function()) {
      return factory()->NewAsyncReturnStatement(expr, pos, end_pos);
    }
    return factory()->NewReturnStatement(expr, pos, end_pos);
  }

  SourceTextModuleDescriptor* module() const {
    return scope()->AsModuleScope()->module();
  }
  Scope* scope() const { return scope_; }

  // Stack of expression expression_scopes.
  // The top of the stack is always pointed to by expression_scope().
  V8_INLINE ExpressionScope* expression_scope() const {
    DCHECK_NOT_NULL(expression_scope_);
    return expression_scope_;
  }

  bool MaybeParsingArrowhead() const {
    return expression_scope_ != nullptr &&
           expression_scope_->has_possible_arrow_parameter_in_scope_chain();
  }

  class V8_NODISCARD AcceptINScope final {
   public:
    AcceptINScope(ParserBase* parser, bool accept_IN)
        : parser_(parser), previous_accept_IN_(parser->accept_IN_) {
      parser_->accept_IN_ = accept_IN;
    }

    ~AcceptINScope() { parser_->accept_IN_ = previous_accept_IN_; }

   private:
    ParserBase* parser_;
    bool previous_accept_IN_;
  };

  class V8_NODISCARD ParameterParsingScope {
   public:
    ParameterParsingScope(Impl* parser, FormalParametersT* parameters)
        : parser_(parser), parent_parameters_(parser_->parameters_) {
      parser_->parameters_ = parameters;
    }

    ~ParameterParsingScope() { parser_->parameters_ = parent_parameters_; }

   private:
    Impl* parser_;
    FormalParametersT* parent_parameters_;
  };

  class V8_NODISCARD FunctionParsingScope {
   public:
    explicit FunctionParsingScope(Impl* parser)
        : parser_(parser), expression_scope_(parser_->expression_scope_) {
      parser_->expression_scope_ = nullptr;
    }

    ~FunctionParsingScope() { parser_->expression_scope_ = expression_scope_; }

   private:
    Impl* parser_;
    ExpressionScope* expression_scope_;
  };

  std::vector<void*>* pointer_buffer() { return &pointer_buffer_; }
  std::vector<std::pair<VariableProxy*, int>>* variable_buffer() {
    return &variable_buffer_;
  }

  // Parser base's protected field members.

  Scope* scope_;                   // Scope stack.
  // Stack of scopes for object literals we're currently parsing.
  Scope* object_literal_scope_ = nullptr;
  Scope* original_scope_;  // The top scope for the current parsing item.
  FunctionState* function_state_;  // Function state stack.
  FuncNameInferrer fni_;
  AstValueFactory* ast_value_factory_;  // Not owned.
  typename Types::Factory ast_node_factory_;
  RuntimeCallStats* runtime_call_stats_;
  internal::Logger* logger_;
  bool parsing_on_main_thread_;
  uintptr_t stack_limit_;
  PendingCompilationErrorHandler* pending_error_handler_;

  // Parser base's private field members.

 private:
  Zone* zone_;
  ExpressionScope* expression_scope_;

  std::vector<void*> pointer_buffer_;
  std::vector<std::pair<VariableProxy*, int>> variable_buffer_;

  Scanner* scanner_;

  const UnoptimizedCompileFlags flags_;
  int function_literal_id_;

  FunctionLiteral::EagerCompileHint default_eager_compile_hint_;

  // This struct is used to move information about the next arrow function from
  // the place where the arrow head was parsed to where the body will be parsed.
  // Nothing can be parsed between the head and the body, so it will be consumed
  // immediately after it's produced.
  // Preallocating the struct as part of the parser minimizes the cost of
  // supporting arrow functions on non-arrow expressions.
  struct NextArrowFunctionInfo {
    Scanner::Location strict_parameter_error_location =
        Scanner::Location::invalid();
    MessageTemplate strict_parameter_error_message = MessageTemplate::kNone;
    DeclarationScope* scope = nullptr;

    bool HasInitialState() const { return scope == nullptr; }

    void Reset() {
      scope = nullptr;
      ClearStrictParameterError();
      DCHECK(HasInitialState());
    }

    // Tracks strict-mode parameter violations of sloppy-mode arrow heads in
    // case the function ends up becoming strict mode. Only one global place to
    // track this is necessary since arrow functions with none-simple parameters
    // cannot become strict-mode later on.
    void ClearStrictParameterError() {
      strict_parameter_error_location = Scanner::Location::invalid();
      strict_parameter_error_message = MessageTemplate::kNone;
    }
  };

  FormalParametersT* parameters_;
  NextArrowFunctionInfo next_arrow_function_info_;

  bool accept_IN_ = true;

  bool allow_eval_cache_ = true;
};

template <typename Impl>
ParserBase<Impl>::FunctionState::FunctionState(
    FunctionState** function_state_stack, Scope** scope_stack,
    DeclarationScope* scope)
    : BlockState(scope_stack, scope),
      expected_property_count_(0),
      suspend_count_(0),
      function_state_stack_(function_state_stack),
      outer_function_state_(*function_state_stack),
      scope_(scope),
      dont_optimize_reason_(BailoutReason::kNoReason),
      next_function_is_likely_called_(false),
      previous_function_was_likely_called_(false),
      contains_function_or_eval_(false) {
  *function_state_stack = this;
  if (outer_function_state_) {
    outer_function_state_->previous_function_was_likely_called_ =
        outer_function_state_->next_function_is_likely_called_;
    outer_function_state_->next_function_is_likely_called_ = false;
  }
}

template <typename Impl>
ParserBase<Impl>::FunctionState::~FunctionState() {
  *function_state_stack_ = outer_function_state_;
}

template <typename Impl>
void ParserBase<Impl>::ReportUnexpectedToken(Token::Value token) {
  return impl()->ReportUnexpectedTokenAt(scanner_->location(), token);
}

template <typename Impl>
bool ParserBase<Impl>::ClassifyPropertyIdentifier(
    Token::Value next, ParsePropertyInfo* prop_info) {
  // Updates made here must be reflected on ParseAndClassifyIdentifier.
  if (V8_LIKELY(base::IsInRange(next, Token::IDENTIFIER, Token::ASYNC))) {
    if (V8_UNLIKELY(impl()->IsArguments(prop_info->name) &&
                    scope()->ShouldBanArguments())) {
      ReportMessage(
          MessageTemplate::kArgumentsDisallowedInInitializerAndStaticBlock);
      return false;
    }
    return true;
  }

  if (!Token::IsValidIdentifier(next, language_mode(), is_generator(),
                                is_await_as_identifier_disallowed())) {
    ReportUnexpectedToken(next);
    return false;
  }

  DCHECK(!prop_info->is_computed_name);

  if (next == Token::AWAIT) {
    DCHECK(!is_async_function());
    expression_scope()->RecordAsyncArrowParametersError(
        scanner()->peek_location(), MessageTemplate::kAwaitBindingIdentifier);
  }
  return true;
}

template <typename Impl>
typename ParserBase<Impl>::IdentifierT
ParserBase<Impl>::ParseAndClassifyIdentifier(Token::Value next) {
  // Updates made here must be reflected on ClassifyPropertyIdentifier.
  DCHECK_EQ(scanner()->current_token(), next);
  if (V8_LIKELY(base::IsInRange(next, Token::IDENTIFIER, Token::ASYNC))) {
    IdentifierT name = impl()->GetIdentifier();
    if (V8_UNLIKELY(impl()->IsArguments(name) &&
                    scope()->ShouldBanArguments())) {
      ReportMessage(
          MessageTemplate::kArgumentsDisallowedInInitializerAndStaticBlock);
      return impl()->EmptyIdentifierString();
    }
    return name;
  }

  if (!Token::IsValidIdentifier(next, language_mode(), is_generator(),
                                is_await_as_identifier_disallowed())) {
    ReportUnexpectedToken(next);
    return impl()->EmptyIdentifierString();
  }

  if (next == Token::AWAIT) {
    expression_scope()->RecordAsyncArrowParametersError(
        scanner()->location(), MessageTemplate::kAwaitBindingIdentifier);
    return impl()->GetIdentifier();
  }

  DCHECK(Token::IsStrictReservedWord(next));
  expression_scope()->RecordStrictModeParameterError(
      scanner()->location(), MessageTemplate::kUnexpectedStrictReserved);
  return impl()->GetIdentifier();
}

template <class Impl>
typename ParserBase<Impl>::IdentifierT ParserBase<Impl>::ParseIdentifier(
    FunctionKind function_kind) {
  Token::Value next = Next();

  if (!Token::IsValidIdentifier(
          next, language_mode(), IsGeneratorFunction(function_kind),
          flags().is_module() ||
              IsAwaitAsIdentifierDisallowed(function_kind))) {
    ReportUnexpectedToken(next);
    return impl()->EmptyIdentifierString();
  }

  return impl()->GetIdentifier();
}

template <typename Impl>
typename ParserBase<Impl>::IdentifierT
ParserBase<Impl>::ParseNonRestrictedIdentifier() {
  IdentifierT result = ParseIdentifier();

  if (is_strict(language_mode()) &&
      V8_UNLIKELY(impl()->IsEvalOrArguments(result))) {
    impl()->ReportMessageAt(scanner()->location(),
                            MessageTemplate::kStrictEvalArguments);
  }

  return result;
}

template <typename Impl>
typename ParserBase<Impl>::IdentifierT ParserBase<Impl>::ParsePropertyName() {
  Token::Value next = Next();
  if (V8_LIKELY(Token::IsPropertyName(next))) {
    if (peek() == Token::COLON) return impl()->GetSymbol();
    return impl()->GetIdentifier();
  }

  ReportUnexpectedToken(next);
  return impl()->EmptyIdentifierString();
}

template <typename Impl>
typename ParserBase<Impl>::ExpressionT
ParserBase<Impl>::ParsePropertyOrPrivatePropertyName() {
  int pos = position();
  IdentifierT name;
  ExpressionT key;
  Token::Value next = Next();
  if (V8_LIKELY(Token::IsPropertyName(next))) {
    name = impl()->GetSymbol();
    key = factory()->NewStringLiteral(name, pos);
  } else if (next == Token::PRIVATE_NAME) {
    // In the case of a top level function, we completely skip
    // analysing it's scope, meaning, we don't have a chance to
    // resolve private names and find that they are not enclosed in a
    // class body.
    //
    // Here, we check if this is a new private name reference in a top
    // level function and throw an error if so.
    PrivateNameScopeIterator private_name_scope_iter(scope());
    // Parse the identifier so that we can display it in the error message
    name = impl()->GetIdentifier();
    if (private_name_scope_iter.Done()) {
      impl()->ReportMessageAt(Scanner::Location(pos, pos + 1),
                              MessageTemplate::kInvalidPrivateFieldResolution,
                              impl()->GetRawNameFromIdentifier(name));
      return impl()->FailureExpression();
    }
    key =
        impl()->ExpressionFromPrivateName(&private_name_scope_iter, name, pos);
  } else {
    ReportUnexpectedToken(next);
    return impl()->FailureExpression();
  }
  impl()->PushLiteralName(name);
  return key;
}

template <typename Impl>
bool ParserBase<Impl>::ValidateRegExpLiteral(const AstRawString* pattern,
                                             RegExpFlags flags,
                                             RegExpError* regexp_error) {
  // TODO(jgruber): If already validated in the preparser, skip validation in
  // the parser.
  DisallowGarbageCollection no_gc;
  ZoneScope zone_scope(zone());  // Free regexp parser memory after use.
  const unsigned char* d = pattern->raw_data();
  if (pattern->is_one_byte()) {
    return RegExp::VerifySyntax(zone(), stack_limit(),
                                static_cast<const uint8_t*>(d),
                                pattern->length(), flags, regexp_error, no_gc);
  } else {
    return RegExp::VerifySyntax(zone(), stack_limit(),
                                reinterpret_cast<const uint16_t*>(d),
                                pattern->length(), flags, regexp_error, no_gc);
  }
}

template <typename Impl>
typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseRegExpLiteral() {
  int pos = peek_position();
  if (!scanner()->ScanRegExpPattern()) {
    Next();
    ReportMessage(MessageTemplate::kUnterminatedRegExp);
    return impl()->FailureExpression();
  }

  const AstRawString* js_pattern = GetNextSymbolForRegExpLiteral();
  base::Optional<RegExpFlags> flags = scanner()->ScanRegExpFlags();
  if (!flags.has_value()) {
    Next();
    ReportMessage(MessageTemplate::kMalformedRegExpFlags);
    return impl()->FailureExpression();
  }
  Next();
  RegExpError regexp_error;
  if (!ValidateRegExpLiteral(js_pattern, flags.value(), &regexp_error)) {
    if (RegExpErrorIsStackOverflow(regexp_error)) set_stack_overflow();
    ReportMessage(MessageTemplate::kMalformedRegExp, js_pattern,
                  RegExpErrorString(regexp_error));
    return impl()->FailureExpression();
  }
  return factory()->NewRegExpLiteral(js_pattern, flags.value(), pos);
}

template <typename Impl>
typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseBindingPattern() {
  // Pattern ::
  //   Identifier
  //   ArrayLiteral
  //   ObjectLiteral

  int beg_pos = peek_position();
  Token::Value token = peek();
  ExpressionT result;

  if (Token::IsAnyIdentifier(token)) {
    IdentifierT name = ParseAndClassifyIdentifier(Next());
    if (V8_UNLIKELY(is_strict(language_mode()) &&
                    impl()->IsEvalOrArguments(name))) {
      impl()->ReportMessageAt(scanner()->location(),
                              MessageTemplate::kStrictEvalArguments);
      return impl()->FailureExpression();
    }
    return impl()->ExpressionFromIdentifier(name, beg_pos);
  }

  CheckStackOverflow();

  if (token == Token::LBRACK) {
    result = ParseArrayLiteral();
  } else if (token == Token::LBRACE) {
    result = ParseObjectLiteral();
  } else {
    ReportUnexpectedToken(Next());
    return impl()->FailureExpression();
  }

  return result;
}

template <typename Impl>
typename ParserBase<Impl>::ExpressionT
ParserBase<Impl>::ParsePrimaryExpression() {
  CheckStackOverflow();

  // PrimaryExpression ::
  //   'this'
  //   'null'
  //   'true'
  //   'false'
  //   Identifier
  //   Number
  //   String
  //   ArrayLiteral
  //   ObjectLiteral
  //   RegExpLiteral
  //   ClassLiteral
  //   '(' Expression ')'
  //   TemplateLiteral
  //   do Block
  //   AsyncFunctionLiteral

  int beg_pos = peek_position();
  Token::Value token = peek();

  if (Token::IsAnyIdentifier(token)) {
    Consume(token);

    FunctionKind kind = FunctionKind::kArrowFunction;

    if (V8_UNLIKELY(token == Token::ASYNC &&
                    !scanner()->HasLineTerminatorBeforeNext() &&
                    !scanner()->literal_contains_escapes())) {
      // async function ...
      if (peek() == Token::FUNCTION) return ParseAsyncFunctionLiteral();

      // async Identifier => ...
      if (peek_any_identifier() && PeekAhead() == Token::ARROW) {
        token = Next();
        beg_pos = position();
        kind = FunctionKind::kAsyncArrowFunction;
      }
    }

    if (V8_UNLIKELY(peek() == Token::ARROW)) {
      ArrowHeadParsingScope parsing_scope(impl(), kind);
      IdentifierT name = ParseAndClassifyIdentifier(token);
      ClassifyParameter(name, beg_pos, end_position());
      ExpressionT result =
          impl()->ExpressionFromIdentifier(name, beg_pos, InferName::kNo);
      parsing_scope.SetInitializers(0, peek_position());
      next_arrow_function_info_.scope = parsing_scope.ValidateAndCreateScope();
      return result;
    }

    IdentifierT name = ParseAndClassifyIdentifier(token);
    return impl()->ExpressionFromIdentifier(name, beg_pos);
  }

  if (Token::IsLiteral(token)) {
    return impl()->ExpressionFromLiteral(Next(), beg_pos);
  }

  switch (token) {
    case Token::NEW:
      return ParseMemberWithPresentNewPrefixesExpression();

    case Token::THIS: {
      Consume(Token::THIS);
      return impl()->NewThisExpression(beg_pos);
    }

    case Token::ASSIGN_DIV:
    case Token::DIV:
      return ParseRegExpLiteral();

    case Token::FUNCTION:
      return ParseFunctionExpression();

    case Token::SUPER: {
      return ParseSuperExpression();
    }
    case Token::IMPORT:
      return ParseImportExpressions();

    case Token::LBRACK:
      return ParseArrayLiteral();

    case Token::LBRACE:
      return ParseObjectLiteral();

    case Token::LPAREN: {
      Consume(Token::LPAREN);
      if (Check(Token::RPAREN)) {
        // ()=>x.  The continuation that consumes the => is in
        // ParseAssignmentExpressionCoverGrammar.
        if (peek() != Token::ARROW) ReportUnexpectedToken(Token::RPAREN);
        next_arrow_function_info_.scope =
            NewFunctionScope(FunctionKind::kArrowFunction);
        return factory()->NewEmptyParentheses(beg_pos);
      }
      Scope::Snapshot scope_snapshot(scope());
      ArrowHeadParsingScope maybe_arrow(impl(), FunctionKind::kArrowFunction);
      // Heuristically try to detect immediately called functions before
      // seeing the call parentheses.
      if (peek() == Token::FUNCTION ||
          (peek() == Token::ASYNC && PeekAhead() == Token::FUNCTION)) {
        function_state_->set_next_function_is_likely_called();
      }
      AcceptINScope scope(this, true);
      ExpressionT expr = ParseExpressionCoverGrammar();
      expr->mark_parenthesized();
      Expect(Token::RPAREN);

      if (peek() == Token::ARROW) {
        next_arrow_function_info_.scope = maybe_arrow.ValidateAndCreateScope();
        scope_snapshot.Reparent(next_arrow_function_info_.scope);
      } else {
        maybe_arrow.ValidateExpression();
      }

      return expr;
    }

    case Token::CLASS: {
      return ParseClassExpression(scope());
    }

    case Token::TEMPLATE_SPAN:
    case Token::TEMPLATE_TAIL:
      return ParseTemplateLiteral(impl()->NullExpression(), beg_pos, false);

    case Token::MOD:
      if (flags().allow_natives_syntax() || impl()->ParsingExtension()) {
        return ParseV8Intrinsic();
      }
      break;

    default:
      break;
  }

  ReportUnexpectedToken(Next());
  return impl()->FailureExpression();
}

template <typename Impl>
typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseExpression() {
  ExpressionParsingScope expression_scope(impl());
  AcceptINScope scope(this, true);
  ExpressionT result = ParseExpressionCoverGrammar();
  expression_scope.ValidateExpression();
  return result;
}

template <typename Impl>
typename ParserBase<Impl>::ExpressionT
ParserBase<Impl>::ParseAssignmentExpression() {
  ExpressionParsingScope expression_scope(impl());
  ExpressionT result = ParseAssignmentExpressionCoverGrammar();
  expression_scope.ValidateExpression();
  return result;
}

template <typename Impl>
typename ParserBase<Impl>::ExpressionT
ParserBase<Impl>::ParseExpressionCoverGrammar() {
  // Expression ::
  //   AssignmentExpression
  //   Expression ',' AssignmentExpression

  ExpressionListT list(pointer_buffer());
  ExpressionT expression;
  AccumulationScope accumulation_scope(expression_scope());
  int variable_index = 0;
  while (true) {
    if (V8_UNLIKELY(peek() == Token::ELLIPSIS)) {
      return ParseArrowParametersWithRest(&list, &accumulation_scope,
                                          variable_index);
    }

    int expr_pos = peek_position();
    expression = ParseAssignmentExpressionCoverGrammar();

    ClassifyArrowParameter(&accumulation_scope, expr_pos, expression);
    list.Add(expression);

    variable_index =
        expression_scope()->SetInitializers(variable_index, peek_position());

    if (!Check(Token::COMMA)) break;

    if (peek() == Token::RPAREN && PeekAhead() == Token::ARROW) {
      // a trailing comma is allowed at the end of an arrow parameter list
      break;
    }

    // Pass on the 'set_next_function_is_likely_called' flag if we have
    // several function literals separated by comma.
    if (peek() == Token::FUNCTION &&
        function_state_->previous_function_was_likely_called()) {
      function_state_->set_next_function_is_likely_called();
    }
  }

  // Return the single element if the list is empty. We need to do this because
  // callers of this function care about the type of the result if there was
  // only a single assignment expression. The preparser would lose this
  // information otherwise.
  if (list.length() == 1) return expression;
  return impl()->ExpressionListToExpression(list);
}

template <typename Impl>
typename ParserBase<Impl>::ExpressionT
ParserBase<Impl>::ParseArrowParametersWithRest(
    typename ParserBase<Impl>::ExpressionListT* list,
    AccumulationScope* accumulation_scope, int seen_variables) {
  Consume(Token::ELLIPSIS);

  Scanner::Location ellipsis = scanner()->location();
  int pattern_pos = peek_position();
  ExpressionT pattern = ParseBindingPattern();
  ClassifyArrowParameter(accumulation_scope, pattern_pos, pattern);

  expression_scope()->RecordNonSimpleParameter();

  if (V8_UNLIKELY(peek() == Token::ASSIGN)) {
    ReportMessage(MessageTemplate::kRestDefaultInitializer);
    return impl()->FailureExpression();
  }

  ExpressionT spread =
      factory()->NewSpread(pattern, ellipsis.beg_pos, pattern_pos);
  if (V8_UNLIKELY(peek() == Token::COMMA)) {
    ReportMessage(MessageTemplate::kParamAfterRest);
    return impl()->FailureExpression();
  }

  expression_scope()->SetInitializers(seen_variables, peek_position());

  // 'x, y, ...z' in CoverParenthesizedExpressionAndArrowParameterList only
  // as the formal parameters of'(x, y, ...z) => foo', and is not itself a
  // valid expression.
  if (peek() != Token::RPAREN || PeekAhead() != Token::ARROW) {
    impl()->ReportUnexpectedTokenAt(ellipsis, Token::ELLIPSIS);
    return impl()->FailureExpression();
  }

  list->Add(spread);
  return impl()->ExpressionListToExpression(*list);
}

template <typename Impl>
typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseArrayLiteral() {
  // ArrayLiteral ::
  //   '[' Expression? (',' Expression?)* ']'

  int pos = peek_position();
  ExpressionListT values(pointer_buffer());
  int first_spread_index = -1;
  Consume(Token::LBRACK);

  AccumulationScope accumulation_scope(expression_scope());

  while (!Check(Token::RBRACK)) {
    ExpressionT elem;
    if (peek() == Token::COMMA) {
      elem = factory()->NewTheHoleLiteral();
    } else if (Check(Token::ELLIPSIS)) {
      int start_pos = position();
      int expr_pos = peek_position();
      AcceptINScope scope(this, true);
      ExpressionT argument =
          ParsePossibleDestructuringSubPattern(&accumulation_scope);
      elem = factory()->NewSpread(argument, start_pos, expr_pos);

      if (first_spread_index < 0) {
        first_spread_index = values.length();
      }

      if (argument->IsAssignment()) {
        expression_scope()->RecordPatternError(
            Scanner::Location(start_pos, end_position()),
            MessageTemplate::kInvalidDestructuringTarget);
      }

      if (peek() == Token::COMMA) {
        expression_scope()->RecordPatternError(
            Scanner::Location(start_pos, end_position()),
            MessageTemplate::kElementAfterRest);
      }
    } else {
      AcceptINScope scope(this, true);
      elem = ParsePossibleDestructuringSubPattern(&accumulation_scope);
    }
    values.Add(elem);
    if (peek() != Token::RBRACK) {
      Expect(Token::COMMA);
      if (elem->IsFailureExpression()) return elem;
    }
  }

  return factory()->NewArrayLiteral(values, first_spread_index, pos);
}

template <class Impl>
typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseProperty(
    ParsePropertyInfo* prop_info) {
  DCHECK_EQ(prop_info->kind, ParsePropertyKind::kNotSet);
  DCHECK_EQ(prop_info->function_flags, ParseFunctionFlag::kIsNormal);
  DCHECK(!prop_info->is_computed_name);

  if (Check(Token::ASYNC)) {
    Token::Value token = peek();
    if ((token != Token::MUL && prop_info->ParsePropertyKindFromToken(token)) ||
        scanner()->HasLineTerminatorBeforeNext()) {
      prop_info->name = impl()->GetIdentifier();
      impl()->PushLiteralName(prop_info->name);
      return factory()->NewStringLiteral(prop_info->name, position());
    }
    if (V8_UNLIKELY(scanner()->literal_contains_escapes())) {
      impl()->ReportUnexpectedToken(Token::ESCAPED_KEYWORD);
    }
    prop_info->function_flags = ParseFunctionFlag::kIsAsync;
    prop_info->kind = ParsePropertyKind::kMethod;
  }

  if (Check(Token::MUL)) {
    prop_info->function_flags |= ParseFunctionFlag::kIsGenerator;
    prop_info->kind = ParsePropertyKind::kMethod;
  }

  if (prop_info->kind == ParsePropertyKind::kNotSet &&
      base::IsInRange(peek(), Token::GET, Token::SET)) {
    Token::Value token = Next();
    if (prop_info->ParsePropertyKindFromToken(peek())) {
      prop_info->name = impl()->GetIdentifier();
      impl()->PushLiteralName(prop_info->name);
      return factory()->NewStringLiteral(prop_info->name, position());
    }
    if (V8_UNLIKELY(scanner()->literal_contains_escapes())) {
      impl()->ReportUnexpectedToken(Token::ESCAPED_KEYWORD);
    }
    if (token == Token::GET) {
      prop_info->kind = ParsePropertyKind::kAccessorGetter;
    } else if (token == Token::SET) {
      prop_info->kind = ParsePropertyKind::kAccessorSetter;
    }
  }

  int pos = peek_position();

  // For non computed property names we normalize the name a bit:
  //
  //   "12" -> 12
  //   12.3 -> "12.3"
  //   12.30 -> "12.3"
  //   identifier -> "identifier"
  //
  // This is important because we use the property name as a key in a hash
  // table when we compute constant properties.
  bool is_array_index;
  uint32_t index;
  switch (peek()) {
    case Token::PRIVATE_NAME:
      prop_info->is_private = true;
      is_array_index = false;
      Consume(Token::PRIVATE_NAME);
      if (prop_info->kind == ParsePropertyKind::kNotSet) {
        prop_info->ParsePropertyKindFromToken(peek());
      }
      prop_info->name = impl()->GetIdentifier();
      if (V8_UNLIKELY(prop_info->position ==
                      PropertyPosition::kObjectLiteral)) {
        ReportUnexpectedToken(Token::PRIVATE_NAME);
        prop_info->kind = ParsePropertyKind::kNotSet;
        return impl()->FailureExpression();
      }
      break;

    case Token::STRING:
      Consume(Token::STRING);
      prop_info->name = peek() == Token::COLON ? impl()->GetSymbol()
                                               : impl()->GetIdentifier();
      is_array_index = impl()->IsArrayIndex(prop_info->name, &index);
      break;

    case Token::SMI:
      Consume(Token::SMI);
      index = scanner()->smi_value();
      is_array_index = true;
      // Token::SMI were scanned from their canonical representation.
      prop_info->name = impl()->GetSymbol();
      break;

    case Token::NUMBER: {
      Consume(Token::NUMBER);
      prop_info->name = impl()->GetNumberAsSymbol();
      is_array_index = impl()->IsArrayIndex(prop_info->name, &index);
      break;
    }

    case Token::BIGINT: {
      Consume(Token::BIGINT);
      prop_info->name = impl()->GetSymbol();
      is_array_index = impl()->IsArrayIndex(prop_info->name, &index);
      break;
    }

    case Token::LBRACK: {
      prop_info->name = impl()->NullIdentifier();
      prop_info->is_computed_name = true;
      Consume(Token::LBRACK);
      AcceptINScope scope(this, true);
      ExpressionT expression = ParseAssignmentExpression();
      Expect(Token::RBRACK);
      if (prop_info->kind == ParsePropertyKind::kNotSet) {
        prop_info->ParsePropertyKindFromToken(peek());
      }
      return expression;
    }

    case Token::ELLIPSIS:
      if (prop_info->kind == ParsePropertyKind::kNotSet) {
        prop_info->name = impl()->NullIdentifier();
        Consume(Token::ELLIPSIS);
        AcceptINScope scope(this, true);
        int start_pos = peek_position();
        ExpressionT expression =
            ParsePossibleDestructuringSubPattern(prop_info->accumulation_scope);
        prop_info->kind = ParsePropertyKind::kSpread;

        if (!IsValidReferenceExpression(expression)) {
          expression_scope()->RecordDeclarationError(
              Scanner::Location(start_pos, end_position()),
              MessageTemplate::kInvalidRestBindingPattern);
          expression_scope()->RecordPatternError(
              Scanner::Location(start_pos, end_position()),
              MessageTemplate::kInvalidRestAssignmentPattern);
        }

        if (peek() != Token::RBRACE) {
          expression_scope()->RecordPatternError(
              scanner()->location(), MessageTemplate::kElementAfterRest);
        }
        return expression;
      }
      V8_FALLTHROUGH;

    default:
      prop_info->name = ParsePropertyName();
      is_array_index = false;
      break;
  }

  if (prop_info->kind == ParsePropertyKind::kNotSet) {
    prop_info->ParsePropertyKindFromToken(peek());
  }
  impl()->PushLiteralName(prop_info->name);
  return is_array_index ? factory()->NewNumberLiteral(index, pos)
                        : factory()->NewStringLiteral(prop_info->name, pos);
}

template <typename Impl>
typename ParserBase<Impl>::ClassLiteralPropertyT
ParserBase<Impl>::ParseClassPropertyDefinition(ClassInfo* class_info,
                                               ParsePropertyInfo* prop_info,
                                               bool has_extends) {
  DCHECK_NOT_NULL(class_info);
  DCHECK_EQ(prop_info->position, PropertyPosition::kClassLiteral);

  Token::Value name_token = peek();
  int property_beg_pos = scanner()->peek_location().beg_pos;
  int name_token_position = property_beg_pos;
  ExpressionT name_expression;
  if (name_token == Token::STATIC) {
    Consume(Token::STATIC);
    name_token_position = scanner()->peek_location().beg_pos;
    if (peek() == Token::LPAREN) {
      prop_info->kind = ParsePropertyKind::kMethod;
      // TODO(bakkot) specialize on 'static'
      prop_info->name = impl()->GetIdentifier();
      name_expression =
          factory()->NewStringLiteral(prop_info->name, position());
    } else if (peek() == Token::ASSIGN || peek() == Token::SEMICOLON ||
               peek() == Token::RBRACE) {
      // TODO(bakkot) specialize on 'static'
      prop_info->name = impl()->GetIdentifier();
      name_expression =
          factory()->NewStringLiteral(prop_info->name, position());
    } else {
      prop_info->is_static = true;
      name_expression = ParseProperty(prop_info);
    }
  } else {
    name_expression = ParseProperty(prop_info);
  }

  switch (prop_info->kind) {
    case ParsePropertyKind::kAssign:
    case ParsePropertyKind::kClassField:
    case ParsePropertyKind::kShorthandOrClassField:
    case ParsePropertyKind::kNotSet: {  // This case is a name followed by a
                                        // name or other property. Here we have
                                        // to assume that's an uninitialized
                                        // field followed by a linebreak
                                        // followed by a property, with ASI
                                        // adding the semicolon. If not, there
                                        // will be a syntax error after parsing
                                        // the first name as an uninitialized
                                        // field.
      prop_info->kind = ParsePropertyKind::kClassField;
      DCHECK_IMPLIES(prop_info->is_computed_name, !prop_info->is_private);

      if (!prop_info->is_computed_name) {
        CheckClassFieldName(prop_info->name, prop_info->is_static);
      }

      ExpressionT initializer = ParseMemberInitializer(
          class_info, property_beg_pos, prop_info->is_static);
      ExpectSemicolon();

      ClassLiteralPropertyT result = factory()->NewClassLiteralProperty(
          name_expression, initializer, ClassLiteralProperty::FIELD,
          prop_info->is_static, prop_info->is_computed_name,
          prop_info->is_private);
      impl()->SetFunctionNameFromPropertyName(result, prop_info->name);

      return result;
    }
    case ParsePropertyKind::kMethod: {
      // MethodDefinition
      //    PropertyName '(' StrictFormalParameters ')' '{' FunctionBody '}'
      //    '*' PropertyName '(' StrictFormalParameters ')' '{' FunctionBody '}'
      //    async PropertyName '(' StrictFormalParameters ')'
      //        '{' FunctionBody '}'
      //    async '*' PropertyName '(' StrictFormalParameters ')'
      //        '{' FunctionBody '}'

      if (!prop_info->is_computed_name) {
        CheckClassMethodName(prop_info->name, ParsePropertyKind::kMethod,
                             prop_info->function_flags, prop_info->is_static,
                             &class_info->has_seen_constructor);
      }

      FunctionKind kind =
          MethodKindFor(prop_info->is_static, prop_info->function_flags);

      if (!prop_info->is_static && impl()->IsConstructor(prop_info->name)) {
        class_info->has_seen_constructor = true;
        kind = has_extends ? FunctionKind::kDerivedConstructor
                           : FunctionKind::kBaseConstructor;
      }

      ExpressionT value = impl()->ParseFunctionLiteral(
          prop_info->name, scanner()->location(), kSkipFunctionNameCheck, kind,
          name_token_position, FunctionSyntaxKind::kAccessorOrMethod,
          language_mode(), nullptr);

      ClassLiteralPropertyT result = factory()->NewClassLiteralProperty(
          name_expression, value, ClassLiteralProperty::METHOD,
          prop_info->is_static, prop_info->is_computed_name,
          prop_info->is_private);
      impl()->SetFunctionNameFromPropertyName(result, prop_info->name);
      return result;
    }

    case ParsePropertyKind::kAccessorGetter:
    case ParsePropertyKind::kAccessorSetter: {
      DCHECK_EQ(prop_info->function_flags, ParseFunctionFlag::kIsNormal);
      bool is_get = prop_info->kind == ParsePropertyKind::kAccessorGetter;

      if (!prop_info->is_computed_name) {
        CheckClassMethodName(prop_info->name, prop_info->kind,
                             ParseFunctionFlag::kIsNormal, prop_info->is_static,
                             &class_info->has_seen_constructor);
        // Make sure the name expression is a string since we need a Name for
        // Runtime_DefineAccessorPropertyUnchecked and since we can determine
        // this statically we can skip the extra runtime check.
        name_expression = factory()->NewStringLiteral(
            prop_info->name, name_expression->position());
      }

      FunctionKind kind;
      if (prop_info->is_static) {
        kind = is_get ? FunctionKind::kStaticGetterFunction
                      : FunctionKind::kStaticSetterFunction;
      } else {
        kind = is_get ? FunctionKind::kGetterFunction
                      : FunctionKind::kSetterFunction;
      }

      FunctionLiteralT value = impl()->ParseFunctionLiteral(
          prop_info->name, scanner()->location(), kSkipFunctionNameCheck, kind,
          name_token_position, FunctionSyntaxKind::kAccessorOrMethod,
          language_mode(), nullptr);

      ClassLiteralProperty::Kind property_kind =
          is_get ? ClassLiteralProperty::GETTER : ClassLiteralProperty::SETTER;
      ClassLiteralPropertyT result = factory()->NewClassLiteralProperty(
          name_expression, value, property_kind, prop_info->is_static,
          prop_info->is_computed_name, prop_info->is_private);
      const AstRawString* prefix =
          is_get ? ast_value_factory()->get_space_string()
                 : ast_value_factory()->set_space_string();
      impl()->SetFunctionNameFromPropertyName(result, prop_info->name, prefix);
      return result;
    }
    case ParsePropertyKind::kValue:
    case ParsePropertyKind::kShorthand:
    case ParsePropertyKind::kSpread:
      impl()->ReportUnexpectedTokenAt(
          Scanner::Location(name_token_position, name_expression->position()),
          name_token);
      return impl()->NullLiteralProperty();
  }
  UNREACHABLE();
}

template <typename Impl>
typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseMemberInitializer(
    ClassInfo* class_info, int beg_pos, bool is_static) {
  FunctionParsingScope body_parsing_scope(impl());
  DeclarationScope* initializer_scope =
      is_static ? class_info->static_elements_scope
                : class_info->instance_members_scope;
  FunctionKind function_kind =
      is_static ? FunctionKind::kClassStaticInitializerFunction
                : FunctionKind::kClassMembersInitializerFunction;

  if (initializer_scope == nullptr) {
    initializer_scope = NewFunctionScope(function_kind);
    initializer_scope->SetLanguageMode(LanguageMode::kStrict);
  }

  ExpressionT initializer;
  if (Check(Token::ASSIGN)) {
    FunctionState initializer_state(&function_state_, &scope_,
                                    initializer_scope);

    AcceptINScope scope(this, true);
    initializer = ParseAssignmentExpression();
  } else {
    initializer = factory()->NewUndefinedLiteral(kNoSourcePosition);
  }

  if (is_static) {
    // For the instance initializer, we will save the positions
    // later with the positions of the class body so that we can reparse
    // it later.
    // TODO(joyee): Make scopes be non contiguous.
    initializer_scope->set_start_position(beg_pos);
    initializer_scope->set_end_position(end_position());
    class_info->static_elements_scope = initializer_scope;
    class_info->has_static_elements = true;
  } else {
    class_info->instance_members_scope = initializer_scope;
    class_info->has_instance_members = true;
  }

  return initializer;
}

template <typename Impl>
typename ParserBase<Impl>::BlockT ParserBase<Impl>::ParseClassStaticBlock(
    ClassInfo* class_info) {
  Consume(Token::STATIC);

  DeclarationScope* initializer_scope = class_info->static_elements_scope;
  if (initializer_scope == nullptr) {
    initializer_scope =
        NewFunctionScope(FunctionKind::kClassStaticInitializerFunction);
    initializer_scope->set_start_position(position());
    initializer_scope->SetLanguageMode(LanguageMode::kStrict);
    class_info->static_elements_scope = initializer_scope;
  }

  FunctionState initializer_state(&function_state_, &scope_, initializer_scope);
  AcceptINScope accept_in(this, true);

  // Each static block has its own var and lexical scope, so make a new var
  // block scope instead of using the synthetic members initializer function
  // scope.
  BlockT static_block = ParseBlock(nullptr, NewVarblockScope());
  initializer_scope->set_end_position(end_position());
  class_info->has_static_elements = true;
  return static_block;
}

template <typename Impl>
typename ParserBase<Impl>::ObjectLiteralPropertyT
ParserBase<Impl>::ParseObjectPropertyDefinition(ParsePropertyInfo* prop_info,
                                                bool* has_seen_proto) {
  DCHECK_EQ(prop_info->position, PropertyPosition::kObjectLiteral);
  Token::Value name_token = peek();
  Scanner::Location next_loc = scanner()->peek_location();

  ExpressionT name_expression = ParseProperty(prop_info);

  DCHECK_IMPLIES(name_token == Token::PRIVATE_NAME, has_error());

  IdentifierT name = prop_info->name;
  ParseFunctionFlags function_flags = prop_info->function_flags;

  switch (prop_info->kind) {
    case ParsePropertyKind::kSpread:
      DCHECK_EQ(function_flags, ParseFunctionFlag::kIsNormal);
      DCHECK(!prop_info->is_computed_name);
      DCHECK_EQ(Token::ELLIPSIS, name_token);

      prop_info->is_computed_name = true;
      prop_info->is_rest = true;

      return factory()->NewObjectLiteralProperty(
          factory()->NewTheHoleLiteral(), name_expression,
          ObjectLiteralProperty::SPREAD, true);

    case ParsePropertyKind::kValue: {
      DCHECK_EQ(function_flags, ParseFunctionFlag::kIsNormal);

      if (!prop_info->is_computed_name &&
          scanner()->CurrentLiteralEquals("__proto__")) {
        if (*has_seen_proto) {
          expression_scope()->RecordExpressionError(
              scanner()->location(), MessageTemplate::kDuplicateProto);
        }
        *has_seen_proto = true;
      }
      Consume(Token::COLON);
      AcceptINScope scope(this, true);
      ExpressionT value =
          ParsePossibleDestructuringSubPattern(prop_info->accumulation_scope);

      ObjectLiteralPropertyT result = factory()->NewObjectLiteralProperty(
          name_expression, value, prop_info->is_computed_name);
      impl()->SetFunctionNameFromPropertyName(result, name);
      return result;
    }

    case ParsePropertyKind::kAssign:
    case ParsePropertyKind::kShorthandOrClassField:
    case ParsePropertyKind::kShorthand: {
      // PropertyDefinition
      //    IdentifierReference
      //    CoverInitializedName
      //
      // CoverInitializedName
      //    IdentifierReference Initializer?
      DCHECK_EQ(function_flags, ParseFunctionFlag::kIsNormal);

      if (!ClassifyPropertyIdentifier(name_token, prop_info)) {
        return impl()->NullLiteralProperty();
      }

      ExpressionT lhs =
          impl()->ExpressionFromIdentifier(name, next_loc.beg_pos);
      if (!IsAssignableIdentifier(lhs)) {
        expression_scope()->RecordPatternError(
            next_loc, MessageTemplate::kStrictEvalArguments);
      }

      ExpressionT value;
      if (peek() == Token::ASSIGN) {
        Consume(Token::ASSIGN);
        {
          AcceptINScope scope(this, true);
          ExpressionT rhs = ParseAssignmentExpression();
          value = factory()->NewAssignment(Token::ASSIGN, lhs, rhs,
                                           kNoSourcePosition);
          impl()->SetFunctionNameFromIdentifierRef(rhs, lhs);
        }
        expression_scope()->RecordExpressionError(
            Scanner::Location(next_loc.beg_pos, end_position()),
            MessageTemplate::kInvalidCoverInitializedName);
      } else {
        value = lhs;
      }

      ObjectLiteralPropertyT result = factory()->NewObjectLiteralProperty(
          name_expression, value, ObjectLiteralProperty::COMPUTED, false);
      impl()->SetFunctionNameFromPropertyName(result, name);
      return result;
    }

    case ParsePropertyKind::kMethod: {
      // MethodDefinition
      //    PropertyName '(' StrictFormalParameters ')' '{' FunctionBody '}'
      //    '*' PropertyName '(' StrictFormalParameters ')' '{' FunctionBody '}'

      expression_scope()->RecordPatternError(
          Scanner::Location(next_loc.beg_pos, end_position()),
          MessageTemplate::kInvalidDestructuringTarget);

      std::unique_ptr<BlockState> block_state;
      if (object_literal_scope_ != nullptr) {
        DCHECK_EQ(object_literal_scope_->outer_scope(), scope_);
        block_state.reset(new BlockState(&scope_, object_literal_scope_));
      }
      constexpr bool kIsStatic = false;
      FunctionKind kind = MethodKindFor(kIsStatic, function_flags);

      ExpressionT value = impl()->ParseFunctionLiteral(
          name, scanner()->location(), kSkipFunctionNameCheck, kind,
          next_loc.beg_pos, FunctionSyntaxKind::kAccessorOrMethod,
          language_mode(), nullptr);

      ObjectLiteralPropertyT result = factory()->NewObjectLiteralProperty(
          name_expression, value, ObjectLiteralProperty::COMPUTED,
          prop_info->is_computed_name);
      impl()->SetFunctionNameFromPropertyName(result, name);
      return result;
    }

    case ParsePropertyKind::kAccessorGetter:
    case ParsePropertyKind::kAccessorSetter: {
      DCHECK_EQ(function_flags, ParseFunctionFlag::kIsNormal);
      bool is_get = prop_info->kind == ParsePropertyKind::kAccessorGetter;

      expression_scope()->RecordPatternError(
          Scanner::Location(next_loc.beg_pos, end_position()),
          MessageTemplate::kInvalidDestructuringTarget);

      if (!prop_info->is_computed_name) {
        // Make sure the name expression is a string since we need a Name for
        // Runtime_DefineAccessorPropertyUnchecked and since we can determine
        // this statically we can skip the extra runtime check.
        name_expression =
            factory()->NewStringLiteral(name, name_expression->position());
      }

      std::unique_ptr<BlockState> block_state;
      if (object_literal_scope_ != nullptr) {
        DCHECK_EQ(object_literal_scope_->outer_scope(), scope_);
        block_state.reset(new BlockState(&scope_, object_literal_scope_));
      }

      FunctionKind kind = is_get ? FunctionKind::kGetterFunction
                                 : FunctionKind::kSetterFunction;

      FunctionLiteralT value = impl()->ParseFunctionLiteral(
          name, scanner()->location(), kSkipFunctionNameCheck, kind,
          next_loc.beg_pos, FunctionSyntaxKind::kAccessorOrMethod,
          language_mode(), nullptr);

      ObjectLiteralPropertyT result = factory()->NewObjectLiteralProperty(
          name_expression, value,
          is_get ? ObjectLiteralProperty::GETTER
                 : ObjectLiteralProperty::SETTER,
          prop_info->is_computed_name);
      const AstRawString* prefix =
          is_get ? ast_value_factory()->get_space_string()
                 : ast_value_factory()->set_space_string();
      impl()->SetFunctionNameFromPropertyName(result, name, prefix);
      return result;
    }

    case ParsePropertyKind::kClassField:
    case ParsePropertyKind::kNotSet:
      ReportUnexpectedToken(Next());
      return impl()->NullLiteralProperty();
  }
  UNREACHABLE();
}

template <typename Impl>
typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseObjectLiteral() {
  // ObjectLiteral ::
  // '{' (PropertyDefinition (',' PropertyDefinition)* ','? )? '}'

  int pos = peek_position();
  ObjectPropertyListT properties(pointer_buffer());
  int number_of_boilerplate_properties = 0;

  bool has_computed_names = false;
  bool has_rest_property = false;
  bool has_seen_proto = false;

  Consume(Token::LBRACE);
  AccumulationScope accumulation_scope(expression_scope());

  // If methods appear inside the object literal, we'll enter this scope.
  Scope* block_scope = NewBlockScopeForObjectLiteral();
  block_scope->set_start_position(pos);
  BlockState object_literal_scope_state(&object_literal_scope_, block_scope);

  while (!Check(Token::RBRACE)) {
    FuncNameInferrerState fni_state(&fni_);

    ParsePropertyInfo prop_info(this, &accumulation_scope);
    prop_info.position = PropertyPosition::kObjectLiteral;
    ObjectLiteralPropertyT property =
        ParseObjectPropertyDefinition(&prop_info, &has_seen_proto);
    if (impl()->IsNull(property)) return impl()->FailureExpression();

    if (prop_info.is_computed_name) {
      has_computed_names = true;
    }

    if (prop_info.is_rest) {
      has_rest_property = true;
    }

    if (impl()->IsBoilerplateProperty(property) && !has_computed_names) {
      // Count CONSTANT or COMPUTED properties to maintain the enumeration
      // order.
      number_of_boilerplate_properties++;
    }

    properties.Add(property);

    if (peek() != Token::RBRACE) {
      Expect(Token::COMMA);
    }

    fni_.Infer();
  }

  Variable* home_object = nullptr;
  if (block_scope->needs_home_object()) {
    home_object = block_scope->DeclareHomeObjectVariable(ast_value_factory());
    block_scope->set_end_position(end_position());
  } else {
    block_scope = block_scope->FinalizeBlockScope();
    DCHECK_NULL(block_scope);
  }

  // In pattern rewriter, we rewrite rest property to call out to a
  // runtime function passing all the other properties as arguments to
  // this runtime function. Here, we make sure that the number of
  // properties is less than number of arguments allowed for a runtime
  // call.
  if (has_rest_property && properties.length() > Code::kMaxArguments) {
    expression_scope()->RecordPatternError(Scanner::Location(pos, position()),
                                           MessageTemplate::kTooManyArguments);
  }

  return impl()->InitializeObjectLiteral(
      factory()->NewObjectLiteral(properties, number_of_boilerplate_properties,
                                  pos, has_rest_property, home_object));
}

template <typename Impl>
void ParserBase<Impl>::ParseArguments(
    typename ParserBase<Impl>::ExpressionListT* args, bool* has_spread,
    ParsingArrowHeadFlag maybe_arrow) {
  // Arguments ::
  //   '(' (AssignmentExpression)*[','] ')'

  *has_spread = false;
  Consume(Token::LPAREN);
  AccumulationScope accumulation_scope(expression_scope());

  int variable_index = 0;
  while (peek() != Token::RPAREN) {
    int start_pos = peek_position();
    bool is_spread = Check(Token::ELLIPSIS);
    int expr_pos = peek_position();

    AcceptINScope scope(this, true);
    ExpressionT argument = ParseAssignmentExpressionCoverGrammar();

    if (V8_UNLIKELY(maybe_arrow == kMaybeArrowHead)) {
      ClassifyArrowParameter(&accumulation_scope, expr_pos, argument);
      if (is_spread) {
        expression_scope()->RecordNonSimpleParameter();
        if (argument->IsAssignment()) {
          expression_scope()->RecordAsyncArrowParametersError(
              scanner()->location(), MessageTemplate::kRestDefaultInitializer);
        }
        if (peek() == Token::COMMA) {
          expression_scope()->RecordAsyncArrowParametersError(
              scanner()->peek_location(), MessageTemplate::kParamAfterRest);
        }
      }
    }
    if (is_spread) {
      *has_spread = true;
      argument = factory()->NewSpread(argument, start_pos, expr_pos);
    }
    args->Add(argument);

    variable_index =
        expression_scope()->SetInitializers(variable_index, peek_position());

    if (!Check(Token::COMMA)) break;
  }

  if (args->length() > Code::kMaxArguments) {
    ReportMessage(MessageTemplate::kTooManyArguments);
    return;
  }

  Scanner::Location location = scanner_->location();
  if (!Check(Token::RPAREN)) {
    impl()->ReportMessageAt(location, MessageTemplate::kUnterminatedArgList);
  }
}

// Precedence = 2
template <typename Impl>
typename ParserBase<Impl>::ExpressionT
ParserBase<Impl>::ParseAssignmentExpressionCoverGrammar() {
  // AssignmentExpression ::
  //   ConditionalExpression
  //   ArrowFunction
  //   YieldExpression
  //   LeftHandSideExpression AssignmentOperator AssignmentExpression
  int lhs_beg_pos = peek_position();

  if (peek() == Token::YIELD && is_generator()) {
    return ParseYieldExpression();
  }

  FuncNameInferrerState fni_state(&fni_);

  DCHECK_IMPLIES(!has_error(), next_arrow_function_info_.HasInitialState());

  ExpressionT expression = ParseConditionalExpression();

  Token::Value op = peek();

  if (!Token::IsArrowOrAssignmentOp(op)) return expression;

  // Arrow functions.
  if (V8_UNLIKELY(op == Token::ARROW)) {
    Scanner::Location loc(lhs_beg_pos, end_position());

    if (!impl()->IsIdentifier(expression) && !expression->is_parenthesized()) {
      impl()->ReportMessageAt(
          Scanner::Location(expression->position(), position()),
          MessageTemplate::kMalformedArrowFunParamList);
      return impl()->FailureExpression();
    }

    DeclarationScope* scope = next_arrow_function_info_.scope;
    scope->set_start_position(lhs_beg_pos);

    FormalParametersT parameters(scope);
    parameters.set_strict_parameter_error(
        next_arrow_function_info_.strict_parameter_error_location,
        next_arrow_function_info_.strict_parameter_error_message);
    parameters.is_simple = scope->has_simple_parameters();
    next_arrow_function_info_.Reset();

    impl()->DeclareArrowFunctionFormalParameters(&parameters, expression, loc);

    expression = ParseArrowFunctionLiteral(parameters);

    return expression;
  }

  if (V8_LIKELY(impl()->IsAssignableIdentifier(expression))) {
    if (expression->is_parenthesized()) {
      expression_scope()->RecordDeclarationError(
          Scanner::Location(lhs_beg_pos, end_position()),
          MessageTemplate::kInvalidDestructuringTarget);
    }
    expression_scope()->MarkIdentifierAsAssigned();
  } else if (expression->IsProperty()) {
    expression_scope()->RecordDeclarationError(
        Scanner::Location(lhs_beg_pos, end_position()),
        MessageTemplate::kInvalidPropertyBindingPattern);
    expression_scope()->ValidateAsExpression();
  } else if (expression->IsPattern() && op == Token::ASSIGN) {
    // Destructuring assignmment.
    if (expression->is_parenthesized()) {
      Scanner::Location loc(lhs_beg_pos, end_position());
      if (expression_scope()->IsCertainlyDeclaration()) {
        impl()->ReportMessageAt(loc,
                                MessageTemplate::kInvalidDestructuringTarget);
      } else {
        // Syntax Error if LHS is neither object literal nor an array literal
        // (Parenthesized literals are
        // CoverParenthesizedExpressionAndArrowParameterList).
        // #sec-assignment-operators-static-semantics-early-errors
        impl()->ReportMessageAt(loc, MessageTemplate::kInvalidLhsInAssignment);
      }
    }
    expression_scope()->ValidateAsPattern(expression, lhs_beg_pos,
                                          end_position());
  } else {
    DCHECK(!IsValidReferenceExpression(expression));
    // For web compatibility reasons, throw early errors only for logical
    // assignment, not for regular assignment.
    const bool early_error = Token::IsLogicalAssignmentOp(op);
    expression = RewriteInvalidReferenceExpression(
        expression, lhs_beg_pos, end_position(),
        MessageTemplate::kInvalidLhsInAssignment, early_error);
  }

  Consume(op);
  int op_position = position();

  ExpressionT right = ParseAssignmentExpression();

  // Anonymous function name inference applies to =, ||=, &&=, and ??=.
  if (op == Token::ASSIGN || Token::IsLogicalAssignmentOp(op)) {
    impl()->CheckAssigningFunctionLiteralToProperty(expression, right);

    // Check if the right hand side is a call to avoid inferring a
    // name if we're dealing with "a = function(){...}();"-like
    // expression.
    if (right->IsCall() || right->IsCallNew()) {
      fni_.RemoveLastFunction();
    } else {
      fni_.Infer();
    }

    impl()->SetFunctionNameFromIdentifierRef(right, expression);
  } else {
    fni_.RemoveLastFunction();
  }

  if (op == Token::ASSIGN) {
    // We try to estimate the set of properties set by constructors. We define a
    // new property whenever there is an assignment to a property of 'this'. We
    // should probably only add properties if we haven't seen them before.
    // Otherwise we'll probably overestimate the number of properties.
    if (impl()->IsThisProperty(expression)) function_state_->AddProperty();
  } else {
    // Only initializers (i.e. no compound assignments) are allowed in patterns.
    expression_scope()->RecordPatternError(
        Scanner::Location(lhs_beg_pos, end_position()),
        MessageTemplate::kInvalidDestructuringTarget);
  }

  return factory()->NewAssignment(op, expression, right, op_position);
}

template <typename Impl>
typename ParserBase<Impl>::ExpressionT
ParserBase<Impl>::ParseYieldExpression() {
  // YieldExpression ::
  //   'yield' ([no line terminator] '*'? AssignmentExpression)?
  int pos = peek_position();
  expression_scope()->RecordParameterInitializerError(
      scanner()->peek_location(), MessageTemplate::kYieldInParameter);
  Consume(Token::YIELD);
  if (V8_UNLIKELY(scanner()->literal_contains_escapes())) {
    impl()->ReportUnexpectedToken(Token::ESCAPED_KEYWORD);
  }

  CheckStackOverflow();

  // The following initialization is necessary.
  ExpressionT expression = impl()->NullExpression();
  bool delegating = false;  // yield*
  if (!scanner()->HasLineTerminatorBeforeNext()) {
    if (Check(Token::MUL)) delegating = true;
    switch (peek()) {
      case Token::EOS:
      case Token::SEMICOLON:
      case Token::RBRACE:
      case Token::RBRACK:
      case Token::RPAREN:
      case Token::COLON:
      case Token::COMMA:
      case Token::IN:
        // The above set of tokens is the complete set of tokens that can appear
        // after an AssignmentExpression, and none of them can start an
        // AssignmentExpression.  This allows us to avoid looking for an RHS for
        // a regular yield, given only one look-ahead token.
        if (!delegating) break;
        // Delegating yields require an RHS; fall through.
        V8_FALLTHROUGH;
      default:
        expression = ParseAssignmentExpressionCoverGrammar();
        break;
    }
  }

  if (delegating) {
    ExpressionT yieldstar = factory()->NewYieldStar(expression, pos);
    impl()->RecordSuspendSourceRange(yieldstar, PositionAfterSemicolon());
    function_state_->AddSuspend();
    if (IsAsyncGeneratorFunction(function_state_->kind())) {
      // return, iterator_close and delegated_iterator_output suspend ids.
      function_state_->AddSuspend();
      function_state_->AddSuspend();
      function_state_->AddSuspend();
    }
    return yieldstar;
  }

  // Hackily disambiguate o from o.next and o [Symbol.iterator]().
  // TODO(verwaest): Come up with a better solution.
  ExpressionT yield =
      factory()->NewYield(expression, pos, Suspend::kOnExceptionThrow);
  impl()->RecordSuspendSourceRange(yield, PositionAfterSemicolon());
  function_state_->AddSuspend();
  return yield;
}

// Precedence = 3
template <typename Impl>
typename ParserBase<Impl>::ExpressionT
ParserBase<Impl>::ParseConditionalExpression() {
  // ConditionalExpression ::
  //   LogicalExpression
  //   LogicalExpression '?' AssignmentExpression ':' AssignmentExpression
  //
  int pos = peek_position();
  ExpressionT expression = ParseLogicalExpression();
  return peek() == Token::CONDITIONAL
             ? ParseConditionalContinuation(expression, pos)
             : expression;
}

template <typename Impl>
typename ParserBase<Impl>::ExpressionT
ParserBase<Impl>::ParseLogicalExpression() {
  // LogicalExpression ::
  //   LogicalORExpression
  //   CoalesceExpression

  // Both LogicalORExpression and CoalesceExpression start with BitwiseOR.
  // Parse for binary expressions >= 6 (BitwiseOR);
  ExpressionT expression = ParseBinaryExpression(6);
  if (peek() == Token::AND || peek() == Token::OR) {
    // LogicalORExpression, pickup parsing where we left off.
    int prec1 = Token::Precedence(peek(), accept_IN_);
    expression = ParseBinaryContinuation(expression, 4, prec1);
  } else if (V8_UNLIKELY(peek() == Token::NULLISH)) {
    expression = ParseCoalesceExpression(expression);
  }
  return expression;
}

template <typename Impl>
typename ParserBase<Impl>::ExpressionT
ParserBase<Impl>::ParseCoalesceExpression(ExpressionT expression) {
  // CoalesceExpression ::
  //   CoalesceExpressionHead ?? BitwiseORExpression
  //
  //   CoalesceExpressionHead ::
  //     CoalesceExpression
  //     BitwiseORExpression

  // We create a binary operation for the first nullish, otherwise collapse
  // into an nary expresion.
  bool first_nullish = true;
  while (peek() == Token::NULLISH) {
    SourceRange right_range;
    int pos;
    ExpressionT y;
    {
      SourceRangeScope right_range_scope(scanner(), &right_range);
      Consume(Token::NULLISH);
      pos = peek_position();
      // Parse BitwiseOR or higher.
      y = ParseBinaryExpression(6);
    }
    if (first_nullish) {
      expression =
          factory()->NewBinaryOperation(Token::NULLISH, expression, y, pos);
      impl()->RecordBinaryOperationSourceRange(expression, right_range);
      first_nullish = false;
    } else {
      impl()->CollapseNaryExpression(&expression, y, Token::NULLISH, pos,
                                     right_range);
    }
  }
  return expression;
}

template <typename Impl>
typename ParserBase<Impl>::ExpressionT
ParserBase<Impl>::ParseConditionalContinuation(ExpressionT expression,
                                               int pos) {
  SourceRange then_range, else_range;

  ExpressionT left;
  {
    SourceRangeScope range_scope(scanner(), &then_range);
    Consume(Token::CONDITIONAL);
    // In parsing the first assignment expression in conditional
    // expressions we always accept the 'in' keyword; see ECMA-262,
    // section 11.12, page 58.
    AcceptINScope scope(this, true);
    left = ParseAssignmentExpression();
  }
  ExpressionT right;
  {
    SourceRangeScope range_scope(scanner(), &else_range);
    Expect(Token::COLON);
    right = ParseAssignmentExpression();
  }
  ExpressionT expr = factory()->NewConditional(expression, left, right, pos);
  impl()->RecordConditionalSourceRange(expr, then_range, else_range);
  return expr;
}

// Precedence >= 4
template <typename Impl>
typename ParserBase<Impl>::ExpressionT
ParserBase<Impl>::ParseBinaryContinuation(ExpressionT x, int prec, int prec1) {
  do {
    // prec1 >= 4
    while (Token::Precedence(peek(), accept_IN_) == prec1) {
      SourceRange right_range;
      int pos = peek_position();
      ExpressionT y;
      Token::Value op;
      {
        SourceRangeScope right_range_scope(scanner(), &right_range);
        op = Next();

        const bool is_right_associative = op == Token::EXP;
        const int next_prec = is_right_associative ? prec1 : prec1 + 1;
        y = ParseBinaryExpression(next_prec);
      }

      // For now we distinguish between comparisons and other binary
      // operations.  (We could combine the two and get rid of this
      // code and AST node eventually.)
      if (Token::IsCompareOp(op)) {
        // We have a comparison.
        Token::Value cmp = op;
        switch (op) {
          case Token::NE: cmp = Token::EQ; break;
          case Token::NE_STRICT: cmp = Token::EQ_STRICT; break;
          default: break;
        }
        x = factory()->NewCompareOperation(cmp, x, y, pos);
        if (cmp != op) {
          // The comparison was negated - add a NOT.
          x = factory()->NewUnaryOperation(Token::NOT, x, pos);
        }
      } else if (!impl()->ShortcutNumericLiteralBinaryExpression(&x, y, op,
                                                                 pos) &&
                 !impl()->CollapseNaryExpression(&x, y, op, pos, right_range)) {
        // We have a "normal" binary operation.
        x = factory()->NewBinaryOperation(op, x, y, pos);
        if (op == Token::OR || op == Token::AND) {
          impl()->RecordBinaryOperationSourceRange(x, right_range);
        }
      }
    }
    --prec1;
  } while (prec1 >= prec);

  return x;
}

// Precedence >= 4
template <typename Impl>
typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseBinaryExpression(
    int prec) {
  DCHECK_GE(prec, 4);

  // "#foo in ShiftExpression" needs to be parsed separately, since private
  // identifiers are not valid PrimaryExpressions.
  if (V8_UNLIKELY(FLAG_harmony_private_brand_checks &&
                  peek() == Token::PRIVATE_NAME)) {
    ExpressionT x = ParsePropertyOrPrivatePropertyName();
    int prec1 = Token::Precedence(peek(), accept_IN_);
    if (peek() != Token::IN || prec1 < prec) {
      ReportUnexpectedToken(Token::PRIVATE_NAME);
      return impl()->FailureExpression();
    }
    return ParseBinaryContinuation(x, prec, prec1);
  }

  ExpressionT x = ParseUnaryExpression();
  int prec1 = Token::Precedence(peek(), accept_IN_);
  if (prec1 >= prec) {
    return ParseBinaryContinuation(x, prec, prec1);
  }
  return x;
}

template <typename Impl>
typename ParserBase<Impl>::ExpressionT
ParserBase<Impl>::ParseUnaryOrPrefixExpression() {
  Token::Value op = Next();
  int pos = position();

  // Assume "! function ..." indicates the function is likely to be called.
  if (op == Token::NOT && peek() == Token::FUNCTION) {
    function_state_->set_next_function_is_likely_called();
  }

  CheckStackOverflow();

  int expression_position = peek_position();
  ExpressionT expression = ParseUnaryExpression();

  if (Token::IsUnaryOp(op)) {
    if (op == Token::DELETE) {
      if (impl()->IsIdentifier(expression) && is_strict(language_mode())) {
        // "delete identifier" is a syntax error in strict mode.
        ReportMessage(MessageTemplate::kStrictDelete);
        return impl()->FailureExpression();
      }

      if (impl()->IsPrivateReference(expression)) {
        ReportMessage(MessageTemplate::kDeletePrivateField);
        return impl()->FailureExpression();
      }
    }

    if (peek() == Token::EXP) {
      impl()->ReportMessageAt(
          Scanner::Location(pos, peek_end_position()),
          MessageTemplate::kUnexpectedTokenUnaryExponentiation);
      return impl()->FailureExpression();
    }

    // Allow the parser's implementation to rewrite the expression.
    return impl()->BuildUnaryExpression(expression, op, pos);
  }

  DCHECK(Token::IsCountOp(op));

  if (V8_LIKELY(IsValidReferenceExpression(expression))) {
    if (impl()->IsIdentifier(expression)) {
      expression_scope()->MarkIdentifierAsAssigned();
    }
  } else {
    const bool early_error = false;
    expression = RewriteInvalidReferenceExpression(
        expression, expression_position, end_position(),
        MessageTemplate::kInvalidLhsInPrefixOp, early_error);
  }

  return factory()->NewCountOperation(op, true /* prefix */, expression,
                                      position());
}

template <typename Impl>
typename ParserBase<Impl>::ExpressionT
ParserBase<Impl>::ParseAwaitExpression() {
  expression_scope()->RecordParameterInitializerError(
      scanner()->peek_location(),
      MessageTemplate::kAwaitExpressionFormalParameter);
  int await_pos = peek_position();
  Consume(Token::AWAIT);
  if (V8_UNLIKELY(scanner()->literal_contains_escapes())) {
    impl()->ReportUnexpectedToken(Token::ESCAPED_KEYWORD);
  }

  CheckStackOverflow();

  ExpressionT value = ParseUnaryExpression();

  // 'await' is a unary operator according to the spec, even though it's treated
  // specially in the parser.
  if (peek() == Token::EXP) {
    impl()->ReportMessageAt(
        Scanner::Location(await_pos, peek_end_position()),
        MessageTemplate::kUnexpectedTokenUnaryExponentiation);
    return impl()->FailureExpression();
  }

  ExpressionT expr = factory()->NewAwait(value, await_pos);
  function_state_->AddSuspend();
  impl()->RecordSuspendSourceRange(expr, PositionAfterSemicolon());
  return expr;
}

template <typename Impl>
typename ParserBase<Impl>::ExpressionT
ParserBase<Impl>::ParseUnaryExpression() {
  // UnaryExpression ::
  //   PostfixExpression
  //   'delete' UnaryExpression
  //   'void' UnaryExpression
  //   'typeof' UnaryExpression
  //   '++' UnaryExpression
  //   '--' UnaryExpression
  //   '+' UnaryExpression
  //   '-' UnaryExpression
  //   '~' UnaryExpression
  //   '!' UnaryExpression
  //   [+Await] AwaitExpression[?Yield]

  Token::Value op = peek();
  if (Token::IsUnaryOrCountOp(op)) return ParseUnaryOrPrefixExpression();
  if (is_await_allowed() && op == Token::AWAIT) {
    return ParseAwaitExpression();
  }
  return ParsePostfixExpression();
}

template <typename Impl>
typename ParserBase<Impl>::ExpressionT
ParserBase<Impl>::ParsePostfixExpression() {
  // PostfixExpression ::
  //   LeftHandSideExpression ('++' | '--')?

  int lhs_beg_pos = peek_position();
  ExpressionT expression = ParseLeftHandSideExpression();
  if (V8_LIKELY(!Token::IsCountOp(peek()) ||
                scanner()->HasLineTerminatorBeforeNext())) {
    return expression;
  }
  return ParsePostfixContinuation(expression, lhs_beg_pos);
}

template <typename Impl>
typename ParserBase<Impl>::ExpressionT
ParserBase<Impl>::ParsePostfixContinuation(ExpressionT expression,
                                           int lhs_beg_pos) {
  if (V8_UNLIKELY(!IsValidReferenceExpression(expression))) {
    const bool early_error = false;
    expression = RewriteInvalidReferenceExpression(
        expression, lhs_beg_pos, end_position(),
        MessageTemplate::kInvalidLhsInPostfixOp, early_error);
  }
  if (impl()->IsIdentifier(expression)) {
    expression_scope()->MarkIdentifierAsAssigned();
  }

  Token::Value next = Next();
  return factory()->NewCountOperation(next, false /* postfix */, expression,
                                      position());
}

template <typename Impl>
typename ParserBase<Impl>::ExpressionT
ParserBase<Impl>::ParseLeftHandSideExpression() {
  // LeftHandSideExpression ::
  //   (NewExpression | MemberExpression) ...

  ExpressionT result = ParseMemberExpression();
  if (!Token::IsPropertyOrCall(peek())) return result;
  return ParseLeftHandSideContinuation(result);
}

template <typename Impl>
typename ParserBase<Impl>::ExpressionT
ParserBase<Impl>::ParseLeftHandSideContinuation(ExpressionT result) {
  DCHECK(Token::IsPropertyOrCall(peek()));

  if (V8_UNLIKELY(peek() == Token::LPAREN && impl()->IsIdentifier(result) &&
                  scanner()->current_token() == Token::ASYNC &&
                  !scanner()->HasLineTerminatorBeforeNext() &&
                  !scanner()->literal_contains_escapes())) {
    DCHECK(impl()->IsAsync(impl()->AsIdentifier(result)));
    int pos = position();

    ArrowHeadParsingScope maybe_arrow(impl(),
                                      FunctionKind::kAsyncArrowFunction);
    Scope::Snapshot scope_snapshot(scope());

    ExpressionListT args(pointer_buffer());
    bool has_spread;
    ParseArguments(&args, &has_spread, kMaybeArrowHead);
    if (V8_LIKELY(peek() == Token::ARROW)) {
      fni_.RemoveAsyncKeywordFromEnd();
      next_arrow_function_info_.scope = maybe_arrow.ValidateAndCreateScope();
      scope_snapshot.Reparent(next_arrow_function_info_.scope);
      // async () => ...
      if (!args.length()) return factory()->NewEmptyParentheses(pos);
      // async ( Arguments ) => ...
      result = impl()->ExpressionListToExpression(args);
      result->mark_parenthesized();
      return result;
    }

    result = factory()->NewCall(result, args, pos, has_spread);

    maybe_arrow.ValidateExpression();

    fni_.RemoveLastFunction();
    if (!Token::IsPropertyOrCall(peek())) return result;
  }

  bool optional_chaining = false;
  bool is_optional = false;
  int optional_link_begin;
  do {
    switch (peek()) {
      case Token::QUESTION_PERIOD: {
        if (is_optional) {
          ReportUnexpectedToken(peek());
          return impl()->FailureExpression();
        }
        // Include the ?. in the source range position.
        optional_link_begin = scanner()->peek_location().beg_pos;
        Consume(Token::QUESTION_PERIOD);
        is_optional = true;
        optional_chaining = true;
        if (Token::IsPropertyOrCall(peek())) continue;
        int pos = position();
        ExpressionT key = ParsePropertyOrPrivatePropertyName();
        result = factory()->NewProperty(result, key, pos, is_optional);
        break;
      }

      /* Property */
      case Token::LBRACK: {
        Consume(Token::LBRACK);
        int pos = position();
        AcceptINScope scope(this, true);
        ExpressionT index = ParseExpressionCoverGrammar();
        result = factory()->NewProperty(result, index, pos, is_optional);
        Expect(Token::RBRACK);
        break;
      }

      /* Property */
      case Token::PERIOD: {
        if (is_optional) {
          ReportUnexpectedToken(Next());
          return impl()->FailureExpression();
        }
        Consume(Token::PERIOD);
        int pos = position();
        ExpressionT key = ParsePropertyOrPrivatePropertyName();
        result = factory()->NewProperty(result, key, pos, is_optional);
        break;
      }

      /* Call */
      case Token::LPAREN: {
        int pos;
        if (Token::IsCallable(scanner()->current_token())) {
          // For call of an identifier we want to report position of
          // the identifier as position of the call in the stack trace.
          pos = position();
        } else {
          // For other kinds of calls we record position of the parenthesis as
          // position of the call. Note that this is extremely important for
          // expressions of the form function(){...}() for which call position
          // should not point to the closing brace otherwise it will intersect
          // with positions recorded for function literal and confuse debugger.
          pos = peek_position();
          // Also the trailing parenthesis are a hint that the function will
          // be called immediately. If we happen to have parsed a preceding
          // function literal eagerly, we can also compile it eagerly.
          if (result->IsFunctionLiteral()) {
            result->AsFunctionLiteral()->SetShouldEagerCompile();
          }
        }
        bool has_spread;
        ExpressionListT args(pointer_buffer());
        ParseArguments(&args, &has_spread);

        // Keep track of eval() calls since they disable all local variable
        // optimizations.
        // The calls that need special treatment are the
        // direct eval calls. These calls are all of the form eval(...), with
        // no explicit receiver.
        // These calls are marked as potentially direct eval calls. Whether
        // they are actually direct calls to eval is determined at run time.
        Call::PossiblyEval is_possibly_eval =
            CheckPossibleEvalCall(result, is_optional, scope());

        result = factory()->NewCall(result, args, pos, has_spread,
                                    is_possibly_eval, is_optional);

        fni_.RemoveLastFunction();
        break;
      }

      default:
        // Template literals in/after an Optional Chain not supported:
        if (optional_chaining) {
          impl()->ReportMessageAt(scanner()->peek_location(),
                                  MessageTemplate::kOptionalChainingNoTemplate);
          return impl()->FailureExpression();
        }
        /* Tagged Template */
        DCHECK(Token::IsTemplate(peek()));
        result = ParseTemplateLiteral(result, position(), true);
        break;
    }
    if (is_optional) {
      SourceRange chain_link_range(optional_link_begin, end_position());
      impl()->RecordExpressionSourceRange(result, chain_link_range);
      is_optional = false;
    }
  } while (Token::IsPropertyOrCall(peek()));
  if (optional_chaining) return factory()->NewOptionalChain(result);
  return result;
}

template <typename Impl>
typename ParserBase<Impl>::ExpressionT
ParserBase<Impl>::ParseMemberWithPresentNewPrefixesExpression() {
  // NewExpression ::
  //   ('new')+ MemberExpression
  //
  // NewTarget ::
  //   'new' '.' 'target'

  // The grammar for new expressions is pretty warped. We can have several 'new'
  // keywords following each other, and then a MemberExpression. When we see '('
  // after the MemberExpression, it's associated with the rightmost unassociated
  // 'new' to create a NewExpression with arguments. However, a NewExpression
  // can also occur without arguments.

  // Examples of new expression:
  // new foo.bar().baz means (new (foo.bar)()).baz
  // new foo()() means (new foo())()
  // new new foo()() means (new (new foo())())
  // new new foo means new (new foo)
  // new new foo() means new (new foo())
  // new new foo().bar().baz means (new (new foo()).bar()).baz
  // new super.x means new (super.x)
  Consume(Token::NEW);
  int new_pos = position();
  ExpressionT result;

  CheckStackOverflow();

  if (peek() == Token::IMPORT && PeekAhead() == Token::LPAREN) {
    impl()->ReportMessageAt(scanner()->peek_location(),
                            MessageTemplate::kImportCallNotNewExpression);
    return impl()->FailureExpression();
  } else if (peek() == Token::PERIOD) {
    result = ParseNewTargetExpression();
    return ParseMemberExpressionContinuation(result);
  } else {
    result = ParseMemberExpression();
    if (result->IsSuperCallReference()) {
      // new super() is never allowed
      impl()->ReportMessageAt(scanner()->location(),
                              MessageTemplate::kUnexpectedSuper);
      return impl()->FailureExpression();
    }
  }
  if (peek() == Token::LPAREN) {
    // NewExpression with arguments.
    {
      ExpressionListT args(pointer_buffer());
      bool has_spread;
      ParseArguments(&args, &has_spread);

      result = factory()->NewCallNew(result, args, new_pos, has_spread);
    }
    // The expression can still continue with . or [ after the arguments.
    return ParseMemberExpressionContinuation(result);
  }

  if (peek() == Token::QUESTION_PERIOD) {
    impl()->ReportMessageAt(scanner()->peek_location(),
                            MessageTemplate::kOptionalChainingNoNew);
    return impl()->FailureExpression();
  }

  // NewExpression without arguments.
  ExpressionListT args(pointer_buffer());
  return factory()->NewCallNew(result, args, new_pos, false);
}

template <typename Impl>
typename ParserBase<Impl>::ExpressionT
ParserBase<Impl>::ParseFunctionExpression() {
  Consume(Token::FUNCTION);
  int function_token_position = position();

  FunctionKind function_kind = Check(Token::MUL)
                                   ? FunctionKind::kGeneratorFunction
                                   : FunctionKind::kNormalFunction;
  IdentifierT name = impl()->NullIdentifier();
  bool is_strict_reserved_name = Token::IsStrictReservedWord(peek());
  Scanner::Location function_name_location = Scanner::Location::invalid();
  FunctionSyntaxKind function_syntax_kind =
      FunctionSyntaxKind::kAnonymousExpression;
  if (impl()->ParsingDynamicFunctionDeclaration()) {
    // We don't want dynamic functions to actually declare their name
    // "anonymous". We just want that name in the toString().
    Consume(Token::IDENTIFIER);
    DCHECK_IMPLIES(!has_error(),
                   scanner()->CurrentSymbol(ast_value_factory()) ==
                       ast_value_factory()->anonymous_string());
  } else if (peek_any_identifier()) {
    name = ParseIdentifier(function_kind);
    function_name_location = scanner()->location();
    function_syntax_kind = FunctionSyntaxKind::kNamedExpression;
  }
  FunctionLiteralT result = impl()->ParseFunctionLiteral(
      name, function_name_location,
      is_strict_reserved_name ? kFunctionNameIsStrictReserved
                              : kFunctionNameValidityUnknown,
      function_kind, function_token_position, function_syntax_kind,
      language_mode(), nullptr);
  // TODO(verwaest): FailureFunctionLiteral?
  if (impl()->IsNull(result)) return impl()->FailureExpression();
  return result;
}

template <typename Impl>
typename ParserBase<Impl>::ExpressionT
ParserBase<Impl>::ParseMemberExpression() {
  // MemberExpression ::
  //   (PrimaryExpression | FunctionLiteral | ClassLiteral)
  //     ('[' Expression ']' | '.' Identifier | Arguments | TemplateLiteral)*
  //
  // CallExpression ::
  //   (SuperCall | ImportCall)
  //     ('[' Expression ']' | '.' Identifier | Arguments | TemplateLiteral)*
  //
  // The '[' Expression ']' and '.' Identifier parts are parsed by
  // ParseMemberExpressionContinuation, and everything preceeding it is merged
  // into ParsePrimaryExpression.

  // Parse the initial primary or function expression.
  ExpressionT result = ParsePrimaryExpression();
  return ParseMemberExpressionContinuation(result);
}

template <typename Impl>
typename ParserBase<Impl>::ExpressionT
ParserBase<Impl>::ParseImportExpressions() {
  Consume(Token::IMPORT);
  int pos = position();
  if (Check(Token::PERIOD)) {
    ExpectContextualKeyword(ast_value_factory()->meta_string(), "import.meta",
                            pos);
    if (!flags().is_module()) {
      impl()->ReportMessageAt(scanner()->location(),
                              MessageTemplate::kImportMetaOutsideModule);
      return impl()->FailureExpression();
    }

    return impl()->ImportMetaExpression(pos);
  }

  if (V8_UNLIKELY(peek() != Token::LPAREN)) {
    if (!flags().is_module()) {
      impl()->ReportMessageAt(scanner()->location(),
                              MessageTemplate::kImportOutsideModule);
    } else {
      ReportUnexpectedToken(Next());
    }
    return impl()->FailureExpression();
  }

  Consume(Token::LPAREN);
  if (peek() == Token::RPAREN) {
    impl()->ReportMessageAt(scanner()->location(),
                            MessageTemplate::kImportMissingSpecifier);
    return impl()->FailureExpression();
  }

  AcceptINScope scope(this, true);
  ExpressionT specifier = ParseAssignmentExpressionCoverGrammar();

  if (FLAG_harmony_import_assertions && Check(Token::COMMA)) {
    if (Check(Token::RPAREN)) {
      // A trailing comma allowed after the specifier.
      return factory()->NewImportCallExpression(specifier, pos);
    } else {
      ExpressionT import_assertions = ParseAssignmentExpressionCoverGrammar();
      Check(Token::COMMA);  // A trailing comma is allowed after the import
                            // assertions.
      Expect(Token::RPAREN);
      return factory()->NewImportCallExpression(specifier, import_assertions,
                                                pos);
    }
  }

  Expect(Token::RPAREN);
  return factory()->NewImportCallExpression(specifier, pos);
}

template <typename Impl>
typename ParserBase<Impl>::ExpressionT
ParserBase<Impl>::ParseSuperExpression() {
  Consume(Token::SUPER);
  int pos = position();

  DeclarationScope* scope = GetReceiverScope();
  FunctionKind kind = scope->function_kind();
  if (IsConciseMethod(kind) || IsAccessorFunction(kind) ||
      IsClassConstructor(kind)) {
    if (Token::IsProperty(peek())) {
      if (peek() == Token::PERIOD && PeekAhead() == Token::PRIVATE_NAME) {
        Consume(Token::PERIOD);
        Consume(Token::PRIVATE_NAME);

        impl()->ReportMessage(MessageTemplate::kUnexpectedPrivateField);
        return impl()->FailureExpression();
      }
      if (peek() == Token::QUESTION_PERIOD) {
        Consume(Token::QUESTION_PERIOD);
        impl()->ReportMessage(MessageTemplate::kOptionalChainingNoSuper);
        return impl()->FailureExpression();
      }
      Scope* home_object_scope = scope->RecordSuperPropertyUsage();
      UseThis();
      return impl()->NewSuperPropertyReference(home_object_scope, pos);
    }
    // super() is only allowed in derived constructor. new super() is never
    // allowed; it's reported as an error by
    // ParseMemberWithPresentNewPrefixesExpression.
    if (peek() == Token::LPAREN && IsDerivedConstructor(kind)) {
      // TODO(rossberg): This might not be the correct FunctionState for the
      // method here.
      expression_scope()->RecordThisUse();
      UseThis();
      return impl()->NewSuperCallReference(pos);
    }
  }

  impl()->ReportMessageAt(scanner()->location(),
                          MessageTemplate::kUnexpectedSuper);
  return impl()->FailureExpression();
}

template <typename Impl>
typename ParserBase<Impl>::ExpressionT
ParserBase<Impl>::ParseNewTargetExpression() {
  int pos = position();
  Consume(Token::PERIOD);
  ExpectContextualKeyword(ast_value_factory()->target_string(), "new.target",
                          pos);

  if (!GetReceiverScope()->is_function_scope()) {
    impl()->ReportMessageAt(scanner()->location(),
                            MessageTemplate::kUnexpectedNewTarget);
    return impl()->FailureExpression();
  }

  return impl()->NewTargetExpression(pos);
}

template <typename Impl>
typename ParserBase<Impl>::ExpressionT
ParserBase<Impl>::DoParseMemberExpressionContinuation(ExpressionT expression) {
  DCHECK(Token::IsMember(peek()));
  // Parses this part of MemberExpression:
  // ('[' Expression ']' | '.' Identifier | TemplateLiteral)*
  do {
    switch (peek()) {
      case Token::LBRACK: {
        Consume(Token::LBRACK);
        int pos = position();
        AcceptINScope scope(this, true);
        ExpressionT index = ParseExpressionCoverGrammar();
        expression = factory()->NewProperty(expression, index, pos);
        impl()->PushPropertyName(index);
        Expect(Token::RBRACK);
        break;
      }
      case Token::PERIOD: {
        Consume(Token::PERIOD);
        int pos = peek_position();
        ExpressionT key = ParsePropertyOrPrivatePropertyName();
        expression = factory()->NewProperty(expression, key, pos);
        break;
      }
      default: {
        DCHECK(Token::IsTemplate(peek()));
        int pos;
        if (scanner()->current_token() == Token::IDENTIFIER) {
          pos = position();
        } else {
          pos = peek_position();
          if (expression->IsFunctionLiteral()) {
            // If the tag function looks like an IIFE, set_parenthesized() to
            // force eager compilation.
            expression->AsFunctionLiteral()->SetShouldEagerCompile();
          }
        }
        expression = ParseTemplateLiteral(expression, pos, true);
        break;
      }
    }
  } while (Token::IsMember(peek()));
  return expression;
}

template <typename Impl>
void ParserBase<Impl>::ParseFormalParameter(FormalParametersT* parameters) {
  // FormalParameter[Yield,GeneratorParameter] :
  //   BindingElement[?Yield, ?GeneratorParameter]
  FuncNameInferrerState fni_state(&fni_);
  int pos = peek_position();
  auto declaration_it = scope()->declarations()->end();
  ExpressionT pattern = ParseBindingPattern();
  if (impl()->IsIdentifier(pattern)) {
    ClassifyParameter(impl()->AsIdentifier(pattern), pos, end_position());
  } else {
    parameters->is_simple = false;
  }

  ExpressionT initializer = impl()->NullExpression();
  if (Check(Token::ASSIGN)) {
    parameters->is_simple = false;

    if (parameters->has_rest) {
      ReportMessage(MessageTemplate::kRestDefaultInitializer);
      return;
    }

    AcceptINScope accept_in_scope(this, true);
    initializer = ParseAssignmentExpression();
    impl()->SetFunctionNameFromIdentifierRef(initializer, pattern);
  }

  auto declaration_end = scope()->declarations()->end();
  int initializer_end = end_position();
  for (; declaration_it != declaration_end; ++declaration_it) {
    Variable* var = declaration_it->var();

    // The first time a variable is initialized (i.e. when the initializer
    // position is unset), clear its maybe_assigned flag as it is not a true
    // assignment. Since this is done directly on the Variable objects, it has
    // no effect on VariableProxy objects appearing on the left-hand side of
    // true assignments, so x will be still be marked as maybe_assigned for:
    // (x = 1, y = (x = 2)) => {}
    // and even:
    // (x = (x = 2)) => {}.
    if (var->initializer_position() == kNoSourcePosition)
      var->clear_maybe_assigned();
    var->set_initializer_position(initializer_end);
  }

  impl()->AddFormalParameter(parameters, pattern, initializer, end_position(),
                             parameters->has_rest);
}

template <typename Impl>
void ParserBase<Impl>::ParseFormalParameterList(FormalParametersT* parameters) {
  // FormalParameters[Yield] :
  //   [empty]
  //   FunctionRestParameter[?Yield]
  //   FormalParameterList[?Yield]
  //   FormalParameterList[?Yield] ,
  //   FormalParameterList[?Yield] , FunctionRestParameter[?Yield]
  //
  // FormalParameterList[Yield] :
  //   FormalParameter[?Yield]
  //   FormalParameterList[?Yield] , FormalParameter[?Yield]
  ParameterParsingScope scope(impl(), parameters);

  DCHECK_EQ(0, parameters->arity);

  if (peek() != Token::RPAREN) {
    while (true) {
      // Add one since we're going to be adding a parameter.
      if (parameters->arity + 1 > Code::kMaxArguments) {
        ReportMessage(MessageTemplate::kTooManyParameters);
        return;
      }
      parameters->has_rest = Check(Token::ELLIPSIS);
      ParseFormalParameter(parameters);

      if (parameters->has_rest) {
        parameters->is_simple = false;
        if (peek() == Token::COMMA) {
          impl()->ReportMessageAt(scanner()->peek_location(),
                                  MessageTemplate::kParamAfterRest);
          return;
        }
        break;
      }
      if (!Check(Token::COMMA)) break;
      if (peek() == Token::RPAREN) {
        // allow the trailing comma
        break;
      }
    }
  }

  impl()->DeclareFormalParameters(parameters);
}

template <typename Impl>
void ParserBase<Impl>::ParseVariableDeclarations(
    VariableDeclarationContext var_context,
    DeclarationParsingResult* parsing_result,
    ZonePtrList<const AstRawString>* names) {
  // VariableDeclarations ::
  //   ('var' | 'const' | 'let') (Identifier ('=' AssignmentExpression)?)+[',']
  //
  // ES6:
  // FIXME(marja, nikolaos): Add an up-to-date comment about ES6 variable
  // declaration syntax.

  DCHECK_NOT_NULL(parsing_result);
  parsing_result->descriptor.kind = NORMAL_VARIABLE;
  parsing_result->descriptor.declaration_pos = peek_position();
  parsing_result->descriptor.initialization_pos = peek_position();

  switch (peek()) {
    case Token::VAR:
      parsing_result->descriptor.mode = VariableMode::kVar;
      Consume(Token::VAR);
      break;
    case Token::CONST:
      Consume(Token::CONST);
      DCHECK_NE(var_context, kStatement);
      parsing_result->descriptor.mode = VariableMode::kConst;
      break;
    case Token::LET:
      Consume(Token::LET);
      DCHECK_NE(var_context, kStatement);
      parsing_result->descriptor.mode = VariableMode::kLet;
      break;
    default:
      UNREACHABLE();  // by current callers
      break;
  }

  VariableDeclarationParsingScope declaration(
      impl(), parsing_result->descriptor.mode, names);
  Scope* target_scope = IsLexicalVariableMode(parsing_result->descriptor.mode)
                            ? scope()
                            : scope()->GetDeclarationScope();

  auto declaration_it = target_scope->declarations()->end();

  int bindings_start = peek_position();
  do {
    // Parse binding pattern.
    FuncNameInferrerState fni_state(&fni_);

    int decl_pos = peek_position();

    IdentifierT name;
    ExpressionT pattern;
    // Check for an identifier first, so that we can elide the pattern in cases
    // where there is no initializer (and so no proxy needs to be created).
    if (V8_LIKELY(Token::IsAnyIdentifier(peek()))) {
      name = ParseAndClassifyIdentifier(Next());
      if (V8_UNLIKELY(is_strict(language_mode()) &&
                      impl()->IsEvalOrArguments(name))) {
        impl()->ReportMessageAt(scanner()->location(),
                                MessageTemplate::kStrictEvalArguments);
        return;
      }
      if (peek() == Token::ASSIGN ||
          (var_context == kForStatement && PeekInOrOf()) ||
          parsing_result->descriptor.mode == VariableMode::kLet) {
        // Assignments need the variable expression for the assignment LHS, and
        // for of/in will need it later, so create the expression now.
        pattern = impl()->ExpressionFromIdentifier(name, decl_pos);
      } else {
        // Otherwise, elide the variable expression and just declare it.
        impl()->DeclareIdentifier(name, decl_pos);
        pattern = impl()->NullExpression();
      }
    } else {
      name = impl()->NullIdentifier();
      pattern = ParseBindingPattern();
      DCHECK(!impl()->IsIdentifier(pattern));
    }

    Scanner::Location variable_loc = scanner()->location();

    ExpressionT value = impl()->NullExpression();
    int value_beg_pos = kNoSourcePosition;
    if (Check(Token::ASSIGN)) {
      DCHECK(!impl()->IsNull(pattern));
      {
        value_beg_pos = peek_position();
        AcceptINScope scope(this, var_context != kForStatement);
        value = ParseAssignmentExpression();
      }
      variable_loc.end_pos = end_position();

      if (!parsing_result->first_initializer_loc.IsValid()) {
        parsing_result->first_initializer_loc = variable_loc;
      }

      // Don't infer if it is "a = function(){...}();"-like expression.
      if (impl()->IsIdentifier(pattern)) {
        if (!value->IsCall() && !value->IsCallNew()) {
          fni_.Infer();
        } else {
          fni_.RemoveLastFunction();
        }
      }

      impl()->SetFunctionNameFromIdentifierRef(value, pattern);
    } else {
#ifdef DEBUG
      // We can fall through into here on error paths, so don't DCHECK those.
      if (!has_error()) {
        // We should never get identifier patterns for the non-initializer path,
        // as those expressions should be elided.
        DCHECK_EQ(!impl()->IsNull(name),
                  Token::IsAnyIdentifier(scanner()->current_token()));
        DCHECK_IMPLIES(impl()->IsNull(pattern), !impl()->IsNull(name));
        // The only times we have a non-null pattern are:
        //   1. This is a destructuring declaration (with no initializer, which
        //      is immediately an error),
        //   2. This is a declaration in a for in/of loop, or
        //   3. This is a let (which has an implicit undefined initializer)
        DCHECK_IMPLIES(
            !impl()->IsNull(pattern),
            !impl()->IsIdentifier(pattern) ||
                (var_context == kForStatement && PeekInOrOf()) ||
                parsing_result->descriptor.mode == VariableMode::kLet);
      }
#endif

      if (var_context != kForStatement || !PeekInOrOf()) {
        // ES6 'const' and binding patterns require initializers.
        if (parsing_result->descriptor.mode == VariableMode::kConst ||
            impl()->IsNull(name)) {
          impl()->ReportMessageAt(
              Scanner::Location(decl_pos, end_position()),
              MessageTemplate::kDeclarationMissingInitializer,
              impl()->IsNull(name) ? "destructuring" : "const");
          return;
        }
        // 'let x' initializes 'x' to undefined.
        if (parsing_result->descriptor.mode == VariableMode::kLet) {
          value = factory()->NewUndefinedLiteral(position());
        }
      }
    }

    int initializer_position = end_position();
    auto declaration_end = target_scope->declarations()->end();
    for (; declaration_it != declaration_end; ++declaration_it) {
      declaration_it->var()->set_initializer_position(initializer_position);
    }

    // Patterns should be elided iff. they don't have an initializer.
    DCHECK_IMPLIES(impl()->IsNull(pattern),
                   impl()->IsNull(value) ||
                       (var_context == kForStatement && PeekInOrOf()));

    typename DeclarationParsingResult::Declaration decl(pattern, value);
    decl.value_beg_pos = value_beg_pos;

    parsing_result->declarations.push_back(decl);
  } while (Check(Token::COMMA));

  parsing_result->bindings_loc =
      Scanner::Location(bindings_start, end_position());
}

template <typename Impl>
typename ParserBase<Impl>::StatementT
ParserBase<Impl>::ParseFunctionDeclaration() {
  Consume(Token::FUNCTION);

  int pos = position();
  ParseFunctionFlags flags = ParseFunctionFlag::kIsNormal;
  if (Check(Token::MUL)) {
    impl()->ReportMessageAt(
        scanner()->location(),
        MessageTemplate::kGeneratorInSingleStatementContext);
    return impl()->NullStatement();
  }
  return ParseHoistableDeclaration(pos, flags, nullptr, false);
}

template <typename Impl>
typename ParserBase<Impl>::StatementT
ParserBase<Impl>::ParseHoistableDeclaration(
    ZonePtrList<const AstRawString>* names, bool default_export) {
  Consume(Token::FUNCTION);

  int pos = position();
  ParseFunctionFlags flags = ParseFunctionFlag::kIsNormal;
  if (Check(Token::MUL)) {
    flags |= ParseFunctionFlag::kIsGenerator;
  }
  return ParseHoistableDeclaration(pos, flags, names, default_export);
}

template <typename Impl>
typename ParserBase<Impl>::StatementT
ParserBase<Impl>::ParseHoistableDeclaration(
    int pos, ParseFunctionFlags flags, ZonePtrList<const AstRawString>* names,
    bool default_export) {
  CheckStackOverflow();

  // FunctionDeclaration ::
  //   'function' Identifier '(' FormalParameters ')' '{' FunctionBody '}'
  //   'function' '(' FormalParameters ')' '{' FunctionBody '}'
  // GeneratorDeclaration ::
  //   'function' '*' Identifier '(' FormalParameters ')' '{' FunctionBody '}'
  //   'function' '*' '(' FormalParameters ')' '{' FunctionBody '}'
  //
  // The anonymous forms are allowed iff [default_export] is true.
  //
  // 'function' and '*' (if present) have been consumed by the caller.

  DCHECK_IMPLIES((flags & ParseFunctionFlag::kIsAsync) != 0,
                 (flags & ParseFunctionFlag::kIsGenerator) == 0);

  if ((flags & ParseFunctionFlag::kIsAsync) != 0 && Check(Token::MUL)) {
    // Async generator
    flags |= ParseFunctionFlag::kIsGenerator;
  }

  IdentifierT name;
  FunctionNameValidity name_validity;
  IdentifierT variable_name;
  if (peek() == Token::LPAREN) {
    if (default_export) {
      impl()->GetDefaultStrings(&name, &variable_name);
      name_validity = kSkipFunctionNameCheck;
    } else {
      ReportMessage(MessageTemplate::kMissingFunctionName);
      return impl()->NullStatement();
    }
  } else {
    bool is_strict_reserved = Token::IsStrictReservedWord(peek());
    name = ParseIdentifier();
    name_validity = is_strict_reserved ? kFunctionNameIsStrictReserved
                                       : kFunctionNameValidityUnknown;
    variable_name = name;
  }

  FuncNameInferrerState fni_state(&fni_);
  impl()->PushEnclosingName(name);

  FunctionKind function_kind = FunctionKindFor(flags);

  FunctionLiteralT function = impl()->ParseFunctionLiteral(
      name, scanner()->location(), name_validity, function_kind, pos,
      FunctionSyntaxKind::kDeclaration, language_mode(), nullptr);

  // In ES6, a function behaves as a lexical binding, except in
  // a script scope, or the initial scope of eval or another function.
  VariableMode mode =
      (!scope()->is_declaration_scope() || scope()->is_module_scope())
          ? VariableMode::kLet
          : VariableMode::kVar;
  // Async functions don't undergo sloppy mode block scoped hoisting, and don't
  // allow duplicates in a block. Both are represented by the
  // sloppy_block_functions_. Don't add them to the map for async functions.
  // Generators are also supposed to be prohibited; currently doing this behind
  // a flag and UseCounting violations to assess web compatibility.
  VariableKind kind = is_sloppy(language_mode()) &&
                              !scope()->is_declaration_scope() &&
                              flags == ParseFunctionFlag::kIsNormal
                          ? SLOPPY_BLOCK_FUNCTION_VARIABLE
                          : NORMAL_VARIABLE;

  return impl()->DeclareFunction(variable_name, function, mode, kind, pos,
                                 end_position(), names);
}

template <typename Impl>
typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseClassDeclaration(
    ZonePtrList<const AstRawString>* names, bool default_export) {
  // ClassDeclaration ::
  //   'class' Identifier ('extends' LeftHandExpression)? '{' ClassBody '}'
  //   'class' ('extends' LeftHandExpression)? '{' ClassBody '}'
  //
  // The anonymous form is allowed iff [default_export] is true.
  //
  // 'class' is expected to be consumed by the caller.
  //
  // A ClassDeclaration
  //
  //   class C { ... }
  //
  // has the same semantics as:
  //
  //   let C = class C { ... };
  //
  // so rewrite it as such.

  int class_token_pos = position();
  IdentifierT name = impl()->NullIdentifier();
  bool is_strict_reserved = Token::IsStrictReservedWord(peek());
  IdentifierT variable_name = impl()->NullIdentifier();
  if (default_export && (peek() == Token::EXTENDS || peek() == Token::LBRACE)) {
    impl()->GetDefaultStrings(&name, &variable_name);
  } else {
    name = ParseIdentifier();
    variable_name = name;
  }

  ExpressionParsingScope no_expression_scope(impl());
  ExpressionT value = ParseClassLiteral(scope(), name, scanner()->location(),
                                        is_strict_reserved, class_token_pos);
  no_expression_scope.ValidateExpression();
  int end_pos = position();
  return impl()->DeclareClass(variable_name, value, names, class_token_pos,
                              end_pos);
}

// Language extension which is only enabled for source files loaded
// through the API's extension mechanism.  A native function
// declaration is resolved by looking up the function through a
// callback provided by the extension.
template <typename Impl>
typename ParserBase<Impl>::StatementT
ParserBase<Impl>::ParseNativeDeclaration() {
  function_state_->DisableOptimization(BailoutReason::kNativeFunctionLiteral);

  int pos = peek_position();
  Consume(Token::FUNCTION);
  // Allow "eval" or "arguments" for backward compatibility.
  IdentifierT name = ParseIdentifier();
  Expect(Token::LPAREN);
  if (peek() != Token::RPAREN) {
    do {
      ParseIdentifier();
    } while (Check(Token::COMMA));
  }
  Expect(Token::RPAREN);
  Expect(Token::SEMICOLON);
  return impl()->DeclareNative(name, pos);
}

template <typename Impl>
typename ParserBase<Impl>::StatementT
ParserBase<Impl>::ParseAsyncFunctionDeclaration(
    ZonePtrList<const AstRawString>* names, bool default_export) {
  // AsyncFunctionDeclaration ::
  //   async [no LineTerminator here] function BindingIdentifier[Await]
  //       ( FormalParameters[Await] ) { AsyncFunctionBody }
  DCHECK_EQ(scanner()->current_token(), Token::ASYNC);
  if (V8_UNLIKELY(scanner()->literal_contains_escapes())) {
    impl()->ReportUnexpectedToken(Token::ESCAPED_KEYWORD);
  }
  int pos = position();
  DCHECK(!scanner()->HasLineTerminatorBeforeNext());
  Consume(Token::FUNCTION);
  ParseFunctionFlags flags = ParseFunctionFlag::kIsAsync;
  return ParseHoistableDeclaration(pos, flags, names, default_export);
}

template <typename Impl>
void ParserBase<Impl>::ParseFunctionBody(
    StatementListT* body, IdentifierT function_name, int pos,
    const FormalParametersT& parameters, FunctionKind kind,
    FunctionSyntaxKind function_syntax_kind, FunctionBodyType body_type) {
  if (IsResumableFunction(kind)) impl()->PrepareGeneratorVariables();

  DeclarationScope* function_scope = parameters.scope;
  DeclarationScope* inner_scope = function_scope;

  // Building the parameter initialization block declares the parameters.
  // TODO(verwaest): Rely on ArrowHeadParsingScope instead.
  if (V8_UNLIKELY(!parameters.is_simple)) {
    if (has_error()) return;
    BlockT init_block = impl()->BuildParameterInitializationBlock(parameters);
    if (IsAsyncFunction(kind) && !IsAsyncGeneratorFunction(kind)) {
      init_block = impl()->BuildRejectPromiseOnException(init_block);
    }
    body->Add(init_block);
    if (has_error()) return;

    inner_scope = NewVarblockScope();
    inner_scope->set_start_position(scanner()->location().beg_pos);
  }

  StatementListT inner_body(pointer_buffer());

  {
    BlockState block_state(&scope_, inner_scope);

    if (body_type == FunctionBodyType::kExpression) {
      ExpressionT expression = ParseAssignmentExpression();

      if (IsAsyncFunction(kind)) {
        BlockT block = factory()->NewBlock(1, true);
        impl()->RewriteAsyncFunctionBody(&inner_body, block, expression);
      } else {
        inner_body.Add(
            BuildReturnStatement(expression, expression->position()));
      }
    } else {
      DCHECK(accept_IN_);
      DCHECK_EQ(FunctionBodyType::kBlock, body_type);
      // If we are parsing the source as if it is wrapped in a function, the
      // source ends without a closing brace.
      Token::Value closing_token =
          function_syntax_kind == FunctionSyntaxKind::kWrapped ? Token::EOS
                                                               : Token::RBRACE;

      if (IsAsyncGeneratorFunction(kind)) {
        impl()->ParseAndRewriteAsyncGeneratorFunctionBody(pos, kind,
                                                          &inner_body);
      } else if (IsGeneratorFunction(kind)) {
        impl()->ParseAndRewriteGeneratorFunctionBody(pos, kind, &inner_body);
      } else if (IsAsyncFunction(kind)) {
        ParseAsyncFunctionBody(inner_scope, &inner_body);
      } else {
        ParseStatementList(&inner_body, closing_token);
      }

      if (IsDerivedConstructor(kind)) {
        ExpressionParsingScope expression_scope(impl());
        inner_body.Add(factory()->NewReturnStatement(impl()->ThisExpression(),
                                                     kNoSourcePosition));
        expression_scope.ValidateExpression();
      }
      Expect(closing_token);
    }
  }

  scope()->set_end_position(end_position());

  bool allow_duplicate_parameters = false;

  CheckConflictingVarDeclarations(inner_scope);

  if (V8_LIKELY(parameters.is_simple)) {
    DCHECK_EQ(inner_scope, function_scope);
    if (is_sloppy(function_scope->language_mode())) {
      impl()->InsertSloppyBlockFunctionVarBindings(function_scope);
    }
    allow_duplicate_parameters =
        is_sloppy(function_scope->language_mode()) && !IsConciseMethod(kind);
  } else {
    DCHECK_NOT_NULL(inner_scope);
    DCHECK_EQ(function_scope, scope());
    DCHECK_EQ(function_scope, inner_scope->outer_scope());
    impl()->SetLanguageMode(function_scope, inner_scope->language_mode());

    if (is_sloppy(inner_scope->language_mode())) {
      impl()->InsertSloppyBlockFunctionVarBindings(inner_scope);
    }

    inner_scope->set_end_position(end_position());
    if (inner_scope->FinalizeBlockScope() != nullptr) {
      BlockT inner_block = factory()->NewBlock(true, inner_body);
      inner_body.Rewind();
      inner_body.Add(inner_block);
      inner_block->set_scope(inner_scope);
      impl()->RecordBlockSourceRange(inner_block, scope()->end_position());
      if (!impl()->HasCheckedSyntax()) {
        const AstRawString* conflict = inner_scope->FindVariableDeclaredIn(
            function_scope, VariableMode::kLastLexicalVariableMode);
        if (conflict != nullptr) {
          impl()->ReportVarRedeclarationIn(conflict, inner_scope);
        }
      }
      impl()->InsertShadowingVarBindingInitializers(inner_block);
    }
  }

  ValidateFormalParameters(language_mode(), parameters,
                           allow_duplicate_parameters);

  if (!IsArrowFunction(kind)) {
    // Declare arguments after parsing the function since lexical 'arguments'
    // masks the arguments object. Declare arguments before declaring the
    // function var since the arguments object masks 'function arguments'.
    function_scope->DeclareArguments(ast_value_factory());
  }

  impl()->DeclareFunctionNameVar(function_name, function_syntax_kind,
                                 function_scope);

  inner_body.MergeInto(body);
}

template <typename Impl>
void ParserBase<Impl>::CheckArityRestrictions(int param_count,
                                              FunctionKind function_kind,
                                              bool has_rest,
                                              int formals_start_pos,
                                              int formals_end_pos) {
  if (impl()->HasCheckedSyntax()) return;
  if (IsGetterFunction(function_kind)) {
    if (param_count != 0) {
      impl()->ReportMessageAt(
          Scanner::Location(formals_start_pos, formals_end_pos),
          MessageTemplate::kBadGetterArity);
    }
  } else if (IsSetterFunction(function_kind)) {
    if (param_count != 1) {
      impl()->ReportMessageAt(
          Scanner::Location(formals_start_pos, formals_end_pos),
          MessageTemplate::kBadSetterArity);
    }
    if (has_rest) {
      impl()->ReportMessageAt(
          Scanner::Location(formals_start_pos, formals_end_pos),
          MessageTemplate::kBadSetterRestParameter);
    }
  }
}

template <typename Impl>
bool ParserBase<Impl>::IsNextLetKeyword() {
  DCHECK_EQ(Token::LET, peek());
  Token::Value next_next = PeekAhead();
  switch (next_next) {
    case Token::LBRACE:
    case Token::LBRACK:
    case Token::IDENTIFIER:
    case Token::STATIC:
    case Token::LET:  // `let let;` is disallowed by static semantics, but the
                      // token must be first interpreted as a keyword in order
                      // for those semantics to apply. This ensures that ASI is
                      // not honored when a LineTerminator separates the
                      // tokens.
    case Token::YIELD:
    case Token::AWAIT:
    case Token::GET:
    case Token::SET:
    case Token::ASYNC:
      return true;
    case Token::FUTURE_STRICT_RESERVED_WORD:
    case Token::ESCAPED_STRICT_RESERVED_WORD:
      // The early error rule for future reserved keywords
      // (ES#sec-identifiers-static-semantics-early-errors) uses the static
      // semantics StringValue of IdentifierName, which normalizes escape
      // sequences. So, both escaped and unescaped future reserved keywords are
      // allowed as identifiers in sloppy mode.
      return is_sloppy(language_mode());
    default:
      return false;
  }
}

template <typename Impl>
typename ParserBase<Impl>::ExpressionT
ParserBase<Impl>::ParseArrowFunctionLiteral(
    const FormalParametersT& formal_parameters) {
  RCS_SCOPE(runtime_call_stats_,
            Impl::IsPreParser()
                ? RuntimeCallCounterId::kPreParseArrowFunctionLiteral
                : RuntimeCallCounterId::kParseArrowFunctionLiteral,
            RuntimeCallStats::kThreadSpecific);
  base::ElapsedTimer timer;
  if (V8_UNLIKELY(FLAG_log_function_events)) timer.Start();

  DCHECK_IMPLIES(!has_error(), peek() == Token::ARROW);
  if (!impl()->HasCheckedSyntax() && scanner_->HasLineTerminatorBeforeNext()) {
    // ASI inserts `;` after arrow parameters if a line terminator is found.
    // `=> ...` is never a valid expression, so report as syntax error.
    // If next token is not `=>`, it's a syntax error anyways.
    impl()->ReportUnexpectedTokenAt(scanner_->peek_location(), Token::ARROW);
    return impl()->FailureExpression();
  }

  int expected_property_count = 0;
  int suspend_count = 0;
  int function_literal_id = GetNextFunctionLiteralId();

  FunctionKind kind = formal_parameters.scope->function_kind();
  FunctionLiteral::EagerCompileHint eager_compile_hint =
      default_eager_compile_hint_;
  bool can_preparse = impl()->parse_lazily() &&
                      eager_compile_hint == FunctionLiteral::kShouldLazyCompile;
  // TODO(marja): consider lazy-parsing inner arrow functions too. is_this
  // handling in Scope::ResolveVariable needs to change.
  bool is_lazy_top_level_function =
      can_preparse && impl()->AllowsLazyParsingWithoutUnresolvedVariables();
  bool has_braces = true;
  ProducedPreparseData* produced_preparse_data = nullptr;
  StatementListT body(pointer_buffer());
  {
    FunctionState function_state(&function_state_, &scope_,
                                 formal_parameters.scope);

    Consume(Token::ARROW);

    if (peek() == Token::LBRACE) {
      // Multiple statement body
      DCHECK_EQ(scope(), formal_parameters.scope);

      if (is_lazy_top_level_function) {
        // FIXME(marja): Arrow function parameters will be parsed even if the
        // body is preparsed; move relevant parts of parameter handling to
        // simulate consistent parameter handling.

        // Building the parameter initialization block declares the parameters.
        // TODO(verwaest): Rely on ArrowHeadParsingScope instead.
        if (!formal_parameters.is_simple) {
          impl()->BuildParameterInitializationBlock(formal_parameters);
          if (has_error()) return impl()->FailureExpression();
        }

        // For arrow functions, we don't need to retrieve data about function
        // parameters.
        int dummy_num_parameters = -1;
        int dummy_function_length = -1;
        DCHECK(IsArrowFunction(kind));
        bool did_preparse_successfully = impl()->SkipFunction(
            nullptr, kind, FunctionSyntaxKind::kAnonymousExpression,
            formal_parameters.scope, &dummy_num_parameters,
            &dummy_function_length, &produced_preparse_data);

        DCHECK_NULL(produced_preparse_data);

        if (did_preparse_successfully) {
          // Validate parameter names. We can do this only after preparsing the
          // function, since the function can declare itself strict.
          ValidateFormalParameters(language_mode(), formal_parameters, false);
        } else {
          // In case we did not sucessfully preparse the function because of an
          // unidentified error we do a full reparse to return the error.
          // Parse again in the outer scope, since the language mode may change.
          BlockState block_state(&scope_, scope()->outer_scope());
          ExpressionT expression = ParseConditionalExpression();
          // Reparsing the head may have caused a stack overflow.
          if (has_error()) return impl()->FailureExpression();

          DeclarationScope* function_scope = next_arrow_function_info_.scope;
          FunctionState inner_function_state(&function_state_, &scope_,
                                             function_scope);
          Scanner::Location loc(function_scope->start_position(),
                                end_position());
          FormalParametersT parameters(function_scope);
          parameters.is_simple = function_scope->has_simple_parameters();
          impl()->DeclareArrowFunctionFormalParameters(&parameters, expression,
                                                       loc);
          next_arrow_function_info_.Reset();

          Consume(Token::ARROW);
          Consume(Token::LBRACE);

          AcceptINScope scope(this, true);
          FunctionParsingScope body_parsing_scope(impl());
          ParseFunctionBody(&body, impl()->NullIdentifier(), kNoSourcePosition,
                            parameters, kind,
                            FunctionSyntaxKind::kAnonymousExpression,
                            FunctionBodyType::kBlock);
          CHECK(has_error());
          return impl()->FailureExpression();
        }
      } else {
        Consume(Token::LBRACE);
        AcceptINScope scope(this, true);
        FunctionParsingScope body_parsing_scope(impl());
        ParseFunctionBody(&body, impl()->NullIdentifier(), kNoSourcePosition,
                          formal_parameters, kind,
                          FunctionSyntaxKind::kAnonymousExpression,
                          FunctionBodyType::kBlock);
        expected_property_count = function_state.expected_property_count();
      }
    } else {
      // Single-expression body
      has_braces = false;
      FunctionParsingScope body_parsing_scope(impl());
      ParseFunctionBody(&body, impl()->NullIdentifier(), kNoSourcePosition,
                        formal_parameters, kind,
                        FunctionSyntaxKind::kAnonymousExpression,
                        FunctionBodyType::kExpression);
      expected_property_count = function_state.expected_property_count();
    }

    formal_parameters.scope->set_end_position(end_position());

    // Validate strict mode.
    if (is_strict(language_mode())) {
      CheckStrictOctalLiteral(formal_parameters.scope->start_position(),
                              end_position());
    }
    suspend_count = function_state.suspend_count();
  }

  FunctionLiteralT function_literal = factory()->NewFunctionLiteral(
      impl()->EmptyIdentifierString(), formal_parameters.scope, body,
      expected_property_count, formal_parameters.num_parameters(),
      formal_parameters.function_length,
      FunctionLiteral::kNoDuplicateParameters,
      FunctionSyntaxKind::kAnonymousExpression, eager_compile_hint,
      formal_parameters.scope->start_position(), has_braces,
      function_literal_id, produced_preparse_data);

  function_literal->set_suspend_count(suspend_count);
  function_literal->set_function_token_position(
      formal_parameters.scope->start_position());

  impl()->RecordFunctionLiteralSourceRange(function_literal);
  impl()->AddFunctionForNameInference(function_literal);

  if (V8_UNLIKELY((FLAG_log_function_events))) {
    Scope* scope = formal_parameters.scope;
    double ms = timer.Elapsed().InMillisecondsF();
    const char* event_name =
        is_lazy_top_level_function ? "preparse-no-resolution" : "parse";
    const char* name = "arrow function";
    logger_->FunctionEvent(event_name, flags().script_id(), ms,
                           scope->start_position(), scope->end_position(), name,
                           strlen(name));
  }

  return function_literal;
}

template <typename Impl>
typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseClassExpression(
    Scope* outer_scope) {
  Consume(Token::CLASS);
  int class_token_pos = position();
  IdentifierT name = impl()->NullIdentifier();
  bool is_strict_reserved_name = false;
  Scanner::Location class_name_location = Scanner::Location::invalid();
  if (peek_any_identifier()) {
    name = ParseAndClassifyIdentifier(Next());
    class_name_location = scanner()->location();
    is_strict_reserved_name =
        Token::IsStrictReservedWord(scanner()->current_token());
  }
  return ParseClassLiteral(outer_scope, name, class_name_location,
                           is_strict_reserved_name, class_token_pos);
}

template <typename Impl>
typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseClassLiteral(
    Scope* outer_scope, IdentifierT name, Scanner::Location class_name_location,
    bool name_is_strict_reserved, int class_token_pos) {
  bool is_anonymous = impl()->IsNull(name);

  // All parts of a ClassDeclaration and ClassExpression are strict code.
  if (!impl()->HasCheckedSyntax() && !is_anonymous) {
    if (name_is_strict_reserved) {
      impl()->ReportMessageAt(class_name_location,
                              MessageTemplate::kUnexpectedStrictReserved);
      return impl()->FailureExpression();
    }
    if (impl()->IsEvalOrArguments(name)) {
      impl()->ReportMessageAt(class_name_location,
                              MessageTemplate::kStrictEvalArguments);
      return impl()->FailureExpression();
    }
  }

  ClassScope* class_scope = NewClassScope(outer_scope, is_anonymous);
  BlockState block_state(&scope_, class_scope);
  RaiseLanguageMode(LanguageMode::kStrict);

  BlockState object_literal_scope_state(&object_literal_scope_, nullptr);

  ClassInfo class_info(this);
  class_info.is_anonymous = is_anonymous;

  scope()->set_start_position(class_token_pos);
  if (Check(Token::EXTENDS)) {
    ClassScope::HeritageParsingScope heritage(class_scope);
    FuncNameInferrerState fni_state(&fni_);
    ExpressionParsingScope scope(impl());
    class_info.extends = ParseLeftHandSideExpression();
    scope.ValidateExpression();
  }

  Expect(Token::LBRACE);

  const bool has_extends = !impl()->IsNull(class_info.extends);
  while (peek() != Token::RBRACE) {
    if (Check(Token::SEMICOLON)) continue;

    // Either we're parsing a `static { }` initialization block or a property.
    if (FLAG_harmony_class_static_blocks && peek() == Token::STATIC &&
        PeekAhead() == Token::LBRACE) {
      BlockT static_block = ParseClassStaticBlock(&class_info);
      impl()->AddClassStaticBlock(static_block, &class_info);
      continue;
    }

    FuncNameInferrerState fni_state(&fni_);
    // If we haven't seen the constructor yet, it potentially is the next
    // property.
    bool is_constructor = !class_info.has_seen_constructor;
    ParsePropertyInfo prop_info(this);
    prop_info.position = PropertyPosition::kClassLiteral;

    ClassLiteralPropertyT property =
        ParseClassPropertyDefinition(&class_info, &prop_info, has_extends);

    if (has_error()) return impl()->FailureExpression();

    ClassLiteralProperty::Kind property_kind =
        ClassPropertyKindFor(prop_info.kind);
    if (!class_info.has_static_computed_names && prop_info.is_static &&
        prop_info.is_computed_name) {
      class_info.has_static_computed_names = true;
    }
    is_constructor &= class_info.has_seen_constructor;

    bool is_field = property_kind == ClassLiteralProperty::FIELD;

    if (V8_UNLIKELY(prop_info.is_private)) {
      DCHECK(!is_constructor);
      class_info.requires_brand |= (!is_field && !prop_info.is_static);
      bool is_method = property_kind == ClassLiteralProperty::METHOD;
      class_info.has_private_methods |= is_method;
      class_info.has_static_private_methods |= is_method && prop_info.is_static;
      impl()->DeclarePrivateClassMember(class_scope, prop_info.name, property,
                                        property_kind, prop_info.is_static,
                                        &class_info);
      impl()->InferFunctionName();
      continue;
    }

    if (V8_UNLIKELY(is_field)) {
      DCHECK(!prop_info.is_private);
      if (prop_info.is_computed_name) {
        class_info.computed_field_count++;
      }
      impl()->DeclarePublicClassField(class_scope, property,
                                      prop_info.is_static,
                                      prop_info.is_computed_name, &class_info);
      impl()->InferFunctionName();
      continue;
    }

    impl()->DeclarePublicClassMethod(name, property, is_constructor,
                                     &class_info);
    impl()->InferFunctionName();
  }

  Expect(Token::RBRACE);
  int end_pos = end_position();
  class_scope->set_end_position(end_pos);
  if (class_info.instance_members_scope != nullptr) {
    // Use the positions of the class body for the instance initializer
    // function so that we can reparse it later.
    class_info.instance_members_scope->set_start_position(class_token_pos);
    class_info.instance_members_scope->set_end_position(end_pos);
  }

  VariableProxy* unresolvable = class_scope->ResolvePrivateNamesPartially();
  if (unresolvable != nullptr) {
    impl()->ReportMessageAt(Scanner::Location(unresolvable->position(),
                                              unresolvable->position() + 1),
                            MessageTemplate::kInvalidPrivateFieldResolution,
                            unresolvable->raw_name());
    return impl()->FailureExpression();
  }

  if (class_info.requires_brand) {
    class_scope->DeclareBrandVariable(
        ast_value_factory(), IsStaticFlag::kNotStatic, kNoSourcePosition);
  }

  if (class_scope->needs_home_object()) {
    class_info.home_object_variable =
        class_scope->DeclareHomeObjectVariable(ast_value_factory());
    class_info.static_home_object_variable =
        class_scope->DeclareStaticHomeObjectVariable(ast_value_factory());
  }

  bool should_save_class_variable_index =
      class_scope->should_save_class_variable_index();
  if (!is_anonymous || should_save_class_variable_index) {
    impl()->DeclareClassVariable(class_scope, name, &class_info,
                                 class_token_pos);
    if (should_save_class_variable_index) {
      class_scope->class_variable()->set_is_used();
      class_scope->class_variable()->ForceContextAllocation();
    }
  }

  return impl()->RewriteClassLiteral(class_scope, name, &class_info,
                                     class_token_pos, end_pos);
}

template <typename Impl>
void ParserBase<Impl>::ParseAsyncFunctionBody(Scope* scope,
                                              StatementListT* body) {
  BlockT block = impl()->NullBlock();
  {
    StatementListT statements(pointer_buffer());
    ParseStatementList(&statements, Token::RBRACE);
    block = factory()->NewBlock(true, statements);
  }
  impl()->RewriteAsyncFunctionBody(
      body, block, factory()->NewUndefinedLiteral(kNoSourcePosition));
  scope->set_end_position(end_position());
}

template <typename Impl>
typename ParserBase<Impl>::ExpressionT
ParserBase<Impl>::ParseAsyncFunctionLiteral() {
  // AsyncFunctionLiteral ::
  //   async [no LineTerminator here] function ( FormalParameters[Await] )
  //       { AsyncFunctionBody }
  //
  //   async [no LineTerminator here] function BindingIdentifier[Await]
  //       ( FormalParameters[Await] ) { AsyncFunctionBody }
  DCHECK_EQ(scanner()->current_token(), Token::ASYNC);
  if (V8_UNLIKELY(scanner()->literal_contains_escapes())) {
    impl()->ReportUnexpectedToken(Token::ESCAPED_KEYWORD);
  }
  int pos = position();
  Consume(Token::FUNCTION);
  IdentifierT name = impl()->NullIdentifier();
  FunctionSyntaxKind syntax_kind = FunctionSyntaxKind::kAnonymousExpression;

  ParseFunctionFlags flags = ParseFunctionFlag::kIsAsync;
  if (Check(Token::MUL)) flags |= ParseFunctionFlag::kIsGenerator;
  const FunctionKind kind = FunctionKindFor(flags);
  bool is_strict_reserved = Token::IsStrictReservedWord(peek());

  if (impl()->ParsingDynamicFunctionDeclaration()) {
    // We don't want dynamic functions to actually declare their name
    // "anonymous". We just want that name in the toString().

    // Consuming token we did not peek yet, which could lead to a ILLEGAL token
    // in the case of a stackoverflow.
    Consume(Token::IDENTIFIER);
    DCHECK_IMPLIES(!has_error(),
                   scanner()->CurrentSymbol(ast_value_factory()) ==
                       ast_value_factory()->anonymous_string());
  } else if (peek_any_identifier()) {
    syntax_kind = FunctionSyntaxKind::kNamedExpression;
    name = ParseIdentifier(kind);
  }
  FunctionLiteralT result = impl()->ParseFunctionLiteral(
      name, scanner()->location(),
      is_strict_reserved ? kFunctionNameIsStrictReserved
                         : kFunctionNameValidityUnknown,
      kind, pos, syntax_kind, language_mode(), nullptr);
  if (impl()->IsNull(result)) return impl()->FailureExpression();
  return result;
}

template <typename Impl>
typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseTemplateLiteral(
    ExpressionT tag, int start, bool tagged) {
  // A TemplateLiteral is made up of 0 or more TEMPLATE_SPAN tokens (literal
  // text followed by a substitution expression), finalized by a single
  // TEMPLATE_TAIL.
  //
  // In terms of draft language, TEMPLATE_SPAN may be either the TemplateHead or
  // TemplateMiddle productions, while TEMPLATE_TAIL is either TemplateTail, or
  // NoSubstitutionTemplate.
  //
  // When parsing a TemplateLiteral, we must have scanned either an initial
  // TEMPLATE_SPAN, or a TEMPLATE_TAIL.
  DCHECK(peek() == Token::TEMPLATE_SPAN || peek() == Token::TEMPLATE_TAIL);

  if (tagged) {
    // TaggedTemplate expressions prevent the eval compilation cache from being
    // used. This flag is only used if an eval is being parsed.
    set_allow_eval_cache(false);
  }

  bool forbid_illegal_escapes = !tagged;

  // If we reach a TEMPLATE_TAIL first, we are parsing a NoSubstitutionTemplate.
  // In this case we may simply consume the token and build a template with a
  // single TEMPLATE_SPAN and no expressions.
  if (peek() == Token::TEMPLATE_TAIL) {
    Consume(Token::TEMPLATE_TAIL);
    int pos = position();
    typename Impl::TemplateLiteralState ts = impl()->OpenTemplateLiteral(pos);
    bool is_valid = CheckTemplateEscapes(forbid_illegal_escapes);
    impl()->AddTemplateSpan(&ts, is_valid, true);
    return impl()->CloseTemplateLiteral(&ts, start, tag);
  }

  Consume(Token::TEMPLATE_SPAN);
  int pos = position();
  typename Impl::TemplateLiteralState ts = impl()->OpenTemplateLiteral(pos);
  bool is_valid = CheckTemplateEscapes(forbid_illegal_escapes);
  impl()->AddTemplateSpan(&ts, is_valid, false);
  Token::Value next;

  // If we open with a TEMPLATE_SPAN, we must scan the subsequent expression,
  // and repeat if the following token is a TEMPLATE_SPAN as well (in this
  // case, representing a TemplateMiddle).

  do {
    next = peek();

    int expr_pos = peek_position();
    AcceptINScope scope(this, true);
    ExpressionT expression = ParseExpressionCoverGrammar();
    impl()->AddTemplateExpression(&ts, expression);

    if (peek() != Token::RBRACE) {
      impl()->ReportMessageAt(Scanner::Location(expr_pos, peek_position()),
                              MessageTemplate::kUnterminatedTemplateExpr);
      return impl()->FailureExpression();
    }

    // If we didn't die parsing that expression, our next token should be a
    // TEMPLATE_SPAN or TEMPLATE_TAIL.
    next = scanner()->ScanTemplateContinuation();
    Next();
    pos = position();

    is_valid = CheckTemplateEscapes(forbid_illegal_escapes);
    impl()->AddTemplateSpan(&ts, is_valid, next == Token::TEMPLATE_TAIL);
  } while (next == Token::TEMPLATE_SPAN);

  DCHECK_IMPLIES(!has_error(), next == Token::TEMPLATE_TAIL);
  // Once we've reached a TEMPLATE_TAIL, we can close the TemplateLiteral.
  return impl()->CloseTemplateLiteral(&ts, start, tag);
}

template <typename Impl>
typename ParserBase<Impl>::ExpressionT
ParserBase<Impl>::RewriteInvalidReferenceExpression(ExpressionT expression,
                                                    int beg_pos, int end_pos,
                                                    MessageTemplate message,
                                                    bool early_error) {
  DCHECK(!IsValidReferenceExpression(expression));
  if (impl()->IsIdentifier(expression)) {
    DCHECK(is_strict(language_mode()));
    DCHECK(impl()->IsEvalOrArguments(impl()->AsIdentifier(expression)));

    ReportMessageAt(Scanner::Location(beg_pos, end_pos),
                    MessageTemplate::kStrictEvalArguments);
    return impl()->FailureExpression();
  }
  if (expression->IsCall() && !expression->AsCall()->is_tagged_template() &&
      !early_error) {
    expression_scope()->RecordPatternError(
        Scanner::Location(beg_pos, end_pos),
        MessageTemplate::kInvalidDestructuringTarget);
    // If it is a call, make it a runtime error for legacy web compatibility.
    // Bug: https://bugs.chromium.org/p/v8/issues/detail?id=4480
    // Rewrite `expr' to `expr[throw ReferenceError]'.
    impl()->CountUsage(
        is_strict(language_mode())
            ? v8::Isolate::kAssigmentExpressionLHSIsCallInStrict
            : v8::Isolate::kAssigmentExpressionLHSIsCallInSloppy);
    ExpressionT error = impl()->NewThrowReferenceError(message, beg_pos);
    return factory()->NewProperty(expression, error, beg_pos);
  }
  // Tagged templates and other modern language features (which pass early_error
  // = true) are exempt from the web compatibility hack. Throw a regular early
  // error.
  ReportMessageAt(Scanner::Location(beg_pos, end_pos), message);
  return impl()->FailureExpression();
}

template <typename Impl>
void ParserBase<Impl>::ClassifyParameter(IdentifierT parameter, int begin,
                                         int end) {
  if (impl()->IsEvalOrArguments(parameter)) {
    expression_scope()->RecordStrictModeParameterError(
        Scanner::Location(begin, end), MessageTemplate::kStrictEvalArguments);
  }
}

template <typename Impl>
void ParserBase<Impl>::ClassifyArrowParameter(
    AccumulationScope* accumulation_scope, int position,
    ExpressionT parameter) {
  accumulation_scope->Accumulate();
  if (parameter->is_parenthesized() ||
      !(impl()->IsIdentifier(parameter) || parameter->IsPattern() ||
        parameter->IsAssignment())) {
    expression_scope()->RecordDeclarationError(
        Scanner::Location(position, end_position()),
        MessageTemplate::kInvalidDestructuringTarget);
  } else if (impl()->IsIdentifier(parameter)) {
    ClassifyParameter(impl()->AsIdentifier(parameter), position,
                      end_position());
  } else {
    expression_scope()->RecordNonSimpleParameter();
  }
}

template <typename Impl>
bool ParserBase<Impl>::IsValidReferenceExpression(ExpressionT expression) {
  return IsAssignableIdentifier(expression) || expression->IsProperty();
}

template <typename Impl>
typename ParserBase<Impl>::ExpressionT
ParserBase<Impl>::ParsePossibleDestructuringSubPattern(
    AccumulationScope* scope) {
  if (scope) scope->Accumulate();
  int begin = peek_position();
  ExpressionT result = ParseAssignmentExpressionCoverGrammar();

  if (IsValidReferenceExpression(result)) {
    // Parenthesized identifiers and property references are allowed as part of
    // a larger assignment pattern, even though parenthesized patterns
    // themselves are not allowed, e.g., "[(x)] = []". Only accumulate
    // assignment pattern errors if the parsed expression is more complex.
    if (impl()->IsIdentifier(result)) {
      if (result->is_parenthesized()) {
        expression_scope()->RecordDeclarationError(
            Scanner::Location(begin, end_position()),
            MessageTemplate::kInvalidDestructuringTarget);
      }
      IdentifierT identifier = impl()->AsIdentifier(result);
      ClassifyParameter(identifier, begin, end_position());
    } else {
      DCHECK(result->IsProperty());
      expression_scope()->RecordDeclarationError(
          Scanner::Location(begin, end_position()),
          MessageTemplate::kInvalidPropertyBindingPattern);
      if (scope != nullptr) scope->ValidateExpression();
    }
  } else if (result->is_parenthesized() ||
             (!result->IsPattern() && !result->IsAssignment())) {
    expression_scope()->RecordPatternError(
        Scanner::Location(begin, end_position()),
        MessageTemplate::kInvalidDestructuringTarget);
  }

  return result;
}

template <typename Impl>
typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseV8Intrinsic() {
  // CallRuntime ::
  //   '%' Identifier Arguments

  int pos = peek_position();
  Consume(Token::MOD);
  // Allow "eval" or "arguments" for backward compatibility.
  IdentifierT name = ParseIdentifier();
  if (peek() != Token::LPAREN) {
    impl()->ReportUnexpectedToken(peek());
    return impl()->FailureExpression();
  }
  bool has_spread;
  ExpressionListT args(pointer_buffer());
  ParseArguments(&args, &has_spread);

  if (has_spread) {
    ReportMessageAt(Scanner::Location(pos, position()),
                    MessageTemplate::kIntrinsicWithSpread);
    return impl()->FailureExpression();
  }

  return impl()->NewV8Intrinsic(name, args, pos);
}

template <typename Impl>
void ParserBase<Impl>::ParseStatementList(StatementListT* body,
                                          Token::Value end_token) {
  // StatementList ::
  //   (StatementListItem)* <end_token>
  DCHECK_NOT_NULL(body);

  while (peek() == Token::STRING) {
    bool use_strict = false;
#if V8_ENABLE_WEBASSEMBLY
    bool use_asm = false;
#endif  // V8_ENABLE_WEBASSEMBLY

    Scanner::Location token_loc = scanner()->peek_location();

    if (scanner()->NextLiteralExactlyEquals("use strict")) {
      use_strict = true;
#if V8_ENABLE_WEBASSEMBLY
    } else if (scanner()->NextLiteralExactlyEquals("use asm")) {
      use_asm = true;
#endif  // V8_ENABLE_WEBASSEMBLY
    }

    StatementT stat = ParseStatementListItem();
    if (impl()->IsNull(stat)) return;

    body->Add(stat);

    if (!impl()->IsStringLiteral(stat)) break;

    if (use_strict) {
      // Directive "use strict" (ES5 14.1).
      RaiseLanguageMode(LanguageMode::kStrict);
      if (!scope()->HasSimpleParameters()) {
        // TC39 deemed "use strict" directives to be an error when occurring
        // in the body of a function with non-simple parameter list, on
        // 29/7/2015. https://goo.gl/ueA7Ln
        impl()->ReportMessageAt(token_loc,
                                MessageTemplate::kIllegalLanguageModeDirective,
                                "use strict");
        return;
      }
#if V8_ENABLE_WEBASSEMBLY
    } else if (use_asm) {
      // Directive "use asm".
      impl()->SetAsmModule();
#endif  // V8_ENABLE_WEBASSEMBLY
    } else {
      // Possibly an unknown directive.
      // Should not change mode, but will increment usage counters
      // as appropriate. Ditto usages below.
      RaiseLanguageMode(LanguageMode::kSloppy);
    }
  }

  while (peek() != end_token) {
    StatementT stat = ParseStatementListItem();
    if (impl()->IsNull(stat)) return;
    if (stat->IsEmptyStatement()) continue;
    body->Add(stat);
  }
}

template <typename Impl>
typename ParserBase<Impl>::StatementT
ParserBase<Impl>::ParseStatementListItem() {
  // ECMA 262 6th Edition
  // StatementListItem[Yield, Return] :
  //   Statement[?Yield, ?Return]
  //   Declaration[?Yield]
  //
  // Declaration[Yield] :
  //   HoistableDeclaration[?Yield]
  //   ClassDeclaration[?Yield]
  //   LexicalDeclaration[In, ?Yield]
  //
  // HoistableDeclaration[Yield, Default] :
  //   FunctionDeclaration[?Yield, ?Default]
  //   GeneratorDeclaration[?Yield, ?Default]
  //
  // LexicalDeclaration[In, Yield] :
  //   LetOrConst BindingList[?In, ?Yield] ;

  switch (peek()) {
    case Token::FUNCTION:
      return ParseHoistableDeclaration(nullptr, false);
    case Token::CLASS:
      Consume(Token::CLASS);
      return ParseClassDeclaration(nullptr, false);
    case Token::VAR:
    case Token::CONST:
      return ParseVariableStatement(kStatementListItem, nullptr);
    case Token::LET:
      if (IsNextLetKeyword()) {
        return ParseVariableStatement(kStatementListItem, nullptr);
      }
      break;
    case Token::ASYNC:
      if (PeekAhead() == Token::FUNCTION &&
          !scanner()->HasLineTerminatorAfterNext()) {
        Consume(Token::ASYNC);
        return ParseAsyncFunctionDeclaration(nullptr, false);
      }
      break;
    default:
      break;
  }
  return ParseStatement(nullptr, nullptr, kAllowLabelledFunctionStatement);
}

template <typename Impl>
typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseStatement(
    ZonePtrList<const AstRawString>* labels,
    ZonePtrList<const AstRawString>* own_labels,
    AllowLabelledFunctionStatement allow_function) {
  // Statement ::
  //   Block
  //   VariableStatement
  //   EmptyStatement
  //   ExpressionStatement
  //   IfStatement
  //   IterationStatement
  //   ContinueStatement
  //   BreakStatement
  //   ReturnStatement
  //   WithStatement
  //   LabelledStatement
  //   SwitchStatement
  //   ThrowStatement
  //   TryStatement
  //   DebuggerStatement

  // {own_labels} is always a subset of {labels}.
  DCHECK_IMPLIES(labels == nullptr, own_labels == nullptr);

  // Note: Since labels can only be used by 'break' and 'continue'
  // statements, which themselves are only valid within blocks,
  // iterations or 'switch' statements (i.e., BreakableStatements),
  // labels can be simply ignored in all other cases; except for
  // trivial labeled break statements 'label: break label' which is
  // parsed into an empty statement.
  switch (peek()) {
    case Token::LBRACE:
      return ParseBlock(labels);
    case Token::SEMICOLON:
      Next();
      return factory()->EmptyStatement();
    case Token::IF:
      return ParseIfStatement(labels);
    case Token::DO:
      return ParseDoWhileStatement(labels, own_labels);
    case Token::WHILE:
      return ParseWhileStatement(labels, own_labels);
    case Token::FOR:
      if (V8_UNLIKELY(is_await_allowed() && PeekAhead() == Token::AWAIT)) {
        return ParseForAwaitStatement(labels, own_labels);
      }
      return ParseForStatement(labels, own_labels);
    case Token::CONTINUE:
      return ParseContinueStatement();
    case Token::BREAK:
      return ParseBreakStatement(labels);
    case Token::RETURN:
      return ParseReturnStatement();
    case Token::THROW:
      return ParseThrowStatement();
    case Token::TRY: {
      // It is somewhat complicated to have labels on try-statements.
      // When breaking out of a try-finally statement, one must take
      // great care not to treat it as a fall-through. It is much easier
      // just to wrap the entire try-statement in a statement block and
      // put the labels there.
      if (labels == nullptr) return ParseTryStatement();
      StatementListT statements(pointer_buffer());
      BlockT result = factory()->NewBlock(false, true);
      Target target(this, result, labels, nullptr,
                    Target::TARGET_FOR_NAMED_ONLY);
      StatementT statement = ParseTryStatement();
      statements.Add(statement);
      result->InitializeStatements(statements, zone());
      return result;
    }
    case Token::WITH:
      return ParseWithStatement(labels);
    case Token::SWITCH:
      return ParseSwitchStatement(labels);
    case Token::FUNCTION:
      // FunctionDeclaration only allowed as a StatementListItem, not in
      // an arbitrary Statement position. Exceptions such as
      // ES#sec-functiondeclarations-in-ifstatement-statement-clauses
      // are handled by calling ParseScopedStatement rather than
      // ParseStatement directly.
      impl()->ReportMessageAt(scanner()->peek_location(),
                              is_strict(language_mode())
                                  ? MessageTemplate::kStrictFunction
                                  : MessageTemplate::kSloppyFunction);
      return impl()->NullStatement();
    case Token::DEBUGGER:
      return ParseDebuggerStatement();
    case Token::VAR:
      return ParseVariableStatement(kStatement, nullptr);
    case Token::ASYNC:
      if (!impl()->HasCheckedSyntax() &&
          !scanner()->HasLineTerminatorAfterNext() &&
          PeekAhead() == Token::FUNCTION) {
        impl()->ReportMessageAt(
            scanner()->peek_location(),
            MessageTemplate::kAsyncFunctionInSingleStatementContext);
        return impl()->NullStatement();
      }
      V8_FALLTHROUGH;
    default:
      return ParseExpressionOrLabelledStatement(labels, own_labels,
                                                allow_function);
  }
}

template <typename Impl>
typename ParserBase<Impl>::BlockT ParserBase<Impl>::ParseBlock(
    ZonePtrList<const AstRawString>* labels, Scope* block_scope) {
  // Block ::
  //   '{' StatementList '}'

  // Parse the statements and collect escaping labels.
  BlockT body = factory()->NewBlock(false, labels != nullptr);
  StatementListT statements(pointer_buffer());

  CheckStackOverflow();

  {
    BlockState block_state(&scope_, block_scope);
    scope()->set_start_position(peek_position());
    Target target(this, body, labels, nullptr, Target::TARGET_FOR_NAMED_ONLY);

    Expect(Token::LBRACE);

    while (peek() != Token::RBRACE) {
      StatementT stat = ParseStatementListItem();
      if (impl()->IsNull(stat)) return body;
      if (stat->IsEmptyStatement()) continue;
      statements.Add(stat);
    }

    Expect(Token::RBRACE);

    int end_pos = end_position();
    scope()->set_end_position(end_pos);

    impl()->RecordBlockSourceRange(body, end_pos);
    body->set_scope(scope()->FinalizeBlockScope());
  }

  body->InitializeStatements(statements, zone());
  return body;
}

template <typename Impl>
typename ParserBase<Impl>::BlockT ParserBase<Impl>::ParseBlock(
    ZonePtrList<const AstRawString>* labels) {
  return ParseBlock(labels, NewScope(BLOCK_SCOPE));
}

template <typename Impl>
typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseScopedStatement(
    ZonePtrList<const AstRawString>* labels) {
  if (is_strict(language_mode()) || peek() != Token::FUNCTION) {
    return ParseStatement(labels, nullptr);
  } else {
    // Make a block around the statement for a lexical binding
    // is introduced by a FunctionDeclaration.
    BlockState block_state(zone(), &scope_);
    scope()->set_start_position(scanner()->location().beg_pos);
    BlockT block = factory()->NewBlock(1, false);
    StatementT body = ParseFunctionDeclaration();
    block->statements()->Add(body, zone());
    scope()->set_end_position(end_position());
    block->set_scope(scope()->FinalizeBlockScope());
    return block;
  }
}

template <typename Impl>
typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseVariableStatement(
    VariableDeclarationContext var_context,
    ZonePtrList<const AstRawString>* names) {
  // VariableStatement ::
  //   VariableDeclarations ';'

  // The scope of a var declared variable anywhere inside a function
  // is the entire function (ECMA-262, 3rd, 10.1.3, and 12.2). Thus we can
  // transform a source-level var declaration into a (Function) Scope
  // declaration, and rewrite the source-level initialization into an assignment
  // statement. We use a block to collect multiple assignments.
  //
  // We mark the block as initializer block because we don't want the
  // rewriter to add a '.result' assignment to such a block (to get compliant
  // behavior for code such as print(eval('var x = 7')), and for cosmetic
  // reasons when pretty-printing. Also, unless an assignment (initialization)
  // is inside an initializer block, it is ignored.

  DeclarationParsingResult parsing_result;
  ParseVariableDeclarations(var_context, &parsing_result, names);
  ExpectSemicolon();
  return impl()->BuildInitializationBlock(&parsing_result);
}

template <typename Impl>
typename ParserBase<Impl>::StatementT
ParserBase<Impl>::ParseDebuggerStatement() {
  // In ECMA-262 'debugger' is defined as a reserved keyword. In some browser
  // contexts this is used as a statement which invokes the debugger as i a
  // break point is present.
  // DebuggerStatement ::
  //   'debugger' ';'

  int pos = peek_position();
  Consume(Token::DEBUGGER);
  ExpectSemicolon();
  return factory()->NewDebuggerStatement(pos);
}

template <typename Impl>
typename ParserBase<Impl>::StatementT
ParserBase<Impl>::ParseExpressionOrLabelledStatement(
    ZonePtrList<const AstRawString>* labels,
    ZonePtrList<const AstRawString>* own_labels,
    AllowLabelledFunctionStatement allow_function) {
  // ExpressionStatement | LabelledStatement ::
  //   Expression ';'
  //   Identifier ':' Statement
  //
  // ExpressionStatement[Yield] :
  //   [lookahead notin {{, function, class, let [}] Expression[In, ?Yield] ;

  int pos = peek_position();

  switch (peek()) {
    case Token::FUNCTION:
    case Token::LBRACE:
      UNREACHABLE();  // Always handled by the callers.
    case Token::CLASS:
      ReportUnexpectedToken(Next());
      return impl()->NullStatement();
    case Token::LET: {
      Token::Value next_next = PeekAhead();
      // "let" followed by either "[", "{" or an identifier means a lexical
      // declaration, which should not appear here.
      // However, ASI may insert a line break before an identifier or a brace.
      if (next_next != Token::LBRACK &&
          ((next_next != Token::LBRACE && next_next != Token::IDENTIFIER) ||
           scanner_->HasLineTerminatorAfterNext())) {
        break;
      }
      impl()->ReportMessageAt(scanner()->peek_location(),
                              MessageTemplate::kUnexpectedLexicalDeclaration);
      return impl()->NullStatement();
    }
    default:
      break;
  }

  bool starts_with_identifier = peek_any_identifier();

  ExpressionT expr;
  {
    // Effectively inlines ParseExpression, so potential labels can be extracted
    // from expression_scope.
    ExpressionParsingScope expression_scope(impl());
    AcceptINScope scope(this, true);
    expr = ParseExpressionCoverGrammar();
    expression_scope.ValidateExpression();

    if (peek() == Token::COLON && starts_with_identifier &&
        impl()->IsIdentifier(expr)) {
      // The whole expression was a single identifier, and not, e.g.,
      // something starting with an identifier or a parenthesized identifier.
      DCHECK_EQ(expression_scope.variable_list()->length(), 1);
      VariableProxy* label = expression_scope.variable_list()->at(0).first;
      impl()->DeclareLabel(&labels, &own_labels, label->raw_name());

      // Remove the "ghost" variable that turned out to be a label from the top
      // scope. This way, we don't try to resolve it during the scope
      // processing.
      this->scope()->DeleteUnresolved(label);

      Consume(Token::COLON);
      // ES#sec-labelled-function-declarations Labelled Function Declarations
      if (peek() == Token::FUNCTION && is_sloppy(language_mode()) &&
          allow_function == kAllowLabelledFunctionStatement) {
        return ParseFunctionDeclaration();
      }
      return ParseStatement(labels, own_labels, allow_function);
    }
  }

  // We allow a native function declaration if we're parsing the source for an
  // extension. A native function declaration starts with "native function"
  // with no line-terminator between the two words.
  if (impl()->ParsingExtension() && peek() == Token::FUNCTION &&
      !scanner()->HasLineTerminatorBeforeNext() && impl()->IsNative(expr) &&
      !scanner()->literal_contains_escapes()) {
    return ParseNativeDeclaration();
  }

  // Parsed expression statement, followed by semicolon.
  ExpectSemicolon();
  if (expr->IsFailureExpression()) return impl()->NullStatement();
  return factory()->NewExpressionStatement(expr, pos);
}

template <typename Impl>
typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseIfStatement(
    ZonePtrList<const AstRawString>* labels) {
  // IfStatement ::
  //   'if' '(' Expression ')' Statement ('else' Statement)?

  int pos = peek_position();
  Consume(Token::IF);
  Expect(Token::LPAREN);
  ExpressionT condition = ParseExpression();
  Expect(Token::RPAREN);

  SourceRange then_range, else_range;
  StatementT then_statement = impl()->NullStatement();
  {
    SourceRangeScope range_scope(scanner(), &then_range);
    // Make a copy of {labels} to avoid conflicts with any
    // labels that may be applied to the else clause below.
    auto labels_copy =
        labels == nullptr
            ? labels
            : zone()->template New<ZonePtrList<const AstRawString>>(*labels,
                                                                    zone());
    then_statement = ParseScopedStatement(labels_copy);
  }

  StatementT else_statement = impl()->NullStatement();
  if (Check(Token::ELSE)) {
    else_statement = ParseScopedStatement(labels);
    else_range = SourceRange::ContinuationOf(then_range, end_position());
  } else {
    else_statement = factory()->EmptyStatement();
  }
  StatementT stmt =
      factory()->NewIfStatement(condition, then_statement, else_statement, pos);
  impl()->RecordIfStatementSourceRange(stmt, then_range, else_range);
  return stmt;
}

template <typename Impl>
typename ParserBase<Impl>::StatementT
ParserBase<Impl>::ParseContinueStatement() {
  // ContinueStatement ::
  //   'continue' Identifier? ';'

  int pos = peek_position();
  Consume(Token::CONTINUE);
  IdentifierT label = impl()->NullIdentifier();
  Token::Value tok = peek();
  if (!scanner()->HasLineTerminatorBeforeNext() &&
      !Token::IsAutoSemicolon(tok)) {
    // ECMA allows "eval" or "arguments" as labels even in strict mode.
    label = ParseIdentifier();
  }
  IterationStatementT target = LookupContinueTarget(label);
  if (impl()->IsNull(target)) {
    // Illegal continue statement.
    MessageTemplate message = MessageTemplate::kIllegalContinue;
    BreakableStatementT breakable_target = LookupBreakTarget(label);
    if (impl()->IsNull(label)) {
      message = MessageTemplate::kNoIterationStatement;
    } else if (impl()->IsNull(breakable_target)) {
      message = MessageTemplate::kUnknownLabel;
    }
    ReportMessage(message, label);
    return impl()->NullStatement();
  }
  ExpectSemicolon();
  StatementT stmt = factory()->NewContinueStatement(target, pos);
  impl()->RecordJumpStatementSourceRange(stmt, end_position());
  return stmt;
}

template <typename Impl>
typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseBreakStatement(
    ZonePtrList<const AstRawString>* labels) {
  // BreakStatement ::
  //   'break' Identifier? ';'

  int pos = peek_position();
  Consume(Token::BREAK);
  IdentifierT label = impl()->NullIdentifier();
  Token::Value tok = peek();
  if (!scanner()->HasLineTerminatorBeforeNext() &&
      !Token::IsAutoSemicolon(tok)) {
    // ECMA allows "eval" or "arguments" as labels even in strict mode.
    label = ParseIdentifier();
  }
  // Parse labeled break statements that target themselves into
  // empty statements, e.g. 'l1: l2: l3: break l2;'
  if (!impl()->IsNull(label) &&
      impl()->ContainsLabel(labels, impl()->GetRawNameFromIdentifier(label))) {
    ExpectSemicolon();
    return factory()->EmptyStatement();
  }
  BreakableStatementT target = LookupBreakTarget(label);
  if (impl()->IsNull(target)) {
    // Illegal break statement.
    MessageTemplate message = MessageTemplate::kIllegalBreak;
    if (!impl()->IsNull(label)) {
      message = MessageTemplate::kUnknownLabel;
    }
    ReportMessage(message, label);
    return impl()->NullStatement();
  }
  ExpectSemicolon();
  StatementT stmt = factory()->NewBreakStatement(target, pos);
  impl()->RecordJumpStatementSourceRange(stmt, end_position());
  return stmt;
}

template <typename Impl>
typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseReturnStatement() {
  // ReturnStatement ::
  //   'return' [no line terminator] Expression? ';'

  // Consume the return token. It is necessary to do that before
  // reporting any errors on it, because of the way errors are
  // reported (underlining).
  Consume(Token::RETURN);
  Scanner::Location loc = scanner()->location();

  switch (GetDeclarationScope()->scope_type()) {
    case SCRIPT_SCOPE:
    case EVAL_SCOPE:
    case MODULE_SCOPE:
      impl()->ReportMessageAt(loc, MessageTemplate::kIllegalReturn);
      return impl()->NullStatement();
    case BLOCK_SCOPE:
      // Class static blocks disallow return. They are their own var scopes and
      // have a varblock scope.
      if (function_state_->kind() ==
          FunctionKind::kClassStaticInitializerFunction) {
        impl()->ReportMessageAt(loc, MessageTemplate::kIllegalReturn);
        return impl()->NullStatement();
      }
      break;
    default:
      break;
  }

  Token::Value tok = peek();
  ExpressionT return_value = impl()->NullExpression();
  if (scanner()->HasLineTerminatorBeforeNext() || Token::IsAutoSemicolon(tok)) {
    if (IsDerivedConstructor(function_state_->kind())) {
      ExpressionParsingScope expression_scope(impl());
      return_value = impl()->ThisExpression();
      expression_scope.ValidateExpression();
    }
  } else {
    return_value = ParseExpression();
  }
  ExpectSemicolon();

  return_value = impl()->RewriteReturn(return_value, loc.beg_pos);
  int continuation_pos = end_position();
  StatementT stmt =
      BuildReturnStatement(return_value, loc.beg_pos, continuation_pos);
  impl()->RecordJumpStatementSourceRange(stmt, end_position());
  return stmt;
}

template <typename Impl>
typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseWithStatement(
    ZonePtrList<const AstRawString>* labels) {
  // WithStatement ::
  //   'with' '(' Expression ')' Statement

  Consume(Token::WITH);
  int pos = position();

  if (is_strict(language_mode())) {
    ReportMessage(MessageTemplate::kStrictWith);
    return impl()->NullStatement();
  }

  Expect(Token::LPAREN);
  ExpressionT expr = ParseExpression();
  Expect(Token::RPAREN);

  Scope* with_scope = NewScope(WITH_SCOPE);
  StatementT body = impl()->NullStatement();
  {
    BlockState block_state(&scope_, with_scope);
    with_scope->set_start_position(scanner()->peek_location().beg_pos);
    body = ParseStatement(labels, nullptr);
    with_scope->set_end_position(end_position());
  }
  return factory()->NewWithStatement(with_scope, expr, body, pos);
}

template <typename Impl>
typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseDoWhileStatement(
    ZonePtrList<const AstRawString>* labels,
    ZonePtrList<const AstRawString>* own_labels) {
  // DoStatement ::
  //   'do' Statement 'while' '(' Expression ')' ';'
  typename FunctionState::LoopScope loop_scope(function_state_);

  auto loop = factory()->NewDoWhileStatement(peek_position());
  Target target(this, loop, labels, own_labels, Target::TARGET_FOR_ANONYMOUS);

  SourceRange body_range;
  StatementT body = impl()->NullStatement();

  Consume(Token::DO);

  CheckStackOverflow();
  {
    SourceRangeScope range_scope(scanner(), &body_range);
    body = ParseStatement(nullptr, nullptr);
  }
  Expect(Token::WHILE);
  Expect(Token::LPAREN);

  ExpressionT cond = ParseExpression();
  Expect(Token::RPAREN);

  // Allow do-statements to be terminated with and without
  // semi-colons. This allows code such as 'do;while(0)return' to
  // parse, which would not be the case if we had used the
  // ExpectSemicolon() functionality here.
  Check(Token::SEMICOLON);

  loop->Initialize(cond, body);
  impl()->RecordIterationStatementSourceRange(loop, body_range);

  return loop;
}

template <typename Impl>
typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseWhileStatement(
    ZonePtrList<const AstRawString>* labels,
    ZonePtrList<const AstRawString>* own_labels) {
  // WhileStatement ::
  //   'while' '(' Expression ')' Statement
  typename FunctionState::LoopScope loop_scope(function_state_);

  auto loop = factory()->NewWhileStatement(peek_position());
  Target target(this, loop, labels, own_labels, Target::TARGET_FOR_ANONYMOUS);

  SourceRange body_range;
  StatementT body = impl()->NullStatement();

  Consume(Token::WHILE);
  Expect(Token::LPAREN);
  ExpressionT cond = ParseExpression();
  Expect(Token::RPAREN);
  {
    SourceRangeScope range_scope(scanner(), &body_range);
    body = ParseStatement(nullptr, nullptr);
  }

  loop->Initialize(cond, body);
  impl()->RecordIterationStatementSourceRange(loop, body_range);

  return loop;
}

template <typename Impl>
typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseThrowStatement() {
  // ThrowStatement ::
  //   'throw' Expression ';'

  Consume(Token::THROW);
  int pos = position();
  if (scanner()->HasLineTerminatorBeforeNext()) {
    ReportMessage(MessageTemplate::kNewlineAfterThrow);
    return impl()->NullStatement();
  }
  ExpressionT exception = ParseExpression();
  ExpectSemicolon();

  StatementT stmt = impl()->NewThrowStatement(exception, pos);
  impl()->RecordThrowSourceRange(stmt, end_position());

  return stmt;
}

template <typename Impl>
typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseSwitchStatement(
    ZonePtrList<const AstRawString>* labels) {
  // SwitchStatement ::
  //   'switch' '(' Expression ')' '{' CaseClause* '}'
  // CaseClause ::
  //   'case' Expression ':' StatementList
  //   'default' ':' StatementList
  int switch_pos = peek_position();

  Consume(Token::SWITCH);
  Expect(Token::LPAREN);
  ExpressionT tag = ParseExpression();
  Expect(Token::RPAREN);

  auto switch_statement = factory()->NewSwitchStatement(tag, switch_pos);

  {
    BlockState cases_block_state(zone(), &scope_);
    scope()->set_start_position(switch_pos);
    scope()->SetNonlinear();
    Target target(this, switch_statement, labels, nullptr,
                  Target::TARGET_FOR_ANONYMOUS);

    bool default_seen = false;
    Expect(Token::LBRACE);
    while (peek() != Token::RBRACE) {
      // An empty label indicates the default case.
      ExpressionT label = impl()->NullExpression();
      StatementListT statements(pointer_buffer());
      SourceRange clause_range;
      {
        SourceRangeScope range_scope(scanner(), &clause_range);
        if (Check(Token::CASE)) {
          label = ParseExpression();
        } else {
          Expect(Token::DEFAULT);
          if (default_seen) {
            ReportMessage(MessageTemplate::kMultipleDefaultsInSwitch);
            return impl()->NullStatement();
          }
          default_seen = true;
        }
        Expect(Token::COLON);
        while (peek() != Token::CASE && peek() != Token::DEFAULT &&
               peek() != Token::RBRACE) {
          StatementT stat = ParseStatementListItem();
          if (impl()->IsNull(stat)) return stat;
          if (stat->IsEmptyStatement()) continue;
          statements.Add(stat);
        }
      }
      auto clause = factory()->NewCaseClause(label, statements);
      impl()->RecordCaseClauseSourceRange(clause, clause_range);
      switch_statement->cases()->Add(clause, zone());
    }
    Expect(Token::RBRACE);

    int end_pos = end_position();
    scope()->set_end_position(end_pos);
    impl()->RecordSwitchStatementSourceRange(switch_statement, end_pos);
    Scope* switch_scope = scope()->FinalizeBlockScope();
    if (switch_scope != nullptr) {
      return impl()->RewriteSwitchStatement(switch_statement, switch_scope);
    }
    return switch_statement;
  }
}

template <typename Impl>
typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseTryStatement() {
  // TryStatement ::
  //   'try' Block Catch
  //   'try' Block Finally
  //   'try' Block Catch Finally
  //
  // Catch ::
  //   'catch' '(' Identifier ')' Block
  //
  // Finally ::
  //   'finally' Block

  Consume(Token::TRY);
  int pos = position();

  BlockT try_block = ParseBlock(nullptr);

  CatchInfo catch_info(this);

  if (peek() != Token::CATCH && peek() != Token::FINALLY) {
    ReportMessage(MessageTemplate::kNoCatchOrFinally);
    return impl()->NullStatement();
  }

  SourceRange catch_range, finally_range;

  BlockT catch_block = impl()->NullBlock();
  {
    SourceRangeScope catch_range_scope(scanner(), &catch_range);
    if (Check(Token::CATCH)) {
      bool has_binding;
      has_binding = Check(Token::LPAREN);

      if (has_binding) {
        catch_info.scope = NewScope(CATCH_SCOPE);
        catch_info.scope->set_start_position(scanner()->location().beg_pos);

        {
          BlockState catch_block_state(&scope_, catch_info.scope);
          StatementListT catch_statements(pointer_buffer());

          // Create a block scope to hold any lexical declarations created
          // as part of destructuring the catch parameter.
          {
            BlockState catch_variable_block_state(zone(), &scope_);
            scope()->set_start_position(position());

            if (peek_any_identifier()) {
              IdentifierT identifier = ParseNonRestrictedIdentifier();
              RETURN_IF_PARSE_ERROR;
              catch_info.variable = impl()->DeclareCatchVariableName(
                  catch_info.scope, identifier);
            } else {
              catch_info.variable = catch_info.scope->DeclareCatchVariableName(
                  ast_value_factory()->dot_catch_string());

              auto declaration_it = scope()->declarations()->end();

              VariableDeclarationParsingScope destructuring(
                  impl(), VariableMode::kLet, nullptr);
              catch_info.pattern = ParseBindingPattern();

              int initializer_position = end_position();
              auto declaration_end = scope()->declarations()->end();
              for (; declaration_it != declaration_end; ++declaration_it) {
                declaration_it->var()->set_initializer_position(
                    initializer_position);
              }

              RETURN_IF_PARSE_ERROR;
              catch_statements.Add(impl()->RewriteCatchPattern(&catch_info));
            }

            Expect(Token::RPAREN);

            BlockT inner_block = ParseBlock(nullptr);
            catch_statements.Add(inner_block);

            // Check for `catch(e) { let e; }` and similar errors.
            if (!impl()->HasCheckedSyntax()) {
              Scope* inner_scope = inner_block->scope();
              if (inner_scope != nullptr) {
                const AstRawString* conflict = nullptr;
                if (impl()->IsNull(catch_info.pattern)) {
                  const AstRawString* name = catch_info.variable->raw_name();
                  if (inner_scope->LookupLocal(name)) conflict = name;
                } else {
                  conflict = inner_scope->FindVariableDeclaredIn(
                      scope(), VariableMode::kVar);
                }
                if (conflict != nullptr) {
                  impl()->ReportVarRedeclarationIn(conflict, inner_scope);
                }
              }
            }

            scope()->set_end_position(end_position());
            catch_block = factory()->NewBlock(false, catch_statements);
            catch_block->set_scope(scope()->FinalizeBlockScope());
          }
        }

        catch_info.scope->set_end_position(end_position());
      } else {
        catch_block = ParseBlock(nullptr);
      }
    }
  }

  BlockT finally_block = impl()->NullBlock();
  DCHECK(has_error() || peek() == Token::FINALLY ||
         !impl()->IsNull(catch_block));
  {
    SourceRangeScope range_scope(scanner(), &finally_range);
    if (Check(Token::FINALLY)) {
      finally_block = ParseBlock(nullptr);
    }
  }

  RETURN_IF_PARSE_ERROR;
  return impl()->RewriteTryStatement(try_block, catch_block, catch_range,
                                     finally_block, finally_range, catch_info,
                                     pos);
}

template <typename Impl>
typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForStatement(
    ZonePtrList<const AstRawString>* labels,
    ZonePtrList<const AstRawString>* own_labels) {
  // Either a standard for loop
  //   for (<init>; <cond>; <next>) { ... }
  // or a for-each loop
  //   for (<each> of|in <iterable>) { ... }
  //
  // We parse a declaration/expression after the 'for (' and then read the first
  // expression/declaration before we know if this is a for or a for-each.
  typename FunctionState::LoopScope loop_scope(function_state_);

  int stmt_pos = peek_position();
  ForInfo for_info(this);

  Consume(Token::FOR);
  Expect(Token::LPAREN);

  bool starts_with_let = peek() == Token::LET;
  if (peek() == Token::CONST || (starts_with_let && IsNextLetKeyword())) {
    // The initializer contains lexical declarations,
    // so create an in-between scope.
    BlockState for_state(zone(), &scope_);
    scope()->set_start_position(position());

    // Also record whether inner functions or evals are found inside
    // this loop, as this information is used to simplify the desugaring
    // if none are found.
    typename FunctionState::FunctionOrEvalRecordingScope recording_scope(
        function_state_);

    // Create an inner block scope which will be the parent scope of scopes
    // possibly created by ParseVariableDeclarations.
    Scope* inner_block_scope = NewScope(BLOCK_SCOPE);
    {
      BlockState inner_state(&scope_, inner_block_scope);
      ParseVariableDeclarations(kForStatement, &for_info.parsing_result,
                                &for_info.bound_names);
    }
    DCHECK(IsLexicalVariableMode(for_info.parsing_result.descriptor.mode));
    for_info.position = position();

    if (CheckInOrOf(&for_info.mode)) {
      scope()->set_is_hidden();
      return ParseForEachStatementWithDeclarations(
          stmt_pos, &for_info, labels, own_labels, inner_block_scope);
    }

    Expect(Token::SEMICOLON);

    // Parse the remaining code in the inner block scope since the declaration
    // above was parsed there. We'll finalize the unnecessary outer block scope
    // after parsing the rest of the loop.
    StatementT result = impl()->NullStatement();
    inner_block_scope->set_start_position(scope()->start_position());
    {
      BlockState inner_state(&scope_, inner_block_scope);
      StatementT init =
          impl()->BuildInitializationBlock(&for_info.parsing_result);

      result = ParseStandardForLoopWithLexicalDeclarations(
          stmt_pos, init, &for_info, labels, own_labels);
    }
    Scope* finalized = scope()->FinalizeBlockScope();
    DCHECK_NULL(finalized);
    USE(finalized);
    return result;
  }

  StatementT init = impl()->NullStatement();
  if (peek() == Token::VAR) {
    ParseVariableDeclarations(kForStatement, &for_info.parsing_result,
                              &for_info.bound_names);
    DCHECK_EQ(for_info.parsing_result.descriptor.mode, VariableMode::kVar);
    for_info.position = scanner()->location().beg_pos;

    if (CheckInOrOf(&for_info.mode)) {
      return ParseForEachStatementWithDeclarations(stmt_pos, &for_info, labels,
                                                   own_labels, scope());
    }

    init = impl()->BuildInitializationBlock(&for_info.parsing_result);
  } else if (peek() != Token::SEMICOLON) {
    // The initializer does not contain declarations.
    Scanner::Location next_loc = scanner()->peek_location();
    int lhs_beg_pos = next_loc.beg_pos;
    int lhs_end_pos;
    bool is_for_each;
    ExpressionT expression;

    {
      ExpressionParsingScope parsing_scope(impl());
      AcceptINScope scope(this, false);
      expression = ParseExpressionCoverGrammar();
      // `for (async of` is disallowed but `for (async.x of` is allowed, so
      // check if the token is ASYNC after parsing the expression.
      bool expression_is_async = scanner()->current_token() == Token::ASYNC &&
                                 !scanner()->literal_contains_escapes();
      // Initializer is reference followed by in/of.
      lhs_end_pos = end_position();
      is_for_each = CheckInOrOf(&for_info.mode);
      if (is_for_each) {
        if ((starts_with_let || expression_is_async) &&
            for_info.mode == ForEachStatement::ITERATE) {
          impl()->ReportMessageAt(next_loc, starts_with_let
                                                ? MessageTemplate::kForOfLet
                                                : MessageTemplate::kForOfAsync);
          return impl()->NullStatement();
        }
        if (expression->IsPattern()) {
          parsing_scope.ValidatePattern(expression, lhs_beg_pos, lhs_end_pos);
        } else {
          expression = parsing_scope.ValidateAndRewriteReference(
              expression, lhs_beg_pos, lhs_end_pos);
        }
      } else {
        parsing_scope.ValidateExpression();
      }
    }

    if (is_for_each) {
      return ParseForEachStatementWithoutDeclarations(
          stmt_pos, expression, lhs_beg_pos, lhs_end_pos, &for_info, labels,
          own_labels);
    }
    // Initializer is just an expression.
    init = factory()->NewExpressionStatement(expression, lhs_beg_pos);
  }

  Expect(Token::SEMICOLON);

  // Standard 'for' loop, we have parsed the initializer at this point.
  ExpressionT cond = impl()->NullExpression();
  StatementT next = impl()->NullStatement();
  StatementT body = impl()->NullStatement();
  ForStatementT loop =
      ParseStandardForLoop(stmt_pos, labels, own_labels, &cond, &next, &body);
  RETURN_IF_PARSE_ERROR;
  loop->Initialize(init, cond, next, body);
  return loop;
}

template <typename Impl>
typename ParserBase<Impl>::StatementT
ParserBase<Impl>::ParseForEachStatementWithDeclarations(
    int stmt_pos, ForInfo* for_info, ZonePtrList<const AstRawString>* labels,
    ZonePtrList<const AstRawString>* own_labels, Scope* inner_block_scope) {
  // Just one declaration followed by in/of.
  if (for_info->parsing_result.declarations.size() != 1) {
    impl()->ReportMessageAt(for_info->parsing_result.bindings_loc,
                            MessageTemplate::kForInOfLoopMultiBindings,
                            ForEachStatement::VisitModeString(for_info->mode));
    return impl()->NullStatement();
  }
  if (for_info->parsing_result.first_initializer_loc.IsValid() &&
      (is_strict(language_mode()) ||
       for_info->mode == ForEachStatement::ITERATE ||
       IsLexicalVariableMode(for_info->parsing_result.descriptor.mode) ||
       !impl()->IsIdentifier(
           for_info->parsing_result.declarations[0].pattern))) {
    impl()->ReportMessageAt(for_info->parsing_result.first_initializer_loc,
                            MessageTemplate::kForInOfLoopInitializer,
                            ForEachStatement::VisitModeString(for_info->mode));
    return impl()->NullStatement();
  }

  BlockT init_block = impl()->RewriteForVarInLegacy(*for_info);

  auto loop = factory()->NewForEachStatement(for_info->mode, stmt_pos);
  Target target(this, loop, labels, own_labels, Target::TARGET_FOR_ANONYMOUS);

  ExpressionT enumerable = impl()->NullExpression();
  if (for_info->mode == ForEachStatement::ITERATE) {
    AcceptINScope scope(this, true);
    enumerable = ParseAssignmentExpression();
  } else {
    enumerable = ParseExpression();
  }

  Expect(Token::RPAREN);

  if (IsLexicalVariableMode(for_info->parsing_result.descriptor.mode)) {
    inner_block_scope->set_start_position(position());
  }

  ExpressionT each_variable = impl()->NullExpression();
  BlockT body_block = impl()->NullBlock();
  {
    BlockState block_state(&scope_, inner_block_scope);

    SourceRange body_range;
    StatementT body = impl()->NullStatement();
    {
      SourceRangeScope range_scope(scanner(), &body_range);
      body = ParseStatement(nullptr, nullptr);
    }
    impl()->RecordIterationStatementSourceRange(loop, body_range);

    impl()->DesugarBindingInForEachStatement(for_info, &body_block,
                                             &each_variable);
    body_block->statements()->Add(body, zone());

    if (IsLexicalVariableMode(for_info->parsing_result.descriptor.mode)) {
      scope()->set_end_position(end_position());
      body_block->set_scope(scope()->FinalizeBlockScope());
    }
  }

  loop->Initialize(each_variable, enumerable, body_block);

  init_block = impl()->CreateForEachStatementTDZ(init_block, *for_info);

  // Parsed for-in loop w/ variable declarations.
  if (!impl()->IsNull(init_block)) {
    init_block->statements()->Add(loop, zone());
    if (IsLexicalVariableMode(for_info->parsing_result.descriptor.mode)) {
      scope()->set_end_position(end_position());
      init_block->set_scope(scope()->FinalizeBlockScope());
    }
    return init_block;
  }

  return loop;
}

template <typename Impl>
typename ParserBase<Impl>::StatementT
ParserBase<Impl>::ParseForEachStatementWithoutDeclarations(
    int stmt_pos, ExpressionT expression, int lhs_beg_pos, int lhs_end_pos,
    ForInfo* for_info, ZonePtrList<const AstRawString>* labels,
    ZonePtrList<const AstRawString>* own_labels) {
  auto loop = factory()->NewForEachStatement(for_info->mode, stmt_pos);
  Target target(this, loop, labels, own_labels, Target::TARGET_FOR_ANONYMOUS);

  ExpressionT enumerable = impl()->NullExpression();
  if (for_info->mode == ForEachStatement::ITERATE) {
    AcceptINScope scope(this, true);
    enumerable = ParseAssignmentExpression();
  } else {
    enumerable = ParseExpression();
  }

  Expect(Token::RPAREN);

  StatementT body = impl()->NullStatement();
  SourceRange body_range;
  {
    SourceRangeScope range_scope(scanner(), &body_range);
    body = ParseStatement(nullptr, nullptr);
  }
  impl()->RecordIterationStatementSourceRange(loop, body_range);
  RETURN_IF_PARSE_ERROR;
  loop->Initialize(expression, enumerable, body);
  return loop;
}

template <typename Impl>
typename ParserBase<Impl>::StatementT
ParserBase<Impl>::ParseStandardForLoopWithLexicalDeclarations(
    int stmt_pos, StatementT init, ForInfo* for_info,
    ZonePtrList<const AstRawString>* labels,
    ZonePtrList<const AstRawString>* own_labels) {
  // The condition and the next statement of the for loop must be parsed
  // in a new scope.
  Scope* inner_scope = NewScope(BLOCK_SCOPE);
  ForStatementT loop = impl()->NullStatement();
  ExpressionT cond = impl()->NullExpression();
  StatementT next = impl()->NullStatement();
  StatementT body = impl()->NullStatement();
  {
    BlockState block_state(&scope_, inner_scope);
    scope()->set_start_position(scanner()->location().beg_pos);
    loop =
        ParseStandardForLoop(stmt_pos, labels, own_labels, &cond, &next, &body);
    RETURN_IF_PARSE_ERROR;
    scope()->set_end_position(end_position());
  }

  scope()->set_end_position(end_position());
  if (for_info->bound_names.length() > 0 &&
      function_state_->contains_function_or_eval()) {
    scope()->set_is_hidden();
    return impl()->DesugarLexicalBindingsInForStatement(
        loop, init, cond, next, body, inner_scope, *for_info);
  } else {
    inner_scope = inner_scope->FinalizeBlockScope();
    DCHECK_NULL(inner_scope);
    USE(inner_scope);
  }

  Scope* for_scope = scope()->FinalizeBlockScope();
  if (for_scope != nullptr) {
    // Rewrite a for statement of the form
    //   for (const x = i; c; n) b
    //
    // into
    //
    //   {
    //     const x = i;
    //     for (; c; n) b
    //   }
    //
    DCHECK(!impl()->IsNull(init));
    BlockT block = factory()->NewBlock(2, false);
    block->statements()->Add(init, zone());
    block->statements()->Add(loop, zone());
    block->set_scope(for_scope);
    loop->Initialize(impl()->NullStatement(), cond, next, body);
    return block;
  }

  loop->Initialize(init, cond, next, body);
  return loop;
}

template <typename Impl>
typename ParserBase<Impl>::ForStatementT ParserBase<Impl>::ParseStandardForLoop(
    int stmt_pos, ZonePtrList<const AstRawString>* labels,
    ZonePtrList<const AstRawString>* own_labels, ExpressionT* cond,
    StatementT* next, StatementT* body) {
  CheckStackOverflow();
  ForStatementT loop = factory()->NewForStatement(stmt_pos);
  Target target(this, loop, labels, own_labels, Target::TARGET_FOR_ANONYMOUS);

  if (peek() != Token::SEMICOLON) {
    *cond = ParseExpression();
  }
  Expect(Token::SEMICOLON);

  if (peek() != Token::RPAREN) {
    ExpressionT exp = ParseExpression();
    *next = factory()->NewExpressionStatement(exp, exp->position());
  }
  Expect(Token::RPAREN);

  SourceRange body_range;
  {
    SourceRangeScope range_scope(scanner(), &body_range);
    *body = ParseStatement(nullptr, nullptr);
  }
  impl()->RecordIterationStatementSourceRange(loop, body_range);

  return loop;
}

template <typename Impl>
typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForAwaitStatement(
    ZonePtrList<const AstRawString>* labels,
    ZonePtrList<const AstRawString>* own_labels) {
  // for await '(' ForDeclaration of AssignmentExpression ')'
  DCHECK(is_await_allowed());
  typename FunctionState::LoopScope loop_scope(function_state_);

  int stmt_pos = peek_position();

  ForInfo for_info(this);
  for_info.mode = ForEachStatement::ITERATE;

  // Create an in-between scope for let-bound iteration variables.
  BlockState for_state(zone(), &scope_);
  Expect(Token::FOR);
  Expect(Token::AWAIT);
  Expect(Token::LPAREN);
  scope()->set_start_position(scanner()->location().beg_pos);
  scope()->set_is_hidden();

  auto loop = factory()->NewForOfStatement(stmt_pos, IteratorType::kAsync);
  // Two suspends: one for next() and one for return()
  function_state_->AddSuspend();
  function_state_->AddSuspend();

  Target target(this, loop, labels, own_labels, Target::TARGET_FOR_ANONYMOUS);

  ExpressionT each_variable = impl()->NullExpression();

  bool has_declarations = false;
  Scope* inner_block_scope = NewScope(BLOCK_SCOPE);

  bool starts_with_let = peek() == Token::LET;
  if (peek() == Token::VAR || peek() == Token::CONST ||
      (starts_with_let && IsNextLetKeyword())) {
    // The initializer contains declarations
    // 'for' 'await' '(' ForDeclaration 'of' AssignmentExpression ')'
    //     Statement
    // 'for' 'await' '(' 'var' ForBinding 'of' AssignmentExpression ')'
    //     Statement
    has_declarations = true;

    {
      BlockState inner_state(&scope_, inner_block_scope);
      ParseVariableDeclarations(kForStatement, &for_info.parsing_result,
                                &for_info.bound_names);
    }
    for_info.position = scanner()->location().beg_pos;

    // Only a single declaration is allowed in for-await-of loops
    if (for_info.parsing_result.declarations.size() != 1) {
      impl()->ReportMessageAt(for_info.parsing_result.bindings_loc,
                              MessageTemplate::kForInOfLoopMultiBindings,
                              "for-await-of");
      return impl()->NullStatement();
    }

    // for-await-of's declarations do not permit initializers.
    if (for_info.parsing_result.first_initializer_loc.IsValid()) {
      impl()->ReportMessageAt(for_info.parsing_result.first_initializer_loc,
                              MessageTemplate::kForInOfLoopInitializer,
                              "for-await-of");
      return impl()->NullStatement();
    }
  } else {
    // The initializer does not contain declarations.
    // 'for' 'await' '(' LeftHandSideExpression 'of' AssignmentExpression ')'
    //     Statement
    if (starts_with_let) {
      impl()->ReportMessageAt(scanner()->peek_location(),
                              MessageTemplate::kForOfLet);
      return impl()->NullStatement();
    }
    int lhs_beg_pos = peek_position();
    BlockState inner_state(&scope_, inner_block_scope);
    ExpressionParsingScope parsing_scope(impl());
    ExpressionT lhs = each_variable = ParseLeftHandSideExpression();
    int lhs_end_pos = end_position();

    if (lhs->IsPattern()) {
      parsing_scope.ValidatePattern(lhs, lhs_beg_pos, lhs_end_pos);
    } else {
      each_variable = parsing_scope.ValidateAndRewriteReference(
          lhs, lhs_beg_pos, lhs_end_pos);
    }
  }

  ExpectContextualKeyword(ast_value_factory()->of_string());

  const bool kAllowIn = true;
  ExpressionT iterable = impl()->NullExpression();

  {
    AcceptINScope scope(this, kAllowIn);
    iterable = ParseAssignmentExpression();
  }

  Expect(Token::RPAREN);

  StatementT body = impl()->NullStatement();
  {
    BlockState block_state(&scope_, inner_block_scope);
    scope()->set_start_position(scanner()->location().beg_pos);

    SourceRange body_range;
    {
      SourceRangeScope range_scope(scanner(), &body_range);
      body = ParseStatement(nullptr, nullptr);
      scope()->set_end_position(end_position());
    }
    impl()->RecordIterationStatementSourceRange(loop, body_range);

    if (has_declarations) {
      BlockT body_block = impl()->NullBlock();
      impl()->DesugarBindingInForEachStatement(&for_info, &body_block,
                                               &each_variable);
      body_block->statements()->Add(body, zone());
      body_block->set_scope(scope()->FinalizeBlockScope());
      body = body_block;
    } else {
      Scope* block_scope = scope()->FinalizeBlockScope();
      DCHECK_NULL(block_scope);
      USE(block_scope);
    }
  }

  loop->Initialize(each_variable, iterable, body);

  if (!has_declarations) {
    Scope* for_scope = scope()->FinalizeBlockScope();
    DCHECK_NULL(for_scope);
    USE(for_scope);
    return loop;
  }

  BlockT init_block =
      impl()->CreateForEachStatementTDZ(impl()->NullBlock(), for_info);

  scope()->set_end_position(end_position());
  Scope* for_scope = scope()->FinalizeBlockScope();
  // Parsed for-in loop w/ variable declarations.
  if (!impl()->IsNull(init_block)) {
    init_block->statements()->Add(loop, zone());
    init_block->set_scope(for_scope);
    return init_block;
  }
  DCHECK_NULL(for_scope);
  return loop;
}

template <typename Impl>
void ParserBase<Impl>::CheckClassMethodName(IdentifierT name,
                                            ParsePropertyKind type,
                                            ParseFunctionFlags flags,
                                            bool is_static,
                                            bool* has_seen_constructor) {
  DCHECK(type == ParsePropertyKind::kMethod || IsAccessor(type));

  AstValueFactory* avf = ast_value_factory();

  if (impl()->IdentifierEquals(name, avf->private_constructor_string())) {
    ReportMessage(MessageTemplate::kConstructorIsPrivate);
    return;
  } else if (is_static) {
    if (impl()->IdentifierEquals(name, avf->prototype_string())) {
      ReportMessage(MessageTemplate::kStaticPrototype);
      return;
    }
  } else if (impl()->IdentifierEquals(name, avf->constructor_string())) {
    if (flags != ParseFunctionFlag::kIsNormal || IsAccessor(type)) {
      MessageTemplate msg = (flags & ParseFunctionFlag::kIsGenerator) != 0
                                ? MessageTemplate::kConstructorIsGenerator
                                : (flags & ParseFunctionFlag::kIsAsync) != 0
                                      ? MessageTemplate::kConstructorIsAsync
                                      : MessageTemplate::kConstructorIsAccessor;
      ReportMessage(msg);
      return;
    }
    if (*has_seen_constructor) {
      ReportMessage(MessageTemplate::kDuplicateConstructor);
      return;
    }
    *has_seen_constructor = true;
    return;
  }
}

template <typename Impl>
void ParserBase<Impl>::CheckClassFieldName(IdentifierT name, bool is_static) {
  AstValueFactory* avf = ast_value_factory();
  if (is_static && impl()->IdentifierEquals(name, avf->prototype_string())) {
    ReportMessage(MessageTemplate::kStaticPrototype);
    return;
  }

  if (impl()->IdentifierEquals(name, avf->constructor_string()) ||
      impl()->IdentifierEquals(name, avf->private_constructor_string())) {
    ReportMessage(MessageTemplate::kConstructorClassField);
    return;
  }
}

#undef RETURN_IF_PARSE_ERROR

}  // namespace internal
}  // namespace v8

#endif  // V8_PARSING_PARSER_BASE_H_