| // 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/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" |
| |
| 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_function_sent_(false), |
| allow_harmony_public_fields_(false), |
| allow_harmony_static_fields_(false), |
| allow_harmony_dynamic_import_(false), |
| allow_harmony_import_meta_(false), |
| allow_harmony_optional_catch_binding_(false), |
| allow_harmony_private_fields_(false) {} |
| |
| #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_function_sent); |
| ALLOW_ACCESSORS(harmony_public_fields); |
| ALLOW_ACCESSORS(harmony_static_fields); |
| ALLOW_ACCESSORS(harmony_dynamic_import); |
| ALLOW_ACCESSORS(harmony_import_meta); |
| ALLOW_ACCESSORS(harmony_optional_catch_binding); |
| |
| #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_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_; } |
| |
| 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.length(), pos); |
| for (int i = pos; i < outer_assignments.length(); ++i) { |
| auto expr = outer_assignments[i]; |
| expr->set_scope(scope_); |
| destructuring_assignments_to_rewrite_.Add(expr, scope_->zone()); |
| } |
| outer_function_state_->RewindDestructuringAssignments(pos); |
| } |
| |
| const ZoneList<RewritableExpressionT>& |
| destructuring_assignments_to_rewrite() const { |
| return destructuring_assignments_to_rewrite_; |
| } |
| |
| ZoneList<typename ExpressionClassifier::Error>* GetReportedErrorList() { |
| return &reported_errors_; |
| } |
| |
| ZoneList<RewritableExpressionT>* non_patterns_to_rewrite() { |
| return &non_patterns_to_rewrite_; |
| } |
| |
| 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_.Add(expr, scope_->zone()); |
| } |
| |
| // Properties count estimation. |
| int expected_property_count_; |
| |
| FunctionState** function_state_stack_; |
| FunctionState* outer_function_state_; |
| DeclarationScope* scope_; |
| |
| ZoneList<RewritableExpressionT> destructuring_assignments_to_rewrite_; |
| ZoneList<RewritableExpressionT> non_patterns_to_rewrite_; |
| |
| ZoneList<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; |
| ZoneList<const AstRawString*> bound_names; |
| }; |
| |
| struct ForInfo { |
| public: |
| explicit ForInfo(ParserBase* parser) |
| : bound_names(1, parser->zone()), |
| mode(ForEachStatement::ENUMERATE), |
| position(kNoSourcePosition), |
| parsing_result() {} |
| ZoneList<const AstRawString*> bound_names; |
| ForEachStatement::VisitMode mode; |
| int position; |
| DeclarationParsingResult parsing_result; |
| }; |
| |
| struct ClassInfo { |
| public: |
| explicit ClassInfo(ParserBase* parser) |
| : variable(nullptr), |
| extends(parser->impl()->NullExpression()), |
| properties(parser->impl()->NewClassPropertyList(4)), |
| static_fields(parser->impl()->NewClassPropertyList(4)), |
| instance_fields(parser->impl()->NewClassPropertyList(4)), |
| constructor(parser->impl()->NullExpression()), |
| has_seen_constructor(false), |
| has_name_static_property(false), |
| has_static_computed_names(false), |
| has_static_class_fields(false), |
| has_instance_class_fields(false), |
| is_anonymous(false), |
| static_fields_scope(nullptr), |
| instance_fields_scope(nullptr), |
| computed_field_count(0) {} |
| Variable* variable; |
| ExpressionT extends; |
| typename Types::ClassPropertyList properties; |
| typename Types::ClassPropertyList static_fields; |
| typename Types::ClassPropertyList instance_fields; |
| FunctionLiteralT constructor; |
| |
| // TODO(gsathya): Use a bitfield store all the booleans. |
| bool has_seen_constructor; |
| bool has_name_static_property; |
| bool has_static_computed_names; |
| bool has_static_class_fields; |
| bool has_instance_class_fields; |
| bool is_anonymous; |
| DeclarationScope* static_fields_scope; |
| DeclarationScope* instance_fields_scope; |
| int computed_field_count; |
| }; |
| |
| const AstRawString* ClassFieldVariableName(AstValueFactory* ast_value_factory, |
| int index) { |
| std::string name = ".class-field-" + std::to_string(index); |
| return ast_value_factory->GetOneByteString(name.c_str()); |
| } |
| |
| DeclarationScope* NewScriptScope() const { |
| return new (zone()) DeclarationScope(zone(), ast_value_factory()); |
| } |
| |
| DeclarationScope* NewVarblockScope() const { |
| return new (zone()) DeclarationScope(zone(), scope(), BLOCK_SCOPE); |
| } |
| |
| ModuleScope* NewModuleScope(DeclarationScope* parent) const { |
| return new (zone()) ModuleScope(parent, ast_value_factory()); |
| } |
| |
| DeclarationScope* NewEvalScope(Scope* parent) const { |
| return new (zone()) DeclarationScope(zone(), parent, EVAL_SCOPE); |
| } |
| |
| Scope* NewScope(ScopeType scope_type) const { |
| return NewScopeWithParent(scope(), scope_type); |
| } |
| |
| // This constructor should only be used when absolutely necessary. Most scopes |
| // should automatically use scope() as parent, and be fine with |
| // NewScope(ScopeType) above. |
| Scope* NewScopeWithParent(Scope* parent, ScopeType scope_type) const { |
| // Must always use the specific constructors for the blacklisted scope |
| // types. |
| DCHECK_NE(FUNCTION_SCOPE, scope_type); |
| DCHECK_NE(SCRIPT_SCOPE, scope_type); |
| DCHECK_NE(MODULE_SCOPE, scope_type); |
| DCHECK_NOT_NULL(parent); |
| return new (zone()) Scope(zone(), parent, scope_type); |
| } |
| |
| // Creates a function scope that always allocates in zone(). The function |
| // scope itself is either allocated in zone() or in target_zone if one is |
| // passed in. |
| DeclarationScope* NewFunctionScope(FunctionKind kind, |
| Zone* target_zone = nullptr) const { |
| DCHECK(ast_value_factory()); |
| if (target_zone == nullptr) target_zone = zone(); |
| DeclarationScope* result = new (target_zone) |
| DeclarationScope(zone(), scope(), FUNCTION_SCOPE, kind); |
| |
| // Record presence of an inner function scope |
| function_state_->RecordFunctionOrEvalCall(); |
| |
| // TODO(verwaest): Move into the DeclarationScope constructor. |
| if (!IsArrowFunction(kind)) { |
| result->DeclareDefaultFunctionVariables(ast_value_factory()); |
| } |
| return result; |
| } |
| |
| V8_INLINE DeclarationScope* GetDeclarationScope() const { |
| return scope()->GetDeclarationScope(); |
| } |
| V8_INLINE DeclarationScope* GetClosureScope() const { |
| return scope()->GetClosureScope(); |
| } |
| |
| Scanner* scanner() const { return scanner_; } |
| AstValueFactory* ast_value_factory() const { return ast_value_factory_; } |
| int position() const { return scanner_->location().beg_pos; } |
| int peek_position() const { return scanner_->peek_location().beg_pos; } |
| bool stack_overflow() const { |
| return pending_error_handler()->stack_overflow(); |
| } |
| void set_stack_overflow() { pending_error_handler()->set_stack_overflow(); } |
| int script_id() { return script_id_; } |
| void set_script_id(int id) { script_id_ = id; } |
| |
| 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; |
| } |
| |
| INLINE(Token::Value PeekAhead()) { |
| if (stack_overflow()) return Token::ILLEGAL; |
| return scanner()->PeekAhead(); |
| } |
| |
| INLINE(Token::Value Next()) { |
| if (stack_overflow()) return Token::ILLEGAL; |
| { |
| if (GetCurrentStackPosition() < stack_limit_) { |
| // Any further calls to Next or peek will return the illegal token. |
| // The current call must return the next token, which might already |
| // have been peek'ed. |
| set_stack_overflow(); |
| } |
| } |
| return scanner()->Next(); |
| } |
| |
| void Consume(Token::Value token) { |
| Token::Value next = Next(); |
| USE(next); |
| USE(token); |
| DCHECK(next == token); |
| } |
| |
| bool Check(Token::Value token) { |
| Token::Value next = peek(); |
| if (next == token) { |
| Consume(next); |
| return true; |
| } |
| return false; |
| } |
| |
| void Expect(Token::Value token, bool* ok) { |
| Token::Value next = Next(); |
| if (next != token) { |
| ReportUnexpectedToken(next); |
| *ok = false; |
| } |
| } |
| |
| void ExpectSemicolon(bool* ok) { |
| // Check for automatic semicolon insertion according to |
| // the rules given in ECMA-262, section 7.9, page 21. |
| Token::Value tok = peek(); |
| if (tok == Token::SEMICOLON) { |
| Next(); |
| return; |
| } |
| if (scanner()->HasAnyLineTerminatorBeforeNext() || |
| tok == Token::RBRACE || |
| tok == Token::EOS) { |
| return; |
| } |
| |
| Token::Value current = scanner()->current_token(); |
| Scanner::Location current_location = scanner()->location(); |
| Token::Value next = Next(); |
| |
| if (next == Token::SEMICOLON) { |
| return; |
| } |
| |
| *ok = false; |
| if (current == Token::AWAIT && !is_async_function()) { |
| ReportMessageAt(current_location, |
| MessageTemplate::kAwaitNotInAsyncFunction, kSyntaxError); |
| return; |
| } |
| |
| ReportUnexpectedToken(next); |
| } |
| |
| // Dummy functions, just useful as arguments to CHECK_OK_CUSTOM. |
| static void Void() {} |
| template <typename T> |
| static T Return(T result) { |
| return result; |
| } |
| |
| bool is_any_identifier(Token::Value token) { |
| return token == Token::IDENTIFIER || token == Token::ENUM || |
| token == Token::AWAIT || token == Token::ASYNC || |
| token == Token::ESCAPED_STRICT_RESERVED_WORD || |
| token == Token::FUTURE_STRICT_RESERVED_WORD || token == Token::LET || |
| token == Token::STATIC || token == Token::YIELD; |
| } |
| bool peek_any_identifier() { return is_any_identifier(peek()); } |
| |
| bool CheckContextualKeyword(Token::Value token) { |
| if (PeekContextualKeyword(token)) { |
| Consume(Token::IDENTIFIER); |
| return true; |
| } |
| return false; |
| } |
| |
| bool PeekContextualKeyword(Token::Value token) { |
| DCHECK(Token::IsContextualKeyword(token)); |
| return peek() == Token::IDENTIFIER && |
| scanner()->next_contextual_token() == token; |
| } |
| |
| void ExpectMetaProperty(Token::Value property_name, const char* full_name, |
| int pos, bool* ok); |
| |
| void ExpectContextualKeyword(Token::Value token, bool* ok) { |
| DCHECK(Token::IsContextualKeyword(token)); |
| Expect(Token::IDENTIFIER, CHECK_OK_CUSTOM(Void)); |
| if (scanner()->current_contextual_token() != token) { |
| ReportUnexpectedToken(scanner()->current_token()); |
| *ok = false; |
| } |
| } |
| |
| bool CheckInOrOf(ForEachStatement::VisitMode* visit_mode) { |
| if (Check(Token::IN)) { |
| *visit_mode = ForEachStatement::ENUMERATE; |
| return true; |
| } else if (CheckContextualKeyword(Token::OF)) { |
| *visit_mode = ForEachStatement::ITERATE; |
| return true; |
| } |
| return false; |
| } |
| |
| bool PeekInOrOf() { |
| return peek() == Token::IN || PeekContextualKeyword(Token::OF); |
| } |
| |
| // Checks whether an octal literal was last seen between beg_pos and end_pos. |
| // Only called for strict mode strings. |
| void CheckStrictOctalLiteral(int beg_pos, int end_pos, bool* ok) { |
| Scanner::Location octal = scanner()->octal_position(); |
| if (octal.IsValid() && beg_pos <= octal.beg_pos && |
| octal.end_pos <= end_pos) { |
| MessageTemplate::Template message = scanner()->octal_message(); |
| DCHECK_NE(message, MessageTemplate::kNone); |
| impl()->ReportMessageAt(octal, message); |
| scanner()->clear_octal_position(); |
| if (message == MessageTemplate::kStrictDecimalWithLeadingZero) { |
| impl()->CountUsage(v8::Isolate::kDecimalWithLeadingZeroInStrictMode); |
| } |
| *ok = false; |
| } |
| } |
| |
| // Checks if an octal literal or an invalid hex or unicode escape sequence |
| // appears in the current template literal token. In the presence of such, |
| // either returns false or reports an error, depending on should_throw. |
| // Otherwise returns true. |
| inline bool CheckTemplateEscapes(bool should_throw, bool* ok) { |
| DCHECK(scanner()->current_token() == Token::TEMPLATE_SPAN || |
| scanner()->current_token() == Token::TEMPLATE_TAIL); |
| if (!scanner()->has_invalid_template_escape()) { |
| return true; |
| } |
| |
| // Handle error case(s) |
| if (should_throw) { |
| impl()->ReportMessageAt(scanner()->invalid_template_escape_location(), |
| scanner()->invalid_template_escape_message()); |
| *ok = false; |
| } |
| return false; |
| } |
| |
| void CheckDestructuringElement(ExpressionT element, int beg_pos, int end_pos); |
| |
| // Checking the name of a function literal. This has to be done after parsing |
| // the function, since the function can declare itself strict. |
| void CheckFunctionName(LanguageMode language_mode, IdentifierT function_name, |
| FunctionNameValidity function_name_validity, |
| const Scanner::Location& function_name_loc, bool* ok) { |
| if (impl()->IsNull(function_name)) return; |
| if (function_name_validity == kSkipFunctionNameCheck) return; |
| // The function name needs to be checked in strict mode. |
| if (is_sloppy(language_mode)) return; |
| |
| if (impl()->IsEvalOrArguments(function_name)) { |
| impl()->ReportMessageAt(function_name_loc, |
| MessageTemplate::kStrictEvalArguments); |
| *ok = false; |
| return; |
| } |
| if (function_name_validity == kFunctionNameIsStrictReserved) { |
| impl()->ReportMessageAt(function_name_loc, |
| MessageTemplate::kUnexpectedStrictReserved); |
| *ok = false; |
| return; |
| } |
| } |
| |
| // Determine precedence of given token. |
| static int Precedence(Token::Value token, bool accept_IN) { |
| if (token == Token::IN && !accept_IN) |
| return 0; // 0 precedence will terminate binary expression parsing |
| return Token::Precedence(token); |
| } |
| |
| typename Types::Factory* factory() { return &ast_node_factory_; } |
| |
| DeclarationScope* GetReceiverScope() const { |
| return scope()->GetReceiverScope(); |
| } |
| LanguageMode language_mode() { return scope()->language_mode(); } |
| void RaiseLanguageMode(LanguageMode mode) { |
| LanguageMode old = scope()->language_mode(); |
| impl()->SetLanguageMode(scope(), old > mode ? old : mode); |
| } |
| bool is_generator() const { |
| return IsGeneratorFunction(function_state_->kind()); |
| } |
| bool is_async_function() const { |
| return IsAsyncFunction(function_state_->kind()); |
| } |
| bool is_async_generator() const { |
| return IsAsyncGeneratorFunction(function_state_->kind()); |
| } |
| bool is_resumable() const { |
| return IsResumableFunction(function_state_->kind()); |
| } |
| |
| const PendingCompilationErrorHandler* pending_error_handler() const { |
| return pending_error_handler_; |
| } |
| PendingCompilationErrorHandler* pending_error_handler() { |
| return pending_error_handler_; |
| } |
| |
| // Report syntax errors. |
| void ReportMessage(MessageTemplate::Template message) { |
| Scanner::Location source_location = scanner()->location(); |
| impl()->ReportMessageAt(source_location, message, |
| static_cast<const char*>(nullptr), kSyntaxError); |
| } |
| |
| template <typename T> |
| void ReportMessage(MessageTemplate::Template message, T arg, |
| ParseErrorType error_type = kSyntaxError) { |
| Scanner::Location source_location = scanner()->location(); |
| impl()->ReportMessageAt(source_location, message, arg, error_type); |
| } |
| |
| void ReportMessageAt(Scanner::Location location, |
| MessageTemplate::Template message, |
| ParseErrorType error_type) { |
| impl()->ReportMessageAt(location, message, |
| static_cast<const char*>(nullptr), error_type); |
| } |
| |
| void GetUnexpectedTokenMessage( |
| Token::Value token, MessageTemplate::Template* message, |
| Scanner::Location* location, const char** arg, |
| MessageTemplate::Template default_ = MessageTemplate::kUnexpectedToken); |
| |
| void ReportUnexpectedToken(Token::Value token); |
| void ReportUnexpectedTokenAt( |
| Scanner::Location location, Token::Value token, |
| MessageTemplate::Template message = MessageTemplate::kUnexpectedToken); |
| |
| void ReportClassifierError( |
| const typename ExpressionClassifier::Error& error) { |
| impl()->ReportMessageAt(error.location, error.message, error.arg, |
| error.type); |
| } |
| |
| void ValidateExpression(bool* ok) { |
| if (!classifier()->is_valid_expression()) { |
| ReportClassifierError(classifier()->expression_error()); |
| *ok = false; |
| } |
| } |
| |
| void ValidateFormalParameterInitializer(bool* ok) { |
| if (!classifier()->is_valid_formal_parameter_initializer()) { |
| ReportClassifierError(classifier()->formal_parameter_initializer_error()); |
| *ok = false; |
| } |
| } |
| |
| void ValidateBindingPattern(bool* ok) { |
| if (!classifier()->is_valid_binding_pattern()) { |
| ReportClassifierError(classifier()->binding_pattern_error()); |
| *ok = false; |
| } |
| } |
| |
| void ValidateAssignmentPattern(bool* ok) { |
| if (!classifier()->is_valid_assignment_pattern()) { |
| ReportClassifierError(classifier()->assignment_pattern_error()); |
| *ok = false; |
| } |
| } |
| |
| void ValidateFormalParameters(LanguageMode language_mode, |
| bool allow_duplicates, bool* ok) { |
| if (!allow_duplicates && |
| !classifier()->is_valid_formal_parameter_list_without_duplicates()) { |
| ReportClassifierError(classifier()->duplicate_formal_parameter_error()); |
| *ok = false; |
| } else if (is_strict(language_mode) && |
| !classifier()->is_valid_strict_mode_formal_parameters()) { |
| ReportClassifierError(classifier()->strict_mode_formal_parameter_error()); |
| *ok = false; |
| } |
| } |
| |
| bool IsValidArrowFormalParametersStart(Token::Value token) { |
| return is_any_identifier(token) || token == Token::LPAREN; |
| } |
| |
| void ValidateArrowFormalParameters(ExpressionT expr, |
| bool parenthesized_formals, bool is_async, |
| bool* ok) { |
| if (classifier()->is_valid_binding_pattern()) { |
| // A simple arrow formal parameter: IDENTIFIER => BODY. |
| if (!impl()->IsIdentifier(expr)) { |
| impl()->ReportMessageAt(scanner()->location(), |
| MessageTemplate::kUnexpectedToken, |
| Token::String(scanner()->current_token())); |
| *ok = false; |
| } |
| } else if (!classifier()->is_valid_arrow_formal_parameters()) { |
| // If after parsing the expr, we see an error but the expression is |
| // neither a valid binding pattern nor a valid parenthesized formal |
| // parameter list, show the "arrow formal parameters" error if the formals |
| // started with a parenthesis, and the binding pattern error otherwise. |
| const typename ExpressionClassifier::Error& error = |
| parenthesized_formals ? classifier()->arrow_formal_parameters_error() |
| : classifier()->binding_pattern_error(); |
| ReportClassifierError(error); |
| *ok = false; |
| } |
| if (is_async && !classifier()->is_valid_async_arrow_formal_parameters()) { |
| const typename ExpressionClassifier::Error& error = |
| classifier()->async_arrow_formal_parameters_error(); |
| ReportClassifierError(error); |
| *ok = false; |
| } |
| } |
| |
| void ValidateLetPattern(bool* ok) { |
| if (!classifier()->is_valid_let_pattern()) { |
| ReportClassifierError(classifier()->let_pattern_error()); |
| *ok = false; |
| } |
| } |
| |
| void BindingPatternUnexpectedToken() { |
| MessageTemplate::Template message = MessageTemplate::kUnexpectedToken; |
| const char* arg; |
| Scanner::Location location = scanner()->peek_location(); |
| GetUnexpectedTokenMessage(peek(), &message, &location, &arg); |
| classifier()->RecordBindingPatternError(location, message, arg); |
| } |
| |
| void ArrowFormalParametersUnexpectedToken() { |
| MessageTemplate::Template message = MessageTemplate::kUnexpectedToken; |
| const char* arg; |
| Scanner::Location location = scanner()->peek_location(); |
| GetUnexpectedTokenMessage(peek(), &message, &location, &arg); |
| classifier()->RecordArrowFormalParametersError(location, message, arg); |
| } |
| |
| // Recursive descent functions. |
| // All ParseXXX functions take as the last argument an *ok parameter |
| // which is set to false if parsing failed; it is unchanged otherwise. |
| // By making the 'exception handling' explicit, we are forced to check |
| // for failure at the call sites. The family of CHECK_OK* macros can |
| // be useful for this. |
| |
| // Parses an identifier that is valid for the current scope, in particular it |
| // fails on strict mode future reserved keywords in a strict scope. If |
| // allow_eval_or_arguments is kAllowEvalOrArguments, we allow "eval" or |
| // "arguments" as identifier even in strict mode (this is needed in cases like |
| // "var foo = eval;"). |
| IdentifierT ParseIdentifier(AllowRestrictedIdentifiers, bool* ok); |
| IdentifierT ParseAndClassifyIdentifier(bool* ok); |
| // Parses an identifier or a strict mode future reserved word, and indicate |
| // whether it is strict mode future reserved. Allows passing in function_kind |
| // for the case of parsing the identifier in a function expression, where the |
| // relevant "function_kind" bit is of the function being parsed, not the |
| // containing function. |
| IdentifierT ParseIdentifierOrStrictReservedWord(FunctionKind function_kind, |
| bool* is_strict_reserved, |
| bool* is_await, bool* ok); |
| IdentifierT ParseIdentifierOrStrictReservedWord(bool* is_strict_reserved, |
| bool* is_await, bool* ok) { |
| return ParseIdentifierOrStrictReservedWord( |
| function_state_->kind(), is_strict_reserved, is_await, ok); |
| } |
| |
| IdentifierT ParseIdentifierName(bool* ok); |
| |
| ExpressionT 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, bool has_extends, |
| bool* is_computed_name, bool* has_seen_constructor, |
| ClassLiteralProperty::Kind* property_kind, bool* is_static, |
| bool* has_name_static_property, bool* ok); |
| ExpressionT ParseClassFieldInitializer(ClassInfo* class_info, bool is_static, |
| bool* ok); |
| ObjectLiteralPropertyT ParseObjectPropertyDefinition( |
| ObjectLiteralChecker* checker, bool* is_computed_name, |
| bool* is_rest_property, bool* ok); |
| ExpressionListT ParseArguments(Scanner::Location* first_spread_pos, |
| bool maybe_arrow, |
| bool* is_simple_parameter_list, bool* ok); |
| ExpressionListT ParseArguments(Scanner::Location* first_spread_pos, |
| bool* ok) { |
| return ParseArguments(first_spread_pos, false, nullptr, ok); |
| } |
| |
| ExpressionT ParseAssignmentExpression(bool accept_IN, bool* ok); |
| ExpressionT ParseYieldExpression(bool accept_IN, bool* ok); |
| ExpressionT ParseConditionalExpression(bool accept_IN, bool* ok); |
| ExpressionT ParseBinaryExpression(int prec, bool accept_IN, bool* ok); |
| ExpressionT ParseUnaryExpression(bool* ok); |
| ExpressionT ParsePostfixExpression(bool* ok); |
| ExpressionT ParseLeftHandSideExpression(bool* ok); |
| ExpressionT ParseMemberWithNewPrefixesExpression(bool* is_async, bool* ok); |
| ExpressionT ParseMemberExpression(bool* is_async, bool* ok); |
| ExpressionT ParseMemberExpressionContinuation(ExpressionT expression, |
| bool* is_async, bool* ok); |
| |
| // `rewritable_length`: length of the destructuring_assignments_to_rewrite() |
| // queue in the parent function state, prior to parsing of formal parameters. |
| // If the arrow function is lazy, any items added during formal parameter |
| // parsing are removed from the queue. |
| ExpressionT ParseArrowFunctionLiteral(bool accept_IN, |
| const FormalParametersT& parameters, |
| int rewritable_length, bool* ok); |
| void ParseSingleExpressionFunctionBody(StatementListT body, bool is_async, |
| bool accept_IN, bool* ok); |
| void ParseAsyncFunctionBody(Scope* scope, StatementListT body, bool* ok); |
| ExpressionT ParseAsyncFunctionLiteral(bool* ok); |
| ExpressionT ParseClassLiteral(IdentifierT name, |
| Scanner::Location class_name_location, |
| bool name_is_strict_reserved, |
| int class_token_pos, bool* ok); |
| ExpressionT ParseTemplateLiteral(ExpressionT tag, int start, bool tagged, |
| bool* ok); |
| ExpressionT ParseSuperExpression(bool is_new, bool* ok); |
| ExpressionT ParseImportExpressions(bool* ok); |
| ExpressionT ParseNewTargetExpression(bool* ok); |
| |
| void ParseFormalParameter(FormalParametersT* parameters, bool* ok); |
| void ParseFormalParameterList(FormalParametersT* parameters, bool* ok); |
| void CheckArityRestrictions(int param_count, FunctionKind function_type, |
| bool has_rest, int formals_start_pos, |
| int formals_end_pos, bool* ok); |
| |
| BlockT ParseVariableDeclarations(VariableDeclarationContext var_context, |
| DeclarationParsingResult* parsing_result, |
| ZoneList<const AstRawString*>* names, |
| bool* ok); |
| StatementT ParseAsyncFunctionDeclaration(ZoneList<const AstRawString*>* names, |
| bool default_export, bool* ok); |
| StatementT ParseFunctionDeclaration(bool* ok); |
| StatementT ParseHoistableDeclaration(ZoneList<const AstRawString*>* names, |
| bool default_export, bool* ok); |
| StatementT ParseHoistableDeclaration(int pos, ParseFunctionFlags flags, |
| ZoneList<const AstRawString*>* names, |
| bool default_export, bool* ok); |
| StatementT ParseClassDeclaration(ZoneList<const AstRawString*>* names, |
| bool default_export, bool* ok); |
| StatementT ParseNativeDeclaration(bool* ok); |
| |
| // Consumes the ending }. |
| void ParseFunctionBody(StatementListT result, IdentifierT function_name, |
| int pos, const FormalParametersT& parameters, |
| FunctionKind kind, |
| FunctionLiteral::FunctionType function_type, bool* ok); |
| |
| // Under some circumstances, we allow preparsing to abort if the preparsed |
| // function is "long and trivial", and fully parse instead. Our current |
| // definition of "long and trivial" is: |
| // - over kLazyParseTrialLimit statements |
| // - all starting with an identifier (i.e., no if, for, while, etc.) |
| static const int kLazyParseTrialLimit = 200; |
| |
| // TODO(nikolaos, marja): The first argument should not really be passed |
| // by value. The method is expected to add the parsed statements to the |
| // list. This works because in the case of the parser, StatementListT is |
| // a pointer whereas the preparser does not really modify the body. |
| V8_INLINE void ParseStatementList(StatementListT body, Token::Value end_token, |
| bool* ok) { |
| LazyParsingResult result = ParseStatementList(body, end_token, false, ok); |
| USE(result); |
| DCHECK_EQ(result, kLazyParsingComplete); |
| } |
| LazyParsingResult ParseStatementList(StatementListT body, |
| Token::Value end_token, bool may_abort, |
| bool* ok); |
| StatementT ParseStatementListItem(bool* ok); |
| StatementT ParseStatement(ZoneList<const AstRawString*>* labels, bool* ok) { |
| return ParseStatement(labels, kDisallowLabelledFunctionStatement, ok); |
| } |
| StatementT ParseStatement(ZoneList<const AstRawString*>* labels, |
| AllowLabelledFunctionStatement allow_function, |
| bool* ok); |
| BlockT ParseBlock(ZoneList<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(ZoneList<const AstRawString*>* labels, |
| bool* ok); |
| |
| StatementT ParseVariableStatement(VariableDeclarationContext var_context, |
| ZoneList<const AstRawString*>* names, |
| bool* ok); |
| |
| // Magical syntax support. |
| ExpressionT ParseV8Intrinsic(bool* ok); |
| |
| ExpressionT ParseDoExpression(bool* ok); |
| |
| StatementT ParseDebuggerStatement(bool* ok); |
| |
| StatementT ParseExpressionOrLabelledStatement( |
| ZoneList<const AstRawString*>* labels, |
| AllowLabelledFunctionStatement allow_function, bool* ok); |
| StatementT ParseIfStatement(ZoneList<const AstRawString*>* labels, bool* ok); |
| StatementT ParseContinueStatement(bool* ok); |
| StatementT ParseBreakStatement(ZoneList<const AstRawString*>* labels, |
| bool* ok); |
| StatementT ParseReturnStatement(bool* ok); |
| StatementT ParseWithStatement(ZoneList<const AstRawString*>* labels, |
| bool* ok); |
| StatementT ParseDoWhileStatement(ZoneList<const AstRawString*>* labels, |
| bool* ok); |
| StatementT ParseWhileStatement(ZoneList<const AstRawString*>* labels, |
| bool* ok); |
| StatementT ParseThrowStatement(bool* ok); |
| StatementT ParseSwitchStatement(ZoneList<const AstRawString*>* labels, |
| bool* ok); |
| StatementT ParseTryStatement(bool* ok); |
| StatementT ParseForStatement(ZoneList<const AstRawString*>* labels, bool* ok); |
| StatementT ParseForEachStatementWithDeclarations( |
| int stmt_pos, ForInfo* for_info, ZoneList<const AstRawString*>* 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, ZoneList<const AstRawString*>* 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, |
| ZoneList<const AstRawString*>* 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, |
| ZoneList<const AstRawString*>* labels, bool* ok); |
| StatementT ParseForAwaitStatement(ZoneList<const AstRawString*>* 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); |
| } |
| 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 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_function_sent_; |
| bool allow_harmony_public_fields_; |
| bool allow_harmony_static_fields_; |
| bool allow_harmony_dynamic_import_; |
| bool allow_harmony_import_meta_; |
| bool allow_harmony_optional_catch_binding_; |
| bool allow_harmony_private_fields_; |
| |
| friend class DiscardableZoneScope; |
| }; |
| |
| template <typename Impl> |
| ParserBase<Impl>::FunctionState::FunctionState( |
| FunctionState** function_state_stack, Scope** scope_stack, |
| DeclarationScope* scope) |
| : BlockState(scope_stack, scope), |
| expected_property_count_(0), |
| function_state_stack_(function_state_stack), |
| outer_function_state_(*function_state_stack), |
| scope_(scope), |
| destructuring_assignments_to_rewrite_(16, scope->zone()), |
| non_patterns_to_rewrite_(0, scope->zone()), |
| reported_errors_(16, 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; |
| 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>::ParseRegExpLiteral( |
| bool* ok) { |
| int pos = peek_position(); |
| if (!scanner()->ScanRegExpPattern()) { |
| Next(); |
| ReportMessage(MessageTemplate::kUnterminatedRegExp); |
| *ok = false; |
| return impl()->NullExpression(); |
| } |
| |
| IdentifierT js_pattern = impl()->GetNextSymbol(); |
| Maybe<RegExp::Flags> flags = scanner()->ScanRegExpFlags(); |
| if (flags.IsNothing()) { |
| Next(); |
| ReportMessage(MessageTemplate::kMalformedRegExpFlags); |
| *ok = false; |
| return impl()->NullExpression(); |
| } |
| int js_flags = flags.FromJust(); |
| Next(); |
| return factory()->NewRegExpLiteral(js_pattern, js_flags, pos); |
| } |
| |
| template <typename Impl> |
| typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParsePrimaryExpression( |
| bool* is_async, bool* ok) { |
| // PrimaryExpression :: |
| // 'this' |
| // 'null' |
| // 'true' |
| // 'false' |
| // Identifier |
| // Number |
| // String |
| // ArrayLiteral |
| // ObjectLiteral |
| // RegExpLiteral |
| // ClassLiteral |
| // '(' Expression ')' |
| // TemplateLiteral |
| // do Block |
| // AsyncFunctionLiteral |
| |
| int beg_pos = peek_position(); |
| switch (peek()) { |
| case Token::THIS: { |
| BindingPatternUnexpectedToken(); |
| Consume(Token::THIS); |
| return impl()->ThisExpression(beg_pos); |
| } |
| |
| case Token::NULL_LITERAL: |
| case Token::TRUE_LITERAL: |
| case Token::FALSE_LITERAL: |
| case Token::SMI: |
| case Token::NUMBER: |
| case Token::BIGINT: |
| BindingPatternUnexpectedToken(); |
| return impl()->ExpressionFromLiteral(Next(), beg_pos); |
| |
| case Token::ASYNC: |
| if (!scanner()->HasAnyLineTerminatorAfterNext() && |
| PeekAhead() == Token::FUNCTION) { |
| BindingPatternUnexpectedToken(); |
| Consume(Token::ASYNC); |
| return ParseAsyncFunctionLiteral(CHECK_OK); |
| } |
| // CoverCallExpressionAndAsyncArrowHead |
| *is_async = true; |
| /* falls through */ |
| case Token::IDENTIFIER: |
| case Token::LET: |
| case Token::STATIC: |
| case Token::YIELD: |
| case Token::AWAIT: |
| case Token::ESCAPED_STRICT_RESERVED_WORD: |
| case Token::FUTURE_STRICT_RESERVED_WORD: { |
| // Using eval or arguments in this context is OK even in strict mode. |
| IdentifierT name = ParseAndClassifyIdentifier(CHECK_OK); |
| return impl()->ExpressionFromIdentifier(name, beg_pos); |
| } |
| |
| case Token::STRING: { |
| BindingPatternUnexpectedToken(); |
| Consume(Token::STRING); |
| return impl()->ExpressionFromString(beg_pos); |
| } |
| |
| case Token::ASSIGN_DIV: |
| case Token::DIV: |
| classifier()->RecordBindingPatternError( |
| scanner()->peek_location(), MessageTemplate::kUnexpectedTokenRegExp); |
| return ParseRegExpLiteral(ok); |
| |
| case Token::LBRACK: |
| return ParseArrayLiteral(ok); |
| |
| case Token::LBRACE: |
| return ParseObjectLiteral(ok); |
| |
| case Token::LPAREN: { |
| // Arrow function formal parameters are either a single identifier or a |
| // list of BindingPattern productions enclosed in parentheses. |
| // Parentheses are not valid on the LHS of a BindingPattern, so we use the |
| // is_valid_binding_pattern() check to detect multiple levels of |
| // parenthesization. |
| bool pattern_error = !classifier()->is_valid_binding_pattern(); |
| classifier()->RecordPatternError(scanner()->peek_location(), |
| MessageTemplate::kUnexpectedToken, |
| Token::String(Token::LPAREN)); |
| if (pattern_error) ArrowFormalParametersUnexpectedToken(); |
| Consume(Token::LPAREN); |
| if (Check(Token::RPAREN)) { |
| // ()=>x. The continuation that looks for the => is in |
| // ParseAssignmentExpression. |
| classifier()->RecordExpressionError(scanner()->location(), |
| MessageTemplate::kUnexpectedToken, |
| Token::String(Token::RPAREN)); |
| return factory()->NewEmptyParentheses(beg_pos); |
| } |
| // Heuristically try to detect immediately called functions before |
| // seeing the call parentheses. |
| if (peek() == Token::FUNCTION || |
| (peek() == Token::ASYNC && PeekAhead() == Token::FUNCTION)) { |
| function_state_->set_next_function_is_likely_called(); |
| } |
| ExpressionT expr = ParseExpressionCoverGrammar(true, CHECK_OK); |
| Expect(Token::RPAREN, CHECK_OK); |
| return expr; |
| } |
| |
| case Token::CLASS: { |
| BindingPatternUnexpectedToken(); |
| Consume(Token::CLASS); |
| int class_token_pos = position(); |
| IdentifierT name = impl()->NullIdentifier(); |
| bool is_strict_reserved_name = false; |
| Scanner::Location class_name_location = Scanner::Location::invalid(); |
| if (peek_any_identifier()) { |
| bool is_await = false; |
| name = ParseIdentifierOrStrictReservedWord(&is_strict_reserved_name, |
| &is_await, CHECK_OK); |
| class_name_location = scanner()->location(); |
| if (is_await) { |
| classifier()->RecordAsyncArrowFormalParametersError( |
| scanner()->location(), MessageTemplate::kAwaitBindingIdentifier); |
| } |
| } |
| return ParseClassLiteral(name, class_name_location, |
| is_strict_reserved_name, class_token_pos, ok); |
| } |
| |
| case Token::TEMPLATE_SPAN: |
| case Token::TEMPLATE_TAIL: |
| BindingPatternUnexpectedToken(); |
| return ParseTemplateLiteral(impl()->NullExpression(), beg_pos, false, ok); |
| |
| case Token::MOD: |
| if (allow_natives() || extension_ != nullptr) { |
| BindingPatternUnexpectedToken(); |
| return ParseV8Intrinsic(ok); |
| } |
| break; |
| |
| case Token::DO: |
| if (allow_harmony_do_expressions()) { |
| BindingPatternUnexpectedToken(); |
| return ParseDoExpression(ok); |
| } |
| break; |
| |
| default: |
| break; |
| } |
| |
| ReportUnexpectedToken(Next()); |
| *ok = false; |
| return impl()->NullExpression(); |
| } |
| |
| template <typename Impl> |
| typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseExpression( |
| bool accept_IN, bool* ok) { |
| ExpressionClassifier classifier(this); |
| ExpressionT result = ParseExpressionCoverGrammar(accept_IN, CHECK_OK); |
| ValidateExpression(CHECK_OK); |
| return result; |
| } |
| |
| template <typename Impl> |
| typename ParserBase<Impl>::ExpressionT |
| ParserBase<Impl>::ParseExpressionCoverGrammar(bool accept_IN, bool* ok) { |
| // Expression :: |
| // AssignmentExpression |
| // Expression ',' AssignmentExpression |
| |
| ExpressionT result = impl()->NullExpression(); |
| while (true) { |
| int comma_pos = position(); |
| ExpressionClassifier binding_classifier(this); |
| ExpressionT right; |
| if (Check(Token::ELLIPSIS)) { |
| // 'x, y, ...z' in CoverParenthesizedExpressionAndArrowParameterList only |
| // as the formal parameters of'(x, y, ...z) => foo', and is not itself a |
| // valid expression. |
| classifier()->RecordExpressionError(scanner()->location(), |
| MessageTemplate::kUnexpectedToken, |
| Token::String(Token::ELLIPSIS)); |
| int ellipsis_pos = position(); |
| int pattern_pos = peek_position(); |
| ExpressionT pattern = ParsePrimaryExpression(CHECK_OK); |
| if (peek() == Token::ASSIGN) { |
| ReportMessage(MessageTemplate::kRestDefaultInitializer); |
| *ok = false; |
| return result; |
| } |
| ValidateBindingPattern(CHECK_OK); |
| right = factory()->NewSpread(pattern, ellipsis_pos, pattern_pos); |
| } else { |
| right = ParseAssignmentExpression(accept_IN, CHECK_OK); |
| } |
| // No need to accumulate binding pattern-related errors, since |
| // an Expression can't be a binding pattern anyway. |
| AccumulateNonBindingPatternErrors(); |
| if (!impl()->IsIdentifier(right)) classifier()->RecordNonSimpleParameter(); |
| if (impl()->IsNull(result)) { |
| // First time through the loop. |
| result = right; |
| } else if (impl()->CollapseNaryExpression(&result, right, Token::COMMA, |
| comma_pos, |
| SourceRange::Empty())) { |
| // Do nothing, "result" is already updated. |
| } else { |
| result = |
| factory()->NewBinaryOperation(Token::COMMA, result, right, comma_pos); |
| } |
| |
| if (!Check(Token::COMMA)) break; |
| |
| if (right->IsSpread()) { |
| classifier()->RecordArrowFormalParametersError( |
| scanner()->location(), MessageTemplate::kParamAfterRest); |
| } |
| |
| if (peek() == Token::RPAREN && PeekAhead() == Token::ARROW) { |
| // a trailing comma is allowed at the end of an arrow parameter list |
| break; |
| } |
| |
| // Pass on the 'set_next_function_is_likely_called' flag if we have |
| // several function literals separated by comma. |
| if (peek() == Token::FUNCTION && |
| function_state_->previous_function_was_likely_called()) { |
| function_state_->set_next_function_is_likely_called(); |
| } |
| } |
| |
| return result; |
| } |
| |
| template <typename Impl> |
| typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseArrayLiteral( |
| bool* ok) { |
| // ArrayLiteral :: |
| // '[' Expression? (',' Expression?)* ']' |
| |
| int pos = peek_position(); |
| ExpressionListT values = impl()->NewExpressionList(4); |
| int first_spread_index = -1; |
| Expect(Token::LBRACK, CHECK_OK); |
| while (peek() != Token::RBRACK) { |
| ExpressionT elem; |
| if (peek() == Token::COMMA) { |
| elem = factory()->NewTheHoleLiteral(); |
| } else if (peek() == Token::ELLIPSIS) { |
| int start_pos = peek_position(); |
| Consume(Token::ELLIPSIS); |
| int expr_pos = peek_position(); |
| ExpressionT argument = ParseAssignmentExpression(true, CHECK_OK); |
| elem = factory()->NewSpread(argument, start_pos, expr_pos); |
| |
| if (first_spread_index < 0) { |
| first_spread_index = values->length(); |
| } |
| |
| if (argument->IsAssignment()) { |
| classifier()->RecordPatternError( |
| Scanner::Location(start_pos, scanner()->location().end_pos), |
| MessageTemplate::kInvalidDestructuringTarget); |
| } else { |
| CheckDestructuringElement(argument, start_pos, |
| scanner()->location().end_pos); |
| } |
| |
| if (peek() == Token::COMMA) { |
| classifier()->RecordPatternError( |
| Scanner::Location(start_pos, scanner()->location().end_pos), |
| MessageTemplate::kElementAfterRest); |
| } |
| } else { |
| int beg_pos = peek_position(); |
| elem = ParseAssignmentExpression(true, CHECK_OK); |
| CheckDestructuringElement(elem, beg_pos, scanner()->location().end_pos); |
| } |
| values->Add(elem, zone_); |
| if (peek() != Token::RBRACK) { |
| Expect(Token::COMMA, CHECK_OK); |
| } |
| } |
| Expect(Token::RBRACK, CHECK_OK); |
| |
| return factory()->NewArrayLiteral(values, first_spread_index, pos); |
| } |
| |
| template <class Impl> |
| bool ParserBase<Impl>::SetPropertyKindFromToken(Token::Value token, |
| PropertyKind* kind) { |
| // This returns true, setting the property kind, iff the given token is one |
| // which must occur after a property name, indicating that the previous token |
| // was in fact a name and not a modifier (like the "get" in "get x"). |
| switch (token) { |
| case Token::COLON: |
| *kind = PropertyKind::kValueProperty; |
| return true; |
| case Token::COMMA: |
| case Token::RBRACE: |
| case Token::ASSIGN: |
| *kind = PropertyKind::kShorthandProperty; |
| return true; |
| case Token::LPAREN: |
| *kind = PropertyKind::kMethodProperty; |
| return true; |
| case Token::MUL: |
| case Token::SEMICOLON: |
| *kind = PropertyKind::kClassField; |
| return true; |
| case Token::PRIVATE_NAME: |
| *kind = PropertyKind::kClassField; |
| return true; |
| default: |
| break; |
| } |
| return false; |
| } |
| |
| template <class Impl> |
| typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParsePropertyName( |
| IdentifierT* name, PropertyKind* kind, bool* is_generator, bool* is_get, |
| bool* is_set, bool* is_async, bool* is_computed_name, bool* ok) { |
| DCHECK_EQ(*kind, PropertyKind::kNotSet); |
| DCHECK(!*is_generator); |
| DCHECK(!*is_get); |
| DCHECK(!*is_set); |
| DCHECK(!*is_async); |
| DCHECK(!*is_computed_name); |
| |
| *is_generator = Check(Token::MUL); |
| if (*is_generator) { |
| *kind = PropertyKind::kMethodProperty; |
| } |
| |
| Token::Value token = peek(); |
| int pos = peek_position(); |
| |
| if (!*is_generator && token == Token::ASYNC && |
| !scanner()->HasAnyLineTerminatorAfterNext()) { |
| Consume(Token::ASYNC); |
| token = peek(); |
| if (token == Token::MUL && !scanner()->HasAnyLineTerminatorBeforeNext()) { |
| Consume(Token::MUL); |
| token = peek(); |
| *is_generator = true; |
| } else if (SetPropertyKindFromToken(token, kind)) { |
| *name = impl()->GetSymbol(); // TODO(bakkot) specialize on 'async' |
| impl()->PushLiteralName(*name); |
| return factory()->NewStringLiteral(*name, pos); |
| } |
| *kind = PropertyKind::kMethodProperty; |
| *is_async = true; |
| pos = peek_position(); |
| } |
| |
| if (token == Token::IDENTIFIER && !*is_generator && !*is_async) { |
| // This is checking for 'get' and 'set' in particular. |
| Consume(Token::IDENTIFIER); |
| token = peek(); |
| if (SetPropertyKindFromToken(token, kind) || |
| !scanner()->IsGetOrSet(is_get, is_set)) { |
| *name = impl()->GetSymbol(); |
| impl()->PushLiteralName(*name); |
| return factory()->NewStringLiteral(*name, pos); |
| } |
| *kind = PropertyKind::kAccessorProperty; |
| pos = peek_position(); |
| } |
| |
| // For non computed property names we normalize the name a bit: |
| // |
| // "12" -> 12 |
| // 12.3 -> "12.3" |
| // 12.30 -> "12.3" |
| // identifier -> "identifier" |
| // |
| // This is important because we use the property name as a key in a hash |
| // table when we compute constant properties. |
| ExpressionT expression = impl()->NullExpression(); |
| switch (token) { |
| case Token::STRING: |
| Consume(Token::STRING); |
| *name = impl()->GetSymbol(); |
| break; |
| |
| case Token::SMI: |
| Consume(Token::SMI); |
| *name = impl()->GetNumberAsSymbol(); |
| break; |
| |
| case Token::NUMBER: |
| Consume(Token::NUMBER); |
| *name = impl()->GetNumberAsSymbol(); |
| break; |
| |
| case Token::LBRACK: { |
| *name = impl()->NullIdentifier(); |
| *is_computed_name = true; |
| Consume(Token::LBRACK); |
| ExpressionClassifier computed_name_classifier(this); |
| expression = ParseAssignmentExpression(true, CHECK_OK); |
| ValidateExpression(CHECK_OK); |
| AccumulateFormalParameterContainmentErrors(); |
| Expect(Token::RBRACK, CHECK_OK); |
| break; |
| } |
| |
| case Token::ELLIPSIS: |
| if (!*is_generator && !*is_async && !*is_get && !*is_set) { |
| *name = impl()->NullIdentifier(); |
| Consume(Token::ELLIPSIS); |
| expression = ParseAssignmentExpression(true, CHECK_OK); |
| *kind = PropertyKind::kSpreadProperty; |
| |
| if (!impl()->IsIdentifier(expression)) { |
| classifier()->RecordBindingPatternError( |
| scanner()->location(), |
| MessageTemplate::kInvalidRestBindingPattern); |
| } |
| |
| if (!expression->IsValidReferenceExpression()) { |
| classifier()->RecordAssignmentPatternError( |
| scanner()->location(), |
| MessageTemplate::kInvalidRestAssignmentPattern); |
| } |
| |
| if (peek() != Token::RBRACE) { |
| classifier()->RecordPatternError(scanner()->location(), |
| MessageTemplate::kElementAfterRest); |
| } |
| return expression; |
| } |
| // Fall-through. |
| |
| 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, bool has_extends, |
| bool* is_computed_name, bool* has_seen_constructor, |
| ClassLiteralProperty::Kind* property_kind, bool* is_static, |
| bool* has_name_static_property, bool* ok) { |
| DCHECK_NOT_NULL(has_seen_constructor); |
| DCHECK_NOT_NULL(has_name_static_property); |
| bool is_get = false; |
| bool is_set = false; |
| bool is_generator = false; |
| bool is_async = false; |
| *is_static = false; |
| *property_kind = ClassLiteralProperty::METHOD; |
| PropertyKind kind = PropertyKind::kNotSet; |
| |
| Token::Value name_token = peek(); |
| DCHECK_IMPLIES(name_token == Token::PRIVATE_NAME, |
| allow_harmony_private_fields()); |
| |
| int name_token_position = scanner()->peek_location().beg_pos; |
| IdentifierT name = impl()->NullIdentifier(); |
| ExpressionT name_expression; |
| if (name_token == Token::STATIC) { |
| Consume(Token::STATIC); |
| name_token_position = scanner()->peek_location().beg_pos; |
| if (peek() == Token::LPAREN) { |
| kind = PropertyKind::kMethodProperty; |
| name = impl()->GetSymbol(); // TODO(bakkot) specialize on 'static' |
| name_expression = factory()->NewStringLiteral(name, position()); |
| } else if (peek() == Token::ASSIGN || peek() == Token::SEMICOLON || |
| peek() == Token::RBRACE) { |
| name = impl()->GetSymbol(); // TODO(bakkot) specialize on 'static' |
| name_expression = factory()->NewStringLiteral(name, position()); |
| } else if (peek() == Token::PRIVATE_NAME) { |
| DCHECK(allow_harmony_private_fields()); |
| // TODO(gsathya): Make a better error message for this. |
| ReportUnexpectedToken(Next()); |
| *ok = false; |
| return impl()->NullLiteralProperty(); |
| } else { |
| *is_static = true; |
| name_expression = ParsePropertyName(&name, &kind, &is_generator, &is_get, |
| &is_set, &is_async, is_computed_name, |
| CHECK_OK_CUSTOM(NullLiteralProperty)); |
| } |
| } else if (name_token == Token::PRIVATE_NAME) { |
| Consume(Token::PRIVATE_NAME); |
| name = impl()->GetSymbol(); |
| name_expression = factory()->NewStringLiteral(name, position()); |
| } else { |
| name_expression = ParsePropertyName(&name, &kind, &is_generator, &is_get, |
| &is_set, &is_async, is_computed_name, |
| CHECK_OK_CUSTOM(NullLiteralProperty)); |
| } |
| |
| if (!*has_name_static_property && *is_static && impl()->IsName(name)) { |
| *has_name_static_property = true; |
| } |
| |
| switch (kind) { |
| case PropertyKind::kClassField: |
| case PropertyKind::kNotSet: // This case is a name followed by a name or |
| // other property. Here we have to assume |
| // that's an uninitialized field followed by a |
| // linebreak followed by a property, with ASI |
| // adding the semicolon. If not, there will be |
| // a syntax error after parsing the first name |
| // as an uninitialized field. |
| case PropertyKind::kShorthandProperty: |
| case PropertyKind::kValueProperty: |
| if (allow_harmony_public_fields() || allow_harmony_private_fields()) { |
| *property_kind = ClassLiteralProperty::FIELD; |
| if (*is_static && !allow_harmony_static_fields()) { |
| ReportUnexpectedToken(Next()); |
| *ok = false; |
| return impl()->NullLiteralProperty(); |
| } |
| if (!*is_computed_name && name_token != Token::PRIVATE_NAME) { |
| checker->CheckClassFieldName(*is_static, |
| CHECK_OK_CUSTOM(NullLiteralProperty)); |
| } |
| ExpressionT initializer = ParseClassFieldInitializer( |
| class_info, *is_static, CHECK_OK_CUSTOM(NullLiteralProperty)); |
| ExpectSemicolon(CHECK_OK_CUSTOM(NullLiteralProperty)); |
| ClassLiteralPropertyT result = factory()->NewClassLiteralProperty( |
| name_expression, initializer, *property_kind, *is_static, |
| *is_computed_name); |
| impl()->SetFunctionNameFromPropertyName(result, name); |
| return result; |
| |
| } else { |
| ReportUnexpectedToken(Next()); |
| *ok = false; |
| return impl()->NullLiteralProperty(); |
| } |
| |
| case PropertyKind::kMethodProperty: { |
| DCHECK(!is_get && !is_set); |
| |
| // MethodDefinition |
| // PropertyName '(' StrictFormalParameters ')' '{' FunctionBody '}' |
| // '*' PropertyName '(' StrictFormalParameters ')' '{' FunctionBody '}' |
| // async PropertyName '(' StrictFormalParameters ')' |
| // '{' FunctionBody '}' |
| // async '*' PropertyName '(' StrictFormalParameters ')' |
| // '{' FunctionBody '}' |
| |
| if (!*is_computed_name) { |
| checker->CheckClassMethodName(name_token, PropertyKind::kMethodProperty, |
| is_generator, is_async, *is_static, |
| CHECK_OK_CUSTOM(NullLiteralProperty)); |
| } |
| |
| FunctionKind kind = MethodKindFor(is_generator, is_async); |
| |
| if (!*is_static && impl()->IsConstructor(name)) { |
| *has_seen_constructor = true; |
| kind = has_extends ? FunctionKind::kDerivedConstructor |
| : FunctionKind::kBaseConstructor; |
| } |
| |
| ExpressionT value = impl()->ParseFunctionLiteral( |
| name, scanner()->location(), kSkipFunctionNameCheck, kind, |
| FLAG_harmony_function_tostring ? name_token_position |
| : kNoSourcePosition, |
| FunctionLiteral::kAccessorOrMethod, language_mode(), nullptr, |
| CHECK_OK_CUSTOM(NullLiteralProperty)); |
| |
| *property_kind = ClassLiteralProperty::METHOD; |
| ClassLiteralPropertyT result = factory()->NewClassLiteralProperty( |
| name_expression, value, *property_kind, *is_static, |
| *is_computed_name); |
| impl()->SetFunctionNameFromPropertyName(result, name); |
| return result; |
| } |
| |
| case PropertyKind::kAccessorProperty: { |
| DCHECK((is_get || is_set) && !is_generator && !is_async); |
| |
| if (!*is_computed_name) { |
| checker->CheckClassMethodName( |
| name_token, PropertyKind::kAccessorProperty, false, false, |
| *is_static, CHECK_OK_CUSTOM(NullLiteralProperty)); |
| // Make sure the name expression is a string since we need a Name for |
| // Runtime_DefineAccessorPropertyUnchecked and since we can determine |
| // this statically we can skip the extra runtime check. |
| name_expression = |
| factory()->NewStringLiteral(name, name_expression->position()); |
| } |
| |
| FunctionKind kind = is_get ? FunctionKind::kGetterFunction |
| : FunctionKind::kSetterFunction; |
| |
| FunctionLiteralT value = impl()->ParseFunctionLiteral( |
| name, scanner()->location(), kSkipFunctionNameCheck, kind, |
| FLAG_harmony_function_tostring ? name_token_position |
| : kNoSourcePosition, |
| FunctionLiteral::kAccessorOrMethod, language_mode(), nullptr, |
| CHECK_OK_CUSTOM(NullLiteralProperty)); |
| |
| *property_kind = |
| is_get ? ClassLiteralProperty::GETTER : ClassLiteralProperty::SETTER; |
| ClassLiteralPropertyT result = factory()->NewClassLiteralProperty( |
| name_expression, value, *property_kind, *is_static, |
| *is_computed_name); |
| const AstRawString* prefix = |
| is_get ? ast_value_factory()->get_space_string() |
| : ast_value_factory()->set_space_string(); |
| impl()->SetFunctionNameFromPropertyName(result, name, prefix); |
| return result; |
| } |
| case PropertyKind::kSpreadProperty: |
| ReportUnexpectedTokenAt( |
| Scanner::Location(name_token_position, name_expression->position()), |
| name_token); |
| *ok = false; |
| return impl()->NullLiteralProperty(); |
| } |
| UNREACHABLE(); |
| } |
| |
| template <typename Impl> |
| typename ParserBase<Impl>::ExpressionT |
| ParserBase<Impl>::ParseClassFieldInitializer(ClassInfo* class_info, |
| bool is_static, bool* ok) { |
| DeclarationScope* initializer_scope = is_static |
| ? class_info->static_fields_scope |
| : class_info->instance_fields_scope; |
| |
| if (initializer_scope == nullptr) { |
| initializer_scope = |
| NewFunctionScope(FunctionKind::kClassFieldsInitializerFunction); |
| // TODO(gsathya): Make scopes be non contiguous. |
| initializer_scope->set_start_position(scanner()->location().end_pos); |
| initializer_scope->SetLanguageMode(LanguageMode::kStrict); |
| } |
| |
| ExpressionT initializer; |
| if (Check(Token::ASSIGN)) { |
| FunctionState initializer_state(&function_state_, &scope_, |
| initializer_scope); |
| ExpressionClassifier expression_classifier(this); |
| |
| initializer = |
| ParseAssignmentExpression(true, CHECK_OK_CUSTOM(NullExpression)); |
| ValidateExpression(CHECK_OK_CUSTOM(NullExpression)); |
| } else { |
| initializer = factory()->NewUndefinedLiteral(kNoSourcePosition); |
| } |
| |
| initializer_scope->set_end_position(scanner()->location().end_pos); |
| if (is_static) { |
| class_info->static_fields_scope = initializer_scope; |
| class_info->has_static_class_fields = true; |
| } else { |
| class_info->instance_fields_scope = initializer_scope; |
| class_info->has_instance_class_fields = true; |
| } |
| |
| return initializer; |
| } |
| |
| template <typename Impl> |
| typename ParserBase<Impl>::ObjectLiteralPropertyT |
| ParserBase<Impl>::ParseObjectPropertyDefinition(ObjectLiteralChecker* checker, |
| bool* is_computed_name, |
| bool* is_rest_property, |
| bool* ok) { |
| bool is_get = false; |
| bool is_set = false; |
| bool is_generator = false; |
| bool is_async = false; |
| PropertyKind kind = PropertyKind::kNotSet; |
| |
| IdentifierT name = impl()->NullIdentifier(); |
| Token::Value name_token = peek(); |
| int next_beg_pos = scanner()->peek_location().beg_pos; |
| int next_end_pos = scanner()->peek_location().end_pos; |
| |
| ExpressionT name_expression = ParsePropertyName( |
| &name, &kind, &is_generator, &is_get, &is_set, &is_async, |
| is_computed_name, CHECK_OK_CUSTOM(NullLiteralProperty)); |
| |
| switch (kind) { |
| case PropertyKind::kSpreadProperty: |
| DCHECK(!is_get && !is_set && !is_generator && !is_async && |
| !*is_computed_name); |
| DCHECK(name_token == Token::ELLIPSIS); |
| |
| *is_computed_name = true; |
| *is_rest_property = true; |
| |
| 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 = |
|