diff options
Diffstat (limited to 'deps/v8/src/parsing')
22 files changed, 2983 insertions, 2665 deletions
diff --git a/deps/v8/src/parsing/OWNERS b/deps/v8/src/parsing/OWNERS index 24218df199..177f214415 100644 --- a/deps/v8/src/parsing/OWNERS +++ b/deps/v8/src/parsing/OWNERS @@ -2,6 +2,7 @@ set noparent adamk@chromium.org gsathya@chromium.org +leszeks@chromium.org littledan@chromium.org marja@chromium.org neis@chromium.org diff --git a/deps/v8/src/parsing/duplicate-finder.h b/deps/v8/src/parsing/duplicate-finder.h index a4981c1872..65bcc4e00d 100644 --- a/deps/v8/src/parsing/duplicate-finder.h +++ b/deps/v8/src/parsing/duplicate-finder.h @@ -22,7 +22,7 @@ class Scanner; // Scanner::IsDuplicateSymbol. class DuplicateFinder { public: - DuplicateFinder() {} + DuplicateFinder() = default; private: friend class Scanner; diff --git a/deps/v8/src/parsing/expression-classifier.h b/deps/v8/src/parsing/expression-classifier.h index 7833dbc8d3..2eed75b939 100644 --- a/deps/v8/src/parsing/expression-classifier.h +++ b/deps/v8/src/parsing/expression-classifier.h @@ -5,9 +5,10 @@ #ifndef V8_PARSING_EXPRESSION_CLASSIFIER_H_ #define V8_PARSING_EXPRESSION_CLASSIFIER_H_ +#include <type_traits> + #include "src/messages.h" #include "src/parsing/scanner.h" -#include "src/zone/zone-containers.h" namespace v8 { namespace internal { @@ -47,14 +48,38 @@ class DuplicateFinder; // by calling the method Discard. Both actions result in removing the // classifier from the parser's stack. +// Expression classifier is split into four parts. The base implementing the +// general expression classifier logic. Two parts that implement the error +// tracking interface, where one is the actual implementation and the other is +// an empty class providing only the interface without logic. The expression +// classifier class then combines the other parts and provides the full +// expression classifier interface by inheriting conditionally, controlled by +// Types::ExpressionClassifierReportErrors, either from the ErrorTracker or the +// EmptyErrorTracker. +// +// Base +// / \ +// / \ +// / \ +// / \ +// ErrorTracker EmptyErrorTracker +// \ / +// \ / +// \ / +// \ / +// ExpressionClassifier + template <typename Types> -class ExpressionClassifier { +class ExpressionClassifier; + +template <typename Types, typename ErrorTracker> +class ExpressionClassifierBase { public: enum ErrorKind : unsigned { #define DEFINE_ERROR_KIND(NAME, CODE) k##NAME = CODE, ERROR_CODES(DEFINE_ERROR_KIND) #undef DEFINE_ERROR_KIND - kUnusedError = 15 // Larger than error codes; should fit in 4 bits + kUnusedError = 15 // Larger than error codes; should fit in 4 bits }; struct Error { @@ -86,23 +111,14 @@ class ExpressionClassifier { }; // clang-format on - explicit ExpressionClassifier(typename Types::Base* base, - DuplicateFinder* duplicate_finder = nullptr) + explicit ExpressionClassifierBase(typename Types::Base* base, + DuplicateFinder* duplicate_finder = nullptr) : base_(base), - previous_(base->classifier_), - zone_(base->impl()->zone()), - reported_errors_(base->impl()->GetReportedErrorList()), duplicate_finder_(duplicate_finder), invalid_productions_(0), - is_non_simple_parameter_list_(0) { - base->classifier_ = this; - reported_errors_begin_ = reported_errors_end_ = reported_errors_->size(); - } + is_non_simple_parameter_list_(0) {} - V8_INLINE ~ExpressionClassifier() { - Discard(); - if (base_->classifier_ == this) base_->classifier_ = previous_; - } + virtual ~ExpressionClassifierBase() = default; V8_INLINE bool is_valid(unsigned productions) const { return (invalid_productions_ & productions) == 0; @@ -150,80 +166,338 @@ class ExpressionClassifier { return is_valid(AsyncArrowFormalParametersProduction); } + V8_INLINE bool is_simple_parameter_list() const { + return !is_non_simple_parameter_list_; + } + + V8_INLINE void RecordNonSimpleParameter() { + is_non_simple_parameter_list_ = 1; + } + + V8_INLINE void Accumulate(ExpressionClassifier<Types>* const inner, + unsigned productions) { +#ifdef DEBUG + static_cast<ErrorTracker*>(this)->CheckErrorPositions(inner); +#endif + // Propagate errors from inner, but don't overwrite already recorded + // errors. + unsigned non_arrow_inner_invalid_productions = + inner->invalid_productions_ & ~ArrowFormalParametersProduction; + if (non_arrow_inner_invalid_productions) { + unsigned errors = non_arrow_inner_invalid_productions & productions & + ~this->invalid_productions_; + // The result will continue to be a valid arrow formal parameters if the + // inner expression is a valid binding pattern. + bool copy_BP_to_AFP = false; + if (productions & ArrowFormalParametersProduction && + this->is_valid_arrow_formal_parameters()) { + // Also whether we've seen any non-simple parameters + // if expecting an arrow function parameter. + this->is_non_simple_parameter_list_ |= + inner->is_non_simple_parameter_list_; + if (!inner->is_valid_binding_pattern()) { + copy_BP_to_AFP = true; + this->invalid_productions_ |= ArrowFormalParametersProduction; + } + } + if (errors != 0 || copy_BP_to_AFP) { + this->invalid_productions_ |= errors; + static_cast<ErrorTracker*>(this)->AccumulateErrorImpl( + inner, productions, errors, copy_BP_to_AFP); + } + } + static_cast<ErrorTracker*>(this)->RewindErrors(inner); + } + + protected: + typename Types::Base* base_; + DuplicateFinder* duplicate_finder_; + unsigned invalid_productions_ : kUnusedError; + STATIC_ASSERT(kUnusedError <= 15); + unsigned is_non_simple_parameter_list_ : 1; +}; + +template <typename Types> +class ExpressionClassifierErrorTracker + : public ExpressionClassifierBase<Types, + ExpressionClassifierErrorTracker<Types>> { + public: + using BaseClassType = + ExpressionClassifierBase<Types, ExpressionClassifierErrorTracker<Types>>; + using typename BaseClassType::Error; + using typename BaseClassType::ErrorKind; + using TP = typename BaseClassType::TargetProduction; + + ExpressionClassifierErrorTracker(typename Types::Base* base, + DuplicateFinder* duplicate_finder) + : BaseClassType(base, duplicate_finder), + reported_errors_(base->impl()->GetReportedErrorList()) { + reported_errors_begin_ = reported_errors_end_ = reported_errors_->length(); + } + + ~ExpressionClassifierErrorTracker() override { Discard(); } + + V8_INLINE void Discard() { + if (reported_errors_end_ == reported_errors_->length()) { + reported_errors_->Rewind(reported_errors_begin_); + reported_errors_end_ = reported_errors_begin_; + } + DCHECK_EQ(reported_errors_begin_, reported_errors_end_); + } + + protected: + V8_INLINE const Error& reported_error(ErrorKind kind) const { + if (this->invalid_productions_ & (1 << kind)) { + for (int i = reported_errors_begin_; i < reported_errors_end_; i++) { + if (reported_errors_->at(i).kind == kind) + return reported_errors_->at(i); + } + UNREACHABLE(); + } + // We should only be looking for an error when we know that one has + // been reported. But we're not... So this is to make sure we have + // the same behaviour. + UNREACHABLE(); + + // Make MSVC happy by returning an error from this inaccessible path. + static Error none; + return none; + } + + // Adds e to the end of the list of reported errors for this classifier. + // It is expected that this classifier is the last one in the stack. + V8_INLINE void Add(const Error& e) { + DCHECK_EQ(reported_errors_end_, reported_errors_->length()); + reported_errors_->Add(e, this->base_->impl()->zone()); + reported_errors_end_++; + } + + // Copies the error at position i of the list of reported errors, so that + // it becomes the last error reported for this classifier. Position i + // could be either after the existing errors of this classifier (i.e., + // in an inner classifier) or it could be an existing error (in case a + // copy is needed). + V8_INLINE void Copy(int i) { + DCHECK_LT(i, reported_errors_->length()); + if (reported_errors_end_ != i) + reported_errors_->at(reported_errors_end_) = reported_errors_->at(i); + reported_errors_end_++; + } + + private: +#ifdef DEBUG + V8_INLINE void CheckErrorPositions(ExpressionClassifier<Types>* const inner) { + DCHECK_EQ(inner->reported_errors_, this->reported_errors_); + DCHECK_EQ(inner->reported_errors_begin_, this->reported_errors_end_); + DCHECK_EQ(inner->reported_errors_end_, this->reported_errors_->length()); + } +#endif + + V8_INLINE void RewindErrors(ExpressionClassifier<Types>* const inner) { + this->reported_errors_->Rewind(this->reported_errors_end_); + inner->reported_errors_begin_ = inner->reported_errors_end_ = + this->reported_errors_end_; + } + + void AccumulateErrorImpl(ExpressionClassifier<Types>* const inner, + unsigned productions, unsigned errors, + bool copy_BP_to_AFP) { + // Traverse the list of errors reported by the inner classifier + // to copy what's necessary. + int binding_pattern_index = inner->reported_errors_end_; + for (int i = inner->reported_errors_begin_; i < inner->reported_errors_end_; + i++) { + int k = this->reported_errors_->at(i).kind; + if (errors & (1 << k)) this->Copy(i); + // Check if it's a BP error that has to be copied to an AFP error. + if (k == ErrorKind::kBindingPatternProduction && copy_BP_to_AFP) { + if (this->reported_errors_end_ <= i) { + // If the BP error itself has not already been copied, + // copy it now and change it to an AFP error. + this->Copy(i); + this->reported_errors_->at(this->reported_errors_end_ - 1).kind = + ErrorKind::kArrowFormalParametersProduction; + } else { + // Otherwise, if the BP error was already copied, keep its + // position and wait until the end of the traversal. + DCHECK_EQ(this->reported_errors_end_, i + 1); + binding_pattern_index = i; + } + } + } + // Do we still have to copy the BP error to an AFP error? + if (binding_pattern_index < inner->reported_errors_end_) { + // If there's still unused space in the list of the inner + // classifier, copy it there, otherwise add it to the end + // of the list. + if (this->reported_errors_end_ < inner->reported_errors_end_) + this->Copy(binding_pattern_index); + else + Add(this->reported_errors_->at(binding_pattern_index)); + this->reported_errors_->at(this->reported_errors_end_ - 1).kind = + ErrorKind::kArrowFormalParametersProduction; + } + } + + private: + ZoneList<Error>* reported_errors_; + // The uint16_t for reported_errors_begin_ and reported_errors_end_ will + // not be enough in the case of a long series of expressions using nested + // classifiers, e.g., a long sequence of assignments, as in: + // literals with spreads, as in: + // var N=65536; eval("var x;" + "x=".repeat(N) + "42"); + // This should not be a problem, as such things currently fail with a + // stack overflow while parsing. + uint16_t reported_errors_begin_; + uint16_t reported_errors_end_; + + friend BaseClassType; +}; + +template <typename Types> +class ExpressionClassifierEmptyErrorTracker + : public ExpressionClassifierBase< + Types, ExpressionClassifierEmptyErrorTracker<Types>> { + public: + using BaseClassType = + ExpressionClassifierBase<Types, + ExpressionClassifierEmptyErrorTracker<Types>>; + using typename BaseClassType::Error; + using typename BaseClassType::ErrorKind; + using TP = typename BaseClassType::TargetProduction; + + ExpressionClassifierEmptyErrorTracker(typename Types::Base* base, + DuplicateFinder* duplicate_finder) + : BaseClassType(base, duplicate_finder) {} + + V8_INLINE void Discard() {} + + protected: + V8_INLINE const Error& reported_error(ErrorKind kind) const { + static Error none; + return none; + } + + V8_INLINE void Add(const Error& e) {} + + private: +#ifdef DEBUG + V8_INLINE void CheckErrorPositions(ExpressionClassifier<Types>* const inner) { + } +#endif + V8_INLINE void RewindErrors(ExpressionClassifier<Types>* const inner) {} + V8_INLINE void AccumulateErrorImpl(ExpressionClassifier<Types>* const inner, + unsigned productions, unsigned errors, + bool copy_BP_to_AFP) {} + + friend BaseClassType; +}; + +template <typename Types> +class ExpressionClassifier + : public std::conditional< + Types::ExpressionClassifierReportErrors, + ExpressionClassifierErrorTracker<Types>, + ExpressionClassifierEmptyErrorTracker<Types>>::type { + static constexpr bool ReportErrors = Types::ExpressionClassifierReportErrors; + + public: + using BaseClassType = typename std::conditional< + Types::ExpressionClassifierReportErrors, + typename ExpressionClassifierErrorTracker<Types>::BaseClassType, + typename ExpressionClassifierEmptyErrorTracker<Types>::BaseClassType>:: + type; + using typename BaseClassType::Error; + using typename BaseClassType::ErrorKind; + using TP = typename BaseClassType::TargetProduction; + + explicit ExpressionClassifier(typename Types::Base* base, + DuplicateFinder* duplicate_finder = nullptr) + : std::conditional<Types::ExpressionClassifierReportErrors, + ExpressionClassifierErrorTracker<Types>, + ExpressionClassifierEmptyErrorTracker<Types>>:: + type(base, duplicate_finder), + previous_(base->classifier_) { + base->classifier_ = this; + } + + V8_INLINE ~ExpressionClassifier() override { + if (this->base_->classifier_ == this) this->base_->classifier_ = previous_; + } + V8_INLINE const Error& expression_error() const { - return reported_error(kExpressionProduction); + return this->reported_error(ErrorKind::kExpressionProduction); } V8_INLINE const Error& formal_parameter_initializer_error() const { - return reported_error(kFormalParameterInitializerProduction); + return this->reported_error( + ErrorKind::kFormalParameterInitializerProduction); } V8_INLINE const Error& binding_pattern_error() const { - return reported_error(kBindingPatternProduction); + return this->reported_error(ErrorKind::kBindingPatternProduction); } V8_INLINE const Error& assignment_pattern_error() const { - return reported_error(kAssignmentPatternProduction); + return this->reported_error(ErrorKind::kAssignmentPatternProduction); } V8_INLINE const Error& arrow_formal_parameters_error() const { - return reported_error(kArrowFormalParametersProduction); + return this->reported_error(ErrorKind::kArrowFormalParametersProduction); } V8_INLINE const Error& duplicate_formal_parameter_error() const { - return reported_error(kDistinctFormalParametersProduction); + return this->reported_error(ErrorKind::kDistinctFormalParametersProduction); } V8_INLINE const Error& strict_mode_formal_parameter_error() const { - return reported_error(kStrictModeFormalParametersProduction); + return this->reported_error( + ErrorKind::kStrictModeFormalParametersProduction); } V8_INLINE const Error& let_pattern_error() const { - return reported_error(kLetPatternProduction); + return this->reported_error(ErrorKind::kLetPatternProduction); } V8_INLINE const Error& async_arrow_formal_parameters_error() const { - return reported_error(kAsyncArrowFormalParametersProduction); - } - - V8_INLINE bool is_simple_parameter_list() const { - return !is_non_simple_parameter_list_; + return this->reported_error( + ErrorKind::kAsyncArrowFormalParametersProduction); } - V8_INLINE void RecordNonSimpleParameter() { - is_non_simple_parameter_list_ = 1; - } + V8_INLINE bool does_error_reporting() { return ReportErrors; } void RecordExpressionError(const Scanner::Location& loc, MessageTemplate::Template message, const char* arg = nullptr) { - if (!is_valid_expression()) return; - invalid_productions_ |= ExpressionProduction; - Add(Error(loc, message, kExpressionProduction, arg)); + if (!this->is_valid_expression()) return; + this->invalid_productions_ |= TP::ExpressionProduction; + this->Add(Error(loc, message, ErrorKind::kExpressionProduction, arg)); } void RecordFormalParameterInitializerError(const Scanner::Location& loc, MessageTemplate::Template message, const char* arg = nullptr) { - if (!is_valid_formal_parameter_initializer()) return; - invalid_productions_ |= FormalParameterInitializerProduction; - Add(Error(loc, message, kFormalParameterInitializerProduction, arg)); + if (!this->is_valid_formal_parameter_initializer()) return; + this->invalid_productions_ |= TP::FormalParameterInitializerProduction; + this->Add(Error(loc, message, + ErrorKind::kFormalParameterInitializerProduction, arg)); } void RecordBindingPatternError(const Scanner::Location& loc, MessageTemplate::Template message, const char* arg = nullptr) { - if (!is_valid_binding_pattern()) return; - invalid_productions_ |= BindingPatternProduction; - Add(Error(loc, message, kBindingPatternProduction, arg)); + if (!this->is_valid_binding_pattern()) return; + this->invalid_productions_ |= TP::BindingPatternProduction; + this->Add(Error(loc, message, ErrorKind::kBindingPatternProduction, arg)); } void RecordAssignmentPatternError(const Scanner::Location& loc, MessageTemplate::Template message, const char* arg = nullptr) { - if (!is_valid_assignment_pattern()) return; - invalid_productions_ |= AssignmentPatternProduction; - Add(Error(loc, message, kAssignmentPatternProduction, arg)); + if (!this->is_valid_assignment_pattern()) return; + this->invalid_productions_ |= TP::AssignmentPatternProduction; + this->Add( + Error(loc, message, ErrorKind::kAssignmentPatternProduction, arg)); } void RecordPatternError(const Scanner::Location& loc, @@ -236,24 +510,26 @@ class ExpressionClassifier { void RecordArrowFormalParametersError(const Scanner::Location& loc, MessageTemplate::Template message, const char* arg = nullptr) { - if (!is_valid_arrow_formal_parameters()) return; - invalid_productions_ |= ArrowFormalParametersProduction; - Add(Error(loc, message, kArrowFormalParametersProduction, arg)); + if (!this->is_valid_arrow_formal_parameters()) return; + this->invalid_productions_ |= TP::ArrowFormalParametersProduction; + this->Add( + Error(loc, message, ErrorKind::kArrowFormalParametersProduction, arg)); } void RecordAsyncArrowFormalParametersError(const Scanner::Location& loc, MessageTemplate::Template message, const char* arg = nullptr) { - if (!is_valid_async_arrow_formal_parameters()) return; - invalid_productions_ |= AsyncArrowFormalParametersProduction; - Add(Error(loc, message, kAsyncArrowFormalParametersProduction, arg)); + if (!this->is_valid_async_arrow_formal_parameters()) return; + this->invalid_productions_ |= TP::AsyncArrowFormalParametersProduction; + this->Add(Error(loc, message, + ErrorKind::kAsyncArrowFormalParametersProduction, arg)); } void RecordDuplicateFormalParameterError(const Scanner::Location& loc) { - if (!is_valid_formal_parameter_list_without_duplicates()) return; - invalid_productions_ |= DistinctFormalParametersProduction; - Add(Error(loc, MessageTemplate::kParamDupe, - kDistinctFormalParametersProduction)); + if (!this->is_valid_formal_parameter_list_without_duplicates()) return; + this->invalid_productions_ |= TP::DistinctFormalParametersProduction; + this->Add(Error(loc, MessageTemplate::kParamDupe, + ErrorKind::kDistinctFormalParametersProduction)); } // Record a binding that would be invalid in strict mode. Confusingly this @@ -262,160 +538,30 @@ class ExpressionClassifier { void RecordStrictModeFormalParameterError(const Scanner::Location& loc, MessageTemplate::Template message, const char* arg = nullptr) { - if (!is_valid_strict_mode_formal_parameters()) return; - invalid_productions_ |= StrictModeFormalParametersProduction; - Add(Error(loc, message, kStrictModeFormalParametersProduction, arg)); + if (!this->is_valid_strict_mode_formal_parameters()) return; + this->invalid_productions_ |= TP::StrictModeFormalParametersProduction; + this->Add(Error(loc, message, + ErrorKind::kStrictModeFormalParametersProduction, arg)); } void RecordLetPatternError(const Scanner::Location& loc, MessageTemplate::Template message, const char* arg = nullptr) { - if (!is_valid_let_pattern()) return; - invalid_productions_ |= LetPatternProduction; - Add(Error(loc, message, kLetPatternProduction, arg)); - } - - void Accumulate(ExpressionClassifier* inner, unsigned productions) { - DCHECK_EQ(inner->reported_errors_, reported_errors_); - DCHECK_EQ(inner->reported_errors_begin_, reported_errors_end_); - DCHECK_EQ(inner->reported_errors_end_, reported_errors_->size()); - // Propagate errors from inner, but don't overwrite already recorded - // errors. - unsigned non_arrow_inner_invalid_productions = - inner->invalid_productions_ & ~ArrowFormalParametersProduction; - if (non_arrow_inner_invalid_productions) { - unsigned errors = non_arrow_inner_invalid_productions & productions & - ~invalid_productions_; - // The result will continue to be a valid arrow formal parameters if the - // inner expression is a valid binding pattern. - bool copy_BP_to_AFP = false; - if (productions & ArrowFormalParametersProduction && - is_valid_arrow_formal_parameters()) { - // Also whether we've seen any non-simple parameters - // if expecting an arrow function parameter. - is_non_simple_parameter_list_ |= inner->is_non_simple_parameter_list_; - if (!inner->is_valid_binding_pattern()) { - copy_BP_to_AFP = true; - invalid_productions_ |= ArrowFormalParametersProduction; - } - } - // Traverse the list of errors reported by the inner classifier - // to copy what's necessary. - if (errors != 0 || copy_BP_to_AFP) { - invalid_productions_ |= errors; - int binding_pattern_index = inner->reported_errors_end_; - for (int i = inner->reported_errors_begin_; - i < inner->reported_errors_end_; i++) { - int k = reported_errors_->at(i).kind; - if (errors & (1 << k)) Copy(i); - // Check if it's a BP error that has to be copied to an AFP error. - if (k == kBindingPatternProduction && copy_BP_to_AFP) { - if (reported_errors_end_ <= i) { - // If the BP error itself has not already been copied, - // copy it now and change it to an AFP error. - Copy(i); - reported_errors_->at(reported_errors_end_-1).kind = - kArrowFormalParametersProduction; - } else { - // Otherwise, if the BP error was already copied, keep its - // position and wait until the end of the traversal. - DCHECK_EQ(reported_errors_end_, i+1); - binding_pattern_index = i; - } - } - } - // Do we still have to copy the BP error to an AFP error? - if (binding_pattern_index < inner->reported_errors_end_) { - // If there's still unused space in the list of the inner - // classifier, copy it there, otherwise add it to the end - // of the list. - if (reported_errors_end_ < inner->reported_errors_end_) - Copy(binding_pattern_index); - else - Add(reported_errors_->at(binding_pattern_index)); - reported_errors_->at(reported_errors_end_-1).kind = - kArrowFormalParametersProduction; - } - } - } - reported_errors_->resize(reported_errors_end_); - inner->reported_errors_begin_ = inner->reported_errors_end_ = - reported_errors_end_; - } - - V8_INLINE void Discard() { - if (reported_errors_end_ == reported_errors_->size()) { - reported_errors_->resize(reported_errors_begin_); - reported_errors_end_ = reported_errors_begin_; - } - DCHECK_EQ(reported_errors_begin_, reported_errors_end_); + if (!this->is_valid_let_pattern()) return; + this->invalid_productions_ |= TP::LetPatternProduction; + this->Add(Error(loc, message, ErrorKind::kLetPatternProduction, arg)); } ExpressionClassifier* previous() const { return previous_; } private: - V8_INLINE const Error& reported_error(ErrorKind kind) const { - if (invalid_productions_ & (1 << kind)) { - for (int i = reported_errors_begin_; i < reported_errors_end_; i++) { - if (reported_errors_->at(i).kind == kind) - return reported_errors_->at(i); - } - UNREACHABLE(); - } - // We should only be looking for an error when we know that one has - // been reported. But we're not... So this is to make sure we have - // the same behaviour. - UNREACHABLE(); - - // Make MSVC happy by returning an error from this inaccessible path. - static Error none; - return none; - } - - // Adds e to the end of the list of reported errors for this classifier. - // It is expected that this classifier is the last one in the stack. - V8_INLINE void Add(const Error& e) { - DCHECK_EQ(reported_errors_end_, reported_errors_->size()); - reported_errors_->push_back(e); - reported_errors_end_++; - } - - // Copies the error at position i of the list of reported errors, so that - // it becomes the last error reported for this classifier. Position i - // could be either after the existing errors of this classifier (i.e., - // in an inner classifier) or it could be an existing error (in case a - // copy is needed). - V8_INLINE void Copy(int i) { - DCHECK_LT(i, reported_errors_->size()); - if (reported_errors_end_ != i) - reported_errors_->at(reported_errors_end_) = reported_errors_->at(i); - reported_errors_end_++; - } - - typename Types::Base* base_; ExpressionClassifier* previous_; - Zone* zone_; - ZoneVector<Error>* reported_errors_; - DuplicateFinder* duplicate_finder_; - unsigned invalid_productions_ : 15; - unsigned is_non_simple_parameter_list_ : 1; - // The uint16_t for reported_errors_begin_ and reported_errors_end_ will - // not be enough in the case of a long series of expressions using nested - // classifiers, e.g., a long sequence of assignments, as in: - // literals with spreads, as in: - // var N=65536; eval("var x;" + "x=".repeat(N) + "42"); - // This should not be a problem, as such things currently fail with a - // stack overflow while parsing. - uint16_t reported_errors_begin_; - uint16_t reported_errors_end_; DISALLOW_COPY_AND_ASSIGN(ExpressionClassifier); }; - #undef ERROR_CODES - } // namespace internal } // namespace v8 diff --git a/deps/v8/src/parsing/func-name-inferrer.h b/deps/v8/src/parsing/func-name-inferrer.h index 8f0f428a05..d46d7f2c2b 100644 --- a/deps/v8/src/parsing/func-name-inferrer.h +++ b/deps/v8/src/parsing/func-name-inferrer.h @@ -36,12 +36,8 @@ class FuncNameInferrer : public ZoneObject { // on the stack. class State { public: - explicit State(FuncNameInferrer* fni) : fni_(fni) { - if (fni_ != nullptr) fni_->Enter(); - } - ~State() { - if (fni_ != nullptr) fni_->Leave(); - } + explicit State(FuncNameInferrer* fni) : fni_(fni) { fni_->Enter(); } + ~State() { fni_->Leave(); } private: FuncNameInferrer* fni_; diff --git a/deps/v8/src/parsing/parse-info.cc b/deps/v8/src/parsing/parse-info.cc index 0a58c4f0bd..129b00a2c2 100644 --- a/deps/v8/src/parsing/parse-info.cc +++ b/deps/v8/src/parsing/parse-info.cc @@ -16,7 +16,7 @@ namespace v8 { namespace internal { -ParseInfo::ParseInfo(Isolate* isolate, AccountingAllocator* zone_allocator) +ParseInfo::ParseInfo(AccountingAllocator* zone_allocator) : zone_(base::make_unique<Zone>(zone_allocator, ZONE_NAME)), flags_(0), extension_(nullptr), @@ -37,7 +37,10 @@ ParseInfo::ParseInfo(Isolate* isolate, AccountingAllocator* zone_allocator) function_name_(nullptr), runtime_call_stats_(nullptr), source_range_map_(nullptr), - literal_(nullptr) { + literal_(nullptr) {} + +ParseInfo::ParseInfo(Isolate* isolate, AccountingAllocator* zone_allocator) + : ParseInfo(zone_allocator) { set_hash_seed(isolate->heap()->HashSeed()); set_stack_limit(isolate->stack_guard()->real_climit()); set_unicode_cache(isolate->unicode_cache()); @@ -54,6 +57,18 @@ ParseInfo::ParseInfo(Isolate* isolate) LOG(isolate, ScriptEvent(Logger::ScriptEventType::kReserveId, script_id_)); } +template <typename T> +void ParseInfo::SetFunctionInfo(T function) { + set_is_named_expression(function->is_named_expression()); + set_language_mode(function->language_mode()); + set_function_kind(function->kind()); + set_declaration(function->is_declaration()); + set_requires_instance_fields_initializer( + function->requires_instance_fields_initializer()); + set_toplevel(function->is_toplevel()); + set_wrapped_as_function(function->is_wrapped()); +} + ParseInfo::ParseInfo(Isolate* isolate, Handle<SharedFunctionInfo> shared) : ParseInfo(isolate, isolate->allocator()) { // Do not support re-parsing top-level function of a wrapped script. @@ -61,19 +76,13 @@ ParseInfo::ParseInfo(Isolate* isolate, Handle<SharedFunctionInfo> shared) // wrapped script at all. DCHECK_IMPLIES(is_toplevel(), !Script::cast(shared->script())->is_wrapped()); - set_toplevel(shared->is_toplevel()); - set_wrapped_as_function(shared->is_wrapped()); set_allow_lazy_parsing(FLAG_lazy_inner_functions); - set_is_named_expression(shared->is_named_expression()); + set_asm_wasm_broken(shared->is_asm_wasm_broken()); + set_start_position(shared->StartPosition()); set_end_position(shared->EndPosition()); function_literal_id_ = shared->FunctionLiteralId(isolate); - set_language_mode(shared->language_mode()); - set_function_kind(shared->kind()); - set_declaration(shared->is_declaration()); - set_requires_instance_fields_initializer( - shared->requires_instance_fields_initializer()); - set_asm_wasm_broken(shared->is_asm_wasm_broken()); + SetFunctionInfo(shared); Handle<Script> script(Script::cast(shared->script()), isolate); set_script(script); @@ -99,37 +108,44 @@ ParseInfo::ParseInfo(Isolate* isolate, Handle<Script> script) script->IsUserJavaScript()); } -ParseInfo::~ParseInfo() {} - -DeclarationScope* ParseInfo::scope() const { return literal()->scope(); } - -void ParseInfo::EmitBackgroundParseStatisticsOnBackgroundThread() { - // If runtime call stats was enabled by tracing, emit a trace event at the - // end of background parsing on the background thread. - if (runtime_call_stats_ && - (FLAG_runtime_stats & - v8::tracing::TracingCategoryObserver::ENABLED_BY_TRACING)) { - auto value = v8::tracing::TracedValue::Create(); - runtime_call_stats_->Dump(value.get()); - TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("v8.runtime_stats"), - "V8.RuntimeStats", TRACE_EVENT_SCOPE_THREAD, - "runtime-call-stats", std::move(value)); - } +// static +std::unique_ptr<ParseInfo> ParseInfo::FromParent( + const ParseInfo* outer_parse_info, AccountingAllocator* zone_allocator, + const FunctionLiteral* literal, const AstRawString* function_name) { + std::unique_ptr<ParseInfo> result = + base::make_unique<ParseInfo>(zone_allocator); + + // Replicate shared state of the outer_parse_info. + result->flags_ = outer_parse_info->flags_; + result->script_id_ = outer_parse_info->script_id_; + result->set_logger(outer_parse_info->logger()); + result->set_ast_string_constants(outer_parse_info->ast_string_constants()); + result->set_hash_seed(outer_parse_info->hash_seed()); + + DCHECK_EQ(outer_parse_info->parameters_end_pos(), kNoSourcePosition); + DCHECK_NULL(outer_parse_info->extension()); + DCHECK(outer_parse_info->maybe_outer_scope_info().is_null()); + + // Clone the function_name AstRawString into the ParseInfo's own + // AstValueFactory. + const AstRawString* cloned_function_name = + result->GetOrCreateAstValueFactory()->CloneFromOtherFactory( + function_name); + + // Setup function specific details. + DCHECK(!literal->is_toplevel()); + result->set_function_name(cloned_function_name); + result->set_start_position(literal->start_position()); + result->set_end_position(literal->end_position()); + result->set_function_literal_id(literal->function_literal_id()); + result->SetFunctionInfo(literal); + + return result; } -void ParseInfo::UpdateBackgroundParseStatisticsOnMainThread(Isolate* isolate) { - // Copy over the counters from the background thread to the main counters on - // the isolate. - RuntimeCallStats* main_call_stats = isolate->counters()->runtime_call_stats(); - if (FLAG_runtime_stats == - v8::tracing::TracingCategoryObserver::ENABLED_BY_NATIVE) { - DCHECK_NE(main_call_stats, runtime_call_stats()); - DCHECK_NOT_NULL(main_call_stats); - DCHECK_NOT_NULL(runtime_call_stats()); - main_call_stats->Add(runtime_call_stats()); - } - set_runtime_call_stats(main_call_stats); -} +ParseInfo::~ParseInfo() = default; + +DeclarationScope* ParseInfo::scope() const { return literal()->scope(); } Handle<Script> ParseInfo::CreateScript(Isolate* isolate, Handle<String> source, ScriptOriginOptions origin_options, diff --git a/deps/v8/src/parsing/parse-info.h b/deps/v8/src/parsing/parse-info.h index 64a50806f5..ba3e3d2898 100644 --- a/deps/v8/src/parsing/parse-info.h +++ b/deps/v8/src/parsing/parse-info.h @@ -12,6 +12,7 @@ #include "include/v8.h" #include "src/globals.h" #include "src/handles.h" +#include "src/objects/script.h" #include "src/parsing/preparsed-scope-data.h" #include "src/pending-compilation-error-handler.h" @@ -37,11 +38,18 @@ class Zone; // A container for the inputs, configuration options, and outputs of parsing. class V8_EXPORT_PRIVATE ParseInfo { public: - ParseInfo(Isolate*); + explicit ParseInfo(AccountingAllocator* zone_allocator); + explicit ParseInfo(Isolate*); ParseInfo(Isolate*, AccountingAllocator* zone_allocator); ParseInfo(Isolate* isolate, Handle<Script> script); ParseInfo(Isolate* isolate, Handle<SharedFunctionInfo> shared); + // Creates a new parse info based on parent top-level |outer_parse_info| for + // function |literal|. + static std::unique_ptr<ParseInfo> FromParent( + const ParseInfo* outer_parse_info, AccountingAllocator* zone_allocator, + const FunctionLiteral* literal, const AstRawString* function_name); + ~ParseInfo(); Handle<Script> CreateScript(Isolate* isolate, Handle<String> source, @@ -105,9 +113,12 @@ class V8_EXPORT_PRIVATE ParseInfo { v8::Extension* extension() const { return extension_; } void set_extension(v8::Extension* extension) { extension_ = extension; } - + void set_consumed_preparsed_scope_data( + std::unique_ptr<ConsumedPreParsedScopeData> data) { + consumed_preparsed_scope_data_.swap(data); + } ConsumedPreParsedScopeData* consumed_preparsed_scope_data() { - return &consumed_preparsed_scope_data_; + return consumed_preparsed_scope_data_.get(); } DeclarationScope* script_scope() const { return script_scope_; } @@ -198,6 +209,8 @@ class V8_EXPORT_PRIVATE ParseInfo { // TODO(titzer): these should not be part of ParseInfo. //-------------------------------------------------------------------------- Handle<Script> script() const { return script_; } + void set_script(Handle<Script> script); + MaybeHandle<ScopeInfo> maybe_outer_scope_info() const { return maybe_outer_scope_info_; } @@ -216,12 +229,13 @@ class V8_EXPORT_PRIVATE ParseInfo { set_strict_mode(is_strict(language_mode)); } - void EmitBackgroundParseStatisticsOnBackgroundThread(); - void UpdateBackgroundParseStatisticsOnMainThread(Isolate* isolate); - private: void SetScriptForToplevelCompile(Isolate* isolate, Handle<Script> script); - void set_script(Handle<Script> script); + + // Set function info flags based on those in either FunctionLiteral or + // SharedFunctionInfo |function| + template <typename T> + void SetFunctionInfo(T function); // Various configuration flags for parsing. enum Flag { @@ -268,7 +282,7 @@ class V8_EXPORT_PRIVATE ParseInfo { //----------- Inputs+Outputs of parsing and scope analysis ----------------- std::unique_ptr<Utf16CharacterStream> character_stream_; - ConsumedPreParsedScopeData consumed_preparsed_scope_data_; + std::unique_ptr<ConsumedPreParsedScopeData> consumed_preparsed_scope_data_; std::unique_ptr<AstValueFactory> ast_value_factory_; const class AstStringConstants* ast_string_constants_; const AstRawString* function_name_; diff --git a/deps/v8/src/parsing/parser-base.h b/deps/v8/src/parsing/parser-base.h index 9d13724f06..d1ad0e9d13 100644 --- a/deps/v8/src/parsing/parser-base.h +++ b/deps/v8/src/parsing/parser-base.h @@ -5,12 +5,14 @@ #ifndef V8_PARSING_PARSER_BASE_H_ #define V8_PARSING_PARSER_BASE_H_ +#include <stdint.h> #include <vector> #include "src/ast/ast-source-ranges.h" #include "src/ast/ast.h" #include "src/ast/scopes.h" #include "src/bailout-reason.h" +#include "src/base/flags.h" #include "src/base/hashmap.h" #include "src/base/v8-fallthrough.h" #include "src/counters.h" @@ -37,31 +39,13 @@ enum AllowLabelledFunctionStatement { kDisallowLabelledFunctionStatement, }; -enum class ParseFunctionFlags { +enum class ParseFunctionFlag : uint8_t { kIsNormal = 0, - kIsGenerator = 1, - kIsAsync = 2, - kIsDefault = 4 + kIsGenerator = 1 << 0, + kIsAsync = 1 << 1 }; -static inline ParseFunctionFlags operator|(ParseFunctionFlags lhs, - ParseFunctionFlags rhs) { - typedef unsigned char T; - return static_cast<ParseFunctionFlags>(static_cast<T>(lhs) | - static_cast<T>(rhs)); -} - -static inline ParseFunctionFlags& operator|=(ParseFunctionFlags& lhs, - const ParseFunctionFlags& rhs) { - lhs = lhs | rhs; - return lhs; -} - -static inline bool operator&(ParseFunctionFlags bitfield, - ParseFunctionFlags mask) { - typedef unsigned char T; - return static_cast<T>(bitfield) & static_cast<T>(mask); -} +typedef base::Flags<ParseFunctionFlag> ParseFunctionFlags; struct FormalParametersBase { explicit FormalParametersBase(DeclarationScope* scope) : scope(scope) {} @@ -228,6 +212,17 @@ class SourceRangeScope final { template <typename Impl> struct ParserTypes; +enum class ParsePropertyKind : uint8_t { + kAccessorGetter, + kAccessorSetter, + kValue, + kShorthand, + kMethod, + kClassField, + kSpread, + kNotSet +}; + template <typename Impl> class ParserBase { public: @@ -248,6 +243,10 @@ class ParserBase { typedef typename Types::ForStatement ForStatementT; typedef typename v8::internal::ExpressionClassifier<Types> ExpressionClassifier; + typedef typename Types::FuncNameInferrer FuncNameInferrer; + typedef typename Types::FuncNameInferrer::State FuncNameInferrerState; + typedef typename Types::SourceRange SourceRange; + typedef typename Types::SourceRangeScope SourceRangeScope; // All implementation-specific methods must be called through this. Impl* impl() { return static_cast<Impl*>(this); } @@ -262,7 +261,7 @@ class ParserBase { original_scope_(nullptr), function_state_(nullptr), extension_(extension), - fni_(nullptr), + fni_(ast_value_factory, zone), ast_value_factory_(ast_value_factory), ast_node_factory_(ast_value_factory, zone), runtime_call_stats_(runtime_call_stats), @@ -300,12 +299,6 @@ class ParserBase { #undef ALLOW_ACCESSORS - bool allow_harmony_bigint() const { - return scanner()->allow_harmony_bigint(); - } - void set_allow_harmony_bigint(bool allow) { - scanner()->set_allow_harmony_bigint(allow); - } bool allow_harmony_numeric_separator() const { return scanner()->allow_harmony_numeric_separator(); } @@ -370,7 +363,7 @@ class ParserBase { // The parser's current scope is in scope_. BlockState and FunctionState // constructors push on the scope stack and the destructors pop. They are also // used to hold the parser's per-funcion state. - class BlockState BASE_EMBEDDED { + class BlockState { public: BlockState(Scope** scope_stack, Scope* scope) : scope_stack_(scope_stack), outer_scope_(*scope_stack) { @@ -433,7 +426,7 @@ class ParserBase { return destructuring_assignments_to_rewrite_; } - ZoneVector<typename ExpressionClassifier::Error>* GetReportedErrorList() { + ZoneList<typename ExpressionClassifier::Error>* GetReportedErrorList() { return &reported_errors_; } @@ -490,8 +483,7 @@ class ParserBase { ZoneChunkList<RewritableExpressionT> destructuring_assignments_to_rewrite_; - // We use a ZoneVector here because we need to do a lot of random access. - ZoneVector<typename ExpressionClassifier::Error> reported_errors_; + ZoneList<typename ExpressionClassifier::Error> reported_errors_; // A reason, if any, why this function should not be optimized. BailoutReason dont_optimize_reason_; @@ -652,11 +644,11 @@ class ParserBase { // scope itself is either allocated in zone() or in target_zone if one is // passed in. DeclarationScope* NewFunctionScope(FunctionKind kind, - Zone* target_zone = nullptr) const { + Zone* parse_zone = nullptr) const { DCHECK(ast_value_factory()); - if (target_zone == nullptr) target_zone = zone(); - DeclarationScope* result = new (target_zone) - DeclarationScope(zone(), scope(), FUNCTION_SCOPE, kind); + if (parse_zone == nullptr) parse_zone = zone(); + DeclarationScope* result = new (zone()) + DeclarationScope(parse_zone, scope(), FUNCTION_SCOPE, kind); // Record presence of an inner function scope function_state_->RecordFunctionOrEvalCall(); @@ -679,6 +671,8 @@ class ParserBase { AstValueFactory* ast_value_factory() const { return ast_value_factory_; } int position() const { return scanner_->location().beg_pos; } int peek_position() const { return scanner_->peek_location().beg_pos; } + int end_position() const { return scanner_->location().end_pos; } + int peek_end_position() const { return scanner_->peek_location().end_pos; } bool stack_overflow() const { return pending_error_handler()->stack_overflow(); } @@ -694,8 +688,7 @@ class ParserBase { // Returns the position past the following semicolon (if it exists), and the // position past the end of the current token otherwise. int PositionAfterSemicolon() { - return (peek() == Token::SEMICOLON) ? scanner_->peek_location().end_pos - : scanner_->location().end_pos; + return (peek() == Token::SEMICOLON) ? peek_end_position() : end_position(); } V8_INLINE Token::Value PeekAhead() { @@ -720,7 +713,7 @@ class ParserBase { Token::Value next = Next(); USE(next); USE(token); - DCHECK(next == token); + DCHECK_EQ(next, token); } bool Check(Token::Value token) { @@ -753,22 +746,14 @@ class ParserBase { return; } - Token::Value current = scanner()->current_token(); - Scanner::Location current_location = scanner()->location(); - Token::Value next = Next(); - - if (next == Token::SEMICOLON) { - return; - } - *ok = false; - if (current == Token::AWAIT && !is_async_function()) { - ReportMessageAt(current_location, + if (scanner()->current_token() == Token::AWAIT && !is_async_function()) { + ReportMessageAt(scanner()->location(), MessageTemplate::kAwaitNotInAsyncFunction, kSyntaxError); return; } - ReportUnexpectedToken(next); + ReportUnexpectedToken(Next()); } // Dummy functions, just useful as arguments to CHECK_OK_CUSTOM. @@ -778,14 +763,7 @@ class ParserBase { return result; } - bool is_any_identifier(Token::Value token) { - return token == Token::IDENTIFIER || token == Token::ENUM || - token == Token::AWAIT || token == Token::ASYNC || - token == Token::ESCAPED_STRICT_RESERVED_WORD || - token == Token::FUTURE_STRICT_RESERVED_WORD || token == Token::LET || - token == Token::STATIC || token == Token::YIELD; - } - bool peek_any_identifier() { return is_any_identifier(peek()); } + bool peek_any_identifier() { return Token::IsAnyIdentifier(peek()); } bool CheckContextualKeyword(Token::Value token) { if (PeekContextualKeyword(token)) { @@ -961,7 +939,11 @@ class ParserBase { void ReportClassifierError( const typename ExpressionClassifier::Error& error) { - impl()->ReportMessageAt(error.location, error.message, error.arg); + if (classifier()->does_error_reporting()) { + impl()->ReportMessageAt(error.location, error.message, error.arg); + } else { + impl()->ReportUnidentifiableError(); + } } void ValidateExpression(bool* ok) { @@ -1006,7 +988,7 @@ class ParserBase { } bool IsValidArrowFormalParametersStart(Token::Value token) { - return is_any_identifier(token) || token == Token::LPAREN; + return Token::IsAnyIdentifier(token) || token == Token::LPAREN; } void ValidateArrowFormalParameters(ExpressionT expr, @@ -1096,15 +1078,12 @@ class ParserBase { ExpressionT ParseRegExpLiteral(bool* ok); + ExpressionT ParseBindingPattern(bool* ok); ExpressionT ParsePrimaryExpression(bool* is_async, bool* ok); - ExpressionT ParsePrimaryExpression(bool* ok) { - bool is_async; - return ParsePrimaryExpression(&is_async, ok); - } // Use when parsing an expression that is known to not be a pattern or part // of a pattern. - V8_INLINE ExpressionT ParseExpression(bool accept_IN, bool* ok); + V8_INLINE ExpressionT ParseExpression(bool* ok); // This method does not wrap the parsing of the expression inside a // new expression classifier; it uses the top-level classifier instead. @@ -1117,28 +1096,21 @@ class ParserBase { ExpressionT ParseArrayLiteral(bool* ok); - enum class PropertyKind { - kAccessorProperty, - kValueProperty, - kShorthandProperty, - kMethodProperty, - kClassField, - kSpreadProperty, - kNotSet - }; + inline static bool IsAccessor(ParsePropertyKind kind) { + return IsInRange(kind, ParsePropertyKind::kAccessorGetter, + ParsePropertyKind::kAccessorSetter); + } - bool SetPropertyKindFromToken(Token::Value token, PropertyKind* kind); - ExpressionT ParsePropertyName(IdentifierT* name, PropertyKind* kind, - bool* is_generator, bool* is_get, bool* is_set, - bool* is_async, bool* is_computed_name, - bool* ok); + ExpressionT ParsePropertyName(IdentifierT* name, ParsePropertyKind* kind, + ParseFunctionFlags* flags, + bool* is_computed_name, bool* ok); ExpressionT ParseObjectLiteral(bool* ok); ClassLiteralPropertyT ParseClassPropertyDefinition( ClassLiteralChecker* checker, ClassInfo* class_info, IdentifierT* property_name, bool has_extends, bool* is_computed_name, ClassLiteralProperty::Kind* property_kind, bool* is_static, bool* ok); - ExpressionT ParseClassFieldInitializer(ClassInfo* class_info, bool is_static, - bool* ok); + ExpressionT ParseClassFieldInitializer(ClassInfo* class_info, int beg_pos, + bool is_static, bool* ok); ObjectLiteralPropertyT ParseObjectPropertyDefinition( ObjectLiteralChecker* checker, bool* is_computed_name, bool* is_rest_property, bool* ok); @@ -1147,17 +1119,26 @@ class ParserBase { bool* is_simple_parameter_list, bool* ok); ExpressionListT ParseArguments(Scanner::Location* first_spread_pos, bool* ok) { - return ParseArguments(first_spread_pos, false, nullptr, ok); + bool is_simple = true; + return ParseArguments(first_spread_pos, false, &is_simple, ok); } ExpressionT ParseAssignmentExpression(bool accept_IN, bool* ok); ExpressionT ParseYieldExpression(bool accept_IN, bool* ok); V8_INLINE ExpressionT ParseConditionalExpression(bool accept_IN, bool* ok); + ExpressionT ParseConditionalContinuation(ExpressionT expression, + bool accept_IN, int pos, bool* ok); ExpressionT ParseBinaryExpression(int prec, bool accept_IN, bool* ok); - ExpressionT ParseUnaryExpression(bool* ok); + ExpressionT ParseUnaryOpExpression(bool* ok); + ExpressionT ParseAwaitExpression(bool* ok); + ExpressionT ParsePrefixExpression(bool* ok); + V8_INLINE ExpressionT ParseUnaryExpression(bool* ok); V8_INLINE ExpressionT ParsePostfixExpression(bool* ok); V8_INLINE ExpressionT ParseLeftHandSideExpression(bool* ok); - ExpressionT ParseMemberWithNewPrefixesExpression(bool* is_async, bool* ok); + ExpressionT ParseMemberWithPresentNewPrefixesExpression(bool* is_async, + bool* ok); + V8_INLINE ExpressionT ParseMemberWithNewPrefixesExpression(bool* is_async, + bool* ok); V8_INLINE ExpressionT ParseMemberExpression(bool* is_async, bool* ok); V8_INLINE ExpressionT ParseMemberExpressionContinuation( ExpressionT expression, bool* is_async, bool* ok); @@ -1169,8 +1150,6 @@ class ParserBase { ExpressionT ParseArrowFunctionLiteral(bool accept_IN, const FormalParametersT& parameters, int rewritable_length, bool* ok); - void ParseSingleExpressionFunctionBody(StatementListT body, bool is_async, - bool accept_IN, bool* ok); void ParseAsyncFunctionBody(Scope* scope, StatementListT body, bool* ok); ExpressionT ParseAsyncFunctionLiteral(bool* ok); ExpressionT ParseClassLiteral(IdentifierT name, @@ -1205,11 +1184,14 @@ class ParserBase { bool default_export, bool* ok); StatementT ParseNativeDeclaration(bool* ok); + // Whether we're parsing a single-expression arrow function or something else. + enum class FunctionBodyType { kExpression, kBlock }; // Consumes the ending }. void ParseFunctionBody(StatementListT result, IdentifierT function_name, int pos, const FormalParametersT& parameters, FunctionKind kind, - FunctionLiteral::FunctionType function_type, bool* ok); + FunctionLiteral::FunctionType function_type, + FunctionBodyType body_type, bool accept_IN, bool* ok); // Under some circumstances, we allow preparsing to abort if the preparsed // function is "long and trivial", and fully parse instead. Our current @@ -1363,8 +1345,7 @@ class ParserBase { Scope* scope, Variable* var, typename DeclarationDescriptor::Kind declaration_kind); - FunctionKind FunctionKindForImpl(bool is_method, bool is_generator, - bool is_async) { + FunctionKind FunctionKindForImpl(bool is_method, ParseFunctionFlags flags) { static const FunctionKind kFunctionKinds[][2][2] = { { // is_method=false @@ -1382,17 +1363,19 @@ class ParserBase { FunctionKind::kConciseGeneratorMethod, FunctionKind::kAsyncConciseGeneratorMethod}, }}; - return kFunctionKinds[is_method][is_generator][is_async]; + return kFunctionKinds[is_method] + [(flags & ParseFunctionFlag::kIsGenerator) != 0] + [(flags & ParseFunctionFlag::kIsAsync) != 0]; } - inline FunctionKind FunctionKindFor(bool is_generator, bool is_async) { + inline FunctionKind FunctionKindFor(ParseFunctionFlags flags) { const bool kIsMethod = false; - return FunctionKindForImpl(kIsMethod, is_generator, is_async); + return FunctionKindForImpl(kIsMethod, flags); } - inline FunctionKind MethodKindFor(bool is_generator, bool is_async) { + inline FunctionKind MethodKindFor(ParseFunctionFlags flags) { const bool kIsMethod = true; - return FunctionKindForImpl(kIsMethod, is_generator, is_async); + return FunctionKindForImpl(kIsMethod, flags); } // Keep track of eval() calls since they disable all local variable @@ -1466,8 +1449,8 @@ class ParserBase { explicit ClassLiteralChecker(ParserBase* parser) : parser_(parser), has_seen_constructor_(false) {} - void CheckClassMethodName(Token::Value property, PropertyKind type, - bool is_generator, bool is_async, bool is_static, + void CheckClassMethodName(Token::Value property, ParsePropertyKind type, + ParseFunctionFlags flags, bool is_static, bool* ok); void CheckClassFieldName(bool is_static, bool* ok); @@ -1546,7 +1529,7 @@ class ParserBase { Scope* original_scope_; // The top scope for the current parsing item. FunctionState* function_state_; // Function state stack. v8::Extension* extension_; - FuncNameInferrer* fni_; + FuncNameInferrer fni_; AstValueFactory* ast_value_factory_; // Not owned. typename Types::Factory ast_node_factory_; RuntimeCallStats* runtime_call_stats_; @@ -1577,8 +1560,6 @@ class ParserBase { bool allow_harmony_import_meta_; bool allow_harmony_private_fields_; bool allow_eval_cache_; - - friend class DiscardableZoneScope; }; template <typename Impl> @@ -1592,13 +1573,12 @@ ParserBase<Impl>::FunctionState::FunctionState( outer_function_state_(*function_state_stack), scope_(scope), destructuring_assignments_to_rewrite_(scope->zone()), - reported_errors_(scope_->zone()), + reported_errors_(16, scope->zone()), dont_optimize_reason_(BailoutReason::kNoReason), next_function_is_likely_called_(false), previous_function_was_likely_called_(false), contains_function_or_eval_(false) { *function_state_stack = this; - reported_errors_.reserve(16); if (outer_function_state_) { outer_function_state_->previous_function_was_likely_called_ = outer_function_state_->next_function_is_likely_called_; @@ -1704,16 +1684,10 @@ template <typename Impl> typename ParserBase<Impl>::IdentifierT ParserBase<Impl>::ParseAndClassifyIdentifier(bool* ok) { Token::Value next = Next(); - if (next == Token::IDENTIFIER || next == Token::ASYNC || - (next == Token::AWAIT && !parsing_module_ && !is_async_function())) { + STATIC_ASSERT(Token::IDENTIFIER + 1 == Token::ASYNC); + if (IsInRange(next, Token::IDENTIFIER, Token::ASYNC)) { IdentifierT name = impl()->GetSymbol(); - if (impl()->IsArguments(name) && scope()->ShouldBanArguments()) { - ReportMessage(MessageTemplate::kArgumentsDisallowedInInitializer); - *ok = false; - return impl()->NullIdentifier(); - } - // When this function is used to read a formal parameter, we don't always // know whether the function is going to be strict or sloppy. Indeed for // arrow functions we don't always know that the identifier we are reading @@ -1721,15 +1695,18 @@ ParserBase<Impl>::ParseAndClassifyIdentifier(bool* ok) { // must detect because we know we're in strict mode, we also record any // error that we might make in the future once we know the language mode. if (impl()->IsEvalOrArguments(name)) { + if (impl()->IsArguments(name) && scope()->ShouldBanArguments()) { + ReportMessage(MessageTemplate::kArgumentsDisallowedInInitializer); + *ok = false; + return impl()->NullIdentifier(); + } + classifier()->RecordStrictModeFormalParameterError( scanner()->location(), MessageTemplate::kStrictEvalArguments); if (is_strict(language_mode())) { classifier()->RecordBindingPatternError( scanner()->location(), MessageTemplate::kStrictEvalArguments); } - } else if (next == Token::AWAIT) { - classifier()->RecordAsyncArrowFormalParametersError( - scanner()->location(), MessageTemplate::kAwaitBindingIdentifier); } if (classifier()->duplicate_finder() != nullptr && @@ -1737,20 +1714,17 @@ ParserBase<Impl>::ParseAndClassifyIdentifier(bool* ok) { ast_value_factory())) { classifier()->RecordDuplicateFormalParameterError(scanner()->location()); } + return name; + } else if (next == Token::AWAIT && !parsing_module_ && !is_async_function()) { + classifier()->RecordAsyncArrowFormalParametersError( + scanner()->location(), MessageTemplate::kAwaitBindingIdentifier); + return impl()->GetSymbol(); } else if (is_sloppy(language_mode()) && - (next == Token::FUTURE_STRICT_RESERVED_WORD || - next == Token::ESCAPED_STRICT_RESERVED_WORD || - next == Token::LET || next == Token::STATIC || + (Token::IsStrictReservedWord(next) || (next == Token::YIELD && !is_generator()))) { classifier()->RecordStrictModeFormalParameterError( scanner()->location(), MessageTemplate::kUnexpectedStrictReserved); - if (next == Token::ESCAPED_STRICT_RESERVED_WORD && - is_strict(language_mode())) { - ReportUnexpectedToken(next); - *ok = false; - return impl()->NullIdentifier(); - } if (scanner()->IsLet()) { classifier()->RecordLetPatternError( scanner()->location(), MessageTemplate::kLetInLexicalBinding); @@ -1774,9 +1748,7 @@ ParserBase<Impl>::ParseIdentifierOrStrictReservedWord( next == Token::ASYNC) { *is_strict_reserved = false; *is_await = next == Token::AWAIT; - } else if (next == Token::ESCAPED_STRICT_RESERVED_WORD || - next == Token::FUTURE_STRICT_RESERVED_WORD || next == Token::LET || - next == Token::STATIC || + } else if (Token::IsStrictReservedWord(next) || (next == Token::YIELD && !IsGeneratorFunction(function_kind))) { *is_strict_reserved = true; } else { @@ -1792,12 +1764,8 @@ template <typename Impl> typename ParserBase<Impl>::IdentifierT ParserBase<Impl>::ParseIdentifierName( bool* ok) { Token::Value next = Next(); - if (next != Token::IDENTIFIER && next != Token::ASYNC && - next != Token::ENUM && next != Token::AWAIT && next != Token::LET && - next != Token::STATIC && next != Token::YIELD && - next != Token::FUTURE_STRICT_RESERVED_WORD && - next != Token::ESCAPED_KEYWORD && - next != Token::ESCAPED_STRICT_RESERVED_WORD && !Token::IsKeyword(next)) { + if (!Token::IsAnyIdentifier(next) && next != Token::ESCAPED_KEYWORD && + !Token::IsKeyword(next)) { ReportUnexpectedToken(next); *ok = false; return impl()->NullIdentifier(); @@ -1852,6 +1820,39 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseRegExpLiteral( } template <typename Impl> +typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseBindingPattern( + bool* ok) { + // Pattern :: + // Identifier + // ArrayLiteral + // ObjectLiteral + + int beg_pos = peek_position(); + Token::Value token = peek(); + ExpressionT result; + + if (Token::IsAnyIdentifier(token)) { + IdentifierT name = ParseAndClassifyIdentifier(CHECK_OK); + result = impl()->ExpressionFromIdentifier(name, beg_pos); + } else { + classifier()->RecordNonSimpleParameter(); + + if (token == Token::LBRACK) { + result = ParseArrayLiteral(CHECK_OK); + } else if (token == Token::LBRACE) { + result = ParseObjectLiteral(CHECK_OK); + } else { + ReportUnexpectedToken(Next()); + *ok = false; + return impl()->NullExpression(); + } + } + + ValidateBindingPattern(CHECK_OK); + return result; +} + +template <typename Impl> typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParsePrimaryExpression( bool* is_async, bool* ok) { // PrimaryExpression :: @@ -1872,7 +1873,8 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParsePrimaryExpression( // AsyncFunctionLiteral int beg_pos = peek_position(); - switch (peek()) { + Token::Value token = peek(); + switch (token) { case Token::THIS: { BindingPatternUnexpectedToken(); Consume(Token::THIS); @@ -1884,16 +1886,25 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParsePrimaryExpression( case Token::FALSE_LITERAL: case Token::SMI: case Token::NUMBER: - case Token::BIGINT: + case Token::BIGINT: { + // Ensure continuous enum range. + DCHECK(Token::IsLiteral(token)); BindingPatternUnexpectedToken(); return impl()->ExpressionFromLiteral(Next(), beg_pos); + } + case Token::STRING: { + DCHECK(Token::IsLiteral(token)); + BindingPatternUnexpectedToken(); + Consume(Token::STRING); + return impl()->ExpressionFromString(beg_pos); + } case Token::ASYNC: if (!scanner()->HasLineTerminatorAfterNext() && PeekAhead() == Token::FUNCTION) { BindingPatternUnexpectedToken(); Consume(Token::ASYNC); - return ParseAsyncFunctionLiteral(CHECK_OK); + return ParseAsyncFunctionLiteral(ok); } // CoverCallExpressionAndAsyncArrowHead *is_async = true; @@ -1903,19 +1914,16 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParsePrimaryExpression( case Token::STATIC: case Token::YIELD: case Token::AWAIT: - case Token::ESCAPED_STRICT_RESERVED_WORD: - case Token::FUTURE_STRICT_RESERVED_WORD: { + case Token::FUTURE_STRICT_RESERVED_WORD: + case Token::ESCAPED_STRICT_RESERVED_WORD: { + // Ensure continuous enum range. + DCHECK(IsInRange(token, Token::IDENTIFIER, + Token::ESCAPED_STRICT_RESERVED_WORD)); // Using eval or arguments in this context is OK even in strict mode. IdentifierT name = ParseAndClassifyIdentifier(CHECK_OK); return impl()->ExpressionFromIdentifier(name, beg_pos); } - case Token::STRING: { - BindingPatternUnexpectedToken(); - Consume(Token::STRING); - return impl()->ExpressionFromString(beg_pos); - } - case Token::ASSIGN_DIV: case Token::DIV: classifier()->RecordBindingPatternError( @@ -1931,8 +1939,8 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParsePrimaryExpression( 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 + // Parentheses are not valid on the LHS of a BindingPattern, so we use + // the is_valid_binding_pattern() check to detect multiple levels of // parenthesization. bool pattern_error = !classifier()->is_valid_binding_pattern(); classifier()->RecordPatternError(scanner()->peek_location(), @@ -1955,7 +1963,7 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParsePrimaryExpression( function_state_->set_next_function_is_likely_called(); } ExpressionT expr = ParseExpressionCoverGrammar(true, CHECK_OK); - Expect(Token::RPAREN, CHECK_OK); + Expect(Token::RPAREN, ok); return expr; } @@ -2010,10 +2018,10 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParsePrimaryExpression( template <typename Impl> typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseExpression( - bool accept_IN, bool* ok) { + bool* ok) { ExpressionClassifier classifier(this); - ExpressionT result = ParseExpressionCoverGrammar(accept_IN, CHECK_OK); - ValidateExpression(CHECK_OK); + ExpressionT result = ParseExpressionCoverGrammar(true, CHECK_OK); + ValidateExpression(ok); return result; } @@ -2038,13 +2046,12 @@ ParserBase<Impl>::ParseExpressionCoverGrammar(bool accept_IN, bool* ok) { Token::String(Token::ELLIPSIS)); int ellipsis_pos = position(); int pattern_pos = peek_position(); - ExpressionT pattern = ParsePrimaryExpression(CHECK_OK); + ExpressionT pattern = ParseBindingPattern(CHECK_OK); if (peek() == Token::ASSIGN) { ReportMessage(MessageTemplate::kRestDefaultInitializer); *ok = false; return result; } - ValidateBindingPattern(CHECK_OK); right = factory()->NewSpread(pattern, ellipsis_pos, pattern_pos); } else { right = ParseAssignmentExpression(accept_IN, CHECK_OK); @@ -2097,14 +2104,13 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseArrayLiteral( int pos = peek_position(); ExpressionListT values = impl()->NewExpressionList(4); int first_spread_index = -1; - Expect(Token::LBRACK, CHECK_OK); - while (peek() != Token::RBRACK) { + Consume(Token::LBRACK); + while (!Check(Token::RBRACK)) { ExpressionT elem; if (peek() == Token::COMMA) { elem = factory()->NewTheHoleLiteral(); - } else if (peek() == Token::ELLIPSIS) { - int start_pos = peek_position(); - Consume(Token::ELLIPSIS); + } else if (Check(Token::ELLIPSIS)) { + int start_pos = position(); int expr_pos = peek_position(); ExpressionT argument = ParseAssignmentExpression(true, CHECK_OK); elem = factory()->NewSpread(argument, start_pos, expr_pos); @@ -2115,57 +2121,54 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseArrayLiteral( if (argument->IsAssignment()) { classifier()->RecordPatternError( - Scanner::Location(start_pos, scanner()->location().end_pos), + Scanner::Location(start_pos, end_position()), MessageTemplate::kInvalidDestructuringTarget); } else { - CheckDestructuringElement(argument, start_pos, - scanner()->location().end_pos); + CheckDestructuringElement(argument, start_pos, end_position()); } if (peek() == Token::COMMA) { classifier()->RecordPatternError( - Scanner::Location(start_pos, scanner()->location().end_pos), + Scanner::Location(start_pos, end_position()), MessageTemplate::kElementAfterRest); } } else { int beg_pos = peek_position(); elem = ParseAssignmentExpression(true, CHECK_OK); - CheckDestructuringElement(elem, beg_pos, scanner()->location().end_pos); + CheckDestructuringElement(elem, beg_pos, end_position()); } values->Add(elem, zone_); if (peek() != Token::RBRACK) { Expect(Token::COMMA, CHECK_OK); } } - Expect(Token::RBRACK, CHECK_OK); return factory()->NewArrayLiteral(values, first_spread_index, pos); } -template <class Impl> -bool ParserBase<Impl>::SetPropertyKindFromToken(Token::Value token, - PropertyKind* kind) { +inline bool ParsePropertyKindFromToken(Token::Value token, + ParsePropertyKind* kind) { // This returns true, setting the property kind, iff the given token is one // which must occur after a property name, indicating that the previous token // was in fact a name and not a modifier (like the "get" in "get x"). switch (token) { case Token::COLON: - *kind = PropertyKind::kValueProperty; + *kind = ParsePropertyKind::kValue; return true; case Token::COMMA: case Token::RBRACE: case Token::ASSIGN: - *kind = PropertyKind::kShorthandProperty; + *kind = ParsePropertyKind::kShorthand; return true; case Token::LPAREN: - *kind = PropertyKind::kMethodProperty; + *kind = ParsePropertyKind::kMethod; return true; case Token::MUL: case Token::SEMICOLON: - *kind = PropertyKind::kClassField; + *kind = ParsePropertyKind::kClassField; return true; case Token::PRIVATE_NAME: - *kind = PropertyKind::kClassField; + *kind = ParsePropertyKind::kClassField; return true; default: break; @@ -2173,57 +2176,55 @@ bool ParserBase<Impl>::SetPropertyKindFromToken(Token::Value token, return false; } -template <class Impl> -typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParsePropertyName( - IdentifierT* name, PropertyKind* kind, bool* is_generator, bool* is_get, - bool* is_set, bool* is_async, bool* is_computed_name, bool* ok) { - DCHECK_EQ(*kind, PropertyKind::kNotSet); - DCHECK(!*is_generator); - DCHECK(!*is_get); - DCHECK(!*is_set); - DCHECK(!*is_async); - DCHECK(!*is_computed_name); +inline bool ParseAsAccessor(Token::Value token, Token::Value contextual_token, + ParsePropertyKind* kind) { + if (ParsePropertyKindFromToken(token, kind)) return false; - *is_generator = Check(Token::MUL); - if (*is_generator) { - *kind = PropertyKind::kMethodProperty; + if (contextual_token == Token::GET) { + *kind = ParsePropertyKind::kAccessorGetter; + } else if (contextual_token == Token::SET) { + *kind = ParsePropertyKind::kAccessorSetter; + } else { + return false; } - Token::Value token = peek(); - int pos = peek_position(); + return true; +} - if (!*is_generator && token == Token::ASYNC && - !scanner()->HasLineTerminatorAfterNext()) { - Consume(Token::ASYNC); - token = peek(); - if (token == Token::MUL && !scanner()->HasLineTerminatorBeforeNext()) { - Consume(Token::MUL); - token = peek(); - *is_generator = true; - } else if (SetPropertyKindFromToken(token, kind)) { - *name = impl()->GetSymbol(); // TODO(bakkot) specialize on 'async' - impl()->PushLiteralName(*name); - return factory()->NewStringLiteral(*name, pos); - } - *kind = PropertyKind::kMethodProperty; - *is_async = true; - pos = peek_position(); - } +template <class Impl> +typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParsePropertyName( + IdentifierT* name, ParsePropertyKind* kind, ParseFunctionFlags* flags, + bool* is_computed_name, bool* ok) { + DCHECK_EQ(ParsePropertyKind::kNotSet, *kind); + DCHECK_EQ(*flags, ParseFunctionFlag::kIsNormal); + DCHECK(!*is_computed_name); - if (token == Token::IDENTIFIER && !*is_generator && !*is_async) { - // This is checking for 'get' and 'set' in particular. - Consume(Token::IDENTIFIER); - token = peek(); - if (SetPropertyKindFromToken(token, kind) || - !scanner()->IsGetOrSet(is_get, is_set)) { + if (Check(Token::ASYNC)) { + Token::Value token = peek(); + if ((token != Token::MUL && ParsePropertyKindFromToken(token, kind)) || + scanner()->HasLineTerminatorBeforeNext()) { *name = impl()->GetSymbol(); impl()->PushLiteralName(*name); - return factory()->NewStringLiteral(*name, pos); + return factory()->NewStringLiteral(*name, position()); } - *kind = PropertyKind::kAccessorProperty; - pos = peek_position(); + *flags = ParseFunctionFlag::kIsAsync; + *kind = ParsePropertyKind::kMethod; + } + + if (Check(Token::MUL)) { + *flags |= ParseFunctionFlag::kIsGenerator; + *kind = ParsePropertyKind::kMethod; } + if (*kind == ParsePropertyKind::kNotSet && Check(Token::IDENTIFIER) && + !ParseAsAccessor(peek(), scanner()->current_contextual_token(), kind)) { + *name = impl()->GetSymbol(); + impl()->PushLiteralName(*name); + return factory()->NewStringLiteral(*name, position()); + } + + int pos = peek_position(); + // For non computed property names we normalize the name a bit: // // "12" -> 12 @@ -2233,41 +2234,50 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParsePropertyName( // // This is important because we use the property name as a key in a hash // table when we compute constant properties. - ExpressionT expression = impl()->NullExpression(); - switch (token) { + bool is_array_index; + uint32_t index; + switch (peek()) { case Token::STRING: Consume(Token::STRING); *name = impl()->GetSymbol(); + is_array_index = impl()->IsArrayIndex(*name, &index); break; case Token::SMI: Consume(Token::SMI); - *name = impl()->GetNumberAsSymbol(); + index = scanner()->smi_value(); + is_array_index = true; + // Token::SMI were scanned from their canonical representation. + *name = impl()->GetSymbol(); break; - case Token::NUMBER: + case Token::NUMBER: { Consume(Token::NUMBER); *name = impl()->GetNumberAsSymbol(); + is_array_index = impl()->IsArrayIndex(*name, &index); break; - + } case Token::LBRACK: { *name = impl()->NullIdentifier(); *is_computed_name = true; Consume(Token::LBRACK); ExpressionClassifier computed_name_classifier(this); - expression = ParseAssignmentExpression(true, CHECK_OK); + ExpressionT expression = ParseAssignmentExpression(true, CHECK_OK); ValidateExpression(CHECK_OK); AccumulateFormalParameterContainmentErrors(); Expect(Token::RBRACK, CHECK_OK); - break; + if (*kind == ParsePropertyKind::kNotSet) { + ParsePropertyKindFromToken(peek(), kind); + } + return expression; } case Token::ELLIPSIS: - if (!*is_generator && !*is_async && !*is_get && !*is_set) { + if (*kind == ParsePropertyKind::kNotSet) { *name = impl()->NullIdentifier(); Consume(Token::ELLIPSIS); - expression = ParseAssignmentExpression(true, CHECK_OK); - *kind = PropertyKind::kSpreadProperty; + ExpressionT expression = ParseAssignmentExpression(true, CHECK_OK); + *kind = ParsePropertyKind::kSpread; if (!impl()->IsIdentifier(expression)) { classifier()->RecordBindingPatternError( @@ -2291,23 +2301,16 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParsePropertyName( default: *name = ParseIdentifierName(CHECK_OK); + is_array_index = false; break; } - if (*kind == PropertyKind::kNotSet) { - SetPropertyKindFromToken(peek(), kind); + if (*kind == ParsePropertyKind::kNotSet) { + ParsePropertyKindFromToken(peek(), kind); } - - if (*is_computed_name) { - return expression; - } - impl()->PushLiteralName(*name); - - uint32_t index; - return impl()->IsArrayIndex(*name, &index) - ? factory()->NewNumberLiteral(index, pos) - : factory()->NewStringLiteral(*name, pos); + return is_array_index ? factory()->NewNumberLiteral(index, pos) + : factory()->NewStringLiteral(*name, pos); } template <typename Impl> @@ -2317,26 +2320,24 @@ ParserBase<Impl>::ParseClassPropertyDefinition( bool has_extends, bool* is_computed_name, ClassLiteralProperty::Kind* property_kind, bool* is_static, bool* ok) { DCHECK_NOT_NULL(class_info); - bool is_get = false; - bool is_set = false; - bool is_generator = false; - bool is_async = false; + ParseFunctionFlags function_flags = ParseFunctionFlag::kIsNormal; *is_static = false; *property_kind = ClassLiteralProperty::METHOD; - PropertyKind kind = PropertyKind::kNotSet; + ParsePropertyKind kind = ParsePropertyKind::kNotSet; Token::Value name_token = peek(); DCHECK_IMPLIES(name_token == Token::PRIVATE_NAME, allow_harmony_private_fields()); - int name_token_position = scanner()->peek_location().beg_pos; + int property_beg_pos = scanner()->peek_location().beg_pos; + int name_token_position = property_beg_pos; *name = impl()->NullIdentifier(); ExpressionT name_expression; if (name_token == Token::STATIC) { Consume(Token::STATIC); name_token_position = scanner()->peek_location().beg_pos; if (peek() == Token::LPAREN) { - kind = PropertyKind::kMethodProperty; + kind = ParsePropertyKind::kMethod; *name = impl()->GetSymbol(); // TODO(bakkot) specialize on 'static' name_expression = factory()->NewStringLiteral(*name, position()); } else if (peek() == Token::ASSIGN || peek() == Token::SEMICOLON || @@ -2351,18 +2352,18 @@ ParserBase<Impl>::ParseClassPropertyDefinition( return impl()->NullLiteralProperty(); } else { *is_static = true; - name_expression = ParsePropertyName(name, &kind, &is_generator, &is_get, - &is_set, &is_async, is_computed_name, - CHECK_OK_CUSTOM(NullLiteralProperty)); + name_expression = + ParsePropertyName(name, &kind, &function_flags, is_computed_name, + CHECK_OK_CUSTOM(NullLiteralProperty)); } } else if (name_token == Token::PRIVATE_NAME) { Consume(Token::PRIVATE_NAME); *name = impl()->GetSymbol(); name_expression = factory()->NewStringLiteral(*name, position()); } else { - name_expression = ParsePropertyName(name, &kind, &is_generator, &is_get, - &is_set, &is_async, is_computed_name, - CHECK_OK_CUSTOM(NullLiteralProperty)); + name_expression = + ParsePropertyName(name, &kind, &function_flags, is_computed_name, + CHECK_OK_CUSTOM(NullLiteralProperty)); } if (!class_info->has_name_static_property && *is_static && @@ -2371,16 +2372,17 @@ ParserBase<Impl>::ParseClassPropertyDefinition( } switch (kind) { - case PropertyKind::kClassField: - case PropertyKind::kNotSet: // This case is a name followed by a name or - // other property. Here we have to assume - // that's an uninitialized field followed by a - // linebreak followed by a property, with ASI - // adding the semicolon. If not, there will be - // a syntax error after parsing the first name - // as an uninitialized field. - case PropertyKind::kShorthandProperty: - case PropertyKind::kValueProperty: + case ParsePropertyKind::kClassField: + case ParsePropertyKind::kNotSet: // This case is a name followed by a name + // or other property. Here we have to + // assume that's an uninitialized field + // followed by a linebreak followed by a + // property, with ASI adding the + // semicolon. If not, there will be a + // syntax error after parsing the first + // name as an uninitialized field. + case ParsePropertyKind::kShorthand: + case ParsePropertyKind::kValue: if (allow_harmony_public_fields() || allow_harmony_private_fields()) { *property_kind = name_token == Token::PRIVATE_NAME ? ClassLiteralProperty::PRIVATE_FIELD @@ -2394,8 +2396,9 @@ ParserBase<Impl>::ParseClassPropertyDefinition( checker->CheckClassFieldName(*is_static, CHECK_OK_CUSTOM(NullLiteralProperty)); } - ExpressionT initializer = ParseClassFieldInitializer( - class_info, *is_static, CHECK_OK_CUSTOM(NullLiteralProperty)); + ExpressionT initializer = + ParseClassFieldInitializer(class_info, property_beg_pos, *is_static, + CHECK_OK_CUSTOM(NullLiteralProperty)); ExpectSemicolon(CHECK_OK_CUSTOM(NullLiteralProperty)); ClassLiteralPropertyT result = factory()->NewClassLiteralProperty( name_expression, initializer, *property_kind, *is_static, @@ -2409,9 +2412,7 @@ ParserBase<Impl>::ParseClassPropertyDefinition( return impl()->NullLiteralProperty(); } - case PropertyKind::kMethodProperty: { - DCHECK(!is_get && !is_set); - + case ParsePropertyKind::kMethod: { // MethodDefinition // PropertyName '(' StrictFormalParameters ')' '{' FunctionBody '}' // '*' PropertyName '(' StrictFormalParameters ')' '{' FunctionBody '}' @@ -2421,12 +2422,12 @@ ParserBase<Impl>::ParseClassPropertyDefinition( // '{' FunctionBody '}' if (!*is_computed_name) { - checker->CheckClassMethodName(name_token, PropertyKind::kMethodProperty, - is_generator, is_async, *is_static, + checker->CheckClassMethodName(name_token, ParsePropertyKind::kMethod, + function_flags, *is_static, CHECK_OK_CUSTOM(NullLiteralProperty)); } - FunctionKind kind = MethodKindFor(is_generator, is_async); + FunctionKind kind = MethodKindFor(function_flags); if (!*is_static && impl()->IsConstructor(*name)) { class_info->has_seen_constructor = true; @@ -2436,10 +2437,8 @@ ParserBase<Impl>::ParseClassPropertyDefinition( ExpressionT value = impl()->ParseFunctionLiteral( *name, scanner()->location(), kSkipFunctionNameCheck, kind, - FLAG_harmony_function_tostring ? name_token_position - : kNoSourcePosition, - FunctionLiteral::kAccessorOrMethod, language_mode(), nullptr, - CHECK_OK_CUSTOM(NullLiteralProperty)); + name_token_position, FunctionLiteral::kAccessorOrMethod, + language_mode(), nullptr, CHECK_OK_CUSTOM(NullLiteralProperty)); *property_kind = ClassLiteralProperty::METHOD; ClassLiteralPropertyT result = factory()->NewClassLiteralProperty( @@ -2449,13 +2448,15 @@ ParserBase<Impl>::ParseClassPropertyDefinition( return result; } - case PropertyKind::kAccessorProperty: { - DCHECK((is_get || is_set) && !is_generator && !is_async); + case ParsePropertyKind::kAccessorGetter: + case ParsePropertyKind::kAccessorSetter: { + DCHECK_EQ(function_flags, ParseFunctionFlag::kIsNormal); + bool is_get = kind == ParsePropertyKind::kAccessorGetter; if (!*is_computed_name) { - checker->CheckClassMethodName( - name_token, PropertyKind::kAccessorProperty, false, false, - *is_static, CHECK_OK_CUSTOM(NullLiteralProperty)); + checker->CheckClassMethodName(name_token, kind, + ParseFunctionFlag::kIsNormal, *is_static, + CHECK_OK_CUSTOM(NullLiteralProperty)); // Make sure the name expression is a string since we need a Name for // Runtime_DefineAccessorPropertyUnchecked and since we can determine // this statically we can skip the extra runtime check. @@ -2468,10 +2469,8 @@ ParserBase<Impl>::ParseClassPropertyDefinition( FunctionLiteralT value = impl()->ParseFunctionLiteral( *name, scanner()->location(), kSkipFunctionNameCheck, kind, - FLAG_harmony_function_tostring ? name_token_position - : kNoSourcePosition, - FunctionLiteral::kAccessorOrMethod, language_mode(), nullptr, - CHECK_OK_CUSTOM(NullLiteralProperty)); + name_token_position, FunctionLiteral::kAccessorOrMethod, + language_mode(), nullptr, CHECK_OK_CUSTOM(NullLiteralProperty)); *property_kind = is_get ? ClassLiteralProperty::GETTER : ClassLiteralProperty::SETTER; @@ -2484,7 +2483,7 @@ ParserBase<Impl>::ParseClassPropertyDefinition( impl()->SetFunctionNameFromPropertyName(result, *name, prefix); return result; } - case PropertyKind::kSpreadProperty: + case ParsePropertyKind::kSpread: ReportUnexpectedTokenAt( Scanner::Location(name_token_position, name_expression->position()), name_token); @@ -2496,7 +2495,7 @@ ParserBase<Impl>::ParseClassPropertyDefinition( template <typename Impl> typename ParserBase<Impl>::ExpressionT -ParserBase<Impl>::ParseClassFieldInitializer(ClassInfo* class_info, +ParserBase<Impl>::ParseClassFieldInitializer(ClassInfo* class_info, int beg_pos, bool is_static, bool* ok) { DeclarationScope* initializer_scope = is_static ? class_info->static_fields_scope @@ -2506,7 +2505,7 @@ ParserBase<Impl>::ParseClassFieldInitializer(ClassInfo* class_info, initializer_scope = NewFunctionScope(FunctionKind::kClassFieldsInitializerFunction); // TODO(gsathya): Make scopes be non contiguous. - initializer_scope->set_start_position(scanner()->location().end_pos); + initializer_scope->set_start_position(beg_pos); initializer_scope->SetLanguageMode(LanguageMode::kStrict); } @@ -2523,7 +2522,7 @@ ParserBase<Impl>::ParseClassFieldInitializer(ClassInfo* class_info, initializer = factory()->NewUndefinedLiteral(kNoSourcePosition); } - initializer_scope->set_end_position(scanner()->location().end_pos); + initializer_scope->set_end_position(end_position()); if (is_static) { class_info->static_fields_scope = initializer_scope; class_info->has_static_class_fields = true; @@ -2541,26 +2540,23 @@ ParserBase<Impl>::ParseObjectPropertyDefinition(ObjectLiteralChecker* checker, bool* is_computed_name, bool* is_rest_property, bool* ok) { - bool is_get = false; - bool is_set = false; - bool is_generator = false; - bool is_async = false; - PropertyKind kind = PropertyKind::kNotSet; + ParseFunctionFlags function_flags = ParseFunctionFlag::kIsNormal; + ParsePropertyKind kind = ParsePropertyKind::kNotSet; IdentifierT name = impl()->NullIdentifier(); Token::Value name_token = peek(); - int next_beg_pos = scanner()->peek_location().beg_pos; - int next_end_pos = scanner()->peek_location().end_pos; + int next_beg_pos = peek_position(); + int next_end_pos = peek_end_position(); - ExpressionT name_expression = ParsePropertyName( - &name, &kind, &is_generator, &is_get, &is_set, &is_async, - is_computed_name, CHECK_OK_CUSTOM(NullLiteralProperty)); + ExpressionT name_expression = + ParsePropertyName(&name, &kind, &function_flags, is_computed_name, + CHECK_OK_CUSTOM(NullLiteralProperty)); switch (kind) { - case PropertyKind::kSpreadProperty: - DCHECK(!is_get && !is_set && !is_generator && !is_async && - !*is_computed_name); - DCHECK(name_token == Token::ELLIPSIS); + case ParsePropertyKind::kSpread: + DCHECK_EQ(function_flags, ParseFunctionFlag::kIsNormal); + DCHECK(!*is_computed_name); + DCHECK_EQ(Token::ELLIPSIS, name_token); *is_computed_name = true; *is_rest_property = true; @@ -2569,8 +2565,8 @@ ParserBase<Impl>::ParseObjectPropertyDefinition(ObjectLiteralChecker* checker, factory()->NewTheHoleLiteral(), name_expression, ObjectLiteralProperty::SPREAD, true); - case PropertyKind::kValueProperty: { - DCHECK(!is_get && !is_set && !is_generator && !is_async); + case ParsePropertyKind::kValue: { + DCHECK_EQ(function_flags, ParseFunctionFlag::kIsNormal); if (!*is_computed_name) { checker->CheckDuplicateProto(name_token); @@ -2579,7 +2575,7 @@ ParserBase<Impl>::ParseObjectPropertyDefinition(ObjectLiteralChecker* checker, int beg_pos = peek_position(); ExpressionT value = ParseAssignmentExpression(true, CHECK_OK_CUSTOM(NullLiteralProperty)); - CheckDestructuringElement(value, beg_pos, scanner()->location().end_pos); + CheckDestructuringElement(value, beg_pos, end_position()); ObjectLiteralPropertyT result = factory()->NewObjectLiteralProperty( name_expression, value, *is_computed_name); @@ -2587,14 +2583,14 @@ ParserBase<Impl>::ParseObjectPropertyDefinition(ObjectLiteralChecker* checker, return result; } - case PropertyKind::kShorthandProperty: { + case ParsePropertyKind::kShorthand: { // PropertyDefinition // IdentifierReference // CoverInitializedName // // CoverInitializedName // IdentifierReference Initializer? - DCHECK(!is_get && !is_set && !is_generator && !is_async); + DCHECK_EQ(function_flags, ParseFunctionFlag::kIsNormal); if (!Token::IsIdentifier(name_token, language_mode(), this->is_generator(), @@ -2642,7 +2638,7 @@ ParserBase<Impl>::ParseObjectPropertyDefinition(ObjectLiteralChecker* checker, value = factory()->NewAssignment(Token::ASSIGN, lhs, rhs, kNoSourcePosition); classifier()->RecordExpressionError( - Scanner::Location(next_beg_pos, scanner()->location().end_pos), + Scanner::Location(next_beg_pos, end_position()), MessageTemplate::kInvalidCoverInitializedName); impl()->SetFunctionNameFromIdentifierRef(rhs, lhs); @@ -2656,24 +2652,21 @@ ParserBase<Impl>::ParseObjectPropertyDefinition(ObjectLiteralChecker* checker, return result; } - case PropertyKind::kMethodProperty: { - DCHECK(!is_get && !is_set); - + case ParsePropertyKind::kMethod: { // MethodDefinition // PropertyName '(' StrictFormalParameters ')' '{' FunctionBody '}' // '*' PropertyName '(' StrictFormalParameters ')' '{' FunctionBody '}' classifier()->RecordPatternError( - Scanner::Location(next_beg_pos, scanner()->location().end_pos), + Scanner::Location(next_beg_pos, end_position()), MessageTemplate::kInvalidDestructuringTarget); - FunctionKind kind = MethodKindFor(is_generator, is_async); + FunctionKind kind = MethodKindFor(function_flags); ExpressionT value = impl()->ParseFunctionLiteral( name, scanner()->location(), kSkipFunctionNameCheck, kind, - FLAG_harmony_function_tostring ? next_beg_pos : kNoSourcePosition, - FunctionLiteral::kAccessorOrMethod, language_mode(), nullptr, - CHECK_OK_CUSTOM(NullLiteralProperty)); + next_beg_pos, FunctionLiteral::kAccessorOrMethod, language_mode(), + nullptr, CHECK_OK_CUSTOM(NullLiteralProperty)); ObjectLiteralPropertyT result = factory()->NewObjectLiteralProperty( name_expression, value, ObjectLiteralProperty::COMPUTED, @@ -2682,12 +2675,13 @@ ParserBase<Impl>::ParseObjectPropertyDefinition(ObjectLiteralChecker* checker, return result; } - case PropertyKind::kAccessorProperty: { - DCHECK((is_get || is_set) && !(is_set && is_get) && !is_generator && - !is_async); + case ParsePropertyKind::kAccessorGetter: + case ParsePropertyKind::kAccessorSetter: { + DCHECK_EQ(function_flags, ParseFunctionFlag::kIsNormal); + bool is_get = kind == ParsePropertyKind::kAccessorGetter; classifier()->RecordPatternError( - Scanner::Location(next_beg_pos, scanner()->location().end_pos), + Scanner::Location(next_beg_pos, end_position()), MessageTemplate::kInvalidDestructuringTarget); if (!*is_computed_name) { @@ -2703,9 +2697,8 @@ ParserBase<Impl>::ParseObjectPropertyDefinition(ObjectLiteralChecker* checker, FunctionLiteralT value = impl()->ParseFunctionLiteral( name, scanner()->location(), kSkipFunctionNameCheck, kind, - FLAG_harmony_function_tostring ? next_beg_pos : kNoSourcePosition, - FunctionLiteral::kAccessorOrMethod, language_mode(), nullptr, - CHECK_OK_CUSTOM(NullLiteralProperty)); + next_beg_pos, FunctionLiteral::kAccessorOrMethod, language_mode(), + nullptr, CHECK_OK_CUSTOM(NullLiteralProperty)); ObjectLiteralPropertyT result = factory()->NewObjectLiteralProperty( name_expression, value, @@ -2719,8 +2712,8 @@ ParserBase<Impl>::ParseObjectPropertyDefinition(ObjectLiteralChecker* checker, return result; } - case PropertyKind::kClassField: - case PropertyKind::kNotSet: + case ParsePropertyKind::kClassField: + case ParsePropertyKind::kNotSet: ReportUnexpectedToken(Next()); *ok = false; return impl()->NullLiteralProperty(); @@ -2743,10 +2736,10 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseObjectLiteral( bool has_rest_property = false; ObjectLiteralChecker checker(this); - Expect(Token::LBRACE, CHECK_OK); + Consume(Token::LBRACE); - while (peek() != Token::RBRACE) { - FuncNameInferrer::State fni_state(fni_); + while (!Check(Token::RBRACE)) { + FuncNameInferrerState fni_state(&fni_); bool is_computed_name = false; bool is_rest_property = false; @@ -2774,9 +2767,8 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseObjectLiteral( Expect(Token::COMMA, CHECK_OK); } - if (fni_ != nullptr) fni_->Infer(); + fni_.Infer(); } - Expect(Token::RBRACE, CHECK_OK); // In pattern rewriter, we rewrite rest property to call out to a // runtime function passing all the other properties as arguments to @@ -2802,25 +2794,20 @@ typename ParserBase<Impl>::ExpressionListT ParserBase<Impl>::ParseArguments( Scanner::Location spread_arg = Scanner::Location::invalid(); ExpressionListT result = impl()->NewExpressionList(4); Expect(Token::LPAREN, CHECK_OK_CUSTOM(NullExpressionList)); - bool done = (peek() == Token::RPAREN); - while (!done) { + while (peek() != Token::RPAREN) { int start_pos = peek_position(); bool is_spread = Check(Token::ELLIPSIS); int expr_pos = peek_position(); ExpressionT argument = ParseAssignmentExpression(true, CHECK_OK_CUSTOM(NullExpressionList)); - if (!impl()->IsIdentifier(argument) && - is_simple_parameter_list != nullptr) { - *is_simple_parameter_list = false; - } + if (!impl()->IsIdentifier(argument)) *is_simple_parameter_list = false; + if (!maybe_arrow) { ValidateExpression(CHECK_OK_CUSTOM(NullExpressionList)); } if (is_spread) { - if (is_simple_parameter_list != nullptr) { - *is_simple_parameter_list = false; - } + *is_simple_parameter_list = false; if (!spread_arg.IsValid()) { spread_arg.beg_pos = start_pos; spread_arg.end_pos = peek_position(); @@ -2833,24 +2820,22 @@ typename ParserBase<Impl>::ExpressionListT ParserBase<Impl>::ParseArguments( } result->Add(argument, zone_); - if (result->length() > Code::kMaxArguments) { - ReportMessage(MessageTemplate::kTooManyArguments); - *ok = false; - return impl()->NullExpressionList(); - } - done = (peek() != Token::COMMA); - if (!done) { - Next(); - if (argument->IsSpread()) { - classifier()->RecordAsyncArrowFormalParametersError( - scanner()->location(), MessageTemplate::kParamAfterRest); - } - if (peek() == Token::RPAREN) { - // allow trailing comma - done = true; - } + if (peek() != Token::COMMA) break; + + Next(); + + if (argument->IsSpread()) { + classifier()->RecordAsyncArrowFormalParametersError( + scanner()->location(), MessageTemplate::kParamAfterRest); } } + + if (result->length() > Code::kMaxArguments) { + ReportMessage(MessageTemplate::kTooManyArguments); + *ok = false; + return impl()->NullExpressionList(); + } + Scanner::Location location = scanner_->location(); if (Token::RPAREN != Next()) { impl()->ReportMessageAt(location, MessageTemplate::kUnterminatedArgList); @@ -2883,7 +2868,7 @@ ParserBase<Impl>::ParseAssignmentExpression(bool accept_IN, bool* ok) { return ParseYieldExpression(accept_IN, ok); } - FuncNameInferrer::State fni_state(fni_); + FuncNameInferrerState fni_state(&fni_); ExpressionClassifier arrow_formals_classifier( this, classifier()->duplicate_finder()); @@ -2915,10 +2900,8 @@ ParserBase<Impl>::ParseAssignmentExpression(bool accept_IN, bool* ok) { IdentifierT name = ParseAndClassifyIdentifier(CHECK_OK); expression = impl()->ExpressionFromIdentifier(name, position(), InferName::kNo); - if (fni_) { - // Remove `async` keyword from inferred name stack. - fni_->RemoveAsyncKeywordFromEnd(); - } + // Remove `async` keyword from inferred name stack. + fni_.RemoveAsyncKeywordFromEnd(); } if (peek() == Token::ARROW) { @@ -2931,9 +2914,9 @@ ParserBase<Impl>::ParseAssignmentExpression(bool accept_IN, bool* ok) { // in an arrow parameter list, this is correct. // TODO(adamk): Rename "FormalParameterInitializerError" to refer to // "YieldExpression", which is its only use. - ValidateFormalParameterInitializer(ok); + ValidateFormalParameterInitializer(CHECK_OK); - Scanner::Location loc(lhs_beg_pos, scanner()->location().end_pos); + Scanner::Location loc(lhs_beg_pos, end_position()); DeclarationScope* scope = NewFunctionScope(is_async ? FunctionKind::kAsyncArrowFunction : FunctionKind::kArrowFunction); @@ -2962,7 +2945,7 @@ ParserBase<Impl>::ParseAssignmentExpression(bool accept_IN, bool* ok) { MessageTemplate::kUnexpectedToken, Token::String(Token::ARROW)); - if (fni_ != nullptr) fni_->Infer(); + fni_.Infer(); return expression; } @@ -2997,7 +2980,7 @@ ParserBase<Impl>::ParseAssignmentExpression(bool accept_IN, bool* ok) { ValidateAssignmentPattern(CHECK_OK); } else { expression = CheckAndRewriteReferenceExpression( - expression, lhs_beg_pos, scanner()->location().end_pos, + expression, lhs_beg_pos, end_position(), MessageTemplate::kInvalidLhsInAssignment, CHECK_OK); } @@ -3027,15 +3010,13 @@ ParserBase<Impl>::ParseAssignmentExpression(bool accept_IN, bool* ok) { impl()->CheckAssigningFunctionLiteralToProperty(expression, right); - if (fni_ != nullptr) { - // Check if the right hand side is a call to avoid inferring a - // name if we're dealing with "a = function(){...}();"-like - // expression. - if (op == Token::ASSIGN && !right->IsCall() && !right->IsCallNew()) { - fni_->Infer(); - } else { - fni_->RemoveLastFunction(); - } + // Check if the right hand side is a call to avoid inferring a + // name if we're dealing with "a = function(){...}();"-like + // expression. + if (op == Token::ASSIGN && !right->IsCall() && !right->IsCallNew()) { + fni_.Infer(); + } else { + fni_.RemoveLastFunction(); } if (op == Token::ASSIGN) { @@ -3124,11 +3105,20 @@ ParserBase<Impl>::ParseConditionalExpression(bool accept_IN, // LogicalOrExpression // LogicalOrExpression '?' AssignmentExpression ':' AssignmentExpression - SourceRange then_range, else_range; int pos = peek_position(); // We start using the binary expression parser for prec >= 4 only! ExpressionT expression = ParseBinaryExpression(4, accept_IN, CHECK_OK); - if (peek() != Token::CONDITIONAL) return expression; + return peek() == Token::CONDITIONAL + ? ParseConditionalContinuation(expression, accept_IN, pos, ok) + : expression; +} + +template <typename Impl> +typename ParserBase<Impl>::ExpressionT +ParserBase<Impl>::ParseConditionalContinuation(ExpressionT expression, + bool accept_IN, int pos, + bool* ok) { + SourceRange then_range, else_range; ValidateExpression(CHECK_OK); BindingPatternUnexpectedToken(); ArrowFormalParametersUnexpectedToken(); @@ -3184,10 +3174,6 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseBinaryExpression( right_range_scope.Finalize(); ValidateExpression(CHECK_OK); - if (impl()->ShortcutNumericLiteralBinaryExpression(&x, y, op, pos)) { - continue; - } - // For now we distinguish between comparisons and other binary // operations. (We could combine the two and get rid of this // code and AST node eventually.) @@ -3204,9 +3190,9 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseBinaryExpression( // The comparison was negated - add a NOT. x = factory()->NewUnaryOperation(Token::NOT, x, pos); } - } else if (impl()->CollapseNaryExpression(&x, y, op, pos, right_range)) { - continue; - } else { + } else if (!impl()->ShortcutNumericLiteralBinaryExpression(&x, y, op, + pos) && + !impl()->CollapseNaryExpression(&x, y, op, pos, right_range)) { // We have a "normal" binary operation. x = factory()->NewBinaryOperation(op, x, y, pos); if (op == Token::OR || op == Token::AND) { @@ -3219,97 +3205,109 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseBinaryExpression( } template <typename Impl> -typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseUnaryExpression( +typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseUnaryOpExpression( bool* ok) { - // UnaryExpression :: - // PostfixExpression - // 'delete' UnaryExpression - // 'void' UnaryExpression - // 'typeof' UnaryExpression - // '++' UnaryExpression - // '--' UnaryExpression - // '+' UnaryExpression - // '-' UnaryExpression - // '~' UnaryExpression - // '!' UnaryExpression - // [+Await] AwaitExpression[?Yield] - - Token::Value op = peek(); - if (Token::IsUnaryOp(op)) { - BindingPatternUnexpectedToken(); - ArrowFormalParametersUnexpectedToken(); - - op = Next(); - int pos = position(); + BindingPatternUnexpectedToken(); + ArrowFormalParametersUnexpectedToken(); - // Assume "! function ..." indicates the function is likely to be called. - if (op == Token::NOT && peek() == Token::FUNCTION) { - function_state_->set_next_function_is_likely_called(); - } + Token::Value op = Next(); + int pos = position(); - ExpressionT expression = ParseUnaryExpression(CHECK_OK); - ValidateExpression(CHECK_OK); + // Assume "! function ..." indicates the function is likely to be called. + if (op == Token::NOT && peek() == Token::FUNCTION) { + function_state_->set_next_function_is_likely_called(); + } - if (op == Token::DELETE) { - if (impl()->IsIdentifier(expression) && is_strict(language_mode())) { - // "delete identifier" is a syntax error in strict mode. - ReportMessage(MessageTemplate::kStrictDelete); - *ok = false; - return impl()->NullExpression(); - } + ExpressionT expression = ParseUnaryExpression(CHECK_OK); + ValidateExpression(CHECK_OK); - if (impl()->IsPropertyWithPrivateFieldKey(expression)) { - ReportMessage(MessageTemplate::kDeletePrivateField); - *ok = false; - return impl()->NullExpression(); - } + if (op == Token::DELETE) { + if (impl()->IsIdentifier(expression) && is_strict(language_mode())) { + // "delete identifier" is a syntax error in strict mode. + ReportMessage(MessageTemplate::kStrictDelete); + *ok = false; + return impl()->NullExpression(); } - if (peek() == Token::EXP) { - ReportUnexpectedToken(Next()); + if (impl()->IsPropertyWithPrivateFieldKey(expression)) { + ReportMessage(MessageTemplate::kDeletePrivateField); *ok = false; return impl()->NullExpression(); } + } - // Allow the parser's implementation to rewrite the expression. - return impl()->BuildUnaryExpression(expression, op, pos); - } else if (Token::IsCountOp(op)) { - BindingPatternUnexpectedToken(); - ArrowFormalParametersUnexpectedToken(); - op = Next(); - int beg_pos = peek_position(); - ExpressionT expression = ParseUnaryExpression(CHECK_OK); - expression = CheckAndRewriteReferenceExpression( - expression, beg_pos, scanner()->location().end_pos, - MessageTemplate::kInvalidLhsInPrefixOp, CHECK_OK); - impl()->MarkExpressionAsAssigned(expression); - ValidateExpression(CHECK_OK); + if (peek() == Token::EXP) { + ReportUnexpectedToken(Next()); + *ok = false; + return impl()->NullExpression(); + } - return factory()->NewCountOperation(op, - true /* prefix */, - expression, - position()); + // Allow the parser's implementation to rewrite the expression. + return impl()->BuildUnaryExpression(expression, op, pos); +} - } else if (is_async_function() && peek() == Token::AWAIT) { - classifier()->RecordFormalParameterInitializerError( - scanner()->peek_location(), - MessageTemplate::kAwaitExpressionFormalParameter); - int await_pos = peek_position(); - Consume(Token::AWAIT); +template <typename Impl> +typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParsePrefixExpression( + bool* ok) { + BindingPatternUnexpectedToken(); + ArrowFormalParametersUnexpectedToken(); + Token::Value op = Next(); + int beg_pos = peek_position(); + ExpressionT expression = ParseUnaryExpression(CHECK_OK); + expression = CheckAndRewriteReferenceExpression( + expression, beg_pos, end_position(), + MessageTemplate::kInvalidLhsInPrefixOp, CHECK_OK); + impl()->MarkExpressionAsAssigned(expression); + ValidateExpression(CHECK_OK); - ExpressionT value = ParseUnaryExpression(CHECK_OK); + return factory()->NewCountOperation(op, true /* prefix */, expression, + position()); +} - classifier()->RecordBindingPatternError( - Scanner::Location(await_pos, scanner()->location().end_pos), - MessageTemplate::kInvalidDestructuringTarget); +template <typename Impl> +typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseAwaitExpression( + bool* ok) { + classifier()->RecordFormalParameterInitializerError( + scanner()->peek_location(), + MessageTemplate::kAwaitExpressionFormalParameter); + int await_pos = peek_position(); + Consume(Token::AWAIT); - ExpressionT expr = factory()->NewAwait(value, await_pos); - function_state_->AddSuspend(); - impl()->RecordSuspendSourceRange(expr, PositionAfterSemicolon()); - return expr; - } else { - return ParsePostfixExpression(ok); + ExpressionT value = ParseUnaryExpression(CHECK_OK); + + classifier()->RecordBindingPatternError( + Scanner::Location(await_pos, end_position()), + MessageTemplate::kInvalidDestructuringTarget); + + ExpressionT expr = factory()->NewAwait(value, await_pos); + function_state_->AddSuspend(); + impl()->RecordSuspendSourceRange(expr, PositionAfterSemicolon()); + return expr; +} + +template <typename Impl> +typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseUnaryExpression( + bool* ok) { + // UnaryExpression :: + // PostfixExpression + // 'delete' UnaryExpression + // 'void' UnaryExpression + // 'typeof' UnaryExpression + // '++' UnaryExpression + // '--' UnaryExpression + // '+' UnaryExpression + // '-' UnaryExpression + // '~' UnaryExpression + // '!' UnaryExpression + // [+Await] AwaitExpression[?Yield] + + Token::Value op = peek(); + if (Token::IsUnaryOp(op)) return ParseUnaryOpExpression(ok); + if (Token::IsCountOp(op)) return ParsePrefixExpression(ok); + if (is_async_function() && op == Token::AWAIT) { + return ParseAwaitExpression(ok); } + return ParsePostfixExpression(ok); } template <typename Impl> @@ -3325,7 +3323,7 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParsePostfixExpression( ArrowFormalParametersUnexpectedToken(); expression = CheckAndRewriteReferenceExpression( - expression, lhs_beg_pos, scanner()->location().end_pos, + expression, lhs_beg_pos, end_position(), MessageTemplate::kInvalidLhsInPostfixOp, CHECK_OK); impl()->MarkExpressionAsAssigned(expression); ValidateExpression(CHECK_OK); @@ -3398,9 +3396,7 @@ ParserBase<Impl>::ParseLeftHandSideExpression(bool* ok) { args = ParseArguments(&spread_pos, true, &is_simple_parameter_list, CHECK_OK); if (peek() == Token::ARROW) { - if (fni_) { - fni_->RemoveAsyncKeywordFromEnd(); - } + fni_.RemoveAsyncKeywordFromEnd(); ValidateBindingPattern(CHECK_OK); ValidateFormalParameterInitializer(CHECK_OK); if (!classifier()->is_valid_async_arrow_formal_parameters()) { @@ -3443,7 +3439,7 @@ ParserBase<Impl>::ParseLeftHandSideExpression(bool* ok) { result = factory()->NewCall(result, args, pos, is_possibly_eval); } - if (fni_ != nullptr) fni_->RemoveLastFunction(); + fni_.RemoveLastFunction(); break; } @@ -3475,8 +3471,8 @@ ParserBase<Impl>::ParseLeftHandSideExpression(bool* ok) { template <typename Impl> typename ParserBase<Impl>::ExpressionT -ParserBase<Impl>::ParseMemberWithNewPrefixesExpression(bool* is_async, - bool* ok) { +ParserBase<Impl>::ParseMemberWithPresentNewPrefixesExpression(bool* is_async, + bool* ok) { // NewExpression :: // ('new')+ MemberExpression // @@ -3496,49 +3492,52 @@ ParserBase<Impl>::ParseMemberWithNewPrefixesExpression(bool* is_async, // 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 + BindingPatternUnexpectedToken(); + ArrowFormalParametersUnexpectedToken(); + Consume(Token::NEW); + int new_pos = position(); + ExpressionT result; + if (peek() == Token::SUPER) { + const bool is_new = true; + result = ParseSuperExpression(is_new, CHECK_OK); + } else if (allow_harmony_dynamic_import() && peek() == Token::IMPORT && + (!allow_harmony_import_meta() || PeekAhead() == Token::LPAREN)) { + impl()->ReportMessageAt(scanner()->peek_location(), + MessageTemplate::kImportCallNotNewExpression); + *ok = false; + return impl()->NullExpression(); + } else if (peek() == Token::PERIOD) { + *is_async = false; + result = ParseNewTargetExpression(CHECK_OK); + return ParseMemberExpressionContinuation(result, is_async, ok); + } else { + result = ParseMemberWithNewPrefixesExpression(is_async, CHECK_OK); + } + ValidateExpression(CHECK_OK); + if (peek() == Token::LPAREN) { + // NewExpression with arguments. + Scanner::Location spread_pos; + ExpressionListT args = ParseArguments(&spread_pos, CHECK_OK); - if (peek() == Token::NEW) { - BindingPatternUnexpectedToken(); - ArrowFormalParametersUnexpectedToken(); - Consume(Token::NEW); - int new_pos = position(); - ExpressionT result; - if (peek() == Token::SUPER) { - const bool is_new = true; - result = ParseSuperExpression(is_new, CHECK_OK); - } else if (allow_harmony_dynamic_import() && peek() == Token::IMPORT && - (!allow_harmony_import_meta() || PeekAhead() == Token::LPAREN)) { - impl()->ReportMessageAt(scanner()->peek_location(), - MessageTemplate::kImportCallNotNewExpression); - *ok = false; - return impl()->NullExpression(); - } else if (peek() == Token::PERIOD) { - *is_async = false; - result = ParseNewTargetExpression(CHECK_OK); - return ParseMemberExpressionContinuation(result, is_async, CHECK_OK); + if (spread_pos.IsValid()) { + result = impl()->SpreadCallNew(result, args, new_pos); } else { - result = ParseMemberWithNewPrefixesExpression(is_async, CHECK_OK); + result = factory()->NewCallNew(result, args, new_pos); } - ValidateExpression(CHECK_OK); - if (peek() == Token::LPAREN) { - // NewExpression with arguments. - Scanner::Location spread_pos; - ExpressionListT args = ParseArguments(&spread_pos, CHECK_OK); - - if (spread_pos.IsValid()) { - result = impl()->SpreadCallNew(result, args, new_pos); - } else { - result = factory()->NewCallNew(result, args, new_pos); - } - // The expression can still continue with . or [ after the arguments. - result = ParseMemberExpressionContinuation(result, is_async, CHECK_OK); - return result; - } - // NewExpression without arguments. - return factory()->NewCallNew(result, impl()->NewExpressionList(0), new_pos); + // The expression can still continue with . or [ after the arguments. + return ParseMemberExpressionContinuation(result, is_async, ok); } - // No 'new' or 'super' keyword. - return ParseMemberExpression(is_async, ok); + // NewExpression without arguments. + return factory()->NewCallNew(result, impl()->NewExpressionList(0), new_pos); +} + +template <typename Impl> +typename ParserBase<Impl>::ExpressionT +ParserBase<Impl>::ParseMemberWithNewPrefixesExpression(bool* is_async, + bool* ok) { + return peek() == Token::NEW + ? ParseMemberWithPresentNewPrefixesExpression(is_async, ok) + : ParseMemberExpression(is_async, ok); } template <typename Impl> @@ -3604,8 +3603,7 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseMemberExpression( result = ParsePrimaryExpression(is_async, CHECK_OK); } - result = ParseMemberExpressionContinuation(result, is_async, CHECK_OK); - return result; + return ParseMemberExpressionContinuation(result, is_async, ok); } template <typename Impl> @@ -3678,9 +3676,9 @@ void ParserBase<Impl>::ExpectMetaProperty(Token::Value property_name, Consume(Token::PERIOD); ExpectContextualKeyword(property_name, CHECK_OK_CUSTOM(Void)); if (scanner()->literal_contains_escapes()) { - impl()->ReportMessageAt( - Scanner::Location(pos, scanner()->location().end_pos), - MessageTemplate::kInvalidEscapedMetaProperty, full_name); + impl()->ReportMessageAt(Scanner::Location(pos, end_position()), + MessageTemplate::kInvalidEscapedMetaProperty, + full_name); *ok = false; } } @@ -3692,7 +3690,7 @@ ParserBase<Impl>::ParseNewTargetExpression(bool* ok) { ExpectMetaProperty(Token::TARGET, "new.target", pos, CHECK_OK); classifier()->RecordAssignmentPatternError( - Scanner::Location(pos, scanner()->location().end_pos), + Scanner::Location(pos, end_position()), MessageTemplate::kInvalidDestructuringTarget); if (!GetReceiverScope()->is_function_scope()) { @@ -3780,14 +3778,11 @@ void ParserBase<Impl>::ParseFormalParameter(FormalParametersT* parameters, // BindingElement[?Yield, ?GeneratorParameter] bool is_rest = parameters->has_rest; - FuncNameInferrer::State fni_state(fni_); - ExpressionT pattern = ParsePrimaryExpression(CHECK_OK_CUSTOM(Void)); - ValidateBindingPattern(CHECK_OK_CUSTOM(Void)); - + FuncNameInferrerState fni_state(&fni_); + ExpressionT pattern = ParseBindingPattern(CHECK_OK_CUSTOM(Void)); if (!impl()->IsIdentifier(pattern)) { parameters->is_simple = false; ValidateFormalParameterInitializer(CHECK_OK_CUSTOM(Void)); - classifier()->RecordNonSimpleParameter(); } ExpressionT initializer = impl()->NullExpression(); @@ -3808,8 +3803,8 @@ void ParserBase<Impl>::ParseFormalParameter(FormalParametersT* parameters, impl()->SetFunctionNameFromIdentifierRef(initializer, pattern); } - impl()->AddFormalParameter(parameters, pattern, initializer, - scanner()->location().end_pos, is_rest); + impl()->AddFormalParameter(parameters, pattern, initializer, end_position(), + is_rest); } template <typename Impl> @@ -3908,23 +3903,21 @@ typename ParserBase<Impl>::BlockT ParserBase<Impl>::ParseVariableDeclarations( int bindings_start = peek_position(); do { // Parse binding pattern. - FuncNameInferrer::State fni_state(fni_); + FuncNameInferrerState fni_state(&fni_); ExpressionT pattern = impl()->NullExpression(); int decl_pos = peek_position(); { ExpressionClassifier pattern_classifier(this); - pattern = ParsePrimaryExpression(CHECK_OK_CUSTOM(NullStatement)); + pattern = ParseBindingPattern(CHECK_OK_CUSTOM(NullStatement)); - ValidateBindingPattern(CHECK_OK_CUSTOM(NullStatement)); if (IsLexicalVariableMode(parsing_result->descriptor.mode)) { ValidateLetPattern(CHECK_OK_CUSTOM(NullStatement)); } } - Scanner::Location variable_loc = scanner()->location(); - bool single_name = impl()->IsIdentifier(pattern); + bool single_name = impl()->IsIdentifier(pattern); if (single_name) { impl()->PushVariableName(impl()->AsIdentifier(pattern)); } @@ -3939,32 +3932,32 @@ typename ParserBase<Impl>::BlockT ParserBase<Impl>::ParseVariableDeclarations( value = ParseAssignmentExpression(var_context != kForStatement, CHECK_OK_CUSTOM(NullStatement)); ValidateExpression(CHECK_OK_CUSTOM(NullStatement)); - variable_loc.end_pos = scanner()->location().end_pos; + variable_loc.end_pos = end_position(); if (!parsing_result->first_initializer_loc.IsValid()) { parsing_result->first_initializer_loc = variable_loc; } // Don't infer if it is "a = function(){...}();"-like expression. - if (single_name && fni_ != nullptr) { + if (single_name) { if (!value->IsCall() && !value->IsCallNew()) { - fni_->Infer(); + fni_.Infer(); } else { - fni_->RemoveLastFunction(); + fni_.RemoveLastFunction(); } } impl()->SetFunctionNameFromIdentifierRef(value, pattern); // End position of the initializer is after the assignment expression. - initializer_position = scanner()->location().end_pos; + initializer_position = end_position(); } else { if (var_context != kForStatement || !PeekInOrOf()) { // ES6 'const' and binding patterns require initializers. if (parsing_result->descriptor.mode == VariableMode::kConst || !impl()->IsIdentifier(pattern)) { impl()->ReportMessageAt( - Scanner::Location(decl_pos, scanner()->location().end_pos), + Scanner::Location(decl_pos, end_position()), MessageTemplate::kDeclarationMissingInitializer, !impl()->IsIdentifier(pattern) ? "destructuring" : "const"); *ok = false; @@ -3998,7 +3991,7 @@ typename ParserBase<Impl>::BlockT ParserBase<Impl>::ParseVariableDeclarations( } while (Check(Token::COMMA)); parsing_result->bindings_loc = - Scanner::Location(bindings_start, scanner()->location().end_pos); + Scanner::Location(bindings_start, end_position()); DCHECK(*ok); return init_block; @@ -4009,7 +4002,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseFunctionDeclaration(bool* ok) { Consume(Token::FUNCTION); int pos = position(); - ParseFunctionFlags flags = ParseFunctionFlags::kIsNormal; + ParseFunctionFlags flags = ParseFunctionFlag::kIsNormal; if (Check(Token::MUL)) { impl()->ReportMessageAt( scanner()->location(), @@ -4026,9 +4019,9 @@ ParserBase<Impl>::ParseHoistableDeclaration( ZonePtrList<const AstRawString>* names, bool default_export, bool* ok) { Expect(Token::FUNCTION, CHECK_OK_CUSTOM(NullStatement)); int pos = position(); - ParseFunctionFlags flags = ParseFunctionFlags::kIsNormal; + ParseFunctionFlags flags = ParseFunctionFlag::kIsNormal; if (Check(Token::MUL)) { - flags |= ParseFunctionFlags::kIsGenerator; + flags |= ParseFunctionFlag::kIsGenerator; } return ParseHoistableDeclaration(pos, flags, names, default_export, ok); } @@ -4049,13 +4042,12 @@ ParserBase<Impl>::ParseHoistableDeclaration( // // 'function' and '*' (if present) have been consumed by the caller. - bool is_generator = flags & ParseFunctionFlags::kIsGenerator; - const bool is_async = flags & ParseFunctionFlags::kIsAsync; - DCHECK(!is_generator || !is_async); + DCHECK_IMPLIES((flags & ParseFunctionFlag::kIsAsync) != 0, + (flags & ParseFunctionFlag::kIsGenerator) == 0); - if (is_async && Check(Token::MUL)) { + if ((flags & ParseFunctionFlag::kIsAsync) != 0 && Check(Token::MUL)) { // Async generator - is_generator = true; + flags |= ParseFunctionFlag::kIsGenerator; } IdentifierT name; @@ -4074,10 +4066,10 @@ ParserBase<Impl>::ParseHoistableDeclaration( variable_name = name; } - FuncNameInferrer::State fni_state(fni_); + FuncNameInferrerState fni_state(&fni_); impl()->PushEnclosingName(name); - FunctionKind kind = FunctionKindFor(is_generator, is_async); + FunctionKind kind = FunctionKindFor(flags); FunctionLiteralT function = impl()->ParseFunctionLiteral( name, scanner()->location(), name_validity, kind, pos, @@ -4097,7 +4089,7 @@ ParserBase<Impl>::ParseHoistableDeclaration( // a flag and UseCounting violations to assess web compatibility. bool is_sloppy_block_function = is_sloppy(language_mode()) && !scope()->is_declaration_scope() && - !is_async && !is_generator; + flags == ParseFunctionFlag::kIsNormal; return impl()->DeclareFunction(variable_name, function, mode, pos, is_sloppy_block_function, names, ok); @@ -4187,7 +4179,7 @@ ParserBase<Impl>::ParseAsyncFunctionDeclaration( return impl()->NullStatement(); } Expect(Token::FUNCTION, CHECK_OK_CUSTOM(NullStatement)); - ParseFunctionFlags flags = ParseFunctionFlags::kIsAsync; + ParseFunctionFlags flags = ParseFunctionFlag::kIsAsync; return ParseHoistableDeclaration(pos, flags, names, default_export, ok); } @@ -4195,7 +4187,8 @@ template <typename Impl> void ParserBase<Impl>::ParseFunctionBody( typename ParserBase<Impl>::StatementListT result, IdentifierT function_name, int pos, const FormalParametersT& parameters, FunctionKind kind, - FunctionLiteral::FunctionType function_type, bool* ok) { + FunctionLiteral::FunctionType function_type, FunctionBodyType body_type, + bool accept_IN, bool* ok) { DeclarationScope* function_scope = scope()->AsDeclarationScope(); DeclarationScope* inner_scope = function_scope; BlockT inner_block = impl()->NullStatement(); @@ -4209,35 +4202,56 @@ void ParserBase<Impl>::ParseFunctionBody( body = inner_block->statements(); } - // If we are parsing the source as if it is wrapped in a function, the source - // ends without a closing brace. - Token::Value closing_token = - function_type == FunctionLiteral::kWrapped ? Token::EOS : Token::RBRACE; - { BlockState block_state(&scope_, inner_scope); if (IsResumableFunction(kind)) impl()->PrepareGeneratorVariables(); - if (IsAsyncGeneratorFunction(kind)) { - impl()->ParseAndRewriteAsyncGeneratorFunctionBody(pos, kind, body, ok); - } else if (IsGeneratorFunction(kind)) { - impl()->ParseAndRewriteGeneratorFunctionBody(pos, kind, body, ok); - } else if (IsAsyncFunction(kind)) { - ParseAsyncFunctionBody(inner_scope, body, CHECK_OK_VOID); + if (body_type == FunctionBodyType::kExpression) { + ExpressionClassifier classifier(this); + ExpressionT expression = + ParseAssignmentExpression(accept_IN, CHECK_OK_VOID); + ValidateExpression(CHECK_OK_VOID); + + if (IsAsyncFunction(kind)) { + BlockT block = factory()->NewBlock(1, true); + impl()->RewriteAsyncFunctionBody(body, block, expression, + CHECK_OK_VOID); + } else { + body->Add(BuildReturnStatement(expression, expression->position()), + zone()); + } } else { - ParseStatementList(body, closing_token, CHECK_OK_VOID); - } + DCHECK(accept_IN); + DCHECK_EQ(FunctionBodyType::kBlock, body_type); + // If we are parsing the source as if it is wrapped in a function, the + // source ends without a closing brace. + Token::Value closing_token = function_type == FunctionLiteral::kWrapped + ? Token::EOS + : Token::RBRACE; + + if (IsAsyncGeneratorFunction(kind)) { + impl()->ParseAndRewriteAsyncGeneratorFunctionBody(pos, kind, body, + CHECK_OK_VOID); + } else if (IsGeneratorFunction(kind)) { + impl()->ParseAndRewriteGeneratorFunctionBody(pos, kind, body, + CHECK_OK_VOID); + } else if (IsAsyncFunction(kind)) { + ParseAsyncFunctionBody(inner_scope, body, CHECK_OK_VOID); + } else { + ParseStatementList(body, closing_token, CHECK_OK_VOID); + } - if (IsDerivedConstructor(kind)) { - body->Add(factory()->NewReturnStatement(impl()->ThisExpression(), - kNoSourcePosition), - zone()); + if (IsDerivedConstructor(kind)) { + body->Add(factory()->NewReturnStatement(impl()->ThisExpression(), + kNoSourcePosition), + zone()); + } + Expect(closing_token, CHECK_OK_VOID); } } - Expect(closing_token, CHECK_OK_VOID); - scope()->set_end_position(scanner()->location().end_pos); + scope()->set_end_position(end_position()); if (!parameters.is_simple) { DCHECK_NOT_NULL(inner_scope); @@ -4256,7 +4270,7 @@ void ParserBase<Impl>::ParseFunctionBody( init_block = impl()->BuildRejectPromiseOnException(init_block); } - inner_scope->set_end_position(scanner()->location().end_pos); + inner_scope->set_end_position(end_position()); if (inner_scope->FinalizeBlockScope() != nullptr) { impl()->CheckConflictingVarDeclarations(inner_scope, CHECK_OK_VOID); impl()->InsertShadowingVarBindingInitializers(inner_block); @@ -4315,7 +4329,7 @@ void ParserBase<Impl>::CheckArityRestrictions(int param_count, template <typename Impl> bool ParserBase<Impl>::IsNextLetKeyword() { - DCHECK(peek() == Token::LET); + DCHECK_EQ(Token::LET, peek()); Token::Value next_next = PeekAhead(); switch (next_next) { case Token::LBRACE: @@ -4340,17 +4354,13 @@ bool ParserBase<Impl>::IsNextLetKeyword() { template <typename Impl> bool ParserBase<Impl>::IsTrivialExpression() { - Token::Value peek_token = peek(); - if (peek_token == Token::SMI || peek_token == Token::NUMBER || - peek_token == Token::BIGINT || peek_token == Token::NULL_LITERAL || - peek_token == Token::TRUE_LITERAL || peek_token == Token::FALSE_LITERAL || - peek_token == Token::STRING || peek_token == Token::IDENTIFIER || - peek_token == Token::THIS) { - // PeekAhead() is expensive & may not always be called, so we only call it - // after checking peek(). + if (Token::IsTrivialExpressionToken(peek())) { + // PeekAhead() may not always be called, so we only call it after checking + // peek(). Token::Value peek_ahead = PeekAhead(); if (peek_ahead == Token::COMMA || peek_ahead == Token::RPAREN || - peek_ahead == Token::SEMICOLON || peek_ahead == Token::RBRACK) { + peek_ahead == Token::SEMICOLON || peek_ahead == Token::RBRACK || + Token::IsAssignmentOp(peek_ahead)) { return true; } } @@ -4421,39 +4431,52 @@ ParserBase<Impl>::ParseArrowFunctionLiteral( // parameters. int dummy_num_parameters = -1; DCHECK_NE(kind & FunctionKind::kArrowFunction, 0); - LazyParsingResult result = impl()->SkipFunction( + FunctionLiteral::EagerCompileHint hint; + bool did_preparse_successfully = impl()->SkipFunction( nullptr, kind, FunctionLiteral::kAnonymousExpression, formal_parameters.scope, &dummy_num_parameters, - &produced_preparsed_scope_data, false, false, CHECK_OK); - DCHECK_NE(result, kLazyParsingAborted); + &produced_preparsed_scope_data, false, false, &hint, CHECK_OK); + DCHECK_NULL(produced_preparsed_scope_data); - USE(result); - formal_parameters.scope->ResetAfterPreparsing(ast_value_factory_, - false); - // Discard any queued destructuring assignments which appeared - // in this function's parameter list, and which were adopted - // into this function state, above. - function_state.RewindDestructuringAssignments(0); + + if (did_preparse_successfully) { + // Discard any queued destructuring assignments which appeared + // in this function's parameter list, and which were adopted + // into this function state, above. + function_state.RewindDestructuringAssignments(0); + } else { + // In case we did not sucessfully preparse the function because of an + // unidentified error we do a full reparse to return the error. + Consume(Token::LBRACE); + body = impl()->NewStatementList(8); + ParseFunctionBody(body, impl()->NullIdentifier(), kNoSourcePosition, + formal_parameters, kind, + FunctionLiteral::kAnonymousExpression, + FunctionBodyType::kBlock, true, ok); + CHECK(!*ok); + return impl()->NullExpression(); + } } else { Consume(Token::LBRACE); body = impl()->NewStatementList(8); ParseFunctionBody(body, impl()->NullIdentifier(), kNoSourcePosition, formal_parameters, kind, - FunctionLiteral::kAnonymousExpression, CHECK_OK); + FunctionLiteral::kAnonymousExpression, + FunctionBodyType::kBlock, true, CHECK_OK); expected_property_count = function_state.expected_property_count(); } } else { // Single-expression body has_braces = false; - const bool is_async = IsAsyncFunction(kind); body = impl()->NewStatementList(1); - impl()->AddParameterInitializationBlock(formal_parameters, body, is_async, - CHECK_OK); - ParseSingleExpressionFunctionBody(body, is_async, accept_IN, CHECK_OK); + ParseFunctionBody(body, impl()->NullIdentifier(), kNoSourcePosition, + formal_parameters, kind, + FunctionLiteral::kAnonymousExpression, + FunctionBodyType::kExpression, accept_IN, CHECK_OK); expected_property_count = function_state.expected_property_count(); } - formal_parameters.scope->set_end_position(scanner()->location().end_pos); + formal_parameters.scope->set_end_position(end_position()); // Arrow function formal parameters are parsed as StrictFormalParameterList, // which is not the same as "parameters of a strict function"; it only means @@ -4466,7 +4489,7 @@ ParserBase<Impl>::ParseArrowFunctionLiteral( // Validate strict mode. if (is_strict(language_mode())) { CheckStrictOctalLiteral(formal_parameters.scope->start_position(), - scanner()->location().end_pos, CHECK_OK); + end_position(), CHECK_OK); } impl()->CheckConflictingVarDeclarations(formal_parameters.scope, CHECK_OK); @@ -4532,9 +4555,9 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseClassLiteral( class_info.is_anonymous = is_anonymous; impl()->DeclareClassVariable(name, &class_info, class_token_pos, CHECK_OK); - scope()->set_start_position(scanner()->location().end_pos); + scope()->set_start_position(end_position()); if (Check(Token::EXTENDS)) { - FuncNameInferrer::State fni_state(fni_); + FuncNameInferrerState fni_state(&fni_); ExpressionClassifier extends_classifier(this); class_info.extends = ParseLeftHandSideExpression(CHECK_OK); ValidateExpression(CHECK_OK); @@ -4548,7 +4571,7 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseClassLiteral( const bool has_extends = !impl()->IsNull(class_info.extends); while (peek() != Token::RBRACE) { if (Check(Token::SEMICOLON)) continue; - FuncNameInferrer::State fni_state(fni_); + FuncNameInferrerState fni_state(&fni_); bool is_computed_name = false; // Classes do not care about computed // property names here. bool is_static; @@ -4580,32 +4603,13 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseClassLiteral( } Expect(Token::RBRACE, CHECK_OK); - int end_pos = scanner()->location().end_pos; + int end_pos = end_position(); block_scope->set_end_position(end_pos); return impl()->RewriteClassLiteral(block_scope, name, &class_info, class_token_pos, end_pos, ok); } template <typename Impl> -void ParserBase<Impl>::ParseSingleExpressionFunctionBody(StatementListT body, - bool is_async, - bool accept_IN, - bool* ok) { - if (is_async) impl()->PrepareGeneratorVariables(); - - ExpressionClassifier classifier(this); - ExpressionT expression = ParseAssignmentExpression(accept_IN, CHECK_OK_VOID); - ValidateExpression(CHECK_OK_VOID); - - if (is_async) { - BlockT block = factory()->NewBlock(1, true); - impl()->RewriteAsyncFunctionBody(body, block, expression, CHECK_OK_VOID); - } else { - body->Add(BuildReturnStatement(expression, expression->position()), zone()); - } -} - -template <typename Impl> void ParserBase<Impl>::ParseAsyncFunctionBody(Scope* scope, StatementListT body, bool* ok) { BlockT block = factory()->NewBlock(8, true); @@ -4614,7 +4618,7 @@ void ParserBase<Impl>::ParseAsyncFunctionBody(Scope* scope, StatementListT body, impl()->RewriteAsyncFunctionBody( body, block, factory()->NewUndefinedLiteral(kNoSourcePosition), CHECK_OK_VOID); - scope->set_end_position(scanner()->location().end_pos); + scope->set_end_position(end_position()); } template <typename Impl> @@ -4633,9 +4637,9 @@ ParserBase<Impl>::ParseAsyncFunctionLiteral(bool* ok) { IdentifierT name = impl()->NullIdentifier(); FunctionLiteral::FunctionType type = FunctionLiteral::kAnonymousExpression; - bool is_generator = Check(Token::MUL); - const bool kIsAsync = true; - const FunctionKind kind = FunctionKindFor(is_generator, kIsAsync); + ParseFunctionFlags flags = ParseFunctionFlag::kIsAsync; + if (Check(Token::MUL)) flags |= ParseFunctionFlag::kIsGenerator; + const FunctionKind kind = FunctionKindFor(flags); if (impl()->ParsingDynamicFunctionDeclaration()) { // We don't want dynamic functions to actually declare their name @@ -4659,7 +4663,7 @@ ParserBase<Impl>::ParseAsyncFunctionLiteral(bool* ok) { name, scanner()->location(), is_strict_reserved ? kFunctionNameIsStrictReserved : kFunctionNameValidityUnknown, - kind, pos, type, language_mode(), nullptr, CHECK_OK); + kind, pos, type, language_mode(), nullptr, ok); } template <typename Impl> @@ -5112,6 +5116,7 @@ typename ParserBase<Impl>::BlockT ParserBase<Impl>::ParseBlock( Expect(Token::LBRACE, CHECK_OK_CUSTOM(NullStatement)); { BlockState block_state(zone(), &scope_); + // Scope starts before opening brace. scope()->set_start_position(scanner()->location().beg_pos); typename Types::Target target(this, body); @@ -5123,9 +5128,10 @@ typename ParserBase<Impl>::BlockT ParserBase<Impl>::ParseBlock( } Expect(Token::RBRACE, CHECK_OK_CUSTOM(NullStatement)); - int end_pos = scanner()->location().end_pos; - scope()->set_end_position(end_pos); - impl()->RecordBlockSourceRange(body, end_pos); + // Scope ends after closing brace. + scope()->set_end_position(scanner()->location().end_pos); + // Coverage range uses position before closing brace. + impl()->RecordBlockSourceRange(body, scanner()->location().beg_pos); body->set_scope(scope()->FinalizeBlockScope()); } return body; @@ -5144,7 +5150,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseScopedStatement( BlockT block = factory()->NewBlock(1, false); StatementT body = ParseFunctionDeclaration(CHECK_OK); block->statements()->Add(body, zone()); - scope()->set_end_position(scanner()->location().end_pos); + scope()->set_end_position(end_position()); block->set_scope(scope()->FinalizeBlockScope()); return block; } @@ -5172,7 +5178,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseVariableStatement( DeclarationParsingResult parsing_result; StatementT result = ParseVariableDeclarations(var_context, &parsing_result, names, CHECK_OK); - ExpectSemicolon(CHECK_OK); + ExpectSemicolon(ok); return result; } @@ -5234,7 +5240,7 @@ ParserBase<Impl>::ParseExpressionOrLabelledStatement( } bool starts_with_identifier = peek_any_identifier(); - ExpressionT expr = ParseExpression(true, CHECK_OK); + ExpressionT expr = ParseExpression(CHECK_OK); if (peek() == Token::COLON && starts_with_identifier && impl()->IsIdentifier(expr)) { // The whole expression was a single identifier, and not, e.g., @@ -5273,7 +5279,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseIfStatement( int pos = peek_position(); Expect(Token::IF, CHECK_OK); Expect(Token::LPAREN, CHECK_OK); - ExpressionT condition = ParseExpression(true, CHECK_OK); + ExpressionT condition = ParseExpression(CHECK_OK); Expect(Token::RPAREN, CHECK_OK); SourceRange then_range, else_range; @@ -5285,9 +5291,8 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseIfStatement( StatementT else_statement = impl()->NullStatement(); if (Check(Token::ELSE)) { - else_range = SourceRange::ContinuationOf(then_range); else_statement = ParseScopedStatement(labels, CHECK_OK); - else_range.end = scanner_->location().end_pos; + else_range = SourceRange::ContinuationOf(then_range, end_position()); } else { else_statement = factory()->NewEmptyStatement(kNoSourcePosition); } @@ -5330,7 +5335,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseContinueStatement( } ExpectSemicolon(CHECK_OK); StatementT stmt = factory()->NewContinueStatement(target, pos); - impl()->RecordJumpStatementSourceRange(stmt, scanner_->location().end_pos); + impl()->RecordJumpStatementSourceRange(stmt, end_position()); return stmt; } @@ -5369,7 +5374,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseBreakStatement( } ExpectSemicolon(CHECK_OK); StatementT stmt = factory()->NewBreakStatement(target, pos); - impl()->RecordJumpStatementSourceRange(stmt, scanner_->location().end_pos); + impl()->RecordJumpStatementSourceRange(stmt, end_position()); return stmt; } @@ -5404,14 +5409,14 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseReturnStatement( return_value = impl()->ThisExpression(loc.beg_pos); } } else { - return_value = ParseExpression(true, CHECK_OK); + return_value = ParseExpression(CHECK_OK); } ExpectSemicolon(CHECK_OK); return_value = impl()->RewriteReturn(return_value, loc.beg_pos); - int continuation_pos = scanner_->location().end_pos; + int continuation_pos = end_position(); StatementT stmt = BuildReturnStatement(return_value, loc.beg_pos, continuation_pos); - impl()->RecordJumpStatementSourceRange(stmt, scanner_->location().end_pos); + impl()->RecordJumpStatementSourceRange(stmt, end_position()); return stmt; } @@ -5431,7 +5436,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseWithStatement( } Expect(Token::LPAREN, CHECK_OK); - ExpressionT expr = ParseExpression(true, CHECK_OK); + ExpressionT expr = ParseExpression(CHECK_OK); Expect(Token::RPAREN, CHECK_OK); Scope* with_scope = NewScope(WITH_SCOPE); @@ -5440,7 +5445,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseWithStatement( BlockState block_state(&scope_, with_scope); with_scope->set_start_position(scanner()->peek_location().beg_pos); body = ParseStatement(labels, nullptr, CHECK_OK); - with_scope->set_end_position(scanner()->location().end_pos); + with_scope->set_end_position(end_position()); } return factory()->NewWithStatement(with_scope, expr, body, pos); } @@ -5467,7 +5472,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseDoWhileStatement( Expect(Token::WHILE, CHECK_OK); Expect(Token::LPAREN, CHECK_OK); - ExpressionT cond = ParseExpression(true, CHECK_OK); + ExpressionT cond = ParseExpression(CHECK_OK); Expect(Token::RPAREN, CHECK_OK); // Allow do-statements to be terminated with and without @@ -5497,7 +5502,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseWhileStatement( Expect(Token::WHILE, CHECK_OK); Expect(Token::LPAREN, CHECK_OK); - ExpressionT cond = ParseExpression(true, CHECK_OK); + ExpressionT cond = ParseExpression(CHECK_OK); Expect(Token::RPAREN, CHECK_OK); { SourceRangeScope range_scope(scanner(), &body_range); @@ -5523,11 +5528,11 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseThrowStatement( *ok = false; return impl()->NullStatement(); } - ExpressionT exception = ParseExpression(true, CHECK_OK); + ExpressionT exception = ParseExpression(CHECK_OK); ExpectSemicolon(CHECK_OK); StatementT stmt = impl()->NewThrowStatement(exception, pos); - impl()->RecordThrowSourceRange(stmt, scanner_->location().end_pos); + impl()->RecordThrowSourceRange(stmt, end_position()); return stmt; } @@ -5545,7 +5550,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseSwitchStatement( Expect(Token::SWITCH, CHECK_OK); Expect(Token::LPAREN, CHECK_OK); - ExpressionT tag = ParseExpression(true, CHECK_OK); + ExpressionT tag = ParseExpression(CHECK_OK); Expect(Token::RPAREN, CHECK_OK); auto switch_statement = @@ -5565,7 +5570,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseSwitchStatement( SourceRange clause_range; SourceRangeScope range_scope(scanner(), &clause_range); if (Check(Token::CASE)) { - label = ParseExpression(true, CHECK_OK); + label = ParseExpression(CHECK_OK); } else { Expect(Token::DEFAULT, CHECK_OK); if (default_seen) { @@ -5588,9 +5593,9 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseSwitchStatement( } Expect(Token::RBRACE, CHECK_OK); - int end_position = scanner()->location().end_pos; - scope()->set_end_position(end_position); - impl()->RecordSwitchStatementSourceRange(switch_statement, end_position); + int end_pos = end_position(); + scope()->set_end_position(end_pos); + impl()->RecordSwitchStatementSourceRange(switch_statement, end_pos); Scope* switch_scope = scope()->FinalizeBlockScope(); if (switch_scope != nullptr) { return impl()->RewriteSwitchStatement(switch_statement, switch_scope); @@ -5659,8 +5664,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseTryStatement( ParseIdentifier(kDontAllowRestrictedIdentifiers, CHECK_OK); } else { ExpressionClassifier pattern_classifier(this); - catch_info.pattern = ParsePrimaryExpression(CHECK_OK); - ValidateBindingPattern(CHECK_OK); + catch_info.pattern = ParseBindingPattern(CHECK_OK); } Expect(Token::RPAREN, CHECK_OK); @@ -5672,12 +5676,12 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseTryStatement( catch_info.inner_block = ParseBlock(nullptr, CHECK_OK); catch_block->statements()->Add(catch_info.inner_block, zone()); impl()->ValidateCatchBlock(catch_info, CHECK_OK); - scope()->set_end_position(scanner()->location().end_pos); + scope()->set_end_position(end_position()); catch_block->set_scope(scope()->FinalizeBlockScope()); } } - catch_info.scope->set_end_position(scanner()->location().end_pos); + catch_info.scope->set_end_position(end_position()); } else { catch_block = ParseBlock(nullptr, CHECK_OK); } @@ -5777,7 +5781,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForStatement( int lhs_beg_pos = peek_position(); ExpressionClassifier classifier(this); ExpressionT expression = ParseExpressionCoverGrammar(false, CHECK_OK); - int lhs_end_pos = scanner()->location().end_pos; + int lhs_end_pos = end_position(); bool is_for_each = CheckInOrOf(&for_info.mode); bool is_destructuring = is_for_each && (expression->IsArrayLiteral() || @@ -5853,7 +5857,7 @@ ParserBase<Impl>::ParseForEachStatementWithDeclarations( enumerable = ParseAssignmentExpression(true, CHECK_OK); ValidateExpression(CHECK_OK); } else { - enumerable = ParseExpression(true, CHECK_OK); + enumerable = ParseExpression(CHECK_OK); } Expect(Token::RPAREN, CHECK_OK); @@ -5861,7 +5865,7 @@ ParserBase<Impl>::ParseForEachStatementWithDeclarations( Scope* for_scope = nullptr; if (inner_block_scope != nullptr) { for_scope = inner_block_scope->outer_scope(); - DCHECK(for_scope == scope()); + DCHECK_EQ(for_scope, scope()); inner_block_scope->set_start_position(scanner()->location().beg_pos); } @@ -5882,7 +5886,7 @@ ParserBase<Impl>::ParseForEachStatementWithDeclarations( body_block->statements()->Add(body, zone()); if (inner_block_scope != nullptr) { - inner_block_scope->set_end_position(scanner()->location().end_pos); + inner_block_scope->set_end_position(end_position()); body_block->set_scope(inner_block_scope->FinalizeBlockScope()); } } @@ -5890,10 +5894,11 @@ ParserBase<Impl>::ParseForEachStatementWithDeclarations( StatementT final_loop = impl()->InitializeForEachStatement( loop, each_variable, enumerable, body_block); - init_block = impl()->CreateForEachStatementTDZ(init_block, *for_info, ok); + init_block = + impl()->CreateForEachStatementTDZ(init_block, *for_info, CHECK_OK); if (for_scope != nullptr) { - for_scope->set_end_position(scanner()->location().end_pos); + for_scope->set_end_position(end_position()); for_scope = for_scope->FinalizeBlockScope(); } @@ -5931,7 +5936,7 @@ ParserBase<Impl>::ParseForEachStatementWithoutDeclarations( enumerable = ParseAssignmentExpression(true, CHECK_OK); ValidateExpression(CHECK_OK); } else { - enumerable = ParseExpression(true, CHECK_OK); + enumerable = ParseExpression(CHECK_OK); } Expect(Token::RPAREN, CHECK_OK); @@ -5965,10 +5970,10 @@ ParserBase<Impl>::ParseStandardForLoopWithLexicalDeclarations( scope()->set_start_position(scanner()->location().beg_pos); loop = ParseStandardForLoop(stmt_pos, labels, own_labels, &cond, &next, &body, CHECK_OK); - scope()->set_end_position(scanner()->location().end_pos); + scope()->set_end_position(end_position()); } - scope()->set_end_position(scanner()->location().end_pos); + scope()->set_end_position(end_position()); if (for_info->bound_names.length() > 0 && function_state_->contains_function_or_eval()) { scope()->set_is_hidden(); @@ -6014,12 +6019,12 @@ typename ParserBase<Impl>::ForStatementT ParserBase<Impl>::ParseStandardForLoop( typename Types::Target target(this, loop); if (peek() != Token::SEMICOLON) { - *cond = ParseExpression(true, CHECK_OK); + *cond = ParseExpression(CHECK_OK); } Expect(Token::SEMICOLON, CHECK_OK); if (peek() != Token::RPAREN) { - ExpressionT exp = ParseExpression(true, CHECK_OK); + ExpressionT exp = ParseExpression(CHECK_OK); *next = factory()->NewExpressionStatement(exp, exp->position()); } Expect(Token::RPAREN, CHECK_OK); @@ -6114,7 +6119,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForAwaitStatement( BlockState inner_state(&scope_, inner_block_scope); ExpressionClassifier classifier(this); ExpressionT lhs = each_variable = ParseLeftHandSideExpression(CHECK_OK); - int lhs_end_pos = scanner()->location().end_pos; + int lhs_end_pos = end_position(); if (lhs->IsArrayLiteral() || lhs->IsObjectLiteral()) { ValidateAssignmentPattern(CHECK_OK); @@ -6149,7 +6154,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForAwaitStatement( SourceRangeScope range_scope(scanner(), &body_range); body = ParseStatement(nullptr, nullptr, CHECK_OK); - scope()->set_end_position(scanner()->location().end_pos); + scope()->set_end_position(end_position()); impl()->RecordIterationStatementSourceRange(loop, range_scope.Finalize()); if (has_declarations) { @@ -6177,10 +6182,10 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForAwaitStatement( return final_loop; } - BlockT init_block = - impl()->CreateForEachStatementTDZ(impl()->NullStatement(), for_info, ok); + BlockT init_block = impl()->CreateForEachStatementTDZ(impl()->NullStatement(), + for_info, CHECK_OK); - scope()->set_end_position(scanner()->location().end_pos); + scope()->set_end_position(end_position()); Scope* for_scope = scope()->FinalizeBlockScope(); // Parsed for-in loop w/ variable declarations. if (!impl()->IsNull(init_block)) { @@ -6209,10 +6214,9 @@ void ParserBase<Impl>::ObjectLiteralChecker::CheckDuplicateProto( template <typename Impl> void ParserBase<Impl>::ClassLiteralChecker::CheckClassMethodName( - Token::Value property, PropertyKind type, bool is_generator, bool is_async, + Token::Value property, ParsePropertyKind type, ParseFunctionFlags flags, bool is_static, bool* ok) { - DCHECK(type == PropertyKind::kMethodProperty || - type == PropertyKind::kAccessorProperty); + DCHECK(type == ParsePropertyKind::kMethod || IsAccessor(type)); if (property == Token::SMI || property == Token::NUMBER) return; @@ -6223,11 +6227,13 @@ void ParserBase<Impl>::ClassLiteralChecker::CheckClassMethodName( return; } } else if (IsConstructor()) { - if (is_generator || is_async || type == PropertyKind::kAccessorProperty) { + if (flags != ParseFunctionFlag::kIsNormal || IsAccessor(type)) { MessageTemplate::Template msg = - is_generator ? MessageTemplate::kConstructorIsGenerator - : is_async ? MessageTemplate::kConstructorIsAsync - : MessageTemplate::kConstructorIsAccessor; + (flags & ParseFunctionFlag::kIsGenerator) != 0 + ? MessageTemplate::kConstructorIsGenerator + : (flags & ParseFunctionFlag::kIsAsync) != 0 + ? MessageTemplate::kConstructorIsAsync + : MessageTemplate::kConstructorIsAccessor; this->parser()->ReportMessage(msg); *ok = false; return; diff --git a/deps/v8/src/parsing/parser.cc b/deps/v8/src/parsing/parser.cc index 41ff551091..6d0d9fff21 100644 --- a/deps/v8/src/parsing/parser.cc +++ b/deps/v8/src/parsing/parser.cc @@ -29,56 +29,6 @@ namespace v8 { namespace internal { - - -// Helper for putting parts of the parse results into a temporary zone when -// parsing inner function bodies. -class DiscardableZoneScope { - public: - DiscardableZoneScope(Parser* parser, Zone* temp_zone, bool use_temp_zone) - : fni_(parser->ast_value_factory_, temp_zone), - parser_(parser), - prev_fni_(parser->fni_), - prev_zone_(parser->zone_), - prev_allow_lazy_(parser->allow_lazy_), - prev_temp_zoned_(parser->temp_zoned_) { - if (use_temp_zone) { - DCHECK(!parser_->temp_zoned_); - parser_->allow_lazy_ = false; - parser_->temp_zoned_ = true; - parser_->fni_ = &fni_; - parser_->zone_ = temp_zone; - parser_->factory()->set_zone(temp_zone); - if (parser_->reusable_preparser_ != nullptr) { - parser_->reusable_preparser_->zone_ = temp_zone; - parser_->reusable_preparser_->factory()->set_zone(temp_zone); - } - } - } - void Reset() { - parser_->fni_ = prev_fni_; - parser_->zone_ = prev_zone_; - parser_->factory()->set_zone(prev_zone_); - parser_->allow_lazy_ = prev_allow_lazy_; - parser_->temp_zoned_ = prev_temp_zoned_; - if (parser_->reusable_preparser_ != nullptr) { - parser_->reusable_preparser_->zone_ = prev_zone_; - parser_->reusable_preparser_->factory()->set_zone(prev_zone_); - } - } - ~DiscardableZoneScope() { Reset(); } - - private: - FuncNameInferrer fni_; - Parser* parser_; - FuncNameInferrer* prev_fni_; - Zone* prev_zone_; - bool prev_allow_lazy_; - bool prev_temp_zoned_; - - DISALLOW_COPY_AND_ASSIGN(DiscardableZoneScope); -}; - FunctionLiteral* Parser::DefaultConstructor(const AstRawString* name, bool call_super, int pos, int end_pos) { @@ -414,12 +364,12 @@ Parser::Parser(ParseInfo* info) info->is_module(), true), scanner_(info->unicode_cache(), info->character_stream(), info->is_module()), + preparser_zone_(info->zone()->allocator(), ZONE_NAME), reusable_preparser_(nullptr), mode_(PARSE_EAGERLY), // Lazy mode must be set explicitly. source_range_map_(info->source_range_map()), target_stack_(nullptr), total_preparse_skipped_(0), - temp_zoned_(false), consumed_preparsed_scope_data_(info->consumed_preparsed_scope_data()), parameters_end_pos_(info->parameters_end_pos()) { // Even though we were passed ParseInfo, we should not store it in @@ -449,7 +399,6 @@ Parser::Parser(ParseInfo* info) set_allow_harmony_static_fields(FLAG_harmony_static_fields); set_allow_harmony_dynamic_import(FLAG_harmony_dynamic_import); set_allow_harmony_import_meta(FLAG_harmony_import_meta); - set_allow_harmony_bigint(FLAG_harmony_bigint); set_allow_harmony_numeric_separator(FLAG_harmony_numeric_separator); set_allow_harmony_private_fields(FLAG_harmony_private_fields); for (int feature = 0; feature < v8::Isolate::kUseCounterFeatureCount; @@ -458,22 +407,27 @@ Parser::Parser(ParseInfo* info) } } -void Parser::DeserializeScopeChain( - Isolate* isolate, ParseInfo* info, - MaybeHandle<ScopeInfo> maybe_outer_scope_info) { +void Parser::InitializeEmptyScopeChain(ParseInfo* info) { + DCHECK_NULL(original_scope_); + DCHECK_NULL(info->script_scope()); // TODO(wingo): Add an outer SCRIPT_SCOPE corresponding to the native // context, which will have the "this" binding for script scopes. DeclarationScope* script_scope = NewScriptScope(); info->set_script_scope(script_scope); - Scope* scope = script_scope; + original_scope_ = script_scope; +} + +void Parser::DeserializeScopeChain( + Isolate* isolate, ParseInfo* info, + MaybeHandle<ScopeInfo> maybe_outer_scope_info) { + InitializeEmptyScopeChain(info); Handle<ScopeInfo> outer_scope_info; if (maybe_outer_scope_info.ToHandle(&outer_scope_info)) { DCHECK(ThreadId::Current().Equals(isolate->thread_id())); - scope = Scope::DeserializeScopeChain( - isolate, zone(), *outer_scope_info, script_scope, ast_value_factory(), - Scope::DeserializationMode::kScopesOnly); + original_scope_ = Scope::DeserializeScopeChain( + isolate, zone(), *outer_scope_info, info->script_scope(), + ast_value_factory(), Scope::DeserializationMode::kScopesOnly); } - original_scope_ = scope; } namespace { @@ -503,7 +457,6 @@ FunctionLiteral* Parser::ParseProgram(Isolate* isolate, ParseInfo* info) { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"), "V8.ParseProgram"); base::ElapsedTimer timer; if (V8_UNLIKELY(FLAG_log_function_events)) timer.Start(); - fni_ = new (zone()) FuncNameInferrer(ast_value_factory(), zone()); // Initialize parser state. DeserializeScopeChain(isolate, info, info->maybe_outer_scope_info()); @@ -749,8 +702,7 @@ FunctionLiteral* Parser::DoParseFunction(Isolate* isolate, ParseInfo* info, DCHECK_NULL(target_stack_); DCHECK(ast_value_factory()); - fni_ = new (zone()) FuncNameInferrer(ast_value_factory(), zone()); - fni_->PushEnclosingName(raw_name); + fni_.PushEnclosingName(raw_name); ResetFunctionLiteralId(); DCHECK_LT(0, info->function_literal_id()); @@ -2584,116 +2536,71 @@ FunctionLiteral* Parser::ParseFunctionLiteral( int function_literal_id = GetNextFunctionLiteralId(); ProducedPreParsedScopeData* produced_preparsed_scope_data = nullptr; - Zone* outer_zone = zone(); - DeclarationScope* scope; - - { - // Temporary zones can nest. When we migrate free variables (see below), we - // need to recreate them in the previous Zone. - AstNodeFactory previous_zone_ast_node_factory(ast_value_factory(), zone()); - - // Open a new zone scope, which sets our AstNodeFactory to allocate in the - // new temporary zone if the preconditions are satisfied, and ensures that - // the previous zone is always restored after parsing the body. To be able - // to do scope analysis correctly after full parsing, we migrate needed - // information when the function is parsed. - Zone temp_zone(zone()->allocator(), ZONE_NAME); - DiscardableZoneScope zone_scope(this, &temp_zone, should_preparse); - - // This Scope lives in the main zone. We'll migrate data into that zone - // later. - scope = NewFunctionScope(kind, outer_zone); - SetLanguageMode(scope, language_mode); + // This Scope lives in the main zone. We'll migrate data into that zone later. + Zone* parse_zone = should_preparse ? &preparser_zone_ : zone(); + DeclarationScope* scope = NewFunctionScope(kind, parse_zone); + SetLanguageMode(scope, language_mode); #ifdef DEBUG - scope->SetScopeName(function_name); - if (should_preparse) scope->set_needs_migration(); + scope->SetScopeName(function_name); #endif - if (!is_wrapped) Expect(Token::LPAREN, CHECK_OK); - scope->set_start_position(scanner()->location().beg_pos); - - // Eager or lazy parse? If is_lazy_top_level_function, we'll parse - // lazily. We'll call SkipFunction, which may decide to - // abort lazy parsing if it suspects that wasn't a good idea. If so (in - // which case the parser is expected to have backtracked), or if we didn't - // try to lazy parse in the first place, we'll have to parse eagerly. - if (should_preparse) { - DCHECK(parse_lazily()); - DCHECK(is_lazy_top_level_function || is_lazy_inner_function); - DCHECK(!is_wrapped); - Scanner::BookmarkScope bookmark(scanner()); - bookmark.Set(); - LazyParsingResult result = SkipFunction( - function_name, kind, function_type, scope, &num_parameters, - &produced_preparsed_scope_data, is_lazy_inner_function, - is_lazy_top_level_function, CHECK_OK); - - if (result == kLazyParsingAborted) { - DCHECK(is_lazy_top_level_function); - bookmark.Apply(); - // This is probably an initialization function. Inform the compiler it - // should also eager-compile this function. - eager_compile_hint = FunctionLiteral::kShouldEagerCompile; - scope->ResetAfterPreparsing(ast_value_factory(), true); - zone_scope.Reset(); - // Trigger eager (re-)parsing, just below this block. - should_preparse = false; - } - } - - if (should_preparse) { - scope->AnalyzePartially(&previous_zone_ast_node_factory); - } else { - body = ParseFunction( - function_name, pos, kind, function_type, scope, &num_parameters, - &function_length, &has_duplicate_parameters, &expected_property_count, - &suspend_count, arguments_for_wrapped_function, CHECK_OK); - } - - DCHECK_EQ(should_preparse, temp_zoned_); - if (V8_UNLIKELY(FLAG_log_function_events)) { - double ms = timer.Elapsed().InMillisecondsF(); - const char* event_name = should_preparse - ? (is_top_level ? "preparse-no-resolution" - : "preparse-resolution") - : "full-parse"; - logger_->FunctionEvent( - event_name, script_id(), ms, scope->start_position(), - scope->end_position(), - reinterpret_cast<const char*>(function_name->raw_data()), - function_name->byte_length()); - } - if (V8_UNLIKELY(FLAG_runtime_stats)) { - if (should_preparse) { - RuntimeCallCounterId counter_id = - parsing_on_main_thread_ - ? RuntimeCallCounterId::kPreParseWithVariableResolution - : RuntimeCallCounterId:: - kPreParseBackgroundWithVariableResolution; - if (is_top_level) { - counter_id = parsing_on_main_thread_ - ? RuntimeCallCounterId::kPreParseNoVariableResolution - : RuntimeCallCounterId:: - kPreParseBackgroundNoVariableResolution; - } - if (runtime_call_stats_) { - runtime_call_stats_->CorrectCurrentCounterId(counter_id); - } - } + if (!is_wrapped) Expect(Token::LPAREN, CHECK_OK); + scope->set_start_position(position()); + + // Eager or lazy parse? If is_lazy_top_level_function, we'll parse + // lazily. We'll call SkipFunction, which may decide to + // abort lazy parsing if it suspects that wasn't a good idea. If so (in + // which case the parser is expected to have backtracked), or if we didn't + // try to lazy parse in the first place, we'll have to parse eagerly. + bool did_preparse_successfully = + should_preparse && + SkipFunction(function_name, kind, function_type, scope, &num_parameters, + &produced_preparsed_scope_data, is_lazy_inner_function, + is_lazy_top_level_function, &eager_compile_hint, CHECK_OK); + if (!did_preparse_successfully) { + body = ParseFunction( + function_name, pos, kind, function_type, scope, &num_parameters, + &function_length, &has_duplicate_parameters, &expected_property_count, + &suspend_count, arguments_for_wrapped_function, CHECK_OK); + } + + if (V8_UNLIKELY(FLAG_log_function_events)) { + double ms = timer.Elapsed().InMillisecondsF(); + const char* event_name = + should_preparse + ? (is_top_level ? "preparse-no-resolution" : "preparse-resolution") + : "full-parse"; + logger_->FunctionEvent( + event_name, script_id(), ms, scope->start_position(), + scope->end_position(), + reinterpret_cast<const char*>(function_name->raw_data()), + function_name->byte_length()); + } + if (V8_UNLIKELY(FLAG_runtime_stats) && did_preparse_successfully) { + const RuntimeCallCounterId counters[2][2] = { + {RuntimeCallCounterId::kPreParseBackgroundNoVariableResolution, + RuntimeCallCounterId::kPreParseNoVariableResolution}, + {RuntimeCallCounterId::kPreParseBackgroundWithVariableResolution, + RuntimeCallCounterId::kPreParseWithVariableResolution}}; + if (runtime_call_stats_) { + bool tracked_variables = + PreParser::ShouldTrackUnresolvedVariables(is_lazy_top_level_function); + runtime_call_stats_->CorrectCurrentCounterId( + counters[tracked_variables][parsing_on_main_thread_]); } + } - // Validate function name. We can do this only after parsing the function, - // since the function can declare itself strict. - language_mode = scope->language_mode(); - CheckFunctionName(language_mode, function_name, function_name_validity, - function_name_location, CHECK_OK); + // Validate function name. We can do this only after parsing the function, + // since the function can declare itself strict. + language_mode = scope->language_mode(); + CheckFunctionName(language_mode, function_name, function_name_validity, + function_name_location, CHECK_OK); - if (is_strict(language_mode)) { - CheckStrictOctalLiteral(scope->start_position(), scope->end_position(), - CHECK_OK); - } - CheckConflictingVarDeclarations(scope, CHECK_OK); - } // DiscardableZoneScope goes out of scope. + if (is_strict(language_mode)) { + CheckStrictOctalLiteral(scope->start_position(), scope->end_position(), + CHECK_OK); + } + CheckConflictingVarDeclarations(scope, CHECK_OK); FunctionLiteral::ParameterFlag duplicate_parameters = has_duplicate_parameters ? FunctionLiteral::kHasDuplicateParameters @@ -2708,19 +2615,20 @@ FunctionLiteral* Parser::ParseFunctionLiteral( function_literal->set_suspend_count(suspend_count); if (should_infer_name) { - DCHECK_NOT_NULL(fni_); - fni_->AddFunction(function_literal); + fni_.AddFunction(function_literal); } return function_literal; } -Parser::LazyParsingResult Parser::SkipFunction( +bool Parser::SkipFunction( const AstRawString* function_name, FunctionKind kind, FunctionLiteral::FunctionType function_type, DeclarationScope* function_scope, int* num_parameters, ProducedPreParsedScopeData** produced_preparsed_scope_data, - bool is_inner_function, bool may_abort, bool* ok) { + bool is_inner_function, bool may_abort, + FunctionLiteral::EagerCompileHint* hint, bool* ok) { FunctionState function_state(&function_state_, &scope_, function_scope); + function_scope->set_zone(&preparser_zone_); DCHECK_NE(kNoSourcePosition, function_scope->start_position()); DCHECK_EQ(kNoSourcePosition, parameters_end_pos_); @@ -2729,8 +2637,7 @@ Parser::LazyParsingResult Parser::SkipFunction( scanner()->current_token() == Token::ARROW); // FIXME(marja): There are 2 ways to skip functions now. Unify them. - DCHECK_NOT_NULL(consumed_preparsed_scope_data_); - if (consumed_preparsed_scope_data_->HasData()) { + if (consumed_preparsed_scope_data_) { DCHECK(FLAG_preparser_scope_analysis); int end_position; LanguageMode language_mode; @@ -2752,9 +2659,13 @@ Parser::LazyParsingResult Parser::SkipFunction( function_scope->RecordSuperPropertyUsage(); } SkipFunctionLiterals(num_inner_functions); - return kLazyParsingComplete; + function_scope->ResetAfterPreparsing(ast_value_factory_, false); + return true; } + Scanner::BookmarkScope bookmark(scanner()); + bookmark.Set(); + // With no cached data, we partially parse the function, without building an // AST. This gathers the data needed to build a lazy function. TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"), "V8.PreParse"); @@ -2768,28 +2679,41 @@ Parser::LazyParsingResult Parser::SkipFunction( may_abort, use_counts_, produced_preparsed_scope_data, this->script_id()); // Return immediately if pre-parser decided to abort parsing. - if (result == PreParser::kPreParseAbort) return kLazyParsingAborted; + if (result == PreParser::kPreParseAbort) { + bookmark.Apply(); + function_scope->ResetAfterPreparsing(ast_value_factory(), true); + *hint = FunctionLiteral::kShouldEagerCompile; + return false; + } + if (result == PreParser::kPreParseStackOverflow) { // Propagate stack overflow. set_stack_overflow(); *ok = false; - return kLazyParsingComplete; - } - if (pending_error_handler()->has_pending_error()) { + } else if (pending_error_handler()->ErrorUnidentifiableByPreParser()) { + // If we encounter an error that the preparser can not identify we reset to + // the state before preparsing. The caller may then fully parse the function + // to identify the actual error. + bookmark.Apply(); + function_scope->ResetAfterPreparsing(ast_value_factory(), true); + pending_error_handler()->ResetUnidentifiableError(); + return false; + } else if (pending_error_handler()->has_pending_error()) { *ok = false; - return kLazyParsingComplete; - } + } else { + set_allow_eval_cache(reusable_preparser()->allow_eval_cache()); - set_allow_eval_cache(reusable_preparser()->allow_eval_cache()); + PreParserLogger* logger = reusable_preparser()->logger(); + function_scope->set_end_position(logger->end()); + Expect(Token::RBRACE, CHECK_OK_VALUE(kLazyParsingComplete)); + total_preparse_skipped_ += + function_scope->end_position() - function_scope->start_position(); + *num_parameters = logger->num_parameters(); + SkipFunctionLiterals(logger->num_inner_functions()); + function_scope->AnalyzePartially(factory()); + } - PreParserLogger* logger = reusable_preparser()->logger(); - function_scope->set_end_position(logger->end()); - Expect(Token::RBRACE, CHECK_OK_VALUE(kLazyParsingComplete)); - total_preparse_skipped_ += - function_scope->end_position() - function_scope->start_position(); - *num_parameters = logger->num_parameters(); - SkipFunctionLiterals(logger->num_inner_functions()); - return kLazyParsingComplete; + return true; } Statement* Parser::BuildAssertIsCoercible(Variable* var, @@ -2863,7 +2787,7 @@ Block* Parser::BuildParameterInitializationBlock( DCHECK(!parameters.is_simple); DCHECK(scope()->is_function_scope()); DCHECK_EQ(scope(), parameters.scope); - Block* init_block = factory()->NewBlock(1, true); + Block* init_block = factory()->NewBlock(parameters.num_parameters(), true); int index = 0; for (auto parameter : parameters.params) { DeclarationDescriptor descriptor; @@ -3002,19 +2926,6 @@ Block* Parser::BuildRejectPromiseOnException(Block* inner_block) { return result; } -Expression* Parser::BuildResolvePromise(Expression* value, int pos) { - // %ResolvePromise(.promise, value), .promise - ZonePtrList<Expression>* args = - new (zone()) ZonePtrList<Expression>(2, zone()); - args->Add(factory()->NewVariableProxy(PromiseVariable()), zone()); - args->Add(value, zone()); - Expression* call_runtime = - factory()->NewCallRuntime(Runtime::kInlineResolvePromise, args, pos); - return factory()->NewBinaryOperation( - Token::COMMA, call_runtime, - factory()->NewVariableProxy(PromiseVariable()), pos); -} - Expression* Parser::BuildRejectPromise(Expression* value, int pos) { // %promise_internal_reject(.promise, value, false), .promise // Disables the additional debug event for the rejection since a debug event @@ -3038,7 +2949,7 @@ Variable* Parser::PromiseVariable() { Variable* promise = function_state_->scope()->promise_var(); if (promise == nullptr) { promise = function_state_->scope()->DeclarePromiseVar( - ast_value_factory()->empty_string()); + ast_value_factory()->dot_promise_string()); } return promise; } @@ -3128,7 +3039,8 @@ ZonePtrList<Statement>* Parser::ParseFunction( *function_length = formals.function_length; ZonePtrList<Statement>* body = new (zone()) ZonePtrList<Statement>(8, zone()); - ParseFunctionBody(body, function_name, pos, formals, kind, function_type, ok); + ParseFunctionBody(body, function_name, pos, formals, kind, function_type, + FunctionBodyType::kBlock, true, ok); // Validate parameter names. We can do this only after parsing the function, // since the function can declare itself strict. @@ -3238,7 +3150,8 @@ void Parser::DeclareClassProperty(const AstRawString* class_name, } FunctionLiteral* Parser::CreateInitializerFunction( - DeclarationScope* scope, ZonePtrList<ClassLiteral::Property>* fields) { + const char* name, DeclarationScope* scope, + ZonePtrList<ClassLiteral::Property>* fields) { DCHECK_EQ(scope->function_kind(), FunctionKind::kClassFieldsInitializerFunction); // function() { .. class fields initializer .. } @@ -3247,10 +3160,10 @@ FunctionLiteral* Parser::CreateInitializerFunction( factory()->NewInitializeClassFieldsStatement(fields, kNoSourcePosition); statements->Add(static_fields, zone()); return factory()->NewFunctionLiteral( - ast_value_factory()->empty_string(), scope, statements, 0, 0, 0, + ast_value_factory()->GetOneByteString(name), scope, statements, 0, 0, 0, FunctionLiteral::kNoDuplicateParameters, FunctionLiteral::kAnonymousExpression, - FunctionLiteral::kShouldEagerCompile, scope->start_position(), true, + FunctionLiteral::kShouldEagerCompile, scope->start_position(), false, GetNextFunctionLiteralId()); } @@ -3285,13 +3198,15 @@ Expression* Parser::RewriteClassLiteral(Scope* block_scope, FunctionLiteral* static_fields_initializer = nullptr; if (class_info->has_static_class_fields) { static_fields_initializer = CreateInitializerFunction( - class_info->static_fields_scope, class_info->static_fields); + "<static_fields_initializer>", class_info->static_fields_scope, + class_info->static_fields); } FunctionLiteral* instance_fields_initializer_function = nullptr; if (class_info->has_instance_class_fields) { instance_fields_initializer_function = CreateInitializerFunction( - class_info->instance_fields_scope, class_info->instance_fields); + "<instance_fields_initializer>", class_info->instance_fields_scope, + class_info->instance_fields); class_info->constructor->set_requires_instance_fields_initializer(true); } @@ -3460,7 +3375,6 @@ void Parser::ParseOnBackground(ParseInfo* info) { // position set at the end of the script (the top scope and possible eval // scopes) and set their end position after we know the script length. if (info->is_toplevel()) { - fni_ = new (zone()) FuncNameInferrer(ast_value_factory(), zone()); result = DoParseProgram(/* isolate = */ nullptr, info); } else { result = @@ -3647,10 +3561,9 @@ void Parser::RewriteAsyncFunctionBody(ZonePtrList<Statement>* body, // }) // } - return_value = BuildResolvePromise(return_value, return_value->position()); - block->statements()->Add( - factory()->NewReturnStatement(return_value, return_value->position()), - zone()); + block->statements()->Add(factory()->NewAsyncReturnStatement( + return_value, return_value->position()), + zone()); block = BuildRejectPromiseOnException(block); body->Add(block, zone()); } diff --git a/deps/v8/src/parsing/parser.h b/deps/v8/src/parsing/parser.h index 00e73f37a2..35de0656d3 100644 --- a/deps/v8/src/parsing/parser.h +++ b/deps/v8/src/parsing/parser.h @@ -11,11 +11,11 @@ #include "src/ast/ast.h" #include "src/ast/scopes.h" #include "src/base/compiler-specific.h" +#include "src/base/threaded-list.h" #include "src/globals.h" #include "src/parsing/parser-base.h" #include "src/parsing/parsing.h" #include "src/parsing/preparser.h" -#include "src/utils.h" #include "src/zone/zone-chunk-list.h" namespace v8 { @@ -31,7 +31,7 @@ class ParserTargetScope; class PendingCompilationErrorHandler; class PreParsedScopeData; -class FunctionEntry BASE_EMBEDDED { +class FunctionEntry { public: enum { kStartPositionIndex, @@ -109,7 +109,7 @@ struct ParserFormalParameters : FormalParametersBase { explicit ParserFormalParameters(DeclarationScope* scope) : FormalParametersBase(scope) {} - ThreadedList<Parameter> params; + base::ThreadedList<Parameter> params; }; template <> @@ -135,12 +135,17 @@ struct ParserTypes<Parser> { typedef v8::internal::BreakableStatement* BreakableStatement; typedef v8::internal::ForStatement* ForStatement; typedef v8::internal::IterationStatement* IterationStatement; + typedef v8::internal::FuncNameInferrer FuncNameInferrer; + typedef v8::internal::SourceRange SourceRange; + typedef v8::internal::SourceRangeScope SourceRangeScope; // For constructing objects returned by the traversing functions. typedef AstNodeFactory Factory; typedef ParserTarget Target; typedef ParserTargetScope TargetScope; + + static constexpr bool ExpressionClassifierReportErrors = true; }; class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { @@ -155,6 +160,10 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { void ParseOnBackground(ParseInfo* info); + // Initializes an empty scope chain for top-level scripts, or scopes which + // consist of only the native context. + void InitializeEmptyScopeChain(ParseInfo* info); + // Deserialize the scope chain prior to parsing in which the script is going // to be executed. If the script is a top-level script, or the scope chain // consists of only a native context, maybe_outer_scope_info should be an @@ -172,7 +181,8 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { private: friend class ParserBase<Parser>; - friend class v8::internal::ExpressionClassifier<ParserTypes<Parser>>; + friend class v8::internal::ExpressionClassifierErrorTracker< + ParserTypes<Parser>>; friend bool v8::internal::parsing::ParseProgram(ParseInfo*, Isolate*); friend bool v8::internal::parsing::ParseFunction( ParseInfo*, Handle<SharedFunctionInfo> shared_info, Isolate*); @@ -185,7 +195,7 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { bool parse_lazily() const { return mode_ == PARSE_LAZILY; } enum Mode { PARSE_LAZILY, PARSE_EAGERLY }; - class ParsingModeScope BASE_EMBEDDED { + class ParsingModeScope { public: ParsingModeScope(Parser* parser, Mode mode) : parser_(parser), old_mode_(parser->mode_) { @@ -233,14 +243,12 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { ParseInfo* info, Zone* zone); - void StitchAst(ParseInfo* top_level_parse_info, Isolate* isolate); - PreParser* reusable_preparser() { if (reusable_preparser_ == nullptr) { - reusable_preparser_ = - new PreParser(zone(), &scanner_, stack_limit_, ast_value_factory(), - pending_error_handler(), runtime_call_stats_, logger_, - -1, parsing_module_, parsing_on_main_thread_); + reusable_preparser_ = new PreParser( + &preparser_zone_, &scanner_, stack_limit_, ast_value_factory(), + pending_error_handler(), runtime_call_stats_, logger_, -1, + parsing_module_, parsing_on_main_thread_); #define SET_ALLOW(name) reusable_preparser_->set_allow_##name(allow_##name()); SET_ALLOW(natives); SET_ALLOW(harmony_do_expressions); @@ -248,7 +256,6 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { SET_ALLOW(harmony_static_fields); SET_ALLOW(harmony_dynamic_import); SET_ALLOW(harmony_import_meta); - SET_ALLOW(harmony_bigint); SET_ALLOW(harmony_private_fields); SET_ALLOW(eval_cache); #undef SET_ALLOW @@ -315,7 +322,8 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { Variable* CreateSyntheticContextVariable(const AstRawString* synthetic_name, bool* ok); FunctionLiteral* CreateInitializerFunction( - DeclarationScope* scope, ZonePtrList<ClassLiteral::Property>* fields); + const char* name, DeclarationScope* scope, + ZonePtrList<ClassLiteral::Property>* fields); V8_INLINE Statement* DeclareClass(const AstRawString* variable_name, Expression* value, ZonePtrList<const AstRawString>* names, @@ -442,12 +450,19 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { // by parsing the function with PreParser. Consumes the ending }. // If may_abort == true, the (pre-)parser may decide to abort skipping // in order to force the function to be eagerly parsed, after all. - LazyParsingResult SkipFunction( - const AstRawString* function_name, FunctionKind kind, - FunctionLiteral::FunctionType function_type, - DeclarationScope* function_scope, int* num_parameters, - ProducedPreParsedScopeData** produced_preparsed_scope_data, - bool is_inner_function, bool may_abort, bool* ok); + // In case the preparser detects an error it cannot identify, it resets the + // scanner- and preparser state to the initial one, before PreParsing the + // function. + // SkipFunction returns true if it correctly parsed the function, including + // cases where we detect an error. It returns false, if we needed to stop + // parsing or could not identify an error correctly, meaning the caller needs + // to fully reparse. In this case it resets the scanner and preparser state. + bool SkipFunction(const AstRawString* function_name, FunctionKind kind, + FunctionLiteral::FunctionType function_type, + DeclarationScope* function_scope, int* num_parameters, + ProducedPreParsedScopeData** produced_preparsed_scope_data, + bool is_inner_function, bool may_abort, + FunctionLiteral::EagerCompileHint* hint, bool* ok); Block* BuildParameterInitializationBlock( const ParserFormalParameters& parameters, bool* ok); @@ -532,7 +547,6 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { Expression* BuildInitialYield(int pos, FunctionKind kind); Assignment* BuildCreateJSGeneratorObject(int pos, FunctionKind kind); - Expression* BuildResolvePromise(Expression* value, int pos); Expression* BuildRejectPromise(Expression* value, int pos); Variable* PromiseVariable(); Variable* AsyncGeneratorAwaitVariable(); @@ -662,38 +676,30 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { // Functions for encapsulating the differences between parsing and preparsing; // operations interleaved with the recursive descent. V8_INLINE void PushLiteralName(const AstRawString* id) { - DCHECK_NOT_NULL(fni_); - fni_->PushLiteralName(id); + fni_.PushLiteralName(id); } V8_INLINE void PushVariableName(const AstRawString* id) { - DCHECK_NOT_NULL(fni_); - fni_->PushVariableName(id); + fni_.PushVariableName(id); } V8_INLINE void PushPropertyName(Expression* expression) { - DCHECK_NOT_NULL(fni_); if (expression->IsPropertyName()) { - fni_->PushLiteralName(expression->AsLiteral()->AsRawPropertyName()); + fni_.PushLiteralName(expression->AsLiteral()->AsRawPropertyName()); } else { - fni_->PushLiteralName(ast_value_factory()->anonymous_function_string()); + fni_.PushLiteralName(ast_value_factory()->anonymous_function_string()); } } V8_INLINE void PushEnclosingName(const AstRawString* name) { - DCHECK_NOT_NULL(fni_); - fni_->PushEnclosingName(name); + fni_.PushEnclosingName(name); } V8_INLINE void AddFunctionForNameInference(FunctionLiteral* func_to_infer) { - DCHECK_NOT_NULL(fni_); - fni_->AddFunction(func_to_infer); + fni_.AddFunction(func_to_infer); } - V8_INLINE void InferFunctionName() { - DCHECK_NOT_NULL(fni_); - fni_->Infer(); - } + V8_INLINE void InferFunctionName() { fni_.Infer(); } // If we assign a function literal to a property we pretenure the // literal so it can be added as a constant function property. @@ -784,6 +790,10 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { arg, error_type); } + // Dummy implementation. The parser should never have a unidentifiable + // error. + V8_INLINE void ReportUnidentifiableError() { UNREACHABLE(); } + void ReportMessageAt(Scanner::Location source_location, MessageTemplate::Template message, const AstRawString* arg, @@ -856,14 +866,14 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { const AstRawString* name, int start_position, InferName infer = InferName::kYes) { if (infer == InferName::kYes) { - fni_->PushVariableName(name); + fni_.PushVariableName(name); } return NewUnresolved(name, start_position); } V8_INLINE Expression* ExpressionFromString(int pos) { const AstRawString* symbol = GetSymbol(); - fni_->PushLiteralName(symbol); + fni_.PushLiteralName(symbol); return factory()->NewStringLiteral(symbol, pos); } @@ -891,18 +901,6 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { factory()->NewThrow(exception, pos), pos); } - V8_INLINE void AddParameterInitializationBlock( - const ParserFormalParameters& parameters, ZonePtrList<Statement>* body, - bool is_async, bool* ok) { - if (parameters.is_simple) return; - auto* init_block = BuildParameterInitializationBlock(parameters, ok); - if (!*ok) return; - if (is_async) { - init_block = BuildRejectPromiseOnException(init_block); - } - body->Add(init_block, zone()); - } - V8_INLINE void AddFormalParameter(ParserFormalParameters* parameters, Expression* pattern, Expression* initializer, @@ -923,7 +921,7 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { V8_INLINE void DeclareFormalParameters( DeclarationScope* scope, - const ThreadedList<ParserFormalParameters::Parameter>& parameters, + const base::ThreadedList<ParserFormalParameters::Parameter>& parameters, bool is_simple, bool* has_duplicate = nullptr) { if (!is_simple) scope->SetHasNonSimpleParameters(); for (auto parameter : parameters) { @@ -958,7 +956,7 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { void SetFunctionNameFromIdentifierRef(Expression* value, Expression* identifier); - V8_INLINE ZoneVector<typename ExpressionClassifier::Error>* + V8_INLINE ZoneList<typename ExpressionClassifier::Error>* GetReportedErrorList() const { return function_state_->GetReportedErrorList(); } @@ -1094,11 +1092,10 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { } // Parser's private field members. - friend class DiscardableZoneScope; // Uses reusable_preparser_. - // FIXME(marja): Make reusable_preparser_ always use its own temp Zone (call - // DeleteAll after each function), so this won't be needed. + friend class PreParserZoneScope; // Uses reusable_preparser(). Scanner scanner_; + Zone preparser_zone_; PreParser* reusable_preparser_; Mode mode_; @@ -1131,7 +1128,7 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { // 'continue' statement targets). Upon construction, a new target is // added; it is removed upon destruction. -class ParserTarget BASE_EMBEDDED { +class ParserTarget { public: ParserTarget(ParserBase<Parser>* parser, BreakableStatement* statement) : variable_(&parser->impl()->target_stack_), @@ -1151,7 +1148,7 @@ class ParserTarget BASE_EMBEDDED { ParserTarget* previous_; }; -class ParserTargetScope BASE_EMBEDDED { +class ParserTargetScope { public: explicit ParserTargetScope(ParserBase<Parser>* parser) : variable_(&parser->impl()->target_stack_), diff --git a/deps/v8/src/parsing/pattern-rewriter.cc b/deps/v8/src/parsing/pattern-rewriter.cc index ed3231c151..4465670a8f 100644 --- a/deps/v8/src/parsing/pattern-rewriter.cc +++ b/deps/v8/src/parsing/pattern-rewriter.cc @@ -31,38 +31,32 @@ class PatternRewriter final : public AstVisitor<PatternRewriter> { const Parser::DeclarationParsingResult::Declaration* declaration, ZonePtrList<const AstRawString>* names, bool* ok); - static void RewriteDestructuringAssignment(Parser* parser, - RewritableExpression* to_rewrite, - Scope* scope); + static Expression* RewriteDestructuringAssignment(Parser* parser, + Assignment* to_rewrite, + Scope* scope); private: - enum PatternContext { BINDING, ASSIGNMENT, ASSIGNMENT_ELEMENT }; - - class AssignmentElementScope { - public: - explicit AssignmentElementScope(PatternRewriter* rewriter) - : rewriter_(rewriter), context_(rewriter->context()) { - if (context_ == ASSIGNMENT) rewriter->context_ = ASSIGNMENT_ELEMENT; - } - ~AssignmentElementScope() { rewriter_->context_ = context_; } - - private: - PatternRewriter* const rewriter_; - const PatternContext context_; - }; - - PatternRewriter(Scope* scope, Parser* parser, PatternContext context) + enum PatternContext : uint8_t { BINDING, ASSIGNMENT }; + + PatternRewriter(Scope* scope, Parser* parser, PatternContext context, + const DeclarationDescriptor* descriptor = nullptr, + ZonePtrList<const AstRawString>* names = nullptr, + int initializer_position = kNoSourcePosition, + int value_beg_position = kNoSourcePosition, + bool declares_parameter_containing_sloppy_eval = false) : scope_(scope), parser_(parser), - context_(context), - initializer_position_(kNoSourcePosition), - value_beg_position_(kNoSourcePosition), block_(nullptr), - descriptor_(nullptr), - names_(nullptr), + descriptor_(descriptor), + names_(names), current_value_(nullptr), - recursion_level_(0), - ok_(nullptr) {} + ok_(nullptr), + initializer_position_(initializer_position), + value_beg_position_(value_beg_position), + context_(context), + declares_parameter_containing_sloppy_eval_( + declares_parameter_containing_sloppy_eval), + recursion_level_(0) {} #define DECLARE_VISIT(type) void Visit##type(v8::internal::type* node); // Visiting functions for AST nodes make this an AstVisitor. @@ -80,16 +74,34 @@ class PatternRewriter final : public AstVisitor<PatternRewriter> { current_value_ = old_value; } + Expression* Rewrite(Assignment* assign) { + DCHECK_EQ(Token::ASSIGN, assign->op()); + + int pos = assign->position(); + DCHECK_NULL(block_); + block_ = factory()->NewBlock(8, true); + Variable* temp = nullptr; + Expression* pattern = assign->target(); + Expression* old_value = current_value_; + current_value_ = assign->value(); + if (pattern->IsObjectLiteral()) { + VisitObjectLiteral(pattern->AsObjectLiteral(), &temp); + } else { + DCHECK(pattern->IsArrayLiteral()); + VisitArrayLiteral(pattern->AsArrayLiteral(), &temp); + } + DCHECK_NOT_NULL(temp); + current_value_ = old_value; + return factory()->NewDoExpression(block_, temp, pos); + } + void VisitObjectLiteral(ObjectLiteral* node, Variable** temp_var); void VisitArrayLiteral(ArrayLiteral* node, Variable** temp_var); bool IsBindingContext() const { return context_ == BINDING; } - bool IsAssignmentContext() const { - return context_ == ASSIGNMENT || context_ == ASSIGNMENT_ELEMENT; - } + bool IsAssignmentContext() const { return context_ == ASSIGNMENT; } bool IsSubPattern() const { return recursion_level_ > 1; } - bool DeclaresParameterContainingSloppyEval() const; void RewriteParameterScopes(Expression* expr); Variable* CreateTempVar(Expression* value = nullptr); @@ -103,15 +115,16 @@ class PatternRewriter final : public AstVisitor<PatternRewriter> { Scope* const scope_; Parser* const parser_; - PatternContext context_; - int initializer_position_; - int value_beg_position_; Block* block_; const DeclarationDescriptor* descriptor_; ZonePtrList<const AstRawString>* names_; Expression* current_value_; - int recursion_level_; bool* ok_; + const int initializer_position_; + const int value_beg_position_; + PatternContext context_; + const bool declares_parameter_containing_sloppy_eval_ : 1; + int recursion_level_; DEFINE_AST_VISITOR_MEMBERS_WITHOUT_STACKOVERFLOW() }; @@ -125,15 +138,18 @@ void Parser::DeclareAndInitializeVariables( } void Parser::RewriteDestructuringAssignment(RewritableExpression* to_rewrite) { - PatternRewriter::RewriteDestructuringAssignment(this, to_rewrite, scope()); + DCHECK(!to_rewrite->is_rewritten()); + Assignment* assignment = to_rewrite->expression()->AsAssignment(); + Expression* result = PatternRewriter::RewriteDestructuringAssignment( + this, assignment, scope()); + to_rewrite->Rewrite(result); } Expression* Parser::RewriteDestructuringAssignment(Assignment* assignment) { DCHECK_NOT_NULL(assignment); DCHECK_EQ(Token::ASSIGN, assignment->op()); - auto to_rewrite = factory()->NewRewritableExpression(assignment, scope()); - RewriteDestructuringAssignment(to_rewrite); - return to_rewrite->expression(); + return PatternRewriter::RewriteDestructuringAssignment(this, assignment, + scope()); } void PatternRewriter::DeclareAndInitializeVariables( @@ -143,25 +159,26 @@ void PatternRewriter::DeclareAndInitializeVariables( ZonePtrList<const AstRawString>* names, bool* ok) { DCHECK(block->ignore_completion_value()); - PatternRewriter rewriter(declaration_descriptor->scope, parser, BINDING); - rewriter.initializer_position_ = declaration->initializer_position; - rewriter.value_beg_position_ = declaration->value_beg_position; + Scope* scope = declaration_descriptor->scope; + PatternRewriter rewriter(scope, parser, BINDING, declaration_descriptor, + names, declaration->initializer_position, + declaration->value_beg_position, + declaration_descriptor->declaration_kind == + DeclarationDescriptor::PARAMETER && + scope->is_block_scope()); rewriter.block_ = block; - rewriter.descriptor_ = declaration_descriptor; - rewriter.names_ = names; rewriter.ok_ = ok; rewriter.RecurseIntoSubpattern(declaration->pattern, declaration->initializer); } -void PatternRewriter::RewriteDestructuringAssignment( - Parser* parser, RewritableExpression* to_rewrite, Scope* scope) { +Expression* PatternRewriter::RewriteDestructuringAssignment( + Parser* parser, Assignment* to_rewrite, Scope* scope) { DCHECK(!scope->HasBeenRemoved()); - DCHECK(!to_rewrite->is_rewritten()); PatternRewriter rewriter(scope, parser, ASSIGNMENT); - rewriter.RecurseIntoSubpattern(to_rewrite, nullptr); + return rewriter.Rewrite(to_rewrite); } void PatternRewriter::VisitVariableProxy(VariableProxy* pattern) { @@ -181,7 +198,16 @@ void PatternRewriter::VisitVariableProxy(VariableProxy* pattern) { DCHECK_NOT_NULL(descriptor_); DCHECK_NOT_NULL(ok_); - descriptor_->scope->RemoveUnresolved(pattern); + Scope* outer_function_scope = nullptr; + bool success; + if (declares_parameter_containing_sloppy_eval_) { + outer_function_scope = scope()->outer_scope(); + success = outer_function_scope->RemoveUnresolved(pattern); + } else { + success = scope()->RemoveUnresolved(pattern); + } + USE(success); + DCHECK(success); // Declare variable. // Note that we *always* must treat the initial value via a separate init @@ -192,15 +218,13 @@ void PatternRewriter::VisitVariableProxy(VariableProxy* pattern) { // an initial value in the declaration (because they are initialized upon // entering the function). const AstRawString* name = pattern->raw_name(); - VariableProxy* proxy = - factory()->NewVariableProxy(name, NORMAL_VARIABLE, pattern->position()); + VariableProxy* proxy = pattern; Declaration* declaration; if (descriptor_->mode == VariableMode::kVar && - !descriptor_->scope->is_declaration_scope()) { - DCHECK(descriptor_->scope->is_block_scope() || - descriptor_->scope->is_with_scope()); + !scope()->is_declaration_scope()) { + DCHECK(scope()->is_block_scope() || scope()->is_with_scope()); declaration = factory()->NewNestedVariableDeclaration( - proxy, descriptor_->scope, descriptor_->declaration_pos); + proxy, scope(), descriptor_->declaration_pos); } else { declaration = factory()->NewVariableDeclaration(proxy, descriptor_->declaration_pos); @@ -210,10 +234,6 @@ void PatternRewriter::VisitVariableProxy(VariableProxy* pattern) { // a sloppy eval in a default parameter or function body, the parameter // needs to be declared in the function's scope, not in the varblock // scope which will be used for the initializer expression. - Scope* outer_function_scope = nullptr; - if (DeclaresParameterContainingSloppyEval()) { - outer_function_scope = descriptor_->scope->outer_scope(); - } Variable* var = parser_->Declare( declaration, descriptor_->declaration_kind, descriptor_->mode, Variable::DefaultInitializationFlag(descriptor_->mode), ok_, @@ -224,12 +244,11 @@ void PatternRewriter::VisitVariableProxy(VariableProxy* pattern) { DCHECK_NE(initializer_position_, kNoSourcePosition); var->set_initializer_position(initializer_position_); - Scope* declaration_scope = - outer_function_scope != nullptr - ? outer_function_scope - : (IsLexicalVariableMode(descriptor_->mode) - ? descriptor_->scope - : descriptor_->scope->GetDeclarationScope()); + Scope* declaration_scope = outer_function_scope != nullptr + ? outer_function_scope + : (IsLexicalVariableMode(descriptor_->mode) + ? scope() + : scope()->GetDeclarationScope()); if (declaration_scope->num_var() > kMaxNumFunctionLocals) { parser_->ReportMessage(MessageTemplate::kTooManyVariables); *ok_ = false; @@ -242,7 +261,7 @@ void PatternRewriter::VisitVariableProxy(VariableProxy* pattern) { // If there's no initializer, we're done. if (value == nullptr) return; - Scope* var_init_scope = descriptor_->scope; + Scope* var_init_scope = scope(); Parser::MarkLoopVariableAsAssigned(var_init_scope, proxy->var(), descriptor_->declaration_kind); @@ -254,15 +273,15 @@ void PatternRewriter::VisitVariableProxy(VariableProxy* pattern) { // // var v; v = x; // - // In particular, we need to re-lookup 'v' as it may be a different - // 'v' than the 'v' in the declaration (e.g., if we are inside a - // 'with' statement or 'catch' block). Global var declarations - // also need special treatment. - - // For 'let' and 'const' declared variables the initialization always - // assigns to the declared variable. - // But for var declarations we need to do a new lookup. - if (descriptor_->mode == VariableMode::kVar) { + // In particular, we need to re-lookup 'v' if it may be a different 'v' than + // the 'v' in the declaration (e.g., if we are inside a 'with' statement or + // 'catch' block). + + // For 'let' and 'const' declared variables the initialization always assigns + // to the declared variable. But for var declarations that target a different + // scope we need to do a new lookup. + if (descriptor_->mode == VariableMode::kVar && + var_init_scope != declaration_scope) { proxy = var_init_scope->NewUnresolved(factory(), name); } else { DCHECK_NOT_NULL(proxy); @@ -294,64 +313,13 @@ Variable* PatternRewriter::CreateTempVar(Expression* value) { } void PatternRewriter::VisitRewritableExpression(RewritableExpression* node) { - if (!node->expression()->IsAssignment()) { - // RewritableExpressions are also used for desugaring Spread, which is - // orthogonal to PatternRewriter; just visit the underlying expression. - DCHECK_EQ(AstNode::kArrayLiteral, node->expression()->node_type()); - return Visit(node->expression()); - } else if (context() != ASSIGNMENT) { - // This is not a destructuring assignment. Mark the node as rewritten to - // prevent redundant rewriting and visit the underlying expression. - DCHECK(!node->is_rewritten()); - node->set_rewritten(); - return Visit(node->expression()); - } - + DCHECK(node->expression()->IsAssignment()); + // This is not a top-level destructuring assignment. Mark the node as + // rewritten to prevent redundant rewriting and visit the underlying + // expression. DCHECK(!node->is_rewritten()); - DCHECK_EQ(ASSIGNMENT, context()); - Assignment* assign = node->expression()->AsAssignment(); - DCHECK_NOT_NULL(assign); - DCHECK_EQ(Token::ASSIGN, assign->op()); - - int pos = assign->position(); - Block* old_block = block_; - block_ = factory()->NewBlock(8, true); - Variable* temp = nullptr; - Expression* pattern = assign->target(); - Expression* old_value = current_value_; - current_value_ = assign->value(); - if (pattern->IsObjectLiteral()) { - VisitObjectLiteral(pattern->AsObjectLiteral(), &temp); - } else { - DCHECK(pattern->IsArrayLiteral()); - VisitArrayLiteral(pattern->AsArrayLiteral(), &temp); - } - DCHECK_NOT_NULL(temp); - current_value_ = old_value; - Expression* expr = factory()->NewDoExpression(block_, temp, pos); - node->Rewrite(expr); - block_ = old_block; - if (block_) { - block_->statements()->Add(factory()->NewExpressionStatement(expr, pos), - zone()); - } -} - -bool PatternRewriter::DeclaresParameterContainingSloppyEval() const { - // Need to check for a binding context to make sure we have a descriptor. - if (IsBindingContext() && - // Only relevant for parameters. - descriptor_->declaration_kind == DeclarationDescriptor::PARAMETER && - // And only when scope is a block scope; - // without eval, it is a function scope. - scope()->is_block_scope()) { - DCHECK(scope()->is_declaration_scope()); - DCHECK(scope()->AsDeclarationScope()->calls_sloppy_eval()); - DCHECK(scope()->outer_scope()->is_function_scope()); - return true; - } - - return false; + node->set_rewritten(); + return Visit(node->expression()); } // When an extra declaration scope needs to be inserted to account for @@ -359,7 +327,7 @@ bool PatternRewriter::DeclaresParameterContainingSloppyEval() const { // needs to be in that new inner scope which was added after initial // parsing. void PatternRewriter::RewriteParameterScopes(Expression* expr) { - if (DeclaresParameterContainingSloppyEval()) { + if (declares_parameter_containing_sloppy_eval_) { ReparentExpressionScope(parser_->stack_limit(), expr, scope()); } } @@ -428,7 +396,6 @@ void PatternRewriter::VisitObjectLiteral(ObjectLiteral* pattern, kNoSourcePosition); } - AssignmentElementScope element_scope(this); RecurseIntoSubpattern(property->value(), value); } } @@ -557,10 +524,7 @@ void PatternRewriter::VisitArrayLiteral(ArrayLiteral* node, factory()->NewExpressionStatement(assignment, nopos), zone()); } - { - AssignmentElementScope element_scope(this); - RecurseIntoSubpattern(value, factory()->NewVariableProxy(v)); - } + RecurseIntoSubpattern(value, factory()->NewVariableProxy(v)); { // completion = kNormalCompletion; @@ -709,10 +673,6 @@ void PatternRewriter::VisitAssignment(Assignment* node) { // <pattern> = temp === undefined ? <init> : temp; DCHECK_EQ(Token::ASSIGN, node->op()); - // Rewriting of Assignment nodes for destructuring assignment - // is handled in VisitRewritableExpression(). - DCHECK_NE(ASSIGNMENT, context()); - auto initializer = node->value(); auto value = initializer; auto temp = CreateTempVar(current_value_); diff --git a/deps/v8/src/parsing/preparsed-scope-data-impl.h b/deps/v8/src/parsing/preparsed-scope-data-impl.h new file mode 100644 index 0000000000..e2d31c07d5 --- /dev/null +++ b/deps/v8/src/parsing/preparsed-scope-data-impl.h @@ -0,0 +1,259 @@ +// Copyright 2018 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_PREPARSED_SCOPE_DATA_IMPL_H_ +#define V8_PARSING_PREPARSED_SCOPE_DATA_IMPL_H_ + +#include "src/parsing/preparsed-scope-data.h" + +#include "src/assert-scope.h" + +namespace v8 { +namespace internal { + +// Classes which are internal to prepared-scope-data.cc, but are exposed in +// a header for tests. + +struct PreParsedScopeByteDataConstants { +#ifdef DEBUG + static constexpr int kMagicValue = 0xC0DE0DE; + + static constexpr size_t kUint32Size = 5; + static constexpr size_t kUint8Size = 2; + static constexpr size_t kQuarterMarker = 0; + static constexpr size_t kPlaceholderSize = kUint32Size; +#else + static constexpr size_t kUint32Size = 4; + static constexpr size_t kUint8Size = 1; + static constexpr size_t kPlaceholderSize = 0; +#endif + + static const size_t kSkippableFunctionDataSize = + 4 * kUint32Size + 1 * kUint8Size; +}; + +class PreParsedScopeDataBuilder::ByteData + : public ZoneObject, + public PreParsedScopeByteDataConstants { + public: + explicit ByteData(Zone* zone) + : backing_store_(zone), free_quarters_in_last_byte_(0) {} + + void WriteUint32(uint32_t data); + void WriteUint8(uint8_t data); + void WriteQuarter(uint8_t data); + +#ifdef DEBUG + // For overwriting previously written data at position 0. + void OverwriteFirstUint32(uint32_t data); +#endif + + Handle<PodArray<uint8_t>> Serialize(Isolate* isolate); + + size_t size() const { return backing_store_.size(); } + + ZoneChunkList<uint8_t>::iterator begin() { return backing_store_.begin(); } + + ZoneChunkList<uint8_t>::iterator end() { return backing_store_.end(); } + + private: + ZoneChunkList<uint8_t> backing_store_; + uint8_t free_quarters_in_last_byte_; +}; + +template <class Data> +class BaseConsumedPreParsedScopeData : public ConsumedPreParsedScopeData { + public: + class ByteData : public PreParsedScopeByteDataConstants { + public: + ByteData() + : data_(nullptr), index_(0), stored_quarters_(0), stored_byte_(0) {} + + // Reading from the ByteData is only allowed when a ReadingScope is on the + // stack. This ensures that we have a DisallowHeapAllocation in place + // whenever ByteData holds a raw pointer into the heap. + class ReadingScope { + public: + ReadingScope(ByteData* consumed_data, Data* data) + : consumed_data_(consumed_data) { + consumed_data->data_ = data; + } + explicit ReadingScope(BaseConsumedPreParsedScopeData<Data>* parent) + : ReadingScope(parent->scope_data_.get(), parent->GetScopeData()) {} + ~ReadingScope() { consumed_data_->data_ = nullptr; } + + private: + ByteData* consumed_data_; + DisallowHeapAllocation no_gc; + }; + + void SetPosition(int position) { index_ = position; } + + size_t RemainingBytes() const { + DCHECK_NOT_NULL(data_); + return data_->length() - index_; + } + + int32_t ReadUint32() { + DCHECK_NOT_NULL(data_); + DCHECK_GE(RemainingBytes(), kUint32Size); +#ifdef DEBUG + // Check that there indeed is an integer following. + DCHECK_EQ(data_->get(index_++), kUint32Size); +#endif + int32_t result = 0; + byte* p = reinterpret_cast<byte*>(&result); + for (int i = 0; i < 4; ++i) { + *p++ = data_->get(index_++); + } + stored_quarters_ = 0; + return result; + } + + uint8_t ReadUint8() { + DCHECK_NOT_NULL(data_); + DCHECK_GE(RemainingBytes(), kUint8Size); +#ifdef DEBUG + // Check that there indeed is a byte following. + DCHECK_EQ(data_->get(index_++), kUint8Size); +#endif + stored_quarters_ = 0; + return data_->get(index_++); + } + + uint8_t ReadQuarter() { + DCHECK_NOT_NULL(data_); + if (stored_quarters_ == 0) { + DCHECK_GE(RemainingBytes(), kUint8Size); +#ifdef DEBUG + // Check that there indeed are quarters following. + DCHECK_EQ(data_->get(index_++), kQuarterMarker); +#endif + stored_byte_ = data_->get(index_++); + stored_quarters_ = 4; + } + // Read the first 2 bits from stored_byte_. + uint8_t result = (stored_byte_ >> 6) & 3; + DCHECK_LE(result, 3); + --stored_quarters_; + stored_byte_ <<= 2; + return result; + } + + private: + Data* data_; + int index_; + uint8_t stored_quarters_; + uint8_t stored_byte_; + }; + + BaseConsumedPreParsedScopeData() + : scope_data_(new ByteData()), child_index_(0) {} + + virtual Data* GetScopeData() = 0; + + virtual ProducedPreParsedScopeData* GetChildData(Zone* zone, + int child_index) = 0; + + ProducedPreParsedScopeData* GetDataForSkippableFunction( + Zone* zone, int start_position, int* end_position, int* num_parameters, + int* num_inner_functions, bool* uses_super_property, + LanguageMode* language_mode) final; + + void RestoreScopeAllocationData(DeclarationScope* scope) final; + +#ifdef DEBUG + void VerifyDataStart(); +#endif + + private: + void RestoreData(Scope* scope); + void RestoreDataForVariable(Variable* var); + void RestoreDataForInnerScopes(Scope* scope); + + std::unique_ptr<ByteData> scope_data_; + // When consuming the data, these indexes point to the data we're going to + // consume next. + int child_index_; + + DISALLOW_COPY_AND_ASSIGN(BaseConsumedPreParsedScopeData); +}; + +// Implementation of ConsumedPreParsedScopeData for on-heap data. +class OnHeapConsumedPreParsedScopeData final + : public BaseConsumedPreParsedScopeData<PodArray<uint8_t>> { + public: + OnHeapConsumedPreParsedScopeData(Isolate* isolate, + Handle<PreParsedScopeData> data); + + PodArray<uint8_t>* GetScopeData() final; + ProducedPreParsedScopeData* GetChildData(Zone* zone, int child_index) final; + + private: + Isolate* isolate_; + Handle<PreParsedScopeData> data_; +}; + +// Wraps a ZoneVector<uint8_t> to have with functions named the same as +// PodArray<uint8_t>. +class ZoneVectorWrapper { + public: + explicit ZoneVectorWrapper(ZoneVector<uint8_t>* data) : data_(data) {} + + int length() const { return static_cast<int>(data_->size()); } + + uint8_t get(int index) const { return data_->at(index); } + + private: + ZoneVector<uint8_t>* data_; + + DISALLOW_COPY_AND_ASSIGN(ZoneVectorWrapper); +}; + +// A serialized PreParsedScopeData in zone memory (as apposed to being on-heap). +class ZonePreParsedScopeData : public ZoneObject { + public: + ZonePreParsedScopeData(Zone* zone, + ZoneChunkList<uint8_t>::iterator byte_data_begin, + ZoneChunkList<uint8_t>::iterator byte_data_end, + int child_length); + + Handle<PreParsedScopeData> Serialize(Isolate* isolate); + + int child_length() const { return static_cast<int>(children_.size()); } + + ZonePreParsedScopeData* get_child(int index) { return children_[index]; } + + void set_child(int index, ZonePreParsedScopeData* child) { + children_[index] = child; + } + + ZoneVector<uint8_t>* byte_data() { return &byte_data_; } + + private: + ZoneVector<uint8_t> byte_data_; + ZoneVector<ZonePreParsedScopeData*> children_; + + DISALLOW_COPY_AND_ASSIGN(ZonePreParsedScopeData); +}; + +// Implementation of ConsumedPreParsedScopeData for PreParsedScopeData +// serialized into zone memory. +class ZoneConsumedPreParsedScopeData final + : public BaseConsumedPreParsedScopeData<ZoneVectorWrapper> { + public: + ZoneConsumedPreParsedScopeData(Zone* zone, ZonePreParsedScopeData* data); + + ZoneVectorWrapper* GetScopeData() final; + ProducedPreParsedScopeData* GetChildData(Zone* zone, int child_index) final; + + private: + ZonePreParsedScopeData* data_; + ZoneVectorWrapper scope_data_wrapper_; +}; + +} // namespace internal +} // namespace v8 + +#endif // V8_PARSING_PREPARSED_SCOPE_DATA_IMPL_H_ diff --git a/deps/v8/src/parsing/preparsed-scope-data.cc b/deps/v8/src/parsing/preparsed-scope-data.cc index 90e8819e32..9d61740753 100644 --- a/deps/v8/src/parsing/preparsed-scope-data.cc +++ b/deps/v8/src/parsing/preparsed-scope-data.cc @@ -4,11 +4,14 @@ #include "src/parsing/preparsed-scope-data.h" +#include <vector> + #include "src/ast/scopes.h" #include "src/ast/variables.h" #include "src/handles.h" #include "src/objects-inl.h" #include "src/objects/shared-function-info.h" +#include "src/parsing/preparsed-scope-data-impl.h" #include "src/parsing/preparser.h" namespace v8 { @@ -24,22 +27,6 @@ class VariableMaybeAssignedField : public BitField8<bool, 0, 1> {}; class VariableContextAllocatedField : public BitField8<bool, VariableMaybeAssignedField::kNext, 1> {}; - -#ifdef DEBUG -const int kMagicValue = 0xC0DE0DE; - -const size_t kUint32Size = 5; -const size_t kUint8Size = 2; -const size_t kQuarterMarker = 0; -const size_t kPlaceholderSize = kUint32Size; -#else -const size_t kUint32Size = 4; -const size_t kUint8Size = 1; -const size_t kPlaceholderSize = 0; -#endif - -const size_t kSkippableFunctionDataSize = 4 * kUint32Size + 1 * kUint8Size; - class LanguageField : public BitField8<LanguageMode, 0, 1> {}; class UsesSuperField : public BitField8<bool, LanguageField::kNext, 1> {}; STATIC_ASSERT(LanguageModeSize <= LanguageField::kNumValues); @@ -48,7 +35,7 @@ STATIC_ASSERT(LanguageModeSize <= LanguageField::kNumValues); /* - Internal data format for the backing store of ProducedPreparsedScopeData and + Internal data format for the backing store of PreParsedScopeDataBuilder and PreParsedScopeData::scope_data (on the heap): (Skippable function data:) @@ -91,7 +78,7 @@ STATIC_ASSERT(LanguageModeSize <= LanguageField::kNumValues); */ -void ProducedPreParsedScopeData::ByteData::WriteUint32(uint32_t data) { +void PreParsedScopeDataBuilder::ByteData::WriteUint32(uint32_t data) { #ifdef DEBUG // Save expected item size in debug mode. backing_store_.push_back(kUint32Size); @@ -104,7 +91,7 @@ void ProducedPreParsedScopeData::ByteData::WriteUint32(uint32_t data) { } #ifdef DEBUG -void ProducedPreParsedScopeData::ByteData::OverwriteFirstUint32(uint32_t data) { +void PreParsedScopeDataBuilder::ByteData::OverwriteFirstUint32(uint32_t data) { auto it = backing_store_.begin(); // Check that that position already holds an item of the expected size. DCHECK_GE(backing_store_.size(), kUint32Size); @@ -117,7 +104,7 @@ void ProducedPreParsedScopeData::ByteData::OverwriteFirstUint32(uint32_t data) { } #endif -void ProducedPreParsedScopeData::ByteData::WriteUint8(uint8_t data) { +void PreParsedScopeDataBuilder::ByteData::WriteUint8(uint8_t data) { #ifdef DEBUG // Save expected item size in debug mode. backing_store_.push_back(kUint8Size); @@ -126,7 +113,7 @@ void ProducedPreParsedScopeData::ByteData::WriteUint8(uint8_t data) { free_quarters_in_last_byte_ = 0; } -void ProducedPreParsedScopeData::ByteData::WriteQuarter(uint8_t data) { +void PreParsedScopeDataBuilder::ByteData::WriteQuarter(uint8_t data) { DCHECK_LE(data, 3); if (free_quarters_in_last_byte_ == 0) { #ifdef DEBUG @@ -144,7 +131,7 @@ void ProducedPreParsedScopeData::ByteData::WriteQuarter(uint8_t data) { backing_store_.back() |= (data << shift_amount); } -Handle<PodArray<uint8_t>> ProducedPreParsedScopeData::ByteData::Serialize( +Handle<PodArray<uint8_t>> PreParsedScopeDataBuilder::ByteData::Serialize( Isolate* isolate) { Handle<PodArray<uint8_t>> array = PodArray<uint8_t>::New( isolate, static_cast<int>(backing_store_.size()), TENURED); @@ -159,12 +146,13 @@ Handle<PodArray<uint8_t>> ProducedPreParsedScopeData::ByteData::Serialize( return array; } -ProducedPreParsedScopeData::ProducedPreParsedScopeData( - Zone* zone, ProducedPreParsedScopeData* parent) +PreParsedScopeDataBuilder::PreParsedScopeDataBuilder( + Zone* zone, PreParsedScopeDataBuilder* parent) : parent_(parent), byte_data_(new (zone) ByteData(zone)), data_for_inner_functions_(zone), bailed_out_(false) { + DCHECK(FLAG_preparser_scope_analysis); if (parent != nullptr) { parent->data_for_inner_functions_.push_back(this); } @@ -174,59 +162,43 @@ ProducedPreParsedScopeData::ProducedPreParsedScopeData( #endif } -// Create a ProducedPreParsedScopeData which is just a proxy for a previous -// produced PreParsedScopeData. -ProducedPreParsedScopeData::ProducedPreParsedScopeData( - Handle<PreParsedScopeData> data, Zone* zone) - : parent_(nullptr), - byte_data_(nullptr), - data_for_inner_functions_(zone), - bailed_out_(false), - previously_produced_preparsed_scope_data_(data) {} - -ProducedPreParsedScopeData::DataGatheringScope::DataGatheringScope( +PreParsedScopeDataBuilder::DataGatheringScope::DataGatheringScope( DeclarationScope* function_scope, PreParser* preparser) : function_scope_(function_scope), preparser_(preparser), - produced_preparsed_scope_data_(nullptr) { + builder_(nullptr) { if (FLAG_preparser_scope_analysis) { - ProducedPreParsedScopeData* parent = - preparser->produced_preparsed_scope_data(); + PreParsedScopeDataBuilder* parent = + preparser->preparsed_scope_data_builder(); Zone* main_zone = preparser->main_zone(); - produced_preparsed_scope_data_ = - new (main_zone) ProducedPreParsedScopeData(main_zone, parent); - preparser->set_produced_preparsed_scope_data( - produced_preparsed_scope_data_); - function_scope->set_produced_preparsed_scope_data( - produced_preparsed_scope_data_); + builder_ = new (main_zone) PreParsedScopeDataBuilder(main_zone, parent); + preparser->set_preparsed_scope_data_builder(builder_); + function_scope->set_preparsed_scope_data_builder(builder_); } } -ProducedPreParsedScopeData::DataGatheringScope::~DataGatheringScope() { - if (FLAG_preparser_scope_analysis) { - preparser_->set_produced_preparsed_scope_data( - produced_preparsed_scope_data_->parent_); +PreParsedScopeDataBuilder::DataGatheringScope::~DataGatheringScope() { + if (builder_) { + preparser_->set_preparsed_scope_data_builder(builder_->parent_); } } -void ProducedPreParsedScopeData::DataGatheringScope::MarkFunctionAsSkippable( +void PreParsedScopeDataBuilder::DataGatheringScope::MarkFunctionAsSkippable( int end_position, int num_inner_functions) { - DCHECK(FLAG_preparser_scope_analysis); - DCHECK_NOT_NULL(produced_preparsed_scope_data_); - DCHECK_NOT_NULL(produced_preparsed_scope_data_->parent_); - produced_preparsed_scope_data_->parent_->AddSkippableFunction( + DCHECK_NOT_NULL(builder_); + DCHECK_NOT_NULL(builder_->parent_); + builder_->parent_->AddSkippableFunction( function_scope_->start_position(), end_position, function_scope_->num_parameters(), num_inner_functions, function_scope_->language_mode(), function_scope_->NeedsHomeObject()); } -void ProducedPreParsedScopeData::AddSkippableFunction( - int start_position, int end_position, int num_parameters, - int num_inner_functions, LanguageMode language_mode, - bool uses_super_property) { - DCHECK(FLAG_preparser_scope_analysis); - DCHECK(previously_produced_preparsed_scope_data_.is_null()); - +void PreParsedScopeDataBuilder::AddSkippableFunction(int start_position, + int end_position, + int num_parameters, + int num_inner_functions, + LanguageMode language_mode, + bool uses_super_property) { if (bailed_out_) { return; } @@ -245,15 +217,14 @@ void ProducedPreParsedScopeData::AddSkippableFunction( byte_data_->WriteQuarter(language_and_super); } -void ProducedPreParsedScopeData::SaveScopeAllocationData( +void PreParsedScopeDataBuilder::SaveScopeAllocationData( DeclarationScope* scope) { - DCHECK(FLAG_preparser_scope_analysis); - DCHECK(previously_produced_preparsed_scope_data_.is_null()); // The data contains a uint32 (reserved space for scope_data_start) and // function data items, kSkippableFunctionDataSize each. - DCHECK_GE(byte_data_->size(), kPlaceholderSize); + DCHECK_GE(byte_data_->size(), ByteData::kPlaceholderSize); DCHECK_LE(byte_data_->size(), std::numeric_limits<uint32_t>::max()); - DCHECK_EQ(byte_data_->size() % kSkippableFunctionDataSize, kPlaceholderSize); + DCHECK_EQ(byte_data_->size() % ByteData::kSkippableFunctionDataSize, + ByteData::kPlaceholderSize); if (bailed_out_) { return; @@ -262,7 +233,7 @@ void ProducedPreParsedScopeData::SaveScopeAllocationData( uint32_t scope_data_start = static_cast<uint32_t>(byte_data_->size()); // If there are no skippable inner functions, we don't need to save anything. - if (scope_data_start == kPlaceholderSize) { + if (scope_data_start == ByteData::kPlaceholderSize) { return; } @@ -271,7 +242,7 @@ void ProducedPreParsedScopeData::SaveScopeAllocationData( // For a data integrity check, write a value between data about skipped inner // funcs and data about variables. - byte_data_->WriteUint32(kMagicValue); + byte_data_->WriteUint32(ByteData::kMagicValue); byte_data_->WriteUint32(scope->start_position()); byte_data_->WriteUint32(scope->end_position()); #endif @@ -279,24 +250,19 @@ void ProducedPreParsedScopeData::SaveScopeAllocationData( SaveDataForScope(scope); } -bool ProducedPreParsedScopeData::ContainsInnerFunctions() const { - return byte_data_->size() > kPlaceholderSize; +bool PreParsedScopeDataBuilder::ContainsInnerFunctions() const { + return byte_data_->size() > ByteData::kPlaceholderSize; } -MaybeHandle<PreParsedScopeData> ProducedPreParsedScopeData::Serialize( +MaybeHandle<PreParsedScopeData> PreParsedScopeDataBuilder::Serialize( Isolate* isolate) { - if (!previously_produced_preparsed_scope_data_.is_null()) { - DCHECK(!bailed_out_); - DCHECK_EQ(data_for_inner_functions_.size(), 0); - return previously_produced_preparsed_scope_data_; - } if (bailed_out_) { return MaybeHandle<PreParsedScopeData>(); } DCHECK(!ThisOrParentBailedOut()); - if (byte_data_->size() <= kPlaceholderSize) { + if (byte_data_->size() <= ByteData::kPlaceholderSize) { // The data contains only the placeholder. return MaybeHandle<PreParsedScopeData>(); } @@ -322,7 +288,33 @@ MaybeHandle<PreParsedScopeData> ProducedPreParsedScopeData::Serialize( return data; } -bool ProducedPreParsedScopeData::ScopeNeedsData(Scope* scope) { +ZonePreParsedScopeData* PreParsedScopeDataBuilder::Serialize(Zone* zone) { + if (bailed_out_) { + return nullptr; + } + + DCHECK(!ThisOrParentBailedOut()); + + if (byte_data_->size() <= ByteData::kPlaceholderSize) { + // The data contains only the placeholder. + return nullptr; + } + + int child_length = static_cast<int>(data_for_inner_functions_.size()); + ZonePreParsedScopeData* result = new (zone) ZonePreParsedScopeData( + zone, byte_data_->begin(), byte_data_->end(), child_length); + + int i = 0; + for (const auto& item : data_for_inner_functions_) { + ZonePreParsedScopeData* child = item->Serialize(zone); + result->set_child(i, child); + i++; + } + + return result; +} + +bool PreParsedScopeDataBuilder::ScopeNeedsData(Scope* scope) { if (scope->scope_type() == ScopeType::FUNCTION_SCOPE) { // Default constructors don't need data (they cannot contain inner functions // defined by the user). Other functions do. @@ -344,9 +336,9 @@ bool ProducedPreParsedScopeData::ScopeNeedsData(Scope* scope) { return false; } -bool ProducedPreParsedScopeData::ScopeIsSkippableFunctionScope(Scope* scope) { +bool PreParsedScopeDataBuilder::ScopeIsSkippableFunctionScope(Scope* scope) { // Lazy non-arrow function scopes are skippable. Lazy functions are exactly - // those Scopes which have their own ProducedPreParsedScopeData object. This + // those Scopes which have their own PreParsedScopeDataBuilder object. This // logic ensures that the scope allocation data is consistent with the // skippable function data (both agree on where the lazy function boundaries // are). @@ -355,10 +347,10 @@ bool ProducedPreParsedScopeData::ScopeIsSkippableFunctionScope(Scope* scope) { } DeclarationScope* declaration_scope = scope->AsDeclarationScope(); return !declaration_scope->is_arrow_scope() && - declaration_scope->produced_preparsed_scope_data() != nullptr; + declaration_scope->preparsed_scope_data_builder() != nullptr; } -void ProducedPreParsedScopeData::SaveDataForScope(Scope* scope) { +void PreParsedScopeDataBuilder::SaveDataForScope(Scope* scope) { DCHECK_NE(scope->end_position(), kNoSourcePosition); if (!ScopeNeedsData(scope)) { @@ -392,7 +384,7 @@ void ProducedPreParsedScopeData::SaveDataForScope(Scope* scope) { SaveDataForInnerScopes(scope); } -void ProducedPreParsedScopeData::SaveDataForVariable(Variable* var) { +void PreParsedScopeDataBuilder::SaveDataForVariable(Variable* var) { #ifdef DEBUG // Store the variable name in debug mode; this way we can check that we // restore data to the correct variable. @@ -410,7 +402,7 @@ void ProducedPreParsedScopeData::SaveDataForVariable(Variable* var) { byte_data_->WriteQuarter(variable_data); } -void ProducedPreParsedScopeData::SaveDataForInnerScopes(Scope* scope) { +void PreParsedScopeDataBuilder::SaveDataForInnerScopes(Scope* scope) { // Inner scopes are stored in the reverse order, but we'd like to write the // data in the logical order. There might be many inner scopes, so we don't // want to recurse here. @@ -419,9 +411,9 @@ void ProducedPreParsedScopeData::SaveDataForInnerScopes(Scope* scope) { inner = inner->sibling()) { if (ScopeIsSkippableFunctionScope(inner)) { // Don't save data about function scopes, since they'll have their own - // ProducedPreParsedScopeData where their data is saved. + // PreParsedScopeDataBuilder where their data is saved. DCHECK_NOT_NULL( - inner->AsDeclarationScope()->produced_preparsed_scope_data()); + inner->AsDeclarationScope()->preparsed_scope_data_builder()); continue; } scopes.push_back(inner); @@ -431,91 +423,83 @@ void ProducedPreParsedScopeData::SaveDataForInnerScopes(Scope* scope) { } } -ConsumedPreParsedScopeData::ByteData::ReadingScope::ReadingScope( - ConsumedPreParsedScopeData* parent) - : ReadingScope(parent->scope_data_.get(), parent->data_->scope_data()) {} +class BuilderProducedPreParsedScopeData final + : public ProducedPreParsedScopeData { + public: + explicit BuilderProducedPreParsedScopeData(PreParsedScopeDataBuilder* builder) + : builder_(builder) {} -int32_t ConsumedPreParsedScopeData::ByteData::ReadUint32() { - DCHECK_NOT_NULL(data_); - DCHECK_GE(RemainingBytes(), kUint32Size); -#ifdef DEBUG - // Check that there indeed is an integer following. - DCHECK_EQ(data_->get(index_++), kUint32Size); -#endif - int32_t result = 0; - byte* p = reinterpret_cast<byte*>(&result); - for (int i = 0; i < 4; ++i) { - *p++ = data_->get(index_++); + MaybeHandle<PreParsedScopeData> Serialize(Isolate* isolate) final { + return builder_->Serialize(isolate); } - stored_quarters_ = 0; - return result; -} -uint8_t ConsumedPreParsedScopeData::ByteData::ReadUint8() { - DCHECK_NOT_NULL(data_); - DCHECK_GE(RemainingBytes(), kUint8Size); -#ifdef DEBUG - // Check that there indeed is a byte following. - DCHECK_EQ(data_->get(index_++), kUint8Size); -#endif - stored_quarters_ = 0; - return data_->get(index_++); -} + ZonePreParsedScopeData* Serialize(Zone* zone) final { + return builder_->Serialize(zone); + }; -uint8_t ConsumedPreParsedScopeData::ByteData::ReadQuarter() { - DCHECK_NOT_NULL(data_); - if (stored_quarters_ == 0) { - DCHECK_GE(RemainingBytes(), kUint8Size); -#ifdef DEBUG - // Check that there indeed are quarters following. - DCHECK_EQ(data_->get(index_++), kQuarterMarker); -#endif - stored_byte_ = data_->get(index_++); - stored_quarters_ = 4; - } - // Read the first 2 bits from stored_byte_. - uint8_t result = (stored_byte_ >> 6) & 3; - DCHECK_LE(result, 3); - --stored_quarters_; - stored_byte_ <<= 2; - return result; -} + private: + PreParsedScopeDataBuilder* builder_; +}; -size_t ConsumedPreParsedScopeData::ByteData::RemainingBytes() const { - DCHECK_NOT_NULL(data_); - return data_->length() - index_; -} +class OnHeapProducedPreParsedScopeData final + : public ProducedPreParsedScopeData { + public: + explicit OnHeapProducedPreParsedScopeData(Handle<PreParsedScopeData> data) + : data_(data) {} -ConsumedPreParsedScopeData::ConsumedPreParsedScopeData() - : isolate_(nullptr), scope_data_(new ByteData()), child_index_(0) {} + MaybeHandle<PreParsedScopeData> Serialize(Isolate* isolate) final { + return data_; + } -ConsumedPreParsedScopeData::~ConsumedPreParsedScopeData() {} + ZonePreParsedScopeData* Serialize(Zone* zone) final { + // Not required. + UNREACHABLE(); + }; -void ConsumedPreParsedScopeData::SetData(Isolate* isolate, - Handle<PreParsedScopeData> data) { - DCHECK_NOT_NULL(isolate); - DCHECK(data->IsPreParsedScopeData()); - isolate_ = isolate; - data_ = data; -#ifdef DEBUG - ByteData::ReadingScope reading_scope(this); - int scope_data_start = scope_data_->ReadUint32(); - scope_data_->SetPosition(scope_data_start); - DCHECK_EQ(scope_data_->ReadUint32(), kMagicValue); - // The first data item is scope_data_start. Skip over it. - scope_data_->SetPosition(kPlaceholderSize); -#endif + private: + Handle<PreParsedScopeData> data_; +}; + +class ZoneProducedPreParsedScopeData final : public ProducedPreParsedScopeData { + public: + explicit ZoneProducedPreParsedScopeData(ZonePreParsedScopeData* data) + : data_(data) {} + + MaybeHandle<PreParsedScopeData> Serialize(Isolate* isolate) final { + return data_->Serialize(isolate); + } + + ZonePreParsedScopeData* Serialize(Zone* zone) final { return data_; }; + + private: + ZonePreParsedScopeData* data_; +}; + +ProducedPreParsedScopeData* ProducedPreParsedScopeData::For( + PreParsedScopeDataBuilder* builder, Zone* zone) { + return new (zone) BuilderProducedPreParsedScopeData(builder); +} + +ProducedPreParsedScopeData* ProducedPreParsedScopeData::For( + Handle<PreParsedScopeData> data, Zone* zone) { + return new (zone) OnHeapProducedPreParsedScopeData(data); +} + +ProducedPreParsedScopeData* ProducedPreParsedScopeData::For( + ZonePreParsedScopeData* data, Zone* zone) { + return new (zone) ZoneProducedPreParsedScopeData(data); } +template <class Data> ProducedPreParsedScopeData* -ConsumedPreParsedScopeData::GetDataForSkippableFunction( +BaseConsumedPreParsedScopeData<Data>::GetDataForSkippableFunction( Zone* zone, int start_position, int* end_position, int* num_parameters, int* num_inner_functions, bool* uses_super_property, LanguageMode* language_mode) { // The skippable function *must* be the next function in the data. Use the // start position as a sanity check. - ByteData::ReadingScope reading_scope(this); - CHECK_GE(scope_data_->RemainingBytes(), kSkippableFunctionDataSize); + typename ByteData::ReadingScope reading_scope(this); + CHECK_GE(scope_data_->RemainingBytes(), ByteData::kSkippableFunctionDataSize); int start_position_from_data = scope_data_->ReadUint32(); CHECK_EQ(start_position, start_position_from_data); @@ -531,28 +515,19 @@ ConsumedPreParsedScopeData::GetDataForSkippableFunction( // Retrieve the corresponding PreParsedScopeData and associate it to the // skipped function. If the skipped functions contains inner functions, those // can be skipped when the skipped function is eagerly parsed. - CHECK_GT(data_->length(), child_index_); - Object* child_data = data_->child_data(child_index_++); - if (!child_data->IsPreParsedScopeData()) { - return nullptr; - } - Handle<PreParsedScopeData> child_data_handle( - PreParsedScopeData::cast(child_data), isolate_); - return new (zone) ProducedPreParsedScopeData(child_data_handle, zone); + return GetChildData(zone, child_index_++); } -void ConsumedPreParsedScopeData::RestoreScopeAllocationData( +template <class Data> +void BaseConsumedPreParsedScopeData<Data>::RestoreScopeAllocationData( DeclarationScope* scope) { - DCHECK(FLAG_preparser_scope_analysis); DCHECK_EQ(scope->scope_type(), ScopeType::FUNCTION_SCOPE); - DCHECK(!data_.is_null()); - - ByteData::ReadingScope reading_scope(this); + typename ByteData::ReadingScope reading_scope(this); #ifdef DEBUG int magic_value_from_data = scope_data_->ReadUint32(); // Check that we've consumed all inner function data. - DCHECK_EQ(magic_value_from_data, kMagicValue); + DCHECK_EQ(magic_value_from_data, ByteData::kMagicValue); int start_position_from_data = scope_data_->ReadUint32(); int end_position_from_data = scope_data_->ReadUint32(); @@ -566,7 +541,8 @@ void ConsumedPreParsedScopeData::RestoreScopeAllocationData( DCHECK_EQ(scope_data_->RemainingBytes(), 0); } -void ConsumedPreParsedScopeData::RestoreData(Scope* scope) { +template <typename Data> +void BaseConsumedPreParsedScopeData<Data>::RestoreData(Scope* scope) { if (scope->is_declaration_scope() && scope->AsDeclarationScope()->is_skipped_function()) { return; @@ -575,18 +551,12 @@ void ConsumedPreParsedScopeData::RestoreData(Scope* scope) { // It's possible that scope is not present in the data at all (since PreParser // doesn't create the corresponding scope). In this case, the Scope won't // contain any variables for which we need the data. - if (!ProducedPreParsedScopeData::ScopeNeedsData(scope)) { + if (!PreParsedScopeDataBuilder::ScopeNeedsData(scope)) { return; } - if (scope_data_->RemainingBytes() < kUint8Size) { - // Temporary debugging code for detecting inconsistent data. Write debug - // information on the stack, then crash. - isolate_->PushStackTraceAndDie(); - } - // scope_type is stored only in debug mode. - CHECK_GE(scope_data_->RemainingBytes(), kUint8Size); + CHECK_GE(scope_data_->RemainingBytes(), ByteData::kUint8Size); DCHECK_EQ(scope_data_->ReadUint8(), scope->scope_type()); uint32_t eval = scope_data_->ReadUint8(); @@ -613,7 +583,9 @@ void ConsumedPreParsedScopeData::RestoreData(Scope* scope) { RestoreDataForInnerScopes(scope); } -void ConsumedPreParsedScopeData::RestoreDataForVariable(Variable* var) { +template <typename Data> +void BaseConsumedPreParsedScopeData<Data>::RestoreDataForVariable( + Variable* var) { #ifdef DEBUG const AstRawString* name = var->raw_name(); bool data_one_byte = scope_data_->ReadUint8(); @@ -647,7 +619,9 @@ void ConsumedPreParsedScopeData::RestoreDataForVariable(Variable* var) { } } -void ConsumedPreParsedScopeData::RestoreDataForInnerScopes(Scope* scope) { +template <typename Data> +void BaseConsumedPreParsedScopeData<Data>::RestoreDataForInnerScopes( + Scope* scope) { std::vector<Scope*> scopes; for (Scope* inner = scope->inner_scope(); inner != nullptr; inner = inner->sibling()) { @@ -658,5 +632,106 @@ void ConsumedPreParsedScopeData::RestoreDataForInnerScopes(Scope* scope) { } } +#ifdef DEBUG +template <class Data> +void BaseConsumedPreParsedScopeData<Data>::VerifyDataStart() { + typename ByteData::ReadingScope reading_scope(this); + int scope_data_start = scope_data_->ReadUint32(); + scope_data_->SetPosition(scope_data_start); + DCHECK_EQ(scope_data_->ReadUint32(), ByteData::kMagicValue); + // The first data item is scope_data_start. Skip over it. + scope_data_->SetPosition(ByteData::kPlaceholderSize); +} +#endif + +PodArray<uint8_t>* OnHeapConsumedPreParsedScopeData::GetScopeData() { + return data_->scope_data(); +} + +ProducedPreParsedScopeData* OnHeapConsumedPreParsedScopeData::GetChildData( + Zone* zone, int child_index) { + CHECK_GT(data_->length(), child_index); + Object* child_data = data_->child_data(child_index); + if (!child_data->IsPreParsedScopeData()) { + return nullptr; + } + Handle<PreParsedScopeData> child_data_handle( + PreParsedScopeData::cast(child_data), isolate_); + return ProducedPreParsedScopeData::For(child_data_handle, zone); +} + +OnHeapConsumedPreParsedScopeData::OnHeapConsumedPreParsedScopeData( + Isolate* isolate, Handle<PreParsedScopeData> data) + : BaseConsumedPreParsedScopeData<PodArray<uint8_t>>(), + isolate_(isolate), + data_(data) { + DCHECK_NOT_NULL(isolate); + DCHECK(data->IsPreParsedScopeData()); +#ifdef DEBUG + VerifyDataStart(); +#endif +} + +ZonePreParsedScopeData::ZonePreParsedScopeData( + Zone* zone, ZoneChunkList<uint8_t>::iterator byte_data_begin, + ZoneChunkList<uint8_t>::iterator byte_data_end, int child_length) + : byte_data_(byte_data_begin, byte_data_end, zone), + children_(child_length, zone) {} + +Handle<PreParsedScopeData> ZonePreParsedScopeData::Serialize(Isolate* isolate) { + int child_data_length = child_length(); + Handle<PreParsedScopeData> result = + isolate->factory()->NewPreParsedScopeData(child_data_length); + + Handle<PodArray<uint8_t>> scope_data_array = PodArray<uint8_t>::New( + isolate, static_cast<int>(byte_data()->size()), TENURED); + scope_data_array->copy_in(0, byte_data()->data(), + static_cast<int>(byte_data()->size())); + result->set_scope_data(*scope_data_array); + + for (int i = 0; i < child_data_length; i++) { + ZonePreParsedScopeData* child = get_child(i); + if (child) { + Handle<PreParsedScopeData> child_data = child->Serialize(isolate); + result->set_child_data(i, *child_data); + } + } + return result; +} + +ZoneConsumedPreParsedScopeData::ZoneConsumedPreParsedScopeData( + Zone* zone, ZonePreParsedScopeData* data) + : data_(data), scope_data_wrapper_(data_->byte_data()) { +#ifdef DEBUG + VerifyDataStart(); +#endif +} + +ZoneVectorWrapper* ZoneConsumedPreParsedScopeData::GetScopeData() { + return &scope_data_wrapper_; +} + +ProducedPreParsedScopeData* ZoneConsumedPreParsedScopeData::GetChildData( + Zone* zone, int child_index) { + CHECK_GT(data_->child_length(), child_index); + ZonePreParsedScopeData* child_data = data_->get_child(child_index); + if (child_data == nullptr) { + return nullptr; + } + return ProducedPreParsedScopeData::For(child_data, zone); +} + +std::unique_ptr<ConsumedPreParsedScopeData> ConsumedPreParsedScopeData::For( + Isolate* isolate, Handle<PreParsedScopeData> data) { + DCHECK(!data.is_null()); + return base::make_unique<OnHeapConsumedPreParsedScopeData>(isolate, data); +} + +std::unique_ptr<ConsumedPreParsedScopeData> ConsumedPreParsedScopeData::For( + Zone* zone, ZonePreParsedScopeData* data) { + if (data == nullptr) return {}; + return base::make_unique<ZoneConsumedPreParsedScopeData>(zone, data); +} + } // namespace internal } // namespace v8 diff --git a/deps/v8/src/parsing/preparsed-scope-data.h b/deps/v8/src/parsing/preparsed-scope-data.h index 61d67291a4..25298c4331 100644 --- a/deps/v8/src/parsing/preparsed-scope-data.h +++ b/deps/v8/src/parsing/preparsed-scope-data.h @@ -5,23 +5,21 @@ #ifndef V8_PARSING_PREPARSED_SCOPE_DATA_H_ #define V8_PARSING_PREPARSED_SCOPE_DATA_H_ -#include <set> -#include <unordered_map> -#include <vector> - #include "src/globals.h" #include "src/handles.h" -#include "src/objects/shared-function-info.h" +#include "src/maybe-handles.h" #include "src/zone/zone-chunk-list.h" +#include "src/zone/zone-containers.h" namespace v8 { namespace internal { template <typename T> -class Handle; +class PodArray; class PreParser; class PreParsedScopeData; +class ZonePreParsedScopeData; /* @@ -64,40 +62,15 @@ class PreParsedScopeData; */ -class ProducedPreParsedScopeData : public ZoneObject { +class PreParsedScopeDataBuilder : public ZoneObject { public: - class ByteData : public ZoneObject { - public: - explicit ByteData(Zone* zone) - : backing_store_(zone), free_quarters_in_last_byte_(0) {} - - void WriteUint32(uint32_t data); - void WriteUint8(uint8_t data); - void WriteQuarter(uint8_t data); - -#ifdef DEBUG - // For overwriting previously written data at position 0. - void OverwriteFirstUint32(uint32_t data); -#endif - - Handle<PodArray<uint8_t>> Serialize(Isolate* isolate); - - size_t size() const { return backing_store_.size(); } + class ByteData; - private: - ZoneChunkList<uint8_t> backing_store_; - uint8_t free_quarters_in_last_byte_; - }; - - // Create a ProducedPreParsedScopeData object which will collect data as we + // Create a PreParsedScopeDataBuilder object which will collect data as we // parse. - ProducedPreParsedScopeData(Zone* zone, ProducedPreParsedScopeData* parent); - - // Create a ProducedPreParsedScopeData which is just a proxy for a previous - // produced PreParsedScopeData. - ProducedPreParsedScopeData(Handle<PreParsedScopeData> data, Zone* zone); + PreParsedScopeDataBuilder(Zone* zone, PreParsedScopeDataBuilder* parent); - ProducedPreParsedScopeData* parent() const { return parent_; } + PreParsedScopeDataBuilder* parent() const { return parent_; } // For gathering the inner function data and splitting it up according to the // laziness boundaries. Each lazy function gets its own @@ -112,7 +85,7 @@ class ProducedPreParsedScopeData : public ZoneObject { private: DeclarationScope* function_scope_; PreParser* preparser_; - ProducedPreParsedScopeData* produced_preparsed_scope_data_; + PreParsedScopeDataBuilder* builder_; DISALLOW_COPY_AND_ASSIGN(DataGatheringScope); }; @@ -148,15 +121,15 @@ class ProducedPreParsedScopeData : public ZoneObject { bool ContainsInnerFunctions() const; - // If there is data (if the Scope contains skippable inner functions), move - // the data into the heap and return a Handle to it; otherwise return a null - // MaybeHandle. - MaybeHandle<PreParsedScopeData> Serialize(Isolate* isolate); - static bool ScopeNeedsData(Scope* scope); static bool ScopeIsSkippableFunctionScope(Scope* scope); private: + friend class BuilderProducedPreParsedScopeData; + + virtual MaybeHandle<PreParsedScopeData> Serialize(Isolate* isolate); + virtual ZonePreParsedScopeData* Serialize(Zone* zone); + void AddSkippableFunction(int start_position, int end_position, int num_parameters, int num_inner_functions, LanguageMode language_mode, @@ -166,88 +139,72 @@ class ProducedPreParsedScopeData : public ZoneObject { void SaveDataForVariable(Variable* var); void SaveDataForInnerScopes(Scope* scope); - ProducedPreParsedScopeData* parent_; + PreParsedScopeDataBuilder* parent_; ByteData* byte_data_; - ZoneChunkList<ProducedPreParsedScopeData*> data_for_inner_functions_; + ZoneChunkList<PreParsedScopeDataBuilder*> data_for_inner_functions_; // Whether we've given up producing the data for this function. bool bailed_out_; - // ProducedPreParsedScopeData can also mask a Handle<PreParsedScopeData> - // which was produced already earlier. This happens for deeper lazy functions. - Handle<PreParsedScopeData> previously_produced_preparsed_scope_data_; + DISALLOW_COPY_AND_ASSIGN(PreParsedScopeDataBuilder); +}; - DISALLOW_COPY_AND_ASSIGN(ProducedPreParsedScopeData); +class ProducedPreParsedScopeData : public ZoneObject { + public: + // If there is data (if the Scope contains skippable inner functions), move + // the data into the heap and return a Handle to it; otherwise return a null + // MaybeHandle. + virtual MaybeHandle<PreParsedScopeData> Serialize(Isolate* isolate) = 0; + + // If there is data (if the Scope contains skippable inner functions), return + // an off-heap ZonePreParsedScopeData representing the data; otherwise + // return nullptr. + virtual ZonePreParsedScopeData* Serialize(Zone* zone) = 0; + + // Create a ProducedPreParsedScopeData which is a proxy for a previous + // produced PreParsedScopeData in zone. + static ProducedPreParsedScopeData* For(PreParsedScopeDataBuilder* builder, + Zone* zone); + + // Create a ProducedPreParsedScopeData which is a proxy for a previous + // produced PreParsedScopeData on the heap. + static ProducedPreParsedScopeData* For(Handle<PreParsedScopeData> data, + Zone* zone); + + // Create a ProducedPreParsedScopeData which is a proxy for a previous + // produced PreParsedScopeData in zone. + static ProducedPreParsedScopeData* For(ZonePreParsedScopeData* data, + Zone* zone); }; class ConsumedPreParsedScopeData { public: - class ByteData { - public: - ByteData() - : data_(nullptr), index_(0), stored_quarters_(0), stored_byte_(0) {} - - // Reading from the ByteData is only allowed when a ReadingScope is on the - // stack. This ensures that we have a DisallowHeapAllocation in place - // whenever ByteData holds a raw pointer into the heap. - class ReadingScope { - public: - ReadingScope(ByteData* consumed_data, PodArray<uint8_t>* data) - : consumed_data_(consumed_data) { - consumed_data->data_ = data; - } - explicit ReadingScope(ConsumedPreParsedScopeData* parent); - ~ReadingScope() { consumed_data_->data_ = nullptr; } - - private: - ByteData* consumed_data_; - DisallowHeapAllocation no_gc; - }; - - void SetPosition(int position) { index_ = position; } - - int32_t ReadUint32(); - uint8_t ReadUint8(); - uint8_t ReadQuarter(); - - size_t RemainingBytes() const; - - // private: - PodArray<uint8_t>* data_; - int index_; - uint8_t stored_quarters_; - uint8_t stored_byte_; - }; - - ConsumedPreParsedScopeData(); - ~ConsumedPreParsedScopeData(); + // Creates a ConsumedPreParsedScopeData representing the data of an on-heap + // PreParsedScopeData |data|. + static std::unique_ptr<ConsumedPreParsedScopeData> For( + Isolate* isolate, Handle<PreParsedScopeData> data); - void SetData(Isolate* isolate, Handle<PreParsedScopeData> data); + // Creates a ConsumedPreParsedScopeData representing the data of an off-heap + // ZonePreParsedScopeData |data|. + static std::unique_ptr<ConsumedPreParsedScopeData> For( + Zone* zone, ZonePreParsedScopeData* data); - bool HasData() const { return !data_.is_null(); } + virtual ~ConsumedPreParsedScopeData() = default; - ProducedPreParsedScopeData* GetDataForSkippableFunction( + virtual ProducedPreParsedScopeData* GetDataForSkippableFunction( Zone* zone, int start_position, int* end_position, int* num_parameters, int* num_inner_functions, bool* uses_super_property, - LanguageMode* language_mode); + LanguageMode* language_mode) = 0; // Restores the information needed for allocating the Scope's (and its // subscopes') variables. - void RestoreScopeAllocationData(DeclarationScope* scope); + virtual void RestoreScopeAllocationData(DeclarationScope* scope) = 0; - private: - void RestoreData(Scope* scope); - void RestoreDataForVariable(Variable* var); - void RestoreDataForInnerScopes(Scope* scope); - - Isolate* isolate_; - Handle<PreParsedScopeData> data_; - std::unique_ptr<ByteData> scope_data_; - // When consuming the data, these indexes point to the data we're going to - // consume next. - int child_index_; + protected: + ConsumedPreParsedScopeData() = default; + private: DISALLOW_COPY_AND_ASSIGN(ConsumedPreParsedScopeData); }; diff --git a/deps/v8/src/parsing/preparser.cc b/deps/v8/src/parsing/preparser.cc index d449c8d76b..0e74014542 100644 --- a/deps/v8/src/parsing/preparser.cc +++ b/deps/v8/src/parsing/preparser.cc @@ -123,22 +123,23 @@ PreParser::PreParseResult PreParser::PreParseFunction( int script_id) { DCHECK_EQ(FUNCTION_SCOPE, function_scope->scope_type()); use_counts_ = use_counts; - DCHECK(!track_unresolved_variables_); - track_unresolved_variables_ = is_inner_function; set_script_id(script_id); #ifdef DEBUG function_scope->set_is_being_lazily_parsed(true); #endif + track_unresolved_variables_ = + ShouldTrackUnresolvedVariables(is_inner_function); + // Start collecting data for a new function which might contain skippable // functions. - std::unique_ptr<ProducedPreParsedScopeData::DataGatheringScope> - produced_preparsed_scope_data_scope; + std::unique_ptr<PreParsedScopeDataBuilder::DataGatheringScope> + preparsed_scope_data_builder_scope; if (FLAG_preparser_scope_analysis && !IsArrowFunction(kind)) { - track_unresolved_variables_ = true; - produced_preparsed_scope_data_scope.reset( - new ProducedPreParsedScopeData::DataGatheringScope(function_scope, - this)); + DCHECK(track_unresolved_variables_); + preparsed_scope_data_builder_scope.reset( + new PreParsedScopeDataBuilder::DataGatheringScope(function_scope, + this)); } // In the preparser, we use the function literal ids to count how many @@ -166,7 +167,11 @@ PreParser::PreParseResult PreParser::PreParseFunction( formals_classifier.reset(new ExpressionClassifier(this, &duplicate_finder)); // We return kPreParseSuccess in failure cases too - errors are retrieved // separately by Parser::SkipLazyFunctionBody. - ParseFormalParameterList(&formals, CHECK_OK_VALUE(kPreParseSuccess)); + ParseFormalParameterList( + &formals, + CHECK_OK_VALUE(pending_error_handler()->ErrorUnidentifiableByPreParser() + ? kPreParseNotIdentifiableError + : kPreParseSuccess)); Expect(Token::RPAREN, CHECK_OK_VALUE(kPreParseSuccess)); int formals_end_position = scanner()->location().end_pos; @@ -205,27 +210,22 @@ PreParser::PreParseResult PreParser::PreParseFunction( } } - if (!IsArrowFunction(kind) && track_unresolved_variables_ && - result == kLazyParsingComplete) { - // Declare arguments after parsing the function since lexical 'arguments' - // masks the arguments object. Declare arguments before declaring the - // function var since the arguments object masks 'function arguments'. - function_scope->DeclareArguments(ast_value_factory()); - - DeclareFunctionNameVar(function_name, function_type, function_scope); - } - use_counts_ = nullptr; - track_unresolved_variables_ = false; if (result == kLazyParsingAborted) { + DCHECK(!pending_error_handler()->ErrorUnidentifiableByPreParser()); return kPreParseAbort; } else if (stack_overflow()) { + DCHECK(!pending_error_handler()->ErrorUnidentifiableByPreParser()); return kPreParseStackOverflow; + } else if (pending_error_handler()->ErrorUnidentifiableByPreParser()) { + DCHECK(!*ok); + return kPreParseNotIdentifiableError; } else if (!*ok) { DCHECK(pending_error_handler()->has_pending_error()); } else { DCHECK_EQ(Token::RBRACE, scanner()->peek()); + DCHECK(result == kLazyParsingComplete); if (!IsArrowFunction(kind)) { // Validate parameter names. We can do this only after parsing the @@ -234,17 +234,37 @@ PreParser::PreParseResult PreParser::PreParseFunction( is_sloppy(function_scope->language_mode()) && formals.is_simple && !IsConciseMethod(kind); ValidateFormalParameters(function_scope->language_mode(), - allow_duplicate_parameters, - CHECK_OK_VALUE(kPreParseSuccess)); + allow_duplicate_parameters, ok); + if (!*ok) { + if (pending_error_handler()->ErrorUnidentifiableByPreParser()) { + return kPreParseNotIdentifiableError; + } else { + return kPreParseSuccess; + } + } + + if (track_unresolved_variables_) { + // Declare arguments after parsing the function since lexical + // 'arguments' masks the arguments object. Declare arguments before + // declaring the function var since the arguments object masks 'function + // arguments'. + function_scope->DeclareArguments(ast_value_factory()); - *produced_preparsed_scope_data = produced_preparsed_scope_data_; + DeclareFunctionNameVar(function_name, function_type, function_scope); + } + + *produced_preparsed_scope_data = ProducedPreParsedScopeData::For( + preparsed_scope_data_builder_, main_zone()); } + DCHECK(!pending_error_handler()->ErrorUnidentifiableByPreParser()); if (is_strict(function_scope->language_mode())) { int end_pos = scanner()->location().end_pos; CheckStrictOctalLiteral(function_scope->start_position(), end_pos, ok); } } + + DCHECK(!pending_error_handler()->ErrorUnidentifiableByPreParser()); return kPreParseSuccess; } @@ -290,15 +310,15 @@ PreParser::Expression PreParser::ParseFunctionLiteral( // Start collecting data for a new function which might contain skippable // functions. - std::unique_ptr<ProducedPreParsedScopeData::DataGatheringScope> - produced_preparsed_scope_data_scope; + std::unique_ptr<PreParsedScopeDataBuilder::DataGatheringScope> + preparsed_scope_data_builder_scope; if (!function_state_->next_function_is_likely_called() && - produced_preparsed_scope_data_ != nullptr) { + preparsed_scope_data_builder_ != nullptr) { DCHECK(FLAG_preparser_scope_analysis); DCHECK(track_unresolved_variables_); - produced_preparsed_scope_data_scope.reset( - new ProducedPreParsedScopeData::DataGatheringScope(function_scope, - this)); + preparsed_scope_data_builder_scope.reset( + new PreParsedScopeDataBuilder::DataGatheringScope(function_scope, + this)); } FunctionState function_state(&function_state_, &scope_, function_scope); @@ -324,7 +344,7 @@ PreParser::Expression PreParser::ParseFunctionLiteral( int pos = function_token_pos == kNoSourcePosition ? peek_position() : function_token_pos; ParseFunctionBody(body, function_name, pos, formals, kind, function_type, - CHECK_OK); + FunctionBodyType::kBlock, true, CHECK_OK); // Parsing the body may change the language mode in our scope. language_mode = function_scope->language_mode(); @@ -346,8 +366,8 @@ PreParser::Expression PreParser::ParseFunctionLiteral( CheckStrictOctalLiteral(start_position, end_position, CHECK_OK); } - if (produced_preparsed_scope_data_scope) { - produced_preparsed_scope_data_scope->MarkFunctionAsSkippable( + if (preparsed_scope_data_builder_scope) { + preparsed_scope_data_builder_scope->MarkFunctionAsSkippable( end_position, GetLastFunctionLiteralId() - func_id); } if (V8_UNLIKELY(FLAG_log_function_events)) { @@ -394,19 +414,19 @@ PreParserStatement PreParser::BuildParameterInitializationBlock( DCHECK(scope()->is_function_scope()); if (FLAG_preparser_scope_analysis && scope()->AsDeclarationScope()->calls_sloppy_eval() && - produced_preparsed_scope_data_ != nullptr) { + preparsed_scope_data_builder_ != nullptr) { // We cannot replicate the Scope structure constructed by the Parser, // because we've lost information whether each individual parameter was // simple or not. Give up trying to produce data to skip inner functions. - if (produced_preparsed_scope_data_->parent() != nullptr) { + if (preparsed_scope_data_builder_->parent() != nullptr) { // Lazy parsing started before the current function; the function which // cannot contain skippable functions is the parent function. (Its inner // functions cannot either; they are implicitly bailed out.) - produced_preparsed_scope_data_->parent()->Bailout(); + preparsed_scope_data_builder_->parent()->Bailout(); } else { // Lazy parsing started at the current function; it cannot contain // skippable functions. - produced_preparsed_scope_data_->Bailout(); + preparsed_scope_data_builder_->Bailout(); } } diff --git a/deps/v8/src/parsing/preparser.h b/deps/v8/src/parsing/preparser.h index 10c42fa940..65509a2029 100644 --- a/deps/v8/src/parsing/preparser.h +++ b/deps/v8/src/parsing/preparser.h @@ -10,7 +10,6 @@ #include "src/parsing/parser-base.h" #include "src/parsing/preparser-logger.h" #include "src/pending-compilation-error-handler.h" -#include "src/zone/zone-containers.h" namespace v8 { namespace internal { @@ -23,7 +22,7 @@ namespace internal { // interface as AstNodeFactory, so ParserBase doesn't need to care which one is // used. -class ProducedPreParsedScopeData; +class PreParsedScopeDataBuilder; class PreParserIdentifier { public: @@ -87,16 +86,18 @@ class PreParserIdentifier { friend class PreParserFactory; }; - class PreParserExpression { public: + using VariableZoneThreadedListType = + ZoneThreadedList<VariableProxy, VariableProxy::PreParserNext>; + PreParserExpression() : code_(TypeField::encode(kNull)), variables_(nullptr) {} static PreParserExpression Null() { return PreParserExpression(); } static PreParserExpression Default( - ZonePtrList<VariableProxy>* variables = nullptr) { + VariableZoneThreadedListType* variables = nullptr) { return PreParserExpression(TypeField::encode(kExpression), variables); } @@ -125,9 +126,7 @@ class PreParserExpression { right.variables_); } if (right.variables_ != nullptr) { - for (auto variable : *right.variables_) { - left.variables_->Add(variable, zone); - } + left.variables_->Append(std::move(*right.variables_)); } return PreParserExpression(TypeField::encode(kExpression), left.variables_); @@ -135,7 +134,8 @@ class PreParserExpression { return PreParserExpression(TypeField::encode(kExpression)); } - static PreParserExpression Assignment(ZonePtrList<VariableProxy>* variables) { + static PreParserExpression Assignment( + VariableZoneThreadedListType* variables) { return PreParserExpression(TypeField::encode(kExpression) | ExpressionTypeField::encode(kAssignment), variables); @@ -146,13 +146,13 @@ class PreParserExpression { } static PreParserExpression ObjectLiteral( - ZonePtrList<VariableProxy>* variables) { + VariableZoneThreadedListType* variables) { return PreParserExpression(TypeField::encode(kObjectLiteralExpression), variables); } static PreParserExpression ArrayLiteral( - ZonePtrList<VariableProxy>* variables) { + VariableZoneThreadedListType* variables) { return PreParserExpression(TypeField::encode(kArrayLiteralExpression), variables); } @@ -171,10 +171,9 @@ class PreParserExpression { IsUseAsmField::encode(true)); } - static PreParserExpression This(ZonePtrList<VariableProxy>* variables) { + static PreParserExpression This() { return PreParserExpression(TypeField::encode(kExpression) | - ExpressionTypeField::encode(kThisExpression), - variables); + ExpressionTypeField::encode(kThisExpression)); } static PreParserExpression ThisPropertyWithPrivateFieldKey() { @@ -336,7 +335,7 @@ class PreParserExpression { if (variables_ != nullptr) { DCHECK(IsIdentifier()); DCHECK(AsIdentifier().IsPrivateName()); - DCHECK_EQ(1, variables_->length()); + DCHECK_EQ(1, variables_->LengthForTest()); variables_->first()->set_is_private_field(); } } @@ -374,8 +373,9 @@ class PreParserExpression { kAssignment }; - explicit PreParserExpression(uint32_t expression_code, - ZonePtrList<VariableProxy>* variables = nullptr) + explicit PreParserExpression( + uint32_t expression_code, + VariableZoneThreadedListType* variables = nullptr) : code_(expression_code), variables_(variables) {} void AddVariable(VariableProxy* variable, Zone* zone) { @@ -383,9 +383,9 @@ class PreParserExpression { return; } if (variables_ == nullptr) { - variables_ = new (zone) ZonePtrList<VariableProxy>(1, zone); + variables_ = new (zone) VariableZoneThreadedListType(); } - variables_->Add(variable, zone); + variables_->Add(variable); } // The first three bits are for the Type. @@ -410,64 +410,65 @@ class PreParserExpression { uint32_t code_; // If the PreParser is used in the variable tracking mode, PreParserExpression // accumulates variables in that expression. - ZonePtrList<VariableProxy>* variables_; + VariableZoneThreadedListType* variables_; friend class PreParser; friend class PreParserFactory; - template <typename T> - friend class PreParserList; + friend class PreParserExpressionList; }; // The pre-parser doesn't need to build lists of expressions, identifiers, or // the like. If the PreParser is used in variable tracking mode, it needs to // build lists of variables though. -template <typename T> -class PreParserList { +class PreParserExpressionList { + using VariableZoneThreadedListType = + ZoneThreadedList<VariableProxy, VariableProxy::PreParserNext>; + public: // These functions make list->Add(some_expression) work (and do nothing). - PreParserList() : length_(0), variables_(nullptr) {} - PreParserList* operator->() { return this; } - void Add(const T& element, Zone* zone); + PreParserExpressionList() : PreParserExpressionList(0) {} + PreParserExpressionList* operator->() { return this; } + void Add(const PreParserExpression& expression, Zone* zone) { + if (expression.variables_ != nullptr) { + DCHECK(FLAG_lazy_inner_functions); + DCHECK_NOT_NULL(zone); + if (variables_ == nullptr) { + variables_ = new (zone) VariableZoneThreadedListType(); + } + variables_->Append(std::move(*expression.variables_)); + } + ++length_; + } int length() const { return length_; } - static PreParserList Null() { return PreParserList(-1); } + static PreParserExpressionList Null() { return PreParserExpressionList(-1); } bool IsNull() const { return length_ == -1; } - void Set(int index, const T& element) {} + void Set(int index, const PreParserExpression& element) {} private: - explicit PreParserList(int n) : length_(n), variables_(nullptr) {} + explicit PreParserExpressionList(int n) : length_(n), variables_(nullptr) {} int length_; - ZonePtrList<VariableProxy>* variables_; + + VariableZoneThreadedListType* variables_; friend class PreParser; friend class PreParserFactory; }; -template <> -inline void PreParserList<PreParserExpression>::Add( - const PreParserExpression& expression, Zone* zone) { - if (expression.variables_ != nullptr) { - DCHECK(FLAG_lazy_inner_functions); - DCHECK_NOT_NULL(zone); - if (variables_ == nullptr) { - variables_ = new (zone) ZonePtrList<VariableProxy>(1, zone); - } - for (auto identifier : (*expression.variables_)) { - variables_->Add(identifier, zone); - } - } - ++length_; -} - -template <typename T> -void PreParserList<T>::Add(const T& element, Zone* zone) { - ++length_; -} +class PreParserStatement; -typedef PreParserList<PreParserExpression> PreParserExpressionList; +class PreParserStatementList { + public: + PreParserStatementList() : PreParserStatementList(false) {} + PreParserStatementList* operator->() { return this; } + void Add(const PreParserStatement& element, Zone* zone) {} + static PreParserStatementList Null() { return PreParserStatementList(true); } + bool IsNull() const { return is_null_; } -class PreParserStatement; -typedef PreParserList<PreParserStatement> PreParserStatementList; + private: + explicit PreParserStatementList(bool is_null) : is_null_(is_null) {} + bool is_null_; +}; class PreParserStatement { public: @@ -530,8 +531,6 @@ class PreParserStatement { // and PreParser. PreParserStatement* operator->() { return this; } - // TODO(adamk): These should return something even lighter-weight than - // PreParserStatementList. PreParserStatementList statements() { return PreParserStatementList(); } PreParserStatementList cases() { return PreParserStatementList(); } @@ -563,11 +562,6 @@ class PreParserFactory { explicit PreParserFactory(AstValueFactory* ast_value_factory, Zone* zone) : ast_node_factory_(ast_value_factory, zone), zone_(zone) {} - void set_zone(Zone* zone) { - ast_node_factory_.set_zone(zone); - zone_ = zone; - } - AstNodeFactory* ast_node_factory() { return &ast_node_factory_; } PreParserExpression NewStringLiteral(const PreParserIdentifier& identifier, @@ -852,19 +846,22 @@ class PreParserFactory { struct PreParserFormalParameters : FormalParametersBase { struct Parameter : public ZoneObject { - Parameter(ZonePtrList<VariableProxy>* variables, bool is_rest) + using VariableZoneThreadedListType = + ZoneThreadedList<VariableProxy, VariableProxy::PreParserNext>; + + Parameter(VariableZoneThreadedListType* variables, bool is_rest) : variables_(variables), is_rest(is_rest) {} Parameter** next() { return &next_parameter; } Parameter* const* next() const { return &next_parameter; } - ZonePtrList<VariableProxy>* variables_; + VariableZoneThreadedListType* variables_; Parameter* next_parameter = nullptr; bool is_rest : 1; }; explicit PreParserFormalParameters(DeclarationScope* scope) : FormalParametersBase(scope) {} - ThreadedList<Parameter> params; + base::ThreadedList<Parameter> params; }; @@ -881,6 +878,48 @@ class PreParserTargetScope { explicit PreParserTargetScope(ParserBase<PreParser>* preparser) {} }; +class PreParserFuncNameInferrer { + public: + PreParserFuncNameInferrer(AstValueFactory* avf, Zone* zone) {} + void RemoveAsyncKeywordFromEnd() const {} + void Infer() const {} + void RemoveLastFunction() const {} + + class State { + public: + explicit State(PreParserFuncNameInferrer* fni) {} + + private: + DISALLOW_COPY_AND_ASSIGN(State); + }; + + private: + DISALLOW_COPY_AND_ASSIGN(PreParserFuncNameInferrer); +}; + +class PreParserSourceRange { + public: + PreParserSourceRange() {} + PreParserSourceRange(int start, int end) {} + static PreParserSourceRange Empty() { return PreParserSourceRange(); } + static PreParserSourceRange OpenEnded(int32_t start) { return Empty(); } + static const PreParserSourceRange& ContinuationOf( + const PreParserSourceRange& that, int end) { + return that; + } +}; + +class PreParserSourceRangeScope { + public: + PreParserSourceRangeScope(Scanner* scanner, PreParserSourceRange* range) {} + const PreParserSourceRange& Finalize() const { return range_; } + + private: + PreParserSourceRange range_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(PreParserSourceRangeScope); +}; + template <> struct ParserTypes<PreParser> { typedef ParserBase<PreParser> Base; @@ -910,6 +949,10 @@ struct ParserTypes<PreParser> { typedef PreParserTarget Target; typedef PreParserTargetScope TargetScope; + typedef PreParserFuncNameInferrer FuncNameInferrer; + typedef PreParserSourceRange SourceRange; + typedef PreParserSourceRangeScope SourceRangeScope; + static constexpr bool ExpressionClassifierReportErrors = false; }; @@ -937,6 +980,7 @@ class PreParser : public ParserBase<PreParser> { enum PreParseResult { kPreParseStackOverflow, kPreParseAbort, + kPreParseNotIdentifiableError, kPreParseSuccess }; @@ -952,7 +996,7 @@ class PreParser : public ParserBase<PreParser> { parsing_module, parsing_on_main_thread), use_counts_(nullptr), track_unresolved_variables_(false), - produced_preparsed_scope_data_(nullptr) {} + preparsed_scope_data_builder_(nullptr) {} static bool IsPreParser() { return true; } @@ -980,13 +1024,17 @@ class PreParser : public ParserBase<PreParser> { ProducedPreParsedScopeData** produced_preparser_scope_data, int script_id); - ProducedPreParsedScopeData* produced_preparsed_scope_data() const { - return produced_preparsed_scope_data_; + V8_INLINE static bool ShouldTrackUnresolvedVariables(bool is_inner_function) { + return FLAG_preparser_scope_analysis || is_inner_function; + } + + PreParsedScopeDataBuilder* preparsed_scope_data_builder() const { + return preparsed_scope_data_builder_; } - void set_produced_preparsed_scope_data( - ProducedPreParsedScopeData* produced_preparsed_scope_data) { - produced_preparsed_scope_data_ = produced_preparsed_scope_data; + void set_preparsed_scope_data_builder( + PreParsedScopeDataBuilder* preparsed_scope_data_builder) { + preparsed_scope_data_builder_ = preparsed_scope_data_builder; } private: @@ -1009,12 +1057,13 @@ class PreParser : public ParserBase<PreParser> { return pending_error_handler_; } - V8_INLINE LazyParsingResult - SkipFunction(const AstRawString* name, FunctionKind kind, - FunctionLiteral::FunctionType function_type, - DeclarationScope* function_scope, int* num_parameters, - ProducedPreParsedScopeData** produced_preparsed_scope_data, - bool is_inner_function, bool may_abort, bool* ok) { + V8_INLINE bool SkipFunction( + const AstRawString* name, FunctionKind kind, + FunctionLiteral::FunctionType function_type, + DeclarationScope* function_scope, int* num_parameters, + ProducedPreParsedScopeData** produced_preparsed_scope_data, + bool is_inner_function, bool may_abort, + FunctionLiteral::EagerCompileHint* hint, bool* ok) { UNREACHABLE(); } @@ -1509,6 +1558,10 @@ class PreParser : public ParserBase<PreParser> { arg, error_type); } + V8_INLINE void ReportUnidentifiableError() { + pending_error_handler()->SetUnidentifiableError(); + } + V8_INLINE void ReportMessageAt(Scanner::Location source_location, MessageTemplate::Template message, const PreParserIdentifier& arg, @@ -1558,16 +1611,12 @@ class PreParser : public ParserBase<PreParser> { } V8_INLINE PreParserExpression ThisExpression(int pos = kNoSourcePosition) { - ZonePtrList<VariableProxy>* variables = nullptr; if (track_unresolved_variables_) { - VariableProxy* proxy = scope()->NewUnresolved( - factory()->ast_node_factory(), ast_value_factory()->this_string(), - pos, THIS_VARIABLE); - - variables = new (zone()) ZonePtrList<VariableProxy>(1, zone()); - variables->Add(proxy, zone()); + scope()->NewUnresolved(factory()->ast_node_factory(), + ast_value_factory()->this_string(), pos, + THIS_VARIABLE); } - return PreParserExpression::This(variables); + return PreParserExpression::This(); } V8_INLINE PreParserExpression NewSuperPropertyReference(int pos) { @@ -1648,14 +1697,6 @@ class PreParser : public ParserBase<PreParser> { return PreParserStatement::Jump(); } - V8_INLINE void AddParameterInitializationBlock( - const PreParserFormalParameters& parameters, PreParserStatementList body, - bool is_async, bool* ok) { - if (!parameters.is_simple) { - BuildParameterInitializationBlock(parameters, ok); - } - } - V8_INLINE void AddFormalParameter(PreParserFormalParameters* parameters, const PreParserExpression& pattern, const PreParserExpression& initializer, @@ -1671,14 +1712,15 @@ class PreParser : public ParserBase<PreParser> { V8_INLINE void DeclareFormalParameters( DeclarationScope* scope, - const ThreadedList<PreParserFormalParameters::Parameter>& parameters, + const base::ThreadedList<PreParserFormalParameters::Parameter>& + parameters, bool is_simple) { if (!is_simple) scope->SetHasNonSimpleParameters(); if (track_unresolved_variables_) { DCHECK(FLAG_lazy_inner_functions); for (auto parameter : parameters) { DCHECK_IMPLIES(is_simple, parameter->variables_ != nullptr); - DCHECK_IMPLIES(is_simple, parameter->variables_->length() == 1); + DCHECK_IMPLIES(is_simple, parameter->variables_->LengthForTest() == 1); // Make sure each parameter is added only once even if it's a // destructuring parameter which contains multiple names. bool add_parameter = true; @@ -1733,7 +1775,7 @@ class PreParser : public ParserBase<PreParser> { const PreParserExpression& value, const PreParserExpression& identifier) { } - V8_INLINE ZoneVector<typename ExpressionClassifier::Error>* + V8_INLINE ZoneList<typename ExpressionClassifier::Error>* GetReportedErrorList() const { return function_state_->GetReportedErrorList(); } @@ -1758,7 +1800,7 @@ class PreParser : public ParserBase<PreParser> { bool track_unresolved_variables_; PreParserLogger log_; - ProducedPreParsedScopeData* produced_preparsed_scope_data_; + PreParsedScopeDataBuilder* preparsed_scope_data_builder_; }; PreParserExpression PreParser::SpreadCall(const PreParserExpression& function, diff --git a/deps/v8/src/parsing/scanner-character-streams.cc b/deps/v8/src/parsing/scanner-character-streams.cc index d38fdd7c42..8472e9f4fc 100644 --- a/deps/v8/src/parsing/scanner-character-streams.cc +++ b/deps/v8/src/parsing/scanner-character-streams.cc @@ -4,6 +4,9 @@ #include "src/parsing/scanner-character-streams.h" +#include <memory> +#include <vector> + #include "include/v8.h" #include "src/counters.h" #include "src/globals.h" @@ -15,21 +18,50 @@ namespace v8 { namespace internal { +class ScopedExternalStringLock { + public: + explicit ScopedExternalStringLock(ExternalString* string) { + DCHECK(string); + if (string->IsExternalOneByteString()) { + resource_ = ExternalOneByteString::cast(string)->resource(); + } else { + DCHECK(string->IsExternalTwoByteString()); + resource_ = ExternalTwoByteString::cast(string)->resource(); + } + DCHECK(resource_); + resource_->Lock(); + } + + // Copying a lock increases the locking depth. + ScopedExternalStringLock(const ScopedExternalStringLock& other) + : resource_(other.resource_) { + resource_->Lock(); + } + + ~ScopedExternalStringLock() { resource_->Unlock(); } + + private: + // Not nullptr. + const v8::String::ExternalStringResourceBase* resource_; +}; + namespace { const unibrow::uchar kUtf8Bom = 0xFEFF; } // namespace template <typename Char> -struct HeapStringType; +struct CharTraits; template <> -struct HeapStringType<uint8_t> { +struct CharTraits<uint8_t> { typedef SeqOneByteString String; + typedef ExternalOneByteString ExternalString; }; template <> -struct HeapStringType<uint16_t> { +struct CharTraits<uint16_t> { typedef SeqTwoByteString String; + typedef ExternalTwoByteString ExternalString; }; template <typename Char> @@ -47,16 +79,21 @@ struct Range { template <typename Char> class OnHeapStream { public: - typedef typename HeapStringType<Char>::String String; + typedef typename CharTraits<Char>::String String; OnHeapStream(Handle<String> string, size_t start_offset, size_t end) : string_(string), start_offset_(start_offset), length_(end) {} - Range<Char> GetDataAt(size_t pos) { + OnHeapStream(const OnHeapStream& other) : start_offset_(0), length_(0) { + UNREACHABLE(); + } + + Range<Char> GetDataAt(size_t pos, RuntimeCallStats* stats) { return {&string_->GetChars()[start_offset_ + Min(length_, pos)], &string_->GetChars()[start_offset_ + length_]}; } + static const bool kCanBeCloned = false; static const bool kCanAccessHeap = true; private: @@ -69,14 +106,42 @@ class OnHeapStream { // ExternalTwoByteString. template <typename Char> class ExternalStringStream { + typedef typename CharTraits<Char>::ExternalString ExternalString; + public: - ExternalStringStream(const Char* data, size_t end) - : data_(data), length_(end) {} + ExternalStringStream(ExternalString* string, size_t start_offset, + size_t length) + : lock_(string), + data_(string->GetChars() + start_offset), + length_(length) {} + + ExternalStringStream(const ExternalStringStream& other) + : lock_(other.lock_), data_(other.data_), length_(other.length_) {} - Range<Char> GetDataAt(size_t pos) { + Range<Char> GetDataAt(size_t pos, RuntimeCallStats* stats) { return {&data_[Min(length_, pos)], &data_[length_]}; } + static const bool kCanBeCloned = true; + static const bool kCanAccessHeap = false; + + private: + ScopedExternalStringLock lock_; + const Char* const data_; + const size_t length_; +}; + +// A Char stream backed by a C array. Testing only. +template <typename Char> +class TestingStream { + public: + TestingStream(const Char* data, size_t length) + : data_(data), length_(length) {} + Range<Char> GetDataAt(size_t pos, RuntimeCallStats* stats) { + return {&data_[Min(length_, pos)], &data_[length_]}; + } + + static const bool kCanBeCloned = true; static const bool kCanAccessHeap = false; private: @@ -88,12 +153,16 @@ class ExternalStringStream { template <typename Char> class ChunkedStream { public: - ChunkedStream(ScriptCompiler::ExternalSourceStream* source, - RuntimeCallStats* stats) - : source_(source), stats_(stats) {} + explicit ChunkedStream(ScriptCompiler::ExternalSourceStream* source) + : source_(source) {} + + ChunkedStream(const ChunkedStream& other) { + // TODO(rmcilroy): Implement cloning for chunked streams. + UNREACHABLE(); + } - Range<Char> GetDataAt(size_t pos) { - Chunk chunk = FindChunk(pos); + Range<Char> GetDataAt(size_t pos, RuntimeCallStats* stats) { + Chunk chunk = FindChunk(pos, stats); size_t buffer_end = chunk.length; size_t buffer_pos = Min(buffer_end, pos - chunk.position); return {&chunk.data[buffer_pos], &chunk.data[buffer_end]}; @@ -103,6 +172,7 @@ class ChunkedStream { for (Chunk& chunk : chunks_) delete[] chunk.data; } + static const bool kCanBeCloned = false; static const bool kCanAccessHeap = false; private: @@ -116,13 +186,13 @@ class ChunkedStream { size_t end_position() const { return position + length; } }; - Chunk FindChunk(size_t position) { - while (V8_UNLIKELY(chunks_.empty())) FetchChunk(size_t{0}); + Chunk FindChunk(size_t position, RuntimeCallStats* stats) { + while (V8_UNLIKELY(chunks_.empty())) FetchChunk(size_t{0}, stats); // Walk forwards while the position is in front of the current chunk. while (position >= chunks_.back().end_position() && chunks_.back().length > 0) { - FetchChunk(chunks_.back().end_position()); + FetchChunk(chunks_.back().end_position(), stats); } // Walk backwards. @@ -142,11 +212,11 @@ class ChunkedStream { length / sizeof(Char)); } - void FetchChunk(size_t position) { + void FetchChunk(size_t position, RuntimeCallStats* stats) { const uint8_t* data = nullptr; size_t length; { - RuntimeCallTimerScope scope(stats_, + RuntimeCallTimerScope scope(stats, RuntimeCallCounterId::kGetMoreDataCallback); length = source_->GetMoreData(&data); } @@ -154,102 +224,11 @@ class ChunkedStream { } ScriptCompiler::ExternalSourceStream* source_; - RuntimeCallStats* stats_; protected: std::vector<struct Chunk> chunks_; }; -template <typename Char> -class Utf8ChunkedStream : public ChunkedStream<uint16_t> { - public: - Utf8ChunkedStream(ScriptCompiler::ExternalSourceStream* source, - RuntimeCallStats* stats) - : ChunkedStream<uint16_t>(source, stats) {} - - STATIC_ASSERT(sizeof(Char) == sizeof(uint16_t)); - void ProcessChunk(const uint8_t* data, size_t position, size_t length) final { - if (length == 0) { - unibrow::uchar t = unibrow::Utf8::ValueOfIncrementalFinish(&state_); - if (t != unibrow::Utf8::kBufferEmpty) { - DCHECK_EQ(t, unibrow::Utf8::kBadChar); - incomplete_char_ = 0; - uint16_t* result = new uint16_t[1]; - result[0] = unibrow::Utf8::kBadChar; - chunks_.emplace_back(result, position, 1); - position++; - } - chunks_.emplace_back(nullptr, position, 0); - delete[] data; - return; - } - - // First count the number of complete characters that can be produced. - - unibrow::Utf8::State state = state_; - uint32_t incomplete_char = incomplete_char_; - bool seen_bom = seen_bom_; - - size_t i = 0; - size_t chars = 0; - while (i < length) { - unibrow::uchar t = unibrow::Utf8::ValueOfIncremental(data[i], &i, &state, - &incomplete_char); - if (!seen_bom && t == kUtf8Bom && position + chars == 0) { - seen_bom = true; - // BOM detected at beginning of the stream. Don't copy it. - } else if (t != unibrow::Utf8::kIncomplete) { - chars++; - if (t > unibrow::Utf16::kMaxNonSurrogateCharCode) chars++; - } - } - - // Process the data. - - // If there aren't any complete characters, update the state without - // producing a chunk. - if (chars == 0) { - state_ = state; - incomplete_char_ = incomplete_char; - seen_bom_ = seen_bom; - delete[] data; - return; - } - - // Update the state and produce a chunk with complete characters. - uint16_t* result = new uint16_t[chars]; - uint16_t* cursor = result; - i = 0; - - while (i < length) { - unibrow::uchar t = unibrow::Utf8::ValueOfIncremental(data[i], &i, &state_, - &incomplete_char_); - if (V8_LIKELY(t < kUtf8Bom)) { - *(cursor++) = static_cast<uc16>(t); // The by most frequent case. - } else if (t == unibrow::Utf8::kIncomplete) { - continue; - } else if (!seen_bom_ && t == kUtf8Bom && position == 0 && - cursor == result) { - // BOM detected at beginning of the stream. Don't copy it. - seen_bom_ = true; - } else if (t <= unibrow::Utf16::kMaxNonSurrogateCharCode) { - *(cursor++) = static_cast<uc16>(t); - } else { - *(cursor++) = unibrow::Utf16::LeadSurrogate(t); - *(cursor++) = unibrow::Utf16::TrailSurrogate(t); - } - } - - chunks_.emplace_back(result, position, chars); - delete[] data; - } - - private: - uint32_t incomplete_char_ = 0; - unibrow::Utf8::State state_ = unibrow::Utf8::State::kAccept; - bool seen_bom_ = false; -}; - // Provides a buffered utf-16 view on the bytes from the underlying ByteStream. // Chars are buffered if either the underlying stream isn't utf-16 or the // underlying utf-16 stream might move (is on-heap). @@ -261,6 +240,16 @@ class BufferedCharacterStream : public Utf16CharacterStream { buffer_pos_ = pos; } + bool can_be_cloned() const final { + return ByteStream<uint16_t>::kCanBeCloned; + } + + std::unique_ptr<Utf16CharacterStream> Clone() const override { + CHECK(can_be_cloned()); + return std::unique_ptr<Utf16CharacterStream>( + new BufferedCharacterStream<ByteStream>(*this)); + } + protected: bool ReadBlock() final { size_t position = pos(); @@ -268,7 +257,8 @@ class BufferedCharacterStream : public Utf16CharacterStream { buffer_start_ = &buffer_[0]; buffer_cursor_ = buffer_start_; - Range<uint8_t> range = byte_stream_.GetDataAt(position); + Range<uint8_t> range = + byte_stream_.GetDataAt(position, runtime_call_stats()); if (range.length() == 0) { buffer_end_ = buffer_start_; return false; @@ -280,9 +270,14 @@ class BufferedCharacterStream : public Utf16CharacterStream { return true; } - bool can_access_heap() final { return ByteStream<uint8_t>::kCanAccessHeap; } + bool can_access_heap() const final { + return ByteStream<uint8_t>::kCanAccessHeap; + } private: + BufferedCharacterStream(const BufferedCharacterStream<ByteStream>& other) + : byte_stream_(other.byte_stream_) {} + static const size_t kBufferSize = 512; uc16 buffer_[kBufferSize]; ByteStream<uint8_t> byte_stream_; @@ -298,11 +293,25 @@ class UnbufferedCharacterStream : public Utf16CharacterStream { buffer_pos_ = pos; } + bool can_access_heap() const final { + return ByteStream<uint16_t>::kCanAccessHeap; + } + + bool can_be_cloned() const final { + return ByteStream<uint16_t>::kCanBeCloned; + } + + std::unique_ptr<Utf16CharacterStream> Clone() const override { + return std::unique_ptr<Utf16CharacterStream>( + new UnbufferedCharacterStream<ByteStream>(*this)); + } + protected: bool ReadBlock() final { size_t position = pos(); buffer_pos_ = position; - Range<uint16_t> range = byte_stream_.GetDataAt(position); + Range<uint16_t> range = + byte_stream_.GetDataAt(position, runtime_call_stats()); buffer_start_ = range.start; buffer_end_ = range.end; buffer_cursor_ = buffer_start_; @@ -313,7 +322,8 @@ class UnbufferedCharacterStream : public Utf16CharacterStream { return true; } - bool can_access_heap() final { return ByteStream<uint16_t>::kCanAccessHeap; } + UnbufferedCharacterStream(const UnbufferedCharacterStream<ByteStream>& other) + : byte_stream_(other.byte_stream_) {} ByteStream<uint16_t> byte_stream_; }; @@ -346,7 +356,7 @@ class RelocatingCharacterStream } void UpdateBufferPointers() { - Range<uint16_t> range = byte_stream_.GetDataAt(0); + Range<uint16_t> range = byte_stream_.GetDataAt(0, runtime_call_stats()); if (range.start != buffer_start_) { buffer_cursor_ = (buffer_cursor_ - buffer_start_) + range.start; buffer_start_ = range.start; @@ -412,16 +422,20 @@ bool BufferedUtf16CharacterStream::ReadBlock() { class Utf8ExternalStreamingStream : public BufferedUtf16CharacterStream { public: Utf8ExternalStreamingStream( - ScriptCompiler::ExternalSourceStream* source_stream, - RuntimeCallStats* stats) + ScriptCompiler::ExternalSourceStream* source_stream) : current_({0, {0, 0, 0, unibrow::Utf8::State::kAccept}}), - source_stream_(source_stream), - stats_(stats) {} + source_stream_(source_stream) {} ~Utf8ExternalStreamingStream() final { for (size_t i = 0; i < chunks_.size(); i++) delete[] chunks_[i].data; } - bool can_access_heap() final { return false; } + bool can_access_heap() const final { return false; } + + bool can_be_cloned() const final { return false; } + + std::unique_ptr<Utf16CharacterStream> Clone() const override { + UNREACHABLE(); + } protected: size_t FillBuffer(size_t position) final; @@ -468,7 +482,6 @@ class Utf8ExternalStreamingStream : public BufferedUtf16CharacterStream { std::vector<Chunk> chunks_; Position current_; ScriptCompiler::ExternalSourceStream* source_stream_; - RuntimeCallStats* stats_; }; bool Utf8ExternalStreamingStream::SkipToPosition(size_t position) { @@ -562,7 +575,7 @@ void Utf8ExternalStreamingStream::FillBufferFromCurrentChunk() { } bool Utf8ExternalStreamingStream::FetchChunk() { - RuntimeCallTimerScope scope(stats_, + RuntimeCallTimerScope scope(runtime_call_stats(), RuntimeCallCounterId::kGetMoreDataCallback); DCHECK_EQ(current_.chunk_no, chunks_.size()); DCHECK(chunks_.empty() || chunks_.back().length != 0); @@ -704,14 +717,12 @@ Utf16CharacterStream* ScannerStream::For(Isolate* isolate, Handle<String> data, } if (data->IsExternalOneByteString()) { return new BufferedCharacterStream<ExternalStringStream>( - static_cast<size_t>(start_pos), - ExternalOneByteString::cast(*data)->GetChars() + start_offset, - static_cast<size_t>(end_pos)); + static_cast<size_t>(start_pos), ExternalOneByteString::cast(*data), + start_offset, static_cast<size_t>(end_pos)); } else if (data->IsExternalTwoByteString()) { return new UnbufferedCharacterStream<ExternalStringStream>( - static_cast<size_t>(start_pos), - ExternalTwoByteString::cast(*data)->GetChars() + start_offset, - static_cast<size_t>(end_pos)); + static_cast<size_t>(start_pos), ExternalTwoByteString::cast(*data), + start_offset, static_cast<size_t>(end_pos)); } else if (data->IsSeqOneByteString()) { return new BufferedCharacterStream<OnHeapStream>( static_cast<size_t>(start_pos), Handle<SeqOneByteString>::cast(data), @@ -734,24 +745,23 @@ std::unique_ptr<Utf16CharacterStream> ScannerStream::ForTesting( std::unique_ptr<Utf16CharacterStream> ScannerStream::ForTesting( const char* data, size_t length) { return std::unique_ptr<Utf16CharacterStream>( - new BufferedCharacterStream<ExternalStringStream>( + new BufferedCharacterStream<TestingStream>( static_cast<size_t>(0), reinterpret_cast<const uint8_t*>(data), static_cast<size_t>(length))); } Utf16CharacterStream* ScannerStream::For( ScriptCompiler::ExternalSourceStream* source_stream, - v8::ScriptCompiler::StreamedSource::Encoding encoding, - RuntimeCallStats* stats) { + v8::ScriptCompiler::StreamedSource::Encoding encoding) { switch (encoding) { case v8::ScriptCompiler::StreamedSource::TWO_BYTE: return new UnbufferedCharacterStream<ChunkedStream>( - static_cast<size_t>(0), source_stream, stats); + static_cast<size_t>(0), source_stream); case v8::ScriptCompiler::StreamedSource::ONE_BYTE: return new BufferedCharacterStream<ChunkedStream>(static_cast<size_t>(0), - source_stream, stats); + source_stream); case v8::ScriptCompiler::StreamedSource::UTF8: - return new Utf8ExternalStreamingStream(source_stream, stats); + return new Utf8ExternalStreamingStream(source_stream); } UNREACHABLE(); } diff --git a/deps/v8/src/parsing/scanner-character-streams.h b/deps/v8/src/parsing/scanner-character-streams.h index 091ef5b8ea..4c85f5383f 100644 --- a/deps/v8/src/parsing/scanner-character-streams.h +++ b/deps/v8/src/parsing/scanner-character-streams.h @@ -24,8 +24,7 @@ class V8_EXPORT_PRIVATE ScannerStream { int start_pos, int end_pos); static Utf16CharacterStream* For( ScriptCompiler::ExternalSourceStream* source_stream, - ScriptCompiler::StreamedSource::Encoding encoding, - RuntimeCallStats* stats); + ScriptCompiler::StreamedSource::Encoding encoding); static std::unique_ptr<Utf16CharacterStream> ForTesting(const char* data); static std::unique_ptr<Utf16CharacterStream> ForTesting(const char* data, diff --git a/deps/v8/src/parsing/scanner-inl.h b/deps/v8/src/parsing/scanner-inl.h index 809ef655a7..9647957062 100644 --- a/deps/v8/src/parsing/scanner-inl.h +++ b/deps/v8/src/parsing/scanner-inl.h @@ -5,25 +5,354 @@ #ifndef V8_PARSING_SCANNER_INL_H_ #define V8_PARSING_SCANNER_INL_H_ +#include "src/char-predicates-inl.h" #include "src/parsing/scanner.h" #include "src/unicode-cache-inl.h" namespace v8 { namespace internal { +// Make sure tokens are stored as a single byte. +STATIC_ASSERT(sizeof(Token::Value) == 1); + +// Table of one-character tokens, by character (0x00..0x7F only). +// clang-format off +static const Token::Value one_char_tokens[] = { + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::LPAREN, // 0x28 + Token::RPAREN, // 0x29 + Token::ILLEGAL, + Token::ILLEGAL, + Token::COMMA, // 0x2C + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::COLON, // 0x3A + Token::SEMICOLON, // 0x3B + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::CONDITIONAL, // 0x3F + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::LBRACK, // 0x5B + Token::ILLEGAL, + Token::RBRACK, // 0x5D + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::LBRACE, // 0x7B + Token::ILLEGAL, + Token::RBRACE, // 0x7D + Token::BIT_NOT, // 0x7E + Token::ILLEGAL +}; +// clang-format on + +// ---------------------------------------------------------------------------- +// Keyword Matcher + +#define KEYWORDS(KEYWORD_GROUP, KEYWORD) \ + KEYWORD_GROUP('a') \ + KEYWORD("arguments", Token::ARGUMENTS) \ + KEYWORD("as", Token::AS) \ + KEYWORD("async", Token::ASYNC) \ + KEYWORD("await", Token::AWAIT) \ + KEYWORD("anonymous", Token::ANONYMOUS) \ + KEYWORD_GROUP('b') \ + KEYWORD("break", Token::BREAK) \ + KEYWORD_GROUP('c') \ + KEYWORD("case", Token::CASE) \ + KEYWORD("catch", Token::CATCH) \ + KEYWORD("class", Token::CLASS) \ + KEYWORD("const", Token::CONST) \ + KEYWORD("constructor", Token::CONSTRUCTOR) \ + KEYWORD("continue", Token::CONTINUE) \ + KEYWORD_GROUP('d') \ + KEYWORD("debugger", Token::DEBUGGER) \ + KEYWORD("default", Token::DEFAULT) \ + KEYWORD("delete", Token::DELETE) \ + KEYWORD("do", Token::DO) \ + KEYWORD_GROUP('e') \ + KEYWORD("else", Token::ELSE) \ + KEYWORD("enum", Token::ENUM) \ + KEYWORD("eval", Token::EVAL) \ + KEYWORD("export", Token::EXPORT) \ + KEYWORD("extends", Token::EXTENDS) \ + KEYWORD_GROUP('f') \ + KEYWORD("false", Token::FALSE_LITERAL) \ + KEYWORD("finally", Token::FINALLY) \ + KEYWORD("for", Token::FOR) \ + KEYWORD("from", Token::FROM) \ + KEYWORD("function", Token::FUNCTION) \ + KEYWORD_GROUP('g') \ + KEYWORD("get", Token::GET) \ + KEYWORD_GROUP('i') \ + KEYWORD("if", Token::IF) \ + KEYWORD("implements", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD("import", Token::IMPORT) \ + KEYWORD("in", Token::IN) \ + KEYWORD("instanceof", Token::INSTANCEOF) \ + KEYWORD("interface", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD_GROUP('l') \ + KEYWORD("let", Token::LET) \ + KEYWORD_GROUP('m') \ + KEYWORD("meta", Token::META) \ + KEYWORD_GROUP('n') \ + KEYWORD("name", Token::NAME) \ + KEYWORD("new", Token::NEW) \ + KEYWORD("null", Token::NULL_LITERAL) \ + KEYWORD_GROUP('o') \ + KEYWORD("of", Token::OF) \ + KEYWORD_GROUP('p') \ + KEYWORD("package", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD("private", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD("protected", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD("prototype", Token::PROTOTYPE) \ + KEYWORD("public", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD_GROUP('r') \ + KEYWORD("return", Token::RETURN) \ + KEYWORD_GROUP('s') \ + KEYWORD("set", Token::SET) \ + KEYWORD("static", Token::STATIC) \ + KEYWORD("super", Token::SUPER) \ + KEYWORD("switch", Token::SWITCH) \ + KEYWORD_GROUP('t') \ + KEYWORD("target", Token::TARGET) \ + KEYWORD("this", Token::THIS) \ + KEYWORD("throw", Token::THROW) \ + KEYWORD("true", Token::TRUE_LITERAL) \ + KEYWORD("try", Token::TRY) \ + KEYWORD("typeof", Token::TYPEOF) \ + KEYWORD_GROUP('u') \ + KEYWORD("undefined", Token::UNDEFINED) \ + KEYWORD_GROUP('v') \ + KEYWORD("var", Token::VAR) \ + KEYWORD("void", Token::VOID) \ + KEYWORD_GROUP('w') \ + KEYWORD("while", Token::WHILE) \ + KEYWORD("with", Token::WITH) \ + KEYWORD_GROUP('y') \ + KEYWORD("yield", Token::YIELD) \ + KEYWORD_GROUP('_') \ + KEYWORD("__proto__", Token::PROTO_UNDERSCORED) \ + KEYWORD_GROUP('#') \ + KEYWORD("#constructor", Token::PRIVATE_CONSTRUCTOR) + +V8_INLINE Token::Value KeywordOrIdentifierToken(const uint8_t* input, + int input_length) { + DCHECK_GE(input_length, 1); + const int kMinLength = 2; + const int kMaxLength = 12; + if (input_length < kMinLength || input_length > kMaxLength) { + return Token::IDENTIFIER; + } + switch (input[0]) { + default: +#define KEYWORD_GROUP_CASE(ch) \ + break; \ + case ch: +#define KEYWORD(keyword, token) \ + { \ + /* 'keyword' is a char array, so sizeof(keyword) is */ \ + /* strlen(keyword) plus 1 for the NUL char. */ \ + const int keyword_length = sizeof(keyword) - 1; \ + STATIC_ASSERT(keyword_length >= kMinLength); \ + STATIC_ASSERT(keyword_length <= kMaxLength); \ + DCHECK_EQ(input[0], keyword[0]); \ + DCHECK(token == Token::FUTURE_STRICT_RESERVED_WORD || \ + 0 == strncmp(keyword, Token::String(token), sizeof(keyword))); \ + if (input_length == keyword_length && input[1] == keyword[1] && \ + (keyword_length <= 2 || input[2] == keyword[2]) && \ + (keyword_length <= 3 || input[3] == keyword[3]) && \ + (keyword_length <= 4 || input[4] == keyword[4]) && \ + (keyword_length <= 5 || input[5] == keyword[5]) && \ + (keyword_length <= 6 || input[6] == keyword[6]) && \ + (keyword_length <= 7 || input[7] == keyword[7]) && \ + (keyword_length <= 8 || input[8] == keyword[8]) && \ + (keyword_length <= 9 || input[9] == keyword[9]) && \ + (keyword_length <= 10 || input[10] == keyword[10])) { \ + return token; \ + } \ + } + KEYWORDS(KEYWORD_GROUP_CASE, KEYWORD) + } + return Token::IDENTIFIER; +#undef KEYWORDS +#undef KEYWORD +#undef KEYWORD_GROUP_CASE +} + +V8_INLINE Token::Value Scanner::ScanIdentifierOrKeyword() { + LiteralScope literal(this); + return ScanIdentifierOrKeywordInner(&literal); +} + +V8_INLINE Token::Value Scanner::ScanIdentifierOrKeywordInner( + LiteralScope* literal) { + DCHECK(unicode_cache_->IsIdentifierStart(c0_)); + bool escaped = false; + if (IsInRange(c0_, 'a', 'z') || c0_ == '_') { + do { + AddLiteralChar(static_cast<char>(c0_)); + Advance(); + } while (IsInRange(c0_, 'a', 'z') || c0_ == '_'); + + if (IsDecimalDigit(c0_) || IsInRange(c0_, 'A', 'Z') || c0_ == '$') { + // Identifier starting with lowercase or _. + do { + AddLiteralChar(static_cast<char>(c0_)); + Advance(); + } while (IsAsciiIdentifier(c0_)); + + if (c0_ <= kMaxAscii && c0_ != '\\') { + literal->Complete(); + return Token::IDENTIFIER; + } + } else if (c0_ <= kMaxAscii && c0_ != '\\') { + // Only a-z+ or _: could be a keyword or identifier. + Vector<const uint8_t> chars = next().literal_chars.one_byte_literal(); + Token::Value token = + KeywordOrIdentifierToken(chars.start(), chars.length()); + if (token == Token::IDENTIFIER || + token == Token::FUTURE_STRICT_RESERVED_WORD || + Token::IsContextualKeyword(token)) + literal->Complete(); + return token; + } + } else if (IsInRange(c0_, 'A', 'Z') || c0_ == '$') { + do { + AddLiteralChar(static_cast<char>(c0_)); + Advance(); + } while (IsAsciiIdentifier(c0_)); + + if (c0_ <= kMaxAscii && c0_ != '\\') { + literal->Complete(); + return Token::IDENTIFIER; + } + } else if (c0_ == '\\') { + escaped = true; + uc32 c = ScanIdentifierUnicodeEscape(); + DCHECK(!unicode_cache_->IsIdentifierStart(-1)); + if (c == '\\' || !unicode_cache_->IsIdentifierStart(c)) { + return Token::ILLEGAL; + } + AddLiteralChar(c); + } + + return ScanIdentifierOrKeywordInnerSlow(literal, escaped); +} + V8_INLINE Token::Value Scanner::SkipWhiteSpace() { int start_position = source_pos(); - while (true) { - // We won't skip behind the end of input. - DCHECK(!unicode_cache_->IsWhiteSpace(kEndOfInput)); + // We won't skip behind the end of input. + DCHECK(!unicode_cache_->IsWhiteSpaceOrLineTerminator(kEndOfInput)); - // Advance as long as character is a WhiteSpace or LineTerminator. - // Remember if the latter is the case. - if (unibrow::IsLineTerminator(c0_)) { + // Advance as long as character is a WhiteSpace or LineTerminator. + while (unicode_cache_->IsWhiteSpaceOrLineTerminator(c0_)) { + if (!next().after_line_terminator && unibrow::IsLineTerminator(c0_)) { next().after_line_terminator = true; - } else if (!unicode_cache_->IsWhiteSpace(c0_)) { - break; } Advance(); } @@ -37,6 +366,191 @@ V8_INLINE Token::Value Scanner::SkipWhiteSpace() { return Token::WHITESPACE; } +V8_INLINE Token::Value Scanner::ScanSingleToken() { + Token::Value token; + do { + next().location.beg_pos = source_pos(); + + if (static_cast<unsigned>(c0_) <= 0x7F) { + Token::Value token = one_char_tokens[c0_]; + if (token != Token::ILLEGAL) { + Advance(); + return token; + } + } + + switch (c0_) { + case '"': + case '\'': + return ScanString(); + + case '<': + // < <= << <<= <!-- + Advance(); + if (c0_ == '=') return Select(Token::LTE); + if (c0_ == '<') return Select('=', Token::ASSIGN_SHL, Token::SHL); + if (c0_ == '!') { + token = ScanHtmlComment(); + continue; + } + return Token::LT; + + case '>': + // > >= >> >>= >>> >>>= + Advance(); + if (c0_ == '=') return Select(Token::GTE); + if (c0_ == '>') { + // >> >>= >>> >>>= + Advance(); + if (c0_ == '=') return Select(Token::ASSIGN_SAR); + if (c0_ == '>') return Select('=', Token::ASSIGN_SHR, Token::SHR); + return Token::SAR; + } + return Token::GT; + + case '=': + // = == === => + Advance(); + if (c0_ == '=') return Select('=', Token::EQ_STRICT, Token::EQ); + if (c0_ == '>') return Select(Token::ARROW); + return Token::ASSIGN; + + case '!': + // ! != !== + Advance(); + if (c0_ == '=') return Select('=', Token::NE_STRICT, Token::NE); + return Token::NOT; + + case '+': + // + ++ += + Advance(); + if (c0_ == '+') return Select(Token::INC); + if (c0_ == '=') return Select(Token::ASSIGN_ADD); + return Token::ADD; + + case '-': + // - -- --> -= + Advance(); + if (c0_ == '-') { + Advance(); + if (c0_ == '>' && next().after_line_terminator) { + // For compatibility with SpiderMonkey, we skip lines that + // start with an HTML comment end '-->'. + token = SkipSingleHTMLComment(); + continue; + } + return Token::DEC; + } + if (c0_ == '=') return Select(Token::ASSIGN_SUB); + return Token::SUB; + + case '*': + // * *= + Advance(); + if (c0_ == '*') return Select('=', Token::ASSIGN_EXP, Token::EXP); + if (c0_ == '=') return Select(Token::ASSIGN_MUL); + return Token::MUL; + + case '%': + // % %= + return Select('=', Token::ASSIGN_MOD, Token::MOD); + + case '/': + // / // /* /= + Advance(); + if (c0_ == '/') { + uc32 c = Peek(); + if (c == '#' || c == '@') { + Advance(); + Advance(); + token = SkipSourceURLComment(); + continue; + } + token = SkipSingleLineComment(); + continue; + } + if (c0_ == '*') { + token = SkipMultiLineComment(); + continue; + } + if (c0_ == '=') return Select(Token::ASSIGN_DIV); + return Token::DIV; + + case '&': + // & && &= + Advance(); + if (c0_ == '&') return Select(Token::AND); + if (c0_ == '=') return Select(Token::ASSIGN_BIT_AND); + return Token::BIT_AND; + + case '|': + // | || |= + Advance(); + if (c0_ == '|') return Select(Token::OR); + if (c0_ == '=') return Select(Token::ASSIGN_BIT_OR); + return Token::BIT_OR; + + case '^': + // ^ ^= + return Select('=', Token::ASSIGN_BIT_XOR, Token::BIT_XOR); + + case '.': + // . Number + Advance(); + if (IsDecimalDigit(c0_)) return ScanNumber(true); + if (c0_ == '.') { + if (Peek() == '.') { + Advance(); + Advance(); + return Token::ELLIPSIS; + } + } + return Token::PERIOD; + + case '`': + Advance(); + return ScanTemplateSpan(); + + case '#': + return ScanPrivateName(); + + default: + if (unicode_cache_->IsIdentifierStart(c0_) || + (CombineSurrogatePair() && + unicode_cache_->IsIdentifierStart(c0_))) { + Token::Value token = ScanIdentifierOrKeyword(); + if (!Token::IsContextualKeyword(token)) return token; + + next().contextual_token = token; + return Token::IDENTIFIER; + } + if (IsDecimalDigit(c0_)) return ScanNumber(false); + if (c0_ == kEndOfInput) return Token::EOS; + token = SkipWhiteSpace(); + continue; + } + // Continue scanning for tokens as long as we're just skipping whitespace. + } while (token == Token::WHITESPACE); + + return token; +} + +void Scanner::Scan() { + next().literal_chars.Drop(); + next().raw_literal_chars.Drop(); + next().contextual_token = Token::UNINITIALIZED; + next().invalid_template_escape_message = MessageTemplate::kNone; + + next().token = ScanSingleToken(); + next().location.end_pos = source_pos(); + +#ifdef DEBUG + SanityCheckTokenDesc(current()); + SanityCheckTokenDesc(next()); + SanityCheckTokenDesc(next_next()); +#endif +} + } // namespace internal } // namespace v8 diff --git a/deps/v8/src/parsing/scanner.cc b/deps/v8/src/parsing/scanner.cc index 781832c2e6..525b1bc681 100644 --- a/deps/v8/src/parsing/scanner.cc +++ b/deps/v8/src/parsing/scanner.cc @@ -11,7 +11,6 @@ #include <cmath> #include "src/ast/ast-value-factory.h" -#include "src/char-predicates-inl.h" #include "src/conversions-inl.h" #include "src/objects/bigint.h" #include "src/parsing/duplicate-finder.h" // For Scanner::FindSymbol @@ -134,7 +133,6 @@ const size_t Scanner::BookmarkScope::kBookmarkWasApplied = void Scanner::BookmarkScope::Set() { DCHECK_EQ(bookmark_, kNoBookmark); - DCHECK_EQ(scanner_->next_next().token, Token::UNINITIALIZED); // The first token is a bit special, since current_ will still be // uninitialized. In this case, store kBookmarkAtFirstPos and special-case it @@ -160,11 +158,11 @@ void Scanner::BookmarkScope::Apply() { bookmark_ = kBookmarkWasApplied; } -bool Scanner::BookmarkScope::HasBeenSet() { +bool Scanner::BookmarkScope::HasBeenSet() const { return bookmark_ != kNoBookmark && bookmark_ != kBookmarkWasApplied; } -bool Scanner::BookmarkScope::HasBeenApplied() { +bool Scanner::BookmarkScope::HasBeenApplied() const { return bookmark_ == kBookmarkWasApplied; } @@ -175,12 +173,11 @@ Scanner::Scanner(UnicodeCache* unicode_cache, Utf16CharacterStream* source, bool is_module) : unicode_cache_(unicode_cache), source_(source), - octal_pos_(Location::invalid()), - octal_message_(MessageTemplate::kNone), found_html_comment_(false), - allow_harmony_bigint_(false), allow_harmony_numeric_separator_(false), - is_module_(is_module) { + is_module_(is_module), + octal_pos_(Location::invalid()), + octal_message_(MessageTemplate::kNone) { DCHECK_NOT_NULL(source); } @@ -234,146 +231,7 @@ uc32 Scanner::ScanUnlimitedLengthHexNumber(int max_value, int beg_pos) { return x; } - -// Ensure that tokens can be stored in a byte. -STATIC_ASSERT(Token::NUM_TOKENS <= 0x100); - -// Table of one-character tokens, by character (0x00..0x7F only). -// clang-format off -static const byte one_char_tokens[] = { - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::LPAREN, // 0x28 - Token::RPAREN, // 0x29 - Token::ILLEGAL, - Token::ILLEGAL, - Token::COMMA, // 0x2C - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::COLON, // 0x3A - Token::SEMICOLON, // 0x3B - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::CONDITIONAL, // 0x3F - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::LBRACK, // 0x5B - Token::ILLEGAL, - Token::RBRACK, // 0x5D - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::LBRACE, // 0x7B - Token::ILLEGAL, - Token::RBRACE, // 0x7D - Token::BIT_NOT, // 0x7E - Token::ILLEGAL -}; -// clang-format on - Token::Value Scanner::Next() { - if (next().token == Token::EOS) next().location = current().location; // Rotate through tokens. TokenDesc* previous = current_; current_ = next_; @@ -395,7 +253,6 @@ Token::Value Scanner::Next() { return current().token; } - Token::Value Scanner::PeekAhead() { DCHECK(next().token != Token::DIV); DCHECK(next().token != Token::ASSIGN_DIV); @@ -534,250 +391,6 @@ Token::Value Scanner::ScanHtmlComment() { return SkipSingleHTMLComment(); } -void Scanner::Scan() { - next().literal_chars.Drop(); - next().raw_literal_chars.Drop(); - next().invalid_template_escape_message = MessageTemplate::kNone; - - Token::Value token; - do { - if (static_cast<unsigned>(c0_) <= 0x7F) { - Token::Value token = static_cast<Token::Value>(one_char_tokens[c0_]); - if (token != Token::ILLEGAL) { - int pos = source_pos(); - next().token = token; - next().contextual_token = Token::UNINITIALIZED; - next().location.beg_pos = pos; - next().location.end_pos = pos + 1; - Advance(); - return; - } - } - - // Remember the position of the next token - next().location.beg_pos = source_pos(); - - switch (c0_) { - case '"': - case '\'': - token = ScanString(); - break; - - case '<': - // < <= << <<= <!-- - Advance(); - if (c0_ == '=') { - token = Select(Token::LTE); - } else if (c0_ == '<') { - token = Select('=', Token::ASSIGN_SHL, Token::SHL); - } else if (c0_ == '!') { - token = ScanHtmlComment(); - } else { - token = Token::LT; - } - break; - - case '>': - // > >= >> >>= >>> >>>= - Advance(); - if (c0_ == '=') { - token = Select(Token::GTE); - } else if (c0_ == '>') { - // >> >>= >>> >>>= - Advance(); - if (c0_ == '=') { - token = Select(Token::ASSIGN_SAR); - } else if (c0_ == '>') { - token = Select('=', Token::ASSIGN_SHR, Token::SHR); - } else { - token = Token::SAR; - } - } else { - token = Token::GT; - } - break; - - case '=': - // = == === => - Advance(); - if (c0_ == '=') { - token = Select('=', Token::EQ_STRICT, Token::EQ); - } else if (c0_ == '>') { - token = Select(Token::ARROW); - } else { - token = Token::ASSIGN; - } - break; - - case '!': - // ! != !== - Advance(); - if (c0_ == '=') { - token = Select('=', Token::NE_STRICT, Token::NE); - } else { - token = Token::NOT; - } - break; - - case '+': - // + ++ += - Advance(); - if (c0_ == '+') { - token = Select(Token::INC); - } else if (c0_ == '=') { - token = Select(Token::ASSIGN_ADD); - } else { - token = Token::ADD; - } - break; - - case '-': - // - -- --> -= - Advance(); - if (c0_ == '-') { - Advance(); - if (c0_ == '>' && HasLineTerminatorBeforeNext()) { - // For compatibility with SpiderMonkey, we skip lines that - // start with an HTML comment end '-->'. - token = SkipSingleHTMLComment(); - } else { - token = Token::DEC; - } - } else if (c0_ == '=') { - token = Select(Token::ASSIGN_SUB); - } else { - token = Token::SUB; - } - break; - - case '*': - // * *= - Advance(); - if (c0_ == '*') { - token = Select('=', Token::ASSIGN_EXP, Token::EXP); - } else if (c0_ == '=') { - token = Select(Token::ASSIGN_MUL); - } else { - token = Token::MUL; - } - break; - - case '%': - // % %= - token = Select('=', Token::ASSIGN_MOD, Token::MOD); - break; - - case '/': - // / // /* /= - Advance(); - if (c0_ == '/') { - uc32 c = Peek(); - if (c == '#' || c == '@') { - Advance(); - Advance(); - token = SkipSourceURLComment(); - } else { - token = SkipSingleLineComment(); - } - } else if (c0_ == '*') { - token = SkipMultiLineComment(); - } else if (c0_ == '=') { - token = Select(Token::ASSIGN_DIV); - } else { - token = Token::DIV; - } - break; - - case '&': - // & && &= - Advance(); - if (c0_ == '&') { - token = Select(Token::AND); - } else if (c0_ == '=') { - token = Select(Token::ASSIGN_BIT_AND); - } else { - token = Token::BIT_AND; - } - break; - - case '|': - // | || |= - Advance(); - if (c0_ == '|') { - token = Select(Token::OR); - } else if (c0_ == '=') { - token = Select(Token::ASSIGN_BIT_OR); - } else { - token = Token::BIT_OR; - } - break; - - case '^': - // ^ ^= - token = Select('=', Token::ASSIGN_BIT_XOR, Token::BIT_XOR); - break; - - case '.': - // . Number - Advance(); - if (IsDecimalDigit(c0_)) { - token = ScanNumber(true); - } else { - token = Token::PERIOD; - if (c0_ == '.') { - if (Peek() == '.') { - Advance(); - Advance(); - token = Token::ELLIPSIS; - } - } - } - break; - - case '`': - token = ScanTemplateStart(); - break; - - case '#': - token = ScanPrivateName(); - break; - - default: - if (unicode_cache_->IsIdentifierStart(c0_) || - (CombineSurrogatePair() && - unicode_cache_->IsIdentifierStart(c0_))) { - token = ScanIdentifierOrKeyword(); - } else if (IsDecimalDigit(c0_)) { - token = ScanNumber(false); - } else if (c0_ == kEndOfInput) { - token = Token::EOS; - } else { - token = SkipWhiteSpace(); - if (token == Token::ILLEGAL) Advance(); - } - break; - } - - // Continue scanning for tokens as long as we're just skipping - // whitespace. - } while (token == Token::WHITESPACE); - - next().location.end_pos = source_pos(); - if (Token::IsContextualKeyword(token)) { - next().token = Token::IDENTIFIER; - next().contextual_token = token; - } else { - next().token = token; - next().contextual_token = Token::UNINITIALIZED; - } - -#ifdef DEBUG - SanityCheckTokenDesc(current()); - SanityCheckTokenDesc(next()); - SanityCheckTokenDesc(next_next()); -#endif -} - #ifdef DEBUG void Scanner::SanityCheckTokenDesc(const TokenDesc& token) const { // Most tokens should not have literal_chars or even raw_literal chars. @@ -916,7 +529,7 @@ uc32 Scanner::ScanOctalEscape(uc32 c, int length) { // can be reported later (in strict mode). // We don't report the error immediately, because the octal escape can // occur before the "use strict" directive. - if (c != '0' || i > 0 || c0_ == '8' || c0_ == '9') { + if (c != '0' || i > 0 || IsNonOctalDecimalDigit(c0_)) { octal_pos_ = Location(source_pos() - i - 1, source_pos() - 1); octal_message_ = capture_raw ? MessageTemplate::kTemplateOctalLiteral : MessageTemplate::kStrictOctalEscape; @@ -993,7 +606,7 @@ Token::Value Scanner::ScanTemplateSpan() { Token::Value result = Token::TEMPLATE_SPAN; LiteralScope literal(this); - StartRawLiteral(); + next().raw_literal_chars.Start(); const bool capture_raw = true; while (true) { uc32 c = c0_; @@ -1053,14 +666,6 @@ Token::Value Scanner::ScanTemplateSpan() { return result; } -Token::Value Scanner::ScanTemplateStart() { - DCHECK_EQ(next_next().token, Token::UNINITIALIZED); - DCHECK_EQ(c0_, '`'); - next().location.beg_pos = source_pos(); - Advance(); // Consume ` - return ScanTemplateSpan(); -} - Handle<String> Scanner::SourceUrl(Isolate* isolate) const { Handle<String> tmp; if (source_url_.length() > 0) { @@ -1200,11 +805,11 @@ bool Scanner::ScanImplicitOctalDigits(int start_pos, while (true) { // (possible) octal number - if (c0_ == '8' || c0_ == '9') { + if (IsNonOctalDecimalDigit(c0_)) { *kind = DECIMAL_WITH_LEADING_ZERO; return true; } - if (c0_ < '0' || '7' < c0_) { + if (!IsOctalDigit(c0_)) { // Octal literal finished. octal_pos_ = Location(start_pos, source_pos()); octal_message_ = MessageTemplate::kStrictOctalLiteral; @@ -1272,7 +877,7 @@ Token::Value Scanner::ScanNumber(bool seen_period) { AddLiteralCharAdvance(); kind = BINARY; if (!ScanBinaryDigits()) return Token::ILLEGAL; - } else if ('0' <= c0_ && c0_ <= '7') { + } else if (IsOctalDigit(c0_)) { kind = IMPLICIT_OCTAL; if (!ScanImplicitOctalDigits(start_pos, &kind)) { return Token::ILLEGAL; @@ -1280,7 +885,7 @@ Token::Value Scanner::ScanNumber(bool seen_period) { if (kind == DECIMAL_WITH_LEADING_ZERO) { at_start = false; } - } else if (c0_ == '8' || c0_ == '9') { + } else if (IsNonOctalDecimalDigit(c0_)) { kind = DECIMAL_WITH_LEADING_ZERO; } else if (allow_harmony_numeric_separator() && c0_ == '_') { ReportScannerError(Location(source_pos(), source_pos() + 1), @@ -1326,7 +931,7 @@ Token::Value Scanner::ScanNumber(bool seen_period) { } bool is_bigint = false; - if (allow_harmony_bigint() && c0_ == 'n' && !seen_period && + if (c0_ == 'n' && !seen_period && (kind == DECIMAL || kind == HEX || kind == OCTAL || kind == BINARY)) { // Check that the literal is within our limits for BigInt length. // For simplicity, use 4 bits per character to calculate the maximum @@ -1399,197 +1004,8 @@ uc32 Scanner::ScanUnicodeEscape() { return ScanHexNumber<capture_raw, unicode>(4); } - -// ---------------------------------------------------------------------------- -// Keyword Matcher - -#define KEYWORDS(KEYWORD_GROUP, KEYWORD) \ - KEYWORD_GROUP('a') \ - KEYWORD("arguments", Token::ARGUMENTS) \ - KEYWORD("as", Token::AS) \ - KEYWORD("async", Token::ASYNC) \ - KEYWORD("await", Token::AWAIT) \ - KEYWORD("anonymous", Token::ANONYMOUS) \ - KEYWORD_GROUP('b') \ - KEYWORD("break", Token::BREAK) \ - KEYWORD_GROUP('c') \ - KEYWORD("case", Token::CASE) \ - KEYWORD("catch", Token::CATCH) \ - KEYWORD("class", Token::CLASS) \ - KEYWORD("const", Token::CONST) \ - KEYWORD("constructor", Token::CONSTRUCTOR) \ - KEYWORD("continue", Token::CONTINUE) \ - KEYWORD_GROUP('d') \ - KEYWORD("debugger", Token::DEBUGGER) \ - KEYWORD("default", Token::DEFAULT) \ - KEYWORD("delete", Token::DELETE) \ - KEYWORD("do", Token::DO) \ - KEYWORD_GROUP('e') \ - KEYWORD("else", Token::ELSE) \ - KEYWORD("enum", Token::ENUM) \ - KEYWORD("eval", Token::EVAL) \ - KEYWORD("export", Token::EXPORT) \ - KEYWORD("extends", Token::EXTENDS) \ - KEYWORD_GROUP('f') \ - KEYWORD("false", Token::FALSE_LITERAL) \ - KEYWORD("finally", Token::FINALLY) \ - KEYWORD("for", Token::FOR) \ - KEYWORD("from", Token::FROM) \ - KEYWORD("function", Token::FUNCTION) \ - KEYWORD_GROUP('g') \ - KEYWORD("get", Token::GET) \ - KEYWORD_GROUP('i') \ - KEYWORD("if", Token::IF) \ - KEYWORD("implements", Token::FUTURE_STRICT_RESERVED_WORD) \ - KEYWORD("import", Token::IMPORT) \ - KEYWORD("in", Token::IN) \ - KEYWORD("instanceof", Token::INSTANCEOF) \ - KEYWORD("interface", Token::FUTURE_STRICT_RESERVED_WORD) \ - KEYWORD_GROUP('l') \ - KEYWORD("let", Token::LET) \ - KEYWORD_GROUP('m') \ - KEYWORD("meta", Token::META) \ - KEYWORD_GROUP('n') \ - KEYWORD("name", Token::NAME) \ - KEYWORD("new", Token::NEW) \ - KEYWORD("null", Token::NULL_LITERAL) \ - KEYWORD_GROUP('o') \ - KEYWORD("of", Token::OF) \ - KEYWORD_GROUP('p') \ - KEYWORD("package", Token::FUTURE_STRICT_RESERVED_WORD) \ - KEYWORD("private", Token::FUTURE_STRICT_RESERVED_WORD) \ - KEYWORD("protected", Token::FUTURE_STRICT_RESERVED_WORD) \ - KEYWORD("prototype", Token::PROTOTYPE) \ - KEYWORD("public", Token::FUTURE_STRICT_RESERVED_WORD) \ - KEYWORD_GROUP('r') \ - KEYWORD("return", Token::RETURN) \ - KEYWORD_GROUP('s') \ - KEYWORD("set", Token::SET) \ - KEYWORD("static", Token::STATIC) \ - KEYWORD("super", Token::SUPER) \ - KEYWORD("switch", Token::SWITCH) \ - KEYWORD_GROUP('t') \ - KEYWORD("target", Token::TARGET) \ - KEYWORD("this", Token::THIS) \ - KEYWORD("throw", Token::THROW) \ - KEYWORD("true", Token::TRUE_LITERAL) \ - KEYWORD("try", Token::TRY) \ - KEYWORD("typeof", Token::TYPEOF) \ - KEYWORD_GROUP('u') \ - KEYWORD("undefined", Token::UNDEFINED) \ - KEYWORD_GROUP('v') \ - KEYWORD("var", Token::VAR) \ - KEYWORD("void", Token::VOID) \ - KEYWORD_GROUP('w') \ - KEYWORD("while", Token::WHILE) \ - KEYWORD("with", Token::WITH) \ - KEYWORD_GROUP('y') \ - KEYWORD("yield", Token::YIELD) \ - KEYWORD_GROUP('_') \ - KEYWORD("__proto__", Token::PROTO_UNDERSCORED) \ - KEYWORD_GROUP('#') \ - KEYWORD("#constructor", Token::PRIVATE_CONSTRUCTOR) - -static Token::Value KeywordOrIdentifierToken(const uint8_t* input, - int input_length) { - DCHECK_GE(input_length, 1); - const int kMinLength = 2; - const int kMaxLength = 12; - if (input_length < kMinLength || input_length > kMaxLength) { - return Token::IDENTIFIER; - } - switch (input[0]) { - default: -#define KEYWORD_GROUP_CASE(ch) \ - break; \ - case ch: -#define KEYWORD(keyword, token) \ - { \ - /* 'keyword' is a char array, so sizeof(keyword) is */ \ - /* strlen(keyword) plus 1 for the NUL char. */ \ - const int keyword_length = sizeof(keyword) - 1; \ - STATIC_ASSERT(keyword_length >= kMinLength); \ - STATIC_ASSERT(keyword_length <= kMaxLength); \ - DCHECK_EQ(input[0], keyword[0]); \ - DCHECK(token == Token::FUTURE_STRICT_RESERVED_WORD || \ - 0 == strncmp(keyword, Token::String(token), sizeof(keyword))); \ - if (input_length == keyword_length && input[1] == keyword[1] && \ - (keyword_length <= 2 || input[2] == keyword[2]) && \ - (keyword_length <= 3 || input[3] == keyword[3]) && \ - (keyword_length <= 4 || input[4] == keyword[4]) && \ - (keyword_length <= 5 || input[5] == keyword[5]) && \ - (keyword_length <= 6 || input[6] == keyword[6]) && \ - (keyword_length <= 7 || input[7] == keyword[7]) && \ - (keyword_length <= 8 || input[8] == keyword[8]) && \ - (keyword_length <= 9 || input[9] == keyword[9]) && \ - (keyword_length <= 10 || input[10] == keyword[10])) { \ - return token; \ - } \ - } - KEYWORDS(KEYWORD_GROUP_CASE, KEYWORD) - } - return Token::IDENTIFIER; -#undef KEYWORDS -#undef KEYWORD -#undef KEYWORD_GROUP_CASE -} - -Token::Value Scanner::ScanIdentifierOrKeyword() { - LiteralScope literal(this); - return ScanIdentifierOrKeywordInner(&literal); -} - -Token::Value Scanner::ScanIdentifierOrKeywordInner(LiteralScope* literal) { - DCHECK(unicode_cache_->IsIdentifierStart(c0_)); - bool escaped = false; - if (IsInRange(c0_, 'a', 'z') || c0_ == '_') { - do { - AddLiteralChar(static_cast<char>(c0_)); - Advance(); - } while (IsInRange(c0_, 'a', 'z') || c0_ == '_'); - - if (IsDecimalDigit(c0_) || IsInRange(c0_, 'A', 'Z') || c0_ == '$') { - // Identifier starting with lowercase or _. - do { - AddLiteralChar(static_cast<char>(c0_)); - Advance(); - } while (IsAsciiIdentifier(c0_)); - - if (c0_ <= kMaxAscii && c0_ != '\\') { - literal->Complete(); - return Token::IDENTIFIER; - } - } else if (c0_ <= kMaxAscii && c0_ != '\\') { - // Only a-z+ or _: could be a keyword or identifier. - Vector<const uint8_t> chars = next().literal_chars.one_byte_literal(); - Token::Value token = - KeywordOrIdentifierToken(chars.start(), chars.length()); - if (token == Token::IDENTIFIER || - token == Token::FUTURE_STRICT_RESERVED_WORD || - Token::IsContextualKeyword(token)) - literal->Complete(); - return token; - } - } else if (IsInRange(c0_, 'A', 'Z') || c0_ == '$') { - do { - AddLiteralChar(static_cast<char>(c0_)); - Advance(); - } while (IsAsciiIdentifier(c0_)); - - if (c0_ <= kMaxAscii && c0_ != '\\') { - literal->Complete(); - return Token::IDENTIFIER; - } - } else if (c0_ == '\\') { - escaped = true; - uc32 c = ScanIdentifierUnicodeEscape(); - DCHECK(!unicode_cache_->IsIdentifierStart(-1)); - if (c == '\\' || !unicode_cache_->IsIdentifierStart(c)) { - return Token::ILLEGAL; - } - AddLiteralChar(c); - } - +Token::Value Scanner::ScanIdentifierOrKeywordInnerSlow(LiteralScope* literal, + bool escaped) { while (true) { if (c0_ == '\\') { escaped = true; @@ -1645,18 +1061,12 @@ bool Scanner::ScanRegExpPattern() { // Scan: ('/' | '/=') RegularExpressionBody '/' RegularExpressionFlags bool in_character_class = false; - bool seen_equal = (next().token == Token::ASSIGN_DIV); - - // Previous token is either '/' or '/=', in the second case, the - // pattern starts at =. - next().location.beg_pos = source_pos() - (seen_equal ? 2 : 1); - next().location.end_pos = source_pos() - (seen_equal ? 1 : 0); // Scan regular expression body: According to ECMA-262, 3rd, 7.8.5, // the scanner should pass uninterpreted bodies to the RegExp // constructor. LiteralScope literal(this); - if (seen_equal) { + if (next().token == Token::ASSIGN_DIV) { AddLiteralChar('='); } diff --git a/deps/v8/src/parsing/scanner.h b/deps/v8/src/parsing/scanner.h index e592debd8e..83002b53c8 100644 --- a/deps/v8/src/parsing/scanner.h +++ b/deps/v8/src/parsing/scanner.h @@ -21,13 +21,13 @@ namespace v8 { namespace internal { - class AstRawString; class AstValueFactory; class DuplicateFinder; class ExternalOneByteString; class ExternalTwoByteString; class ParserRecorder; +class RuntimeCallStats; class UnicodeCache; // --------------------------------------------------------------------- @@ -38,7 +38,7 @@ class Utf16CharacterStream { public: static const uc32 kEndOfInput = -1; - virtual ~Utf16CharacterStream() {} + virtual ~Utf16CharacterStream() = default; inline uc32 Peek() { if (V8_LIKELY(buffer_cursor_ < buffer_end_)) { @@ -109,8 +109,21 @@ class Utf16CharacterStream { } } + // Returns true if the stream can be cloned with Clone. + // TODO(rmcilroy): Remove this once ChunkedStreams can be cloned. + virtual bool can_be_cloned() const = 0; + + // Clones the character stream to enable another independent scanner to access + // the same underlying stream. + virtual std::unique_ptr<Utf16CharacterStream> Clone() const = 0; + // Returns true if the stream could access the V8 heap after construction. - virtual bool can_access_heap() = 0; + virtual bool can_access_heap() const = 0; + + RuntimeCallStats* runtime_call_stats() const { return runtime_call_stats_; } + void set_runtime_call_stats(RuntimeCallStats* runtime_call_stats) { + runtime_call_stats_ = runtime_call_stats; + } protected: Utf16CharacterStream(const uint16_t* buffer_start, @@ -172,6 +185,7 @@ class Utf16CharacterStream { const uint16_t* buffer_cursor_; const uint16_t* buffer_end_; size_t buffer_pos_; + RuntimeCallStats* runtime_call_stats_; }; // ---------------------------------------------------------------------------- @@ -186,12 +200,12 @@ class Scanner { : scanner_(scanner), bookmark_(kNoBookmark) { DCHECK_NOT_NULL(scanner_); } - ~BookmarkScope() {} + ~BookmarkScope() = default; void Set(); void Apply(); - bool HasBeenSet(); - bool HasBeenApplied(); + bool HasBeenSet() const; + bool HasBeenApplied() const; private: static const size_t kNoBookmark; @@ -233,19 +247,21 @@ class Scanner { // Returns the token following peek() Token::Value PeekAhead(); // Returns the current token again. - Token::Value current_token() { return current().token; } + Token::Value current_token() const { return current().token; } - Token::Value current_contextual_token() { return current().contextual_token; } - Token::Value next_contextual_token() { return next().contextual_token; } + Token::Value current_contextual_token() const { + return current().contextual_token; + } + Token::Value next_contextual_token() const { return next().contextual_token; } // Returns the location information for the current token // (the token last returned by Next()). - Location location() const { return current().location; } + const Location& location() const { return current().location; } // This error is specifically an invalid hex or unicode escape sequence. bool has_error() const { return scanner_error_ != MessageTemplate::kNone; } MessageTemplate::Template error() const { return scanner_error_; } - Location error_location() const { return scanner_error_location_; } + const Location& error_location() const { return scanner_error_location_; } bool has_invalid_template_escape() const { return current().invalid_template_escape_message != MessageTemplate::kNone; @@ -264,13 +280,14 @@ class Scanner { // One token look-ahead (past the token returned by Next()). Token::Value peek() const { return next().token; } - Location peek_location() const { return next().location; } + const Location& peek_location() const { return next().location; } bool literal_contains_escapes() const { return LiteralContainsEscapes(current()); } const AstRawString* CurrentSymbol(AstValueFactory* ast_value_factory) const; + const AstRawString* NextSymbol(AstValueFactory* ast_value_factory) const; const AstRawString* CurrentRawSymbol( AstValueFactory* ast_value_factory) const; @@ -286,7 +303,7 @@ class Scanner { inline bool CurrentMatchesContextual(Token::Value token) const { DCHECK(Token::IsContextualKeyword(token)); - return current().contextual_token == token; + return current_contextual_token() == token; } // Match the token against the contextual keyword or literal buffer. @@ -297,7 +314,7 @@ class Scanner { // (which was escape-processed already). // Conveniently, !current().literal_chars.is_used() for all proper // keywords, so this second condition should exit early in common cases. - return (current().contextual_token == token) || + return (current_contextual_token() == token) || (current().literal_chars.is_used() && current().literal_chars.Equals(Vector<const char>( Token::String(token), Token::StringLength(token)))); @@ -308,11 +325,11 @@ class Scanner { current().literal_chars.Equals( Vector<const char>("use strict", strlen("use strict"))); } - bool IsGetOrSet(bool* is_get, bool* is_set) const { - *is_get = CurrentMatchesContextual(Token::GET); - *is_set = CurrentMatchesContextual(Token::SET); - return *is_get || *is_set; - } + + bool IsGet() { return CurrentMatchesContextual(Token::GET); } + + bool IsSet() { return CurrentMatchesContextual(Token::SET); } + bool IsLet() const { return CurrentMatches(Token::LET) || CurrentMatchesContextualEscaped(Token::LET); @@ -324,7 +341,7 @@ class Scanner { bool IsDuplicateSymbol(DuplicateFinder* duplicate_finder, AstValueFactory* ast_value_factory) const; - UnicodeCache* unicode_cache() { return unicode_cache_; } + UnicodeCache* unicode_cache() const { return unicode_cache_; } // Returns the location of the last seen octal literal. Location octal_position() const { return octal_pos_; } @@ -362,10 +379,9 @@ class Scanner { Maybe<RegExp::Flags> ScanRegExpFlags(); // Scans the input as a template literal - Token::Value ScanTemplateStart(); Token::Value ScanTemplateContinuation() { DCHECK_EQ(next().token, Token::RBRACE); - next().location.beg_pos = source_pos() - 1; // We already consumed } + DCHECK_EQ(source_pos() - 1, next().location.beg_pos); return ScanTemplateSpan(); } @@ -374,8 +390,6 @@ class Scanner { bool FoundHtmlComment() const { return found_html_comment_; } - bool allow_harmony_bigint() const { return allow_harmony_bigint_; } - void set_allow_harmony_bigint(bool allow) { allow_harmony_bigint_ = allow; } bool allow_harmony_private_fields() const { return allow_harmony_private_fields_; } @@ -389,34 +403,19 @@ class Scanner { allow_harmony_numeric_separator_ = allow; } + const Utf16CharacterStream* stream() const { return source_; } + private: // Scoped helper for saving & restoring scanner error state. // This is used for tagged template literals, in which normally forbidden // escape sequences are allowed. class ErrorState; - // Scoped helper for literal recording. Automatically drops the literal - // if aborting the scanning before it's complete. - class LiteralScope { - public: - explicit LiteralScope(Scanner* self) : scanner_(self), complete_(false) { - scanner_->StartLiteral(); - } - ~LiteralScope() { - if (!complete_) scanner_->DropLiteral(); - } - void Complete() { complete_ = true; } - - private: - Scanner* scanner_; - bool complete_; - }; - // LiteralBuffer - Collector of chars of literals. class LiteralBuffer { public: LiteralBuffer() - : position_(0), is_one_byte_(true), is_used_(false), backing_store_() {} + : backing_store_(), position_(0), is_one_byte_(true), is_used_(false) {} ~LiteralBuffer() { backing_store_.Dispose(); } @@ -506,14 +505,32 @@ class Scanner { void ExpandBuffer(); void ConvertToTwoByte(); + Vector<byte> backing_store_; int position_; bool is_one_byte_; bool is_used_; - Vector<byte> backing_store_; DISALLOW_COPY_AND_ASSIGN(LiteralBuffer); }; + // Scoped helper for literal recording. Automatically drops the literal + // if aborting the scanning before it's complete. + class LiteralScope { + public: + explicit LiteralScope(Scanner* scanner) + : buffer_(&scanner->next().literal_chars), complete_(false) { + buffer_->Start(); + } + ~LiteralScope() { + if (!complete_) buffer_->Drop(); + } + void Complete() { complete_ = true; } + + private: + LiteralBuffer* buffer_; + bool complete_; + }; + // The current and look-ahead token. struct TokenDesc { Location location = {0, 0}; @@ -538,7 +555,7 @@ class Scanner { }; static const int kCharacterLookaheadBufferSize = 1; - const int kMaxAscii = 127; + static const int kMaxAscii = 127; // Scans octal escape sequence. Also accepts "\0" decimal escape sequence. template <bool capture_raw> @@ -574,11 +591,6 @@ class Scanner { // Seek to the next_ token at the given position. void SeekNext(size_t position); - // Literal buffer support - inline void StartLiteral() { next().literal_chars.Start(); } - - inline void StartRawLiteral() { next().raw_literal_chars.Start(); } - V8_INLINE void AddLiteralChar(uc32 c) { next().literal_chars.AddChar(c); } V8_INLINE void AddLiteralChar(char c) { next().literal_chars.AddChar(c); } @@ -587,14 +599,7 @@ class Scanner { next().raw_literal_chars.AddChar(c); } - // Stops scanning of a literal and drop the collected characters, - // e.g., due to an encountered error. - inline void DropLiteral() { - next().literal_chars.Drop(); - next().raw_literal_chars.Drop(); - } - - inline void AddLiteralCharAdvance() { + V8_INLINE void AddLiteralCharAdvance() { AddLiteralChar(c0_); Advance(); } @@ -714,7 +719,8 @@ class Scanner { uc32 ScanUnlimitedLengthHexNumber(int max_value, int beg_pos); // Scans a single JavaScript token. - void Scan(); + V8_INLINE Token::Value ScanSingleToken(); + V8_INLINE void Scan(); V8_INLINE Token::Value SkipWhiteSpace(); Token::Value SkipSingleHTMLComment(); @@ -738,8 +744,10 @@ class Scanner { bool ScanImplicitOctalDigits(int start_pos, NumberKind* kind); Token::Value ScanNumber(bool seen_period); - Token::Value ScanIdentifierOrKeyword(); - Token::Value ScanIdentifierOrKeywordInner(LiteralScope* literal); + V8_INLINE Token::Value ScanIdentifierOrKeyword(); + V8_INLINE Token::Value ScanIdentifierOrKeywordInner(LiteralScope* literal); + Token::Value ScanIdentifierOrKeywordInnerSlow(LiteralScope* literal, + bool escaped); Token::Value ScanString(); Token::Value ScanPrivateName(); @@ -779,13 +787,7 @@ class Scanner { void SanityCheckTokenDesc(const TokenDesc&) const; #endif - UnicodeCache* unicode_cache_; - - // Values parsed from magic comments. - LiteralBuffer source_url_; - LiteralBuffer source_mapping_url_; - - TokenDesc token_storage_[3]; + UnicodeCache* const unicode_cache_; TokenDesc& next() { return *next_; } @@ -800,23 +802,28 @@ class Scanner { // Input stream. Must be initialized to an Utf16CharacterStream. Utf16CharacterStream* const source_; - // Last-seen positions of potentially problematic tokens. - Location octal_pos_; - MessageTemplate::Template octal_message_; - // One Unicode character look-ahead; c0_ < 0 at the end of the input. uc32 c0_; + TokenDesc token_storage_[3]; + // Whether this scanner encountered an HTML comment. bool found_html_comment_; // Harmony flags to allow ESNext features. - bool allow_harmony_bigint_; bool allow_harmony_private_fields_; bool allow_harmony_numeric_separator_; const bool is_module_; + // Values parsed from magic comments. + LiteralBuffer source_url_; + LiteralBuffer source_mapping_url_; + + // Last-seen positions of potentially problematic tokens. + Location octal_pos_; + MessageTemplate::Template octal_message_; + MessageTemplate::Template scanner_error_; Location scanner_error_location_; }; diff --git a/deps/v8/src/parsing/token.cc b/deps/v8/src/parsing/token.cc index 258c7b5d09..4cbf244a2b 100644 --- a/deps/v8/src/parsing/token.cc +++ b/deps/v8/src/parsing/token.cc @@ -29,7 +29,6 @@ const uint8_t Token::string_length_[NUM_TOKENS] = {TOKEN_LIST(T, T, T)}; const int8_t Token::precedence_[NUM_TOKENS] = {TOKEN_LIST(T, T, T)}; #undef T - #define KT(a, b, c) 'T', #define KK(a, b, c) 'K', #define KC(a, b, c) 'C', diff --git a/deps/v8/src/parsing/token.h b/deps/v8/src/parsing/token.h index 660f24361c..e1c6239e36 100644 --- a/deps/v8/src/parsing/token.h +++ b/deps/v8/src/parsing/token.h @@ -7,6 +7,7 @@ #include "src/base/logging.h" #include "src/globals.h" +#include "src/utils.h" namespace v8 { namespace internal { @@ -32,6 +33,27 @@ namespace internal { #define IGNORE_TOKEN(name, string, precedence) +/* Binary operators sorted by precedence */ +#define BINARY_OP_TOKEN_LIST(T, E) \ + E(T, BIT_OR, "|", 6) \ + E(T, BIT_XOR, "^", 7) \ + E(T, BIT_AND, "&", 8) \ + E(T, SHL, "<<", 11) \ + E(T, SAR, ">>", 11) \ + E(T, SHR, ">>>", 11) \ + E(T, ADD, "+", 12) \ + E(T, SUB, "-", 12) \ + E(T, MUL, "*", 13) \ + E(T, DIV, "/", 13) \ + E(T, MOD, "%", 13) \ + E(T, EXP, "**", 14) + +#define EXPAND_BINOP_ASSIGN_TOKEN(T, name, string, precedence) \ + T(ASSIGN_##name, string "=", 2) + +#define EXPAND_BINOP_TOKEN(T, name, string, precedence) \ + T(name, string, precedence) + #define TOKEN_LIST(T, K, C) \ /* End of source indicator. */ \ T(EOS, "EOS", 0) \ @@ -57,18 +79,7 @@ namespace internal { /* contiguous and sorted in the same order! */ \ T(INIT, "=init", 2) /* AST-use only. */ \ T(ASSIGN, "=", 2) \ - T(ASSIGN_BIT_OR, "|=", 2) \ - T(ASSIGN_BIT_XOR, "^=", 2) \ - T(ASSIGN_BIT_AND, "&=", 2) \ - T(ASSIGN_SHL, "<<=", 2) \ - T(ASSIGN_SAR, ">>=", 2) \ - T(ASSIGN_SHR, ">>>=", 2) \ - T(ASSIGN_ADD, "+=", 2) \ - T(ASSIGN_SUB, "-=", 2) \ - T(ASSIGN_MUL, "*=", 2) \ - T(ASSIGN_DIV, "/=", 2) \ - T(ASSIGN_MOD, "%=", 2) \ - T(ASSIGN_EXP, "**=", 2) \ + BINARY_OP_TOKEN_LIST(T, EXPAND_BINOP_ASSIGN_TOKEN) \ \ /* Binary operators sorted by precedence. */ \ /* IsBinaryOp() relies on this block of enum values */ \ @@ -76,25 +87,14 @@ namespace internal { T(COMMA, ",", 1) \ T(OR, "||", 4) \ T(AND, "&&", 5) \ - T(BIT_OR, "|", 6) \ - T(BIT_XOR, "^", 7) \ - T(BIT_AND, "&", 8) \ - T(SHL, "<<", 11) \ - T(SAR, ">>", 11) \ - T(SHR, ">>>", 11) \ - T(ADD, "+", 12) \ - T(SUB, "-", 12) \ - T(MUL, "*", 13) \ - T(DIV, "/", 13) \ - T(MOD, "%", 13) \ - T(EXP, "**", 14) \ + BINARY_OP_TOKEN_LIST(T, EXPAND_BINOP_TOKEN) \ \ /* Compare operators sorted by precedence. */ \ /* IsCompareOp() relies on this block of enum values */ \ /* being contiguous and sorted in the same order! */ \ T(EQ, "==", 9) \ - T(NE, "!=", 9) \ T(EQ_STRICT, "===", 9) \ + T(NE, "!=", 9) \ T(NE_STRICT, "!==", 9) \ T(LT, "<", 10) \ T(GT, ">", 10) \ @@ -131,7 +131,6 @@ namespace internal { K(NEW, "new", 0) \ K(RETURN, "return", 0) \ K(SWITCH, "switch", 0) \ - K(THIS, "this", 0) \ K(THROW, "throw", 0) \ K(TRY, "try", 0) \ /* TYPEOF */ \ @@ -139,6 +138,7 @@ namespace internal { /* VOID */ \ K(WHILE, "while", 0) \ K(WITH, "with", 0) \ + K(THIS, "this", 0) \ \ /* Literals (ECMA-262, section 7.8, page 16). */ \ K(NULL_LITERAL, "null", 0) \ @@ -146,33 +146,34 @@ namespace internal { K(FALSE_LITERAL, "false", 0) \ T(NUMBER, nullptr, 0) \ T(SMI, nullptr, 0) \ - T(STRING, nullptr, 0) \ T(BIGINT, nullptr, 0) \ + T(STRING, nullptr, 0) \ \ + /* BEGIN AnyIdentifier */ \ /* Identifiers (not keywords or future reserved words). */ \ T(IDENTIFIER, nullptr, 0) \ - T(PRIVATE_NAME, nullptr, 0) \ - \ - /* Future reserved words (ECMA-262, section 7.6.1.2). */ \ - T(FUTURE_STRICT_RESERVED_WORD, nullptr, 0) \ K(ASYNC, "async", 0) \ /* `await` is a reserved word in module code only */ \ K(AWAIT, "await", 0) \ + K(YIELD, "yield", 0) \ + K(LET, "let", 0) \ + K(STATIC, "static", 0) \ + /* Future reserved words (ECMA-262, section 7.6.1.2). */ \ + T(FUTURE_STRICT_RESERVED_WORD, nullptr, 0) \ + T(ESCAPED_STRICT_RESERVED_WORD, nullptr, 0) \ + K(ENUM, "enum", 0) \ + /* END AnyIdentifier */ \ K(CLASS, "class", 0) \ K(CONST, "const", 0) \ - K(ENUM, "enum", 0) \ K(EXPORT, "export", 0) \ K(EXTENDS, "extends", 0) \ K(IMPORT, "import", 0) \ - K(LET, "let", 0) \ - K(STATIC, "static", 0) \ - K(YIELD, "yield", 0) \ K(SUPER, "super", 0) \ + T(PRIVATE_NAME, nullptr, 0) \ \ /* Illegal token - not able to scan. */ \ T(ILLEGAL, "ILLEGAL", 0) \ T(ESCAPED_KEYWORD, nullptr, 0) \ - T(ESCAPED_STRICT_RESERVED_WORD, nullptr, 0) \ \ /* Scanner-internal use only. */ \ T(WHITESPACE, nullptr, 0) \ @@ -205,127 +206,103 @@ class Token { public: // All token values. #define T(name, string, precedence) name, - enum Value { TOKEN_LIST(T, T, T) NUM_TOKENS }; + enum Value : uint8_t { TOKEN_LIST(T, T, T) NUM_TOKENS }; #undef T // Returns a string corresponding to the C++ token name // (e.g. "LT" for the token LT). - static const char* Name(Value tok) { - DCHECK(tok < NUM_TOKENS); // tok is unsigned - return name_[tok]; + static const char* Name(Value token) { + DCHECK_GT(NUM_TOKENS, token); // token is unsigned + return name_[token]; } + static char TypeForTesting(Value token) { return token_type[token]; } + // Predicates - static bool IsKeyword(Value tok) { - return token_type[tok] == 'K'; + static bool IsKeyword(Value token) { return token_type[token] == 'K'; } + static bool IsContextualKeyword(Value token) { + return IsInRange(token, GET, ANONYMOUS); } - static bool IsContextualKeyword(Value tok) { return token_type[tok] == 'C'; } - static bool IsIdentifier(Value tok, LanguageMode language_mode, + static bool IsIdentifier(Value token, LanguageMode language_mode, bool is_generator, bool disallow_await) { - switch (tok) { - case IDENTIFIER: - case ASYNC: - return true; - case ESCAPED_STRICT_RESERVED_WORD: - case FUTURE_STRICT_RESERVED_WORD: - case LET: - case STATIC: - return is_sloppy(language_mode); - case YIELD: - return !is_generator && is_sloppy(language_mode); - case AWAIT: - return !disallow_await; - default: - return false; + if (IsInRange(token, IDENTIFIER, ASYNC)) return true; + if (IsInRange(token, LET, ESCAPED_STRICT_RESERVED_WORD)) { + return is_sloppy(language_mode); } - UNREACHABLE(); + if (token == AWAIT) return !disallow_await; + if (token == YIELD) return !is_generator && is_sloppy(language_mode); + return false; } - static bool IsAssignmentOp(Value tok) { - return INIT <= tok && tok <= ASSIGN_EXP; + static bool IsAnyIdentifier(Value token) { + return IsInRange(token, IDENTIFIER, ENUM); } - static bool IsBinaryOp(Value op) { return COMMA <= op && op <= EXP; } + static bool IsStrictReservedWord(Value token) { + return IsInRange(token, LET, ESCAPED_STRICT_RESERVED_WORD); + } - static bool IsCompareOp(Value op) { - return EQ <= op && op <= IN; + static bool IsLiteral(Value token) { + return IsInRange(token, NULL_LITERAL, STRING); } - static bool IsOrderedRelationalCompareOp(Value op) { - return op == LT || op == LTE || op == GT || op == GTE; + static bool IsAssignmentOp(Value token) { + return IsInRange(token, INIT, ASSIGN_EXP); } + static bool IsGetOrSet(Value op) { return IsInRange(op, GET, SET); } + + static bool IsBinaryOp(Value op) { return IsInRange(op, COMMA, EXP); } - static bool IsEqualityOp(Value op) { - return op == EQ || op == EQ_STRICT; + static bool IsCompareOp(Value op) { return IsInRange(op, EQ, IN); } + + static bool IsOrderedRelationalCompareOp(Value op) { + return IsInRange(op, LT, GTE); } + static bool IsEqualityOp(Value op) { return IsInRange(op, EQ, EQ_STRICT); } + static Value BinaryOpForAssignment(Value op) { - DCHECK(IsAssignmentOp(op)); - switch (op) { - case Token::ASSIGN_BIT_OR: - return Token::BIT_OR; - case Token::ASSIGN_BIT_XOR: - return Token::BIT_XOR; - case Token::ASSIGN_BIT_AND: - return Token::BIT_AND; - case Token::ASSIGN_SHL: - return Token::SHL; - case Token::ASSIGN_SAR: - return Token::SAR; - case Token::ASSIGN_SHR: - return Token::SHR; - case Token::ASSIGN_ADD: - return Token::ADD; - case Token::ASSIGN_SUB: - return Token::SUB; - case Token::ASSIGN_MUL: - return Token::MUL; - case Token::ASSIGN_DIV: - return Token::DIV; - case Token::ASSIGN_MOD: - return Token::MOD; - case Token::ASSIGN_EXP: - return Token::EXP; - default: - UNREACHABLE(); - } + DCHECK(IsInRange(op, ASSIGN_BIT_OR, ASSIGN_EXP)); + Value result = static_cast<Value>(op - ASSIGN_BIT_OR + BIT_OR); + DCHECK(IsBinaryOp(result)); + return result; } static bool IsBitOp(Value op) { - return (BIT_OR <= op && op <= SHR) || op == BIT_NOT; + return IsInRange(op, BIT_OR, SHR) || op == BIT_NOT; } static bool IsUnaryOp(Value op) { - return (NOT <= op && op <= VOID) || op == ADD || op == SUB; + return IsInRange(op, NOT, VOID) || IsInRange(op, ADD, SUB); } - static bool IsCountOp(Value op) { - return op == INC || op == DEC; - } + static bool IsCountOp(Value op) { return IsInRange(op, INC, DEC); } + + static bool IsShiftOp(Value op) { return IsInRange(op, SHL, SHR); } - static bool IsShiftOp(Value op) { - return (SHL <= op) && (op <= SHR); + static bool IsTrivialExpressionToken(Value op) { + return IsInRange(op, THIS, IDENTIFIER); } // Returns a string corresponding to the JS token string // (.e., "<" for the token LT) or nullptr if the token doesn't // have a (unique) string (e.g. an IDENTIFIER). - static const char* String(Value tok) { - DCHECK(tok < NUM_TOKENS); // tok is unsigned. - return string_[tok]; + static const char* String(Value token) { + DCHECK_GT(NUM_TOKENS, token); // token is unsigned + return string_[token]; } - static uint8_t StringLength(Value tok) { - DCHECK(tok < NUM_TOKENS); - return string_length_[tok]; + static uint8_t StringLength(Value token) { + DCHECK_GT(NUM_TOKENS, token); // token is unsigned + return string_length_[token]; } // Returns the precedence > 0 for binary and compare // operators; returns 0 otherwise. - static int Precedence(Value tok) { - DCHECK(tok < NUM_TOKENS); // tok is unsigned. - return precedence_[tok]; + static int Precedence(Value token) { + DCHECK_GT(NUM_TOKENS, token); // token is unsigned + return precedence_[token]; } private: |