| // 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 "src/ast/scopes.h" |
| #include "src/bailout-reason.h" |
| #include "src/hashmap.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 |
| }; |
| |
| |
| struct FormalParametersBase { |
| explicit FormalParametersBase(Scope* scope) : scope(scope) {} |
| Scope* scope; |
| bool has_rest = false; |
| bool is_simple = true; |
| int materialized_literals_count = 0; |
| }; |
| |
| |
| // Common base class shared between parser and pre-parser. Traits encapsulate |
| // the differences between Parser and PreParser: |
| |
| // - Return types: For example, Parser functions return Expression* and |
| // PreParser functions return PreParserExpression. |
| |
| // - Creating parse tree nodes: Parser generates an AST during the recursive |
| // descent. PreParser doesn't create a tree. Instead, it passes around minimal |
| // data objects (PreParserExpression, PreParserIdentifier etc.) which contain |
| // just enough data for the upper layer functions. PreParserFactory is |
| // responsible for creating these dummy objects. It provides a similar kind of |
| // interface as AstNodeFactory, so ParserBase doesn't need to care which one is |
| // used. |
| |
| // - Miscellaneous other tasks interleaved with the recursive descent. For |
| // example, Parser keeps track of which function literals should be marked as |
| // pretenured, and PreParser doesn't care. |
| |
| // The traits are expected to contain the following typedefs: |
| // struct Traits { |
| // // In particular... |
| // struct Type { |
| // // Used by FunctionState and BlockState. |
| // typedef Scope; |
| // typedef GeneratorVariable; |
| // // Return types for traversing functions. |
| // typedef Identifier; |
| // typedef Expression; |
| // typedef FunctionLiteral; |
| // typedef ClassLiteral; |
| // typedef ObjectLiteralProperty; |
| // typedef Literal; |
| // typedef ExpressionList; |
| // typedef PropertyList; |
| // typedef FormalParameter; |
| // typedef FormalParameters; |
| // // For constructing objects returned by the traversing functions. |
| // typedef Factory; |
| // }; |
| // // ... |
| // }; |
| |
| template <typename Traits> |
| class ParserBase : public Traits { |
| public: |
| // Shorten type names defined by Traits. |
| typedef typename Traits::Type::Expression ExpressionT; |
| typedef typename Traits::Type::Identifier IdentifierT; |
| typedef typename Traits::Type::FormalParameter FormalParameterT; |
| typedef typename Traits::Type::FormalParameters FormalParametersT; |
| typedef typename Traits::Type::FunctionLiteral FunctionLiteralT; |
| typedef typename Traits::Type::Literal LiteralT; |
| typedef typename Traits::Type::ObjectLiteralProperty ObjectLiteralPropertyT; |
| typedef typename Traits::Type::StatementList StatementListT; |
| |
| ParserBase(Zone* zone, Scanner* scanner, uintptr_t stack_limit, |
| v8::Extension* extension, AstValueFactory* ast_value_factory, |
| ParserRecorder* log, typename Traits::Type::Parser this_object) |
| : Traits(this_object), |
| parenthesized_function_(false), |
| scope_(NULL), |
| function_state_(NULL), |
| extension_(extension), |
| fni_(NULL), |
| ast_value_factory_(ast_value_factory), |
| log_(log), |
| mode_(PARSE_EAGERLY), // Lazy mode must be set explicitly. |
| stack_limit_(stack_limit), |
| zone_(zone), |
| scanner_(scanner), |
| stack_overflow_(false), |
| allow_lazy_(false), |
| allow_natives_(false), |
| allow_harmony_sloppy_(false), |
| allow_harmony_sloppy_function_(false), |
| allow_harmony_sloppy_let_(false), |
| allow_harmony_default_parameters_(false), |
| allow_harmony_destructuring_bind_(false), |
| allow_harmony_destructuring_assignment_(false), |
| allow_strong_mode_(false), |
| allow_legacy_const_(true), |
| allow_harmony_do_expressions_(false), |
| allow_harmony_function_name_(false) {} |
| |
| #define ALLOW_ACCESSORS(name) \ |
| bool allow_##name() const { return allow_##name##_; } \ |
| void set_allow_##name(bool allow) { allow_##name##_ = allow; } |
| |
| ALLOW_ACCESSORS(lazy); |
| ALLOW_ACCESSORS(natives); |
| ALLOW_ACCESSORS(harmony_sloppy); |
| ALLOW_ACCESSORS(harmony_sloppy_function); |
| ALLOW_ACCESSORS(harmony_sloppy_let); |
| ALLOW_ACCESSORS(harmony_default_parameters); |
| ALLOW_ACCESSORS(harmony_destructuring_bind); |
| ALLOW_ACCESSORS(harmony_destructuring_assignment); |
| ALLOW_ACCESSORS(strong_mode); |
| ALLOW_ACCESSORS(legacy_const); |
| ALLOW_ACCESSORS(harmony_do_expressions); |
| ALLOW_ACCESSORS(harmony_function_name); |
| #undef ALLOW_ACCESSORS |
| |
| uintptr_t stack_limit() const { return stack_limit_; } |
| |
| protected: |
| enum AllowRestrictedIdentifiers { |
| kAllowRestrictedIdentifiers, |
| kDontAllowRestrictedIdentifiers |
| }; |
| |
| enum Mode { |
| PARSE_LAZILY, |
| PARSE_EAGERLY |
| }; |
| |
| enum VariableDeclarationContext { |
| kStatementListItem, |
| kStatement, |
| kForStatement |
| }; |
| |
| class Checkpoint; |
| class ObjectLiteralCheckerBase; |
| |
| // --------------------------------------------------------------------------- |
| // FunctionState and BlockState together 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-function and per-block state. |
| class BlockState BASE_EMBEDDED { |
| public: |
| BlockState(Scope** scope_stack, Scope* scope) |
| : scope_stack_(scope_stack), outer_scope_(*scope_stack) { |
| *scope_stack_ = scope; |
| } |
| ~BlockState() { *scope_stack_ = outer_scope_; } |
| |
| private: |
| Scope** scope_stack_; |
| Scope* outer_scope_; |
| }; |
| |
| struct DestructuringAssignment { |
| public: |
| DestructuringAssignment(ExpressionT expression, Scope* scope) |
| : assignment(expression), scope(scope) {} |
| |
| ExpressionT assignment; |
| Scope* scope; |
| }; |
| |
| class FunctionState BASE_EMBEDDED { |
| public: |
| FunctionState(FunctionState** function_state_stack, Scope** scope_stack, |
| Scope* scope, FunctionKind kind, |
| typename Traits::Type::Factory* factory); |
| ~FunctionState(); |
| |
| int NextMaterializedLiteralIndex() { |
| return next_materialized_literal_index_++; |
| } |
| int materialized_literal_count() { |
| return next_materialized_literal_index_; |
| } |
| |
| void SkipMaterializedLiterals(int count) { |
| next_materialized_literal_index_ += count; |
| } |
| |
| void AddProperty() { expected_property_count_++; } |
| int expected_property_count() { return expected_property_count_; } |
| |
| Scanner::Location this_location() const { return this_location_; } |
| Scanner::Location super_location() const { return super_location_; } |
| Scanner::Location return_location() const { return return_location_; } |
| void set_this_location(Scanner::Location location) { |
| this_location_ = location; |
| } |
| void set_super_location(Scanner::Location location) { |
| super_location_ = location; |
| } |
| void set_return_location(Scanner::Location location) { |
| return_location_ = location; |
| } |
| |
| bool is_generator() const { return IsGeneratorFunction(kind_); } |
| |
| FunctionKind kind() const { return kind_; } |
| FunctionState* outer() const { return outer_function_state_; } |
| |
| void set_generator_object_variable( |
| typename Traits::Type::GeneratorVariable* variable) { |
| DCHECK(variable != NULL); |
| DCHECK(is_generator()); |
| generator_object_variable_ = variable; |
| } |
| typename Traits::Type::GeneratorVariable* generator_object_variable() |
| const { |
| return generator_object_variable_; |
| } |
| |
| typename Traits::Type::Factory* factory() { return factory_; } |
| |
| const List<DestructuringAssignment>& destructuring_assignments_to_rewrite() |
| const { |
| return destructuring_assignments_to_rewrite_; |
| } |
| |
| void AddDestructuringAssignment(DestructuringAssignment pair) { |
| destructuring_assignments_to_rewrite_.Add(pair); |
| } |
| |
| private: |
| // Used to assign an index to each literal that needs materialization in |
| // the function. Includes regexp literals, and boilerplate for object and |
| // array literals. |
| int next_materialized_literal_index_; |
| |
| // Properties count estimation. |
| int expected_property_count_; |
| |
| // Location of most recent use of 'this' (invalid if none). |
| Scanner::Location this_location_; |
| |
| // Location of most recent 'return' statement (invalid if none). |
| Scanner::Location return_location_; |
| |
| // Location of call to the "super" constructor (invalid if none). |
| Scanner::Location super_location_; |
| |
| FunctionKind kind_; |
| // For generators, this variable may hold the generator object. It variable |
| // is used by yield expressions and return statements. It is not necessary |
| // for generator functions to have this variable set. |
| Variable* generator_object_variable_; |
| |
| FunctionState** function_state_stack_; |
| FunctionState* outer_function_state_; |
| Scope** scope_stack_; |
| Scope* outer_scope_; |
| |
| List<DestructuringAssignment> destructuring_assignments_to_rewrite_; |
| |
| void RewriteDestructuringAssignments(); |
| |
| typename Traits::Type::Factory* factory_; |
| |
| friend class ParserTraits; |
| friend class Checkpoint; |
| }; |
| |
| // Annoyingly, arrow functions first parse as comma expressions, then when we |
| // see the => we have to go back and reinterpret the arguments as being formal |
| // parameters. To do so we need to reset some of the parser state back to |
| // what it was before the arguments were first seen. |
| class Checkpoint BASE_EMBEDDED { |
| public: |
| explicit Checkpoint(ParserBase* parser) { |
| function_state_ = parser->function_state_; |
| next_materialized_literal_index_ = |
| function_state_->next_materialized_literal_index_; |
| expected_property_count_ = function_state_->expected_property_count_; |
| } |
| |
| void Restore(int* materialized_literal_index_delta) { |
| *materialized_literal_index_delta = |
| function_state_->next_materialized_literal_index_ - |
| next_materialized_literal_index_; |
| function_state_->next_materialized_literal_index_ = |
| next_materialized_literal_index_; |
| function_state_->expected_property_count_ = expected_property_count_; |
| } |
| |
| private: |
| FunctionState* function_state_; |
| int next_materialized_literal_index_; |
| int expected_property_count_; |
| }; |
| |
| class ParsingModeScope BASE_EMBEDDED { |
| public: |
| ParsingModeScope(ParserBase* parser, Mode mode) |
| : parser_(parser), |
| old_mode_(parser->mode()) { |
| parser_->mode_ = mode; |
| } |
| ~ParsingModeScope() { |
| parser_->mode_ = old_mode_; |
| } |
| |
| private: |
| ParserBase* parser_; |
| Mode old_mode_; |
| }; |
| |
| Scope* NewScope(Scope* parent, ScopeType scope_type) { |
| // Must always pass the function kind for FUNCTION_SCOPE. |
| DCHECK(scope_type != FUNCTION_SCOPE); |
| return NewScope(parent, scope_type, kNormalFunction); |
| } |
| |
| Scope* NewScope(Scope* parent, ScopeType scope_type, FunctionKind kind) { |
| DCHECK(ast_value_factory()); |
| DCHECK(scope_type != MODULE_SCOPE || FLAG_harmony_modules); |
| Scope* result = new (zone()) |
| Scope(zone(), parent, scope_type, ast_value_factory(), kind); |
| result->Initialize(); |
| return result; |
| } |
| |
| Scanner* scanner() const { return scanner_; } |
| AstValueFactory* ast_value_factory() const { return ast_value_factory_; } |
| int position() { return scanner_->location().beg_pos; } |
| int peek_position() { return scanner_->peek_location().beg_pos; } |
| bool stack_overflow() const { return stack_overflow_; } |
| void set_stack_overflow() { stack_overflow_ = true; } |
| Mode mode() const { return mode_; } |
| Zone* zone() const { return zone_; } |
| |
| INLINE(Token::Value peek()) { |
| if (stack_overflow_) return Token::ILLEGAL; |
| return scanner()->peek(); |
| } |
| |
| 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. |
| stack_overflow_ = true; |
| } |
| } |
| 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; |
| } |
| Expect(Token::SEMICOLON, ok); |
| } |
| |
| bool peek_any_identifier() { |
| Token::Value next = peek(); |
| return next == Token::IDENTIFIER || next == Token::FUTURE_RESERVED_WORD || |
| next == Token::FUTURE_STRICT_RESERVED_WORD || next == Token::LET || |
| next == Token::STATIC || next == Token::YIELD; |
| } |
| |
| bool CheckContextualKeyword(Vector<const char> keyword) { |
| if (PeekContextualKeyword(keyword)) { |
| Consume(Token::IDENTIFIER); |
| return true; |
| } |
| return false; |
| } |
| |
| bool PeekContextualKeyword(Vector<const char> keyword) { |
| return peek() == Token::IDENTIFIER && |
| scanner()->is_next_contextual_keyword(keyword); |
| } |
| |
| void ExpectContextualKeyword(Vector<const char> keyword, bool* ok) { |
| Expect(Token::IDENTIFIER, ok); |
| if (!*ok) return; |
| if (!scanner()->is_literal_contextual_keyword(keyword)) { |
| ReportUnexpectedToken(scanner()->current_token()); |
| *ok = false; |
| } |
| } |
| |
| bool CheckInOrOf(ForEachStatement::VisitMode* visit_mode, bool* ok) { |
| if (Check(Token::IN)) { |
| if (is_strong(language_mode())) { |
| ReportMessageAt(scanner()->location(), MessageTemplate::kStrongForIn); |
| *ok = false; |
| } else { |
| *visit_mode = ForEachStatement::ENUMERATE; |
| } |
| return true; |
| } else if (CheckContextualKeyword(CStrVector("of"))) { |
| *visit_mode = ForEachStatement::ITERATE; |
| return true; |
| } |
| return false; |
| } |
| |
| // Checks whether an octal literal was last seen between beg_pos and end_pos. |
| // If so, reports an error. Only called for strict mode and template strings. |
| void CheckOctalLiteral(int beg_pos, int end_pos, |
| MessageTemplate::Template message, bool* ok) { |
| Scanner::Location octal = scanner()->octal_position(); |
| if (octal.IsValid() && beg_pos <= octal.beg_pos && |
| octal.end_pos <= end_pos) { |
| ReportMessageAt(octal, message); |
| scanner()->clear_octal_position(); |
| *ok = false; |
| } |
| } |
| |
| inline void CheckStrictOctalLiteral(int beg_pos, int end_pos, bool* ok) { |
| CheckOctalLiteral(beg_pos, end_pos, MessageTemplate::kStrictOctalLiteral, |
| ok); |
| } |
| |
| inline void CheckTemplateOctalLiteral(int beg_pos, int end_pos, bool* ok) { |
| CheckOctalLiteral(beg_pos, end_pos, MessageTemplate::kTemplateOctalLiteral, |
| ok); |
| } |
| |
| void CheckDestructuringElement(ExpressionT element, |
| ExpressionClassifier* classifier, 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 (function_name_validity == kSkipFunctionNameCheck) return; |
| // The function name needs to be checked in strict mode. |
| if (is_sloppy(language_mode)) return; |
| |
| if (this->IsEvalOrArguments(function_name)) { |
| Traits::ReportMessageAt(function_name_loc, |
| MessageTemplate::kStrictEvalArguments); |
| *ok = false; |
| return; |
| } |
| if (function_name_validity == kFunctionNameIsStrictReserved) { |
| Traits::ReportMessageAt(function_name_loc, |
| MessageTemplate::kUnexpectedStrictReserved); |
| *ok = false; |
| return; |
| } |
| if (is_strong(language_mode) && this->IsUndefined(function_name)) { |
| Traits::ReportMessageAt(function_name_loc, |
| MessageTemplate::kStrongUndefined); |
| *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 Traits::Type::Factory* factory() { |
| return function_state_->factory(); |
| } |
| |
| LanguageMode language_mode() { return scope_->language_mode(); } |
| bool is_generator() const { return function_state_->is_generator(); } |
| |
| bool allow_const() { |
| return is_strict(language_mode()) || allow_harmony_sloppy() || |
| allow_legacy_const(); |
| } |
| |
| bool allow_let() { |
| return is_strict(language_mode()) || allow_harmony_sloppy_let(); |
| } |
| |
| // Report syntax errors. |
| void ReportMessage(MessageTemplate::Template message, const char* arg = NULL, |
| ParseErrorType error_type = kSyntaxError) { |
| Scanner::Location source_location = scanner()->location(); |
| Traits::ReportMessageAt(source_location, message, arg, error_type); |
| } |
| |
| void ReportMessageAt(Scanner::Location location, |
| MessageTemplate::Template message, |
| ParseErrorType error_type = kSyntaxError) { |
| Traits::ReportMessageAt(location, message, reinterpret_cast<const char*>(0), |
| error_type); |
| } |
| |
| void GetUnexpectedTokenMessage( |
| Token::Value token, MessageTemplate::Template* message, 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 ExpressionClassifier::Error& error) { |
| Traits::ReportMessageAt(error.location, error.message, error.arg, |
| error.type); |
| } |
| |
| void ValidateExpression(const ExpressionClassifier* classifier, bool* ok) { |
| if (!classifier->is_valid_expression() || |
| classifier->has_cover_initialized_name()) { |
| const Scanner::Location& a = classifier->expression_error().location; |
| const Scanner::Location& b = |
| classifier->cover_initialized_name_error().location; |
| if (a.beg_pos < 0 || (b.beg_pos >= 0 && a.beg_pos > b.beg_pos)) { |
| ReportClassifierError(classifier->cover_initialized_name_error()); |
| } else { |
| ReportClassifierError(classifier->expression_error()); |
| } |
| *ok = false; |
| } |
| } |
| |
| void ValidateFormalParameterInitializer( |
| const ExpressionClassifier* classifier, bool* ok) { |
| if (!classifier->is_valid_formal_parameter_initializer()) { |
| ReportClassifierError(classifier->formal_parameter_initializer_error()); |
| *ok = false; |
| } |
| } |
| |
| void ValidateBindingPattern(const ExpressionClassifier* classifier, |
| bool* ok) { |
| if (!classifier->is_valid_binding_pattern()) { |
| ReportClassifierError(classifier->binding_pattern_error()); |
| *ok = false; |
| } |
| } |
| |
| void ValidateAssignmentPattern(const ExpressionClassifier* classifier, |
| bool* ok) { |
| if (!classifier->is_valid_assignment_pattern()) { |
| ReportClassifierError(classifier->assignment_pattern_error()); |
| *ok = false; |
| } |
| } |
| |
| void ValidateFormalParameters(const ExpressionClassifier* classifier, |
| 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; |
| } else if (is_strong(language_mode) && |
| !classifier->is_valid_strong_mode_formal_parameters()) { |
| ReportClassifierError(classifier->strong_mode_formal_parameter_error()); |
| *ok = false; |
| } |
| } |
| |
| void ValidateArrowFormalParameters(const ExpressionClassifier* classifier, |
| ExpressionT expr, |
| bool parenthesized_formals, bool* ok) { |
| if (classifier->is_valid_binding_pattern()) { |
| // A simple arrow formal parameter: IDENTIFIER => BODY. |
| if (!this->IsIdentifier(expr)) { |
| Traits::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 ExpressionClassifier::Error& error = |
| parenthesized_formals ? classifier->arrow_formal_parameters_error() |
| : classifier->binding_pattern_error(); |
| ReportClassifierError(error); |
| *ok = false; |
| } |
| } |
| |
| void ValidateLetPattern(const ExpressionClassifier* classifier, bool* ok) { |
| if (!classifier->is_valid_let_pattern()) { |
| ReportClassifierError(classifier->let_pattern_error()); |
| *ok = false; |
| } |
| } |
| |
| void ExpressionUnexpectedToken(ExpressionClassifier* classifier) { |
| MessageTemplate::Template message = MessageTemplate::kUnexpectedToken; |
| const char* arg; |
| GetUnexpectedTokenMessage(peek(), &message, &arg); |
| classifier->RecordExpressionError(scanner()->peek_location(), message, arg); |
| } |
| |
| void BindingPatternUnexpectedToken(ExpressionClassifier* classifier) { |
| MessageTemplate::Template message = MessageTemplate::kUnexpectedToken; |
| const char* arg; |
| GetUnexpectedTokenMessage(peek(), &message, &arg); |
| classifier->RecordBindingPatternError(scanner()->peek_location(), message, |
| arg); |
| } |
| |
| void ArrowFormalParametersUnexpectedToken(ExpressionClassifier* classifier) { |
| MessageTemplate::Template message = MessageTemplate::kUnexpectedToken; |
| const char* arg; |
| GetUnexpectedTokenMessage(peek(), &message, &arg); |
| classifier->RecordArrowFormalParametersError(scanner()->peek_location(), |
| message, arg); |
| } |
| |
| void FormalParameterInitializerUnexpectedToken( |
| ExpressionClassifier* classifier) { |
| MessageTemplate::Template message = MessageTemplate::kUnexpectedToken; |
| const char* arg; |
| GetUnexpectedTokenMessage(peek(), &message, &arg); |
| classifier->RecordFormalParameterInitializerError( |
| scanner()->peek_location(), message, arg); |
| } |
| |
| // Recursive descent functions: |
| |
| // 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(ExpressionClassifier* classifier, |
| bool* ok); |
| // Parses an identifier or a strict mode future reserved word, and indicate |
| // whether it is strict mode future reserved. Allows passing in is_generator |
| // for the case of parsing the identifier in a function expression, where the |
| // relevant "is_generator" bit is of the function being parsed, not the |
| // containing |
| // function. |
| IdentifierT ParseIdentifierOrStrictReservedWord(bool is_generator, |
| bool* is_strict_reserved, |
| bool* ok); |
| IdentifierT ParseIdentifierOrStrictReservedWord(bool* is_strict_reserved, |
| bool* ok) { |
| return ParseIdentifierOrStrictReservedWord(this->is_generator(), |
| is_strict_reserved, ok); |
| } |
| |
| IdentifierT ParseIdentifierName(bool* ok); |
| // Parses an identifier and determines whether or not it is 'get' or 'set'. |
| IdentifierT ParseIdentifierNameOrGetOrSet(bool* is_get, bool* is_set, |
| bool* ok); |
| |
| |
| ExpressionT ParseRegExpLiteral(bool seen_equal, |
| ExpressionClassifier* classifier, bool* ok); |
| |
| ExpressionT ParsePrimaryExpression(ExpressionClassifier* classifier, |
| bool* ok); |
| ExpressionT ParseExpression(bool accept_IN, bool* ok); |
| ExpressionT ParseExpression(bool accept_IN, ExpressionClassifier* classifier, |
| bool* ok); |
| ExpressionT ParseExpression(bool accept_IN, int flags, |
| ExpressionClassifier* classifier, bool* ok); |
| ExpressionT ParseArrayLiteral(ExpressionClassifier* classifier, bool* ok); |
| ExpressionT ParsePropertyName(IdentifierT* name, bool* is_get, bool* is_set, |
| bool* is_static, bool* is_computed_name, |
| bool* is_identifier, bool* is_escaped_keyword, |
| ExpressionClassifier* classifier, bool* ok); |
| ExpressionT ParseObjectLiteral(ExpressionClassifier* classifier, bool* ok); |
| ObjectLiteralPropertyT ParsePropertyDefinition( |
| ObjectLiteralCheckerBase* checker, bool in_class, bool has_extends, |
| bool is_static, bool* is_computed_name, bool* has_seen_constructor, |
| ExpressionClassifier* classifier, IdentifierT* name, bool* ok); |
| typename Traits::Type::ExpressionList ParseArguments( |
| Scanner::Location* first_spread_pos, ExpressionClassifier* classifier, |
| bool* ok); |
| |
| enum AssignmentExpressionFlags { |
| kIsLeftHandSide = 0, |
| kIsRightHandSide = 1 << 0, |
| kIsPatternElement = 1 << 1, |
| kIsPossibleArrowFormals = 1 << 2 |
| }; |
| |
| ExpressionT ParseAssignmentExpression(bool accept_IN, int flags, |
| ExpressionClassifier* classifier, |
| bool* ok); |
| ExpressionT ParseAssignmentExpression(bool accept_IN, |
| ExpressionClassifier* classifier, |
| bool* ok) { |
| return ParseAssignmentExpression(accept_IN, kIsLeftHandSide, classifier, |
| ok); |
| } |
| ExpressionT ParseYieldExpression(ExpressionClassifier* classifier, bool* ok); |
| ExpressionT ParseConditionalExpression(bool accept_IN, |
| ExpressionClassifier* classifier, |
| bool* ok); |
| ExpressionT ParseBinaryExpression(int prec, bool accept_IN, |
| ExpressionClassifier* classifier, bool* ok); |
| ExpressionT ParseUnaryExpression(ExpressionClassifier* classifier, bool* ok); |
| ExpressionT ParsePostfixExpression(ExpressionClassifier* classifier, |
| bool* ok); |
| ExpressionT ParseLeftHandSideExpression(ExpressionClassifier* classifier, |
| bool* ok); |
| ExpressionT ParseMemberWithNewPrefixesExpression( |
| ExpressionClassifier* classifier, bool* ok); |
| ExpressionT ParseMemberExpression(ExpressionClassifier* classifier, bool* ok); |
| ExpressionT ParseMemberExpressionContinuation( |
| ExpressionT expression, ExpressionClassifier* classifier, bool* ok); |
| ExpressionT ParseArrowFunctionLiteral(bool accept_IN, |
| const FormalParametersT& parameters, |
| const ExpressionClassifier& classifier, |
| bool* ok); |
| ExpressionT ParseTemplateLiteral(ExpressionT tag, int start, |
| ExpressionClassifier* classifier, bool* ok); |
| void AddTemplateExpression(ExpressionT); |
| ExpressionT ParseSuperExpression(bool is_new, |
| ExpressionClassifier* classifier, bool* ok); |
| ExpressionT ParseNewTargetExpression(bool* ok); |
| ExpressionT ParseStrongInitializationExpression( |
| ExpressionClassifier* classifier, bool* ok); |
| ExpressionT ParseStrongSuperCallExpression(ExpressionClassifier* classifier, |
| bool* ok); |
| |
| void ParseFormalParameter(FormalParametersT* parameters, |
| ExpressionClassifier* classifier, bool* ok); |
| void ParseFormalParameterList(FormalParametersT* parameters, |
| ExpressionClassifier* classifier, bool* ok); |
| void CheckArityRestrictions( |
| int param_count, FunctionLiteral::ArityRestriction arity_restriction, |
| bool has_rest, int formals_start_pos, int formals_end_pos, bool* ok); |
| |
| bool IsNextLetKeyword(); |
| |
| // Checks if the expression is a valid reference expression (e.g., on the |
| // left-hand side of assignments). Although ruled out by ECMA as early errors, |
| // we allow calls for web compatibility and rewrite them to a runtime throw. |
| ExpressionT CheckAndRewriteReferenceExpression( |
| ExpressionT expression, int beg_pos, int end_pos, |
| MessageTemplate::Template message, bool* ok); |
| ExpressionT ClassifyAndRewriteReferenceExpression( |
| ExpressionClassifier* classifier, ExpressionT expression, int beg_pos, |
| int end_pos, MessageTemplate::Template message, |
| ParseErrorType type = kSyntaxError); |
| 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 (!Traits::IsIdentifier(expression)) return false; |
| if (is_strict(language_mode()) && |
| Traits::IsEvalOrArguments(Traits::AsIdentifier(expression))) { |
| return false; |
| } |
| if (is_strong(language_mode()) && |
| Traits::IsUndefined(Traits::AsIdentifier(expression))) { |
| return false; |
| } |
| return true; |
| } |
| |
| // 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. |
| void CheckPossibleEvalCall(ExpressionT expression, Scope* scope) { |
| if (Traits::IsIdentifier(expression) && |
| Traits::IsEval(Traits::AsIdentifier(expression))) { |
| scope->DeclarationScope()->RecordEvalCall(); |
| scope->RecordEvalCall(); |
| } |
| } |
| |
| // Used to validate property names in object literals and class literals |
| enum PropertyKind { |
| kAccessorProperty, |
| kValueProperty, |
| kMethodProperty |
| }; |
| |
| class ObjectLiteralCheckerBase { |
| public: |
| explicit ObjectLiteralCheckerBase(ParserBase* parser) : parser_(parser) {} |
| |
| virtual void CheckProperty(Token::Value property, PropertyKind type, |
| bool is_static, bool is_generator, bool* ok) = 0; |
| |
| virtual ~ObjectLiteralCheckerBase() {} |
| |
| protected: |
| ParserBase* parser() const { return parser_; } |
| Scanner* scanner() const { return parser_->scanner(); } |
| |
| private: |
| ParserBase* parser_; |
| }; |
| |
| // Validation per ES6 object literals. |
| class ObjectLiteralChecker : public ObjectLiteralCheckerBase { |
| public: |
| explicit ObjectLiteralChecker(ParserBase* parser) |
| : ObjectLiteralCheckerBase(parser), has_seen_proto_(false) {} |
| |
| void CheckProperty(Token::Value property, PropertyKind type, bool is_static, |
| bool is_generator, bool* ok) override; |
| |
| private: |
| bool IsProto() { return this->scanner()->LiteralMatches("__proto__", 9); } |
| |
| bool has_seen_proto_; |
| }; |
| |
| // Validation per ES6 class literals. |
| class ClassLiteralChecker : public ObjectLiteralCheckerBase { |
| public: |
| explicit ClassLiteralChecker(ParserBase* parser) |
| : ObjectLiteralCheckerBase(parser), has_seen_constructor_(false) {} |
| |
| void CheckProperty(Token::Value property, PropertyKind type, bool is_static, |
| bool is_generator, bool* ok) override; |
| |
| private: |
| bool IsConstructor() { |
| return this->scanner()->LiteralMatches("constructor", 11); |
| } |
| bool IsPrototype() { |
| return this->scanner()->LiteralMatches("prototype", 9); |
| } |
| |
| bool has_seen_constructor_; |
| }; |
| |
| // If true, the next (and immediately following) function literal is |
| // preceded by a parenthesis. |
| // Heuristically that means that the function will be called immediately, |
| // so never lazily compile it. |
| bool parenthesized_function_; |
| |
| Scope* scope_; // Scope stack. |
| FunctionState* function_state_; // Function state stack. |
| v8::Extension* extension_; |
| FuncNameInferrer* fni_; |
| AstValueFactory* ast_value_factory_; // Not owned. |
| ParserRecorder* log_; |
| Mode mode_; |
| uintptr_t stack_limit_; |
| |
| private: |
| Zone* zone_; |
| |
| Scanner* scanner_; |
| bool stack_overflow_; |
| |
| bool allow_lazy_; |
| bool allow_natives_; |
| bool allow_harmony_sloppy_; |
| bool allow_harmony_sloppy_function_; |
| bool allow_harmony_sloppy_let_; |
| bool allow_harmony_default_parameters_; |
| bool allow_harmony_destructuring_bind_; |
| bool allow_harmony_destructuring_assignment_; |
| bool allow_strong_mode_; |
| bool allow_legacy_const_; |
| bool allow_harmony_do_expressions_; |
| bool allow_harmony_function_name_; |
| }; |
| |
| |
| template <class Traits> |
| ParserBase<Traits>::FunctionState::FunctionState( |
| FunctionState** function_state_stack, Scope** scope_stack, Scope* scope, |
| FunctionKind kind, typename Traits::Type::Factory* factory) |
| : next_materialized_literal_index_(0), |
| expected_property_count_(0), |
| this_location_(Scanner::Location::invalid()), |
| return_location_(Scanner::Location::invalid()), |
| super_location_(Scanner::Location::invalid()), |
| kind_(kind), |
| generator_object_variable_(NULL), |
| function_state_stack_(function_state_stack), |
| outer_function_state_(*function_state_stack), |
| scope_stack_(scope_stack), |
| outer_scope_(*scope_stack), |
| factory_(factory) { |
| *scope_stack_ = scope; |
| *function_state_stack = this; |
| } |
| |
| |
| template <class Traits> |
| ParserBase<Traits>::FunctionState::~FunctionState() { |
| *scope_stack_ = outer_scope_; |
| *function_state_stack_ = outer_function_state_; |
| } |
| |
| |
| template <class Traits> |
| void ParserBase<Traits>::GetUnexpectedTokenMessage( |
| Token::Value token, MessageTemplate::Template* message, const char** arg, |
| MessageTemplate::Template default_) { |
| // Four of the tokens are treated specially |
| switch (token) { |
| case Token::EOS: |
| *message = MessageTemplate::kUnexpectedEOS; |
| *arg = nullptr; |
| break; |
| case Token::SMI: |
| case Token::NUMBER: |
| *message = MessageTemplate::kUnexpectedTokenNumber; |
| *arg = nullptr; |
| break; |
| case Token::STRING: |
| *message = MessageTemplate::kUnexpectedTokenString; |
| *arg = nullptr; |
| break; |
| case Token::IDENTIFIER: |
| *message = MessageTemplate::kUnexpectedTokenIdentifier; |
| *arg = nullptr; |
| break; |
| case Token::FUTURE_RESERVED_WORD: |
| *message = MessageTemplate::kUnexpectedReserved; |
| *arg = nullptr; |
| 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; |
| *arg = nullptr; |
| break; |
| case Token::TEMPLATE_SPAN: |
| case Token::TEMPLATE_TAIL: |
| *message = MessageTemplate::kUnexpectedTemplateString; |
| *arg = nullptr; |
| break; |
| case Token::ESCAPED_STRICT_RESERVED_WORD: |
| case Token::ESCAPED_KEYWORD: |
| *message = MessageTemplate::kInvalidEscapedReservedWord; |
| *arg = nullptr; |
| break; |
| default: |
| const char* name = Token::String(token); |
| DCHECK(name != NULL); |
| *arg = name; |
| break; |
| } |
| } |
| |
| |
| template <class Traits> |
| void ParserBase<Traits>::ReportUnexpectedToken(Token::Value token) { |
| return ReportUnexpectedTokenAt(scanner_->location(), token); |
| } |
| |
| |
| template <class Traits> |
| void ParserBase<Traits>::ReportUnexpectedTokenAt( |
| Scanner::Location source_location, Token::Value token, |
| MessageTemplate::Template message) { |
| const char* arg; |
| GetUnexpectedTokenMessage(token, &message, &arg); |
| Traits::ReportMessageAt(source_location, message, arg); |
| } |
| |
| |
| template <class Traits> |
| typename ParserBase<Traits>::IdentifierT ParserBase<Traits>::ParseIdentifier( |
| AllowRestrictedIdentifiers allow_restricted_identifiers, bool* ok) { |
| ExpressionClassifier classifier; |
| auto result = ParseAndClassifyIdentifier(&classifier, ok); |
| if (!*ok) return Traits::EmptyIdentifier(); |
| |
| if (allow_restricted_identifiers == kDontAllowRestrictedIdentifiers) { |
| ValidateAssignmentPattern(&classifier, ok); |
| if (!*ok) return Traits::EmptyIdentifier(); |
| ValidateBindingPattern(&classifier, ok); |
| if (!*ok) return Traits::EmptyIdentifier(); |
| } else { |
| ValidateExpression(&classifier, ok); |
| if (!*ok) return Traits::EmptyIdentifier(); |
| } |
| |
| return result; |
| } |
| |
| |
| template <class Traits> |
| typename ParserBase<Traits>::IdentifierT |
| ParserBase<Traits>::ParseAndClassifyIdentifier(ExpressionClassifier* classifier, |
| bool* ok) { |
| Token::Value next = Next(); |
| if (next == Token::IDENTIFIER) { |
| IdentifierT name = this->GetSymbol(scanner()); |
| // 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 (this->IsEval(name)) { |
| classifier->RecordStrictModeFormalParameterError( |
| scanner()->location(), MessageTemplate::kStrictEvalArguments); |
| if (is_strict(language_mode())) { |
| classifier->RecordBindingPatternError( |
| scanner()->location(), MessageTemplate::kStrictEvalArguments); |
| } |
| } |
| if (this->IsArguments(name)) { |
| scope_->RecordArgumentsUsage(); |
| classifier->RecordStrictModeFormalParameterError( |
| scanner()->location(), MessageTemplate::kStrictEvalArguments); |
| if (is_strict(language_mode())) { |
| classifier->RecordBindingPatternError( |
| scanner()->location(), MessageTemplate::kStrictEvalArguments); |
| } |
| if (is_strong(language_mode())) { |
| classifier->RecordExpressionError(scanner()->location(), |
| MessageTemplate::kStrongArguments); |
| } |
| } |
| if (this->IsUndefined(name)) { |
| classifier->RecordStrongModeFormalParameterError( |
| scanner()->location(), MessageTemplate::kStrongUndefined); |
| if (is_strong(language_mode())) { |
| // TODO(dslomov): allow 'undefined' in nested patterns. |
| classifier->RecordBindingPatternError( |
| scanner()->location(), MessageTemplate::kStrongUndefined); |
| classifier->RecordAssignmentPatternError( |
| scanner()->location(), MessageTemplate::kStrongUndefined); |
| } |
| } |
| |
| if (classifier->duplicate_finder() != nullptr && |
| scanner()->FindSymbol(classifier->duplicate_finder(), 1) != 0) { |
| 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 Traits::EmptyIdentifier(); |
| } |
| if (next == Token::LET) { |
| classifier->RecordLetPatternError(scanner()->location(), |
| MessageTemplate::kLetInLexicalBinding); |
| } |
| return this->GetSymbol(scanner()); |
| } else { |
| this->ReportUnexpectedToken(next); |
| *ok = false; |
| return Traits::EmptyIdentifier(); |
| } |
| } |
| |
| |
| template <class Traits> |
| typename ParserBase<Traits>::IdentifierT |
| ParserBase<Traits>::ParseIdentifierOrStrictReservedWord( |
| bool is_generator, bool* is_strict_reserved, bool* ok) { |
| Token::Value next = Next(); |
| if (next == Token::IDENTIFIER) { |
| *is_strict_reserved = false; |
| } else if (next == Token::FUTURE_STRICT_RESERVED_WORD || next == Token::LET || |
| next == Token::STATIC || (next == Token::YIELD && !is_generator)) { |
| *is_strict_reserved = true; |
| } else { |
| ReportUnexpectedToken(next); |
| *ok = false; |
| return Traits::EmptyIdentifier(); |
| } |
| |
| IdentifierT name = this->GetSymbol(scanner()); |
| if (this->IsArguments(name)) scope_->RecordArgumentsUsage(); |
| return name; |
| } |
| |
| |
| template <class Traits> |
| typename ParserBase<Traits>::IdentifierT |
| ParserBase<Traits>::ParseIdentifierName(bool* ok) { |
| Token::Value next = Next(); |
| if (next != Token::IDENTIFIER && next != Token::FUTURE_RESERVED_WORD && |
| 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)) { |
| this->ReportUnexpectedToken(next); |
| *ok = false; |
| return Traits::EmptyIdentifier(); |
| } |
| |
| IdentifierT name = this->GetSymbol(scanner()); |
| if (this->IsArguments(name)) scope_->RecordArgumentsUsage(); |
| return name; |
| } |
| |
| |
| template <class Traits> |
| typename ParserBase<Traits>::IdentifierT |
| ParserBase<Traits>::ParseIdentifierNameOrGetOrSet(bool* is_get, |
| bool* is_set, |
| bool* ok) { |
| IdentifierT result = ParseIdentifierName(ok); |
| if (!*ok) return Traits::EmptyIdentifier(); |
| scanner()->IsGetOrSet(is_get, is_set); |
| return result; |
| } |
| |
| |
| template <class Traits> |
| typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseRegExpLiteral( |
| bool seen_equal, ExpressionClassifier* classifier, bool* ok) { |
| int pos = peek_position(); |
| if (!scanner()->ScanRegExpPattern(seen_equal)) { |
| Next(); |
| ReportMessage(MessageTemplate::kUnterminatedRegExp); |
| *ok = false; |
| return Traits::EmptyExpression(); |
| } |
| |
| int literal_index = function_state_->NextMaterializedLiteralIndex(); |
| |
| IdentifierT js_pattern = this->GetNextSymbol(scanner()); |
| Maybe<RegExp::Flags> flags = scanner()->ScanRegExpFlags(); |
| if (flags.IsNothing()) { |
| Next(); |
| ReportMessage(MessageTemplate::kMalformedRegExpFlags); |
| *ok = false; |
| return Traits::EmptyExpression(); |
| } |
| int js_flags = flags.FromJust(); |
| Next(); |
| return factory()->NewRegExpLiteral(js_pattern, js_flags, literal_index, |
| is_strong(language_mode()), pos); |
| } |
| |
| |
| #define CHECK_OK ok); \ |
| if (!*ok) return this->EmptyExpression(); \ |
| ((void)0 |
| #define DUMMY ) // to make indentation work |
| #undef DUMMY |
| |
| // Used in functions where the return type is not ExpressionT. |
| #define CHECK_OK_CUSTOM(x) ok); \ |
| if (!*ok) return this->x(); \ |
| ((void)0 |
| #define DUMMY ) // to make indentation work |
| #undef DUMMY |
| |
| |
| template <class Traits> |
| typename ParserBase<Traits>::ExpressionT |
| ParserBase<Traits>::ParsePrimaryExpression(ExpressionClassifier* classifier, |
| bool* ok) { |
| // PrimaryExpression :: |
| // 'this' |
| // 'null' |
| // 'true' |
| // 'false' |
| // Identifier |
| // Number |
| // String |
| // ArrayLiteral |
| // ObjectLiteral |
| // RegExpLiteral |
| // ClassLiteral |
| // '(' Expression ')' |
| // TemplateLiteral |
| // do Block |
| |
| int beg_pos = peek_position(); |
| switch (peek()) { |
| case Token::THIS: { |
| BindingPatternUnexpectedToken(classifier); |
| Consume(Token::THIS); |
| if (FLAG_strong_this && is_strong(language_mode())) { |
| // Constructors' usages of 'this' in strong mode are parsed separately. |
| // TODO(rossberg): this does not work with arrow functions yet. |
| if (IsClassConstructor(function_state_->kind())) { |
| ReportMessage(MessageTemplate::kStrongConstructorThis); |
| *ok = false; |
| return this->EmptyExpression(); |
| } |
| } |
| return this->ThisExpression(scope_, factory(), beg_pos); |
| } |
| |
| case Token::NULL_LITERAL: |
| case Token::TRUE_LITERAL: |
| case Token::FALSE_LITERAL: |
| BindingPatternUnexpectedToken(classifier); |
| return this->ExpressionFromLiteral(Next(), beg_pos, scanner(), factory()); |
| case Token::SMI: |
| case Token::NUMBER: |
| classifier->RecordBindingPatternError( |
| scanner()->peek_location(), MessageTemplate::kUnexpectedTokenNumber); |
| return this->ExpressionFromLiteral(Next(), beg_pos, scanner(), factory()); |
| |
| case Token::IDENTIFIER: |
| case Token::LET: |
| case Token::STATIC: |
| case Token::YIELD: |
| 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(classifier, CHECK_OK); |
| return this->ExpressionFromIdentifier( |
| name, beg_pos, scanner()->location().end_pos, scope_, factory()); |
| } |
| |
| case Token::STRING: { |
| classifier->RecordBindingPatternError( |
| scanner()->peek_location(), MessageTemplate::kUnexpectedTokenString); |
| Consume(Token::STRING); |
| return this->ExpressionFromString(beg_pos, scanner(), factory()); |
| } |
| |
| case Token::ASSIGN_DIV: |
| classifier->RecordBindingPatternError( |
| scanner()->peek_location(), MessageTemplate::kUnexpectedTokenRegExp); |
| return this->ParseRegExpLiteral(true, classifier, ok); |
| |
| case Token::DIV: |
| classifier->RecordBindingPatternError( |
| scanner()->peek_location(), MessageTemplate::kUnexpectedTokenRegExp); |
| return this->ParseRegExpLiteral(false, classifier, ok); |
| |
| case Token::LBRACK: |
| if (!allow_harmony_destructuring_bind()) { |
| BindingPatternUnexpectedToken(classifier); |
| } |
| return this->ParseArrayLiteral(classifier, ok); |
| |
| case Token::LBRACE: |
| if (!allow_harmony_destructuring_bind()) { |
| BindingPatternUnexpectedToken(classifier); |
| } |
| return this->ParseObjectLiteral(classifier, 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. |
| if (!classifier->is_valid_binding_pattern()) { |
| ArrowFormalParametersUnexpectedToken(classifier); |
| } |
| BindingPatternUnexpectedToken(classifier); |
| 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)); |
| classifier->RecordBindingPatternError(scanner()->location(), |
| MessageTemplate::kUnexpectedToken, |
| Token::String(Token::RPAREN)); |
| return factory()->NewEmptyParentheses(beg_pos); |
| } else if (Check(Token::ELLIPSIS)) { |
| // (...x)=>x. The continuation that looks for the => is in |
| // ParseAssignmentExpression. |
| int ellipsis_pos = position(); |
| classifier->RecordExpressionError(scanner()->location(), |
| MessageTemplate::kUnexpectedToken, |
| Token::String(Token::ELLIPSIS)); |
| classifier->RecordNonSimpleParameter(); |
| ExpressionT expr = |
| this->ParseAssignmentExpression(true, classifier, CHECK_OK); |
| if (peek() == Token::COMMA) { |
| ReportMessageAt(scanner()->peek_location(), |
| MessageTemplate::kParamAfterRest); |
| *ok = false; |
| return this->EmptyExpression(); |
| } |
| Expect(Token::RPAREN, CHECK_OK); |
| return factory()->NewSpread(expr, ellipsis_pos); |
| } |
| // Heuristically try to detect immediately called functions before |
| // seeing the call parentheses. |
| parenthesized_function_ = (peek() == Token::FUNCTION); |
| ExpressionT expr = this->ParseExpression(true, kIsPossibleArrowFormals, |
| classifier, CHECK_OK); |
| Expect(Token::RPAREN, CHECK_OK); |
| if (peek() != Token::ARROW) { |
| ValidateExpression(classifier, CHECK_OK); |
| expr->set_is_parenthesized(); |
| } |
| return expr; |
| } |
| |
| case Token::CLASS: { |
| BindingPatternUnexpectedToken(classifier); |
| Consume(Token::CLASS); |
| if (!allow_harmony_sloppy() && is_sloppy(language_mode())) { |
| ReportMessage(MessageTemplate::kSloppyLexical); |
| *ok = false; |
| return this->EmptyExpression(); |
| } |
| int class_token_position = position(); |
| IdentifierT name = this->EmptyIdentifier(); |
| bool is_strict_reserved_name = false; |
| Scanner::Location class_name_location = Scanner::Location::invalid(); |
| if (peek_any_identifier()) { |
| name = ParseIdentifierOrStrictReservedWord(&is_strict_reserved_name, |
| CHECK_OK); |
| class_name_location = scanner()->location(); |
| } |
| return this->ParseClassLiteral(name, class_name_location, |
| is_strict_reserved_name, |
| class_token_position, ok); |
| } |
| |
| case Token::TEMPLATE_SPAN: |
| case Token::TEMPLATE_TAIL: |
| classifier->RecordBindingPatternError( |
| scanner()->peek_location(), |
| MessageTemplate::kUnexpectedTemplateString); |
| return this->ParseTemplateLiteral(Traits::NoTemplateTag(), beg_pos, |
| classifier, ok); |
| |
| case Token::MOD: |
| if (allow_natives() || extension_ != NULL) { |
| BindingPatternUnexpectedToken(classifier); |
| return this->ParseV8Intrinsic(ok); |
| } |
| break; |
| |
| case Token::DO: |
| if (allow_harmony_do_expressions()) { |
| BindingPatternUnexpectedToken(classifier); |
| return Traits::ParseDoExpression(ok); |
| } |
| break; |
| |
| default: |
| break; |
| } |
| |
| ReportUnexpectedToken(Next()); |
| *ok = false; |
| return this->EmptyExpression(); |
| } |
| |
| |
| template <class Traits> |
| typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseExpression( |
| bool accept_IN, bool* ok) { |
| ExpressionClassifier classifier; |
| ExpressionT result = ParseExpression(accept_IN, &classifier, CHECK_OK); |
| ValidateExpression(&classifier, CHECK_OK); |
| return result; |
| } |
| |
| |
| template <class Traits> |
| typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseExpression( |
| bool accept_IN, ExpressionClassifier* classifier, bool* ok) { |
| return ParseExpression(accept_IN, kIsLeftHandSide, classifier, ok); |
| } |
| |
| |
| template <class Traits> |
| typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseExpression( |
| bool accept_IN, int flags, ExpressionClassifier* classifier, bool* ok) { |
| // Expression :: |
| // AssignmentExpression |
| // Expression ',' AssignmentExpression |
| |
| ExpressionClassifier binding_classifier; |
| ExpressionT result = this->ParseAssignmentExpression( |
| accept_IN, flags, &binding_classifier, CHECK_OK); |
| classifier->Accumulate(binding_classifier, |
| ExpressionClassifier::AllProductions); |
| bool is_simple_parameter_list = this->IsIdentifier(result); |
| bool seen_rest = false; |
| while (peek() == Token::COMMA) { |
| if (seen_rest) { |
| // At this point the production can't possibly be valid, but we don't know |
| // which error to signal. |
| classifier->RecordArrowFormalParametersError( |
| scanner()->peek_location(), MessageTemplate::kParamAfterRest); |
| } |
| Consume(Token::COMMA); |
| bool is_rest = false; |
| if (peek() == Token::ELLIPSIS) { |
| // 'x, y, ...z' in CoverParenthesizedExpressionAndArrowParameterList only |
| // as the formal parameters of'(x, y, ...z) => foo', and is not itself a |
| // valid expression or binding pattern. |
| ExpressionUnexpectedToken(classifier); |
| BindingPatternUnexpectedToken(classifier); |
| Consume(Token::ELLIPSIS); |
| seen_rest = is_rest = true; |
| } |
| int pos = position(); |
| ExpressionT right = this->ParseAssignmentExpression( |
| accept_IN, flags, &binding_classifier, CHECK_OK); |
| if (is_rest) right = factory()->NewSpread(right, pos); |
| is_simple_parameter_list = |
| is_simple_parameter_list && this->IsIdentifier(right); |
| classifier->Accumulate(binding_classifier, |
| ExpressionClassifier::AllProductions); |
| result = factory()->NewBinaryOperation(Token::COMMA, result, right, pos); |
| } |
| if (!is_simple_parameter_list || seen_rest) { |
| classifier->RecordNonSimpleParameter(); |
| } |
| |
| return result; |
| } |
| |
| |
| template <class Traits> |
| typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseArrayLiteral( |
| ExpressionClassifier* classifier, bool* ok) { |
| // ArrayLiteral :: |
| // '[' Expression? (',' Expression?)* ']' |
| |
| int pos = peek_position(); |
| typename Traits::Type::ExpressionList values = |
| this->NewExpressionList(4, zone_); |
| int first_spread_index = -1; |
| Expect(Token::LBRACK, CHECK_OK); |
| while (peek() != Token::RBRACK) { |
| ExpressionT elem = this->EmptyExpression(); |
| if (peek() == Token::COMMA) { |
| if (is_strong(language_mode())) { |
| ReportMessageAt(scanner()->peek_location(), |
| MessageTemplate::kStrongEllision); |
| *ok = false; |
| return this->EmptyExpression(); |
| } |
| elem = this->GetLiteralTheHole(peek_position(), factory()); |
| } else if (peek() == Token::ELLIPSIS) { |
| int start_pos = peek_position(); |
| Consume(Token::ELLIPSIS); |
| ExpressionT argument = |
| this->ParseAssignmentExpression(true, classifier, CHECK_OK); |
| elem = factory()->NewSpread(argument, start_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, classifier, start_pos, |
| scanner()->location().end_pos); |
| } |
| |
| if (peek() == Token::COMMA) { |
| classifier->RecordPatternError( |
| Scanner::Location(start_pos, scanner()->location().end_pos), |
| MessageTemplate::kElementAfterRest); |
| } |
| } else { |
| elem = this->ParseAssignmentExpression(true, kIsPatternElement, |
| classifier, CHECK_OK); |
| } |
| values->Add(elem, zone_); |
| if (peek() != Token::RBRACK) { |
| Expect(Token::COMMA, CHECK_OK); |
| } |
| } |
| Expect(Token::RBRACK, CHECK_OK); |
| |
| // Update the scope information before the pre-parsing bailout. |
| int literal_index = function_state_->NextMaterializedLiteralIndex(); |
| |
| return factory()->NewArrayLiteral(values, first_spread_index, literal_index, |
| is_strong(language_mode()), pos); |
| } |
| |
| |
| template <class Traits> |
| typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParsePropertyName( |
| IdentifierT* name, bool* is_get, bool* is_set, bool* is_static, |
| bool* is_computed_name, bool* is_identifier, bool* is_escaped_keyword, |
| ExpressionClassifier* classifier, bool* ok) { |
| Token::Value token = peek(); |
| int pos = peek_position(); |
| |
| // For non computed property names we normalize the name a bit: |
| // |
| // "12" -> 12 |
| // 12.3 -> "12.3" |
| // 12.30 -> "12.3" |
| // identifier -> "identifier" |
| // |
| // This is important because we use the property name as a key in a hash |
| // table when we compute constant properties. |
| switch (token) { |
| case Token::STRING: |
| Consume(Token::STRING); |
| *name = this->GetSymbol(scanner()); |
| break; |
| |
| case Token::SMI: |
| Consume(Token::SMI); |
| *name = this->GetNumberAsSymbol(scanner()); |
| break; |
| |
| case Token::NUMBER: |
| Consume(Token::NUMBER); |
| *name = this->GetNumberAsSymbol(scanner()); |
| break; |
| |
| case Token::LBRACK: { |
| *is_computed_name = true; |
| Consume(Token::LBRACK); |
| ExpressionClassifier computed_name_classifier; |
| ExpressionT expression = |
| ParseAssignmentExpression(true, &computed_name_classifier, CHECK_OK); |
| classifier->Accumulate(computed_name_classifier, |
| ExpressionClassifier::ExpressionProductions); |
| Expect(Token::RBRACK, CHECK_OK); |
| return expression; |
| } |
| |
| case Token::ESCAPED_KEYWORD: |
| *is_escaped_keyword = true; |
| *name = ParseIdentifierNameOrGetOrSet(is_get, is_set, CHECK_OK); |
| break; |
| |
| case Token::STATIC: |
| *is_static = true; |
| |
| // Fall through. |
| default: |
| *is_identifier = true; |
| *name = ParseIdentifierNameOrGetOrSet(is_get, is_set, CHECK_OK); |
| break; |
| } |
| |
| uint32_t index; |
| return this->IsArrayIndex(*name, &index) |
| ? factory()->NewNumberLiteral(index, pos) |
| : factory()->NewStringLiteral(*name, pos); |
| } |
| |
| |
| template <class Traits> |
| typename ParserBase<Traits>::ObjectLiteralPropertyT |
| ParserBase<Traits>::ParsePropertyDefinition( |
| ObjectLiteralCheckerBase* checker, bool in_class, bool has_extends, |
| bool is_static, bool* is_computed_name, bool* has_seen_constructor, |
| ExpressionClassifier* classifier, IdentifierT* name, bool* ok) { |
| DCHECK(!in_class || is_static || has_seen_constructor != nullptr); |
| ExpressionT value = this->EmptyExpression(); |
| bool is_get = false; |
| bool is_set = false; |
| bool name_is_static = false; |
| bool is_generator = Check(Token::MUL); |
| |
| Token::Value name_token = peek(); |
| int next_beg_pos = scanner()->peek_location().beg_pos; |
| int next_end_pos = scanner()->peek_location().end_pos; |
| bool is_identifier = false; |
| bool is_escaped_keyword = false; |
| ExpressionT name_expression = ParsePropertyName( |
| name, &is_get, &is_set, &name_is_static, is_computed_name, &is_identifier, |
| &is_escaped_keyword, classifier, |
| CHECK_OK_CUSTOM(EmptyObjectLiteralProperty)); |
| |
| if (fni_ != nullptr && !*is_computed_name) { |
| this->PushLiteralName(fni_, *name); |
| } |
| |
| bool escaped_static = |
| is_escaped_keyword && |
| scanner()->is_literal_contextual_keyword(CStrVector("static")); |
| |
| if (!in_class && !is_generator) { |
| DCHECK(!is_static); |
| |
| if (peek() == Token::COLON) { |
| // PropertyDefinition |
| // PropertyName ':' AssignmentExpression |
| if (!*is_computed_name) { |
| checker->CheckProperty(name_token, kValueProperty, false, false, |
| CHECK_OK_CUSTOM(EmptyObjectLiteralProperty)); |
| } |
| Consume(Token::COLON); |
| value = this->ParseAssignmentExpression( |
| true, kIsPatternElement, classifier, |
| CHECK_OK_CUSTOM(EmptyObjectLiteralProperty)); |
| |
| return factory()->NewObjectLiteralProperty(name_expression, value, false, |
| *is_computed_name); |
| } |
| |
| if ((is_identifier || is_escaped_keyword) && |
| (peek() == Token::COMMA || peek() == Token::RBRACE || |
| peek() == Token::ASSIGN)) { |
| // PropertyDefinition |
| // IdentifierReference |
| // CoverInitializedName |
| // |
| // CoverInitializedName |
| // IdentifierReference Initializer? |
| if (!Token::IsIdentifier(name_token, language_mode(), |
| this->is_generator())) { |
| if (!escaped_static) { |
| ReportUnexpectedTokenAt(scanner()->location(), name_token); |
| *ok = false; |
| return this->EmptyObjectLiteralProperty(); |
| } |
| } |
| if (classifier->duplicate_finder() != nullptr && |
| scanner()->FindSymbol(classifier->duplicate_finder(), 1) != 0) { |
| classifier->RecordDuplicateFormalParameterError(scanner()->location()); |
| } |
| if (name_token == Token::LET) { |
| classifier->RecordLetPatternError( |
| scanner()->location(), MessageTemplate::kLetInLexicalBinding); |
| } |
| |
| ExpressionT lhs = this->ExpressionFromIdentifier( |
| *name, next_beg_pos, next_end_pos, scope_, factory()); |
| CheckDestructuringElement(lhs, classifier, next_beg_pos, next_end_pos); |
| |
| if (peek() == Token::ASSIGN) { |
| Consume(Token::ASSIGN); |
| ExpressionClassifier rhs_classifier; |
| ExpressionT rhs = this->ParseAssignmentExpression( |
| true, &rhs_classifier, CHECK_OK_CUSTOM(EmptyObjectLiteralProperty)); |
| classifier->Accumulate(rhs_classifier, |
| ExpressionClassifier::ExpressionProductions); |
| value = factory()->NewAssignment(Token::ASSIGN, lhs, rhs, |
| RelocInfo::kNoPosition); |
| classifier->RecordCoverInitializedNameError( |
| Scanner::Location(next_beg_pos, scanner()->location().end_pos), |
| MessageTemplate::kInvalidCoverInitializedName); |
| } else { |
| value = lhs; |
| } |
| |
| return factory()->NewObjectLiteralProperty( |
| name_expression, value, ObjectLiteralProperty::COMPUTED, false, |
| false); |
| } |
| } |
| |
| if (in_class && escaped_static && !is_static) { |
| ReportUnexpectedTokenAt(scanner()->location(), name_token); |
| *ok = false; |
| return this->EmptyObjectLiteralProperty(); |
| } |
| |
| // Method definitions are never valid in patterns. |
| classifier->RecordPatternError( |
| Scanner::Location(next_beg_pos, scanner()->location().end_pos), |
| MessageTemplate::kInvalidDestructuringTarget); |
| |
| if (is_generator || peek() == Token::LPAREN) { |
| // MethodDefinition |
| // PropertyName '(' StrictFormalParameters ')' '{' FunctionBody '}' |
| // '*' PropertyName '(' StrictFormalParameters ')' '{' FunctionBody '}' |
| if (!*is_computed_name) { |
| checker->CheckProperty(name_token, kMethodProperty, is_static, |
| is_generator, |
| CHECK_OK_CUSTOM(EmptyObjectLiteralProperty)); |
| } |
| |
| FunctionKind kind = is_generator ? FunctionKind::kConciseGeneratorMethod |
| : FunctionKind::kConciseMethod; |
| |
| if (in_class && !is_static && this->IsConstructor(*name)) { |
| *has_seen_constructor = true; |
| kind = has_extends ? FunctionKind::kSubclassConstructor |
| : FunctionKind::kBaseConstructor; |
| } |
| |
| if (!in_class) kind = WithObjectLiteralBit(kind); |
| |
| value = this->ParseFunctionLiteral( |
| *name, scanner()->location(), kSkipFunctionNameCheck, kind, |
| RelocInfo::kNoPosition, FunctionLiteral::kAnonymousExpression, |
| FunctionLiteral::kNormalArity, language_mode(), |
| CHECK_OK_CUSTOM(EmptyObjectLiteralProperty)); |
| |
| return factory()->NewObjectLiteralProperty(name_expression, value, |
| ObjectLiteralProperty::COMPUTED, |
| is_static, *is_computed_name); |
| } |
| |
| if (in_class && name_is_static && !is_static) { |
| // ClassElement (static) |
| // 'static' MethodDefinition |
| *name = this->EmptyIdentifier(); |
| return ParsePropertyDefinition(checker, true, has_extends, true, |
| is_computed_name, nullptr, classifier, name, |
| ok); |
| } |
| |
| if (is_get || is_set) { |
| // MethodDefinition (Accessors) |
| // get PropertyName '(' ')' '{' FunctionBody '}' |
| // set PropertyName '(' PropertySetParameterList ')' '{' FunctionBody '}' |
| *name = this->EmptyIdentifier(); |
| bool dont_care = false; |
| name_token = peek(); |
| |
| name_expression = ParsePropertyName( |
| name, &dont_care, &dont_care, &dont_care, is_computed_name, &dont_care, |
| &dont_care, classifier, CHECK_OK_CUSTOM(EmptyObjectLiteralProperty)); |
| |
| if (!*is_computed_name) { |
| checker->CheckProperty(name_token, kAccessorProperty, is_static, |
| is_generator, |
| CHECK_OK_CUSTOM(EmptyObjectLiteralProperty)); |
| } |
| |
| FunctionKind kind = FunctionKind::kAccessorFunction; |
| if (!in_class) kind = WithObjectLiteralBit(kind); |
| typename Traits::Type::FunctionLiteral value = this->ParseFunctionLiteral( |
| *name, scanner()->location(), kSkipFunctionNameCheck, kind, |
| RelocInfo::kNoPosition, FunctionLiteral::kAnonymousExpression, |
| is_get ? FunctionLiteral::kGetterArity : FunctionLiteral::kSetterArity, |
| language_mode(), CHECK_OK_CUSTOM(EmptyObjectLiteralProperty)); |
| |
| // 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. |
| if (!*is_computed_name) { |
| name_expression = |
| factory()->NewStringLiteral(*name, name_expression->position()); |
| } |
| |
| return factory()->NewObjectLiteralProperty( |
| name_expression, value, |
| is_get ? ObjectLiteralProperty::GETTER : ObjectLiteralProperty::SETTER, |
| is_static, *is_computed_name); |
| } |
| |
| Token::Value next = Next(); |
| ReportUnexpectedToken(next); |
| *ok = false; |
| return this->EmptyObjectLiteralProperty(); |
| } |
| |
| |
| template <class Traits> |
| typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseObjectLiteral( |
| ExpressionClassifier* classifier, bool* ok) { |
| // ObjectLiteral :: |
| // '{' (PropertyDefinition (',' PropertyDefinition)* ','? )? '}' |
| |
| int pos = peek_position(); |
| typename Traits::Type::PropertyList properties = |
| this->NewPropertyList(4, zone_); |
| int number_of_boilerplate_properties = 0; |
| bool has_function = false; |
| bool has_computed_names = false; |
| ObjectLiteralChecker checker(this); |
| |
| Expect(Token::LBRACE, CHECK_OK); |
| |
| while (peek() != Token::RBRACE) { |
| FuncNameInferrer::State fni_state(fni_); |
| |
| const bool in_class = false; |
| const bool is_static = false; |
| const bool has_extends = false; |
| bool is_computed_name = false; |
| IdentifierT name = this->EmptyIdentifier(); |
| ObjectLiteralPropertyT property = this->ParsePropertyDefinition( |
| &checker, in_class, has_extends, is_static, &is_computed_name, NULL, |
| classifier, &name, CHECK_OK); |
| |
| if (is_computed_name) { |
| has_computed_names = true; |
| } |
| |
| // Mark top-level object literals that contain function literals and |
| // pretenure the literal so it can be added as a constant function |
| // property. (Parser only.) |
| this->CheckFunctionLiteralInsideTopLevelObjectLiteral(scope_, property, |
| &has_function); |
| |
| // Count CONSTANT or COMPUTED properties to maintain the enumeration order. |
| if (!has_computed_names && this->IsBoilerplateProperty(property)) { |
| 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(); |
| |
| if (allow_harmony_function_name()) { |
| Traits::SetFunctionNameFromPropertyName(property, name); |
| } |
| } |
| Expect(Token::RBRACE, CHECK_OK); |
| |
| // Computation of literal_index must happen before pre parse bailout. |
| int literal_index = function_state_->NextMaterializedLiteralIndex(); |
| |
| return factory()->NewObjectLiteral(properties, |
| literal_index, |
| number_of_boilerplate_properties, |
| has_function, |
| is_strong(language_mode()), |
| pos); |
| } |
| |
| |
| template <class Traits> |
| typename Traits::Type::ExpressionList ParserBase<Traits>::ParseArguments( |
| Scanner::Location* first_spread_arg_loc, ExpressionClassifier* classifier, |
| bool* ok) { |
| // Arguments :: |
| // '(' (AssignmentExpression)*[','] ')' |
| |
| Scanner::Location spread_arg = Scanner::Location::invalid(); |
| typename Traits::Type::ExpressionList result = |
| this->NewExpressionList(4, zone_); |
| Expect(Token::LPAREN, CHECK_OK_CUSTOM(NullExpressionList)); |
| bool done = (peek() == Token::RPAREN); |
| bool was_unspread = false; |
| int unspread_sequences_count = 0; |
| while (!done) { |
| int start_pos = peek_position(); |
| bool is_spread = Check(Token::ELLIPSIS); |
| |
| ExpressionT argument = this->ParseAssignmentExpression( |
| true, classifier, CHECK_OK_CUSTOM(NullExpressionList)); |
| if (is_spread) { |
| if (!spread_arg.IsValid()) { |
| spread_arg.beg_pos = start_pos; |
| spread_arg.end_pos = peek_position(); |
| } |
| argument = factory()->NewSpread(argument, start_pos); |
| } |
| result->Add(argument, zone_); |
| |
| // unspread_sequences_count is the number of sequences of parameters which |
| // are not prefixed with a spread '...' operator. |
| if (is_spread) { |
| was_unspread = false; |
| } else if (!was_unspread) { |
| was_unspread = true; |
| unspread_sequences_count++; |
| } |
| |
| if (result->length() > Code::kMaxArguments) { |
| ReportMessage(MessageTemplate::kTooManyArguments); |
| *ok = false; |
| return this->NullExpressionList(); |
| } |
| done = (peek() != Token::COMMA); |
| if (!done) { |
| Next(); |
| } |
| } |
| Scanner::Location location = scanner_->location(); |
| if (Token::RPAREN != Next()) { |
| ReportMessageAt(location, MessageTemplate::kUnterminatedArgList); |
| *ok = false; |
| return this->NullExpressionList(); |
| } |
| *first_spread_arg_loc = spread_arg; |
| |
| if (spread_arg.IsValid()) { |
| // Unspread parameter sequences are translated into array literals in the |
| // parser. Ensure that the number of materialized literals matches between |
| // the parser and preparser |
| Traits::MaterializeUnspreadArgumentsLiterals(unspread_sequences_count); |
| } |
| |
| return result; |
| } |
| |
| // Precedence = 2 |
| template <class Traits> |
| typename ParserBase<Traits>::ExpressionT |
| ParserBase<Traits>::ParseAssignmentExpression(bool accept_IN, int flags, |
| ExpressionClassifier* classifier, |
| bool* ok) { |
| // AssignmentExpression :: |
| // ConditionalExpression |
| // ArrowFunction |
| // YieldExpression |
| // LeftHandSideExpression AssignmentOperator AssignmentExpression |
| bool is_rhs = flags & kIsRightHandSide; |
| bool is_pattern_element = flags & kIsPatternElement; |
| bool is_destructuring_assignment = false; |
| bool is_arrow_formals = flags & kIsPossibleArrowFormals; |
| int lhs_beg_pos = peek_position(); |
| |
| if (peek() == Token::YIELD && is_generator()) { |
| return this->ParseYieldExpression(classifier, ok); |
| } |
| |
| FuncNameInferrer::State fni_state(fni_); |
| ParserBase<Traits>::Checkpoint checkpoint(this); |
| ExpressionClassifier arrow_formals_classifier(classifier->duplicate_finder()); |
| bool parenthesized_formals = peek() == Token::LPAREN; |
| if (!parenthesized_formals) { |
| ArrowFormalParametersUnexpectedToken(&arrow_formals_classifier); |
| } |
| ExpressionT expression = this->ParseConditionalExpression( |
| accept_IN, &arrow_formals_classifier, CHECK_OK); |
| if (peek() == Token::ARROW) { |
| BindingPatternUnexpectedToken(classifier); |
| ValidateArrowFormalParameters(&arrow_formals_classifier, expression, |
| parenthesized_formals, CHECK_OK); |
| Scanner::Location loc(lhs_beg_pos, scanner()->location().end_pos); |
| Scope* scope = |
| this->NewScope(scope_, FUNCTION_SCOPE, FunctionKind::kArrowFunction); |
| // Because the arrow's parameters were parsed in the outer scope, any |
| // usage flags that might have been triggered there need to be copied |
| // to the arrow scope. |
| scope_->PropagateUsageFlagsToScope(scope); |
| FormalParametersT parameters(scope); |
| if (!arrow_formals_classifier.is_simple_parameter_list()) { |
| scope->SetHasNonSimpleParameters(); |
| parameters.is_simple = false; |
| } |
| |
| checkpoint.Restore(¶meters.materialized_literals_count); |
| |
| scope->set_start_position(lhs_beg_pos); |
| Scanner::Location duplicate_loc = Scanner::Location::invalid(); |
| this->ParseArrowFunctionFormalParameterList(¶meters, expression, loc, |
| &duplicate_loc, CHECK_OK); |
| if (duplicate_loc.IsValid()) { |
| arrow_formals_classifier.RecordDuplicateFormalParameterError( |
| duplicate_loc); |
| } |
| expression = this->ParseArrowFunctionLiteral( |
| accept_IN, parameters, arrow_formals_classifier, CHECK_OK); |
| if (is_pattern_element) { |
| classifier->RecordPatternError( |
| Scanner::Location(lhs_beg_pos, scanner()->location().end_pos), |
| MessageTemplate::kInvalidDestructuringTarget); |
| } |
| |
| if (fni_ != nullptr) fni_->Infer(); |
| |
| return expression; |
| } |
| |
| if (this->IsValidReferenceExpression(expression)) { |
| arrow_formals_classifier.ForgiveAssignmentPatternError(); |
| } |
| |
| // "expression" was not itself an arrow function parameter list, but it might |
| // form part of one. Propagate speculative formal parameter error locations. |
| classifier->Accumulate( |
| arrow_formals_classifier, |
| ExpressionClassifier::StandardProductions | |
| ExpressionClassifier::FormalParametersProductions | |
| ExpressionClassifier::CoverInitializedNameProduction); |
| |
| bool maybe_pattern = |
| (expression->IsObjectLiteral() || expression->IsArrayLiteral()) && |
| !expression->is_parenthesized(); |
| |
| if (!Token::IsAssignmentOp(peek())) { |
| // Parsed conditional expression only (no assignment). |
| if (is_pattern_element) { |
| CheckDestructuringElement(expression, classifier, lhs_beg_pos, |
| scanner()->location().end_pos); |
| } else if (is_rhs && maybe_pattern) { |
| ValidateExpression(classifier, CHECK_OK); |
| } |
| |
| return expression; |
| } |
| |
| if (!(allow_harmony_destructuring_bind() || |
| allow_harmony_default_parameters())) { |
| BindingPatternUnexpectedToken(classifier); |
| } |
| |
| if (allow_harmony_destructuring_assignment() && maybe_pattern && |
| peek() == Token::ASSIGN) { |
| classifier->ForgiveCoverInitializedNameError(); |
| ValidateAssignmentPattern(classifier, CHECK_OK); |
| is_destructuring_assignment = true; |
| } else if (is_arrow_formals) { |
| expression = this->ClassifyAndRewriteReferenceExpression( |
| classifier, expression, lhs_beg_pos, scanner()->location().end_pos, |
| MessageTemplate::kInvalidLhsInAssignment); |
| } else { |
| if (is_pattern_element) { |
| CheckDestructuringElement(expression, classifier, lhs_beg_pos, |
| scanner()->location().end_pos); |
| } |
| expression = this->CheckAndRewriteReferenceExpression( |
| expression, lhs_beg_pos, scanner()->location().end_pos, |
| MessageTemplate::kInvalidLhsInAssignment, CHECK_OK); |
| } |
| |
| expression = this->MarkExpressionAsAssigned(expression); |
| |
| Token::Value op = Next(); // Get assignment operator. |
| if (op != Token::ASSIGN) { |
| classifier->RecordBindingPatternError(scanner()->location(), |
| MessageTemplate::kUnexpectedToken, |
| Token::String(op)); |
| } |
| int pos = position(); |
| |
| ExpressionClassifier rhs_classifier; |
| |
| int rhs_flags = flags; |
| rhs_flags &= ~(kIsPatternElement | kIsPossibleArrowFormals); |
| rhs_flags |= kIsRightHandSide; |
| ExpressionT right = this->ParseAssignmentExpression( |
| accept_IN, rhs_flags, &rhs_classifier, CHECK_OK); |
| classifier->Accumulate( |
| rhs_classifier, ExpressionClassifier::ExpressionProductions | |
| ExpressionClassifier::CoverInitializedNameProduction); |
| |
| // TODO(1231235): We try to estimate the set of properties set by |
| // constructors. We define a new property whenever there is an |
| // assignment to a property of 'this'. We should probably only add |
| // properties if we haven't seen them before. Otherwise we'll |
| // probably overestimate the number of properties. |
| if (op == Token::ASSIGN && this->IsThisProperty(expression)) { |
| function_state_->AddProperty(); |
| } |
| |
| this->CheckAssigningFunctionLiteralToProperty(expression, right); |
| |
| if (fni_ != NULL) { |
| // Check if the right hand side is a call to avoid inferring a |
| // name if we're dealing with "a = function(){...}();"-like |
| // expression. |
| if ((op == Token::INIT || op == Token::ASSIGN) && |
| (!right->IsCall() && !right->IsCallNew())) { |
| fni_->Infer(); |
| } else { |
| fni_->RemoveLastFunction(); |
| } |
| } |
| |
| ExpressionT result = factory()->NewAssignment(op, expression, right, pos); |
| |
| if (is_destructuring_assignment) { |
| result = factory()->NewRewritableAssignmentExpression(result); |
| Traits::QueueDestructuringAssignmentForRewriting(result); |
| } |
| |
| return result; |
| } |
| |
| template <class Traits> |
| typename ParserBase<Traits>::ExpressionT |
| ParserBase<Traits>::ParseYieldExpression(ExpressionClassifier* classifier, |
| bool* ok) { |
| // YieldExpression :: |
| // 'yield' ([no line terminator] '*'? AssignmentExpression)? |
| int pos = peek_position(); |
| BindingPatternUnexpectedToken(classifier); |
| FormalParameterInitializerUnexpectedToken(classifier); |
| Expect(Token::YIELD, CHECK_OK); |
| ExpressionT generator_object = |
| factory()->NewVariableProxy(function_state_->generator_object_variable()); |
| ExpressionT expression = Traits::EmptyExpression(); |
| Yield::Kind kind = Yield::kSuspend; |
| if (!scanner()->HasAnyLineTerminatorBeforeNext()) { |
| if (Check(Token::MUL)) kind = Yield::kDelegating; |
| switch (peek()) { |
| case Token::EOS: |
| case Token::SEMICOLON: |
| case Token::RBRACE: |
| case Token::RBRACK: |
| case Token::RPAREN: |
| case Token::COLON: |
| case Token::COMMA: |
| // The above set of tokens is the complete set of tokens that can appear |
| // after an AssignmentExpression, and none of them can start an |
| // AssignmentExpression. This allows us to avoid looking for an RHS for |
| // a Yield::kSuspend operation, given only one look-ahead token. |
| if (kind == Yield::kSuspend) |
| break; |
| DCHECK_EQ(Yield::kDelegating, kind); |
| // Delegating yields require an RHS; fall through. |
| default: |
| expression = ParseAssignmentExpression(false, classifier, CHECK_OK); |
| break; |
| } |
| } |
| if (kind == Yield::kDelegating) { |
| // var iterator = subject[Symbol.iterator](); |
| // Hackily disambiguate o from o.next and o [Symbol.iterator](). |
| // TODO(verwaest): Come up with a better solution. |
| expression = this->GetIterator(expression, factory(), pos + 1); |
| } |
| // Hackily disambiguate o from o.next and o [Symbol.iterator](). |
| // TODO(verwaest): Come up with a better solution. |
| typename Traits::Type::YieldExpression yield = |
| factory()->NewYield(generator_object, expression, kind, pos); |
| return yield; |
| } |
| |
| |
| // Precedence = 3 |
| template <class Traits> |
| typename ParserBase<Traits>::ExpressionT |
| ParserBase<Traits>::ParseConditionalExpression(bool accept_IN, |
| ExpressionClassifier* classifier, |
| bool* ok) { |
| // ConditionalExpression :: |
| // LogicalOrExpression |
| // LogicalOrExpression '?' AssignmentExpression ':' AssignmentExpression |
| |
| int pos = peek_position(); |
| // We start using the binary expression parser for prec >= 4 only! |
| ExpressionT expression = |
| this->ParseBinaryExpression(4, accept_IN, classifier, CHECK_OK); |
| if (peek() != Token::CONDITIONAL) return expression; |
| ArrowFormalParametersUnexpectedToken(classifier); |
| BindingPatternUnexpectedToken(classifier); |
| Consume(Token::CONDITIONAL); |
| // In parsing the first assignment expression in conditional |
| // expressions we always accept the 'in' keyword; see ECMA-262, |
| // section 11.12, page 58. |
| ExpressionT left = ParseAssignmentExpression(true, classifier, CHECK_OK); |
| Expect(Token::COLON, CHECK_OK); |
| ExpressionT right = |
| ParseAssignmentExpression(accept_IN, classifier, CHECK_OK); |
| return factory()->NewConditional(expression, left, right, pos); |
| } |
| |
| |
| // Precedence >= 4 |
| template <class Traits> |
| typename ParserBase<Traits>::ExpressionT |
| ParserBase<Traits>::ParseBinaryExpression(int prec, bool accept_IN, |
| ExpressionClassifier* classifier, |
| bool* ok) { |
| DCHECK(prec >= 4); |
| ExpressionT x = this->ParseUnaryExpression(classifier, CHECK_OK); |
| for (int prec1 = Precedence(peek(), accept_IN); prec1 >= prec; prec1--) { |
| // prec1 >= 4 |
| while (Precedence(peek(), accept_IN) == prec1) { |
| BindingPatternUnexpectedToken(classifier); |
| ArrowFormalParametersUnexpectedToken(classifier); |
| Token::Value op = Next(); |
| Scanner::Location op_location = scanner()->location(); |
| int pos = position(); |
| ExpressionT y = |
| ParseBinaryExpression(prec1 + 1, accept_IN, classifier, CHECK_OK); |
| |
| if (this->ShortcutNumericLiteralBinaryExpression(&x, y, op, pos, |
| factory())) { |
| continue; |
| } |
| |
| // For now we distinguish between comparisons and other binary |
| // operations. (We could combine the two and get rid of this |
| // code and AST node eventually.) |
| if (Token::IsCompareOp(op)) { |
| // We have a comparison. |
| Token::Value cmp = op; |
| switch (op) { |
| case Token::NE: cmp = Token::EQ; break; |
| case Token::NE_STRICT: cmp = Token::EQ_STRICT; break; |
| default: break; |
| } |
| if (cmp == Token::EQ && is_strong(language_mode())) { |
| ReportMessageAt(op_location, MessageTemplate::kStrongEqual); |
| *ok = false; |
| return this->EmptyExpression(); |
| } |
| x = factory()->NewCompareOperation(cmp, x, y, pos); |
| if (cmp != op) { |
| // The comparison was negated - add a NOT. |
| x = factory()->NewUnaryOperation(Token::NOT, x, pos); |
| } |
| |
| } else { |
| // We have a "normal" binary operation. |
| x = factory()->NewBinaryOperation(op, x, y, pos); |
| } |
| } |
| } |
| return x; |
| } |
| |
| |
| template <class Traits> |
| typename ParserBase<Traits>::ExpressionT |
| ParserBase<Traits>::ParseUnaryExpression(ExpressionClassifier* classifier, |
| bool* ok) { |
| // UnaryExpression :: |
| // PostfixExpression |
| // 'delete' UnaryExpression |
| // 'void' UnaryExpression |
| // 'typeof' UnaryExpression |
| // '++' UnaryExpression |
| // '--' UnaryExpression |
| // '+' UnaryExpression |
| // '-' UnaryExpression |
| // '~' UnaryExpression |
| // '!' UnaryExpression |
| |
| Token::Value op = peek(); |
| if (Token::IsUnaryOp(op)) { |
| BindingPatternUnexpectedToken(classifier); |
| ArrowFormalParametersUnexpectedToken(classifier); |
| |
| op = Next(); |
| int pos = position(); |
| ExpressionT expression = ParseUnaryExpression(classifier, CHECK_OK); |
| |
| if (op == Token::DELETE && is_strict(language_mode())) { |
| if (is_strong(language_mode())) { |
| ReportMessage(MessageTemplate::kStrongDelete); |
| *ok = false; |
| return this->EmptyExpression(); |
| } else if (this->IsIdentifier(expression)) { |
| // "delete identifier" is a syntax error in strict mode. |
| ReportMessage(MessageTemplate::kStrictDelete); |
| *ok = false; |
| return this->EmptyExpression(); |
| } |
| } |
| |
| // Allow Traits do rewrite the expression. |
| return this->BuildUnaryExpression(expression, op, pos, factory()); |
| } else if (Token::IsCountOp(op)) { |
| BindingPatternUnexpectedToken(classifier); |
| ArrowFormalParametersUnexpectedToken(classifier); |
| op = Next(); |
| int beg_pos = peek_position(); |
| ExpressionT expression = this->ParseUnaryExpression(classifier, CHECK_OK); |
| expression = this->CheckAndRewriteReferenceExpression( |
| expression, beg_pos, scanner()->location().end_pos, |
| MessageTemplate::kInvalidLhsInPrefixOp, CHECK_OK); |
| this->MarkExpressionAsAssigned(expression); |
| |
| return factory()->NewCountOperation(op, |
| true /* prefix */, |
| expression, |
| position()); |
| |
| } else { |
| return this->ParsePostfixExpression(classifier, ok); |
| } |
| } |
| |
| |
| template <class Traits> |
| typename ParserBase<Traits>::ExpressionT |
| ParserBase<Traits>::ParsePostfixExpression(ExpressionClassifier* classifier, |
| bool* ok) { |
| // PostfixExpression :: |
| // LeftHandSideExpression ('++' | '--')? |
| |
| int lhs_beg_pos = peek_position(); |
| ExpressionT expression = |
| this->ParseLeftHandSideExpression(classifier, CHECK_OK); |
| if (!scanner()->HasAnyLineTerminatorBeforeNext() && |
| Token::IsCountOp(peek())) { |
| BindingPatternUnexpectedToken(classifier); |
| ArrowFormalParametersUnexpectedToken(classifier); |
| |
| expression = this->CheckAndRewriteReferenceExpression( |
| expression, lhs_beg_pos, scanner()->location().end_pos, |
| MessageTemplate::kInvalidLhsInPostfixOp, CHECK_OK); |
| expression = this->MarkExpressionAsAssigned(expression); |
| |
| Token::Value next = Next(); |
| expression = |
| factory()->NewCountOperation(next, |
| false /* postfix */, |
| expression, |
| position()); |
| } |
| return expression; |
| } |
| |
| |
| template <class Traits> |
| typename ParserBase<Traits>::ExpressionT |
| ParserBase<Traits>::ParseLeftHandSideExpression( |
| ExpressionClassifier* classifier, bool* ok) { |
| // LeftHandSideExpression :: |
| // (NewExpression | MemberExpression) ... |
| |
| ExpressionT result = |
| this->ParseMemberWithNewPrefixesExpression(classifier, CHECK_OK); |
| |
| while (true) { |
| switch (peek()) { |
| case Token::LBRACK: { |
| BindingPatternUnexpectedToken(classifier); |
| ArrowFormalParametersUnexpectedToken(classifier); |
| Consume(Token::LBRACK); |
| int pos = position(); |
| ExpressionT index = ParseExpression(true, classifier, CHECK_OK); |
| result = factory()->NewProperty(result, index, pos); |
| Expect(Token::RBRACK, CHECK_OK); |
| break; |
| } |
| |
| case Token::LPAREN: { |
| BindingPatternUnexpectedToken(classifier); |
| ArrowFormalParametersUnexpectedToken(classifier); |
| |
| if (is_strong(language_mode()) && this->IsIdentifier(result) && |
| this->IsEval(this->AsIdentifier(result))) { |
| ReportMessage(MessageTemplate::kStrongDirectEval); |
| *ok = false; |
| return this->EmptyExpression(); |
| } |
| int pos; |
| if (scanner()->current_token() == Token::IDENTIFIER || |
| scanner()->current_token() == Token::SUPER) { |
| // For call of an identifier we want to report position of |
| // the identifier as position of the call in the stack trace. |
| pos = position(); |
| } else { |
| // For other kinds of calls we record position of the parenthesis as |
| // position of the call. Note that this is extremely important for |
| // expressions of the form function(){...}() for which call position |
| // should not point to the closing brace otherwise it will intersect |
| // with positions recorded for function literal and confuse debugger. |
| pos = peek_position(); |
| // Also the trailing parenthesis are a hint that the function will |
| // be called immediately. If we happen to have parsed a preceding |
| // function literal eagerly, we can also compile it eagerly. |
| if (result->IsFunctionLiteral() && mode() == PARSE_EAGERLY) { |
| result->AsFunctionLiteral()->set_should_eager_compile(); |
| } |
| } |
| Scanner::Location spread_pos; |
| typename Traits::Type::ExpressionList args = |
| ParseArguments(&spread_pos, classifier, CHECK_OK); |
| |
| // Keep track of eval() calls since they disable all local variable |
| // optimizations. |
| // The calls that need special treatment are the |
| // direct eval calls. These calls are all of the form eval(...), with |
| // no explicit receiver. |
| // These calls are marked as potentially direct eval calls. Whether |
| // they are actually direct calls to eval is determined at run time. |
| this->CheckPossibleEvalCall(result, scope_); |
| |
| bool is_super_call = result->IsSuperCallReference(); |
| if (spread_pos.IsValid()) { |
| args = Traits::PrepareSpreadArguments(args); |
| result = Traits::SpreadCall(result, args, pos); |
| } else { |
| result = factory()->NewCall(result, args, pos); |
| } |
| |
| // Explicit calls to the super constructor using super() perform an |
| // implicit binding assignment to the 'this' variable. |
| if (is_super_call) { |
| ExpressionT this_expr = this->ThisExpression(scope_, factory(), pos); |
| result = |
| factory()->NewAssignment(Token::INIT, this_expr, result, pos); |
| } |
| |
| if (fni_ != NULL) fni_->RemoveLastFunction(); |
| break; |
| } |
| |
| case Token::PERIOD: { |
| BindingPatternUnexpectedToken(classifier); |
| ArrowFormalParametersUnexpectedToken(classifier); |
| Consume(Token::PERIOD); |
| int pos = position(); |
| IdentifierT name = ParseIdentifierName(CHECK_OK); |
| result = factory()->NewProperty( |
| result, factory()->NewStringLiteral(name, pos), pos); |
| if (fni_ != NULL) this->PushLiteralName(fni_, name); |
| break; |
| } |
| |
| case Token::TEMPLATE_SPAN: |
| case Token::TEMPLATE_TAIL: { |
| BindingPatternUnexpectedToken(classifier); |
| ArrowFormalParametersUnexpectedToken(classifier); |
| result = ParseTemplateLiteral(result, position(), classifier, CHECK_OK); |
| break; |
| } |
| |
| default: |
| return result; |
| } |
| } |
| } |
| |
| |
| template <class Traits> |
| typename ParserBase<Traits>::ExpressionT |
| ParserBase<Traits>::ParseMemberWithNewPrefixesExpression( |
| ExpressionClassifier* classifier, bool* ok) { |
| // NewExpression :: |
| // ('new')+ MemberExpression |
| // |
| // NewTarget :: |
| // 'new' '.' 'target' |
| |
| // The grammar for new expressions is pretty warped. We can have several 'new' |
| // keywords following each other, and then a MemberExpression. When we see '(' |
| // after the MemberExpression, it's associated with the rightmost unassociated |
| // 'new' to create a NewExpression with arguments. However, a NewExpression |
| // can also occur without arguments. |
| |
| // Examples of new expression: |
| // new foo.bar().baz means (new (foo.bar)()).baz |
| // new foo()() means (new foo())() |
| // new new foo()() means (new (new foo())()) |
| // new new foo means new (new foo) |
| // new new foo() means new (new foo()) |
| // new new foo().bar().baz means (new (new foo()).bar()).baz |
| |
| if (peek() == Token::NEW) { |
| BindingPatternUnexpectedToken(classifier); |
| ArrowFormalParametersUnexpectedToken(classifier); |
| Consume(Token::NEW); |
| int new_pos = position(); |
| ExpressionT result = this->EmptyExpression(); |
| if (peek() == Token::SUPER) { |
| const bool is_new = true; |
| result = ParseSuperExpression(is_new, classifier, CHECK_OK); |
| } else if (peek() == Token::PERIOD) { |
| return ParseNewTargetExpression(CHECK_OK); |
| } else { |
| result = this->ParseMemberWithNewPrefixesExpression(classifier, CHECK_OK); |
| } |
| if (peek() == Token::LPAREN) { |
| // NewExpression with arguments. |
| Scanner::Location spread_pos; |
| typename Traits::Type::ExpressionList args = |
| this->ParseArguments(&spread_pos, classifier, CHECK_OK); |
| |
| if (spread_pos.IsValid()) { |
| args = Traits::PrepareSpreadArguments(args); |
| result = Traits::SpreadCallNew(result, args, new_pos); |
| } else { |
| result = factory()->NewCallNew(result, args, new_pos); |
| } |
| // The expression can still continue with . or [ after the arguments. |
| result = |
| this->ParseMemberExpressionContinuation(result, classifier, CHECK_OK); |
| return result; |
| } |
| // NewExpression without arguments. |
| return factory()->NewCallNew(result, this->NewExpressionList(0, zone_), |
| new_pos); |
| } |
| // No 'new' or 'super' keyword. |
| return this->ParseMemberExpression(classifier, ok); |
| } |
| |
| |
| template <class Traits> |
| typename ParserBase<Traits>::ExpressionT |
| ParserBase<Traits>::ParseMemberExpression(ExpressionClassifier* classifier, |
| bool* ok) { |
| // MemberExpression :: |
| // (PrimaryExpression | FunctionLiteral | ClassLiteral) |
| // ('[' Expression ']' | '.' Identifier | Arguments | TemplateLiteral)* |
| |
| // The '[' Expression ']' and '.' Identifier parts are parsed by |
| // ParseMemberExpressionContinuation, and the Arguments part is parsed by the |
| // caller. |
| |
| // Parse the initial primary or function expression. |
| ExpressionT result = this->EmptyExpression(); |
| if (peek() == Token::FUNCTION) { |
| BindingPatternUnexpectedToken(classifier); |
| ArrowFormalParametersUnexpectedToken(classifier); |
| |
| Consume(Token::FUNCTION); |
| int function_token_position = position(); |
| bool is_generator = Check(Token::MUL); |
| IdentifierT name = this->EmptyIdentifier(); |
| bool is_strict_reserved_name = false; |
| Scanner::Location function_name_location = Scanner::Location::invalid(); |
| FunctionLiteral::FunctionType function_type = |
| FunctionLiteral::kAnonymousExpression; |
| if (peek_any_identifier()) { |
| name = ParseIdentifierOrStrictReservedWord( |
| is_generator, &is_strict_reserved_name, CHECK_OK); |
| function_name_location = scanner()->location(); |
| function_type = FunctionLiteral::kNamedExpression; |
| } |
| result = this->ParseFunctionLiteral( |
| name, function_name_location, |
| is_strict_reserved_name ? kFunctionNameIsStrictReserved |
| : kFunctionNameValidityUnknown, |
| is_generator ? FunctionKind::kGeneratorFunction |
| : FunctionKind::kNormalFunction, |
| function_token_position, function_type, FunctionLiteral::kNormalArity, |
| language_mode(), CHECK_OK); |
| } else if (peek() == Token::SUPER) { |
| const bool is_new = false; |
| result = ParseSuperExpression(is_new, classifier, CHECK_OK); |
| } else { |
| result = ParsePrimaryExpression(classifier, CHECK_OK); |
| } |
| |
| result = ParseMemberExpressionContinuation(result, classifier, CHECK_OK); |
| return result; |
| } |
| |
| |
| template <class Traits> |
| typename ParserBase<Traits>::ExpressionT |
| ParserBase<Traits>::ParseStrongInitializationExpression( |
| ExpressionClassifier* classifier, bool* ok) { |
| // InitializationExpression :: (strong mode) |
| // 'this' '.' IdentifierName '=' AssignmentExpression |
| // 'this' '[' Expression ']' '=' AssignmentExpression |
| |
| FuncNameInferrer::State fni_state(fni_); |
| |
| Consume(Token::THIS); |
| int pos = position(); |
| function_state_->set_this_location(scanner()->location()); |
| ExpressionT this_expr = this->ThisExpression(scope_, factory(), pos); |
| |
| ExpressionT left = this->EmptyExpression(); |
| switch (peek()) { |
| case Token::LBRACK: { |
| Consume(Token::LBRACK); |
| int pos = position(); |
| ExpressionT index = this->ParseExpression(true, classifier, CHECK_OK); |
| left = factory()->NewProperty(this_expr, index, pos); |
| if (fni_ != NULL) { |
| this->PushPropertyName(fni_, index); |
| } |
| Expect(Token::RBRACK, CHECK_OK); |
| break; |
| } |
| case Token::PERIOD: { |
| Consume(Token::PERIOD); |
| int pos = position(); |
| IdentifierT name = ParseIdentifierName(CHECK_OK); |
| left = factory()->NewProperty( |
| this_expr, factory()->NewStringLiteral(name, pos), pos); |
| if (fni_ != NULL) { |
| this->PushLiteralName(fni_, name); |
| } |
| break; |
| } |
| default: |
| ReportMessage(MessageTemplate::kStrongConstructorThis); |
| *ok = false; |
| return this->EmptyExpression(); |
| } |
| |
| if (peek() != Token::ASSIGN) { |
| ReportMessageAt(function_state_->this_location(), |
| MessageTemplate::kStrongConstructorThis); |
| *ok = false; |
| return this->EmptyExpression(); |
| } |
| Consume(Token::ASSIGN); |
| left = this->MarkExpressionAsAssigned(left); |
| |
| ExpressionT right = |
| this->ParseAssignmentExpression(true, classifier, CHECK_OK); |
| this->CheckAssigningFunctionLiteralToProperty(left, right); |
| function_state_->AddProperty(); |
| if (fni_ != NULL) { |
| // Check if the right hand side is a call to avoid inferring a |
| // name if we're dealing with "this.a = function(){...}();"-like |
| // expression. |
| if (!right->IsCall() && !right->IsCallNew()) { |
| fni_->Infer(); |
| } else { |
| fni_->RemoveLastFunction(); |
| } |
| } |
| |
| if (function_state_->return_location().IsValid()) { |
| ReportMessageAt(function_state_->return_location(), |
| MessageTemplate::kStrongConstructorReturnMisplaced); |
| *ok = false; |
| return this->EmptyExpression(); |
| } |
| |
| return factory()->NewAssignment(Token::ASSIGN, left, right, pos); |
| } |
| |
| |
| template <class Traits> |
| typename ParserBase<Traits>::ExpressionT |
| ParserBase<Traits>::ParseStrongSuperCallExpression( |
| ExpressionClassifier* classifier, bool* ok) { |
| // SuperCallExpression :: (strong mode) |
| // 'super' '(' ExpressionList ')' |
| BindingPatternUnexpectedToken(classifier); |
| |
| Consume(Token::SUPER); |
| int pos = position(); |
| Scanner::Location super_loc = scanner()->location(); |
| ExpressionT expr = this->SuperCallReference(scope_, factory(), pos); |
| |
| if (peek() != Token::LPAREN) { |
| ReportMessage(MessageTemplate::kStrongConstructorSuper); |
| *ok = false; |
| return this->EmptyExpression(); |
| } |
| |
| Scanner::Location spread_pos; |
| typename Traits::Type::ExpressionList args = |
| ParseArguments(&spread_pos, classifier, CHECK_OK); |
| |
| // TODO(rossberg): This doesn't work with arrow functions yet. |
| if (!IsSubclassConstructor(function_state_->kind())) { |
| ReportMessage(MessageTemplate::kUnexpectedSuper); |
| *ok = false; |
| return this->EmptyExpression(); |
| } else if (function_state_->super_location().IsValid()) { |
| ReportMessageAt(scanner()->location(), |
| MessageTemplate::kStrongSuperCallDuplicate); |
| *ok = false; |
| return this->EmptyExpression(); |
| } else if (function_state_->this_location().IsValid()) { |
| ReportMessageAt(scanner()->location(), |
| MessageTemplate::kStrongSuperCallMisplaced); |
| *ok = false; |
| return this->EmptyExpression(); |
| } else if (function_state_->return_location().IsValid()) { |
| ReportMessageAt(function_state_->return_location(), |
| MessageTemplate::kStrongConstructorReturnMisplaced); |
| *ok = false; |
| return this->EmptyExpression(); |
| } |
| |
| function_state_->set_super_location(super_loc); |
| if (spread_pos.IsValid()) { |
| args = Traits::PrepareSpreadArguments(args); |
| expr = Traits::SpreadCall(expr, args, pos); |
| } else { |
| expr = factory()->NewCall(expr, args, pos); |
| } |
| |
| // Explicit calls to the super constructor using super() perform an implicit |
| // binding assignment to the 'this' variable. |
| ExpressionT this_expr = this->ThisExpression(scope_, factory(), pos); |
| return factory()->NewAssignment(Token::INIT, this_expr, expr, pos); |
| } |
| |
| |
| template <class Traits> |
| typename ParserBase<Traits>::ExpressionT |
| ParserBase<Traits>::ParseSuperExpression(bool is_new, |
| ExpressionClassifier* classifier, |
| bool* ok) { |
| Expect(Token::SUPER, CHECK_OK); |
| int pos = position(); |
| |
| Scope* scope = scope_->ReceiverScope(); |
| FunctionKind kind = scope->function_kind(); |
| if (IsConciseMethod(kind) || IsAccessorFunction(kind) || |
| IsClassConstructor(kind)) { |
| if (peek() == Token::PERIOD || peek() == Token::LBRACK) { |
| scope->RecordSuperPropertyUsage(); |
| return this->SuperPropertyReference(scope_, factory(), pos); |
| } |
| // new super() is never allowed. |
| // super() is only allowed in derived constructor |
| if (!is_new && peek() == Token::LPAREN && IsSubclassConstructor(kind)) { |
| if (is_strong(language_mode())) { |
| // Super calls in strong mode are parsed separately. |
| ReportMessageAt(scanner()->location(), |
| MessageTemplate::kStrongConstructorSuper); |
| *ok = false; |
| return this->EmptyExpression(); |
| } |
| // TODO(rossberg): This might not be the correct FunctionState for the |
| // method here. |
| function_state_->set_super_location(scanner()->location()); |
| return this->SuperCallReference(scope_, factory(), pos); |
| } |
| } |
| |
| ReportMessageAt(scanner()->location(), MessageTemplate::kUnexpectedSuper); |
| *ok = false; |
| return this->EmptyExpression(); |
| } |
| |
| |
| template <class Traits> |
| typename ParserBase<Traits>::ExpressionT |
| ParserBase<Traits>::ParseNewTargetExpression(bool* ok) { |
| int pos = position(); |
| Consume(Token::PERIOD); |
| ExpectContextualKeyword(CStrVector("target"), CHECK_OK); |
| |
| if (!scope_->ReceiverScope()->is_function_scope()) { |
| ReportMessageAt(scanner()->location(), |
| MessageTemplate::kUnexpectedNewTarget); |
| *ok = false; |
| return this->EmptyExpression(); |
| } |
| |
| return this->NewTargetExpression(scope_, factory(), pos); |
| } |
| |
| |
| template <class Traits> |
| typename ParserBase<Traits>::ExpressionT |
| ParserBase<Traits>::ParseMemberExpressionContinuation( |
| ExpressionT expression, ExpressionClassifier* classifier, bool* ok) { |
| // Parses this part of MemberExpression: |
| // ('[' Expression ']' | '.' Identifier | TemplateLiteral)* |
| while (true) { |
| switch (peek()) { |
| case Token::LBRACK: { |
| BindingPatternUnexpectedToken(classifier); |
| ArrowFormalParametersUnexpectedToken(classifier); |
| |
| Consume(Token::LBRACK); |
| int pos = position(); |
| ExpressionT index = this->ParseExpression(true, classifier, CHECK_OK); |
| expression = factory()->NewProperty(expression, index, pos); |
| if (fni_ != NULL) { |
| this->PushPropertyName(fni_, index); |
| } |
| Expect(Token::RBRACK, CHECK_OK); |
| break; |
| } |
| case Token::PERIOD: { |
| BindingPatternUnexpectedToken(classifier); |
| ArrowFormalParametersUnexpectedToken(classifier); |
| |
| Consume(Token::PERIOD); |
| int pos = position(); |
| IdentifierT name = ParseIdentifierName(CHECK_OK); |
| expression = factory()->NewProperty( |
| expression, factory()->NewStringLiteral(name, pos), pos); |
| if (fni_ != NULL) { |
| this->PushLiteralName(fni_, name); |
| } |
| break; |
| } |
| case Token::TEMPLATE_SPAN: |
| case Token::TEMPLATE_TAIL: { |
| BindingPatternUnexpectedToken(classifier); |
| ArrowFormalParametersUnexpectedToken(classifier); |
| int pos; |
| if (scanner()->current_token() == Token::IDENTIFIER) { |
| pos = position(); |
| } else { |
| pos = peek_position(); |
| if (expression->IsFunctionLiteral() && mode() == PARSE_EAGERLY) { |
| // If the tag function looks like an IIFE, set_parenthesized() to |
| // force eager compilation. |
| expression->AsFunctionLiteral()->set_should_eager_compile(); |
| } |
| } |
| expression = |
| ParseTemplateLiteral(expression, pos, classifier, CHECK_OK); |
| break; |
| } |
| default: |
| return expression; |
| } |
| } |
| DCHECK(false); |
| return this->EmptyExpression(); |
| } |
| |
| |
| template <class Traits> |
| void ParserBase<Traits>::ParseFormalParameter( |
| FormalParametersT* parameters, ExpressionClassifier* classifier, bool* ok) { |
| // FormalParameter[Yield,GeneratorParameter] : |
| // BindingElement[?Yield, ?GeneratorParameter] |
| bool is_rest = parameters->has_rest; |
| |
| Token::Value next = peek(); |
| ExpressionT pattern = ParsePrimaryExpression(classifier, ok); |
| if (!*ok) return; |
| |
| ValidateBindingPattern(classifier, ok); |
| if (!*ok) return; |
| |
| if (!Traits::IsIdentifier(pattern)) { |
| if (!allow_harmony_destructuring_bind()) { |
| ReportUnexpectedToken(next); |
| *ok = false; |
| return; |
| } |
| parameters->is_simple = false; |
| ValidateFormalParameterInitializer(classifier, ok); |
| if (!*ok) return; |
| classifier->RecordNonSimpleParameter(); |
| } |
| |
| ExpressionT initializer = Traits::EmptyExpression(); |
| if (!is_rest && allow_harmony_default_parameters() && Check(Token::ASSIGN)) { |
| ExpressionClassifier init_classifier; |
| initializer = ParseAssignmentExpression(true, &init_classifier, ok); |
| if (!*ok) return; |
| ValidateExpression(&init_classifier, ok); |
| ValidateFormalParameterInitializer(&init_classifier, ok); |
| if (!*ok) return; |
| parameters->is_simple = false; |
| classifier->RecordNonSimpleParameter(); |
| } |
| |
| Traits::AddFormalParameter(parameters, pattern, initializer, |
| scanner()->location().end_pos, is_rest); |
| } |
| |
| |
| template <class Traits> |
| void ParserBase<Traits>::ParseFormalParameterList( |
| FormalParametersT* parameters, ExpressionClassifier* classifier, bool* ok) { |
| // FormalParameters[Yield,GeneratorParameter] : |
| // [empty] |
| // FormalParameterList[?Yield, ?GeneratorParameter] |
| // |
| // FormalParameterList[Yield,GeneratorParameter] : |
| // FunctionRestParameter[?Yield] |
| // FormalsList[?Yield, ?GeneratorParameter] |
| // FormalsList[?Yield, ?GeneratorParameter] , FunctionRestParameter[?Yield] |
| // |
| // FormalsList[Yield,GeneratorParameter] : |
| // FormalParameter[?Yield, ?GeneratorParameter] |
| // FormalsList[?Yield, ?GeneratorParameter] , |
| // FormalParameter[?Yield,?GeneratorParameter] |
| |
| DCHECK_EQ(0, parameters->Arity()); |
| |
| if (peek() != Token::RPAREN) { |
| do { |
| if (parameters->Arity() > Code::kMaxArguments) { |
| ReportMessage(MessageTemplate::kTooManyParameters); |
| *ok = false; |
| return; |
| } |
| parameters->has_rest = Check(Token::ELLIPSIS); |
| ParseFormalParameter(parameters, classifier, ok); |
| if (!*ok) return; |
| } while (!parameters->has_rest && Check(Token::COMMA)); |
| |
| if (parameters->has_rest) { |
| parameters->is_simple = false; |
| classifier->RecordNonSimpleParameter(); |
| if (peek() == Token::COMMA) { |
| ReportMessageAt(scanner()->peek_location(), |
| MessageTemplate::kParamAfterRest); |
| *ok = false; |
| return; |
| } |
| } |
| } |
| |
| for (int i = 0; i < parameters->Arity(); ++i) { |
| auto parameter = parameters->at(i); |
| Traits::DeclareFormalParameter(parameters->scope, parameter, classifier); |
| } |
| } |
| |
| |
| template <class Traits> |
| void ParserBase<Traits>::CheckArityRestrictions( |
| int param_count, FunctionLiteral::ArityRestriction arity_restriction, |
| bool has_rest, int formals_start_pos, int formals_end_pos, bool* ok) { |
| switch (arity_restriction) { |
| case FunctionLiteral::kGetterArity: |
| if (param_count != 0) { |
| ReportMessageAt(Scanner::Location(formals_start_pos, formals_end_pos), |
| MessageTemplate::kBadGetterArity); |
| *ok = false; |
| } |
| break; |
| case FunctionLiteral::kSetterArity: |
| if (param_count != 1) { |
| ReportMessageAt(Scanner::Location(formals_start_pos, formals_end_pos), |
| MessageTemplate::kBadSetterArity); |
| *ok = false; |
| } |
| if (has_rest) { |
| ReportMessageAt(Scanner::Location(formals_start_pos, formals_end_pos), |
| MessageTemplate::kBadSetterRestParameter); |
| *ok = false; |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| |
| template <class Traits> |
| bool ParserBase<Traits>::IsNextLetKeyword() { |
| DCHECK(peek() == Token::LET); |
| if (!allow_let()) { |
| return false; |
| } |
| Token::Value next_next = PeekAhead(); |
| switch (next_next) { |
| case Token::LBRACE: |
| case Token::LBRACK: |
| case Token::IDENTIFIER: |
| case Token::STATIC: |
| case Token::LET: // Yes, you can do let let = ... in sloppy mode |
| case Token::YIELD: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| |
| template <class Traits> |
| typename ParserBase<Traits>::ExpressionT |
| ParserBase<Traits>::ParseArrowFunctionLiteral( |
| bool accept_IN, const FormalParametersT& formal_parameters, |
| const ExpressionClassifier& formals_classifier, bool* ok) { |
| if (peek() == Token::ARROW && scanner_->HasAnyLineTerminatorBeforeNext()) { |
| // ASI inserts `;` after arrow parameters if a line terminator is found. |
| // `=> ...` is never a valid expression, so report as syntax error. |
| // If next token is not `=>`, it's a syntax error anyways. |
| ReportUnexpectedTokenAt(scanner_->peek_location(), Token::ARROW); |
| *ok = false; |
| return this->EmptyExpression(); |
| } |
| |
| typename Traits::Type::StatementList body; |
| int num_parameters = formal_parameters.scope->num_parameters(); |
| int materialized_literal_count = -1; |
| int expected_property_count = -1; |
| Scanner::Location super_loc; |
| |
| { |
| typename Traits::Type::Factory function_factory(ast_value_factory()); |
| FunctionState function_state(&function_state_, &scope_, |
| formal_parameters.scope, kArrowFunction, |
| &function_factory); |
| |
| function_state.SkipMaterializedLiterals( |
| formal_parameters.materialized_literals_count); |
| |
| this->ReindexLiterals(formal_parameters); |
| |
| Expect(Token::ARROW, CHECK_OK); |
| |
| if (peek() == Token::LBRACE) { |
| // Multiple statement body |
| Consume(Token::LBRACE); |
| bool is_lazily_parsed = |
| (mode() == PARSE_LAZILY && scope_->AllowsLazyCompilation()); |
| if (is_lazily_parsed) { |
| body = this->NewStatementList(0, zone()); |
| this->SkipLazyFunctionBody(&materialized_literal_count, |
| &expected_property_count, CHECK_OK); |
| if (formal_parameters.materialized_literals_count > 0) { |
| materialized_literal_count += |
| formal_parameters.materialized_literals_count; |
| } |
| } else { |
| body = this->ParseEagerFunctionBody( |
| this->EmptyIdentifier(), RelocInfo::kNoPosition, formal_parameters, |
| kArrowFunction, FunctionLiteral::kAnonymousExpression, CHECK_OK); |
| materialized_literal_count = |
| function_state.materialized_literal_count(); |
| expected_property_count = function_state.expected_property_count(); |
| } |
| } else { |
| // Single-expression body |
| int pos = position(); |
| parenthesized_function_ = false; |
| ExpressionClassifier classifier; |
| ExpressionT expression = |
| ParseAssignmentExpression(accept_IN, &classifier, CHECK_OK); |
| ValidateExpression(&classifier, CHECK_OK); |
| body = this->NewStatementList(1, zone()); |
| this->AddParameterInitializationBlock(formal_parameters, body, CHECK_OK); |
| body->Add(factory()->NewReturnStatement(expression, pos), zone()); |
| materialized_literal_count = function_state.materialized_literal_count(); |
| expected_property_count = function_state.expected_property_count(); |
| } |
| super_loc = function_state.super_location(); |
| |
| formal_parameters.scope->set_end_position(scanner()->location().end_pos); |
| |
| // Arrow function formal parameters are parsed as StrictFormalParameterList, |
| // which is not the same as "parameters of a strict function"; it only means |
| // that duplicates are not allowed. Of course, the arrow function may |
| // itself be strict as well. |
| const bool allow_duplicate_parameters = false; |
| this->ValidateFormalParameters(&formals_classifier, language_mode(), |
| allow_duplicate_parameters, CHECK_OK); |
| |
| // Validate strict mode. |
| if (is_strict(language_mode())) { |
| CheckStrictOctalLiteral(formal_parameters.scope->start_position(), |
| scanner()->location().end_pos, CHECK_OK); |
| } |
| if (is_strict(language_mode()) || allow_harmony_sloppy()) { |
| this->CheckConflictingVarDeclarations(formal_parameters.scope, CHECK_OK); |
| } |
| |
| Traits::RewriteDestructuringAssignments(); |
| } |
| |
| FunctionLiteralT function_literal = factory()->NewFunctionLiteral( |
| this->EmptyIdentifierString(), formal_parameters.scope, body, |
| materialized_literal_count, expected_property_count, num_parameters, |
| FunctionLiteral::kNoDuplicateParameters, |
| FunctionLiteral::kAnonymousExpression, |
| FunctionLiteral::kShouldLazyCompile, FunctionKind::kArrowFunction, |
| formal_parameters.scope->start_position()); |
| |
| function_literal->set_function_token_position( |
| formal_parameters.scope->start_position()); |
| if (super_loc.IsValid()) function_state_->set_super_location(super_loc); |
| |
| if (fni_ != NULL) this->InferFunctionName(fni_, function_literal); |
| |
| return function_literal; |
| } |
| |
| |
| template <typename Traits> |
| typename ParserBase<Traits>::ExpressionT |
| ParserBase<Traits>::ParseTemplateLiteral(ExpressionT tag, int start, |
| ExpressionClassifier* classifier, |
| bool* ok) { |
| // A TemplateLiteral is made up of 0 or more TEMPLATE_SPAN tokens (literal |
| // text followed by a substitution expression), finalized by a single |
| // TEMPLATE_TAIL. |
| // |
| // In terms of draft language, TEMPLATE_SPAN may be either the TemplateHead or |
| // TemplateMiddle productions, while TEMPLATE_TAIL is either TemplateTail, or |
| // NoSubstitutionTemplate. |
| // |
| // When parsing a TemplateLiteral, we must have scanned either an initial |
| // TEMPLATE_SPAN, or a TEMPLATE_TAIL. |
| CHECK(peek() == Token::TEMPLATE_SPAN || peek() == Token::TEMPLATE_TAIL); |
| |
| // If we reach a TEMPLATE_TAIL first, we are parsing a NoSubstitutionTemplate. |
| // In this case we may simply consume the token and build a template with a |
| // single TEMPLATE_SPAN and no expressions. |
| if (peek() == Token::TEMPLATE_TAIL) { |
| Consume(Token::TEMPLATE_TAIL); |
| int pos = position(); |
| CheckTemplateOctalLiteral(pos, peek_position(), CHECK_OK); |
| typename Traits::TemplateLiteralState ts = Traits::OpenTemplateLiteral(pos); |
| Traits::AddTemplateSpan(&ts, true); |
| return Traits::CloseTemplateLiteral(&ts, start, tag); |
| } |
| |
| Consume(Token::TEMPLATE_SPAN); |
| int pos = position(); |
| typename Traits::TemplateLiteralState ts = Traits::OpenTemplateLiteral(pos); |
| Traits::AddTemplateSpan(&ts, false); |
| Token::Value next; |
| |
| // If we open with a TEMPLATE_SPAN, we must scan the subsequent expression, |
| // and repeat if the following token is a TEMPLATE_SPAN as well (in this |
| // case, representing a TemplateMiddle). |
| |
| do { |
| CheckTemplateOctalLiteral(pos, peek_position(), CHECK_OK); |
| next = peek(); |
| if (next == Token::EOS) { |
| ReportMessageAt(Scanner::Location(start, peek_position()), |
| MessageTemplate::kUnterminatedTemplate); |
| *ok = false; |
| return Traits::EmptyExpression(); |
| } else if (next == Token::ILLEGAL) { |
| Traits::ReportMessageAt( |
| Scanner::Location(position() + 1, peek_position()), |
| MessageTemplate::kUnexpectedToken, "ILLEGAL", kSyntaxError); |
| *ok = false; |
| return Traits::EmptyExpression(); |
| } |
| |
| int expr_pos = peek_position(); |
| ExpressionT expression = this->ParseExpression(true, classifier, CHECK_OK); |
| Traits::AddTemplateExpression(&ts, expression); |
| |
| if (peek() != Token::RBRACE) { |
| ReportMessageAt(Scanner::Location(expr_pos, peek_position()), |
| MessageTemplate::kUnterminatedTemplateExpr); |
| *ok = false; |
| return Traits::EmptyExpression(); |
| } |
| |
| // If we didn't die parsing that expression, our next token should be a |
| // TEMPLATE_SPAN or TEMPLATE_TAIL. |
| next = scanner()->ScanTemplateContinuation(); |
| Next(); |
| pos = position(); |
| |
| if (next == Token::EOS) { |
| ReportMessageAt(Scanner::Location(start, pos), |
| MessageTemplate::kUnterminatedTemplate); |
| *ok = false; |
| return Traits::EmptyExpression(); |
| } else if (next == Token::ILLEGAL) { |
| Traits::ReportMessageAt( |
| Scanner::Location(position() + 1, peek_position()), |
| MessageTemplate::kUnexpectedToken, "ILLEGAL", kSyntaxError); |
| *ok = false; |
| return Traits::EmptyExpression(); |
| } |
| |
| Traits::AddTemplateSpan(&ts, next == Token::TEMPLATE_TAIL); |
| } while (next == Token::TEMPLATE_SPAN); |
| |
| DCHECK_EQ(next, Token::TEMPLATE_TAIL); |
| CheckTemplateOctalLiteral(pos, peek_position(), CHECK_OK); |
| // Once we've reached a TEMPLATE_TAIL, we can close the TemplateLiteral. |
| return Traits::CloseTemplateLiteral(&ts, start, tag); |
| } |
| |
| |
| template <typename Traits> |
| typename ParserBase<Traits>::ExpressionT |
| ParserBase<Traits>::CheckAndRewriteReferenceExpression( |
| ExpressionT expression, int beg_pos, int end_pos, |
| MessageTemplate::Template message, bool* ok) { |
| return this->CheckAndRewriteReferenceExpression(expression, beg_pos, end_pos, |
| message, kReferenceError, ok); |
| } |
| |
| |
| template <typename Traits> |
| typename ParserBase<Traits>::ExpressionT |
| ParserBase<Traits>::CheckAndRewriteReferenceExpression( |
| ExpressionT expression, int beg_pos, int end_pos, |
| MessageTemplate::Template message, ParseErrorType type, bool* ok) { |
| ExpressionClassifier classifier; |
| ExpressionT result = ClassifyAndRewriteReferenceExpression( |
| &classifier, expression, beg_pos, end_pos, message, type); |
| ValidateExpression(&classifier, ok); |
| if (!*ok) return this->EmptyExpression(); |
| return result; |
| } |
| |
| |
| template <typename Traits> |
| typename ParserBase<Traits>::ExpressionT |
| ParserBase<Traits>::ClassifyAndRewriteReferenceExpression( |
| ExpressionClassifier* classifier, ExpressionT expression, int beg_pos, |
| int end_pos, MessageTemplate::Template message, ParseErrorType type) { |
| Scanner::Location location(beg_pos, end_pos); |
| if (this->IsIdentifier(expression)) { |
| if (is_strict(language_mode()) && |
| this->IsEvalOrArguments(this->AsIdentifier(expression))) { |
| classifier->RecordExpressionError( |
| location, MessageTemplate::kStrictEvalArguments, kSyntaxError); |
| return expression; |
| } |
| if (is_strong(language_mode()) && |
| this->IsUndefined(this->AsIdentifier(expression))) { |
| classifier->RecordExpressionError( |
| location, MessageTemplate::kStrongUndefined, kSyntaxError); |
| return expression; |
| } |
| } |
| if (expression->IsValidReferenceExpression()) { |
| return expression; |
| } else if (expression->IsCall()) { |
| // If it is a call, make it a runtime error for legacy web compatibility. |
| // Rewrite `expr' to `expr[throw ReferenceError]'. |
| int pos = location.beg_pos; |
| ExpressionT error = this->NewThrowReferenceError(message, pos); |
| return factory()->NewProperty(expression, error, pos); |
| } else { |
| classifier->RecordExpressionError(location, message, type); |
| return expression; |
| } |
| } |
| |
| |
| template <typename Traits> |
| bool ParserBase<Traits>::IsValidReferenceExpression(ExpressionT expression) { |
| return this->IsAssignableIdentifier(expression) || expression->IsProperty(); |
| } |
| |
| |
| template <typename Traits> |
| void ParserBase<Traits>::CheckDestructuringElement( |
| ExpressionT expression, ExpressionClassifier* classifier, int begin, |
| int end) { |
| static const MessageTemplate::Template message = |
| MessageTemplate::kInvalidDestructuringTarget; |
| const Scanner::Location location(begin, end); |
| if (expression->IsArrayLiteral() || expression->IsObjectLiteral() || |
| expression->IsAssignment()) { |
| if (expression->is_parenthesized()) { |
| classifier->RecordPatternError(location, message); |
| } |
| return; |
| } |
| |
| if (expression->IsProperty()) { |
| classifier->RecordBindingPatternError(location, message); |
| } else if (!this->IsAssignableIdentifier(expression)) { |
| classifier->RecordPatternError(location, message); |
| } |
| } |
| |
| |
| #undef CHECK_OK |
| #undef CHECK_OK_CUSTOM |
| |
| |
| template <typename Traits> |
| void ParserBase<Traits>::ObjectLiteralChecker::CheckProperty( |
| Token::Value property, PropertyKind type, bool is_static, bool is_generator, |
| bool* ok) { |
| DCHECK(!is_static); |
| DCHECK(!is_generator || type == kMethodProperty); |
| |
| if (property == Token::SMI || property == Token::NUMBER) return; |
| |
| if (type == kValueProperty && IsProto()) { |
| if (has_seen_proto_) { |
| this->parser()->ReportMessage(MessageTemplate::kDuplicateProto); |
| *ok = false; |
| return; |
| } |
| has_seen_proto_ = true; |
| return; |
| } |
| } |
| |
| |
| template <typename Traits> |
| void ParserBase<Traits>::ClassLiteralChecker::CheckProperty( |
| Token::Value property, PropertyKind type, bool is_static, bool is_generator, |
| bool* ok) { |
| DCHECK(type == kMethodProperty || type == kAccessorProperty); |
| |
| if (property == Token::SMI || property == Token::NUMBER) return; |
| |
| if (is_static) { |
| if (IsPrototype()) { |
| this->parser()->ReportMessage(MessageTemplate::kStaticPrototype); |
| *ok = false; |
| return; |
| } |
| } else if (IsConstructor()) { |
| if (is_generator || type == kAccessorProperty) { |
| MessageTemplate::Template msg = |
| is_generator ? MessageTemplate::kConstructorIsGenerator |
| : MessageTemplate::kConstructorIsAccessor; |
| this->parser()->ReportMessage(msg); |
| *ok = false; |
| return; |
| } |
| if (has_seen_constructor_) { |
| this->parser()->ReportMessage(MessageTemplate::kDuplicateConstructor); |
| *ok = false; |
| return; |
| } |
| has_seen_constructor_ = true; |
| return; |
| } |
| } |
| } // namespace internal |
| } // namespace v8 |
| |
| #endif // V8_PARSING_PARSER_BASE_H |