blob: 570202e6e7a8b7c14059fa9a9f79d188c62221eb [file] [log] [blame]
// 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/counters.h"
#include "src/logging/log.h"
#include "src/objects/function-kind.h"
#include "src/parsing/expression-scope.h"
#include "src/parsing/func-name-inferrer.h"
#include "src/parsing/scanner.h"
#include "src/parsing/token.h"
#include "src/utils/pointer-with-payload.h"
#include "src/zone/zone-chunk-list.h"
namespace v8 {
namespace internal {
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 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 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;
using TargetT = typename Types::Target;
using TargetScopeT = typename Types::TargetScope;
// 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,
v8::Extension* extension, AstValueFactory* ast_value_factory,
PendingCompilationErrorHandler* pending_error_handler,
RuntimeCallStats* runtime_call_stats, Logger* logger,
int script_id, bool parsing_module, bool parsing_on_main_thread)
: scope_(nullptr),
original_scope_(nullptr),
function_state_(nullptr),
extension_(extension),
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),
parsing_module_(parsing_module),
stack_limit_(stack_limit),
pending_error_handler_(pending_error_handler),
zone_(zone),
expression_scope_(nullptr),
scanner_(scanner),
function_literal_id_(0),
script_id_(script_id),
default_eager_compile_hint_(FunctionLiteral::kShouldLazyCompile),
allow_natives_(false),
allow_harmony_dynamic_import_(false),
allow_harmony_import_meta_(false),
allow_harmony_private_methods_(false),
allow_eval_cache_(true) {
pointer_buffer_.reserve(32);
variable_buffer_.reserve(32);
}
#define ALLOW_ACCESSORS(name) \
bool allow_##name() const { return allow_##name##_; } \
void set_allow_##name(bool allow) { allow_##name##_ = allow; }
ALLOW_ACCESSORS(natives)
ALLOW_ACCESSORS(harmony_dynamic_import)
ALLOW_ACCESSORS(harmony_import_meta)
ALLOW_ACCESSORS(harmony_private_methods)
ALLOW_ACCESSORS(eval_cache)
#undef ALLOW_ACCESSORS
V8_INLINE bool has_error() const { return scanner()->has_parser_error(); }
bool allow_harmony_optional_chaining() const {
return scanner()->allow_harmony_optional_chaining();
}
void set_allow_harmony_optional_chaining(bool allow) {
scanner()->set_allow_harmony_optional_chaining(allow);
}
bool allow_harmony_nullish() const {
return scanner()->allow_harmony_nullish();
}
void set_allow_harmony_nullish(bool allow) {
scanner()->set_allow_harmony_nullish(allow);
}
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()->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,
new (zone) Scope(zone, *scope_stack, BLOCK_SCOPE)) {}
~BlockState() { *scope_stack_ = outer_scope_; }
private:
Scope** const scope_stack_;
Scope* const outer_scope_;
};
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 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 LoopScope {
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_; }
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_;
// 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)
: variable(nullptr),
extends(parser->impl()->NullExpression()),
properties(parser->impl()->NewClassPropertyList(4)),
static_fields(parser->impl()->NewClassPropertyList(4)),
instance_fields(parser->impl()->NewClassPropertyList(4)),
constructor(parser->impl()->NullExpression()),
has_seen_constructor(false),
has_name_static_property(false),
has_static_computed_names(false),
has_static_class_fields(false),
has_instance_members(false),
requires_brand(false),
is_anonymous(false),
static_fields_scope(nullptr),
instance_members_scope(nullptr),
computed_field_count(0) {}
Variable* variable;
ExpressionT extends;
ClassPropertyListT properties;
ClassPropertyListT static_fields;
ClassPropertyListT instance_fields;
FunctionLiteralT constructor;
bool has_seen_constructor;
bool has_name_static_property;
bool has_static_computed_names;
bool has_static_class_fields;
bool has_instance_members;
bool requires_brand;
bool is_anonymous;
DeclarationScope* static_fields_scope;
DeclarationScope* instance_members_scope;
int computed_field_count;
};
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;
};
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() const {
return new (zone()) DeclarationScope(zone(), ast_value_factory());
}
DeclarationScope* NewVarblockScope() const {
return new (zone()) DeclarationScope(zone(), scope(), BLOCK_SCOPE);
}
ModuleScope* NewModuleScope(DeclarationScope* parent) const {
return new (zone()) ModuleScope(parent, ast_value_factory());
}
DeclarationScope* NewEvalScope(Scope* parent) const {
return new (zone()) DeclarationScope(zone(), parent, EVAL_SCOPE);
}
ClassScope* NewClassScope(Scope* parent) const {
return new (zone()) ClassScope(zone(), parent);
}
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 blacklisted 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 new (zone()) 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 = new (zone())
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();
}
int script_id() { return script_id_; }
void set_script_id(int id) { script_id_ = id; }
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()) {
ReportMessageAt(scanner()->location(),
MessageTemplate::kAwaitNotInAsyncFunction);
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());
}
const PendingCompilationErrorHandler* pending_error_handler() const {
return pending_error_handler_;
}
PendingCompilationErrorHandler* pending_error_handler() {
return pending_error_handler_;
}
// Report syntax errors.
V8_NOINLINE void ReportMessage(MessageTemplate message) {
Scanner::Location source_location = scanner()->location();
impl()->ReportMessageAt(source_location, message,
static_cast<const char*>(nullptr));
}
template <typename T>
V8_NOINLINE void ReportMessage(MessageTemplate message, T arg) {
Scanner::Location source_location = scanner()->location();
impl()->ReportMessageAt(source_location, message, arg);
}
V8_NOINLINE void ReportMessageAt(Scanner::Location location,
MessageTemplate message) {
impl()->ReportMessageAt(location, message,
static_cast<const char*>(nullptr));
}
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);
// 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();
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 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);
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 ParseClassLiteral(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(bool is_new);
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;
Declaration* decl = scope->CheckConflictingVarDeclarations();
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);
// 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.
ExpressionT RewriteInvalidReferenceExpression(ExpressionT expression,
int beg_pos, int end_pos,
MessageTemplate message);
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;
}
FunctionKind FunctionKindForImpl(bool is_method, ParseFunctionFlags flags) {
static const FunctionKind kFunctionKinds[][2][2] = {
{
// is_method=false
{// is_generator=false
FunctionKind::kNormalFunction, FunctionKind::kAsyncFunction},
{// is_generator=true
FunctionKind::kGeneratorFunction,
FunctionKind::kAsyncGeneratorFunction},
},
{
// is_method=true
{// is_generator=false
FunctionKind::kConciseMethod, FunctionKind::kAsyncConciseMethod},
{// is_generator=true
FunctionKind::kConciseGeneratorMethod,
FunctionKind::kAsyncConciseGeneratorMethod},
}};
return kFunctionKinds[is_method]
[(flags & ParseFunctionFlag::kIsGenerator) != 0]
[(flags & ParseFunctionFlag::kIsAsync) != 0];
}
inline FunctionKind FunctionKindFor(ParseFunctionFlags flags) {
const bool kIsMethod = false;
return FunctionKindForImpl(kIsMethod, flags);
}
inline FunctionKind MethodKindFor(ParseFunctionFlags flags) {
const bool kIsMethod = true;
return FunctionKindForImpl(kIsMethod, 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,
Scope* scope) {
if (impl()->IsIdentifier(expression) &&
impl()->IsEval(impl()->AsIdentifier(expression))) {
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 = kNoSourcePosition) {
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 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 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 FunctionBodyParsingScope {
public:
explicit FunctionBodyParsingScope(Impl* parser)
: parser_(parser), expression_scope_(parser_->expression_scope_) {
parser_->expression_scope_ = nullptr;
}
~FunctionBodyParsingScope() {
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.
Scope* original_scope_; // The top scope for the current parsing item.
FunctionState* function_state_; // Function state stack.
v8::Extension* extension_;
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_;
const bool parsing_module_;
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_;
int function_literal_id_;
int script_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_natives_;
bool allow_harmony_dynamic_import_;
bool allow_harmony_import_meta_;
bool allow_harmony_private_methods_;
bool allow_eval_cache_;
};
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>
typename ParserBase<Impl>::IdentifierT
ParserBase<Impl>::ParseAndClassifyIdentifier(Token::Value next) {
DCHECK_EQ(scanner()->current_token(), next);
if (V8_LIKELY(IsInRange(next, Token::IDENTIFIER, Token::ASYNC))) {
IdentifierT name = impl()->GetIdentifier();
if (V8_UNLIKELY(impl()->IsArguments(name) &&
scope()->ShouldBanArguments())) {
ReportMessage(MessageTemplate::kArgumentsDisallowedInInitializer);
return impl()->EmptyIdentifierString();
}
return name;
}
if (!Token::IsValidIdentifier(next, language_mode(), is_generator(),
parsing_module_ || is_async_function())) {
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),
parsing_module_ || IsAsyncFunction(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>
typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseRegExpLiteral() {
int pos = peek_position();
if (!scanner()->ScanRegExpPattern()) {
Next();
ReportMessage(MessageTemplate::kUnterminatedRegExp);
return impl()->FailureExpression();
}
IdentifierT js_pattern = impl()->GetNextSymbol();
Maybe<int> flags = scanner()->ScanRegExpFlags();
if (flags.IsNothing()) {
Next();
ReportMessage(MessageTemplate::kMalformedRegExpFlags);
return impl()->FailureExpression();
}
Next();
return factory()->NewRegExpLiteral(js_pattern, flags.FromJust(), 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()->ThisExpression();
}
case Token::ASSIGN_DIV:
case Token::DIV:
return ParseRegExpLiteral();
case Token::FUNCTION:
return ParseFunctionExpression();
case Token::SUPER: {
const bool is_new = false;
return ParseSuperExpression(is_new);
}
case Token::IMPORT:
if (!allow_harmony_dynamic_import()) break;
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: {
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(name, class_name_location,
is_strict_reserved_name, class_token_pos);
}
case Token::TEMPLATE_SPAN:
case Token::TEMPLATE_TAIL:
return ParseTemplateLiteral(impl()->NullExpression(), beg_pos, false);
case Token::MOD:
if (allow_natives() || extension_ != nullptr) {
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 &&
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();
}
if (V8_UNLIKELY(!allow_harmony_private_methods() &&
(IsAccessor(prop_info->kind) ||
prop_info->kind == ParsePropertyKind::kMethod))) {
ReportUnexpectedToken(Next());
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::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);
}
if (!class_info->has_name_static_property && prop_info->is_static &&
impl()->IsName(prop_info->name)) {
class_info->has_name_static_property = true;
}
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->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 = 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) {
DeclarationScope* initializer_scope =
is_static ? class_info->static_fields_scope
: class_info->instance_members_scope;
if (initializer_scope == nullptr) {
initializer_scope =
NewFunctionScope(FunctionKind::kClassMembersInitializerFunction);
// TODO(gsathya): Make scopes be non contiguous.
initializer_scope->set_start_position(beg_pos);
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);
}
initializer_scope->set_end_position(end_position());
if (is_static) {
class_info->static_fields_scope = initializer_scope;
class_info->has_static_class_fields = true;
} else {
class_info->instance_members_scope = initializer_scope;
class_info->has_instance_members = true;
}
return initializer;
}
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;
ParsePropertyKind kind = prop_info->kind;
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 (!Token::IsValidIdentifier(name_token, language_mode(), is_generator(),
parsing_module_ || is_async_function())) {
ReportUnexpectedToken(Next());
return impl()->NullLiteralProperty();
}
DCHECK(!prop_info->is_computed_name);
if (name_token == Token::AWAIT) {
DCHECK(!is_async_function());
expression_scope()->RecordAsyncArrowParametersError(
next_loc, MessageTemplate::kAwaitBindingIdentifier);
}
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);
FunctionKind kind = MethodKindFor(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 = 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());
}
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());
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();
}
// 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));
}
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;
}