| // 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_; | |
| } | |
| ZoneVector<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_; | |
| // How many suspends are needed for this function. | |
| int suspend_count_; | |
| FunctionState** function_state_stack_; | |
| FunctionState* outer_function_state_; | |
| DeclarationScope* scope_; | |
| ZoneChunkList<RewritableExpressionT> destructuring_assignments_to_rewrite_; | |
| // We use a ZoneVector here because we need to do a lot of random access. | |
| ZoneVector<typename ExpressionClassifier::Error> reported_errors_; | |
| // 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 { | |
| 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; | |
| 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()->HasLineTerminatorBeforeNext() || 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); | |
| } | |
| 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); | |
| } | |
| V8_INLINE 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, | |
| ClassLiteralProperty::Kind* property_kind, bool* is_static, 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); | |
| V8_INLINE ExpressionT ParseConditionalExpression(bool accept_IN, bool* ok); | |
| ExpressionT ParseBinaryExpression(int prec, bool accept_IN, bool* ok); | |
| ExpressionT ParseUnaryExpression(bool* ok); | |
| V8_INLINE ExpressionT ParsePostfixExpression(bool* ok); | |
| V8_INLINE ExpressionT ParseLeftHandSideExpression(bool* ok); | |
| ExpressionT ParseMemberWithNewPrefixesExpression(bool* is_async, bool* ok); | |
| V8_INLINE ExpressionT ParseMemberExpression(bool* is_async, bool* ok); | |
| V8_INLINE 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); | |
| V8_INLINE 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); | |
| } | |
| V8_INLINE 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); | |
| V8_INLINE 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), | |
| suspend_count_(0), | |
| function_state_stack_(function_state_stack), | |
| outer_function_state_(*function_state_stack), | |
| scope_(scope), | |
| destructuring_assignments_to_rewrite_(scope->zone()), | |
| reported_errors_(scope_->zone()), | |
| 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; | |
| reported_errors_.reserve(16); | |
| 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()->HasLineTerminatorAfterNext() && | |
| 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()->HasLineTerminatorAfterNext()) { | |
| Consume(Token::ASYNC); | |
| token = peek(); | |
| if (token == Token::MUL && !scanner()->HasLineTerminatorBeforeNext()) { | |
| 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, | |
| ClassLiteralProperty::Kind* property_kind, bool* is_static, bool* ok) { | |
| DCHECK_NOT_NULL(class_info); | |
| 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 (!class_info->has_name_static_property && *is_static && | |
| impl()->IsName(*name)) { | |
| class_info->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)) { | |
| class_info->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; | |
| return factory()->NewObjectLiteralProperty( | |
| factory()->NewTheHoleLiteral(), name_expression, | |
| ObjectLiteralProperty::SPREAD, true); | |
| case PropertyKind::kValueProperty: { | |
| DCHECK(!is_get && !is_set && !is_generator && !is_async); | |
| if (!*is_computed_name) { | |
| checker->CheckDuplicateProto(name_token); | |
| } | |
| Consume(Token::COLON); | |
| int beg_pos = peek_position(); | |
| ExpressionT value = | |
| ParseAssignmentExpression(true, CHECK_OK_CUSTOM(NullLiteralProperty)); | |
| CheckDestructuringElement(value, beg_pos, scanner()->location().end_pos); | |
| ObjectLiteralPropertyT result = factory()->NewObjectLiteralProperty( | |
| name_expression, value, *is_computed_name); | |
| impl()->SetFunctionNameFromPropertyName(result, name); | |
| return result; | |
| } | |
| case PropertyKind::kShorthandProperty: { | |
| // PropertyDefinition | |
| // IdentifierReference | |
| // CoverInitializedName | |
| // | |
| // CoverInitializedName | |
| // IdentifierReference Initializer? | |
| DCHECK(!is_get && !is_set && !is_generator && !is_async); | |
| if (!Token::IsIdentifier(name_token, language_mode(), | |
| this->is_generator(), | |
| parsing_module_ || is_async_function())) { | |
| ReportUnexpectedToken(Next()); | |
| *ok = false; | |
| return impl()->NullLiteralProperty(); | |
| } | |
| DCHECK(!*is_computed_name); | |
| if (classifier()->duplicate_finder() != nullptr && | |
| scanner()->IsDuplicateSymbol(classifier()->duplicate_finder(), | |
| ast_value_factory())) { | |
| classifier()->RecordDuplicateFormalParameterError( | |
| scanner()->location()); | |
| } | |
| if (impl()->IsEvalOrArguments(name) && is_strict(language_mode())) { | |
| classifier()->RecordBindingPatternError( | |
| scanner()->location(), MessageTemplate::kStrictEvalArguments); | |
| } | |
| if (name_token == Token::LET) { | |
| classifier()->RecordLetPatternError( | |
| scanner()->location(), MessageTemplate::kLetInLexicalBinding); | |
| } | |
| if (name_token == Token::AWAIT) { | |
| DCHECK(!is_async_function()); | |
| classifier()->RecordAsyncArrowFormalParametersError( | |
| Scanner::Location(next_beg_pos, next_end_pos), | |
| MessageTemplate::kAwaitBindingIdentifier); | |
| } | |
| ExpressionT lhs = impl()->ExpressionFromIdentifier(name, next_beg_pos); | |
| CheckDestructuringElement(lhs, next_beg_pos, next_end_pos); | |
| ExpressionT value; | |
| if (peek() == Token::ASSIGN) { | |
| Consume(Token::ASSIGN); | |
| ExpressionClassifier rhs_classifier(this); | |
| ExpressionT rhs = ParseAssignmentExpression( | |
| true, CHECK_OK_CUSTOM(NullLiteralProperty)); | |
| ValidateExpression(CHECK_OK_CUSTOM(NullLiteralProperty)); | |
| AccumulateFormalParameterContainmentErrors(); | |
| value = factory()->NewAssignment(Token::ASSIGN, lhs, rhs, | |
| kNoSourcePosition); | |
| classifier()->RecordExpressionError( | |
| Scanner::Location(next_beg_pos, scanner()->location().end_pos), | |
| MessageTemplate::kInvalidCoverInitializedName); | |
| impl()->SetFunctionNameFromIdentifierRef(rhs, lhs); | |
| } else { | |
| value = lhs; | |
| } | |
| ObjectLiteralPropertyT result = factory()->NewObjectLiteralProperty( | |
| name_expression, value, ObjectLiteralProperty::COMPUTED, false); | |
| impl()->SetFunctionNameFromPropertyName(result, name); | |
| return result; | |
| } | |
| case PropertyKind::kMethodProperty: { | |
| DCHECK(!is_get && !is_set); | |
| // MethodDefinition | |
| // PropertyName '(' StrictFormalParameters ')' '{' FunctionBody '}' | |
| // '*' PropertyName '(' StrictFormalParameters ')' '{' FunctionBody '}' | |
| classifier()->RecordPatternError( | |
| Scanner::Location(next_beg_pos, scanner()->location().end_pos), | |
| MessageTemplate::kInvalidDestructuringTarget); | |
| FunctionKind kind = MethodKindFor(is_generator, is_async); | |
| ExpressionT value = impl()->ParseFunctionLiteral( | |
| name, scanner()->location(), kSkipFunctionNameCheck, kind, | |
| FLAG_harmony_function_tostring ? next_beg_pos : kNoSourcePosition, | |
| FunctionLiteral::kAccessorOrMethod, language_mode(), nullptr, | |
| CHECK_OK_CUSTOM(NullLiteralProperty)); | |
| ObjectLiteralPropertyT result = factory()->NewObjectLiteralProperty( | |
| name_expression, value, ObjectLiteralProperty::COMPUTED, | |
| *is_computed_name); | |
| impl()->SetFunctionNameFromPropertyName(result, name); | |
| return result; | |
| } | |
| case PropertyKind::kAccessorProperty: { | |
| DCHECK((is_get || is_set) && !(is_set && is_get) && !is_generator && | |
| !is_async); | |
| classifier()->RecordPatternError( | |
| Scanner::Location(next_beg_pos, scanner()->location().end_pos), | |
| MessageTemplate::kInvalidDestructuringTarget); | |
| if (!*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, | |
| FLAG_harmony_function_tostring ? next_beg_pos : kNoSourcePosition, | |
| FunctionLiteral::kAccessorOrMethod, language_mode(), nullptr, | |
| CHECK_OK_CUSTOM(NullLiteralProperty)); | |
| ObjectLiteralPropertyT result = factory()->NewObjectLiteralProperty( | |
| name_expression, value, | |
| is_get ? ObjectLiteralProperty::GETTER | |
| : ObjectLiteralProperty::SETTER, | |
| *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::kClassField: | |
| case PropertyKind::kNotSet: | |
| ReportUnexpectedToken(Next()); | |
| *ok = false; | |
| return impl()->NullLiteralProperty(); | |
| } | |
| UNREACHABLE(); | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseObjectLiteral( | |
| bool* ok) { | |
| // ObjectLiteral :: | |
| // '{' (PropertyDefinition (',' PropertyDefinition)* ','? )? '}' | |
| int pos = peek_position(); | |
| typename Types::ObjectPropertyList properties = | |
| impl()->NewObjectPropertyList(4); | |
| int number_of_boilerplate_properties = 0; | |
| bool has_computed_names = false; | |
| bool has_rest_property = false; | |
| ObjectLiteralChecker checker(this); | |
| Expect(Token::LBRACE, CHECK_OK); | |
| while (peek() != Token::RBRACE) { | |
| FuncNameInferrer::State fni_state(fni_); | |
| bool is_computed_name = false; | |
| bool is_rest_property = false; | |
| ObjectLiteralPropertyT property = ParseObjectPropertyDefinition( | |
| &checker, &is_computed_name, &is_rest_property, CHECK_OK); | |
| if (is_computed_name) { | |
| has_computed_names = true; | |
| } | |
| if (is_rest_property) { | |
| 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, zone()); | |
| if (peek() != Token::RBRACE) { | |
| // Need {} because of the CHECK_OK macro. | |
| Expect(Token::COMMA, CHECK_OK); | |
| } | |
| if (fni_ != nullptr) fni_->Infer(); | |
| } | |
| Expect(Token::RBRACE, CHECK_OK); | |
| // 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) { | |
| this->classifier()->RecordPatternError(Scanner::Location(pos, position()), | |
| MessageTemplate::kTooManyArguments); | |
| } | |
| return impl()->InitializeObjectLiteral(factory()->NewObjectLiteral( | |
| properties, number_of_boilerplate_properties, pos, has_rest_property)); | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::ExpressionListT ParserBase<Impl>::ParseArguments( | |
| Scanner::Location* first_spread_arg_loc, bool maybe_arrow, | |
| bool* is_simple_parameter_list, bool* ok) { | |
| // Arguments :: | |
| // '(' (AssignmentExpression)*[','] ')' | |
| Scanner::Location spread_arg = Scanner::Location::invalid(); | |
| ExpressionListT result = impl()->NewExpressionList(4); | |
| Expect(Token::LPAREN, CHECK_OK_CUSTOM(NullExpressionList)); | |
| bool done = (peek() == Token::RPAREN); | |
| while (!done) { | |
| int start_pos = peek_position(); | |
| bool is_spread = Check(Token::ELLIPSIS); | |
| int expr_pos = peek_position(); | |
| ExpressionT argument = | |
| ParseAssignmentExpression(true, CHECK_OK_CUSTOM(NullExpressionList)); | |
| if (!impl()->IsIdentifier(argument) && | |
| is_simple_parameter_list != nullptr) { | |
| *is_simple_parameter_list = false; | |
| } | |
| if (!maybe_arrow) { | |
| ValidateExpression(CHECK_OK_CUSTOM(NullExpressionList)); | |
| } | |
| if (is_spread) { | |
| if (is_simple_parameter_list != nullptr) { | |
| *is_simple_parameter_list = false; | |
| } | |
| if (!spread_arg.IsValid()) { | |
| spread_arg.beg_pos = start_pos; | |
| spread_arg.end_pos = peek_position(); | |
| } | |
| if (argument->IsAssignment()) { | |
| classifier()->RecordAsyncArrowFormalParametersError( | |
| scanner()->location(), MessageTemplate::kRestDefaultInitializer); | |
| } | |
| argument = factory()->NewSpread(argument, start_pos, expr_pos); | |
| } | |
| result->Add(argument, zone_); | |
| if (result->length() > Code::kMaxArguments) { | |
| ReportMessage(MessageTemplate::kTooManyArguments); | |
| *ok = false; | |
| return impl()->NullExpressionList(); | |
| } | |
| done = (peek() != Token::COMMA); | |
| if (!done) { | |
| Next(); | |
| if (argument->IsSpread()) { | |
| classifier()->RecordAsyncArrowFormalParametersError( | |
| scanner()->location(), MessageTemplate::kParamAfterRest); | |
| } | |
| if (peek() == Token::RPAREN) { | |
| // allow trailing comma | |
| done = true; | |
| } | |
| } | |
| } | |
| Scanner::Location location = scanner_->location(); | |
| if (Token::RPAREN != Next()) { | |
| impl()->ReportMessageAt(location, MessageTemplate::kUnterminatedArgList); | |
| *ok = false; | |
| return impl()->NullExpressionList(); | |
| } | |
| *first_spread_arg_loc = spread_arg; | |
| if (!maybe_arrow || peek() != Token::ARROW) { | |
| if (maybe_arrow) { | |
| ValidateExpression(CHECK_OK_CUSTOM(NullExpressionList)); | |
| } | |
| } | |
| return result; | |
| } | |
| // Precedence = 2 | |
| template <typename Impl> | |
| typename ParserBase<Impl>::ExpressionT | |
| ParserBase<Impl>::ParseAssignmentExpression(bool accept_IN, bool* ok) { | |
| // AssignmentExpression :: | |
| // ConditionalExpression | |
| // ArrowFunction | |
| // YieldExpression | |
| // LeftHandSideExpression AssignmentOperator AssignmentExpression | |
| int lhs_beg_pos = peek_position(); | |
| if (peek() == Token::YIELD && is_generator()) { | |
| return ParseYieldExpression(accept_IN, ok); | |
| } | |
| FuncNameInferrer::State fni_state(fni_); | |
| ExpressionClassifier arrow_formals_classifier( | |
| this, classifier()->duplicate_finder()); | |
| Scope::Snapshot scope_snapshot(scope()); | |
| int rewritable_length = static_cast<int>( | |
| function_state_->destructuring_assignments_to_rewrite().size()); | |
| bool is_async = peek() == Token::ASYNC && | |
| !scanner()->HasLineTerminatorAfterNext() && | |
| IsValidArrowFormalParametersStart(PeekAhead()); | |
| bool parenthesized_formals = peek() == Token::LPAREN; | |
| if (!is_async && !parenthesized_formals) { | |
| ArrowFormalParametersUnexpectedToken(); | |
| } | |
| // Parse a simple, faster sub-grammar (primary expression) if it's evident | |
| // that we have only a trivial expression to parse. | |
| ExpressionT expression; | |
| if (IsTrivialExpression()) { | |
| expression = ParsePrimaryExpression(&is_async, CHECK_OK); | |
| } else { | |
| expression = ParseConditionalExpression(accept_IN, CHECK_OK); | |
| } | |
| if (is_async && impl()->IsIdentifier(expression) && peek_any_identifier() && | |
| PeekAhead() == Token::ARROW) { | |
| // async Identifier => AsyncConciseBody | |
| IdentifierT name = ParseAndClassifyIdentifier(CHECK_OK); | |
| expression = | |
| impl()->ExpressionFromIdentifier(name, position(), InferName::kNo); | |
| if (fni_) { | |
| // Remove `async` keyword from inferred name stack. | |
| fni_->RemoveAsyncKeywordFromEnd(); | |
| } | |
| } | |
| if (peek() == Token::ARROW) { | |
| Scanner::Location arrow_loc = scanner()->peek_location(); | |
| ValidateArrowFormalParameters(expression, parenthesized_formals, is_async, | |
| CHECK_OK); | |
| // This reads strangely, but is correct: it checks whether any | |
| // sub-expression of the parameter list failed to be a valid formal | |
| // parameter initializer. Since YieldExpressions are banned anywhere | |
| // in an arrow parameter list, this is correct. | |
| // TODO(adamk): Rename "FormalParameterInitializerError" to refer to | |
| // "YieldExpression", which is its only use. | |
| ValidateFormalParameterInitializer(ok); | |
| Scanner::Location loc(lhs_beg_pos, scanner()->location().end_pos); | |
| DeclarationScope* scope = | |
| NewFunctionScope(is_async ? FunctionKind::kAsyncArrowFunction | |
| : FunctionKind::kArrowFunction); | |
| // Because the arrow's parameters were parsed in the outer scope, | |
| // we need to fix up the scope chain appropriately. | |
| scope_snapshot.Reparent(scope); | |
| FormalParametersT parameters(scope); | |
| if (!classifier()->is_simple_parameter_list()) { | |
| scope->SetHasNonSimpleParameters(); | |
| parameters.is_simple = false; | |
| } | |
| scope->set_start_position(lhs_beg_pos); | |
| Scanner::Location duplicate_loc = Scanner::Location::invalid(); | |
| impl()->DeclareArrowFunctionFormalParameters(¶meters, expression, loc, | |
| &duplicate_loc, CHECK_OK); | |
| if (duplicate_loc.IsValid()) { | |
| classifier()->RecordDuplicateFormalParameterError(duplicate_loc); | |
| } | |
| expression = ParseArrowFunctionLiteral(accept_IN, parameters, | |
| rewritable_length, CHECK_OK); | |
| Accumulate(ExpressionClassifier::AsyncArrowFormalParametersProduction); | |
| classifier()->RecordPatternError(arrow_loc, | |
| MessageTemplate::kUnexpectedToken, | |
| Token::String(Token::ARROW)); | |
| if (fni_ != nullptr) fni_->Infer(); | |
| return expression; | |
| } | |
| // "expression" was not itself an arrow function parameter list, but it might | |
| // form part of one. Propagate speculative formal parameter error locations | |
| // (including those for binding patterns, since formal parameters can | |
| // themselves contain binding patterns). | |
| unsigned productions = ExpressionClassifier::AllProductions & | |
| ~ExpressionClassifier::ArrowFormalParametersProduction; | |
| // Parenthesized identifiers and property references are allowed as part | |
| // of a larger assignment pattern, even though parenthesized patterns | |
| // themselves are not allowed, e.g., "[(x)] = []". Only accumulate | |
| // assignment pattern errors if the parsed expression is more complex. | |
| if (IsValidReferenceExpression(expression)) { | |
| productions &= ~ExpressionClassifier::AssignmentPatternProduction; | |
| } | |
| const bool is_destructuring_assignment = | |
| IsValidPattern(expression) && peek() == Token::ASSIGN; | |
| if (is_destructuring_assignment) { | |
| // This is definitely not an expression so don't accumulate | |
| // expression-related errors. | |
| productions &= ~ExpressionClassifier::ExpressionProduction; | |
| } | |
| Accumulate(productions); | |
| if (!Token::IsAssignmentOp(peek())) return expression; | |
| if (is_destructuring_assignment) { | |
| ValidateAssignmentPattern(CHECK_OK); | |
| } else { | |
| expression = CheckAndRewriteReferenceExpression( | |
| expression, lhs_beg_pos, scanner()->location().end_pos, | |
| MessageTemplate::kInvalidLhsInAssignment, CHECK_OK); | |
| } | |
| impl()->MarkExpressionAsAssigned(expression); | |
| Token::Value op = Next(); // Get assignment operator. | |
| if (op != Token::ASSIGN) { | |
| classifier()->RecordPatternError(scanner()->location(), | |
| MessageTemplate::kUnexpectedToken, | |
| Token::String(op)); | |
| } | |
| int pos = position(); | |
| ExpressionClassifier rhs_classifier(this); | |
| ExpressionT right = ParseAssignmentExpression(accept_IN, CHECK_OK); | |
| ValidateExpression(CHECK_OK); | |
| AccumulateFormalParameterContainmentErrors(); | |
| // We try to estimate the set of properties set by constructors. We define a | |
| // new property whenever there is an assignment to a property of 'this'. We | |
| // should probably only add properties if we haven't seen them | |
| // before. Otherwise we'll probably overestimate the number of properties. | |
| if (op == Token::ASSIGN && impl()->IsThisProperty(expression)) { | |
| function_state_->AddProperty(); | |
| } | |
| impl()->CheckAssigningFunctionLiteralToProperty(expression, right); | |
| if (fni_ != nullptr) { | |
| // Check if the right hand side is a call to avoid inferring a | |
| // name if we're dealing with "a = function(){...}();"-like | |
| // expression. | |
| if (op == Token::ASSIGN && !right->IsCall() && !right->IsCallNew()) { | |
| fni_->Infer(); | |
| } else { | |
| fni_->RemoveLastFunction(); | |
| } | |
| } | |
| if (op == Token::ASSIGN) { | |
| impl()->SetFunctionNameFromIdentifierRef(right, expression); | |
| } | |
| DCHECK_NE(op, Token::INIT); | |
| ExpressionT result = factory()->NewAssignment(op, expression, right, pos); | |
| if (is_destructuring_assignment) { | |
| DCHECK_NE(op, Token::ASSIGN_EXP); | |
| auto rewritable = factory()->NewRewritableExpression(result, scope()); | |
| impl()->QueueDestructuringAssignmentForRewriting(rewritable); | |
| result = rewritable; | |
| } | |
| return result; | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseYieldExpression( | |
| bool accept_IN, bool* ok) { | |
| // YieldExpression :: | |
| // 'yield' ([no line terminator] '*'? AssignmentExpression)? | |
| int pos = peek_position(); | |
| classifier()->RecordPatternError( | |
| scanner()->peek_location(), MessageTemplate::kInvalidDestructuringTarget); | |
| classifier()->RecordFormalParameterInitializerError( | |
| scanner()->peek_location(), MessageTemplate::kYieldInParameter); | |
| Expect(Token::YIELD, CHECK_OK); | |
| // The following initialization is necessary. | |
| ExpressionT expression = impl()->NullExpression(); | |
| bool delegating = false; // yield* | |
| if (!scanner()->HasLineTerminatorBeforeNext()) { | |
| if (Check(Token::MUL)) delegating = true; | |
| switch (peek()) { | |
| case Token::EOS: | |
| case Token::SEMICOLON: | |
| case Token::RBRACE: | |
| case Token::RBRACK: | |
| case Token::RPAREN: | |
| case Token::COLON: | |
| case Token::COMMA: | |
| case Token::IN: | |
| // The above set of tokens is the complete set of tokens that can appear | |
| // after an AssignmentExpression, and none of them can start an | |
| // AssignmentExpression. This allows us to avoid looking for an RHS for | |
| // a regular yield, given only one look-ahead token. | |
| if (!delegating) break; | |
| // Delegating yields require an RHS; fall through. | |
| V8_FALLTHROUGH; | |
| default: | |
| expression = ParseAssignmentExpression(accept_IN, CHECK_OK); | |
| ValidateExpression(CHECK_OK); | |
| break; | |
| } | |
| } | |
| if (delegating) { | |
| ExpressionT yieldstar = factory()->NewYieldStar(expression, pos); | |
| impl()->RecordSuspendSourceRange(yieldstar, PositionAfterSemicolon()); | |
| function_state_->AddSuspend(); | |
| if (IsAsyncGeneratorFunction(function_state_->kind())) { | |
| // iterator_close and delegated_iterator_output suspend ids. | |
| function_state_->AddSuspend(); | |
| function_state_->AddSuspend(); | |
| } | |
| return yieldstar; | |
| } | |
| // Hackily disambiguate o from o.next and o [Symbol.iterator](). | |
| // TODO(verwaest): Come up with a better solution. | |
| ExpressionT yield = | |
| factory()->NewYield(expression, pos, Suspend::kOnExceptionThrow); | |
| impl()->RecordSuspendSourceRange(yield, PositionAfterSemicolon()); | |
| function_state_->AddSuspend(); | |
| return yield; | |
| } | |
| // Precedence = 3 | |
| template <typename Impl> | |
| typename ParserBase<Impl>::ExpressionT | |
| ParserBase<Impl>::ParseConditionalExpression(bool accept_IN, | |
| bool* ok) { | |
| // ConditionalExpression :: | |
| // LogicalOrExpression | |
| // LogicalOrExpression '?' AssignmentExpression ':' AssignmentExpression | |
| SourceRange then_range, else_range; | |
| int pos = peek_position(); | |
| // We start using the binary expression parser for prec >= 4 only! | |
| ExpressionT expression = ParseBinaryExpression(4, accept_IN, CHECK_OK); | |
| if (peek() != Token::CONDITIONAL) return expression; | |
| ValidateExpression(CHECK_OK); | |
| BindingPatternUnexpectedToken(); | |
| ArrowFormalParametersUnexpectedToken(); | |
| ExpressionT left; | |
| { | |
| SourceRangeScope range_scope(scanner(), &then_range); | |
| Consume(Token::CONDITIONAL); | |
| ExpressionClassifier classifier(this); | |
| // In parsing the first assignment expression in conditional | |
| // expressions we always accept the 'in' keyword; see ECMA-262, | |
| // section 11.12, page 58. | |
| left = ParseAssignmentExpression(true, CHECK_OK); | |
| AccumulateNonBindingPatternErrors(); | |
| } | |
| ValidateExpression(CHECK_OK); | |
| ExpressionT right; | |
| { | |
| SourceRangeScope range_scope(scanner(), &else_range); | |
| Expect(Token::COLON, CHECK_OK); | |
| ExpressionClassifier classifier(this); | |
| right = ParseAssignmentExpression(accept_IN, CHECK_OK); | |
| AccumulateNonBindingPatternErrors(); | |
| } | |
| ValidateExpression(CHECK_OK); | |
| ExpressionT expr = factory()->NewConditional(expression, left, right, pos); | |
| impl()->RecordConditionalSourceRange(expr, then_range, else_range); | |
| return expr; | |
| } | |
| // Precedence >= 4 | |
| template <typename Impl> | |
| typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseBinaryExpression( | |
| int prec, bool accept_IN, bool* ok) { | |
| DCHECK_GE(prec, 4); | |
| SourceRange right_range; | |
| ExpressionT x = ParseUnaryExpression(CHECK_OK); | |
| for (int prec1 = Precedence(peek(), accept_IN); prec1 >= prec; prec1--) { | |
| // prec1 >= 4 | |
| while (Precedence(peek(), accept_IN) == prec1) { | |
| ValidateExpression(CHECK_OK); | |
| BindingPatternUnexpectedToken(); | |
| ArrowFormalParametersUnexpectedToken(); | |
| SourceRangeScope right_range_scope(scanner(), &right_range); | |
| Token::Value op = Next(); | |
| int pos = position(); | |
| const bool is_right_associative = op == Token::EXP; | |
| const int next_prec = is_right_associative ? prec1 : prec1 + 1; | |
| ExpressionT y = ParseBinaryExpression(next_prec, accept_IN, CHECK_OK); | |
| right_range_scope.Finalize(); | |
| ValidateExpression(CHECK_OK); | |
| if (impl()->ShortcutNumericLiteralBinaryExpression(&x, y, op, pos)) { | |
| continue; | |
| } | |
| // For now we distinguish between comparisons and other binary | |
| // operations. (We could combine the two and get rid of this | |
| // code and AST node eventually.) | |
| if (Token::IsCompareOp(op)) { | |
| // We have a comparison. | |
| Token::Value cmp = op; | |
| switch (op) { | |
| case Token::NE: cmp = Token::EQ; break; | |
| case Token::NE_STRICT: cmp = Token::EQ_STRICT; break; | |
| default: break; | |
| } | |
| x = factory()->NewCompareOperation(cmp, x, y, pos); | |
| if (cmp != op) { | |
| // The comparison was negated - add a NOT. | |
| x = factory()->NewUnaryOperation(Token::NOT, x, pos); | |
| } | |
| } else if (impl()->CollapseNaryExpression(&x, y, op, pos, right_range)) { | |
| continue; | |
| } else { | |
| // We have a "normal" binary operation. | |
| x = factory()->NewBinaryOperation(op, x, y, pos); | |
| if (op == Token::OR || op == Token::AND) { | |
| impl()->RecordBinaryOperationSourceRange(x, right_range); | |
| } | |
| } | |
| } | |
| } | |
| return x; | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseUnaryExpression( | |
| bool* ok) { | |
| // UnaryExpression :: | |
| // PostfixExpression | |
| // 'delete' UnaryExpression | |
| // 'void' UnaryExpression | |
| // 'typeof' UnaryExpression | |
| // '++' UnaryExpression | |
| // '--' UnaryExpression | |
| // '+' UnaryExpression | |
| // '-' UnaryExpression | |
| // '~' UnaryExpression | |
| // '!' UnaryExpression | |
| // [+Await] AwaitExpression[?Yield] | |
| Token::Value op = peek(); | |
| if (Token::IsUnaryOp(op)) { | |
| BindingPatternUnexpectedToken(); | |
| ArrowFormalParametersUnexpectedToken(); | |
| op = Next(); | |
| int pos = position(); | |
| // Assume "! function ..." indicates the function is likely to be called. | |
| if (op == Token::NOT && peek() == Token::FUNCTION) { | |
| function_state_->set_next_function_is_likely_called(); | |
| } | |
| ExpressionT expression = ParseUnaryExpression(CHECK_OK); | |
| ValidateExpression(CHECK_OK); | |
| if (op == Token::DELETE) { | |
| if (impl()->IsIdentifier(expression) && is_strict(language_mode())) { | |
| // "delete identifier" is a syntax error in strict mode. | |
| ReportMessage(MessageTemplate::kStrictDelete); | |
| *ok = false; | |
| return impl()->NullExpression(); | |
| } | |
| if (impl()->IsPropertyWithPrivateFieldKey(expression)) { | |
| ReportMessage(MessageTemplate::kDeletePrivateField); | |
| *ok = false; | |
| return impl()->NullExpression(); | |
| } | |
| } | |
| if (peek() == Token::EXP) { | |
| ReportUnexpectedToken(Next()); | |
| *ok = false; | |
| return impl()->NullExpression(); | |
| } | |
| // Allow the parser's implementation to rewrite the expression. | |
| return impl()->BuildUnaryExpression(expression, op, pos); | |
| } else if (Token::IsCountOp(op)) { | |
| BindingPatternUnexpectedToken(); | |
| ArrowFormalParametersUnexpectedToken(); | |
| op = Next(); | |
| int beg_pos = peek_position(); | |
| ExpressionT expression = ParseUnaryExpression(CHECK_OK); | |
| expression = CheckAndRewriteReferenceExpression( | |
| expression, beg_pos, scanner()->location().end_pos, | |
| MessageTemplate::kInvalidLhsInPrefixOp, CHECK_OK); | |
| impl()->MarkExpressionAsAssigned(expression); | |
| ValidateExpression(CHECK_OK); | |
| return factory()->NewCountOperation(op, | |
| true /* prefix */, | |
| expression, | |
| position()); | |
| } else if (is_async_function() && peek() == Token::AWAIT) { | |
| classifier()->RecordFormalParameterInitializerError( | |
| scanner()->peek_location(), | |
| MessageTemplate::kAwaitExpressionFormalParameter); | |
| int await_pos = peek_position(); | |
| Consume(Token::AWAIT); | |
| ExpressionT value = ParseUnaryExpression(CHECK_OK); | |
| classifier()->RecordBindingPatternError( | |
| Scanner::Location(await_pos, scanner()->location().end_pos), | |
| MessageTemplate::kInvalidDestructuringTarget); | |
| ExpressionT expr = factory()->NewAwait(value, await_pos); | |
| function_state_->AddSuspend(); | |
| impl()->RecordSuspendSourceRange(expr, PositionAfterSemicolon()); | |
| return expr; | |
| } else { | |
| return ParsePostfixExpression(ok); | |
| } | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParsePostfixExpression( | |
| bool* ok) { | |
| // PostfixExpression :: | |
| // LeftHandSideExpression ('++' | '--')? | |
| int lhs_beg_pos = peek_position(); | |
| ExpressionT expression = ParseLeftHandSideExpression(CHECK_OK); | |
| if (!scanner()->HasLineTerminatorBeforeNext() && Token::IsCountOp(peek())) { | |
| BindingPatternUnexpectedToken(); | |
| ArrowFormalParametersUnexpectedToken(); | |
| expression = CheckAndRewriteReferenceExpression( | |
| expression, lhs_beg_pos, scanner()->location().end_pos, | |
| MessageTemplate::kInvalidLhsInPostfixOp, CHECK_OK); | |
| impl()->MarkExpressionAsAssigned(expression); | |
| ValidateExpression(CHECK_OK); | |
| Token::Value next = Next(); | |
| expression = | |
| factory()->NewCountOperation(next, | |
| false /* postfix */, | |
| expression, | |
| position()); | |
| } | |
| return expression; | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::ExpressionT | |
| ParserBase<Impl>::ParseLeftHandSideExpression(bool* ok) { | |
| // LeftHandSideExpression :: | |
| // (NewExpression | MemberExpression) ... | |
| bool is_async = false; | |
| ExpressionT result = | |
| ParseMemberWithNewPrefixesExpression(&is_async, CHECK_OK); | |
| while (true) { | |
| switch (peek()) { | |
| case Token::LBRACK: { | |
| ValidateExpression(CHECK_OK); | |
| BindingPatternUnexpectedToken(); | |
| ArrowFormalParametersUnexpectedToken(); | |
| Consume(Token::LBRACK); | |
| int pos = position(); | |
| ExpressionT index = ParseExpressionCoverGrammar(true, CHECK_OK); | |
| ValidateExpression(CHECK_OK); | |
| result = factory()->NewProperty(result, index, pos); | |
| Expect(Token::RBRACK, CHECK_OK); | |
| break; | |
| } | |
| case Token::LPAREN: { | |
| int pos; | |
| ValidateExpression(CHECK_OK); | |
| BindingPatternUnexpectedToken(); | |
| if (scanner()->current_token() == Token::IDENTIFIER || | |
| scanner()->current_token() == Token::SUPER || | |
| scanner()->current_token() == Token::ASYNC) { | |
| // For call of an identifier we want to report position of | |
| // the identifier as position of the call in the stack trace. | |
| pos = position(); | |
| } else { | |
| // For other kinds of calls we record position of the parenthesis as | |
| // position of the call. Note that this is extremely important for | |
| // expressions of the form function(){...}() for which call position | |
| // should not point to the closing brace otherwise it will intersect | |
| // with positions recorded for function literal and confuse debugger. | |
| pos = peek_position(); | |
| // Also the trailing parenthesis are a hint that the function will | |
| // be called immediately. If we happen to have parsed a preceding | |
| // function literal eagerly, we can also compile it eagerly. | |
| if (result->IsFunctionLiteral()) { | |
| result->AsFunctionLiteral()->SetShouldEagerCompile(); | |
| result->AsFunctionLiteral()->mark_as_iife(); | |
| } | |
| } | |
| Scanner::Location spread_pos; | |
| ExpressionListT args; | |
| if (V8_UNLIKELY(is_async && impl()->IsIdentifier(result))) { | |
| ExpressionClassifier async_classifier(this); | |
| bool is_simple_parameter_list = true; | |
| args = ParseArguments(&spread_pos, true, &is_simple_parameter_list, | |
| CHECK_OK); | |
| if (peek() == Token::ARROW) { | |
| if (fni_) { | |
| fni_->RemoveAsyncKeywordFromEnd(); | |
| } | |
| ValidateBindingPattern(CHECK_OK); | |
| ValidateFormalParameterInitializer(CHECK_OK); | |
| if (!classifier()->is_valid_async_arrow_formal_parameters()) { | |
| ReportClassifierError( | |
| classifier()->async_arrow_formal_parameters_error()); | |
| *ok = false; | |
| return impl()->NullExpression(); | |
| } | |
| if (args->length()) { | |
| // async ( Arguments ) => ... | |
| if (!is_simple_parameter_list) { | |
| async_classifier.previous()->RecordNonSimpleParameter(); | |
| } | |
| return impl()->ExpressionListToExpression(args); | |
| } | |
| // async () => ... | |
| return factory()->NewEmptyParentheses(pos); | |
| } else { | |
| AccumulateFormalParameterContainmentErrors(); | |
| } | |
| } else { | |
| args = ParseArguments(&spread_pos, CHECK_OK); | |
| } | |
| ArrowFormalParametersUnexpectedToken(); | |
| // Keep track of eval() calls since they disable all local variable | |
| // optimizations. | |
| // The calls that need special treatment are the | |
| // direct eval calls. These calls are all of the form eval(...), with | |
| // no explicit receiver. | |
| // These calls are marked as potentially direct eval calls. Whether | |
| // they are actually direct calls to eval is determined at run time. | |
| Call::PossiblyEval is_possibly_eval = | |
| CheckPossibleEvalCall(result, scope()); | |
| if (spread_pos.IsValid()) { | |
| result = impl()->SpreadCall(result, args, pos, is_possibly_eval); | |
| } else { | |
| result = factory()->NewCall(result, args, pos, is_possibly_eval); | |
| } | |
| if (fni_ != nullptr) fni_->RemoveLastFunction(); | |
| break; | |
| } | |
| case Token::PERIOD: { | |
| ValidateExpression(CHECK_OK); | |
| BindingPatternUnexpectedToken(); | |
| ArrowFormalParametersUnexpectedToken(); | |
| Consume(Token::PERIOD); | |
| int pos = position(); | |
| ExpressionT key = ParseIdentifierNameOrPrivateName(CHECK_OK); | |
| result = factory()->NewProperty(result, key, pos); | |
| break; | |
| } | |
| case Token::TEMPLATE_SPAN: | |
| case Token::TEMPLATE_TAIL: { | |
| ValidateExpression(CHECK_OK); | |
| BindingPatternUnexpectedToken(); | |
| ArrowFormalParametersUnexpectedToken(); | |
| result = ParseTemplateLiteral(result, position(), true, CHECK_OK); | |
| break; | |
| } | |
| default: | |
| return result; | |
| } | |
| } | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::ExpressionT | |
| ParserBase<Impl>::ParseMemberWithNewPrefixesExpression(bool* is_async, | |
| bool* ok) { | |
| // NewExpression :: | |
| // ('new')+ MemberExpression | |
| // | |
| // NewTarget :: | |
| // 'new' '.' 'target' | |
| // The grammar for new expressions is pretty warped. We can have several 'new' | |
| // keywords following each other, and then a MemberExpression. When we see '(' | |
| // after the MemberExpression, it's associated with the rightmost unassociated | |
| // 'new' to create a NewExpression with arguments. However, a NewExpression | |
| // can also occur without arguments. | |
| // Examples of new expression: | |
| // new foo.bar().baz means (new (foo.bar)()).baz | |
| // new foo()() means (new foo())() | |
| // new new foo()() means (new (new foo())()) | |
| // new new foo means new (new foo) | |
| // new new foo() means new (new foo()) | |
| // new new foo().bar().baz means (new (new foo()).bar()).baz | |
| if (peek() == Token::NEW) { | |
| BindingPatternUnexpectedToken(); | |
| ArrowFormalParametersUnexpectedToken(); | |
| Consume(Token::NEW); | |
| int new_pos = position(); | |
| ExpressionT result; | |
| if (peek() == Token::SUPER) { | |
| const bool is_new = true; | |
| result = ParseSuperExpression(is_new, CHECK_OK); | |
| } else if (allow_harmony_dynamic_import() && peek() == Token::IMPORT && | |
| (!allow_harmony_import_meta() || PeekAhead() == Token::LPAREN)) { | |
| impl()->ReportMessageAt(scanner()->peek_location(), | |
| MessageTemplate::kImportCallNotNewExpression); | |
| *ok = false; | |
| return impl()->NullExpression(); | |
| } else if (peek() == Token::PERIOD) { | |
| *is_async = false; | |
| result = ParseNewTargetExpression(CHECK_OK); | |
| return ParseMemberExpressionContinuation(result, is_async, CHECK_OK); | |
| } else { | |
| result = ParseMemberWithNewPrefixesExpression(is_async, CHECK_OK); | |
| } | |
| ValidateExpression(CHECK_OK); | |
| if (peek() == Token::LPAREN) { | |
| // NewExpression with arguments. | |
| Scanner::Location spread_pos; | |
| ExpressionListT args = ParseArguments(&spread_pos, CHECK_OK); | |
| if (spread_pos.IsValid()) { | |
| result = impl()->SpreadCallNew(result, args, new_pos); | |
| } else { | |
| result = factory()->NewCallNew(result, args, new_pos); | |
| } | |
| // The expression can still continue with . or [ after the arguments. | |
| result = ParseMemberExpressionContinuation(result, is_async, CHECK_OK); | |
| return result; | |
| } | |
| // NewExpression without arguments. | |
| return factory()->NewCallNew(result, impl()->NewExpressionList(0), new_pos); | |
| } | |
| // No 'new' or 'super' keyword. | |
| return ParseMemberExpression(is_async, ok); | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseMemberExpression( | |
| bool* is_async, bool* ok) { | |
| // MemberExpression :: | |
| // (PrimaryExpression | FunctionLiteral | ClassLiteral) | |
| // ('[' Expression ']' | '.' Identifier | Arguments | TemplateLiteral)* | |
| // | |
| // CallExpression :: | |
| // (SuperCall | ImportCall) | |
| // ('[' Expression ']' | '.' Identifier | Arguments | TemplateLiteral)* | |
| // | |
| // The '[' Expression ']' and '.' Identifier parts are parsed by | |
| // ParseMemberExpressionContinuation, and the Arguments part is parsed by the | |
| // caller. | |
| // Parse the initial primary or function expression. | |
| ExpressionT result; | |
| if (peek() == Token::FUNCTION) { | |
| BindingPatternUnexpectedToken(); | |
| ArrowFormalParametersUnexpectedToken(); | |
| Consume(Token::FUNCTION); | |
| int function_token_position = position(); | |
| FunctionKind function_kind = Check(Token::MUL) | |
| ? FunctionKind::kGeneratorFunction | |
| : FunctionKind::kNormalFunction; | |
| IdentifierT name = impl()->NullIdentifier(); | |
| bool is_strict_reserved_name = false; | |
| Scanner::Location function_name_location = Scanner::Location::invalid(); | |
| FunctionLiteral::FunctionType function_type = | |
| FunctionLiteral::kAnonymousExpression; | |
| if (impl()->ParsingDynamicFunctionDeclaration()) { | |
| // We don't want dynamic functions to actually declare their name | |
| // "anonymous". We just want that name in the toString(). | |
| if (stack_overflow()) { | |
| *ok = false; | |
| return impl()->NullExpression(); | |
| } | |
| Consume(Token::IDENTIFIER); | |
| DCHECK(scanner()->CurrentMatchesContextual(Token::ANONYMOUS)); | |
| } else if (peek_any_identifier()) { | |
| bool is_await = false; | |
| name = ParseIdentifierOrStrictReservedWord( | |
| function_kind, &is_strict_reserved_name, &is_await, CHECK_OK); | |
| function_name_location = scanner()->location(); | |
| function_type = FunctionLiteral::kNamedExpression; | |
| } | |
| result = impl()->ParseFunctionLiteral( | |
| name, function_name_location, | |
| is_strict_reserved_name ? kFunctionNameIsStrictReserved | |
| : kFunctionNameValidityUnknown, | |
| function_kind, function_token_position, function_type, language_mode(), | |
| nullptr, CHECK_OK); | |
| } else if (peek() == Token::SUPER) { | |
| const bool is_new = false; | |
| result = ParseSuperExpression(is_new, CHECK_OK); | |
| } else if (allow_harmony_dynamic_import() && peek() == Token::IMPORT) { | |
| result = ParseImportExpressions(CHECK_OK); | |
| } else { | |
| result = ParsePrimaryExpression(is_async, CHECK_OK); | |
| } | |
| result = ParseMemberExpressionContinuation(result, is_async, CHECK_OK); | |
| return result; | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseImportExpressions( | |
| bool* ok) { | |
| DCHECK(allow_harmony_dynamic_import()); | |
| classifier()->RecordPatternError(scanner()->peek_location(), | |
| MessageTemplate::kUnexpectedToken, | |
| Token::String(Token::IMPORT)); | |
| Consume(Token::IMPORT); | |
| int pos = position(); | |
| if (allow_harmony_import_meta() && peek() == Token::PERIOD) { | |
| ExpectMetaProperty(Token::META, "import.meta", pos, CHECK_OK); | |
| if (!parsing_module_) { | |
| impl()->ReportMessageAt(scanner()->location(), | |
| MessageTemplate::kImportMetaOutsideModule); | |
| *ok = false; | |
| return impl()->NullExpression(); | |
| } | |
| return impl()->ImportMetaExpression(pos); | |
| } | |
| Expect(Token::LPAREN, CHECK_OK); | |
| if (peek() == Token::RPAREN) { | |
| impl()->ReportMessageAt(scanner()->location(), | |
| MessageTemplate::kImportMissingSpecifier); | |
| *ok = false; | |
| return impl()->NullExpression(); | |
| } | |
| ExpressionT arg = ParseAssignmentExpression(true, CHECK_OK); | |
| Expect(Token::RPAREN, CHECK_OK); | |
| return factory()->NewImportCallExpression(arg, pos); | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseSuperExpression( | |
| bool is_new, bool* ok) { | |
| Expect(Token::SUPER, CHECK_OK); | |
| int pos = position(); | |
| DeclarationScope* scope = GetReceiverScope(); | |
| FunctionKind kind = scope->function_kind(); | |
| if (IsConciseMethod(kind) || IsAccessorFunction(kind) || | |
| IsClassConstructor(kind)) { | |
| if (peek() == Token::PERIOD || peek() == Token::LBRACK) { | |
| scope->RecordSuperPropertyUsage(); | |
| return impl()->NewSuperPropertyReference(pos); | |
| } | |
| // new super() is never allowed. | |
| // super() is only allowed in derived constructor | |
| if (!is_new && peek() == Token::LPAREN && IsDerivedConstructor(kind)) { | |
| // TODO(rossberg): This might not be the correct FunctionState for the | |
| // method here. | |
| return impl()->NewSuperCallReference(pos); | |
| } | |
| } | |
| impl()->ReportMessageAt(scanner()->location(), | |
| MessageTemplate::kUnexpectedSuper); | |
| *ok = false; | |
| return impl()->NullExpression(); | |
| } | |
| template <typename Impl> | |
| void ParserBase<Impl>::ExpectMetaProperty(Token::Value property_name, | |
| const char* full_name, int pos, | |
| bool* ok) { | |
| Consume(Token::PERIOD); | |
| ExpectContextualKeyword(property_name, CHECK_OK_CUSTOM(Void)); | |
| if (scanner()->literal_contains_escapes()) { | |
| impl()->ReportMessageAt( | |
| Scanner::Location(pos, scanner()->location().end_pos), | |
| MessageTemplate::kInvalidEscapedMetaProperty, full_name); | |
| *ok = false; | |
| } | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::ExpressionT | |
| ParserBase<Impl>::ParseNewTargetExpression(bool* ok) { | |
| int pos = position(); | |
| ExpectMetaProperty(Token::TARGET, "new.target", pos, CHECK_OK); | |
| classifier()->RecordAssignmentPatternError( | |
| Scanner::Location(pos, scanner()->location().end_pos), | |
| MessageTemplate::kInvalidDestructuringTarget); | |
| if (!GetReceiverScope()->is_function_scope()) { | |
| impl()->ReportMessageAt(scanner()->location(), | |
| MessageTemplate::kUnexpectedNewTarget); | |
| *ok = false; | |
| return impl()->NullExpression(); | |
| } | |
| return impl()->NewTargetExpression(pos); | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::ExpressionT | |
| ParserBase<Impl>::ParseMemberExpressionContinuation(ExpressionT expression, | |
| bool* is_async, bool* ok) { | |
| // Parses this part of MemberExpression: | |
| // ('[' Expression ']' | '.' Identifier | TemplateLiteral)* | |
| while (true) { | |
| switch (peek()) { | |
| case Token::LBRACK: { | |
| *is_async = false; | |
| ValidateExpression(CHECK_OK); | |
| BindingPatternUnexpectedToken(); | |
| ArrowFormalParametersUnexpectedToken(); | |
| Consume(Token::LBRACK); | |
| int pos = position(); | |
| ExpressionT index = ParseExpressionCoverGrammar(true, CHECK_OK); | |
| ValidateExpression(CHECK_OK); | |
| expression = factory()->NewProperty(expression, index, pos); | |
| impl()->PushPropertyName(index); | |
| Expect(Token::RBRACK, CHECK_OK); | |
| break; | |
| } | |
| case Token::PERIOD: { | |
| *is_async = false; | |
| ValidateExpression(CHECK_OK); | |
| BindingPatternUnexpectedToken(); | |
| ArrowFormalParametersUnexpectedToken(); | |
| Consume(Token::PERIOD); | |
| int pos = peek_position(); | |
| ExpressionT key = ParseIdentifierNameOrPrivateName(CHECK_OK); | |
| expression = factory()->NewProperty(expression, key, pos); | |
| break; | |
| } | |
| case Token::TEMPLATE_SPAN: | |
| case Token::TEMPLATE_TAIL: { | |
| *is_async = false; | |
| ValidateExpression(CHECK_OK); | |
| BindingPatternUnexpectedToken(); | |
| ArrowFormalParametersUnexpectedToken(); | |
| int pos; | |
| if (scanner()->current_token() == Token::IDENTIFIER) { | |
| pos = position(); | |
| } else { | |
| pos = peek_position(); | |
| if (expression->IsFunctionLiteral()) { | |
| // If the tag function looks like an IIFE, set_parenthesized() to | |
| // force eager compilation. | |
| expression->AsFunctionLiteral()->SetShouldEagerCompile(); | |
| } | |
| } | |
| expression = ParseTemplateLiteral(expression, pos, true, CHECK_OK); | |
| break; | |
| } | |
| case Token::ILLEGAL: { | |
| ReportUnexpectedTokenAt(scanner()->peek_location(), Token::ILLEGAL); | |
| *ok = false; | |
| return impl()->NullExpression(); | |
| } | |
| default: | |
| return expression; | |
| } | |
| } | |
| DCHECK(false); | |
| return impl()->NullExpression(); | |
| } | |
| template <typename Impl> | |
| void ParserBase<Impl>::ParseFormalParameter(FormalParametersT* parameters, | |
| bool* ok) { | |
| // FormalParameter[Yield,GeneratorParameter] : | |
| // BindingElement[?Yield, ?GeneratorParameter] | |
| bool is_rest = parameters->has_rest; | |
| FuncNameInferrer::State fni_state(fni_); | |
| ExpressionT pattern = ParsePrimaryExpression(CHECK_OK_CUSTOM(Void)); | |
| ValidateBindingPattern(CHECK_OK_CUSTOM(Void)); | |
| if (!impl()->IsIdentifier(pattern)) { | |
| parameters->is_simple = false; | |
| ValidateFormalParameterInitializer(CHECK_OK_CUSTOM(Void)); | |
| classifier()->RecordNonSimpleParameter(); | |
| } | |
| ExpressionT initializer = impl()->NullExpression(); | |
| if (Check(Token::ASSIGN)) { | |
| if (is_rest) { | |
| ReportMessage(MessageTemplate::kRestDefaultInitializer); | |
| *ok = false; | |
| return; | |
| } | |
| ExpressionClassifier init_classifier(this); | |
| initializer = ParseAssignmentExpression(true, CHECK_OK_CUSTOM(Void)); | |
| ValidateExpression(CHECK_OK_CUSTOM(Void)); | |
| ValidateFormalParameterInitializer(CHECK_OK_CUSTOM(Void)); | |
| parameters->is_simple = false; | |
| DiscardExpressionClassifier(); | |
| classifier()->RecordNonSimpleParameter(); | |
| impl()->SetFunctionNameFromIdentifierRef(initializer, pattern); | |
| } | |
| impl()->AddFormalParameter(parameters, pattern, initializer, | |
| scanner()->location().end_pos, is_rest); | |
| } | |
| template <typename Impl> | |
| void ParserBase<Impl>::ParseFormalParameterList(FormalParametersT* parameters, | |
| bool* ok) { | |
| // FormalParameters[Yield] : | |
| // [empty] | |
| // FunctionRestParameter[?Yield] | |
| // FormalParameterList[?Yield] | |
| // FormalParameterList[?Yield] , | |
| // FormalParameterList[?Yield] , FunctionRestParameter[?Yield] | |
| // | |
| // FormalParameterList[Yield] : | |
| // FormalParameter[?Yield] | |
| // FormalParameterList[?Yield] , FormalParameter[?Yield] | |
| DCHECK_EQ(0, parameters->arity); | |
| if (peek() != Token::RPAREN) { | |
| while (true) { | |
| if (parameters->arity > Code::kMaxArguments) { | |
| ReportMessage(MessageTemplate::kTooManyParameters); | |
| *ok = false; | |
| return; | |
| } | |
| parameters->has_rest = Check(Token::ELLIPSIS); | |
| ParseFormalParameter(parameters, CHECK_OK_CUSTOM(Void)); | |
| if (parameters->has_rest) { | |
| parameters->is_simple = false; | |
| classifier()->RecordNonSimpleParameter(); | |
| if (peek() == Token::COMMA) { | |
| impl()->ReportMessageAt(scanner()->peek_location(), | |
| MessageTemplate::kParamAfterRest); | |
| *ok = false; | |
| return; | |
| } | |
| break; | |
| } | |
| if (!Check(Token::COMMA)) break; | |
| if (peek() == Token::RPAREN) { | |
| // allow the trailing comma | |
| break; | |
| } | |
| } | |
| } | |
| impl()->DeclareFormalParameters(parameters->scope, parameters->params, | |
| parameters->is_simple); | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::BlockT ParserBase<Impl>::ParseVariableDeclarations( | |
| VariableDeclarationContext var_context, | |
| DeclarationParsingResult* parsing_result, | |
| ZonePtrList<const AstRawString>* names, bool* ok) { | |
| // VariableDeclarations :: | |
| // ('var' | 'const' | 'let') (Identifier ('=' AssignmentExpression)?)+[','] | |
| // | |
| // ES6: | |
| // FIXME(marja, nikolaos): Add an up-to-date comment about ES6 variable | |
| // declaration syntax. | |
| DCHECK_NOT_NULL(parsing_result); | |
| parsing_result->descriptor.declaration_kind = DeclarationDescriptor::NORMAL; | |
| parsing_result->descriptor.declaration_pos = peek_position(); | |
| parsing_result->descriptor.initialization_pos = peek_position(); | |
| BlockT init_block = impl()->NullStatement(); | |
| if (var_context != kForStatement) { | |
| init_block = factory()->NewBlock(1, true); | |
| } | |
| switch (peek()) { | |
| case Token::VAR: | |
| parsing_result->descriptor.mode = VariableMode::kVar; | |
| Consume(Token::VAR); | |
| break; | |
| case Token::CONST: | |
| Consume(Token::CONST); | |
| DCHECK_NE(var_context, kStatement); | |
| parsing_result->descriptor.mode = VariableMode::kConst; | |
| break; | |
| case Token::LET: | |
| Consume(Token::LET); | |
| DCHECK_NE(var_context, kStatement); | |
| parsing_result->descriptor.mode = VariableMode::kLet; | |
| break; | |
| default: | |
| UNREACHABLE(); // by current callers | |
| break; | |
| } | |
| parsing_result->descriptor.scope = scope(); | |
| int bindings_start = peek_position(); | |
| do { | |
| // Parse binding pattern. | |
| FuncNameInferrer::State fni_state(fni_); | |
| ExpressionT pattern = impl()->NullExpression(); | |
| int decl_pos = peek_position(); | |
| { | |
| ExpressionClassifier pattern_classifier(this); | |
| pattern = ParsePrimaryExpression(CHECK_OK_CUSTOM(NullStatement)); | |
| ValidateBindingPattern(CHECK_OK_CUSTOM(NullStatement)); | |
| if (IsLexicalVariableMode(parsing_result->descriptor.mode)) { | |
| ValidateLetPattern(CHECK_OK_CUSTOM(NullStatement)); | |
| } | |
| } | |
| Scanner::Location variable_loc = scanner()->location(); | |
| bool single_name = impl()->IsIdentifier(pattern); | |
| if (single_name) { | |
| impl()->PushVariableName(impl()->AsIdentifier(pattern)); | |
| } | |
| ExpressionT value = impl()->NullExpression(); | |
| int initializer_position = kNoSourcePosition; | |
| int value_beg_position = kNoSourcePosition; | |
| if (Check(Token::ASSIGN)) { | |
| value_beg_position = peek_position(); | |
| ExpressionClassifier classifier(this); | |
| value = ParseAssignmentExpression(var_context != kForStatement, | |
| CHECK_OK_CUSTOM(NullStatement)); | |
| ValidateExpression(CHECK_OK_CUSTOM(NullStatement)); | |
| variable_loc.end_pos = scanner()->location().end_pos; | |
| if (!parsing_result->first_initializer_loc.IsValid()) { | |
| parsing_result->first_initializer_loc = variable_loc; | |
| } | |
| // Don't infer if it is "a = function(){...}();"-like expression. | |
| if (single_name && fni_ != nullptr) { | |
| if (!value->IsCall() && !value->IsCallNew()) { | |
| fni_->Infer(); | |
| } else { | |
| fni_->RemoveLastFunction(); | |
| } | |
| } | |
| impl()->SetFunctionNameFromIdentifierRef(value, pattern); | |
| // End position of the initializer is after the assignment expression. | |
| initializer_position = scanner()->location().end_pos; | |
| } else { | |
| if (var_context != kForStatement || !PeekInOrOf()) { | |
| // ES6 'const' and binding patterns require initializers. | |
| if (parsing_result->descriptor.mode == VariableMode::kConst || | |
| !impl()->IsIdentifier(pattern)) { | |
| impl()->ReportMessageAt( | |
| Scanner::Location(decl_pos, scanner()->location().end_pos), | |
| MessageTemplate::kDeclarationMissingInitializer, | |
| !impl()->IsIdentifier(pattern) ? "destructuring" : "const"); | |
| *ok = false; | |
| return impl()->NullStatement(); | |
| } | |
| // 'let x' initializes 'x' to undefined. | |
| if (parsing_result->descriptor.mode == VariableMode::kLet) { | |
| value = factory()->NewUndefinedLiteral(position()); | |
| } | |
| } | |
| // End position of the initializer is after the variable. | |
| initializer_position = position(); | |
| } | |
| typename DeclarationParsingResult::Declaration decl( | |
| pattern, initializer_position, value); | |
| decl.value_beg_position = value_beg_position; | |
| if (var_context == kForStatement) { | |
| // Save the declaration for further handling in ParseForStatement. | |
| parsing_result->declarations.push_back(decl); | |
| } else { | |
| // Immediately declare the variable otherwise. This avoids O(N^2) | |
| // behavior (where N is the number of variables in a single | |
| // declaration) in the PatternRewriter having to do with removing | |
| // and adding VariableProxies to the Scope (see bug 4699). | |
| impl()->DeclareAndInitializeVariables( | |
| init_block, &parsing_result->descriptor, &decl, names, | |
| CHECK_OK_CUSTOM(NullStatement)); | |
| } | |
| } while (Check(Token::COMMA)); | |
| parsing_result->bindings_loc = | |
| Scanner::Location(bindings_start, scanner()->location().end_pos); | |
| DCHECK(*ok); | |
| return init_block; | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::StatementT | |
| ParserBase<Impl>::ParseFunctionDeclaration(bool* ok) { | |
| Consume(Token::FUNCTION); | |
| int pos = position(); | |
| ParseFunctionFlags flags = ParseFunctionFlags::kIsNormal; | |
| if (Check(Token::MUL)) { | |
| impl()->ReportMessageAt( | |
| scanner()->location(), | |
| MessageTemplate::kGeneratorInSingleStatementContext); | |
| *ok = false; | |
| return impl()->NullStatement(); | |
| } | |
| return ParseHoistableDeclaration(pos, flags, nullptr, false, ok); | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::StatementT | |
| ParserBase<Impl>::ParseHoistableDeclaration( | |
| ZonePtrList<const AstRawString>* names, bool default_export, bool* ok) { | |
| Expect(Token::FUNCTION, CHECK_OK_CUSTOM(NullStatement)); | |
| int pos = position(); | |
| ParseFunctionFlags flags = ParseFunctionFlags::kIsNormal; | |
| if (Check(Token::MUL)) { | |
| flags |= ParseFunctionFlags::kIsGenerator; | |
| } | |
| return ParseHoistableDeclaration(pos, flags, names, default_export, ok); | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::StatementT | |
| ParserBase<Impl>::ParseHoistableDeclaration( | |
| int pos, ParseFunctionFlags flags, ZonePtrList<const AstRawString>* names, | |
| bool default_export, bool* ok) { | |
| // FunctionDeclaration :: | |
| // 'function' Identifier '(' FormalParameters ')' '{' FunctionBody '}' | |
| // 'function' '(' FormalParameters ')' '{' FunctionBody '}' | |
| // GeneratorDeclaration :: | |
| // 'function' '*' Identifier '(' FormalParameters ')' '{' FunctionBody '}' | |
| // 'function' '*' '(' FormalParameters ')' '{' FunctionBody '}' | |
| // | |
| // The anonymous forms are allowed iff [default_export] is true. | |
| // | |
| // 'function' and '*' (if present) have been consumed by the caller. | |
| bool is_generator = flags & ParseFunctionFlags::kIsGenerator; | |
| const bool is_async = flags & ParseFunctionFlags::kIsAsync; | |
| DCHECK(!is_generator || !is_async); | |
| if (is_async && Check(Token::MUL)) { | |
| // Async generator | |
| is_generator = true; | |
| } | |
| IdentifierT name; | |
| FunctionNameValidity name_validity; | |
| IdentifierT variable_name; | |
| if (default_export && peek() == Token::LPAREN) { | |
| impl()->GetDefaultStrings(&name, &variable_name); | |
| name_validity = kSkipFunctionNameCheck; | |
| } else { | |
| bool is_strict_reserved; | |
| bool is_await = false; | |
| name = ParseIdentifierOrStrictReservedWord(&is_strict_reserved, &is_await, | |
| CHECK_OK_CUSTOM(NullStatement)); | |
| name_validity = is_strict_reserved ? kFunctionNameIsStrictReserved | |
| : kFunctionNameValidityUnknown; | |
| variable_name = name; | |
| } | |
| FuncNameInferrer::State fni_state(fni_); | |
| impl()->PushEnclosingName(name); | |
| FunctionKind kind = FunctionKindFor(is_generator, is_async); | |
| FunctionLiteralT function = impl()->ParseFunctionLiteral( | |
| name, scanner()->location(), name_validity, kind, pos, | |
| FunctionLiteral::kDeclaration, language_mode(), nullptr, | |
| CHECK_OK_CUSTOM(NullStatement)); | |
| // In ES6, a function behaves as a lexical binding, except in | |
| // a script scope, or the initial scope of eval or another function. | |
| VariableMode mode = | |
| (!scope()->is_declaration_scope() || scope()->is_module_scope()) | |
| ? VariableMode::kLet | |
| : VariableMode::kVar; | |
| // Async functions don't undergo sloppy mode block scoped hoisting, and don't | |
| // allow duplicates in a block. Both are represented by the | |
| // sloppy_block_function_map. Don't add them to the map for async functions. | |
| // Generators are also supposed to be prohibited; currently doing this behind | |
| // a flag and UseCounting violations to assess web compatibility. | |
| bool is_sloppy_block_function = is_sloppy(language_mode()) && | |
| !scope()->is_declaration_scope() && | |
| !is_async && !is_generator; | |
| return impl()->DeclareFunction(variable_name, function, mode, pos, | |
| is_sloppy_block_function, names, ok); | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseClassDeclaration( | |
| ZonePtrList<const AstRawString>* names, bool default_export, bool* ok) { | |
| // ClassDeclaration :: | |
| // 'class' Identifier ('extends' LeftHandExpression)? '{' ClassBody '}' | |
| // 'class' ('extends' LeftHandExpression)? '{' ClassBody '}' | |
| // | |
| // The anonymous form is allowed iff [default_export] is true. | |
| // | |
| // 'class' is expected to be consumed by the caller. | |
| // | |
| // A ClassDeclaration | |
| // | |
| // class C { ... } | |
| // | |
| // has the same semantics as: | |
| // | |
| // let C = class C { ... }; | |
| // | |
| // so rewrite it as such. | |
| int class_token_pos = position(); | |
| IdentifierT name = impl()->NullIdentifier(); | |
| bool is_strict_reserved = false; | |
| IdentifierT variable_name = impl()->NullIdentifier(); | |
| if (default_export && (peek() == Token::EXTENDS || peek() == Token::LBRACE)) { | |
| impl()->GetDefaultStrings(&name, &variable_name); | |
| } else { | |
| bool is_await = false; | |
| name = ParseIdentifierOrStrictReservedWord(&is_strict_reserved, &is_await, | |
| CHECK_OK_CUSTOM(NullStatement)); | |
| variable_name = name; | |
| } | |
| ExpressionClassifier no_classifier(this); | |
| ExpressionT value = | |
| ParseClassLiteral(name, scanner()->location(), is_strict_reserved, | |
| class_token_pos, CHECK_OK_CUSTOM(NullStatement)); | |
| int end_pos = position(); | |
| return impl()->DeclareClass(variable_name, value, names, class_token_pos, | |
| end_pos, ok); | |
| } | |
| // Language extension which is only enabled for source files loaded | |
| // through the API's extension mechanism. A native function | |
| // declaration is resolved by looking up the function through a | |
| // callback provided by the extension. | |
| template <typename Impl> | |
| typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseNativeDeclaration( | |
| bool* ok) { | |
| function_state_->DisableOptimization(BailoutReason::kNativeFunctionLiteral); | |
| int pos = peek_position(); | |
| Expect(Token::FUNCTION, CHECK_OK_CUSTOM(NullStatement)); | |
| // Allow "eval" or "arguments" for backward compatibility. | |
| IdentifierT name = ParseIdentifier(kAllowRestrictedIdentifiers, | |
| CHECK_OK_CUSTOM(NullStatement)); | |
| Expect(Token::LPAREN, CHECK_OK_CUSTOM(NullStatement)); | |
| if (peek() != Token::RPAREN) { | |
| do { | |
| ParseIdentifier(kAllowRestrictedIdentifiers, | |
| CHECK_OK_CUSTOM(NullStatement)); | |
| } while (Check(Token::COMMA)); | |
| } | |
| Expect(Token::RPAREN, CHECK_OK_CUSTOM(NullStatement)); | |
| Expect(Token::SEMICOLON, CHECK_OK_CUSTOM(NullStatement)); | |
| return impl()->DeclareNative(name, pos, ok); | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::StatementT | |
| ParserBase<Impl>::ParseAsyncFunctionDeclaration( | |
| ZonePtrList<const AstRawString>* names, bool default_export, bool* ok) { | |
| // AsyncFunctionDeclaration :: | |
| // async [no LineTerminator here] function BindingIdentifier[Await] | |
| // ( FormalParameters[Await] ) { AsyncFunctionBody } | |
| DCHECK_EQ(scanner()->current_token(), Token::ASYNC); | |
| int pos = position(); | |
| if (scanner()->HasLineTerminatorBeforeNext()) { | |
| *ok = false; | |
| impl()->ReportUnexpectedToken(scanner()->current_token()); | |
| return impl()->NullStatement(); | |
| } | |
| Expect(Token::FUNCTION, CHECK_OK_CUSTOM(NullStatement)); | |
| ParseFunctionFlags flags = ParseFunctionFlags::kIsAsync; | |
| return ParseHoistableDeclaration(pos, flags, names, default_export, ok); | |
| } | |
| template <typename Impl> | |
| void ParserBase<Impl>::ParseFunctionBody( | |
| typename ParserBase<Impl>::StatementListT result, IdentifierT function_name, | |
| int pos, const FormalParametersT& parameters, FunctionKind kind, | |
| FunctionLiteral::FunctionType function_type, bool* ok) { | |
| DeclarationScope* function_scope = scope()->AsDeclarationScope(); | |
| DeclarationScope* inner_scope = function_scope; | |
| BlockT inner_block = impl()->NullStatement(); | |
| StatementListT body = result; | |
| if (!parameters.is_simple) { | |
| inner_scope = NewVarblockScope(); | |
| inner_scope->set_start_position(scanner()->location().beg_pos); | |
| inner_block = factory()->NewBlock(8, true); | |
| inner_block->set_scope(inner_scope); | |
| body = inner_block->statements(); | |
| } | |
| // If we are parsing the source as if it is wrapped in a function, the source | |
| // ends without a closing brace. | |
| Token::Value closing_token = | |
| function_type == FunctionLiteral::kWrapped ? Token::EOS : Token::RBRACE; | |
| { | |
| BlockState block_state(&scope_, inner_scope); | |
| if (IsResumableFunction(kind)) impl()->PrepareGeneratorVariables(); | |
| if (IsAsyncGeneratorFunction(kind)) { | |
| impl()->ParseAndRewriteAsyncGeneratorFunctionBody(pos, kind, body, ok); | |
| } else if (IsGeneratorFunction(kind)) { | |
| impl()->ParseAndRewriteGeneratorFunctionBody(pos, kind, body, ok); | |
| } else if (IsAsyncFunction(kind)) { | |
| ParseAsyncFunctionBody(inner_scope, body, CHECK_OK_VOID); | |
| } else { | |
| ParseStatementList(body, closing_token, CHECK_OK_VOID); | |
| } | |
| if (IsDerivedConstructor(kind)) { | |
| body->Add(factory()->NewReturnStatement(impl()->ThisExpression(), | |
| kNoSourcePosition), | |
| zone()); | |
| } | |
| } | |
| Expect(closing_token, CHECK_OK_VOID); | |
| scope()->set_end_position(scanner()->location().end_pos); | |
| if (!parameters.is_simple) { | |
| DCHECK_NOT_NULL(inner_scope); | |
| DCHECK_EQ(function_scope, scope()); | |
| DCHECK_EQ(function_scope, inner_scope->outer_scope()); | |
| impl()->SetLanguageMode(function_scope, inner_scope->language_mode()); | |
| BlockT init_block = | |
| impl()->BuildParameterInitializationBlock(parameters, CHECK_OK_VOID); | |
| if (is_sloppy(inner_scope->language_mode())) { | |
| impl()->InsertSloppyBlockFunctionVarBindings(inner_scope); | |
| } | |
| // TODO(littledan): Merge the two rejection blocks into one | |
| if (IsAsyncFunction(kind) && !IsAsyncGeneratorFunction(kind)) { | |
| init_block = impl()->BuildRejectPromiseOnException(init_block); | |
| } | |
| inner_scope->set_end_position(scanner()->location().end_pos); | |
| if (inner_scope->FinalizeBlockScope() != nullptr) { | |
| impl()->CheckConflictingVarDeclarations(inner_scope, CHECK_OK_VOID); | |
| impl()->InsertShadowingVarBindingInitializers(inner_block); | |
| } else { | |
| inner_block->set_scope(nullptr); | |
| } | |
| inner_scope = nullptr; | |
| result->Add(init_block, zone()); | |
| result->Add(inner_block, zone()); | |
| } else { | |
| DCHECK_EQ(inner_scope, function_scope); | |
| if (is_sloppy(function_scope->language_mode())) { | |
| impl()->InsertSloppyBlockFunctionVarBindings(function_scope); | |
| } | |
| } | |
| if (!IsArrowFunction(kind)) { | |
| // Declare arguments after parsing the function since lexical 'arguments' | |
| // masks the arguments object. Declare arguments before declaring the | |
| // function var since the arguments object masks 'function arguments'. | |
| function_scope->DeclareArguments(ast_value_factory()); | |
| } | |
| impl()->DeclareFunctionNameVar(function_name, function_type, function_scope); | |
| } | |
| template <typename Impl> | |
| void ParserBase<Impl>::CheckArityRestrictions(int param_count, | |
| FunctionKind function_kind, | |
| bool has_rest, | |
| int formals_start_pos, | |
| int formals_end_pos, bool* ok) { | |
| if (IsGetterFunction(function_kind)) { | |
| if (param_count != 0) { | |
| impl()->ReportMessageAt( | |
| Scanner::Location(formals_start_pos, formals_end_pos), | |
| MessageTemplate::kBadGetterArity); | |
| *ok = false; | |
| } | |
| } else if (IsSetterFunction(function_kind)) { | |
| if (param_count != 1) { | |
| impl()->ReportMessageAt( | |
| Scanner::Location(formals_start_pos, formals_end_pos), | |
| MessageTemplate::kBadSetterArity); | |
| *ok = false; | |
| } | |
| if (has_rest) { | |
| impl()->ReportMessageAt( | |
| Scanner::Location(formals_start_pos, formals_end_pos), | |
| MessageTemplate::kBadSetterRestParameter); | |
| *ok = false; | |
| } | |
| } | |
| } | |
| template <typename Impl> | |
| bool ParserBase<Impl>::IsNextLetKeyword() { | |
| DCHECK(peek() == Token::LET); | |
| Token::Value next_next = PeekAhead(); | |
| switch (next_next) { | |
| case Token::LBRACE: | |
| case Token::LBRACK: | |
| case Token::IDENTIFIER: | |
| case Token::STATIC: | |
| case Token::LET: // `let let;` is disallowed by static semantics, but the | |
| // token must be first interpreted as a keyword in order | |
| // for those semantics to apply. This ensures that ASI is | |
| // not honored when a LineTerminator separates the | |
| // tokens. | |
| case Token::YIELD: | |
| case Token::AWAIT: | |
| case Token::ASYNC: | |
| return true; | |
| case Token::FUTURE_STRICT_RESERVED_WORD: | |
| return is_sloppy(language_mode()); | |
| default: | |
| return false; | |
| } | |
| } | |
| template <typename Impl> | |
| bool ParserBase<Impl>::IsTrivialExpression() { | |
| Token::Value peek_token = peek(); | |
| if (peek_token == Token::SMI || peek_token == Token::NUMBER || | |
| peek_token == Token::BIGINT || peek_token == Token::NULL_LITERAL || | |
| peek_token == Token::TRUE_LITERAL || peek_token == Token::FALSE_LITERAL || | |
| peek_token == Token::STRING || peek_token == Token::IDENTIFIER || | |
| peek_token == Token::THIS) { | |
| // PeekAhead() is expensive & may not always be called, so we only call it | |
| // after checking peek(). | |
| Token::Value peek_ahead = PeekAhead(); | |
| if (peek_ahead == Token::COMMA || peek_ahead == Token::RPAREN || | |
| peek_ahead == Token::SEMICOLON || peek_ahead == Token::RBRACK) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::ExpressionT | |
| ParserBase<Impl>::ParseArrowFunctionLiteral( | |
| bool accept_IN, const FormalParametersT& formal_parameters, | |
| int rewritable_length, bool* ok) { | |
| const RuntimeCallCounterId counters[2][2] = { | |
| {RuntimeCallCounterId::kParseBackgroundArrowFunctionLiteral, | |
| RuntimeCallCounterId::kParseArrowFunctionLiteral}, | |
| {RuntimeCallCounterId::kPreParseBackgroundArrowFunctionLiteral, | |
| RuntimeCallCounterId::kPreParseArrowFunctionLiteral}}; | |
| RuntimeCallTimerScope runtime_timer( | |
| runtime_call_stats_, | |
| counters[Impl::IsPreParser()][parsing_on_main_thread_]); | |
| base::ElapsedTimer timer; | |
| if (V8_UNLIKELY(FLAG_log_function_events)) timer.Start(); | |
| if (peek() == Token::ARROW && scanner_->HasLineTerminatorBeforeNext()) { | |
| // ASI inserts `;` after arrow parameters if a line terminator is found. | |
| // `=> ...` is never a valid expression, so report as syntax error. | |
| // If next token is not `=>`, it's a syntax error anyways. | |
| ReportUnexpectedTokenAt(scanner_->peek_location(), Token::ARROW); | |
| *ok = false; | |
| return impl()->NullExpression(); | |
| } | |
| StatementListT body = impl()->NullStatementList(); | |
| int expected_property_count = -1; | |
| int suspend_count = 0; | |
| int function_literal_id = GetNextFunctionLiteralId(); | |
| FunctionKind kind = formal_parameters.scope->function_kind(); | |
| FunctionLiteral::EagerCompileHint eager_compile_hint = | |
| default_eager_compile_hint_; | |
| bool can_preparse = impl()->parse_lazily() && | |
| eager_compile_hint == FunctionLiteral::kShouldLazyCompile; | |
| // TODO(marja): consider lazy-parsing inner arrow functions too. is_this | |
| // handling in Scope::ResolveVariable needs to change. | |
| bool is_lazy_top_level_function = | |
| can_preparse && impl()->AllowsLazyParsingWithoutUnresolvedVariables(); | |
| bool has_braces = true; | |
| ProducedPreParsedScopeData* produced_preparsed_scope_data = nullptr; | |
| { | |
| FunctionState function_state(&function_state_, &scope_, | |
| formal_parameters.scope); | |
| // Move any queued destructuring assignments which appeared | |
| // in this function's parameter list into its own function_state. | |
| function_state.AdoptDestructuringAssignmentsFromParentState( | |
| rewritable_length); | |
| Expect(Token::ARROW, CHECK_OK); | |
| if (peek() == Token::LBRACE) { | |
| // Multiple statement body | |
| DCHECK_EQ(scope(), formal_parameters.scope); | |
| if (is_lazy_top_level_function) { | |
| // FIXME(marja): Arrow function parameters will be parsed even if the | |
| // body is preparsed; move relevant parts of parameter handling to | |
| // simulate consistent parameter handling. | |
| // For arrow functions, we don't need to retrieve data about function | |
| // parameters. | |
| int dummy_num_parameters = -1; | |
| DCHECK_NE(kind & FunctionKind::kArrowFunction, 0); | |
| LazyParsingResult result = impl()->SkipFunction( | |
| nullptr, kind, FunctionLiteral::kAnonymousExpression, | |
| formal_parameters.scope, &dummy_num_parameters, | |
| &produced_preparsed_scope_data, false, false, CHECK_OK); | |
| DCHECK_NE(result, kLazyParsingAborted); | |
| DCHECK_NULL(produced_preparsed_scope_data); | |
| USE(result); | |
| formal_parameters.scope->ResetAfterPreparsing(ast_value_factory_, | |
| false); | |
| // Discard any queued destructuring assignments which appeared | |
| // in this function's parameter list, and which were adopted | |
| // into this function state, above. | |
| function_state.RewindDestructuringAssignments(0); | |
| } else { | |
| Consume(Token::LBRACE); | |
| body = impl()->NewStatementList(8); | |
| ParseFunctionBody(body, impl()->NullIdentifier(), kNoSourcePosition, | |
| formal_parameters, kind, | |
| FunctionLiteral::kAnonymousExpression, CHECK_OK); | |
| expected_property_count = function_state.expected_property_count(); | |
| } | |
| } else { | |
| // Single-expression body | |
| has_braces = false; | |
| const bool is_async = IsAsyncFunction(kind); | |
| body = impl()->NewStatementList(1); | |
| impl()->AddParameterInitializationBlock(formal_parameters, body, is_async, | |
| CHECK_OK); | |
| ParseSingleExpressionFunctionBody(body, is_async, accept_IN, CHECK_OK); | |
| expected_property_count = function_state.expected_property_count(); | |
| } | |
| formal_parameters.scope->set_end_position(scanner()->location().end_pos); | |
| // Arrow function formal parameters are parsed as StrictFormalParameterList, | |
| // which is not the same as "parameters of a strict function"; it only means | |
| // that duplicates are not allowed. Of course, the arrow function may | |
| // itself be strict as well. | |
| const bool allow_duplicate_parameters = false; | |
| ValidateFormalParameters(language_mode(), allow_duplicate_parameters, | |
| CHECK_OK); | |
| // Validate strict mode. | |
| if (is_strict(language_mode())) { | |
| CheckStrictOctalLiteral(formal_parameters.scope->start_position(), | |
| scanner()->location().end_pos, CHECK_OK); | |
| } | |
| impl()->CheckConflictingVarDeclarations(formal_parameters.scope, CHECK_OK); | |
| impl()->RewriteDestructuringAssignments(); | |
| suspend_count = function_state.suspend_count(); | |
| } | |
| FunctionLiteralT function_literal = factory()->NewFunctionLiteral( | |
| impl()->EmptyIdentifierString(), formal_parameters.scope, body, | |
| expected_property_count, formal_parameters.num_parameters(), | |
| formal_parameters.function_length, | |
| FunctionLiteral::kNoDuplicateParameters, | |
| FunctionLiteral::kAnonymousExpression, eager_compile_hint, | |
| formal_parameters.scope->start_position(), has_braces, | |
| function_literal_id, produced_preparsed_scope_data); | |
| function_literal->set_suspend_count(suspend_count); | |
| function_literal->set_function_token_position( | |
| formal_parameters.scope->start_position()); | |
| impl()->AddFunctionForNameInference(function_literal); | |
| if (V8_UNLIKELY((FLAG_log_function_events))) { | |
| Scope* scope = formal_parameters.scope; | |
| double ms = timer.Elapsed().InMillisecondsF(); | |
| const char* event_name = | |
| is_lazy_top_level_function ? "preparse-no-resolution" : "parse"; | |
| const char* name = "arrow function"; | |
| logger_->FunctionEvent(event_name, script_id(), ms, scope->start_position(), | |
| scope->end_position(), name, strlen(name)); | |
| } | |
| return function_literal; | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseClassLiteral( | |
| IdentifierT name, Scanner::Location class_name_location, | |
| bool name_is_strict_reserved, int class_token_pos, bool* ok) { | |
| bool is_anonymous = impl()->IsNull(name); | |
| // All parts of a ClassDeclaration and ClassExpression are strict code. | |
| if (!is_anonymous) { | |
| if (name_is_strict_reserved) { | |
| impl()->ReportMessageAt(class_name_location, | |
| MessageTemplate::kUnexpectedStrictReserved); | |
| *ok = false; | |
| return impl()->NullExpression(); | |
| } | |
| if (impl()->IsEvalOrArguments(name)) { | |
| impl()->ReportMessageAt(class_name_location, | |
| MessageTemplate::kStrictEvalArguments); | |
| *ok = false; | |
| return impl()->NullExpression(); | |
| } | |
| } | |
| Scope* block_scope = NewScope(BLOCK_SCOPE); | |
| BlockState block_state(&scope_, block_scope); | |
| RaiseLanguageMode(LanguageMode::kStrict); | |
| ClassInfo class_info(this); | |
| class_info.is_anonymous = is_anonymous; | |
| impl()->DeclareClassVariable(name, &class_info, class_token_pos, CHECK_OK); | |
| scope()->set_start_position(scanner()->location().end_pos); | |
| if (Check(Token::EXTENDS)) { | |
| FuncNameInferrer::State fni_state(fni_); | |
| ExpressionClassifier extends_classifier(this); | |
| class_info.extends = ParseLeftHandSideExpression(CHECK_OK); | |
| ValidateExpression(CHECK_OK); | |
| AccumulateFormalParameterContainmentErrors(); | |
| } | |
| ClassLiteralChecker checker(this); | |
| Expect(Token::LBRACE, CHECK_OK); | |
| const bool has_extends = !impl()->IsNull(class_info.extends); | |
| while (peek() != Token::RBRACE) { | |
| if (Check(Token::SEMICOLON)) continue; | |
| FuncNameInferrer::State fni_state(fni_); | |
| bool is_computed_name = false; // Classes do not care about computed | |
| // property names here. | |
| bool is_static; | |
| ClassLiteralProperty::Kind property_kind; | |
| ExpressionClassifier property_classifier(this); | |
| IdentifierT property_name; | |
| // If we haven't seen the constructor yet, it potentially is the next | |
| // property. | |
| bool is_constructor = !class_info.has_seen_constructor; | |
| ClassLiteralPropertyT property = ParseClassPropertyDefinition( | |
| &checker, &class_info, &property_name, has_extends, &is_computed_name, | |
| &property_kind, &is_static, CHECK_OK); | |
| if (!class_info.has_static_computed_names && is_static && | |
| is_computed_name) { | |
| class_info.has_static_computed_names = true; | |
| } | |
| if (is_computed_name && | |
| property_kind == ClassLiteralProperty::PUBLIC_FIELD) { | |
| class_info.computed_field_count++; | |
| } | |
| is_constructor &= class_info.has_seen_constructor; | |
| ValidateExpression(CHECK_OK); | |
| AccumulateFormalParameterContainmentErrors(); | |
| impl()->DeclareClassProperty(name, property, property_name, property_kind, | |
| is_static, is_constructor, is_computed_name, | |
| &class_info, CHECK_OK); | |
| impl()->InferFunctionName(); | |
| } | |
| Expect(Token::RBRACE, CHECK_OK); | |
| int end_pos = scanner()->location().end_pos; | |
| block_scope->set_end_position(end_pos); | |
| return impl()->RewriteClassLiteral(block_scope, name, &class_info, | |
| class_token_pos, end_pos, ok); | |
| } | |
| template <typename Impl> | |
| void ParserBase<Impl>::ParseSingleExpressionFunctionBody(StatementListT body, | |
| bool is_async, | |
| bool accept_IN, | |
| bool* ok) { | |
| if (is_async) impl()->PrepareGeneratorVariables(); | |
| ExpressionClassifier classifier(this); | |
| ExpressionT expression = ParseAssignmentExpression(accept_IN, CHECK_OK_VOID); | |
| ValidateExpression(CHECK_OK_VOID); | |
| if (is_async) { | |
| BlockT block = factory()->NewBlock(1, true); | |
| impl()->RewriteAsyncFunctionBody(body, block, expression, CHECK_OK_VOID); | |
| } else { | |
| body->Add(BuildReturnStatement(expression, expression->position()), zone()); | |
| } | |
| } | |
| template <typename Impl> | |
| void ParserBase<Impl>::ParseAsyncFunctionBody(Scope* scope, StatementListT body, | |
| bool* ok) { | |
| BlockT block = factory()->NewBlock(8, true); | |
| ParseStatementList(block->statements(), Token::RBRACE, CHECK_OK_VOID); | |
| impl()->RewriteAsyncFunctionBody( | |
| body, block, factory()->NewUndefinedLiteral(kNoSourcePosition), | |
| CHECK_OK_VOID); | |
| scope->set_end_position(scanner()->location().end_pos); | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::ExpressionT | |
| ParserBase<Impl>::ParseAsyncFunctionLiteral(bool* ok) { | |
| // AsyncFunctionLiteral :: | |
| // async [no LineTerminator here] function ( FormalParameters[Await] ) | |
| // { AsyncFunctionBody } | |
| // | |
| // async [no LineTerminator here] function BindingIdentifier[Await] | |
| // ( FormalParameters[Await] ) { AsyncFunctionBody } | |
| DCHECK_EQ(scanner()->current_token(), Token::ASYNC); | |
| int pos = position(); | |
| Expect(Token::FUNCTION, CHECK_OK); | |
| bool is_strict_reserved = false; | |
| IdentifierT name = impl()->NullIdentifier(); | |
| FunctionLiteral::FunctionType type = FunctionLiteral::kAnonymousExpression; | |
| bool is_generator = Check(Token::MUL); | |
| const bool kIsAsync = true; | |
| const FunctionKind kind = FunctionKindFor(is_generator, kIsAsync); | |
| if (impl()->ParsingDynamicFunctionDeclaration()) { | |
| // We don't want dynamic functions to actually declare their name | |
| // "anonymous". We just want that name in the toString(). | |
| if (stack_overflow()) { | |
| *ok = false; | |
| return impl()->NullExpression(); | |
| } | |
| Consume(Token::IDENTIFIER); | |
| DCHECK(scanner()->CurrentMatchesContextual(Token::ANONYMOUS)); | |
| } else if (peek_any_identifier()) { | |
| type = FunctionLiteral::kNamedExpression; | |
| bool is_await = false; | |
| name = ParseIdentifierOrStrictReservedWord(kind, &is_strict_reserved, | |
| &is_await, CHECK_OK); | |
| // If the function name is "await", ParseIdentifierOrStrictReservedWord | |
| // recognized the error. | |
| DCHECK(!is_await); | |
| } | |
| return impl()->ParseFunctionLiteral( | |
| name, scanner()->location(), | |
| is_strict_reserved ? kFunctionNameIsStrictReserved | |
| : kFunctionNameValidityUnknown, | |
| kind, pos, type, language_mode(), nullptr, CHECK_OK); | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseTemplateLiteral( | |
| ExpressionT tag, int start, bool tagged, bool* ok) { | |
| // A TemplateLiteral is made up of 0 or more TEMPLATE_SPAN tokens (literal | |
| // text followed by a substitution expression), finalized by a single | |
| // TEMPLATE_TAIL. | |
| // | |
| // In terms of draft language, TEMPLATE_SPAN may be either the TemplateHead or | |
| // TemplateMiddle productions, while TEMPLATE_TAIL is either TemplateTail, or | |
| // NoSubstitutionTemplate. | |
| // | |
| // When parsing a TemplateLiteral, we must have scanned either an initial | |
| // TEMPLATE_SPAN, or a TEMPLATE_TAIL. | |
| DCHECK(peek() == Token::TEMPLATE_SPAN || peek() == Token::TEMPLATE_TAIL); | |
| if (tagged) { | |
| // TaggedTemplate expressions prevent the eval compilation cache from being | |
| // used. This flag is only used if an eval is being parsed. | |
| set_allow_eval_cache(false); | |
| } | |
| bool forbid_illegal_escapes = !tagged; | |
| // If we reach a TEMPLATE_TAIL first, we are parsing a NoSubstitutionTemplate. | |
| // In this case we may simply consume the token and build a template with a | |
| // single TEMPLATE_SPAN and no expressions. | |
| if (peek() == Token::TEMPLATE_TAIL) { | |
| Consume(Token::TEMPLATE_TAIL); | |
| int pos = position(); | |
| typename Impl::TemplateLiteralState ts = impl()->OpenTemplateLiteral(pos); | |
| bool is_valid = CheckTemplateEscapes(forbid_illegal_escapes, CHECK_OK); | |
| impl()->AddTemplateSpan(&ts, is_valid, true); | |
| return impl()->CloseTemplateLiteral(&ts, start, tag); | |
| } | |
| Consume(Token::TEMPLATE_SPAN); | |
| int pos = position(); | |
| typename Impl::TemplateLiteralState ts = impl()->OpenTemplateLiteral(pos); | |
| bool is_valid = CheckTemplateEscapes(forbid_illegal_escapes, CHECK_OK); | |
| impl()->AddTemplateSpan(&ts, is_valid, false); | |
| Token::Value next; | |
| // If we open with a TEMPLATE_SPAN, we must scan the subsequent expression, | |
| // and repeat if the following token is a TEMPLATE_SPAN as well (in this | |
| // case, representing a TemplateMiddle). | |
| do { | |
| next = peek(); | |
| if (next == Token::EOS) { | |
| impl()->ReportMessageAt(Scanner::Location(start, peek_position()), | |
| MessageTemplate::kUnterminatedTemplate); | |
| *ok = false; | |
| return impl()->NullExpression(); | |
| } else if (next == Token::ILLEGAL) { | |
| impl()->ReportMessageAt( | |
| Scanner::Location(position() + 1, peek_position()), | |
| MessageTemplate::kUnexpectedToken, "ILLEGAL", kSyntaxError); | |
| *ok = false; | |
| return impl()->NullExpression(); | |
| } | |
| int expr_pos = peek_position(); | |
| ExpressionT expression = ParseExpressionCoverGrammar(true, CHECK_OK); | |
| ValidateExpression(CHECK_OK); | |
| impl()->AddTemplateExpression(&ts, expression); | |
| if (peek() != Token::RBRACE) { | |
| impl()->ReportMessageAt(Scanner::Location(expr_pos, peek_position()), | |
| MessageTemplate::kUnterminatedTemplateExpr); | |
| *ok = false; | |
| return impl()->NullExpression(); | |
| } | |
| // If we didn't die parsing that expression, our next token should be a | |
| // TEMPLATE_SPAN or TEMPLATE_TAIL. | |
| next = scanner()->ScanTemplateContinuation(); | |
| Next(); | |
| pos = position(); | |
| if (next == Token::EOS) { | |
| impl()->ReportMessageAt(Scanner::Location(start, pos), | |
| MessageTemplate::kUnterminatedTemplate); | |
| *ok = false; | |
| return impl()->NullExpression(); | |
| } else if (next == Token::ILLEGAL) { | |
| impl()->ReportMessageAt( | |
| Scanner::Location(position() + 1, peek_position()), | |
| MessageTemplate::kUnexpectedToken, "ILLEGAL", kSyntaxError); | |
| *ok = false; | |
| return impl()->NullExpression(); | |
| } | |
| bool is_valid = CheckTemplateEscapes(forbid_illegal_escapes, CHECK_OK); | |
| impl()->AddTemplateSpan(&ts, is_valid, next == Token::TEMPLATE_TAIL); | |
| } while (next == Token::TEMPLATE_SPAN); | |
| DCHECK_EQ(next, Token::TEMPLATE_TAIL); | |
| // Once we've reached a TEMPLATE_TAIL, we can close the TemplateLiteral. | |
| return impl()->CloseTemplateLiteral(&ts, start, tag); | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::ExpressionT | |
| ParserBase<Impl>::CheckAndRewriteReferenceExpression( | |
| ExpressionT expression, int beg_pos, int end_pos, | |
| MessageTemplate::Template message, bool* ok) { | |
| return CheckAndRewriteReferenceExpression(expression, beg_pos, end_pos, | |
| message, kReferenceError, ok); | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::ExpressionT | |
| ParserBase<Impl>::CheckAndRewriteReferenceExpression( | |
| ExpressionT expression, int beg_pos, int end_pos, | |
| MessageTemplate::Template message, ParseErrorType type, bool* ok) { | |
| if (impl()->IsIdentifier(expression) && is_strict(language_mode()) && | |
| impl()->IsEvalOrArguments(impl()->AsIdentifier(expression))) { | |
| ReportMessageAt(Scanner::Location(beg_pos, end_pos), | |
| MessageTemplate::kStrictEvalArguments, kSyntaxError); | |
| *ok = false; | |
| return impl()->NullExpression(); | |
| } | |
| if (expression->IsValidReferenceExpression()) { | |
| return expression; | |
| } | |
| if (expression->IsCall() && !expression->AsCall()->is_tagged_template()) { | |
| // If it is a call, make it a runtime error for legacy web compatibility. | |
| // Bug: https://bugs.chromium.org/p/v8/issues/detail?id=4480 | |
| // Rewrite `expr' to `expr[throw ReferenceError]'. | |
| impl()->CountUsage( | |
| is_strict(language_mode()) | |
| ? v8::Isolate::kAssigmentExpressionLHSIsCallInStrict | |
| : v8::Isolate::kAssigmentExpressionLHSIsCallInSloppy); | |
| ExpressionT error = impl()->NewThrowReferenceError(message, beg_pos); | |
| return factory()->NewProperty(expression, error, beg_pos); | |
| } | |
| ReportMessageAt(Scanner::Location(beg_pos, end_pos), message, type); | |
| *ok = false; | |
| return impl()->NullExpression(); | |
| } | |
| template <typename Impl> | |
| bool ParserBase<Impl>::IsValidReferenceExpression(ExpressionT expression) { | |
| return IsAssignableIdentifier(expression) || expression->IsProperty(); | |
| } | |
| template <typename Impl> | |
| void ParserBase<Impl>::CheckDestructuringElement(ExpressionT expression, | |
| int begin, int end) { | |
| if (!IsValidPattern(expression) && !expression->IsAssignment() && | |
| !IsValidReferenceExpression(expression)) { | |
| classifier()->RecordAssignmentPatternError( | |
| Scanner::Location(begin, end), | |
| MessageTemplate::kInvalidDestructuringTarget); | |
| } | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseV8Intrinsic( | |
| bool* ok) { | |
| // CallRuntime :: | |
| // '%' Identifier Arguments | |
| int pos = peek_position(); | |
| Expect(Token::MOD, CHECK_OK); | |
| // Allow "eval" or "arguments" for backward compatibility. | |
| IdentifierT name = ParseIdentifier(kAllowRestrictedIdentifiers, CHECK_OK); | |
| Scanner::Location spread_pos; | |
| ExpressionClassifier classifier(this); | |
| ExpressionListT args = ParseArguments(&spread_pos, CHECK_OK); | |
| if (spread_pos.IsValid()) { | |
| *ok = false; | |
| ReportMessageAt(spread_pos, MessageTemplate::kIntrinsicWithSpread, | |
| kSyntaxError); | |
| return impl()->NullExpression(); | |
| } | |
| return impl()->NewV8Intrinsic(name, args, pos, ok); | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseDoExpression( | |
| bool* ok) { | |
| // AssignmentExpression :: | |
| // do '{' StatementList '}' | |
| int pos = peek_position(); | |
| Expect(Token::DO, CHECK_OK); | |
| BlockT block = ParseBlock(nullptr, CHECK_OK); | |
| return impl()->RewriteDoExpression(block, pos, ok); | |
| } | |
| // Redefinition of CHECK_OK for parsing statements. | |
| #undef CHECK_OK | |
| #define CHECK_OK CHECK_OK_CUSTOM(NullStatement) | |
| template <typename Impl> | |
| typename ParserBase<Impl>::LazyParsingResult | |
| ParserBase<Impl>::ParseStatementList(StatementListT body, | |
| Token::Value end_token, bool may_abort, | |
| bool* ok) { | |
| // StatementList :: | |
| // (StatementListItem)* <end_token> | |
| // Allocate a target stack to use for this set of source | |
| // elements. This way, all scripts and functions get their own | |
| // target stack thus avoiding illegal breaks and continues across | |
| // functions. | |
| typename Types::TargetScope target_scope(this); | |
| int count_statements = 0; | |
| DCHECK(!impl()->IsNull(body)); | |
| bool directive_prologue = true; // Parsing directive prologue. | |
| while (peek() != end_token) { | |
| if (directive_prologue && peek() != Token::STRING) { | |
| directive_prologue = false; | |
| } | |
| bool starts_with_identifier = peek() == Token::IDENTIFIER; | |
| Scanner::Location token_loc = scanner()->peek_location(); | |
| StatementT stat = | |
| ParseStatementListItem(CHECK_OK_CUSTOM(Return, kLazyParsingComplete)); | |
| if (impl()->IsNull(stat) || stat->IsEmptyStatement()) { | |
| directive_prologue = false; // End of directive prologue. | |
| continue; | |
| } | |
| if (directive_prologue) { | |
| // The length of the token is used to distinguish between strings literals | |
| // that evaluate equal to directives but contain either escape sequences | |
| // (e.g., "use \x73trict") or line continuations (e.g., "use \(newline) | |
| // strict"). | |
| if (impl()->IsUseStrictDirective(stat) && | |
| token_loc.end_pos - token_loc.beg_pos == sizeof("use strict") + 1) { | |
| // Directive "use strict" (ES5 14.1). | |
| RaiseLanguageMode(LanguageMode::kStrict); | |
| if (!scope()->HasSimpleParameters()) { | |
| // TC39 deemed "use strict" directives to be an error when occurring | |
| // in the body of a function with non-simple parameter list, on | |
| // 29/7/2015. https://goo.gl/ueA7Ln | |
| impl()->ReportMessageAt( | |
| token_loc, MessageTemplate::kIllegalLanguageModeDirective, | |
| "use strict"); | |
| *ok = false; | |
| return kLazyParsingComplete; | |
| } | |
| } else if (impl()->IsUseAsmDirective(stat) && | |
| token_loc.end_pos - token_loc.beg_pos == | |
| sizeof("use asm") + 1) { | |
| // Directive "use asm". | |
| impl()->SetAsmModule(); | |
| } else if (impl()->IsStringLiteral(stat)) { | |
| // Possibly an unknown directive. | |
| // Should not change mode, but will increment usage counters | |
| // as appropriate. Ditto usages below. | |
| RaiseLanguageMode(LanguageMode::kSloppy); | |
| } else { | |
| // End of the directive prologue. | |
| directive_prologue = false; | |
| RaiseLanguageMode(LanguageMode::kSloppy); | |
| } | |
| } else { | |
| RaiseLanguageMode(LanguageMode::kSloppy); | |
| } | |
| // If we're allowed to abort, we will do so when we see a "long and | |
| // trivial" function. Our current definition of "long and trivial" is: | |
| // - over kLazyParseTrialLimit statements | |
| // - all starting with an identifier (i.e., no if, for, while, etc.) | |
| if (may_abort) { | |
| if (!starts_with_identifier) { | |
| may_abort = false; | |
| } else if (++count_statements > kLazyParseTrialLimit) { | |
| return kLazyParsingAborted; | |
| } | |
| } | |
| body->Add(stat, zone()); | |
| } | |
| return kLazyParsingComplete; | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseStatementListItem( | |
| bool* ok) { | |
| // ECMA 262 6th Edition | |
| // StatementListItem[Yield, Return] : | |
| // Statement[?Yield, ?Return] | |
| // Declaration[?Yield] | |
| // | |
| // Declaration[Yield] : | |
| // HoistableDeclaration[?Yield] | |
| // ClassDeclaration[?Yield] | |
| // LexicalDeclaration[In, ?Yield] | |
| // | |
| // HoistableDeclaration[Yield, Default] : | |
| // FunctionDeclaration[?Yield, ?Default] | |
| // GeneratorDeclaration[?Yield, ?Default] | |
| // | |
| // LexicalDeclaration[In, Yield] : | |
| // LetOrConst BindingList[?In, ?Yield] ; | |
| switch (peek()) { | |
| case Token::FUNCTION: | |
| return ParseHoistableDeclaration(nullptr, false, ok); | |
| case Token::CLASS: | |
| Consume(Token::CLASS); | |
| return ParseClassDeclaration(nullptr, false, ok); | |
| case Token::VAR: | |
| case Token::CONST: | |
| return ParseVariableStatement(kStatementListItem, nullptr, ok); | |
| case Token::LET: | |
| if (IsNextLetKeyword()) { | |
| return ParseVariableStatement(kStatementListItem, nullptr, ok); | |
| } | |
| break; | |
| case Token::ASYNC: | |
| if (PeekAhead() == Token::FUNCTION && | |
| !scanner()->HasLineTerminatorAfterNext()) { | |
| Consume(Token::ASYNC); | |
| return ParseAsyncFunctionDeclaration(nullptr, false, ok); | |
| } | |
| break; | |
| default: | |
| break; | |
| } | |
| return ParseStatement(nullptr, nullptr, kAllowLabelledFunctionStatement, ok); | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseStatement( | |
| ZonePtrList<const AstRawString>* labels, | |
| ZonePtrList<const AstRawString>* own_labels, | |
| AllowLabelledFunctionStatement allow_function, bool* ok) { | |
| // Statement :: | |
| // Block | |
| // VariableStatement | |
| // EmptyStatement | |
| // ExpressionStatement | |
| // IfStatement | |
| // IterationStatement | |
| // ContinueStatement | |
| // BreakStatement | |
| // ReturnStatement | |
| // WithStatement | |
| // LabelledStatement | |
| // SwitchStatement | |
| // ThrowStatement | |
| // TryStatement | |
| // DebuggerStatement | |
| // {own_labels} is always a subset of {labels}. | |
| DCHECK_IMPLIES(labels == nullptr, own_labels == nullptr); | |
| // Note: Since labels can only be used by 'break' and 'continue' | |
| // statements, which themselves are only valid within blocks, | |
| // iterations or 'switch' statements (i.e., BreakableStatements), | |
| // labels can be simply ignored in all other cases; except for | |
| // trivial labeled break statements 'label: break label' which is | |
| // parsed into an empty statement. | |
| switch (peek()) { | |
| case Token::LBRACE: | |
| return ParseBlock(labels, ok); | |
| case Token::SEMICOLON: | |
| Next(); | |
| return factory()->NewEmptyStatement(kNoSourcePosition); | |
| case Token::IF: | |
| return ParseIfStatement(labels, ok); | |
| case Token::DO: | |
| return ParseDoWhileStatement(labels, own_labels, ok); | |
| case Token::WHILE: | |
| return ParseWhileStatement(labels, own_labels, ok); | |
| case Token::FOR: | |
| if (V8_UNLIKELY(is_async_function() && PeekAhead() == Token::AWAIT)) { | |
| return ParseForAwaitStatement(labels, own_labels, ok); | |
| } | |
| return ParseForStatement(labels, own_labels, ok); | |
| case Token::CONTINUE: | |
| return ParseContinueStatement(ok); | |
| case Token::BREAK: | |
| return ParseBreakStatement(labels, ok); | |
| case Token::RETURN: | |
| return ParseReturnStatement(ok); | |
| case Token::THROW: | |
| return ParseThrowStatement(ok); | |
| case Token::TRY: { | |
| // It is somewhat complicated to have labels on try-statements. | |
| // When breaking out of a try-finally statement, one must take | |
| // great care not to treat it as a fall-through. It is much easier | |
| // just to wrap the entire try-statement in a statement block and | |
| // put the labels there. | |
| if (labels == nullptr) return ParseTryStatement(ok); | |
| BlockT result = factory()->NewBlock(1, false, labels); | |
| typename Types::Target target(this, result); | |
| StatementT statement = ParseTryStatement(CHECK_OK); | |
| result->statements()->Add(statement, zone()); | |
| return result; | |
| } | |
| case Token::WITH: | |
| return ParseWithStatement(labels, ok); | |
| case Token::SWITCH: | |
| return ParseSwitchStatement(labels, ok); | |
| case Token::FUNCTION: | |
| // FunctionDeclaration only allowed as a StatementListItem, not in | |
| // an arbitrary Statement position. Exceptions such as | |
| // ES#sec-functiondeclarations-in-ifstatement-statement-clauses | |
| // are handled by calling ParseScopedStatement rather than | |
| // ParseStatement directly. | |
| impl()->ReportMessageAt(scanner()->peek_location(), | |
| is_strict(language_mode()) | |
| ? MessageTemplate::kStrictFunction | |
| : MessageTemplate::kSloppyFunction); | |
| *ok = false; | |
| return impl()->NullStatement(); | |
| case Token::DEBUGGER: | |
| return ParseDebuggerStatement(ok); | |
| case Token::VAR: | |
| return ParseVariableStatement(kStatement, nullptr, ok); | |
| case Token::ASYNC: | |
| if (!scanner()->HasLineTerminatorAfterNext() && | |
| PeekAhead() == Token::FUNCTION) { | |
| impl()->ReportMessageAt( | |
| scanner()->peek_location(), | |
| MessageTemplate::kAsyncFunctionInSingleStatementContext); | |
| *ok = false; | |
| return impl()->NullStatement(); | |
| } | |
| V8_FALLTHROUGH; | |
| default: | |
| return ParseExpressionOrLabelledStatement(labels, own_labels, | |
| allow_function, ok); | |
| } | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::BlockT ParserBase<Impl>::ParseBlock( | |
| ZonePtrList<const AstRawString>* labels, bool* ok) { | |
| // Block :: | |
| // '{' StatementList '}' | |
| // Construct block expecting 16 statements. | |
| BlockT body = factory()->NewBlock(16, false, labels); | |
| // Parse the statements and collect escaping labels. | |
| Expect(Token::LBRACE, CHECK_OK_CUSTOM(NullStatement)); | |
| { | |
| BlockState block_state(zone(), &scope_); | |
| scope()->set_start_position(scanner()->location().beg_pos); | |
| typename Types::Target target(this, body); | |
| while (peek() != Token::RBRACE) { | |
| StatementT stat = ParseStatementListItem(CHECK_OK_CUSTOM(NullStatement)); | |
| if (!impl()->IsNull(stat) && !stat->IsEmptyStatement()) { | |
| body->statements()->Add(stat, zone()); | |
| } | |
| } | |
| Expect(Token::RBRACE, CHECK_OK_CUSTOM(NullStatement)); | |
| int end_pos = scanner()->location().end_pos; | |
| scope()->set_end_position(end_pos); | |
| impl()->RecordBlockSourceRange(body, end_pos); | |
| body->set_scope(scope()->FinalizeBlockScope()); | |
| } | |
| return body; | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseScopedStatement( | |
| ZonePtrList<const AstRawString>* labels, bool* ok) { | |
| if (is_strict(language_mode()) || peek() != Token::FUNCTION) { | |
| return ParseStatement(labels, nullptr, ok); | |
| } else { | |
| // Make a block around the statement for a lexical binding | |
| // is introduced by a FunctionDeclaration. | |
| BlockState block_state(zone(), &scope_); | |
| scope()->set_start_position(scanner()->location().beg_pos); | |
| BlockT block = factory()->NewBlock(1, false); | |
| StatementT body = ParseFunctionDeclaration(CHECK_OK); | |
| block->statements()->Add(body, zone()); | |
| scope()->set_end_position(scanner()->location().end_pos); | |
| block->set_scope(scope()->FinalizeBlockScope()); | |
| return block; | |
| } | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseVariableStatement( | |
| VariableDeclarationContext var_context, | |
| ZonePtrList<const AstRawString>* names, bool* ok) { | |
| // VariableStatement :: | |
| // VariableDeclarations ';' | |
| // The scope of a var declared variable anywhere inside a function | |
| // is the entire function (ECMA-262, 3rd, 10.1.3, and 12.2). Thus we can | |
| // transform a source-level var declaration into a (Function) Scope | |
| // declaration, and rewrite the source-level initialization into an assignment | |
| // statement. We use a block to collect multiple assignments. | |
| // | |
| // We mark the block as initializer block because we don't want the | |
| // rewriter to add a '.result' assignment to such a block (to get compliant | |
| // behavior for code such as print(eval('var x = 7')), and for cosmetic | |
| // reasons when pretty-printing. Also, unless an assignment (initialization) | |
| // is inside an initializer block, it is ignored. | |
| DeclarationParsingResult parsing_result; | |
| StatementT result = | |
| ParseVariableDeclarations(var_context, &parsing_result, names, CHECK_OK); | |
| ExpectSemicolon(CHECK_OK); | |
| return result; | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseDebuggerStatement( | |
| bool* ok) { | |
| // In ECMA-262 'debugger' is defined as a reserved keyword. In some browser | |
| // contexts this is used as a statement which invokes the debugger as i a | |
| // break point is present. | |
| // DebuggerStatement :: | |
| // 'debugger' ';' | |
| int pos = peek_position(); | |
| Expect(Token::DEBUGGER, CHECK_OK); | |
| ExpectSemicolon(CHECK_OK); | |
| return factory()->NewDebuggerStatement(pos); | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::StatementT | |
| ParserBase<Impl>::ParseExpressionOrLabelledStatement( | |
| ZonePtrList<const AstRawString>* labels, | |
| ZonePtrList<const AstRawString>* own_labels, | |
| AllowLabelledFunctionStatement allow_function, bool* ok) { | |
| // ExpressionStatement | LabelledStatement :: | |
| // Expression ';' | |
| // Identifier ':' Statement | |
| // | |
| // ExpressionStatement[Yield] : | |
| // [lookahead notin {{, function, class, let [}] Expression[In, ?Yield] ; | |
| int pos = peek_position(); | |
| switch (peek()) { | |
| case Token::FUNCTION: | |
| case Token::LBRACE: | |
| UNREACHABLE(); // Always handled by the callers. | |
| case Token::CLASS: | |
| ReportUnexpectedToken(Next()); | |
| *ok = false; | |
| return impl()->NullStatement(); | |
| case Token::LET: { | |
| Token::Value next_next = PeekAhead(); | |
| // "let" followed by either "[", "{" or an identifier means a lexical | |
| // declaration, which should not appear here. | |
| // However, ASI may insert a line break before an identifier or a brace. | |
| if (next_next != Token::LBRACK && | |
| ((next_next != Token::LBRACE && next_next != Token::IDENTIFIER) || | |
| scanner_->HasLineTerminatorAfterNext())) { | |
| break; | |
| } | |
| impl()->ReportMessageAt(scanner()->peek_location(), | |
| MessageTemplate::kUnexpectedLexicalDeclaration); | |
| *ok = false; | |
| return impl()->NullStatement(); | |
| } | |
| default: | |
| break; | |
| } | |
| bool starts_with_identifier = peek_any_identifier(); | |
| ExpressionT expr = ParseExpression(true, CHECK_OK); | |
| if (peek() == Token::COLON && starts_with_identifier && | |
| impl()->IsIdentifier(expr)) { | |
| // The whole expression was a single identifier, and not, e.g., | |
| // something starting with an identifier or a parenthesized identifier. | |
| impl()->DeclareLabel(&labels, &own_labels, | |
| impl()->AsIdentifierExpression(expr), CHECK_OK); | |
| Consume(Token::COLON); | |
| // ES#sec-labelled-function-declarations Labelled Function Declarations | |
| if (peek() == Token::FUNCTION && is_sloppy(language_mode()) && | |
| allow_function == kAllowLabelledFunctionStatement) { | |
| return ParseFunctionDeclaration(ok); | |
| } | |
| return ParseStatement(labels, own_labels, allow_function, ok); | |
| } | |
| // If we have an extension, we allow a native function declaration. | |
| // A native function declaration starts with "native function" with | |
| // no line-terminator between the two words. | |
| if (extension_ != nullptr && peek() == Token::FUNCTION && | |
| !scanner()->HasLineTerminatorBeforeNext() && impl()->IsNative(expr) && | |
| !scanner()->literal_contains_escapes()) { | |
| return ParseNativeDeclaration(ok); | |
| } | |
| // Parsed expression statement, followed by semicolon. | |
| ExpectSemicolon(CHECK_OK); | |
| return factory()->NewExpressionStatement(expr, pos); | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseIfStatement( | |
| ZonePtrList<const AstRawString>* labels, bool* ok) { | |
| // IfStatement :: | |
| // 'if' '(' Expression ')' Statement ('else' Statement)? | |
| int pos = peek_position(); | |
| Expect(Token::IF, CHECK_OK); | |
| Expect(Token::LPAREN, CHECK_OK); | |
| ExpressionT condition = ParseExpression(true, CHECK_OK); | |
| Expect(Token::RPAREN, CHECK_OK); | |
| SourceRange then_range, else_range; | |
| StatementT then_statement = impl()->NullStatement(); | |
| { | |
| SourceRangeScope range_scope(scanner(), &then_range); | |
| then_statement = ParseScopedStatement(labels, CHECK_OK); | |
| } | |
| StatementT else_statement = impl()->NullStatement(); | |
| if (Check(Token::ELSE)) { | |
| else_range = SourceRange::ContinuationOf(then_range); | |
| else_statement = ParseScopedStatement(labels, CHECK_OK); | |
| else_range.end = scanner_->location().end_pos; | |
| } else { | |
| else_statement = factory()->NewEmptyStatement(kNoSourcePosition); | |
| } | |
| StatementT stmt = | |
| factory()->NewIfStatement(condition, then_statement, else_statement, pos); | |
| impl()->RecordIfStatementSourceRange(stmt, then_range, else_range); | |
| return stmt; | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseContinueStatement( | |
| bool* ok) { | |
| // ContinueStatement :: | |
| // 'continue' Identifier? ';' | |
| int pos = peek_position(); | |
| Expect(Token::CONTINUE, CHECK_OK); | |
| IdentifierT label = impl()->NullIdentifier(); | |
| Token::Value tok = peek(); | |
| if (!scanner()->HasLineTerminatorBeforeNext() && tok != Token::SEMICOLON && | |
| tok != Token::RBRACE && tok != Token::EOS) { | |
| // ECMA allows "eval" or "arguments" as labels even in strict mode. | |
| label = ParseIdentifier(kAllowRestrictedIdentifiers, CHECK_OK); | |
| } | |
| typename Types::IterationStatement target = | |
| impl()->LookupContinueTarget(label, CHECK_OK); | |
| if (impl()->IsNull(target)) { | |
| // Illegal continue statement. | |
| MessageTemplate::Template message = MessageTemplate::kIllegalContinue; | |
| typename Types::BreakableStatement breakable_target = | |
| impl()->LookupBreakTarget(label, CHECK_OK); | |
| if (impl()->IsNull(label)) { | |
| message = MessageTemplate::kNoIterationStatement; | |
| } else if (impl()->IsNull(breakable_target)) { | |
| message = MessageTemplate::kUnknownLabel; | |
| } | |
| ReportMessage(message, label); | |
| *ok = false; | |
| return impl()->NullStatement(); | |
| } | |
| ExpectSemicolon(CHECK_OK); | |
| StatementT stmt = factory()->NewContinueStatement(target, pos); | |
| impl()->RecordJumpStatementSourceRange(stmt, scanner_->location().end_pos); | |
| return stmt; | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseBreakStatement( | |
| ZonePtrList<const AstRawString>* labels, bool* ok) { | |
| // BreakStatement :: | |
| // 'break' Identifier? ';' | |
| int pos = peek_position(); | |
| Expect(Token::BREAK, CHECK_OK); | |
| IdentifierT label = impl()->NullIdentifier(); | |
| Token::Value tok = peek(); | |
| if (!scanner()->HasLineTerminatorBeforeNext() && tok != Token::SEMICOLON && | |
| tok != Token::RBRACE && tok != Token::EOS) { | |
| // ECMA allows "eval" or "arguments" as labels even in strict mode. | |
| label = ParseIdentifier(kAllowRestrictedIdentifiers, CHECK_OK); | |
| } | |
| // Parse labeled break statements that target themselves into | |
| // empty statements, e.g. 'l1: l2: l3: break l2;' | |
| if (!impl()->IsNull(label) && impl()->ContainsLabel(labels, label)) { | |
| ExpectSemicolon(CHECK_OK); | |
| return factory()->NewEmptyStatement(pos); | |
| } | |
| typename Types::BreakableStatement target = | |
| impl()->LookupBreakTarget(label, CHECK_OK); | |
| if (impl()->IsNull(target)) { | |
| // Illegal break statement. | |
| MessageTemplate::Template message = MessageTemplate::kIllegalBreak; | |
| if (!impl()->IsNull(label)) { | |
| message = MessageTemplate::kUnknownLabel; | |
| } | |
| ReportMessage(message, label); | |
| *ok = false; | |
| return impl()->NullStatement(); | |
| } | |
| ExpectSemicolon(CHECK_OK); | |
| StatementT stmt = factory()->NewBreakStatement(target, pos); | |
| impl()->RecordJumpStatementSourceRange(stmt, scanner_->location().end_pos); | |
| return stmt; | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseReturnStatement( | |
| bool* ok) { | |
| // ReturnStatement :: | |
| // 'return' [no line terminator] Expression? ';' | |
| // Consume the return token. It is necessary to do that before | |
| // reporting any errors on it, because of the way errors are | |
| // reported (underlining). | |
| Expect(Token::RETURN, CHECK_OK); | |
| Scanner::Location loc = scanner()->location(); | |
| switch (GetDeclarationScope()->scope_type()) { | |
| case SCRIPT_SCOPE: | |
| case EVAL_SCOPE: | |
| case MODULE_SCOPE: | |
| impl()->ReportMessageAt(loc, MessageTemplate::kIllegalReturn); | |
| *ok = false; | |
| return impl()->NullStatement(); | |
| default: | |
| break; | |
| } | |
| Token::Value tok = peek(); | |
| ExpressionT return_value = impl()->NullExpression(); | |
| if (scanner()->HasLineTerminatorBeforeNext() || tok == Token::SEMICOLON || | |
| tok == Token::RBRACE || tok == Token::EOS) { | |
| if (IsDerivedConstructor(function_state_->kind())) { | |
| return_value = impl()->ThisExpression(loc.beg_pos); | |
| } | |
| } else { | |
| return_value = ParseExpression(true, CHECK_OK); | |
| } | |
| ExpectSemicolon(CHECK_OK); | |
| return_value = impl()->RewriteReturn(return_value, loc.beg_pos); | |
| int continuation_pos = scanner_->location().end_pos; | |
| StatementT stmt = | |
| BuildReturnStatement(return_value, loc.beg_pos, continuation_pos); | |
| impl()->RecordJumpStatementSourceRange(stmt, scanner_->location().end_pos); | |
| return stmt; | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseWithStatement( | |
| ZonePtrList<const AstRawString>* labels, bool* ok) { | |
| // WithStatement :: | |
| // 'with' '(' Expression ')' Statement | |
| Expect(Token::WITH, CHECK_OK); | |
| int pos = position(); | |
| if (is_strict(language_mode())) { | |
| ReportMessage(MessageTemplate::kStrictWith); | |
| *ok = false; | |
| return impl()->NullStatement(); | |
| } | |
| Expect(Token::LPAREN, CHECK_OK); | |
| ExpressionT expr = ParseExpression(true, CHECK_OK); | |
| Expect(Token::RPAREN, CHECK_OK); | |
| Scope* with_scope = NewScope(WITH_SCOPE); | |
| StatementT body = impl()->NullStatement(); | |
| { | |
| BlockState block_state(&scope_, with_scope); | |
| with_scope->set_start_position(scanner()->peek_location().beg_pos); | |
| body = ParseStatement(labels, nullptr, CHECK_OK); | |
| with_scope->set_end_position(scanner()->location().end_pos); | |
| } | |
| return factory()->NewWithStatement(with_scope, expr, body, pos); | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseDoWhileStatement( | |
| ZonePtrList<const AstRawString>* labels, | |
| ZonePtrList<const AstRawString>* own_labels, bool* ok) { | |
| // DoStatement :: | |
| // 'do' Statement 'while' '(' Expression ')' ';' | |
| auto loop = | |
| factory()->NewDoWhileStatement(labels, own_labels, peek_position()); | |
| typename Types::Target target(this, loop); | |
| SourceRange body_range; | |
| StatementT body = impl()->NullStatement(); | |
| Expect(Token::DO, CHECK_OK); | |
| { | |
| SourceRangeScope range_scope(scanner(), &body_range); | |
| body = ParseStatement(nullptr, nullptr, CHECK_OK); | |
| } | |
| Expect(Token::WHILE, CHECK_OK); | |
| Expect(Token::LPAREN, CHECK_OK); | |
| ExpressionT cond = ParseExpression(true, CHECK_OK); | |
| Expect(Token::RPAREN, CHECK_OK); | |
| // Allow do-statements to be terminated with and without | |
| // semi-colons. This allows code such as 'do;while(0)return' to | |
| // parse, which would not be the case if we had used the | |
| // ExpectSemicolon() functionality here. | |
| Check(Token::SEMICOLON); | |
| loop->Initialize(cond, body); | |
| impl()->RecordIterationStatementSourceRange(loop, body_range); | |
| return loop; | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseWhileStatement( | |
| ZonePtrList<const AstRawString>* labels, | |
| ZonePtrList<const AstRawString>* own_labels, bool* ok) { | |
| // WhileStatement :: | |
| // 'while' '(' Expression ')' Statement | |
| auto loop = factory()->NewWhileStatement(labels, own_labels, peek_position()); | |
| typename Types::Target target(this, loop); | |
| SourceRange body_range; | |
| StatementT body = impl()->NullStatement(); | |
| Expect(Token::WHILE, CHECK_OK); | |
| Expect(Token::LPAREN, CHECK_OK); | |
| ExpressionT cond = ParseExpression(true, CHECK_OK); | |
| Expect(Token::RPAREN, CHECK_OK); | |
| { | |
| SourceRangeScope range_scope(scanner(), &body_range); | |
| body = ParseStatement(nullptr, nullptr, CHECK_OK); | |
| } | |
| loop->Initialize(cond, body); | |
| impl()->RecordIterationStatementSourceRange(loop, body_range); | |
| return loop; | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseThrowStatement( | |
| bool* ok) { | |
| // ThrowStatement :: | |
| // 'throw' Expression ';' | |
| Expect(Token::THROW, CHECK_OK); | |
| int pos = position(); | |
| if (scanner()->HasLineTerminatorBeforeNext()) { | |
| ReportMessage(MessageTemplate::kNewlineAfterThrow); | |
| *ok = false; | |
| return impl()->NullStatement(); | |
| } | |
| ExpressionT exception = ParseExpression(true, CHECK_OK); | |
| ExpectSemicolon(CHECK_OK); | |
| StatementT stmt = impl()->NewThrowStatement(exception, pos); | |
| impl()->RecordThrowSourceRange(stmt, scanner_->location().end_pos); | |
| return stmt; | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseSwitchStatement( | |
| ZonePtrList<const AstRawString>* labels, bool* ok) { | |
| // SwitchStatement :: | |
| // 'switch' '(' Expression ')' '{' CaseClause* '}' | |
| // CaseClause :: | |
| // 'case' Expression ':' StatementList | |
| // 'default' ':' StatementList | |
| int switch_pos = peek_position(); | |
| Expect(Token::SWITCH, CHECK_OK); | |
| Expect(Token::LPAREN, CHECK_OK); | |
| ExpressionT tag = ParseExpression(true, CHECK_OK); | |
| Expect(Token::RPAREN, CHECK_OK); | |
| auto switch_statement = | |
| factory()->NewSwitchStatement(labels, tag, switch_pos); | |
| { | |
| BlockState cases_block_state(zone(), &scope_); | |
| scope()->set_start_position(switch_pos); | |
| scope()->SetNonlinear(); | |
| typename Types::Target target(this, switch_statement); | |
| bool default_seen = false; | |
| Expect(Token::LBRACE, CHECK_OK); | |
| while (peek() != Token::RBRACE) { | |
| // An empty label indicates the default case. | |
| ExpressionT label = impl()->NullExpression(); | |
| SourceRange clause_range; | |
| SourceRangeScope range_scope(scanner(), &clause_range); | |
| if (Check(Token::CASE)) { | |
| label = ParseExpression(true, CHECK_OK); | |
| } else { | |
| Expect(Token::DEFAULT, CHECK_OK); | |
| if (default_seen) { | |
| ReportMessage(MessageTemplate::kMultipleDefaultsInSwitch); | |
| *ok = false; | |
| return impl()->NullStatement(); | |
| } | |
| default_seen = true; | |
| } | |
| Expect(Token::COLON, CHECK_OK); | |
| StatementListT statements = impl()->NewStatementList(5); | |
| while (peek() != Token::CASE && peek() != Token::DEFAULT && | |
| peek() != Token::RBRACE) { | |
| StatementT stat = ParseStatementListItem(CHECK_OK); | |
| statements->Add(stat, zone()); | |
| } | |
| auto clause = factory()->NewCaseClause(label, statements); | |
| impl()->RecordCaseClauseSourceRange(clause, range_scope.Finalize()); | |
| switch_statement->cases()->Add(clause, zone()); | |
| } | |
| Expect(Token::RBRACE, CHECK_OK); | |
| int end_position = scanner()->location().end_pos; | |
| scope()->set_end_position(end_position); | |
| impl()->RecordSwitchStatementSourceRange(switch_statement, end_position); | |
| Scope* switch_scope = scope()->FinalizeBlockScope(); | |
| if (switch_scope != nullptr) { | |
| return impl()->RewriteSwitchStatement(switch_statement, switch_scope); | |
| } | |
| return switch_statement; | |
| } | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseTryStatement( | |
| bool* ok) { | |
| // TryStatement :: | |
| // 'try' Block Catch | |
| // 'try' Block Finally | |
| // 'try' Block Catch Finally | |
| // | |
| // Catch :: | |
| // 'catch' '(' Identifier ')' Block | |
| // | |
| // Finally :: | |
| // 'finally' Block | |
| Expect(Token::TRY, CHECK_OK); | |
| int pos = position(); | |
| BlockT try_block = ParseBlock(nullptr, CHECK_OK); | |
| CatchInfo catch_info(this); | |
| if (peek() != Token::CATCH && peek() != Token::FINALLY) { | |
| ReportMessage(MessageTemplate::kNoCatchOrFinally); | |
| *ok = false; | |
| return impl()->NullStatement(); | |
| } | |
| SourceRange catch_range, finally_range; | |
| BlockT catch_block = impl()->NullStatement(); | |
| { | |
| SourceRangeScope catch_range_scope(scanner(), &catch_range); | |
| if (Check(Token::CATCH)) { | |
| bool has_binding; | |
| has_binding = Check(Token::LPAREN); | |
| if (has_binding) { | |
| catch_info.scope = NewScope(CATCH_SCOPE); | |
| catch_info.scope->set_start_position(scanner()->location().beg_pos); | |
| { | |
| BlockState catch_block_state(&scope_, catch_info.scope); | |
| catch_block = factory()->NewBlock(16, false); | |
| // Create a block scope to hold any lexical declarations created | |
| // as part of destructuring the catch parameter. | |
| { | |
| BlockState catch_variable_block_state(zone(), &scope_); | |
| scope()->set_start_position(scanner()->location().beg_pos); | |
| // This does not simply call ParsePrimaryExpression to avoid | |
| // ExpressionFromIdentifier from being called in the first | |
| // branch, which would introduce an unresolved symbol and mess | |
| // with arrow function names. | |
| if (peek_any_identifier()) { | |
| catch_info.name = | |
| ParseIdentifier(kDontAllowRestrictedIdentifiers, CHECK_OK); | |
| } else { | |
| ExpressionClassifier pattern_classifier(this); | |
| catch_info.pattern = ParsePrimaryExpression(CHECK_OK); | |
| ValidateBindingPattern(CHECK_OK); | |
| } | |
| Expect(Token::RPAREN, CHECK_OK); | |
| impl()->RewriteCatchPattern(&catch_info, CHECK_OK); | |
| if (!impl()->IsNull(catch_info.init_block)) { | |
| catch_block->statements()->Add(catch_info.init_block, zone()); | |
| } | |
| catch_info.inner_block = ParseBlock(nullptr, CHECK_OK); | |
| catch_block->statements()->Add(catch_info.inner_block, zone()); | |
| impl()->ValidateCatchBlock(catch_info, CHECK_OK); | |
| scope()->set_end_position(scanner()->location().end_pos); | |
| catch_block->set_scope(scope()->FinalizeBlockScope()); | |
| } | |
| } | |
| catch_info.scope->set_end_position(scanner()->location().end_pos); | |
| } else { | |
| catch_block = ParseBlock(nullptr, CHECK_OK); | |
| } | |
| } | |
| } | |
| BlockT finally_block = impl()->NullStatement(); | |
| DCHECK(peek() == Token::FINALLY || !impl()->IsNull(catch_block)); | |
| { | |
| SourceRangeScope range_scope(scanner(), &finally_range); | |
| if (Check(Token::FINALLY)) { | |
| finally_block = ParseBlock(nullptr, CHECK_OK); | |
| } | |
| } | |
| return impl()->RewriteTryStatement(try_block, catch_block, catch_range, | |
| finally_block, finally_range, catch_info, | |
| pos); | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForStatement( | |
| ZonePtrList<const AstRawString>* labels, | |
| ZonePtrList<const AstRawString>* own_labels, bool* ok) { | |
| // Either a standard for loop | |
| // for (<init>; <cond>; <next>) { ... } | |
| // or a for-each loop | |
| // for (<each> of|in <iterable>) { ... } | |
| // | |
| // We parse a declaration/expression after the 'for (' and then read the first | |
| // expression/declaration before we know if this is a for or a for-each. | |
| int stmt_pos = peek_position(); | |
| ForInfo for_info(this); | |
| Expect(Token::FOR, CHECK_OK); | |
| Expect(Token::LPAREN, CHECK_OK); | |
| if (peek() == Token::CONST || (peek() == Token::LET && IsNextLetKeyword())) { | |
| // The initializer contains lexical declarations, | |
| // so create an in-between scope. | |
| BlockState for_state(zone(), &scope_); | |
| scope()->set_start_position(scanner()->location().beg_pos); | |
| // Also record whether inner functions or evals are found inside | |
| // this loop, as this information is used to simplify the desugaring | |
| // if none are found. | |
| typename FunctionState::FunctionOrEvalRecordingScope recording_scope( | |
| function_state_); | |
| // Create an inner block scope which will be the parent scope of scopes | |
| // possibly created by ParseVariableDeclarations. | |
| Scope* inner_block_scope = NewScope(BLOCK_SCOPE); | |
| { | |
| BlockState inner_state(&scope_, inner_block_scope); | |
| ParseVariableDeclarations(kForStatement, &for_info.parsing_result, | |
| nullptr, CHECK_OK); | |
| } | |
| DCHECK(IsLexicalVariableMode(for_info.parsing_result.descriptor.mode)); | |
| for_info.position = scanner()->location().beg_pos; | |
| if (CheckInOrOf(&for_info.mode)) { | |
| scope()->set_is_hidden(); | |
| return ParseForEachStatementWithDeclarations( | |
| stmt_pos, &for_info, labels, own_labels, inner_block_scope, ok); | |
| } | |
| Expect(Token::SEMICOLON, CHECK_OK); | |
| StatementT init = impl()->BuildInitializationBlock( | |
| &for_info.parsing_result, &for_info.bound_names, CHECK_OK); | |
| Scope* finalized = inner_block_scope->FinalizeBlockScope(); | |
| // No variable declarations will have been created in inner_block_scope. | |
| DCHECK_NULL(finalized); | |
| USE(finalized); | |
| return ParseStandardForLoopWithLexicalDeclarations( | |
| stmt_pos, init, &for_info, labels, own_labels, ok); | |
| } | |
| StatementT init = impl()->NullStatement(); | |
| if (peek() == Token::VAR) { | |
| ParseVariableDeclarations(kForStatement, &for_info.parsing_result, nullptr, | |
| CHECK_OK); | |
| DCHECK_EQ(for_info.parsing_result.descriptor.mode, VariableMode::kVar); | |
| for_info.position = scanner()->location().beg_pos; | |
| if (CheckInOrOf(&for_info.mode)) { | |
| return ParseForEachStatementWithDeclarations(stmt_pos, &for_info, labels, | |
| own_labels, nullptr, ok); | |
| } | |
| init = impl()->BuildInitializationBlock(&for_info.parsing_result, nullptr, | |
| CHECK_OK); | |
| } else if (peek() != Token::SEMICOLON) { | |
| // The initializer does not contain declarations. | |
| int lhs_beg_pos = peek_position(); | |
| ExpressionClassifier classifier(this); | |
| ExpressionT expression = ParseExpressionCoverGrammar(false, CHECK_OK); | |
| int lhs_end_pos = scanner()->location().end_pos; | |
| bool is_for_each = CheckInOrOf(&for_info.mode); | |
| bool is_destructuring = is_for_each && (expression->IsArrayLiteral() || | |
| expression->IsObjectLiteral()); | |
| if (is_destructuring) { | |
| ValidateAssignmentPattern(CHECK_OK); | |
| } else { | |
| ValidateExpression(CHECK_OK); | |
| } | |
| if (is_for_each) { | |
| return ParseForEachStatementWithoutDeclarations( | |
| stmt_pos, expression, lhs_beg_pos, lhs_end_pos, &for_info, labels, | |
| own_labels, ok); | |
| } | |
| // Initializer is just an expression. | |
| init = factory()->NewExpressionStatement(expression, lhs_beg_pos); | |
| } | |
| Expect(Token::SEMICOLON, CHECK_OK); | |
| // Standard 'for' loop, we have parsed the initializer at this point. | |
| ExpressionT cond = impl()->NullExpression(); | |
| StatementT next = impl()->NullStatement(); | |
| StatementT body = impl()->NullStatement(); | |
| ForStatementT loop = ParseStandardForLoop(stmt_pos, labels, own_labels, &cond, | |
| &next, &body, CHECK_OK); | |
| loop->Initialize(init, cond, next, body); | |
| return loop; | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::StatementT | |
| ParserBase<Impl>::ParseForEachStatementWithDeclarations( | |
| int stmt_pos, ForInfo* for_info, ZonePtrList<const AstRawString>* labels, | |
| ZonePtrList<const AstRawString>* own_labels, Scope* inner_block_scope, | |
| bool* ok) { | |
| // Just one declaration followed by in/of. | |
| if (for_info->parsing_result.declarations.size() != 1) { | |
| impl()->ReportMessageAt(for_info->parsing_result.bindings_loc, | |
| MessageTemplate::kForInOfLoopMultiBindings, | |
| ForEachStatement::VisitModeString(for_info->mode)); | |
| *ok = false; | |
| return impl()->NullStatement(); | |
| } | |
| if (for_info->parsing_result.first_initializer_loc.IsValid() && | |
| (is_strict(language_mode()) || | |
| for_info->mode == ForEachStatement::ITERATE || | |
| IsLexicalVariableMode(for_info->parsing_result.descriptor.mode) || | |
| !impl()->IsIdentifier( | |
| for_info->parsing_result.declarations[0].pattern))) { | |
| impl()->ReportMessageAt(for_info->parsing_result.first_initializer_loc, | |
| MessageTemplate::kForInOfLoopInitializer, | |
| ForEachStatement::VisitModeString(for_info->mode)); | |
| *ok = false; | |
| return impl()->NullStatement(); | |
| } | |
| // Reset the declaration_kind to ensure proper processing during declaration. | |
| for_info->parsing_result.descriptor.declaration_kind = | |
| DeclarationDescriptor::FOR_EACH; | |
| BlockT init_block = impl()->RewriteForVarInLegacy(*for_info); | |
| auto loop = factory()->NewForEachStatement(for_info->mode, labels, own_labels, | |
| stmt_pos); | |
| typename Types::Target target(this, loop); | |
| ExpressionT enumerable = impl()->NullExpression(); | |
| if (for_info->mode == ForEachStatement::ITERATE) { | |
| ExpressionClassifier classifier(this); | |
| enumerable = ParseAssignmentExpression(true, CHECK_OK); | |
| ValidateExpression(CHECK_OK); | |
| } else { | |
| enumerable = ParseExpression(true, CHECK_OK); | |
| } | |
| Expect(Token::RPAREN, CHECK_OK); | |
| Scope* for_scope = nullptr; | |
| if (inner_block_scope != nullptr) { | |
| for_scope = inner_block_scope->outer_scope(); | |
| DCHECK(for_scope == scope()); | |
| inner_block_scope->set_start_position(scanner()->location().beg_pos); | |
| } | |
| ExpressionT each_variable = impl()->NullExpression(); | |
| BlockT body_block = impl()->NullStatement(); | |
| { | |
| BlockState block_state( | |
| &scope_, inner_block_scope != nullptr ? inner_block_scope : scope_); | |
| SourceRange body_range; | |
| SourceRangeScope range_scope(scanner(), &body_range); | |
| StatementT body = ParseStatement(nullptr, nullptr, CHECK_OK); | |
| impl()->RecordIterationStatementSourceRange(loop, range_scope.Finalize()); | |
| impl()->DesugarBindingInForEachStatement(for_info, &body_block, | |
| &each_variable, CHECK_OK); | |
| body_block->statements()->Add(body, zone()); | |
| if (inner_block_scope != nullptr) { | |
| inner_block_scope->set_end_position(scanner()->location().end_pos); | |
| body_block->set_scope(inner_block_scope->FinalizeBlockScope()); | |
| } | |
| } | |
| StatementT final_loop = impl()->InitializeForEachStatement( | |
| loop, each_variable, enumerable, body_block); | |
| init_block = impl()->CreateForEachStatementTDZ(init_block, *for_info, ok); | |
| if (for_scope != nullptr) { | |
| for_scope->set_end_position(scanner()->location().end_pos); | |
| for_scope = for_scope->FinalizeBlockScope(); | |
| } | |
| // Parsed for-in loop w/ variable declarations. | |
| if (!impl()->IsNull(init_block)) { | |
| init_block->statements()->Add(final_loop, zone()); | |
| init_block->set_scope(for_scope); | |
| return init_block; | |
| } | |
| DCHECK_NULL(for_scope); | |
| return final_loop; | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::StatementT | |
| ParserBase<Impl>::ParseForEachStatementWithoutDeclarations( | |
| int stmt_pos, ExpressionT expression, int lhs_beg_pos, int lhs_end_pos, | |
| ForInfo* for_info, ZonePtrList<const AstRawString>* labels, | |
| ZonePtrList<const AstRawString>* own_labels, bool* ok) { | |
| // Initializer is reference followed by in/of. | |
| if (!expression->IsArrayLiteral() && !expression->IsObjectLiteral()) { | |
| expression = CheckAndRewriteReferenceExpression( | |
| expression, lhs_beg_pos, lhs_end_pos, MessageTemplate::kInvalidLhsInFor, | |
| kSyntaxError, CHECK_OK); | |
| } | |
| auto loop = factory()->NewForEachStatement(for_info->mode, labels, own_labels, | |
| stmt_pos); | |
| typename Types::Target target(this, loop); | |
| ExpressionT enumerable = impl()->NullExpression(); | |
| if (for_info->mode == ForEachStatement::ITERATE) { | |
| ExpressionClassifier classifier(this); | |
| enumerable = ParseAssignmentExpression(true, CHECK_OK); | |
| ValidateExpression(CHECK_OK); | |
| } else { | |
| enumerable = ParseExpression(true, CHECK_OK); | |
| } | |
| Expect(Token::RPAREN, CHECK_OK); | |
| StatementT body = impl()->NullStatement(); | |
| { | |
| SourceRange body_range; | |
| SourceRangeScope range_scope(scanner(), &body_range); | |
| body = ParseStatement(nullptr, nullptr, CHECK_OK); | |
| impl()->RecordIterationStatementSourceRange(loop, range_scope.Finalize()); | |
| } | |
| return impl()->InitializeForEachStatement(loop, expression, enumerable, body); | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::StatementT | |
| ParserBase<Impl>::ParseStandardForLoopWithLexicalDeclarations( | |
| int stmt_pos, StatementT init, ForInfo* for_info, | |
| ZonePtrList<const AstRawString>* labels, | |
| ZonePtrList<const AstRawString>* own_labels, bool* ok) { | |
| // The condition and the next statement of the for loop must be parsed | |
| // in a new scope. | |
| Scope* inner_scope = NewScope(BLOCK_SCOPE); | |
| ForStatementT loop = impl()->NullStatement(); | |
| ExpressionT cond = impl()->NullExpression(); | |
| StatementT next = impl()->NullStatement(); | |
| StatementT body = impl()->NullStatement(); | |
| { | |
| BlockState block_state(&scope_, inner_scope); | |
| scope()->set_start_position(scanner()->location().beg_pos); | |
| loop = ParseStandardForLoop(stmt_pos, labels, own_labels, &cond, &next, | |
| &body, CHECK_OK); | |
| scope()->set_end_position(scanner()->location().end_pos); | |
| } | |
| scope()->set_end_position(scanner()->location().end_pos); | |
| if (for_info->bound_names.length() > 0 && | |
| function_state_->contains_function_or_eval()) { | |
| scope()->set_is_hidden(); | |
| return impl()->DesugarLexicalBindingsInForStatement( | |
| loop, init, cond, next, body, inner_scope, *for_info, ok); | |
| } else { | |
| inner_scope = inner_scope->FinalizeBlockScope(); | |
| DCHECK_NULL(inner_scope); | |
| USE(inner_scope); | |
| } | |
| Scope* for_scope = scope()->FinalizeBlockScope(); | |
| if (for_scope != nullptr) { | |
| // Rewrite a for statement of the form | |
| // for (const x = i; c; n) b | |
| // | |
| // into | |
| // | |
| // { | |
| // const x = i; | |
| // for (; c; n) b | |
| // } | |
| // | |
| DCHECK(!impl()->IsNull(init)); | |
| BlockT block = factory()->NewBlock(2, false); | |
| block->statements()->Add(init, zone()); | |
| block->statements()->Add(loop, zone()); | |
| block->set_scope(for_scope); | |
| loop->Initialize(impl()->NullStatement(), cond, next, body); | |
| return block; | |
| } | |
| loop->Initialize(init, cond, next, body); | |
| return loop; | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::ForStatementT ParserBase<Impl>::ParseStandardForLoop( | |
| int stmt_pos, ZonePtrList<const AstRawString>* labels, | |
| ZonePtrList<const AstRawString>* own_labels, ExpressionT* cond, | |
| StatementT* next, StatementT* body, bool* ok) { | |
| ForStatementT loop = factory()->NewForStatement(labels, own_labels, stmt_pos); | |
| typename Types::Target target(this, loop); | |
| if (peek() != Token::SEMICOLON) { | |
| *cond = ParseExpression(true, CHECK_OK); | |
| } | |
| Expect(Token::SEMICOLON, CHECK_OK); | |
| if (peek() != Token::RPAREN) { | |
| ExpressionT exp = ParseExpression(true, CHECK_OK); | |
| *next = factory()->NewExpressionStatement(exp, exp->position()); | |
| } | |
| Expect(Token::RPAREN, CHECK_OK); | |
| SourceRange body_range; | |
| { | |
| SourceRangeScope range_scope(scanner(), &body_range); | |
| *body = ParseStatement(nullptr, nullptr, CHECK_OK); | |
| } | |
| impl()->RecordIterationStatementSourceRange(loop, body_range); | |
| return loop; | |
| } | |
| template <typename Impl> | |
| void ParserBase<Impl>::MarkLoopVariableAsAssigned( | |
| Scope* scope, Variable* var, | |
| typename DeclarationDescriptor::Kind declaration_kind) { | |
| if (!IsLexicalVariableMode(var->mode()) && | |
| (!scope->is_function_scope() || | |
| declaration_kind == DeclarationDescriptor::FOR_EACH)) { | |
| var->set_maybe_assigned(); | |
| } | |
| } | |
| template <typename Impl> | |
| typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForAwaitStatement( | |
| ZonePtrList<const AstRawString>* labels, | |
| ZonePtrList<const AstRawString>* own_labels, bool* ok) { | |
| // for await '(' ForDeclaration of AssignmentExpression ')' | |
| DCHECK(is_async_function()); | |
| int stmt_pos = peek_position(); | |
| ForInfo for_info(this); | |
| for_info.mode = ForEachStatement::ITERATE; | |
| // Create an in-between scope for let-bound iteration variables. | |
| BlockState for_state(zone(), &scope_); | |
| Expect(Token::FOR, CHECK_OK); | |
| Expect(Token::AWAIT, CHECK_OK); | |
| Expect(Token::LPAREN, CHECK_OK); | |
| scope()->set_start_position(scanner()->location().beg_pos); | |
| scope()->set_is_hidden(); | |
| auto loop = factory()->NewForOfStatement(labels, own_labels, stmt_pos); | |
| typename Types::Target target(this, loop); | |
| ExpressionT each_variable = impl()->NullExpression(); | |
| bool has_declarations = false; | |
| Scope* inner_block_scope = NewScope(BLOCK_SCOPE); | |
| if (peek() == Token::VAR || peek() == Token::CONST || | |
| (peek() == Token::LET && IsNextLetKeyword())) { | |
| // The initializer contains declarations | |
| // 'for' 'await' '(' ForDeclaration 'of' AssignmentExpression ')' | |
| // Statement | |
| // 'for' 'await' '(' 'var' ForBinding 'of' AssignmentExpression ')' | |
| // Statement | |
| has_declarations = true; | |
| { | |
| BlockState inner_state(&scope_, inner_block_scope); | |
| ParseVariableDeclarations(kForStatement, &for_info.parsing_result, | |
| nullptr, CHECK_OK); | |
| } | |
| for_info.position = scanner()->location().beg_pos; | |
| // Only a single declaration is allowed in for-await-of loops | |
| if (for_info.parsing_result.declarations.size() != 1) { | |
| impl()->ReportMessageAt(for_info.parsing_result.bindings_loc, | |
| MessageTemplate::kForInOfLoopMultiBindings, | |
| "for-await-of"); | |
| *ok = false; | |
| return impl()->NullStatement(); | |
| } | |
| // for-await-of's declarations do not permit initializers. | |
| if (for_info.parsing_result.first_initializer_loc.IsValid()) { | |
| impl()->ReportMessageAt(for_info.parsing_result.first_initializer_loc, | |
| MessageTemplate::kForInOfLoopInitializer, | |
| "for-await-of"); | |
| *ok = false; | |
| return impl()->NullStatement(); | |
| } | |
| } else { | |
| // The initializer does not contain declarations. | |
| // 'for' 'await' '(' LeftHandSideExpression 'of' AssignmentExpression ')' | |
| // Statement | |
| int lhs_beg_pos = peek_position(); | |
| BlockState inner_state(&scope_, inner_block_scope); | |
| ExpressionClassifier classifier(this); | |
| ExpressionT lhs = each_variable = ParseLeftHandSideExpression(CHECK_OK); | |
| int lhs_end_pos = scanner()->location().end_pos; | |
| if (lhs->IsArrayLiteral() || lhs->IsObjectLiteral()) { | |
| ValidateAssignmentPattern(CHECK_OK); | |
| } else { | |
| ValidateExpression(CHECK_OK); | |
| each_variable = CheckAndRewriteReferenceExpression( | |
| lhs, lhs_beg_pos, lhs_end_pos, MessageTemplate::kInvalidLhsInFor, | |
| kSyntaxError, CHECK_OK); | |
| } | |
| } | |
| ExpectContextualKeyword(Token::OF, CHECK_OK); | |
| int each_keyword_pos = scanner()->location().beg_pos; | |
| const bool kAllowIn = true; | |
| ExpressionT iterable = impl()->NullExpression(); | |
| { | |
| ExpressionClassifier classifier(this); | |
| iterable = ParseAssignmentExpression(kAllowIn, CHECK_OK); | |
| ValidateExpression(CHECK_OK); | |
| } | |
| Expect(Token::RPAREN, CHECK_OK); | |
| StatementT body = impl()->NullStatement(); | |
| { | |
| BlockState block_state(&scope_, inner_block_scope); | |
| scope()->set_start_position(scanner()->location().beg_pos); | |
| SourceRange body_range; | |
| SourceRangeScope range_scope(scanner(), &body_range); | |
| body = ParseStatement(nullptr, nullptr, CHECK_OK); | |
| scope()->set_end_position(scanner()->location().end_pos); | |
| impl()->RecordIterationStatementSourceRange(loop, range_scope.Finalize()); | |
| if (has_declarations) { | |
| BlockT body_block = impl()->NullStatement(); | |
| impl()->DesugarBindingInForEachStatement(&for_info, &body_block, | |
| &each_variable, CHECK_OK); | |
| body_block->statements()->Add(body, zone()); | |
| body_block->set_scope(scope()->FinalizeBlockScope()); | |
| body = body_block; | |
| } else { | |
| Scope* block_scope = scope()->FinalizeBlockScope(); | |
| DCHECK_NULL(block_scope); | |
| USE(block_scope); | |
| } | |
| } | |
| const bool finalize = true; | |
| StatementT final_loop = impl()->InitializeForOfStatement( | |
| loop, each_variable, iterable, body, finalize, IteratorType::kAsync, | |
| each_keyword_pos); | |
| if (!has_declarations) { | |
| Scope* for_scope = scope()->FinalizeBlockScope(); | |
| DCHECK_NULL(for_scope); | |
| USE(for_scope); | |
| return final_loop; | |
| } | |
| BlockT init_block = | |
| impl()->CreateForEachStatementTDZ(impl()->NullStatement(), for_info, ok); | |
| scope()->set_end_position(scanner()->location().end_pos); | |
| Scope* for_scope = scope()->FinalizeBlockScope(); | |
| // Parsed for-in loop w/ variable declarations. | |
| if (!impl()->IsNull(init_block)) { | |
| init_block->statements()->Add(final_loop, zone()); | |
| init_block->set_scope(for_scope); | |
| return init_block; | |
| } | |
| DCHECK_NULL(for_scope); | |
| return final_loop; | |
| } | |
| template <typename Impl> | |
| void ParserBase<Impl>::ObjectLiteralChecker::CheckDuplicateProto( | |
| Token::Value property) { | |
| if (property == Token::SMI || property == Token::NUMBER) return; | |
| if (IsProto()) { | |
| if (has_seen_proto_) { | |
| this->parser()->classifier()->RecordExpressionError( | |
| this->scanner()->location(), MessageTemplate::kDuplicateProto); | |
| return; | |
| } | |
| has_seen_proto_ = true; | |
| } | |
| } | |
| template <typename Impl> | |
| void ParserBase<Impl>::ClassLiteralChecker::CheckClassMethodName( | |
| Token::Value property, PropertyKind type, bool is_generator, bool is_async, | |
| bool is_static, bool* ok) { | |
| DCHECK(type == PropertyKind::kMethodProperty || | |
| type == PropertyKind::kAccessorProperty); | |
| if (property == Token::SMI || property == Token::NUMBER) return; | |
| if (is_static) { | |
| if (IsPrototype()) { | |
| this->parser()->ReportMessage(MessageTemplate::kStaticPrototype); | |
| *ok = false; | |
| return; | |
| } | |
| } else if (IsConstructor()) { | |
| if (is_generator || is_async || type == PropertyKind::kAccessorProperty) { | |
| MessageTemplate::Template msg = | |
| is_generator ? MessageTemplate::kConstructorIsGenerator | |
| : is_async ? MessageTemplate::kConstructorIsAsync | |
| : MessageTemplate::kConstructorIsAccessor; | |
| this->parser()->ReportMessage(msg); | |
| *ok = false; | |
| return; | |
| } | |
| if (has_seen_constructor_) { | |
| this->parser()->ReportMessage(MessageTemplate::kDuplicateConstructor); | |
| *ok = false; | |
| return; | |
| } | |
| has_seen_constructor_ = true; | |
| return; | |
| } | |
| } | |
| template <typename Impl> | |
| void ParserBase<Impl>::ClassLiteralChecker::CheckClassFieldName(bool is_static, | |
| bool* ok) { | |
| if (is_static && IsPrototype()) { | |
| this->parser()->ReportMessage(MessageTemplate::kStaticPrototype); | |
| *ok = false; | |
| return; | |
| } | |
| if (IsConstructor() || IsPrivateConstructor()) { | |
| this->parser()->ReportMessage(MessageTemplate::kConstructorClassField); | |
| *ok = false; | |
| return; | |
| } | |
| } | |
| #undef CHECK_OK | |
| #undef CHECK_OK_CUSTOM | |
| #undef CHECK_OK_VOID | |
| } // namespace internal | |
| } // namespace v8 | |
| #endif // V8_PARSING_PARSER_BASE_H_ |