blob: 0bde3a0687f33c8103406dab6170f2cbb6c6513f [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 <vector>
#include "src/ast/ast-source-ranges.h"
#include "src/ast/ast.h"
#include "src/ast/scopes.h"
#include "src/bailout-reason.h"
#include "src/base/hashmap.h"
#include "src/base/v8-fallthrough.h"
#include "src/counters.h"
#include "src/globals.h"
#include "src/log.h"
#include "src/messages.h"
#include "src/parsing/expression-classifier.h"
#include "src/parsing/func-name-inferrer.h"
#include "src/parsing/scanner.h"
#include "src/parsing/token.h"
#include "src/zone/zone-chunk-list.h"
namespace v8 {
namespace internal {
enum FunctionNameValidity {
kFunctionNameIsStrictReserved,
kSkipFunctionNameCheck,
kFunctionNameValidityUnknown
};
enum AllowLabelledFunctionStatement {
kAllowLabelledFunctionStatement,
kDisallowLabelledFunctionStatement,
};
enum class ParseFunctionFlags {
kIsNormal = 0,
kIsGenerator = 1,
kIsAsync = 2,
kIsDefault = 4
};
static inline ParseFunctionFlags operator|(ParseFunctionFlags lhs,
ParseFunctionFlags rhs) {
typedef unsigned char T;
return static_cast<ParseFunctionFlags>(static_cast<T>(lhs) |
static_cast<T>(rhs));
}
static inline ParseFunctionFlags& operator|=(ParseFunctionFlags& lhs,
const ParseFunctionFlags& rhs) {
lhs = lhs | rhs;
return lhs;
}
static inline bool operator&(ParseFunctionFlags bitfield,
ParseFunctionFlags mask) {
typedef unsigned char T;
return static_cast<T>(bitfield) & static_cast<T>(mask);
}
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:
enum PositionKind {
POSITION_BEG,
POSITION_END,
PEEK_POSITION_BEG,
PEEK_POSITION_END,
};
SourceRangeScope(Scanner* scanner, SourceRange* range,
PositionKind pre_kind = PEEK_POSITION_BEG,
PositionKind post_kind = POSITION_END)
: scanner_(scanner), range_(range), post_kind_(post_kind) {
range_->start = GetPosition(pre_kind);
DCHECK_NE(range_->start, kNoSourcePosition);
}
~SourceRangeScope() { Finalize(); }
const SourceRange& Finalize() {
if (is_finalized_) return *range_;
is_finalized_ = true;
range_->end = GetPosition(post_kind_);
DCHECK_NE(range_->end, kNoSourcePosition);
return *range_;
}
private:
int32_t GetPosition(PositionKind kind) {
switch (kind) {
case POSITION_BEG:
return scanner_->location().beg_pos;
case POSITION_END:
return scanner_->location().end_pos;
case PEEK_POSITION_BEG:
return scanner_->peek_location().beg_pos;
case PEEK_POSITION_END:
return scanner_->peek_location().end_pos;
default:
UNREACHABLE();
}
}
Scanner* scanner_;
SourceRange* range_;
PositionKind post_kind_;
bool is_finalized_ = false;
DISALLOW_IMPLICIT_CONSTRUCTORS(SourceRangeScope);
};
// ----------------------------------------------------------------------------
// The CHECK_OK macro is a convenient macro to enforce error
// handling for functions that may fail (by returning !*ok).
//
// CAUTION: This macro appends extra statements after a call,
// thus it must never be used where only a single statement
// is correct (e.g. an if statement branch w/o braces)!
#define CHECK_OK_CUSTOM(x, ...) ok); \
if (!*ok) return impl()->x(__VA_ARGS__); \
((void)0
#define DUMMY ) // to make indentation work
#undef DUMMY
// Used in functions where the return type is ExpressionT.
#define CHECK_OK CHECK_OK_CUSTOM(NullExpression)
#define CHECK_OK_VOID ok); \
if (!*ok) return; \
((void)0
#define DUMMY ) // to make indentation work
#undef DUMMY
// 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;
template <typename Impl>
class ParserBase {
public:
// Shorten type names defined by ParserTypes<Impl>.
typedef ParserTypes<Impl> Types;
typedef typename Types::Identifier IdentifierT;
typedef typename Types::Expression ExpressionT;
typedef typename Types::FunctionLiteral FunctionLiteralT;
typedef typename Types::ObjectLiteralProperty ObjectLiteralPropertyT;
typedef typename Types::ClassLiteralProperty ClassLiteralPropertyT;
typedef typename Types::Suspend SuspendExpressionT;
typedef typename Types::RewritableExpression RewritableExpressionT;
typedef typename Types::ExpressionList ExpressionListT;
typedef typename Types::FormalParameters FormalParametersT;
typedef typename Types::Statement StatementT;
typedef typename Types::StatementList StatementListT;
typedef typename Types::Block BlockT;
typedef typename Types::ForStatement ForStatementT;
typedef typename v8::internal::ExpressionClassifier<Types>
ExpressionClassifier;
// 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_(nullptr),
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),
classifier_(nullptr),
scanner_(scanner),
default_eager_compile_hint_(FunctionLiteral::kShouldLazyCompile),
function_literal_id_(0),
script_id_(script_id),
allow_natives_(false),
allow_harmony_do_expressions_(false),
allow_harmony_public_fields_(false),
allow_harmony_static_fields_(false),
allow_harmony_dynamic_import_(false),
allow_harmony_import_meta_(false),
allow_harmony_private_fields_(false),
allow_eval_cache_(true) {}
#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_do_expressions);
ALLOW_ACCESSORS(harmony_public_fields);
ALLOW_ACCESSORS(harmony_static_fields);
ALLOW_ACCESSORS(harmony_dynamic_import);
ALLOW_ACCESSORS(harmony_import_meta);
ALLOW_ACCESSORS(eval_cache);
#undef ALLOW_ACCESSORS
bool allow_harmony_bigint() const {
return scanner()->allow_harmony_bigint();
}
void set_allow_harmony_bigint(bool allow) {
scanner()->set_allow_harmony_bigint(allow);
}
bool allow_harmony_numeric_separator() const {
return scanner()->allow_harmony_numeric_separator();
}
void set_allow_harmony_numeric_separator(bool allow) {
scanner()->set_allow_harmony_numeric_separator(allow);
}
bool allow_harmony_private_fields() const {
return scanner()->allow_harmony_private_fields();
}
void set_allow_harmony_private_fields(bool allow) {
scanner()->set_allow_harmony_private_fields(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 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::ExpressionClassifier<ParserTypes<Impl>>;
enum AllowRestrictedIdentifiers {
kAllowRestrictedIdentifiers,
kDontAllowRestrictedIdentifiers
};
enum LazyParsingResult { kLazyParsingComplete, kLazyParsingAborted };
enum VariableDeclarationContext {
kStatementListItem,
kStatement,
kForStatement
};
class ClassLiteralChecker;
class ObjectLiteralChecker;
// ---------------------------------------------------------------------------
// 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 BASE_EMBEDDED {
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(); }
void RewindDestructuringAssignments(int pos) {
destructuring_assignments_to_rewrite_.Rewind(pos);
}
void AdoptDestructuringAssignmentsFromParentState(int pos) {
const auto& outer_assignments =
outer_function_state_->destructuring_assignments_to_rewrite_;
DCHECK_GE(outer_assignments.size(), pos);
auto it = outer_assignments.begin();
it.Advance(pos);
for (; it != outer_assignments.end(); ++it) {
auto expr = *it;
expr->set_scope(scope_);
destructuring_assignments_to_rewrite_.push_back(expr);
}
outer_function_state_->RewindDestructuringAssignments(pos);
}
const ZoneChunkList<RewritableExpressionT>&
destructuring_assignments_to_rewrite() const {
return destructuring_assignments_to_rewrite_;
}
ZoneList<typename ExpressionClassifier::Error>* GetReportedErrorList() {
return &reported_errors_;
}
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_ = true;
}
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_(state) {
prev_value_ = state->contains_function_or_eval_;
state->contains_function_or_eval_ = false;
}
~FunctionOrEvalRecordingScope() {
bool found = state_->contains_function_or_eval_;
if (!found) {
state_->contains_function_or_eval_ = prev_value_;
}
}
private:
FunctionState* state_;
bool prev_value_;
};
private:
void AddDestructuringAssignment(RewritableExpressionT expr) {
destructuring_assignments_to_rewrite_.push_back(expr);
}
// Properties count estimation.
int expected_property_count_;
FunctionState** function_state_stack_;
FunctionState* outer_function_state_;
DeclarationScope* scope_;
ZoneChunkList<RewritableExpressionT> destructuring_assignments_to_rewrite_;
ZoneList<typename ExpressionClassifier::Error> reported_errors_;
// A reason, if any, why this function should not be optimized.
BailoutReason dont_optimize_reason_;
// How many suspends are needed for this function.
int suspend_count_;
// 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 {
enum Kind { NORMAL, PARAMETER, FOR_EACH };
Scope* scope;
VariableMode mode;
int declaration_pos;
int initialization_pos;
Kind declaration_kind;
};
struct DeclarationParsingResult {
struct Declaration {
Declaration(ExpressionT pattern, int initializer_position,
ExpressionT initializer)
: pattern(pattern),
initializer_position(initializer_position),
initializer(initializer) {}
ExpressionT pattern;
int initializer_position;
int value_beg_position = kNoSourcePosition;
ExpressionT initializer;
};
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)
: name(parser->impl()->NullIdentifier()),
pattern(parser->impl()->NullExpression()),
scope(nullptr),
init_block(parser->impl()->NullStatement()),
inner_block(parser->impl()->NullStatement()),
bound_names(1, parser->zone()) {}
IdentifierT name;
ExpressionT pattern;
Scope* scope;
BlockT init_block;
BlockT inner_block;
ZonePtrList<const AstRawString> bound_names;
};
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_class_fields(false),
is_anonymous(false),
static_fields_scope(nullptr),
instance_fields_scope(nullptr),
computed_field_count(0) {}
Variable* variable;
ExpressionT extends;
typename Types::ClassPropertyList properties;
typename Types::ClassPropertyList static_fields;
typename Types::ClassPropertyList instance_fields;
FunctionLiteralT constructor;
// TODO(gsathya): Use a bitfield store all the booleans.
bool has_seen_constructor;
bool has_name_static_property;
bool has_static_computed_names;
bool has_static_class_fields;
bool has_instance_class_fields;
bool is_anonymous;
DeclarationScope* static_fields_scope;
DeclarationScope* instance_fields_scope;
int computed_field_count;
};
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);
}
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* target_zone = nullptr) const {
DCHECK(ast_value_factory());
if (target_zone == nullptr) target_zone = zone();
DeclarationScope* result = new (target_zone)
DeclarationScope(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();
}
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; }
bool stack_overflow() const {
return pending_error_handler()->stack_overflow();
}
void set_stack_overflow() { pending_error_handler()->set_stack_overflow(); }
int script_id() { return script_id_; }
void set_script_id(int id) { script_id_ = id; }
V8_INLINE Token::Value peek() {
if (stack_overflow()) return Token::ILLEGAL;
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) ? scanner_->peek_location().end_pos
: scanner_->location().end_pos;
}
V8_INLINE Token::Value PeekAhead() {
if (stack_overflow()) return Token::ILLEGAL;
return scanner()->PeekAhead();
}
V8_INLINE Token::Value Next() {
if (stack_overflow()) return Token::ILLEGAL;
{
if (GetCurrentStackPosition() < stack_limit_) {
// Any further calls to Next or peek will return the illegal token.
// The current call must return the next token, which might already
// have been peek'ed.
set_stack_overflow();
}
}
return scanner()->Next();
}
void Consume(Token::Value token) {
Token::Value next = Next();
USE(next);
USE(token);
DCHECK(next == token);
}
bool Check(Token::Value token) {
Token::Value next = peek();
if (next == token) {
Consume(next);
return true;
}
return false;
}
void Expect(Token::Value token, bool* ok) {
Token::Value next = Next();
if (next != token) {
ReportUnexpectedToken(next);
*ok = false;
}
}
void ExpectSemicolon(bool* ok) {
// Check for automatic semicolon insertion according to
// the rules given in ECMA-262, section 7.9, page 21.
Token::Value tok = peek();
if (tok == Token::SEMICOLON) {
Next();
return;
}
if (scanner()->HasAnyLineTerminatorBeforeNext() ||
tok == Token::RBRACE ||
tok == Token::EOS) {
return;
}
Token::Value current = scanner()->current_token();
Scanner::Location current_location = scanner()->location();
Token::Value next = Next();
if (next == Token::SEMICOLON) {
return;
}
*ok = false;
if (current == Token::AWAIT && !is_async_function()) {
ReportMessageAt(current_location,
MessageTemplate::kAwaitNotInAsyncFunction, kSyntaxError);
return;
}
ReportUnexpectedToken(next);
}
// Dummy functions, just useful as arguments to CHECK_OK_CUSTOM.
static void Void() {}
template <typename T>
static T Return(T result) {
return result;
}
bool is_any_identifier(Token::Value token) {
return token == Token::IDENTIFIER || token == Token::ENUM ||
token == Token::AWAIT || token == Token::ASYNC ||
token == Token::ESCAPED_STRICT_RESERVED_WORD ||
token == Token::FUTURE_STRICT_RESERVED_WORD || token == Token::LET ||
token == Token::STATIC || token == Token::YIELD;
}
bool peek_any_identifier() { return is_any_identifier(peek()); }
bool CheckContextualKeyword(Token::Value token) {
if (PeekContextualKeyword(token)) {
Consume(Token::IDENTIFIER);
return true;
}
return false;
}
bool PeekContextualKeyword(Token::Value token) {
DCHECK(Token::IsContextualKeyword(token));
return peek() == Token::IDENTIFIER &&
scanner()->next_contextual_token() == token;
}
void ExpectMetaProperty(Token::Value property_name, const char* full_name,
int pos, bool* ok);
void ExpectContextualKeyword(Token::Value token, bool* ok) {
DCHECK(Token::IsContextualKeyword(token));
Expect(Token::IDENTIFIER, CHECK_OK_CUSTOM(Void));
if (scanner()->current_contextual_token() != token) {
ReportUnexpectedToken(scanner()->current_token());
*ok = false;
}
}
bool CheckInOrOf(ForEachStatement::VisitMode* visit_mode) {
if (Check(Token::IN)) {
*visit_mode = ForEachStatement::ENUMERATE;
return true;
} else if (CheckContextualKeyword(Token::OF)) {
*visit_mode = ForEachStatement::ITERATE;
return true;
}
return false;
}
bool PeekInOrOf() {
return peek() == Token::IN || PeekContextualKeyword(Token::OF);
}
// 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, bool* ok) {
Scanner::Location octal = scanner()->octal_position();
if (octal.IsValid() && beg_pos <= octal.beg_pos &&
octal.end_pos <= end_pos) {
MessageTemplate::Template 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);
}
*ok = false;
}
}
// 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, bool* ok) {
DCHECK(scanner()->current_token() == Token::TEMPLATE_SPAN ||
scanner()->current_token() == Token::TEMPLATE_TAIL);
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());
*ok = false;
}
return false;
}
void CheckDestructuringElement(ExpressionT element, int beg_pos, int end_pos);
// 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, bool* ok) {
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);
*ok = false;
return;
}
if (function_name_validity == kFunctionNameIsStrictReserved) {
impl()->ReportMessageAt(function_name_loc,
MessageTemplate::kUnexpectedStrictReserved);
*ok = false;
return;
}
}
// Determine precedence of given token.
static int Precedence(Token::Value token, bool accept_IN) {
if (token == Token::IN && !accept_IN)
return 0; // 0 precedence will terminate binary expression parsing
return Token::Precedence(token);
}
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.
void ReportMessage(MessageTemplate::Template message) {
Scanner::Location source_location = scanner()->location();
impl()->ReportMessageAt(source_location, message,
static_cast<const char*>(nullptr), kSyntaxError);
}
template <typename T>
void ReportMessage(MessageTemplate::Template message, T arg,
ParseErrorType error_type = kSyntaxError) {
Scanner::Location source_location = scanner()->location();
impl()->ReportMessageAt(source_location, message, arg, error_type);
}
void ReportMessageAt(Scanner::Location location,
MessageTemplate::Template message,
ParseErrorType error_type) {
impl()->ReportMessageAt(location, message,
static_cast<const char*>(nullptr), error_type);
}
void GetUnexpectedTokenMessage(
Token::Value token, MessageTemplate::Template* message,
Scanner::Location* location, const char** arg,
MessageTemplate::Template default_ = MessageTemplate::kUnexpectedToken);
void ReportUnexpectedToken(Token::Value token);
void ReportUnexpectedTokenAt(
Scanner::Location location, Token::Value token,
MessageTemplate::Template message = MessageTemplate::kUnexpectedToken);
void ReportClassifierError(
const typename ExpressionClassifier::Error& error) {
impl()->ReportMessageAt(error.location, error.message, error.arg,
error.type);
}
void ValidateExpression(bool* ok) {
if (!classifier()->is_valid_expression()) {
ReportClassifierError(classifier()->expression_error());
*ok = false;
}
}
void ValidateFormalParameterInitializer(bool* ok) {
if (!classifier()->is_valid_formal_parameter_initializer()) {
ReportClassifierError(classifier()->formal_parameter_initializer_error());
*ok = false;
}
}
void ValidateBindingPattern(bool* ok) {
if (!classifier()->is_valid_binding_pattern()) {
ReportClassifierError(classifier()->binding_pattern_error());
*ok = false;
}
}
void ValidateAssignmentPattern(bool* ok) {
if (!classifier()->is_valid_assignment_pattern()) {
ReportClassifierError(classifier()->assignment_pattern_error());
*ok = false;
}
}
void ValidateFormalParameters(LanguageMode language_mode,
bool allow_duplicates, bool* ok) {
if (!allow_duplicates &&
!classifier()->is_valid_formal_parameter_list_without_duplicates()) {
ReportClassifierError(classifier()->duplicate_formal_parameter_error());
*ok = false;
} else if (is_strict(language_mode) &&
!classifier()->is_valid_strict_mode_formal_parameters()) {
ReportClassifierError(classifier()->strict_mode_formal_parameter_error());
*ok = false;
}
}
bool IsValidArrowFormalParametersStart(Token::Value token) {
return is_any_identifier(token) || token == Token::LPAREN;
}
void ValidateArrowFormalParameters(ExpressionT expr,
bool parenthesized_formals, bool is_async,
bool* ok) {
if (classifier()->is_valid_binding_pattern()) {
// A simple arrow formal parameter: IDENTIFIER => BODY.
if (!impl()->IsIdentifier(expr)) {
impl()->ReportMessageAt(scanner()->location(),
MessageTemplate::kUnexpectedToken,
Token::String(scanner()->current_token()));
*ok = false;
}
} else if (!classifier()->is_valid_arrow_formal_parameters()) {
// If after parsing the expr, we see an error but the expression is
// neither a valid binding pattern nor a valid parenthesized formal
// parameter list, show the "arrow formal parameters" error if the formals
// started with a parenthesis, and the binding pattern error otherwise.
const typename ExpressionClassifier::Error& error =
parenthesized_formals ? classifier()->arrow_formal_parameters_error()
: classifier()->binding_pattern_error();
ReportClassifierError(error);
*ok = false;
}
if (is_async && !classifier()->is_valid_async_arrow_formal_parameters()) {
const typename ExpressionClassifier::Error& error =
classifier()->async_arrow_formal_parameters_error();
ReportClassifierError(error);
*ok = false;
}
}
void ValidateLetPattern(bool* ok) {
if (!classifier()->is_valid_let_pattern()) {
ReportClassifierError(classifier()->let_pattern_error());
*ok = false;
}
}
void BindingPatternUnexpectedToken() {
MessageTemplate::Template message = MessageTemplate::kUnexpectedToken;
const char* arg;
Scanner::Location location = scanner()->peek_location();
GetUnexpectedTokenMessage(peek(), &message, &location, &arg);
classifier()->RecordBindingPatternError(location, message, arg);
}
void ArrowFormalParametersUnexpectedToken() {
MessageTemplate::Template message = MessageTemplate::kUnexpectedToken;
const char* arg;
Scanner::Location location = scanner()->peek_location();
GetUnexpectedTokenMessage(peek(), &message, &location, &arg);
classifier()->RecordArrowFormalParametersError(location, message, arg);
}
// Recursive descent functions.
// All ParseXXX functions take as the last argument an *ok parameter
// which is set to false if parsing failed; it is unchanged otherwise.
// By making the 'exception handling' explicit, we are forced to check
// for failure at the call sites. The family of CHECK_OK* macros can
// be useful for this.
// Parses an identifier that is valid for the current scope, in particular it
// fails on strict mode future reserved keywords in a strict scope. If
// allow_eval_or_arguments is kAllowEvalOrArguments, we allow "eval" or
// "arguments" as identifier even in strict mode (this is needed in cases like
// "var foo = eval;").
IdentifierT ParseIdentifier(AllowRestrictedIdentifiers, bool* ok);
IdentifierT ParseAndClassifyIdentifier(bool* ok);
// Parses an identifier or a strict mode future reserved word, and indicate
// whether it is strict mode future reserved. 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.
IdentifierT ParseIdentifierOrStrictReservedWord(FunctionKind function_kind,
bool* is_strict_reserved,
bool* is_await, bool* ok);
IdentifierT ParseIdentifierOrStrictReservedWord(bool* is_strict_reserved,
bool* is_await, bool* ok) {
return ParseIdentifierOrStrictReservedWord(
function_state_->kind(), is_strict_reserved, is_await, ok);
}
IdentifierT ParseIdentifierName(bool* ok);
ExpressionT ParseIdentifierNameOrPrivateName(bool* ok);
ExpressionT ParseRegExpLiteral(bool* ok);
ExpressionT ParsePrimaryExpression(bool* is_async, bool* ok);
ExpressionT ParsePrimaryExpression(bool* ok) {
bool is_async;
return ParsePrimaryExpression(&is_async, ok);
}
// Use when parsing an expression that is known to not be a pattern or part
// of a pattern.
V8_INLINE ExpressionT ParseExpression(bool accept_IN, bool* ok);
// This method does not wrap the parsing of the expression inside a
// new expression classifier; it uses the top-level classifier instead.
// It 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(bool accept_IN, bool* ok);
ExpressionT ParseArrayLiteral(bool* ok);
enum class PropertyKind {
kAccessorProperty,
kValueProperty,
kShorthandProperty,
kMethodProperty,
kClassField,
kSpreadProperty,
kNotSet
};
bool SetPropertyKindFromToken(Token::Value token, PropertyKind* kind);
ExpressionT ParsePropertyName(IdentifierT* name, PropertyKind* kind,
bool* is_generator, bool* is_get, bool* is_set,
bool* is_async, bool* is_computed_name,
bool* ok);
ExpressionT ParseObjectLiteral(bool* ok);
ClassLiteralPropertyT ParseClassPropertyDefinition(
ClassLiteralChecker* checker, ClassInfo* class_info,
IdentifierT* property_name, bool has_extends, bool* is_computed_name,
bool* has_seen_constructor, ClassLiteralProperty::Kind* property_kind,
bool* is_static, bool* has_name_static_property, bool* ok);
ExpressionT ParseClassFieldInitializer(ClassInfo* class_info, bool is_static,
bool* ok);
ObjectLiteralPropertyT ParseObjectPropertyDefinition(
ObjectLiteralChecker* checker, bool* is_computed_name,
bool* is_rest_property, bool* ok);
ExpressionListT ParseArguments(Scanner::Location* first_spread_pos,
bool maybe_arrow,
bool* is_simple_parameter_list, bool* ok);
ExpressionListT ParseArguments(Scanner::Location* first_spread_pos,
bool* ok) {
return ParseArguments(first_spread_pos, false, nullptr, ok);
}
ExpressionT ParseAssignmentExpression(bool accept_IN, bool* ok);
ExpressionT ParseYieldExpression(bool accept_IN, bool* ok);
ExpressionT ParseConditionalExpression(bool accept_IN, bool* ok);
ExpressionT ParseBinaryExpression(int prec, bool accept_IN, bool* ok);
ExpressionT ParseUnaryExpression(bool* ok);
ExpressionT ParsePostfixExpression(bool* ok);
ExpressionT ParseLeftHandSideExpression(bool* ok);
ExpressionT ParseMemberWithNewPrefixesExpression(bool* is_async, bool* ok);
ExpressionT ParseMemberExpression(bool* is_async, bool* ok);
ExpressionT ParseMemberExpressionContinuation(ExpressionT expression,
bool* is_async, bool* ok);
// `rewritable_length`: length of the destructuring_assignments_to_rewrite()
// queue in the parent function state, prior to parsing of formal parameters.
// If the arrow function is lazy, any items added during formal parameter
// parsing are removed from the queue.
ExpressionT ParseArrowFunctionLiteral(bool accept_IN,
const FormalParametersT& parameters,
int rewritable_length, bool* ok);
void ParseSingleExpressionFunctionBody(StatementListT body, bool is_async,
bool accept_IN, bool* ok);
void ParseAsyncFunctionBody(Scope* scope, StatementListT body, bool* ok);
ExpressionT ParseAsyncFunctionLiteral(bool* ok);
ExpressionT ParseClassLiteral(IdentifierT name,
Scanner::Location class_name_location,
bool name_is_strict_reserved,
int class_token_pos, bool* ok);
ExpressionT ParseTemplateLiteral(ExpressionT tag, int start, bool tagged,
bool* ok);
ExpressionT ParseSuperExpression(bool is_new, bool* ok);
ExpressionT ParseImportExpressions(bool* ok);
ExpressionT ParseNewTargetExpression(bool* ok);
void ParseFormalParameter(FormalParametersT* parameters, bool* ok);
void ParseFormalParameterList(FormalParametersT* parameters, bool* ok);
void CheckArityRestrictions(int param_count, FunctionKind function_type,
bool has_rest, int formals_start_pos,
int formals_end_pos, bool* ok);
BlockT ParseVariableDeclarations(VariableDeclarationContext var_context,
DeclarationParsingResult* parsing_result,
ZonePtrList<const AstRawString>* names,
bool* ok);
StatementT ParseAsyncFunctionDeclaration(
ZonePtrList<const AstRawString>* names, bool default_export, bool* ok);
StatementT ParseFunctionDeclaration(bool* ok);
StatementT ParseHoistableDeclaration(ZonePtrList<const AstRawString>* names,
bool default_export, bool* ok);
StatementT ParseHoistableDeclaration(int pos, ParseFunctionFlags flags,
ZonePtrList<const AstRawString>* names,
bool default_export, bool* ok);
StatementT ParseClassDeclaration(ZonePtrList<const AstRawString>* names,
bool default_export, bool* ok);
StatementT ParseNativeDeclaration(bool* ok);
// Consumes the ending }.
void ParseFunctionBody(StatementListT result, IdentifierT function_name,
int pos, const FormalParametersT& parameters,
FunctionKind kind,
FunctionLiteral::FunctionType function_type, bool* ok);
// Under some circumstances, we allow preparsing to abort if the preparsed
// function is "long and trivial", and fully parse instead. Our current
// definition of "long and trivial" is:
// - over kLazyParseTrialLimit statements
// - all starting with an identifier (i.e., no if, for, while, etc.)
static const int kLazyParseTrialLimit = 200;
// 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,
bool* ok) {
LazyParsingResult result = ParseStatementList(body, end_token, false, ok);
USE(result);
DCHECK_EQ(result, kLazyParsingComplete);
}
LazyParsingResult ParseStatementList(StatementListT body,
Token::Value end_token, bool may_abort,
bool* ok);
StatementT ParseStatementListItem(bool* ok);
StatementT ParseStatement(ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels,
bool* ok) {
return ParseStatement(labels, own_labels,
kDisallowLabelledFunctionStatement, ok);
}
StatementT ParseStatement(ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels,
AllowLabelledFunctionStatement allow_function,
bool* ok);
BlockT ParseBlock(ZonePtrList<const AstRawString>* labels, bool* ok);
// 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,
bool* ok);
StatementT ParseVariableStatement(VariableDeclarationContext var_context,
ZonePtrList<const AstRawString>* names,
bool* ok);
// Magical syntax support.
ExpressionT ParseV8Intrinsic(bool* ok);
ExpressionT ParseDoExpression(bool* ok);
StatementT ParseDebuggerStatement(bool* ok);
StatementT ParseExpressionOrLabelledStatement(
ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels,
AllowLabelledFunctionStatement allow_function, bool* ok);
StatementT ParseIfStatement(ZonePtrList<const AstRawString>* labels,
bool* ok);
StatementT ParseContinueStatement(bool* ok);
StatementT ParseBreakStatement(ZonePtrList<const AstRawString>* labels,
bool* ok);
StatementT ParseReturnStatement(bool* ok);
StatementT ParseWithStatement(ZonePtrList<const AstRawString>* labels,
bool* ok);
StatementT ParseDoWhileStatement(ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels,
bool* ok);
StatementT ParseWhileStatement(ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels,
bool* ok);
StatementT ParseThrowStatement(bool* ok);
StatementT ParseSwitchStatement(ZonePtrList<const AstRawString>* labels,
bool* ok);
StatementT ParseTryStatement(bool* ok);
StatementT ParseForStatement(ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels,
bool* ok);
StatementT ParseForEachStatementWithDeclarations(
int stmt_pos, ForInfo* for_info, ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels, Scope* inner_block_scope,
bool* ok);
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, bool* ok);
// 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, bool* ok);
// 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, bool* ok);
StatementT ParseForAwaitStatement(ZonePtrList<const AstRawString>* labels,
ZonePtrList<const AstRawString>* own_labels,
bool* ok);
bool IsNextLetKeyword();
bool IsTrivialExpression();
// 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 CheckAndRewriteReferenceExpression(
ExpressionT expression, int beg_pos, int end_pos,
MessageTemplate::Template message, bool* ok);
ExpressionT CheckAndRewriteReferenceExpression(
ExpressionT expression, int beg_pos, int end_pos,
MessageTemplate::Template message, ParseErrorType type, bool* ok);
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;
}
bool IsValidPattern(ExpressionT expression) {
return expression->IsObjectLiteral() || expression->IsArrayLiteral();
}
// Due to hoisting, the value of a 'var'-declared variable may actually change
// even if the code contains only the "initial" assignment, namely when that
// assignment occurs inside a loop. For example:
//
// let i = 10;
// do { var x = i } while (i--):
//
// As a simple and very conservative approximation of this, we explicitly mark
// as maybe-assigned any non-lexical variable whose initializing "declaration"
// does not syntactically occur in the function scope. (In the example above,
// it occurs in a block scope.)
//
// Note that non-lexical variables include temporaries, which may also get
// assigned inside a loop due to the various rewritings that the parser
// performs.
//
// This also handles marking of loop variables in for-in and for-of loops,
// as determined by declaration_kind.
//
static void MarkLoopVariableAsAssigned(
Scope* scope, Variable* var,
typename DeclarationDescriptor::Kind declaration_kind);
FunctionKind FunctionKindForImpl(bool is_method, bool is_generator,
bool is_async) {
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][is_generator][is_async];
}
inline FunctionKind FunctionKindFor(bool is_generator, bool is_async) {
const bool kIsMethod = false;
return FunctionKindForImpl(kIsMethod, is_generator, is_async);
}
inline FunctionKind MethodKindFor(bool is_generator, bool is_async) {
const bool kIsMethod = true;
return FunctionKindForImpl(kIsMethod, is_generator, is_async);
}
// 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))) {
scope->RecordInnerScopeEvalCall();
function_state_->RecordFunctionOrEvalCall();
if (is_sloppy(scope->language_mode())) {
// For sloppy scopes we also have to record the call at function level,
// in case it includes declarations that will be hoisted.
scope->GetDeclarationScope()->RecordEvalCall();
}
// This call is only necessary to track evals that may be
// inside arrow function parameter lists. In that case,
// Scope::Snapshot::Reparent will move this bit down into
// the arrow function's scope.
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);
}
// Validation per ES6 object literals.
class ObjectLiteralChecker {
public:
explicit ObjectLiteralChecker(ParserBase* parser)
: parser_(parser), has_seen_proto_(false) {}
void CheckDuplicateProto(Token::Value property);
private:
bool IsProto() const {
return this->scanner()->CurrentMatchesContextualEscaped(
Token::PROTO_UNDERSCORED);
}
ParserBase* parser() const { return parser_; }
Scanner* scanner() const { return parser_->scanner(); }
ParserBase* parser_;
bool has_seen_proto_;
};
// Validation per ES6 class literals.
class ClassLiteralChecker {
public:
explicit ClassLiteralChecker(ParserBase* parser)
: parser_(parser), has_seen_constructor_(false) {}
void CheckClassMethodName(Token::Value property, PropertyKind type,
bool is_generator, bool is_async, bool is_static,
bool* ok);
void CheckClassFieldName(bool is_static, bool* ok);
private:
bool IsConstructor() {
return this->scanner()->CurrentMatchesContextualEscaped(
Token::CONSTRUCTOR);
}
bool IsPrivateConstructor() {
return this->scanner()->CurrentMatchesContextualEscaped(
Token::PRIVATE_CONSTRUCTOR);
}
bool IsPrototype() {
return this->scanner()->CurrentMatchesContextualEscaped(Token::PROTOTYPE);
}
ParserBase* parser() const { return parser_; }
Scanner* scanner() const { return parser_->scanner(); }
ParserBase* parser_;
bool has_seen_constructor_;
};
ModuleDescriptor* module() const {
return scope()->AsModuleScope()->module();
}
Scope* scope() const { return scope_; }
// Stack of expression classifiers.
// The top of the stack is always pointed to by classifier().
V8_INLINE ExpressionClassifier* classifier() const {
DCHECK_NOT_NULL(classifier_);
return classifier_;
}
// Accumulates the classifier that is on top of the stack (inner) to
// the one that is right below (outer) and pops the inner.
V8_INLINE void Accumulate(unsigned productions) {
DCHECK_NOT_NULL(classifier_);
ExpressionClassifier* previous = classifier_->previous();
DCHECK_NOT_NULL(previous);
previous->Accumulate(classifier_, productions);
classifier_ = previous;
}
V8_INLINE void AccumulateNonBindingPatternErrors() {
this->Accumulate(ExpressionClassifier::AllProductions &
~(ExpressionClassifier::BindingPatternProduction |
ExpressionClassifier::LetPatternProduction));
}
// Pops and discards the classifier that is on top of the stack
// without accumulating.
V8_INLINE void DiscardExpressionClassifier() {
DCHECK_NOT_NULL(classifier_);
classifier_->Discard();
classifier_ = classifier_->previous();
}
// Accumulate errors that can be arbitrarily deep in an expression.
// These correspond to the ECMAScript spec's 'Contains' operation
// on productions. This includes:
//
// - YieldExpression is disallowed in arrow parameters in a generator.
// - AwaitExpression is disallowed in arrow parameters in an async function.
// - AwaitExpression is disallowed in async arrow parameters.
//
V8_INLINE void AccumulateFormalParameterContainmentErrors() {
Accumulate(ExpressionClassifier::FormalParameterInitializerProduction |
ExpressionClassifier::AsyncArrowFormalParametersProduction);
}
// 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_;
ExpressionClassifier* classifier_;
Scanner* scanner_;
FunctionLiteral::EagerCompileHint default_eager_compile_hint_;
int function_literal_id_;
int script_id_;
bool allow_natives_;
bool allow_harmony_do_expressions_;
bool allow_harmony_public_fields_;
bool allow_harmony_static_fields_;
bool allow_harmony_dynamic_import_;
bool allow_harmony_import_meta_;
bool allow_harmony_private_fields_;
bool allow_eval_cache_;
friend class DiscardableZoneScope;
};
template <typename Impl>
ParserBase<Impl>::FunctionState::FunctionState(
FunctionState** function_state_stack, Scope** scope_stack,
DeclarationScope* scope)
: BlockState(scope_stack, scope),
expected_property_count_(0),
function_state_stack_(function_state_stack),
outer_function_state_(*function_state_stack),
scope_(scope),
destructuring_assignments_to_rewrite_(scope->zone()),
reported_errors_(16, scope->zone()),
dont_optimize_reason_(BailoutReason::kNoReason),
suspend_count_(0),
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>::GetUnexpectedTokenMessage(
Token::Value token, MessageTemplate::Template* message,
Scanner::Location* location, const char** arg,
MessageTemplate::Template default_) {
*arg = nullptr;
switch (token) {
case Token::EOS:
*message = MessageTemplate::kUnexpectedEOS;
break;
case Token::SMI:
case Token::NUMBER:
case Token::BIGINT:
*message = MessageTemplate::kUnexpectedTokenNumber;
break;
case Token::STRING:
*message = MessageTemplate::kUnexpectedTokenString;
break;
case Token::PRIVATE_NAME:
case Token::IDENTIFIER:
*message = MessageTemplate::kUnexpectedTokenIdentifier;
break;
case Token::AWAIT:
case Token::ENUM:
*message = MessageTemplate::kUnexpectedReserved;
break;
case Token::LET:
case Token::STATIC:
case Token::YIELD:
case Token::FUTURE_STRICT_RESERVED_WORD:
*message = is_strict(language_mode())
? MessageTemplate::kUnexpectedStrictReserved
: MessageTemplate::kUnexpectedTokenIdentifier;
break;
case Token::TEMPLATE_SPAN:
case Token::TEMPLATE_TAIL:
*message = MessageTemplate::kUnexpectedTemplateString;
break;
case Token::ESCAPED_STRICT_RESERVED_WORD:
case Token::ESCAPED_KEYWORD:
*message = MessageTemplate::kInvalidEscapedReservedWord;
break;
case Token::ILLEGAL:
if (scanner()->has_error()) {
*message = scanner()->error();
*location = scanner()->error_location();
} else {
*message = MessageTemplate::kInvalidOrUnexpectedToken;
}
break;
case Token::REGEXP_LITERAL:
*message = MessageTemplate::kUnexpectedTokenRegExp;
break;
default:
const char* name = Token::String(token);
DCHECK_NOT_NULL(name);
*arg = name;
break;
}
}
template <typename Impl>
void ParserBase<Impl>::ReportUnexpectedToken(Token::Value token) {
return ReportUnexpectedTokenAt(scanner_->location(), token);
}
template <typename Impl>
void ParserBase<Impl>::ReportUnexpectedTokenAt(
Scanner::Location source_location, Token::Value token,
MessageTemplate::Template message) {
const char* arg;
GetUnexpectedTokenMessage(token, &message, &source_location, &arg);
impl()->ReportMessageAt(source_location, message, arg);
}
template <typename Impl>
typename ParserBase<Impl>::IdentifierT ParserBase<Impl>::ParseIdentifier(
AllowRestrictedIdentifiers allow_restricted_identifiers, bool* ok) {
ExpressionClassifier classifier(this);
auto result = ParseAndClassifyIdentifier(CHECK_OK_CUSTOM(NullIdentifier));
if (allow_restricted_identifiers == kDontAllowRestrictedIdentifiers) {
ValidateAssignmentPattern(CHECK_OK_CUSTOM(NullIdentifier));
ValidateBindingPattern(CHECK_OK_CUSTOM(NullIdentifier));
}
return result;
}
template <typename Impl>
typename ParserBase<Impl>::IdentifierT
ParserBase<Impl>::ParseAndClassifyIdentifier(bool* ok) {
Token::Value next = Next();
if (next == Token::IDENTIFIER || next == Token::ASYNC ||
(next == Token::AWAIT && !parsing_module_ && !is_async_function())) {
IdentifierT name = impl()->GetSymbol();
if (impl()->IsArguments(name) && scope()->ShouldBanArguments()) {
ReportMessage(MessageTemplate::kArgumentsDisallowedInInitializer);
*ok = false;
return impl()->NullIdentifier();
}
// When this function is used to read a formal parameter, we don't always
// know whether the function is going to be strict or sloppy. Indeed for
// arrow functions we don't always know that the identifier we are reading
// is actually a formal parameter. Therefore besides the errors that we
// must detect because we know we're in strict mode, we also record any
// error that we might make in the future once we know the language mode.
if (impl()->IsEvalOrArguments(name)) {
classifier()->RecordStrictModeFormalParameterError(
scanner()->location(), MessageTemplate::kStrictEvalArguments);
if (is_strict(language_mode())) {
classifier()->RecordBindingPatternError(
scanner()->location(), MessageTemplate::kStrictEvalArguments);
}
} else if (next == Token::AWAIT) {
classifier()->RecordAsyncArrowFormalParametersError(
scanner()->location(), MessageTemplate::kAwaitBindingIdentifier);
}
if (classifier()->duplicate_finder() != nullptr &&
scanner()->IsDuplicateSymbol(classifier()->duplicate_finder(),
ast_value_factory())) {
classifier()->RecordDuplicateFormalParameterError(scanner()->location());
}
return name;
} else if (is_sloppy(language_mode()) &&
(next == Token::FUTURE_STRICT_RESERVED_WORD ||
next == Token::ESCAPED_STRICT_RESERVED_WORD ||
next == Token::LET || next == Token::STATIC ||
(next == Token::YIELD && !is_generator()))) {
classifier()->RecordStrictModeFormalParameterError(
scanner()->location(), MessageTemplate::kUnexpectedStrictReserved);
if (next == Token::ESCAPED_STRICT_RESERVED_WORD &&
is_strict(language_mode())) {
ReportUnexpectedToken(next);
*ok = false;
return impl()->NullIdentifier();
}
if (scanner()->IsLet()) {
classifier()->RecordLetPatternError(
scanner()->location(), MessageTemplate::kLetInLexicalBinding);
}
return impl()->GetSymbol();
} else {
ReportUnexpectedToken(next);
*ok = false;
return impl()->NullIdentifier();
}
}
template <class Impl>
typename ParserBase<Impl>::IdentifierT
ParserBase<Impl>::ParseIdentifierOrStrictReservedWord(
FunctionKind function_kind, bool* is_strict_reserved, bool* is_await,
bool* ok) {
Token::Value next = Next();
if (next == Token::IDENTIFIER || (next == Token::AWAIT && !parsing_module_ &&
!IsAsyncFunction(function_kind)) ||
next == Token::ASYNC) {
*is_strict_reserved = false;
*is_await = next == Token::AWAIT;
} else if (next == Token::ESCAPED_STRICT_RESERVED_WORD ||
next == Token::FUTURE_STRICT_RESERVED_WORD || next == Token::LET ||
next == Token::STATIC ||
(next == Token::YIELD && !IsGeneratorFunction(function_kind))) {
*is_strict_reserved = true;
} else {
ReportUnexpectedToken(next);
*ok = false;
return impl()->NullIdentifier();
}
return impl()->GetSymbol();
}
template <typename Impl>
typename ParserBase<Impl>::IdentifierT ParserBase<Impl>::ParseIdentifierName(
bool* ok) {
Token::Value next = Next();
if (next != Token::IDENTIFIER && next != Token::ASYNC &&
next != Token::ENUM && next != Token::AWAIT && next != Token::LET &&
next != Token::STATIC && next != Token::YIELD &&
next != Token::FUTURE_STRICT_RESERVED_WORD &&
next != Token::ESCAPED_KEYWORD &&
next != Token::ESCAPED_STRICT_RESERVED_WORD && !Token::IsKeyword(next)) {
ReportUnexpectedToken(next);
*ok = false;
return impl()->NullIdentifier();
}
return impl()->GetSymbol();
}
template <typename Impl>
typename ParserBase<Impl>::ExpressionT
ParserBase<Impl>::ParseIdentifierNameOrPrivateName(bool* ok) {
int pos = position();
IdentifierT name;
ExpressionT key;
if (allow_harmony_private_fields() && peek() == Token::PRIVATE_NAME) {
Consume(Token::PRIVATE_NAME);
name = impl()->GetSymbol();
auto key_proxy =
impl()->ExpressionFromIdentifier(name, pos, InferName::kNo);
key_proxy->set_is_private_field();
key = key_proxy;
} else {
name = ParseIdentifierName(CHECK_OK);
key = factory()->NewStringLiteral(name, pos);
}
impl()->PushLiteralName(name);
return key;
}
template <typename Impl>
typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseRegExpLiteral(
bool* ok) {
int pos = peek_position();
if (!scanner()->ScanRegExpPattern()) {
Next();
ReportMessage(MessageTemplate::kUnterminatedRegExp);
*ok = false;
return impl()->NullExpression();
}
IdentifierT js_pattern = impl()->GetNextSymbol();
Maybe<RegExp::Flags> flags = scanner()->ScanRegExpFlags();
if (flags.IsNothing()) {
Next();
ReportMessage(MessageTemplate::kMalformedRegExpFlags);
*ok = false;
return impl()->NullExpression();
}
int js_flags = flags.FromJust();
Next();
return factory()->NewRegExpLiteral(js_pattern, js_flags, pos);
}
template <typename Impl>
typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParsePrimaryExpression(
bool* is_async, bool* ok) {
// PrimaryExpression ::
// 'this'
// 'null'
// 'true'
// 'false'
// Identifier
// Number
// String
// ArrayLiteral
// ObjectLiteral
// RegExpLiteral
// ClassLiteral
// '(' Expression ')'
// TemplateLiteral
// do Block
// AsyncFunctionLiteral
int beg_pos = peek_position();
switch (peek()) {
case Token::THIS: {
BindingPatternUnexpectedToken();
Consume(Token::THIS);
return impl()->ThisExpression(beg_pos);
}
case Token::NULL_LITERAL:
case Token::TRUE_LITERAL:
case Token::FALSE_LITERAL:
case Token::SMI:
case Token::NUMBER:
case Token::BIGINT:
BindingPatternUnexpectedToken();
return impl()->ExpressionFromLiteral(Next(), beg_pos);
case Token::ASYNC:
if (!scanner()->HasAnyLineTerminatorAfterNext() &&
PeekAhead() == Token::FUNCTION) {
BindingPatternUnexpectedToken();
Consume(Token::ASYNC);
return ParseAsyncFunctionLiteral(CHECK_OK);
}
// CoverCallExpressionAndAsyncArrowHead
*is_async = true;
V8_FALLTHROUGH;
case Token::IDENTIFIER:
case Token::LET:
case Token::STATIC:
case Token::YIELD:
case Token::AWAIT:
case Token::ESCAPED_STRICT_RESERVED_WORD:
case Token::FUTURE_STRICT_RESERVED_WORD: {
// Using eval or arguments in this context is OK even in strict mode.
IdentifierT name = ParseAndClassifyIdentifier(CHECK_OK);
return impl()->ExpressionFromIdentifier(name, beg_pos);
}
case Token::STRING: {
BindingPatternUnexpectedToken();
Consume(Token::STRING);
return impl()->ExpressionFromString(beg_pos);
}
case Token::ASSIGN_DIV:
case Token::DIV:
classifier()->RecordBindingPatternError(
scanner()->peek_location(), MessageTemplate::kUnexpectedTokenRegExp);
return ParseRegExpLiteral(ok);
case Token::LBRACK:
return ParseArrayLiteral(ok);
case Token::LBRACE:
return ParseObjectLiteral(ok);
case Token::LPAREN: {
// Arrow function formal parameters are either a single identifier or a
// list of BindingPattern productions enclosed in parentheses.
// Parentheses are not valid on the LHS of a BindingPattern, so we use the
// is_valid_binding_pattern() check to detect multiple levels of
// parenthesization.
bool pattern_error = !classifier()->is_valid_binding_pattern();
classifier()->RecordPatternError(scanner()->peek_location(),
MessageTemplate::kUnexpectedToken,
Token::String(Token::LPAREN));
if (pattern_error) ArrowFormalParametersUnexpectedToken();
Consume(Token::LPAREN);
if (Check(Token::RPAREN)) {
// ()=>x. The continuation that looks for the => is in
// ParseAssignmentExpression.
classifier()->RecordExpressionError(scanner()->location(),
MessageTemplate::kUnexpectedToken,
Token::String(Token::RPAREN));
return factory()->NewEmptyParentheses(beg_pos);
}
// 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();
}
ExpressionT expr = ParseExpressionCoverGrammar(true, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK);
return expr;
}
case Token::CLASS: {
BindingPatternUnexpectedToken();
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()) {
bool is_await = false;
name = ParseIdentifierOrStrictReservedWord(&is_strict_reserved_name,
&is_await, CHECK_OK);
class_name_location = scanner()->location();
if (is_await) {
classifier()->RecordAsyncArrowFormalParametersError(
scanner()->location(), MessageTemplate::kAwaitBindingIdentifier);
}
}
return ParseClassLiteral(name, class_name_location,
is_strict_reserved_name, class_token_pos, ok);
}
case Token::TEMPLATE_SPAN:
case Token::TEMPLATE_TAIL:
BindingPatternUnexpectedToken();
return ParseTemplateLiteral(impl()->NullExpression(), beg_pos, false, ok);
case Token::MOD:
if (allow_natives() || extension_ != nullptr) {
BindingPatternUnexpectedToken();
return ParseV8Intrinsic(ok);
}
break;
case Token::DO:
if (allow_harmony_do_expressions()) {
BindingPatternUnexpectedToken();
return ParseDoExpression(ok);
}
break;
default:
break;
}
ReportUnexpectedToken(Next());
*ok = false;
return impl()->NullExpression();
}
template <typename Impl>
typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseExpression(
bool accept_IN, bool* ok) {
ExpressionClassifier classifier(this);
ExpressionT result = ParseExpressionCoverGrammar(accept_IN, CHECK_OK);
ValidateExpression(CHECK_OK);
return result;
}
template <typename Impl>
typename ParserBase<Impl>::ExpressionT
ParserBase<Impl>::ParseExpressionCoverGrammar(bool accept_IN, bool* ok) {
// Expression ::
// AssignmentExpression
// Expression ',' AssignmentExpression
ExpressionT result = impl()->NullExpression();
while (true) {
int comma_pos = position();
ExpressionClassifier binding_classifier(this);
ExpressionT right;
if (Check(Token::ELLIPSIS)) {
// 'x, y, ...z' in CoverParenthesizedExpressionAndArrowParameterList only
// as the formal parameters of'(x, y, ...z) => foo', and is not itself a
// valid expression.
classifier()->RecordExpressionError(scanner()->location(),
MessageTemplate::kUnexpectedToken,
Token::String(Token::ELLIPSIS));
int ellipsis_pos = position();
int pattern_pos = peek_position();
ExpressionT pattern = ParsePrimaryExpression(CHECK_OK);
if (peek() == Token::ASSIGN) {
ReportMessage(MessageTemplate::kRestDefaultInitializer);
*ok = false;
return result;
}
ValidateBindingPattern(CHECK_OK);
right = factory()->NewSpread(pattern, ellipsis_pos, pattern_pos);
} else {
right = ParseAssignmentExpression(accept_IN, CHECK_OK);
}
// No need to accumulate binding pattern-related errors, since
// an Expression can't be a binding pattern anyway.
AccumulateNonBindingPatternErrors();
if (!impl()->IsIdentifier(right)) classifier()->RecordNonSimpleParameter();
if (impl()->IsNull(result)) {
// First time through the loop.
result = right;
} else if (impl()->CollapseNaryExpression(&result, right, Token::COMMA,
comma_pos,
SourceRange::Empty())) {
// Do nothing, "result" is already updated.
} else {
result =
factory()->NewBinaryOperation(Token::COMMA, result, right, comma_pos);
}
if (!Check(Token::COMMA)) break;
if (right->IsSpread()) {
classifier()->RecordArrowFormalParametersError(
scanner()->location(), MessageTemplate::kParamAfterRest);
}
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 result;
}
template <typename Impl>
typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseArrayLiteral(
bool* ok) {
// ArrayLiteral ::
// '[' Expression? (',' Expression?)* ']'
int pos = peek_position();
ExpressionListT values = impl()->NewExpressionList(4);
int first_spread_index = -1;
Expect(Token::LBRACK, CHECK_OK);
while (peek() != Token::RBRACK) {
ExpressionT elem;
if (peek() == Token::COMMA) {
elem = factory()->NewTheHoleLiteral();
} else if (peek() == Token::ELLIPSIS) {
int start_pos = peek_position();
Consume(Token::ELLIPSIS);
int expr_pos = peek_position();
ExpressionT argument = ParseAssignmentExpression(true, CHECK_OK);
elem = factory()->NewSpread(argument, start_pos, expr_pos);
if (first_spread_index < 0) {
first_spread_index = values->length();
}
if (argument->IsAssignment()) {
classifier()->RecordPatternError(
Scanner::Location(start_pos, scanner()->location().end_pos),
MessageTemplate::kInvalidDestructuringTarget);
} else {
CheckDestructuringElement(argument, start_pos,
scanner()->location().end_pos);
}
if (peek() == Token::COMMA) {
classifier()->RecordPatternError(
Scanner::Location(start_pos, scanner()->location().end_pos),
MessageTemplate::kElementAfterRest);
}
} else {
int beg_pos = peek_position();
elem = ParseAssignmentExpression(true, CHECK_OK);
CheckDestructuringElement(elem, beg_pos, scanner()->location().end_pos);
}
values->Add(elem, zone_);
if (peek() != Token::RBRACK) {
Expect(Token::COMMA, CHECK_OK);
}
}
Expect(Token::RBRACK, CHECK_OK);
return factory()->NewArrayLiteral(values, first_spread_index, pos);
}
template <class Impl>
bool ParserBase<Impl>::SetPropertyKindFromToken(Token::Value token,
PropertyKind* kind) {
// 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 = PropertyKind::kValueProperty;
return true;
case Token::COMMA:
case Token::RBRACE:
case Token::ASSIGN:
*kind = PropertyKind::kShorthandProperty;
return true;
case Token::LPAREN:
*kind = PropertyKind::kMethodProperty;
return true;
case Token::MUL:
case Token::SEMICOLON:
*kind = PropertyKind::kClassField;
return true;
case Token::PRIVATE_NAME:
*kind = PropertyKind::kClassField;
return true;
default:
break;
}
return false;
}
template <class Impl>
typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParsePropertyName(
IdentifierT* name, PropertyKind* kind, bool* is_generator, bool* is_get,
bool* is_set, bool* is_async, bool* is_computed_name, bool* ok) {
DCHECK_EQ(*kind, PropertyKind::kNotSet);
DCHECK(!*is_generator);
DCHECK(!*is_get);
DCHECK(!*is_set);
DCHECK(!*is_async);
DCHECK(!*is_computed_name);
*is_generator = Check(Token::MUL);
if (*is_generator) {
*kind = PropertyKind::kMethodProperty;
}
Token::Value token = peek();
int pos = peek_position();
if (!*is_generator && token == Token::ASYNC &&
!scanner()->HasAnyLineTerminatorAfterNext()) {
Consume(Token::ASYNC);
token = peek();
if (token == Token::MUL && !scanner()->HasAnyLineTerminatorBeforeNext()) {
Consume(Token::MUL);
token = peek();
*is_generator = true;
} else if (SetPropertyKindFromToken(token, kind)) {
*name = impl()->GetSymbol(); // TODO(bakkot) specialize on 'async'
impl()->PushLiteralName(*name);
return factory()->NewStringLiteral(*name, pos);
}
*kind = PropertyKind::kMethodProperty;
*is_async = true;
pos = peek_position();
}
if (token == Token::IDENTIFIER && !*is_generator && !*is_async) {
// This is checking for 'get' and 'set' in particular.
Consume(Token::IDENTIFIER);
token = peek();
if (SetPropertyKindFromToken(token, kind) ||
!scanner()->IsGetOrSet(is_get, is_set)) {
*name = impl()->GetSymbol();
impl()->PushLiteralName(*name);
return factory()->NewStringLiteral(*name, pos);
}
*kind = PropertyKind::kAccessorProperty;
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.
ExpressionT expression = impl()->NullExpression();
switch (token) {
case Token::STRING:
Consume(Token::STRING);
*name = impl()->GetSymbol();
break;
case Token::SMI:
Consume(Token::SMI);
*name = impl()->GetNumberAsSymbol();
break;
case Token::NUMBER:
Consume(Token::NUMBER);
*name = impl()->GetNumberAsSymbol();
break;
case Token::LBRACK: {
*name = impl()->NullIdentifier();
*is_computed_name = true;
Consume(Token::LBRACK);
ExpressionClassifier computed_name_classifier(this);
expression = ParseAssignmentExpression(true, CHECK_OK);
ValidateExpression(CHECK_OK);
AccumulateFormalParameterContainmentErrors();
Expect(Token::RBRACK, CHECK_OK);
break;
}
case Token::ELLIPSIS:
if (!*is_generator && !*is_async && !*is_get && !*is_set) {
*name = impl()->NullIdentifier();
Consume(Token::ELLIPSIS);
expression = ParseAssignmentExpression(true, CHECK_OK);
*kind = PropertyKind::kSpreadProperty;
if (!impl()->IsIdentifier(expression)) {
classifier()->RecordBindingPatternError(
scanner()->location(),
MessageTemplate::kInvalidRestBindingPattern);
}
if (!expression->IsValidReferenceExpression()) {
classifier()->RecordAssignmentPatternError(
scanner()->location(),
MessageTemplate::kInvalidRestAssignmentPattern);
}
if (peek() != Token::RBRACE) {
classifier()->RecordPatternError(scanner()->location(),
MessageTemplate::kElementAfterRest);
}
return expression;
}
V8_FALLTHROUGH;
default:
*name = ParseIdentifierName(CHECK_OK);
break;
}
if (*kind == PropertyKind::kNotSet) {
SetPropertyKindFromToken(peek(), kind);
}
if (*is_computed_name) {
return expression;
}
impl()->PushLiteralName(*name);
uint32_t index;
return impl()->IsArrayIndex(*name, &index)
? factory()->NewNumberLiteral(index, pos)
: factory()->NewStringLiteral(*name, pos);
}
template <typename Impl>
typename ParserBase<Impl>::ClassLiteralPropertyT
ParserBase<Impl>::ParseClassPropertyDefinition(
ClassLiteralChecker* checker, ClassInfo* class_info, IdentifierT* name,
bool has_extends, bool* is_computed_name, bool* has_seen_constructor,
ClassLiteralProperty::Kind* property_kind, bool* is_static,
bool* has_name_static_property, bool* ok) {
DCHECK_NOT_NULL(has_seen_constructor);
DCHECK_NOT_NULL(has_name_static_property);
bool is_get = false;
bool is_set = false;
bool is_generator = false;
bool is_async = false;
*is_static = false;
*property_kind = ClassLiteralProperty::METHOD;
PropertyKind kind = PropertyKind::kNotSet;
Token::Value name_token = peek();
DCHECK_IMPLIES(name_token == Token::PRIVATE_NAME,
allow_harmony_private_fields());
int name_token_position = scanner()->peek_location().beg_pos;
*name = impl()->NullIdentifier();
ExpressionT name_expression;
if (name_token == Token::STATIC) {
Consume(Token::STATIC);
name_token_position = scanner()->peek_location().beg_pos;
if (peek() == Token::LPAREN) {
kind = PropertyKind::kMethodProperty;
*name = impl()->GetSymbol(); // TODO(bakkot) specialize on 'static'
name_expression = factory()->NewStringLiteral(*name, position());
} else if (peek() == Token::ASSIGN || peek() == Token::SEMICOLON ||
peek() == Token::RBRACE) {
*name = impl()->GetSymbol(); // TODO(bakkot) specialize on 'static'
name_expression = factory()->NewStringLiteral(*name, position());
} else if (peek() == Token::PRIVATE_NAME) {
DCHECK(allow_harmony_private_fields());
// TODO(gsathya): Make a better error message for this.
ReportUnexpectedToken(Next());
*ok = false;
return impl()->NullLiteralProperty();
} else {
*is_static = true;
name_expression = ParsePropertyName(name, &kind, &is_generator, &is_get,
&is_set, &is_async, is_computed_name,
CHECK_OK_CUSTOM(NullLiteralProperty));
}
} else if (name_token == Token::PRIVATE_NAME) {
Consume(Token::PRIVATE_NAME);
*name = impl()->GetSymbol();
name_expression = factory()->NewStringLiteral(*name, position());
} else {
name_expression = ParsePropertyName(name, &kind, &is_generator, &is_get,
&is_set, &is_async, is_computed_name,
CHECK_OK_CUSTOM(NullLiteralProperty));
}
if (!*has_name_static_property && *is_static && impl()->IsName(*name)) {
*has_name_static_property = true;
}
switch (kind) {
case PropertyKind::kClassField:
case PropertyKind::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.
case PropertyKind::kShorthandProperty:
case PropertyKind::kValueProperty:
if (allow_harmony_public_fields() || allow_harmony_private_fields()) {
*property_kind = name_token == Token::PRIVATE_NAME
? ClassLiteralProperty::PRIVATE_FIELD
: ClassLiteralProperty::PUBLIC_FIELD;
if (*is_static && !allow_harmony_static_fields()) {
ReportUnexpectedToken(Next());
*ok = false;
return impl()->NullLiteralProperty();
}
if (!*is_computed_name) {
checker->CheckClassFieldName(*is_static,
CHECK_OK_CUSTOM(NullLiteralProperty));
}
ExpressionT initializer = ParseClassFieldInitializer(
class_info, *is_static, CHECK_OK_CUSTOM(NullLiteralProperty));
ExpectSemicolon(CHECK_OK_CUSTOM(NullLiteralProperty));
ClassLiteralPropertyT result = factory()->NewClassLiteralProperty(
name_expression, initializer, *property_kind, *is_static,
*is_computed_name);
impl()->SetFunctionNameFromPropertyName(result, *name);
return result;
} else {
ReportUnexpectedToken(Next());
*ok = false;
return impl()->NullLiteralProperty();
}
case PropertyKind::kMethodProperty: {
DCHECK(!is_get && !is_set);
// MethodDefinition
// PropertyName '(' StrictFormalParameters ')' '{' FunctionBody '}'
// '*' PropertyName '(' StrictFormalParameters ')' '{' FunctionBody '}'
// async PropertyName '(' StrictFormalParameters ')'
// '{' FunctionBody '}'
// async '*' PropertyName '(' StrictFormalParameters ')'
// '{' FunctionBody '}'
if (!*is_computed_name) {
checker->CheckClassMethodName(name_token, PropertyKind::kMethodProperty,
is_generator, is_async, *is_static,
CHECK_OK_CUSTOM(NullLiteralProperty));
}
FunctionKind kind = MethodKindFor(is_generator, is_async);
if (!*is_static && impl()->IsConstructor(*name)) {
*has_seen_constructor = true;
kind = has_extends ? FunctionKind::kDerivedConstructor
: FunctionKind::kBaseConstructor;
}
ExpressionT value = impl()->ParseFunctionLiteral(
*name, scanner()->location(), kSkipFunctionNameCheck, kind,
FLAG_harmony_function_tostring ? name_token_position
: kNoSourcePosition,
FunctionLiteral::kAccessorOrMethod, language_mode(), nullptr,
CHECK_OK_CUSTOM(NullLiteralProperty));
*property_kind = ClassLiteralProperty::METHOD;
ClassLiteralPropertyT result = factory()->NewClassLiteralProperty(
name_expression, value, *property_kind, *is_static,
*is_computed_name);
impl()->SetFunctionNameFromPropertyName(result, *name);
return result;
}
case PropertyKind::kAccessorProperty: {
DCHECK((is_get || is_set) && !is_generator && !is_async);
if (!*is_computed_name) {
checker->CheckClassMethodName(
name_token, PropertyKind::kAccessorProperty, false, false,
*is_static, CHECK_OK_CUSTOM(NullLiteralProperty));
// 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,
FLAG_harmony_function_tostring ? name_token_position
: kNoSourcePosition,
FunctionLiteral::kAccessorOrMethod, language_mode(), nullptr,
CHECK_OK_CUSTOM(NullLiteralProperty));
*property_kind =
is_get ? ClassLiteralProperty::GETTER : ClassLiteralProperty::SETTER;
ClassLiteralPropertyT result = factory()->NewClassLiteralProperty(
name_expression, value, *property_kind, *is_static,
*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 PropertyKind::kSpreadProperty:
ReportUnexpectedTokenAt(
Scanner::Location(name_token_position, name_expression->position()),
name_token);
*ok = false;
return impl()->NullLiteralProperty();
}
UNREACHABLE();
}
template <typename Impl>
typename ParserBase<Impl>::ExpressionT
ParserBase<Impl>::ParseClassFieldInitializer(ClassInfo* class_info,
bool is_static, bool* ok) {
DeclarationScope* initializer_scope = is_static
? class_info->static_fields_scope
: class_info->instance_fields_scope;
if (initializer_scope == nullptr) {
initializer_scope =
NewFunctionScope(FunctionKind::kClassFieldsInitializerFunction);
// TODO(gsathya): Make scopes be non contiguous.
initializer_scope->set_start_position(scanner()->location().end_pos);
initializer_scope->SetLanguageMode(LanguageMode::kStrict);
}
ExpressionT initializer;
if (Check(Token::ASSIGN)) {
FunctionState initializer_state(&function_state_, &scope_,
initializer_scope);
ExpressionClassifier expression_classifier(this);
initializer =
ParseAssignmentExpression(true, CHECK_OK_CUSTOM(NullExpression));
ValidateExpression(CHECK_OK_CUSTOM(NullExpression));
} else {
initializer = factory()->NewUndefinedLiteral(kNoSourcePosition);
}
initializer_scope->set_end_position(scanner()->location().end_pos);
if (is_static) {
class_info->static_fields_scope = initializer_scope;
class_info->has_static_class_fields = true;
} else {
class_info->instance_fields_scope = initializer_scope;
class_info->has_instance_class_fields = true;
}
return initializer;
}
template <typename Impl>
typename ParserBase<Impl>::ObjectLiteralPropertyT
ParserBase<Impl>::ParseObjectPropertyDefinition(ObjectLiteralChecker* checker,
bool* is_computed_name,
bool* is_rest_property,
bool* ok) {
bool is_get = false;
bool is_set = false;
bool is_generator = false;
bool is_async = false;
PropertyKind kind = PropertyKind::kNotSet;
IdentifierT name = impl()->NullIdentifier();
Token::Value name_token = peek();
int next_beg_pos = scanner()->peek_location().beg_pos;
int next_end_pos = scanner()->peek_location().end_pos;
ExpressionT name_expression = ParsePropertyName(
&name, &kind, &is_generator, &is_get, &is_set, &is_async,
is_computed_name, CHECK_OK_CUSTOM(NullLiteralProperty));
switch (kind) {
case PropertyKind::kSpreadProperty:
DCHECK(!is_get && !is_set && !is_generator && !is_async &&
!*is_computed_name);
DCHECK(name_token == Token::ELLIPSIS);
*is_computed_name = true;
*is_rest_property = true;