diff options
author | Michaël Zasso <targos@protonmail.com> | 2019-03-12 09:01:49 +0100 |
---|---|---|
committer | Michaël Zasso <targos@protonmail.com> | 2019-03-14 18:49:21 +0100 |
commit | 7b48713334469818661fe276cf571de9c7899f2d (patch) | |
tree | 4dbda49ac88db76ce09dc330a0cb587e68e139ba /deps/v8/src/parsing | |
parent | 8549ac09b256666cf5275224ec58fab9939ff32e (diff) | |
download | node-new-7b48713334469818661fe276cf571de9c7899f2d.tar.gz |
deps: update V8 to 7.3.492.25
PR-URL: https://github.com/nodejs/node/pull/25852
Reviewed-By: Ujjwal Sharma <usharma1998@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Ali Ijaz Sheikh <ofrobots@google.com>
Diffstat (limited to 'deps/v8/src/parsing')
31 files changed, 6805 insertions, 8547 deletions
diff --git a/deps/v8/src/parsing/duplicate-finder.h b/deps/v8/src/parsing/duplicate-finder.h deleted file mode 100644 index 65bcc4e00d..0000000000 --- a/deps/v8/src/parsing/duplicate-finder.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2011 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_DUPLICATE_FINDER_H_ -#define V8_PARSING_DUPLICATE_FINDER_H_ - -#include <set> - -namespace v8 { -namespace internal { - -class Scanner; - -// DuplicateFinder : Helper class to discover duplicate symbols. -// -// Allocate a DuplicateFinder for each set of symbols you want to check -// for duplicates and then pass this instance into -// Scanner::IsDuplicateSymbol(..). -// -// This class only holds the data; all actual logic is in -// Scanner::IsDuplicateSymbol. -class DuplicateFinder { - public: - DuplicateFinder() = default; - - private: - friend class Scanner; - - std::set<const void*> known_symbols_; -}; - -} // namespace internal -} // namespace v8 - -#endif // V8_PARSING_DUPLICATE_FINDER_H_ diff --git a/deps/v8/src/parsing/expression-classifier.h b/deps/v8/src/parsing/expression-classifier.h deleted file mode 100644 index 2eed75b939..0000000000 --- a/deps/v8/src/parsing/expression-classifier.h +++ /dev/null @@ -1,568 +0,0 @@ -// Copyright 2015 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_EXPRESSION_CLASSIFIER_H_ -#define V8_PARSING_EXPRESSION_CLASSIFIER_H_ - -#include <type_traits> - -#include "src/messages.h" -#include "src/parsing/scanner.h" - -namespace v8 { -namespace internal { - -class DuplicateFinder; - -#define ERROR_CODES(T) \ - T(ExpressionProduction, 0) \ - T(FormalParameterInitializerProduction, 1) \ - T(BindingPatternProduction, 2) \ - T(AssignmentPatternProduction, 3) \ - T(DistinctFormalParametersProduction, 4) \ - T(StrictModeFormalParametersProduction, 5) \ - T(ArrowFormalParametersProduction, 6) \ - T(LetPatternProduction, 7) \ - T(AsyncArrowFormalParametersProduction, 8) - -// Expression classifiers serve two purposes: -// -// 1) They keep track of error messages that are pending (and other -// related information), waiting for the parser to decide whether -// the parsed expression is a pattern or not. -// 2) They keep track of expressions that may need to be rewritten, if -// the parser decides that they are not patterns. (A different -// mechanism implements the rewriting of patterns.) -// -// Expression classifiers are used by the parser in a stack fashion. -// Each new classifier is pushed on top of the stack. This happens -// automatically by the class's constructor. While on top of the -// stack, the classifier records pending error messages and tracks the -// pending non-patterns of the expression that is being parsed. -// -// At the end of its life, a classifier is either "accumulated" to the -// one that is below it on the stack, or is "discarded". The former -// is achieved by calling the method Accumulate. The latter is -// achieved automatically by the destructor, but it can happen earlier -// 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; - -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 - }; - - struct Error { - V8_INLINE Error() - : location(Scanner::Location::invalid()), - message(MessageTemplate::kNone), - kind(kUnusedError), - arg(nullptr) {} - V8_INLINE explicit Error(Scanner::Location loc, - MessageTemplate::Template msg, ErrorKind k, - const char* a = nullptr) - : location(loc), message(msg), kind(k), arg(a) {} - - Scanner::Location location; - MessageTemplate::Template message : 28; - unsigned kind : 4; - const char* arg; - }; - - // clang-format off - enum TargetProduction : unsigned { -#define DEFINE_PRODUCTION(NAME, CODE) NAME = 1 << CODE, - ERROR_CODES(DEFINE_PRODUCTION) -#undef DEFINE_PRODUCTION - -#define DEFINE_ALL_PRODUCTIONS(NAME, CODE) NAME | - AllProductions = ERROR_CODES(DEFINE_ALL_PRODUCTIONS) /* | */ 0 -#undef DEFINE_ALL_PRODUCTIONS - }; - // clang-format on - - explicit ExpressionClassifierBase(typename Types::Base* base, - DuplicateFinder* duplicate_finder = nullptr) - : base_(base), - duplicate_finder_(duplicate_finder), - invalid_productions_(0), - is_non_simple_parameter_list_(0) {} - - virtual ~ExpressionClassifierBase() = default; - - V8_INLINE bool is_valid(unsigned productions) const { - return (invalid_productions_ & productions) == 0; - } - - V8_INLINE DuplicateFinder* duplicate_finder() const { - return duplicate_finder_; - } - - V8_INLINE bool is_valid_expression() const { - return is_valid(ExpressionProduction); - } - - V8_INLINE bool is_valid_formal_parameter_initializer() const { - return is_valid(FormalParameterInitializerProduction); - } - - V8_INLINE bool is_valid_binding_pattern() const { - return is_valid(BindingPatternProduction); - } - - V8_INLINE bool is_valid_assignment_pattern() const { - return is_valid(AssignmentPatternProduction); - } - - V8_INLINE bool is_valid_arrow_formal_parameters() const { - return is_valid(ArrowFormalParametersProduction); - } - - V8_INLINE bool is_valid_formal_parameter_list_without_duplicates() const { - return is_valid(DistinctFormalParametersProduction); - } - - // Note: callers should also check - // is_valid_formal_parameter_list_without_duplicates(). - V8_INLINE bool is_valid_strict_mode_formal_parameters() const { - return is_valid(StrictModeFormalParametersProduction); - } - - V8_INLINE bool is_valid_let_pattern() const { - return is_valid(LetPatternProduction); - } - - bool is_valid_async_arrow_formal_parameters() const { - 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 this->reported_error(ErrorKind::kExpressionProduction); - } - - V8_INLINE const Error& formal_parameter_initializer_error() const { - return this->reported_error( - ErrorKind::kFormalParameterInitializerProduction); - } - - V8_INLINE const Error& binding_pattern_error() const { - return this->reported_error(ErrorKind::kBindingPatternProduction); - } - - V8_INLINE const Error& assignment_pattern_error() const { - return this->reported_error(ErrorKind::kAssignmentPatternProduction); - } - - V8_INLINE const Error& arrow_formal_parameters_error() const { - return this->reported_error(ErrorKind::kArrowFormalParametersProduction); - } - - V8_INLINE const Error& duplicate_formal_parameter_error() const { - return this->reported_error(ErrorKind::kDistinctFormalParametersProduction); - } - - V8_INLINE const Error& strict_mode_formal_parameter_error() const { - return this->reported_error( - ErrorKind::kStrictModeFormalParametersProduction); - } - - V8_INLINE const Error& let_pattern_error() const { - return this->reported_error(ErrorKind::kLetPatternProduction); - } - - V8_INLINE const Error& async_arrow_formal_parameters_error() const { - return this->reported_error( - ErrorKind::kAsyncArrowFormalParametersProduction); - } - - V8_INLINE bool does_error_reporting() { return ReportErrors; } - - void RecordExpressionError(const Scanner::Location& loc, - MessageTemplate::Template message, - const char* arg = nullptr) { - 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 (!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 (!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 (!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, - MessageTemplate::Template message, - const char* arg = nullptr) { - RecordBindingPatternError(loc, message, arg); - RecordAssignmentPatternError(loc, message, arg); - } - - void RecordArrowFormalParametersError(const Scanner::Location& loc, - MessageTemplate::Template message, - const char* arg = nullptr) { - 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 (!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 (!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 - // is not the same as StrictFormalParameterList, which simply forbids - // duplicate bindings. - void RecordStrictModeFormalParameterError(const Scanner::Location& loc, - MessageTemplate::Template message, - const char* arg = nullptr) { - 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 (!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: - ExpressionClassifier* previous_; - - DISALLOW_COPY_AND_ASSIGN(ExpressionClassifier); -}; - -#undef ERROR_CODES - -} // namespace internal -} // namespace v8 - -#endif // V8_PARSING_EXPRESSION_CLASSIFIER_H_ diff --git a/deps/v8/src/parsing/expression-scope-reparenter.cc b/deps/v8/src/parsing/expression-scope-reparenter.cc index 30e96d1688..95e4935c53 100644 --- a/deps/v8/src/parsing/expression-scope-reparenter.cc +++ b/deps/v8/src/parsing/expression-scope-reparenter.cc @@ -27,7 +27,6 @@ class Reparenter final : public AstTraversalVisitor<Reparenter> { void VisitFunctionLiteral(FunctionLiteral* expr); void VisitClassLiteral(ClassLiteral* expr); void VisitVariableProxy(VariableProxy* expr); - void VisitRewritableExpression(RewritableExpression* expr); void VisitBlock(Block* stmt); void VisitTryCatchStatement(TryCatchStatement* stmt); @@ -79,11 +78,6 @@ void Reparenter::VisitVariableProxy(VariableProxy* proxy) { } } -void Reparenter::VisitRewritableExpression(RewritableExpression* expr) { - Visit(expr->expression()); - expr->set_scope(scope_); -} - void Reparenter::VisitBlock(Block* stmt) { if (stmt->scope()) stmt->scope()->ReplaceOuterScope(scope_); diff --git a/deps/v8/src/parsing/expression-scope.h b/deps/v8/src/parsing/expression-scope.h new file mode 100644 index 0000000000..878cb3cf25 --- /dev/null +++ b/deps/v8/src/parsing/expression-scope.h @@ -0,0 +1,710 @@ +// 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_EXPRESSION_SCOPE_H_ +#define V8_PARSING_EXPRESSION_SCOPE_H_ + +#include "src/ast/scopes.h" +#include "src/message-template.h" +#include "src/parsing/scanner.h" +#include "src/zone/zone.h" // For ScopedPtrList. + +namespace v8 { +namespace internal { + +template <typename Types> +class ExpressionParsingScope; +template <typename Types> +class AccumulationScope; +template <typename Types> +class ArrowHeadParsingScope; +template <typename Types> +class ParameterDeclarationParsingScope; +template <typename Types> +class VariableDeclarationParsingScope; +class VariableProxy; + +// ExpressionScope is used in a stack fashion, and is used to specialize +// expression parsing for the task at hand. It allows the parser to reuse the +// same code to parse destructuring declarations, assignment patterns, +// expressions, and (async) arrow function heads. +// +// One of the specific subclasses needs to be instantiated to tell the parser +// the meaning of the expression it will parse next. The parser then calls +// Record* on the expression_scope() to indicate errors. The expression_scope +// will either discard those errors, immediately report those errors, or +// classify the errors for later validation. +// TODO(verwaest): Record is a slightly odd name since it will directly throw +// for unambiguous scopes. +template <typename Types> +class ExpressionScope { + public: + typedef typename Types::Impl ParserT; + typedef typename Types::Expression ExpressionT; + + VariableProxy* NewVariable(const AstRawString* name, + int pos = kNoSourcePosition) { + VariableProxy* result = parser_->NewRawVariable(name, pos); + if (CanBeExpression()) { + AsExpressionParsingScope()->TrackVariable(result); + } else if (type_ == kParameterDeclaration) { + AsParameterDeclarationParsingScope()->Declare(result); + } else { + return AsVariableDeclarationParsingScope()->Declare(result); + } + return result; + } + + void MarkIdentifierAsAssigned() { + if (!CanBeExpression()) return; + AsExpressionParsingScope()->MarkIdentifierAsAssigned(); + } + + void ValidateAsPattern(ExpressionT expression, int begin, int end) { + if (!CanBeExpression()) return; + AsExpressionParsingScope()->ValidatePattern(expression, begin, end); + AsExpressionParsingScope()->ClearExpressionError(); + } + + // Record async arrow parameters errors in all ambiguous async arrow scopes in + // the chain up to the first unambiguous scope. + void RecordAsyncArrowParametersError(const Scanner::Location& loc, + MessageTemplate message) { + // Only ambiguous scopes (ExpressionParsingScope, *ArrowHeadParsingScope) + // need to propagate errors to a possible kAsyncArrowHeadParsingScope, so + // immediately return if the current scope is not ambiguous. + if (!CanBeExpression()) return; + AsExpressionParsingScope()->RecordAsyncArrowParametersError(loc, message); + } + + // Record initializer errors in all scopes that can turn into parameter scopes + // (ArrowHeadParsingScopes) up to the first known unambiguous parameter scope. + void RecordParameterInitializerError(const Scanner::Location& loc, + MessageTemplate message) { + ExpressionScope* scope = this; + while (!scope->IsCertainlyParameterDeclaration()) { + if (!has_possible_parameter_in_scope_chain_) return; + if (scope->CanBeParameterDeclaration()) { + scope->AsArrowHeadParsingScope()->RecordDeclarationError(loc, message); + } + scope = scope->parent(); + if (scope == nullptr) return; + } + Report(loc, message); + } + + void RecordPatternError(const Scanner::Location& loc, + MessageTemplate message) { + // TODO(verwaest): Non-assigning expression? + if (IsCertainlyPattern()) { + Report(loc, message); + } else { + AsExpressionParsingScope()->RecordPatternError(loc, message); + } + } + + void RecordStrictModeParameterError(const Scanner::Location& loc, + MessageTemplate message) { + DCHECK_IMPLIES(!has_error(), loc.IsValid()); + if (!CanBeParameterDeclaration()) return; + if (IsCertainlyParameterDeclaration()) { + if (is_strict(parser_->language_mode())) { + Report(loc, message); + } else { + parser_->parameters_->set_strict_parameter_error(loc, message); + } + } else { + parser_->next_arrow_function_info_.strict_parameter_error_location = loc; + parser_->next_arrow_function_info_.strict_parameter_error_message = + message; + } + } + + void RecordDeclarationError(const Scanner::Location& loc, + MessageTemplate message) { + if (!CanBeDeclaration()) return; + if (IsCertainlyDeclaration()) { + Report(loc, message); + } else { + AsArrowHeadParsingScope()->RecordDeclarationError(loc, message); + } + } + + void RecordExpressionError(const Scanner::Location& loc, + MessageTemplate message) { + if (!CanBeExpression()) return; + // TODO(verwaest): Non-assigning expression? + // if (IsCertainlyExpression()) Report(loc, message); + AsExpressionParsingScope()->RecordExpressionError(loc, message); + } + + void RecordLexicalDeclarationError(const Scanner::Location& loc, + MessageTemplate message) { + if (IsLexicalDeclaration()) Report(loc, message); + } + + void RecordNonSimpleParameter() { + if (!IsArrowHeadParsingScope()) return; + AsArrowHeadParsingScope()->RecordNonSimpleParameter(); + } + + protected: + enum ScopeType : uint8_t { + // Expression or assignment target. + kExpression, + + // Declaration or expression or assignment target. + kMaybeArrowParameterDeclaration, + kMaybeAsyncArrowParameterDeclaration, + + // Declarations. + kParameterDeclaration, + kVarDeclaration, + kLexicalDeclaration, + }; + + ParserT* parser() const { return parser_; } + ExpressionScope* parent() const { return parent_; } + + void Report(const Scanner::Location& loc, MessageTemplate message) const { + parser_->ReportMessageAt(loc, message); + } + + ExpressionScope(ParserT* parser, ScopeType type) + : parser_(parser), + parent_(parser->expression_scope_), + type_(type), + has_possible_parameter_in_scope_chain_( + CanBeParameterDeclaration() || + (parent_ && parent_->has_possible_parameter_in_scope_chain_)) { + parser->expression_scope_ = this; + } + + ~ExpressionScope() { + DCHECK(parser_->expression_scope_ == this || + parser_->expression_scope_ == parent_); + parser_->expression_scope_ = parent_; + } + + ExpressionParsingScope<Types>* AsExpressionParsingScope() { + DCHECK(CanBeExpression()); + return static_cast<ExpressionParsingScope<Types>*>(this); + } + +#ifdef DEBUG + bool has_error() const { return parser_->has_error(); } +#endif + + bool CanBeExpression() const { + return IsInRange(type_, kExpression, kMaybeAsyncArrowParameterDeclaration); + } + bool CanBeDeclaration() const { + return IsInRange(type_, kMaybeArrowParameterDeclaration, + kLexicalDeclaration); + } + bool IsCertainlyDeclaration() const { + return IsInRange(type_, kParameterDeclaration, kLexicalDeclaration); + } + bool IsVariableDeclaration() const { + return IsInRange(type_, kVarDeclaration, kLexicalDeclaration); + } + bool IsLexicalDeclaration() const { return type_ == kLexicalDeclaration; } + bool IsAsyncArrowHeadParsingScope() const { + return type_ == kMaybeAsyncArrowParameterDeclaration; + } + + private: + friend class AccumulationScope<Types>; + friend class ExpressionParsingScope<Types>; + + ArrowHeadParsingScope<Types>* AsArrowHeadParsingScope() { + DCHECK(IsArrowHeadParsingScope()); + return static_cast<ArrowHeadParsingScope<Types>*>(this); + } + + ParameterDeclarationParsingScope<Types>* + AsParameterDeclarationParsingScope() { + DCHECK(IsCertainlyParameterDeclaration()); + return static_cast<ParameterDeclarationParsingScope<Types>*>(this); + } + + VariableDeclarationParsingScope<Types>* AsVariableDeclarationParsingScope() { + DCHECK(IsVariableDeclaration()); + return static_cast<VariableDeclarationParsingScope<Types>*>(this); + } + + bool IsArrowHeadParsingScope() const { + return IsInRange(type_, kMaybeArrowParameterDeclaration, + kMaybeAsyncArrowParameterDeclaration); + } + bool IsCertainlyPattern() const { return IsCertainlyDeclaration(); } + bool CanBeParameterDeclaration() const { + return IsInRange(type_, kMaybeArrowParameterDeclaration, + kParameterDeclaration); + } + bool IsCertainlyParameterDeclaration() const { + return type_ == kParameterDeclaration; + } + + ParserT* parser_; + ExpressionScope<Types>* parent_; + ScopeType type_; + bool has_possible_parameter_in_scope_chain_; + + DISALLOW_COPY_AND_ASSIGN(ExpressionScope); +}; + +// Used to unambiguously parse var, let, const declarations. +template <typename Types> +class VariableDeclarationParsingScope : public ExpressionScope<Types> { + public: + typedef typename Types::Impl ParserT; + typedef class ExpressionScope<Types> ExpressionScopeT; + typedef typename ExpressionScopeT::ScopeType ScopeType; + + VariableDeclarationParsingScope(ParserT* parser, VariableMode mode, + ZonePtrList<const AstRawString>* names) + : ExpressionScopeT(parser, IsLexicalVariableMode(mode) + ? ExpressionScopeT::kLexicalDeclaration + : ExpressionScopeT::kVarDeclaration), + mode_(mode), + names_(names) {} + + VariableProxy* Declare(VariableProxy* proxy) { + VariableKind kind = NORMAL_VARIABLE; + bool was_added; + this->parser()->DeclareVariable( + proxy, kind, mode_, Variable::DefaultInitializationFlag(mode_), + this->parser()->scope(), &was_added, proxy->position()); + if (was_added && + this->parser()->scope()->num_var() > kMaxNumFunctionLocals) { + this->parser()->ReportMessage(MessageTemplate::kTooManyVariables); + } + if (names_) names_->Add(proxy->raw_name(), this->parser()->zone()); + if (this->IsLexicalDeclaration()) { + if (this->parser()->IsLet(proxy->raw_name())) { + this->parser()->ReportMessageAt(proxy->location(), + MessageTemplate::kLetInLexicalBinding); + } + } else { + if (this->parser()->loop_nesting_depth() > 0) { + // Due to hoisting, the value of a 'var'-declared variable may actually + // change even if the code contains only the "initial" assignment, + // namely when that assignment occurs inside a loop. For example: + // + // let i = 10; + // do { var x = i } while (i--): + // + // Note that non-lexical variables include temporaries, which may also + // get assigned inside a loop due to the various rewritings that the + // parser performs. + // + // Pessimistically mark all vars in loops as assigned. This + // overapproximates the actual assigned vars due to unassigned var + // without initializer, but that's unlikely anyway. + // + // This also handles marking of loop variables in for-in and for-of + // loops, as determined by loop-nesting-depth. + proxy->set_is_assigned(); + } + + // Make sure we'll properly resolve the variable since we might be in a + // with or catch scope. In those cases the assignment isn't guaranteed to + // write to the variable declared above. + if (!this->parser()->scope()->is_declaration_scope()) { + proxy = + this->parser()->NewUnresolved(proxy->raw_name(), proxy->position()); + } + } + return proxy; + } + + private: + // Limit the allowed number of local variables in a function. The hard limit + // in Ignition is 2^31-1 due to the size of register operands. We limit it to + // a more reasonable lower up-limit. + static const int kMaxNumFunctionLocals = (1 << 23) - 1; + + VariableMode mode_; + ZonePtrList<const AstRawString>* names_; + + DISALLOW_COPY_AND_ASSIGN(VariableDeclarationParsingScope); +}; + +template <typename Types> +class ParameterDeclarationParsingScope : public ExpressionScope<Types> { + public: + typedef typename Types::Impl ParserT; + typedef class ExpressionScope<Types> ExpressionScopeT; + typedef typename ExpressionScopeT::ScopeType ScopeType; + + explicit ParameterDeclarationParsingScope(ParserT* parser) + : ExpressionScopeT(parser, ExpressionScopeT::kParameterDeclaration) {} + + void Declare(VariableProxy* proxy) { + VariableKind kind = PARAMETER_VARIABLE; + VariableMode mode = VariableMode::kVar; + bool was_added; + this->parser()->DeclareVariable( + proxy, kind, mode, Variable::DefaultInitializationFlag(mode), + this->parser()->scope(), &was_added, proxy->position()); + if (!has_duplicate() && !was_added) { + duplicate_loc_ = proxy->location(); + } + } + + bool has_duplicate() const { return duplicate_loc_.IsValid(); } + + const Scanner::Location& duplicate_location() const { return duplicate_loc_; } + + private: + Scanner::Location duplicate_loc_ = Scanner::Location::invalid(); + DISALLOW_COPY_AND_ASSIGN(ParameterDeclarationParsingScope); +}; + +// Parsing expressions is always ambiguous between at least left-hand-side and +// right-hand-side of assignments. This class is used to keep track of errors +// relevant for either side until it is clear what was being parsed. +// The class also keeps track of all variable proxies that are created while the +// scope was active. If the scope is an expression, the variable proxies will be +// added to the unresolved list. Otherwise they are declarations and aren't +// added. The list is also used to mark the variables as assigned in case we are +// parsing an assignment expression. +template <typename Types> +class ExpressionParsingScope : public ExpressionScope<Types> { + public: + typedef typename Types::Impl ParserT; + typedef typename Types::Expression ExpressionT; + typedef class ExpressionScope<Types> ExpressionScopeT; + typedef typename ExpressionScopeT::ScopeType ScopeType; + + ExpressionParsingScope(ParserT* parser, + ScopeType type = ExpressionScopeT::kExpression) + : ExpressionScopeT(parser, type), + variable_list_(parser->variable_buffer()), + has_async_arrow_in_scope_chain_( + type == ExpressionScopeT::kMaybeAsyncArrowParameterDeclaration || + (this->parent() && this->parent()->CanBeExpression() && + this->parent() + ->AsExpressionParsingScope() + ->has_async_arrow_in_scope_chain_)) { + DCHECK(this->CanBeExpression()); + clear(kExpressionIndex); + clear(kPatternIndex); + } + + void RecordAsyncArrowParametersError(const Scanner::Location& loc, + MessageTemplate message) { + for (ExpressionScopeT* scope = this; scope != nullptr; + scope = scope->parent()) { + if (!has_async_arrow_in_scope_chain_) break; + if (scope->type_ == + ExpressionScopeT::kMaybeAsyncArrowParameterDeclaration) { + scope->AsArrowHeadParsingScope()->RecordDeclarationError(loc, message); + } + } + } + + ~ExpressionParsingScope() { DCHECK(this->has_error() || verified_); } + + ExpressionT ValidateAndRewriteReference(ExpressionT expression, int beg_pos, + int end_pos) { + if (V8_LIKELY(this->parser()->IsAssignableIdentifier(expression))) { + MarkIdentifierAsAssigned(); + this->mark_verified(); + return expression; + } else if (V8_LIKELY(expression->IsProperty())) { + ValidateExpression(); + return expression; + } + this->mark_verified(); + return this->parser()->RewriteInvalidReferenceExpression( + expression, beg_pos, end_pos, MessageTemplate::kInvalidLhsInFor, + kSyntaxError); + } + + void RecordExpressionError(const Scanner::Location& loc, + MessageTemplate message) { + Record(kExpressionIndex, loc, message); + } + + void RecordPatternError(const Scanner::Location& loc, + MessageTemplate message) { + Record(kPatternIndex, loc, message); + } + + void ValidateExpression() { Validate(kExpressionIndex); } + + void ValidatePattern(ExpressionT expression, int begin, int end) { + Validate(kPatternIndex); + if (expression->is_parenthesized()) { + ExpressionScopeT::Report(Scanner::Location(begin, end), + MessageTemplate::kInvalidDestructuringTarget); + } + for (int i = 0; i < variable_list_.length(); i++) { + variable_list_.at(i)->set_is_assigned(); + } + } + + void ClearExpressionError() { + DCHECK(verified_); +#ifdef DEBUG + verified_ = false; +#endif + clear(kExpressionIndex); + } + + void TrackVariable(VariableProxy* variable) { + if (!this->CanBeDeclaration()) { + this->parser()->scope()->AddUnresolved(variable); + } + variable_list_.Add(variable); + } + + void MarkIdentifierAsAssigned() { + // It's possible we're parsing a syntax error. In that case it's not + // guaranteed that there's a variable in the list. + if (variable_list_.length() == 0) return; + variable_list_.at(variable_list_.length() - 1)->set_is_assigned(); + } + + protected: + bool is_verified() const { +#ifdef DEBUG + return verified_; +#else + return false; +#endif + } + + void ValidatePattern() { Validate(kPatternIndex); } + + ScopedPtrList<VariableProxy>* variable_list() { return &variable_list_; } + + private: + friend class AccumulationScope<Types>; + + enum ErrorNumber : uint8_t { + kExpressionIndex = 0, + kPatternIndex = 1, + kNumberOfErrors = 2, + }; + void clear(int index) { + messages_[index] = MessageTemplate::kNone; + locations_[index] = Scanner::Location::invalid(); + } + bool is_valid(int index) const { return !locations_[index].IsValid(); } + void Record(int index, const Scanner::Location& loc, + MessageTemplate message) { + DCHECK_IMPLIES(!this->has_error(), loc.IsValid()); + if (!is_valid(index)) return; + messages_[index] = message; + locations_[index] = loc; + } + void Validate(int index) { + DCHECK(!this->is_verified()); + if (!is_valid(index)) Report(index); + this->mark_verified(); + } + void Report(int index) const { + ExpressionScopeT::Report(locations_[index], messages_[index]); + } + + // Debug verification to make sure every scope is validated exactly once. + void mark_verified() { +#ifdef DEBUG + verified_ = true; +#endif + } + void clear_verified() { +#ifdef DEBUG + verified_ = false; +#endif + } +#ifdef DEBUG + bool verified_ = false; +#endif + + ScopedPtrList<VariableProxy> variable_list_; + MessageTemplate messages_[kNumberOfErrors]; + Scanner::Location locations_[kNumberOfErrors]; + bool has_async_arrow_in_scope_chain_; + + DISALLOW_COPY_AND_ASSIGN(ExpressionParsingScope); +}; + +// This class is used to parse multiple ambiguous expressions and declarations +// in the same scope. E.g., in async(X,Y,Z) or [X,Y,Z], X and Y and Z will all +// be parsed in the respective outer ArrowHeadParsingScope and +// ExpressionParsingScope. It provides a clean error state in the underlying +// scope to parse the individual expressions, while keeping track of the +// expression and pattern errors since the start. The AccumulationScope is only +// used to keep track of the errors so far, and the underlying ExpressionScope +// keeps being used as the expression_scope(). If the expression_scope() isn't +// ambiguous, this class does not do anything. +template <typename Types> +class AccumulationScope { + public: + typedef typename Types::Impl ParserT; + + static const int kNumberOfErrors = + ExpressionParsingScope<Types>::kNumberOfErrors; + explicit AccumulationScope(ExpressionScope<Types>* scope) : scope_(nullptr) { + if (!scope->CanBeExpression()) return; + scope_ = scope->AsExpressionParsingScope(); + for (int i = 0; i < kNumberOfErrors; i++) { + // If the underlying scope is already invalid at the start, stop + // accumulating. That means an error was found outside of an + // accumulating path. + if (!scope_->is_valid(i)) { + scope_ = nullptr; + break; + } + copy(i); + } + } + + // Merge errors from the underlying ExpressionParsingScope into this scope. + // Only keeps the first error across all accumulate calls, and removes the + // error from the underlying scope. + void Accumulate() { + if (scope_ == nullptr) return; + DCHECK(!scope_->is_verified()); + for (int i = 0; i < kNumberOfErrors; i++) { + if (!locations_[i].IsValid()) copy(i); + scope_->clear(i); + } + } + + // This is called instead of Accumulate in case the parsed member is already + // known to be an expression. In that case we don't need to accumulate the + // expression but rather validate it immediately. We also ignore the pattern + // error since the parsed member is known to not be a pattern. This is + // necessary for "{x:1}.y" parsed as part of an assignment pattern. {x:1} will + // record a pattern error, but "{x:1}.y" is actually a valid as part of an + // assignment pattern since it's a property access. + void ValidateExpression() { + if (scope_ == nullptr) return; + DCHECK(!scope_->is_verified()); + scope_->ValidateExpression(); + DCHECK(scope_->is_verified()); + scope_->clear(ExpressionParsingScope<Types>::kPatternIndex); +#ifdef DEBUG + scope_->clear_verified(); +#endif + } + + ~AccumulationScope() { + if (scope_ == nullptr) return; + Accumulate(); + for (int i = 0; i < kNumberOfErrors; i++) copy_back(i); + } + + private: + void copy(int entry) { + messages_[entry] = scope_->messages_[entry]; + locations_[entry] = scope_->locations_[entry]; + } + + void copy_back(int entry) { + if (!locations_[entry].IsValid()) return; + scope_->messages_[entry] = messages_[entry]; + scope_->locations_[entry] = locations_[entry]; + } + + ExpressionParsingScope<Types>* scope_; + MessageTemplate messages_[2]; + Scanner::Location locations_[2]; + + DISALLOW_COPY_AND_ASSIGN(AccumulationScope); +}; + +// The head of an arrow function is ambiguous between expression, assignment +// pattern and declaration. This keeps track of the additional declaration +// error and allows the scope to be validated as a declaration rather than an +// expression or a pattern. +template <typename Types> +class ArrowHeadParsingScope : public ExpressionParsingScope<Types> { + public: + typedef typename Types::Impl ParserT; + typedef typename ExpressionScope<Types>::ScopeType ScopeType; + + ArrowHeadParsingScope(ParserT* parser, FunctionKind kind) + : ExpressionParsingScope<Types>( + parser, + kind == FunctionKind::kArrowFunction + ? ExpressionScope<Types>::kMaybeArrowParameterDeclaration + : ExpressionScope< + Types>::kMaybeAsyncArrowParameterDeclaration) { + DCHECK(kind == FunctionKind::kAsyncArrowFunction || + kind == FunctionKind::kArrowFunction); + DCHECK(this->CanBeDeclaration()); + DCHECK(!this->IsCertainlyDeclaration()); + } + + void ValidateExpression() { + // Turns out this is not an arrow head. Clear any possible tracked strict + // parameter errors, and reinterpret tracked variables as unresolved + // references. + this->parser()->next_arrow_function_info_.ClearStrictParameterError(); + ExpressionParsingScope<Types>::ValidateExpression(); + for (int i = 0; i < this->variable_list()->length(); i++) { + this->parser()->scope()->AddUnresolved(this->variable_list()->at(i)); + } + } + + DeclarationScope* ValidateAndCreateScope() { + DCHECK(!this->is_verified()); + if (declaration_error_location.IsValid()) { + ExpressionScope<Types>::Report(declaration_error_location, + declaration_error_message); + } + this->ValidatePattern(); + + DeclarationScope* result = this->parser()->NewFunctionScope(kind()); + if (!has_simple_parameter_list_) result->SetHasNonSimpleParameters(); + VariableKind kind = PARAMETER_VARIABLE; + VariableMode mode = + has_simple_parameter_list_ ? VariableMode::kVar : VariableMode::kLet; + for (int i = 0; i < this->variable_list()->length(); i++) { + VariableProxy* proxy = this->variable_list()->at(i); + bool was_added; + this->parser()->DeclareVariable(proxy, kind, mode, + Variable::DefaultInitializationFlag(mode), + result, &was_added, proxy->position()); + if (!was_added) { + ExpressionScope<Types>::Report(proxy->location(), + MessageTemplate::kParamDupe); + } + } + return result; + } + + void RecordDeclarationError(const Scanner::Location& loc, + MessageTemplate message) { + DCHECK_IMPLIES(!this->has_error(), loc.IsValid()); + declaration_error_location = loc; + declaration_error_message = message; + } + + void RecordNonSimpleParameter() { has_simple_parameter_list_ = false; } + + private: + FunctionKind kind() const { + return this->IsAsyncArrowHeadParsingScope() + ? FunctionKind::kAsyncArrowFunction + : FunctionKind::kArrowFunction; + } + + Scanner::Location declaration_error_location = Scanner::Location::invalid(); + MessageTemplate declaration_error_message = MessageTemplate::kNone; + bool has_simple_parameter_list_ = true; + + DISALLOW_COPY_AND_ASSIGN(ArrowHeadParsingScope); +}; + +} // namespace internal +} // namespace v8 + +#endif // V8_PARSING_EXPRESSION_SCOPE_H_ diff --git a/deps/v8/src/parsing/func-name-inferrer.cc b/deps/v8/src/parsing/func-name-inferrer.cc index 7d476f1e64..c21fb35ae9 100644 --- a/deps/v8/src/parsing/func-name-inferrer.cc +++ b/deps/v8/src/parsing/func-name-inferrer.cc @@ -11,13 +11,8 @@ namespace v8 { namespace internal { -FuncNameInferrer::FuncNameInferrer(AstValueFactory* ast_value_factory, - Zone* zone) - : ast_value_factory_(ast_value_factory), - entries_stack_(zone), - names_stack_(zone), - funcs_to_infer_(zone), - zone_(zone) {} +FuncNameInferrer::FuncNameInferrer(AstValueFactory* ast_value_factory) + : ast_value_factory_(ast_value_factory) {} void FuncNameInferrer::PushEnclosingName(const AstRawString* name) { // Enclosing name is a name of a constructor function. To check @@ -45,35 +40,31 @@ void FuncNameInferrer::PushVariableName(const AstRawString* name) { void FuncNameInferrer::RemoveAsyncKeywordFromEnd() { if (IsOpen()) { CHECK_GT(names_stack_.size(), 0); - CHECK(names_stack_.back().name->IsOneByteEqualTo("async")); + CHECK(names_stack_.back().name()->IsOneByteEqualTo("async")); names_stack_.pop_back(); } } -void FuncNameInferrer::Leave() { - DCHECK(IsOpen()); - size_t last_entry = entries_stack_.back(); - entries_stack_.pop_back(); - names_stack_.Rewind(last_entry); - if (entries_stack_.is_empty()) funcs_to_infer_.Rewind(); -} - const AstConsString* FuncNameInferrer::MakeNameFromStack() { + if (names_stack_.size() == 0) { + return ast_value_factory_->empty_cons_string(); + } AstConsString* result = ast_value_factory_->NewConsString(); auto it = names_stack_.begin(); while (it != names_stack_.end()) { // Advance the iterator to be able to peek the next value. auto current = it++; // Skip consecutive variable declarations. - if (it != names_stack_.end() && current->type == kVariableName && - it->type == kVariableName) { + if (it != names_stack_.end() && current->type() == kVariableName && + it->type() == kVariableName) { continue; } // Add name. Separate names with ".". + Zone* zone = ast_value_factory_->zone(); if (!result->IsEmpty()) { - result->AddString(zone(), ast_value_factory_->dot_string()); + result->AddString(zone, ast_value_factory_->dot_string()); } - result->AddString(zone(), current->name); + result->AddString(zone, current->name()); } return result; } @@ -83,7 +74,7 @@ void FuncNameInferrer::InferFunctionsNames() { for (FunctionLiteral* func : funcs_to_infer_) { func->set_raw_inferred_name(func_name); } - funcs_to_infer_.Rewind(0); + funcs_to_infer_.resize(0); } diff --git a/deps/v8/src/parsing/func-name-inferrer.h b/deps/v8/src/parsing/func-name-inferrer.h index d46d7f2c2b..bdc58221e1 100644 --- a/deps/v8/src/parsing/func-name-inferrer.h +++ b/deps/v8/src/parsing/func-name-inferrer.h @@ -5,8 +5,10 @@ #ifndef V8_PARSING_FUNC_NAME_INFERRER_H_ #define V8_PARSING_FUNC_NAME_INFERRER_H_ -#include "src/zone/zone-chunk-list.h" -#include "src/zone/zone.h" +#include <vector> + +#include "src/base/macros.h" +#include "src/pointer-with-payload.h" namespace v8 { namespace internal { @@ -18,6 +20,11 @@ class FunctionLiteral; enum class InferName { kYes, kNo }; +template <> +struct PointerWithPayloadTraits<AstRawString> { + static constexpr int value = 2; +}; + // FuncNameInferrer is a stateful class that is used to perform name // inference for anonymous functions during static analysis of source code. // Inference is performed in cases when an anonymous function is assigned @@ -28,25 +35,33 @@ enum class InferName { kYes, kNo }; // and during parsing of the RHS, a function literal can be collected. After // parsing the RHS we can infer a name for function literals that do not have // a name. -class FuncNameInferrer : public ZoneObject { +class FuncNameInferrer { public: - FuncNameInferrer(AstValueFactory* ast_value_factory, Zone* zone); + explicit FuncNameInferrer(AstValueFactory* ast_value_factory); // To enter function name inference state, put a FuncNameInferrer::State // on the stack. class State { public: - explicit State(FuncNameInferrer* fni) : fni_(fni) { fni_->Enter(); } - ~State() { fni_->Leave(); } + explicit State(FuncNameInferrer* fni) + : fni_(fni), top_(fni->names_stack_.size()) { + ++fni_->scope_depth_; + } + ~State() { + DCHECK(fni_->IsOpen()); + fni_->names_stack_.resize(top_); + --fni_->scope_depth_; + } private: FuncNameInferrer* fni_; + size_t top_; DISALLOW_COPY_AND_ASSIGN(State); }; // Returns whether we have entered name collection state. - bool IsOpen() const { return !entries_stack_.is_empty(); } + bool IsOpen() const { return scope_depth_ > 0; } // Pushes an enclosing the name of enclosing function onto names stack. void PushEnclosingName(const AstRawString* name); @@ -64,9 +79,7 @@ class FuncNameInferrer : public ZoneObject { } void RemoveLastFunction() { - if (IsOpen() && !funcs_to_infer_.is_empty()) { - funcs_to_infer_.pop_back(); - } + if (IsOpen() && !funcs_to_infer_.empty()) funcs_to_infer_.pop_back(); } void RemoveAsyncKeywordFromEnd(); @@ -74,29 +87,28 @@ class FuncNameInferrer : public ZoneObject { // Infers a function name and leaves names collection state. void Infer() { DCHECK(IsOpen()); - if (!funcs_to_infer_.is_empty()) { - InferFunctionsNames(); - } + if (!funcs_to_infer_.empty()) InferFunctionsNames(); } private: - enum NameType { + enum NameType : uint8_t { kEnclosingConstructorName, kLiteralName, kVariableName }; struct Name { - Name(const AstRawString* name, NameType type) : name(name), type(type) {} - const AstRawString* name; - NameType type; + // Needed for names_stack_.resize() + Name() { UNREACHABLE(); } + Name(const AstRawString* name, NameType type) + : name_and_type_(name, type) {} + + PointerWithPayload<const AstRawString, NameType, 2> name_and_type_; + inline const AstRawString* name() const { + return name_and_type_.GetPointer(); + } + inline NameType type() const { return name_and_type_.GetPayload(); } }; - void Enter() { entries_stack_.push_back(names_stack_.size()); } - - void Leave(); - - Zone* zone() const { return zone_; } - // Constructs a full name in dotted notation from gathered names. const AstConsString* MakeNameFromStack(); @@ -104,10 +116,9 @@ class FuncNameInferrer : public ZoneObject { void InferFunctionsNames(); AstValueFactory* ast_value_factory_; - ZoneChunkList<size_t> entries_stack_; - ZoneChunkList<Name> names_stack_; - ZoneChunkList<FunctionLiteral*> funcs_to_infer_; - Zone* zone_; + std::vector<Name> names_stack_; + std::vector<FunctionLiteral*> funcs_to_infer_; + size_t scope_depth_ = 0; DISALLOW_COPY_AND_ASSIGN(FuncNameInferrer); }; diff --git a/deps/v8/src/parsing/keywords-gen.h b/deps/v8/src/parsing/keywords-gen.h new file mode 100644 index 0000000000..67c47a2dda --- /dev/null +++ b/deps/v8/src/parsing/keywords-gen.h @@ -0,0 +1,177 @@ +// 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. + +// This file is automatically generated by gen-keywords-gen-h.py and should not +// be modified manually. + +#ifndef V8_PARSING_KEYWORDS_GEN_H_ +#define V8_PARSING_KEYWORDS_GEN_H_ + +#include "src/parsing/token.h" + +namespace v8 { +namespace internal { + +/* C++ code produced by gperf version 3.1 */ +/* Command-line: gperf -m100 src/parsing/keywords.txt */ +/* Computed positions: -k'1-2' */ + +#if !( \ + (' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) && ('%' == 37) && \ + ('&' == 38) && ('\'' == 39) && ('(' == 40) && (')' == 41) && \ + ('*' == 42) && ('+' == 43) && (',' == 44) && ('-' == 45) && ('.' == 46) && \ + ('/' == 47) && ('0' == 48) && ('1' == 49) && ('2' == 50) && ('3' == 51) && \ + ('4' == 52) && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) && \ + ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) && ('=' == 61) && \ + ('>' == 62) && ('?' == 63) && ('A' == 65) && ('B' == 66) && ('C' == 67) && \ + ('D' == 68) && ('E' == 69) && ('F' == 70) && ('G' == 71) && ('H' == 72) && \ + ('I' == 73) && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) && \ + ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) && ('R' == 82) && \ + ('S' == 83) && ('T' == 84) && ('U' == 85) && ('V' == 86) && ('W' == 87) && \ + ('X' == 88) && ('Y' == 89) && ('Z' == 90) && ('[' == 91) && \ + ('\\' == 92) && (']' == 93) && ('^' == 94) && ('_' == 95) && \ + ('a' == 97) && ('b' == 98) && ('c' == 99) && ('d' == 100) && \ + ('e' == 101) && ('f' == 102) && ('g' == 103) && ('h' == 104) && \ + ('i' == 105) && ('j' == 106) && ('k' == 107) && ('l' == 108) && \ + ('m' == 109) && ('n' == 110) && ('o' == 111) && ('p' == 112) && \ + ('q' == 113) && ('r' == 114) && ('s' == 115) && ('t' == 116) && \ + ('u' == 117) && ('v' == 118) && ('w' == 119) && ('x' == 120) && \ + ('y' == 121) && ('z' == 122) && ('{' == 123) && ('|' == 124) && \ + ('}' == 125) && ('~' == 126)) +/* The character set is not based on ISO-646. */ +#error "gperf generated tables don't work with this execution character set." +// If you see this error, please report a bug to <bug-gperf@gnu.org>. +#endif + +struct PerfectKeywordHashTableEntry { + const char* name; + Token::Value value; +}; +enum { + TOTAL_KEYWORDS = 47, + MIN_WORD_LENGTH = 2, + MAX_WORD_LENGTH = 10, + MIN_HASH_VALUE = 2, + MAX_HASH_VALUE = 51 +}; + +/* maximum key range = 50, duplicates = 0 */ + +class PerfectKeywordHash { + private: + static inline unsigned int Hash(const char* str, int len); + + public: + static inline Token::Value GetToken(const char* str, int len); +}; + +inline unsigned int PerfectKeywordHash::Hash(const char* str, int len) { + DCHECK_LT(str[1], 128); + DCHECK_LT(str[0], 128); + static const unsigned char asso_values[128] = { + 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, + 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, + 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, + 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, + 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, + 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, + 52, 8, 2, 6, 0, 0, 9, 52, 21, 0, 52, 52, 36, 40, 0, 3, + 6, 52, 17, 13, 16, 16, 38, 25, 6, 26, 52, 52, 52, 52, 52, 52}; + return len + asso_values[static_cast<unsigned char>(str[1])] + + asso_values[static_cast<unsigned char>(str[0])]; +} + +static const unsigned char kPerfectKeywordLengthTable[64] = { + 0, 0, 2, 3, 4, 2, 6, 7, 8, 9, 10, 2, 6, 7, 5, 3, 7, 8, 4, 5, 4, 7, + 5, 6, 5, 0, 5, 0, 6, 4, 7, 5, 9, 8, 5, 6, 3, 4, 5, 3, 4, 4, 5, 0, + 6, 4, 6, 5, 6, 3, 10, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +static const struct PerfectKeywordHashTableEntry kPerfectKeywordHashTable[64] = + {{"", Token::IDENTIFIER}, + {"", Token::IDENTIFIER}, + {"in", Token::IN}, + {"new", Token::NEW}, + {"enum", Token::ENUM}, + {"do", Token::DO}, + {"delete", Token::DELETE}, + {"default", Token::DEFAULT}, + {"debugger", Token::DEBUGGER}, + {"interface", Token::FUTURE_STRICT_RESERVED_WORD}, + {"instanceof", Token::INSTANCEOF}, + {"if", Token::IF}, + {"export", Token::EXPORT}, + {"extends", Token::EXTENDS}, + {"const", Token::CONST}, + {"for", Token::FOR}, + {"finally", Token::FINALLY}, + {"continue", Token::CONTINUE}, + {"case", Token::CASE}, + {"catch", Token::CATCH}, + {"null", Token::NULL_LITERAL}, + {"package", Token::FUTURE_STRICT_RESERVED_WORD}, + {"false", Token::FALSE_LITERAL}, + {"return", Token::RETURN}, + {"break", Token::BREAK}, + {"", Token::IDENTIFIER}, + {"async", Token::ASYNC}, + {"", Token::IDENTIFIER}, + {"public", Token::FUTURE_STRICT_RESERVED_WORD}, + {"with", Token::WITH}, + {"private", Token::FUTURE_STRICT_RESERVED_WORD}, + {"yield", Token::YIELD}, + {"protected", Token::FUTURE_STRICT_RESERVED_WORD}, + {"function", Token::FUNCTION}, + {"super", Token::SUPER}, + {"static", Token::STATIC}, + {"try", Token::TRY}, + {"true", Token::TRUE_LITERAL}, + {"await", Token::AWAIT}, + {"let", Token::LET}, + {"else", Token::ELSE}, + {"this", Token::THIS}, + {"throw", Token::THROW}, + {"", Token::IDENTIFIER}, + {"switch", Token::SWITCH}, + {"void", Token::VOID}, + {"import", Token::IMPORT}, + {"class", Token::CLASS}, + {"typeof", Token::TYPEOF}, + {"var", Token::VAR}, + {"implements", Token::FUTURE_STRICT_RESERVED_WORD}, + {"while", Token::WHILE}, + {"", Token::IDENTIFIER}, + {"", Token::IDENTIFIER}, + {"", Token::IDENTIFIER}, + {"", Token::IDENTIFIER}, + {"", Token::IDENTIFIER}, + {"", Token::IDENTIFIER}, + {"", Token::IDENTIFIER}, + {"", Token::IDENTIFIER}, + {"", Token::IDENTIFIER}, + {"", Token::IDENTIFIER}, + {"", Token::IDENTIFIER}, + {"", Token::IDENTIFIER}}; + +inline Token::Value PerfectKeywordHash::GetToken(const char* str, int len) { + if (IsInRange(len, MIN_WORD_LENGTH, MAX_WORD_LENGTH)) { + unsigned int key = Hash(str, len) & 0x3f; + + DCHECK_LT(key, arraysize(kPerfectKeywordLengthTable)); + DCHECK_LT(key, arraysize(kPerfectKeywordHashTable)); + if (len == kPerfectKeywordLengthTable[key]) { + const char* s = kPerfectKeywordHashTable[key].name; + + while (*s != 0) { + if (*s++ != *str++) return Token::IDENTIFIER; + } + return kPerfectKeywordHashTable[key].value; + } + } + return Token::IDENTIFIER; +} + +} // namespace internal +} // namespace v8 + +#endif // V8_PARSING_KEYWORDS_GEN_H_ diff --git a/deps/v8/src/parsing/keywords.txt b/deps/v8/src/parsing/keywords.txt new file mode 100644 index 0000000000..a3b3e4614d --- /dev/null +++ b/deps/v8/src/parsing/keywords.txt @@ -0,0 +1,64 @@ +%struct-type +%language=C++ +%global-table +%define initializer-suffix ,Token::IDENTIFIER +%define hash-function-name Hash +%define lookup-function-name GetToken +%define class-name PerfectKeywordHash +%define word-array-name kPerfectKeywordHashTable +%define length-table-name kPerfectKeywordLengthTable +%7bit +%compare-lengths +%enum +%readonly-tables +%compare-strncmp + +struct PerfectKeywordHashTableEntry { const char* name; Token::Value value; }; +%% +async, Token::ASYNC +await, Token::AWAIT +break, Token::BREAK +case, Token::CASE +catch, Token::CATCH +class, Token::CLASS +const, Token::CONST +continue, Token::CONTINUE +debugger, Token::DEBUGGER +default, Token::DEFAULT +delete, Token::DELETE +do, Token::DO +else, Token::ELSE +enum, Token::ENUM +export, Token::EXPORT +extends, Token::EXTENDS +false, Token::FALSE_LITERAL +finally, Token::FINALLY +for, Token::FOR +function, Token::FUNCTION +if, Token::IF +implements, Token::FUTURE_STRICT_RESERVED_WORD +import, Token::IMPORT +in, Token::IN +instanceof, Token::INSTANCEOF +interface, Token::FUTURE_STRICT_RESERVED_WORD +let, Token::LET +new, Token::NEW +null, Token::NULL_LITERAL +package, Token::FUTURE_STRICT_RESERVED_WORD +private, Token::FUTURE_STRICT_RESERVED_WORD +protected, Token::FUTURE_STRICT_RESERVED_WORD +public, Token::FUTURE_STRICT_RESERVED_WORD +return, Token::RETURN +static, Token::STATIC +super, Token::SUPER +switch, Token::SWITCH +this, Token::THIS +throw, Token::THROW +true, Token::TRUE_LITERAL +try, Token::TRY +typeof, Token::TYPEOF +var, Token::VAR +void, Token::VOID +while, Token::WHILE +with, Token::WITH +yield, Token::YIELD diff --git a/deps/v8/src/parsing/parse-info.cc b/deps/v8/src/parsing/parse-info.cc index 129b00a2c2..3050e01b48 100644 --- a/deps/v8/src/parsing/parse-info.cc +++ b/deps/v8/src/parsing/parse-info.cc @@ -8,7 +8,10 @@ #include "src/ast/ast-value-factory.h" #include "src/ast/ast.h" #include "src/base/template-utils.h" +#include "src/compiler-dispatcher/compiler-dispatcher.h" +#include "src/counters.h" #include "src/heap/heap-inl.h" +#include "src/log.h" #include "src/objects-inl.h" #include "src/objects/scope-info.h" #include "src/zone/zone.h" @@ -21,7 +24,6 @@ ParseInfo::ParseInfo(AccountingAllocator* zone_allocator) flags_(0), extension_(nullptr), script_scope_(nullptr), - unicode_cache_(nullptr), stack_limit_(0), hash_seed_(0), function_kind_(FunctionKind::kNormalFunction), @@ -43,12 +45,24 @@ 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()); set_runtime_call_stats(isolate->counters()->runtime_call_stats()); set_logger(isolate->logger()); set_ast_string_constants(isolate->ast_string_constants()); if (isolate->is_block_code_coverage()) set_block_coverage_enabled(); if (isolate->is_collecting_type_profile()) set_collect_type_profile(); + if (isolate->compiler_dispatcher()->IsEnabled()) { + parallel_tasks_.reset(new ParallelTasks(isolate->compiler_dispatcher())); + } + set_might_always_opt(FLAG_always_opt || FLAG_prepare_always_opt); + set_allow_lazy_compile(FLAG_lazy); + set_allow_natives_syntax(FLAG_allow_natives_syntax); + set_allow_harmony_public_fields(FLAG_harmony_public_fields); + 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_numeric_separator(FLAG_harmony_numeric_separator); + set_allow_harmony_private_fields(FLAG_harmony_private_fields); + set_allow_harmony_private_methods(FLAG_harmony_private_methods); } ParseInfo::ParseInfo(Isolate* isolate) @@ -63,9 +77,10 @@ void ParseInfo::SetFunctionInfo(T function) { 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_requires_instance_members_initializer( + function->requires_instance_members_initializer()); set_toplevel(function->is_toplevel()); + set_is_oneshot_iife(function->is_oneshot_iife()); set_wrapped_as_function(function->is_wrapped()); } @@ -76,7 +91,7 @@ ParseInfo::ParseInfo(Isolate* isolate, Handle<SharedFunctionInfo> shared) // wrapped script at all. DCHECK_IMPLIES(is_toplevel(), !Script::cast(shared->script())->is_wrapped()); - set_allow_lazy_parsing(FLAG_lazy_inner_functions); + set_allow_lazy_parsing(true); set_asm_wasm_broken(shared->is_asm_wasm_broken()); set_start_position(shared->StartPosition()); @@ -225,5 +240,15 @@ void ParseInfo::set_script(Handle<Script> script) { } } +void ParseInfo::ParallelTasks::Enqueue(ParseInfo* outer_parse_info, + const AstRawString* function_name, + FunctionLiteral* literal) { + base::Optional<CompilerDispatcher::JobId> job_id = + dispatcher_->Enqueue(outer_parse_info, function_name, literal); + if (job_id) { + enqueued_jobs_.emplace_front(std::make_pair(literal, *job_id)); + } +} + } // namespace internal } // namespace v8 diff --git a/deps/v8/src/parsing/parse-info.h b/deps/v8/src/parsing/parse-info.h index ba3e3d2898..7ab236c82d 100644 --- a/deps/v8/src/parsing/parse-info.h +++ b/deps/v8/src/parsing/parse-info.h @@ -13,7 +13,7 @@ #include "src/globals.h" #include "src/handles.h" #include "src/objects/script.h" -#include "src/parsing/preparsed-scope-data.h" +#include "src/parsing/preparse-data.h" #include "src/pending-compilation-error-handler.h" namespace v8 { @@ -26,12 +26,12 @@ class AccountingAllocator; class AstRawString; class AstStringConstants; class AstValueFactory; +class CompilerDispatcher; class DeclarationScope; class FunctionLiteral; class RuntimeCallStats; class Logger; class SourceRangeMap; -class UnicodeCache; class Utf16CharacterStream; class Zone; @@ -81,6 +81,8 @@ class V8_EXPORT_PRIVATE ParseInfo { FLAG_ACCESSOR(kCollectTypeProfile, collect_type_profile, set_collect_type_profile) FLAG_ACCESSOR(kIsAsmWasmBroken, is_asm_wasm_broken, set_asm_wasm_broken) + FLAG_ACCESSOR(kContainsAsmModule, contains_asm_module, + set_contains_asm_module) FLAG_ACCESSOR(kBlockCoverageEnabled, block_coverage_enabled, set_block_coverage_enabled) FLAG_ACCESSOR(kOnBackgroundThread, on_background_thread, @@ -89,9 +91,30 @@ class V8_EXPORT_PRIVATE ParseInfo { set_wrapped_as_function) FLAG_ACCESSOR(kAllowEvalCache, allow_eval_cache, set_allow_eval_cache) FLAG_ACCESSOR(kIsDeclaration, is_declaration, set_declaration) - FLAG_ACCESSOR(kRequiresInstanceFieldsInitializer, - requires_instance_fields_initializer, - set_requires_instance_fields_initializer); + FLAG_ACCESSOR(kRequiresInstanceMembersInitializer, + requires_instance_members_initializer, + set_requires_instance_members_initializer); + FLAG_ACCESSOR(kMightAlwaysOpt, might_always_opt, set_might_always_opt) + FLAG_ACCESSOR(kAllowNativeSyntax, allow_natives_syntax, + set_allow_natives_syntax) + FLAG_ACCESSOR(kAllowLazyCompile, allow_lazy_compile, set_allow_lazy_compile) + FLAG_ACCESSOR(kAllowNativeSyntax, allow_native_syntax, + set_allow_native_syntax); + FLAG_ACCESSOR(kAllowHarmonyPublicFields, allow_harmony_public_fields, + set_allow_harmony_public_fields); + FLAG_ACCESSOR(kAllowHarmonyStaticFields, allow_harmony_static_fields, + set_allow_harmony_static_fields); + FLAG_ACCESSOR(kAllowHarmonyDynamicImport, allow_harmony_dynamic_import, + set_allow_harmony_dynamic_import); + FLAG_ACCESSOR(kAllowHarmonyImportMeta, allow_harmony_import_meta, + set_allow_harmony_import_meta); + FLAG_ACCESSOR(kAllowHarmonyNumericSeparator, allow_harmony_numeric_separator, + set_allow_harmony_numeric_separator); + FLAG_ACCESSOR(kAllowHarmonyPrivateFields, allow_harmony_private_fields, + set_allow_harmony_private_fields); + FLAG_ACCESSOR(kAllowHarmonyPrivateMethods, allow_harmony_private_methods, + set_allow_harmony_private_methods); + FLAG_ACCESSOR(kIsOneshotIIFE, is_oneshot_iife, set_is_oneshot_iife); #undef FLAG_ACCESSOR void set_parse_restriction(ParseRestriction restriction) { @@ -113,12 +136,11 @@ 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); + void set_consumed_preparse_data(std::unique_ptr<ConsumedPreparseData> data) { + consumed_preparse_data_.swap(data); } - ConsumedPreParsedScopeData* consumed_preparsed_scope_data() { - return consumed_preparsed_scope_data_.get(); + ConsumedPreparseData* consumed_preparse_data() { + return consumed_preparse_data_.get(); } DeclarationScope* script_scope() const { return script_scope_; } @@ -141,11 +163,6 @@ class V8_EXPORT_PRIVATE ParseInfo { DeclarationScope* scope() const; - UnicodeCache* unicode_cache() const { return unicode_cache_; } - void set_unicode_cache(UnicodeCache* unicode_cache) { - unicode_cache_ = unicode_cache; - } - uintptr_t stack_limit() const { return stack_limit_; } void set_stack_limit(uintptr_t stack_limit) { stack_limit_ = stack_limit; } @@ -205,6 +222,31 @@ class V8_EXPORT_PRIVATE ParseInfo { return &pending_error_handler_; } + class ParallelTasks { + public: + explicit ParallelTasks(CompilerDispatcher* compiler_dispatcher) + : dispatcher_(compiler_dispatcher) { + DCHECK(dispatcher_); + } + + void Enqueue(ParseInfo* outer_parse_info, const AstRawString* function_name, + FunctionLiteral* literal); + + typedef std::forward_list<std::pair<FunctionLiteral*, uintptr_t>>::iterator + EnqueuedJobsIterator; + + EnqueuedJobsIterator begin() { return enqueued_jobs_.begin(); } + EnqueuedJobsIterator end() { return enqueued_jobs_.end(); } + + CompilerDispatcher* dispatcher() { return dispatcher_; } + + private: + CompilerDispatcher* dispatcher_; + std::forward_list<std::pair<FunctionLiteral*, uintptr_t>> enqueued_jobs_; + }; + + ParallelTasks* parallel_tasks() { return parallel_tasks_.get(); } + //-------------------------------------------------------------------------- // TODO(titzer): these should not be part of ParseInfo. //-------------------------------------------------------------------------- @@ -257,7 +299,19 @@ class V8_EXPORT_PRIVATE ParseInfo { kWrappedAsFunction = 1 << 14, // Implicitly wrapped as function. kAllowEvalCache = 1 << 15, kIsDeclaration = 1 << 16, - kRequiresInstanceFieldsInitializer = 1 << 17, + kRequiresInstanceMembersInitializer = 1 << 17, + kContainsAsmModule = 1 << 18, + kMightAlwaysOpt = 1 << 19, + kAllowLazyCompile = 1 << 20, + kAllowNativeSyntax = 1 << 21, + kAllowHarmonyPublicFields = 1 << 22, + kAllowHarmonyStaticFields = 1 << 23, + kAllowHarmonyDynamicImport = 1 << 24, + kAllowHarmonyImportMeta = 1 << 25, + kAllowHarmonyNumericSeparator = 1 << 26, + kAllowHarmonyPrivateFields = 1 << 27, + kAllowHarmonyPrivateMethods = 1 << 28, + kIsOneshotIIFE = 1 << 29 }; //------------- Inputs to parsing and scope analysis ----------------------- @@ -265,7 +319,6 @@ class V8_EXPORT_PRIVATE ParseInfo { unsigned flags_; v8::Extension* extension_; DeclarationScope* script_scope_; - UnicodeCache* unicode_cache_; uintptr_t stack_limit_; uint64_t hash_seed_; FunctionKind function_kind_; @@ -282,13 +335,14 @@ class V8_EXPORT_PRIVATE ParseInfo { //----------- Inputs+Outputs of parsing and scope analysis ----------------- std::unique_ptr<Utf16CharacterStream> character_stream_; - std::unique_ptr<ConsumedPreParsedScopeData> consumed_preparsed_scope_data_; + std::unique_ptr<ConsumedPreparseData> consumed_preparse_data_; std::unique_ptr<AstValueFactory> ast_value_factory_; const class AstStringConstants* ast_string_constants_; const AstRawString* function_name_; RuntimeCallStats* runtime_call_stats_; Logger* logger_; SourceRangeMap* source_range_map_; // Used when block coverage is enabled. + std::unique_ptr<ParallelTasks> parallel_tasks_; //----------- Output of parsing and scope analysis ------------------------ FunctionLiteral* literal_; diff --git a/deps/v8/src/parsing/parser-base.h b/deps/v8/src/parsing/parser-base.h index bfb056e0c8..33c165cd92 100644 --- a/deps/v8/src/parsing/parser-base.h +++ b/deps/v8/src/parsing/parser-base.h @@ -18,11 +18,12 @@ #include "src/counters.h" #include "src/globals.h" #include "src/log.h" -#include "src/messages.h" -#include "src/parsing/expression-classifier.h" +#include "src/message-template.h" +#include "src/parsing/expression-scope.h" #include "src/parsing/func-name-inferrer.h" #include "src/parsing/scanner.h" #include "src/parsing/token.h" +#include "src/pointer-with-payload.h" #include "src/zone/zone-chunk-list.h" namespace v8 { @@ -39,6 +40,8 @@ enum AllowLabelledFunctionStatement { kDisallowLabelledFunctionStatement, }; +enum ParsingArrowHeadFlag { kCertainlyNotArrowHead, kMaybeArrowHead }; + enum class ParseFunctionFlag : uint8_t { kIsNormal = 0, kIsGenerator = 1 << 0, @@ -74,77 +77,39 @@ struct FormalParametersBase { // Stack-allocated scope to collect source ranges from the parser. class SourceRangeScope final { public: - enum PositionKind { - POSITION_BEG, - POSITION_END, - PEEK_POSITION_BEG, - PEEK_POSITION_END, - }; - - SourceRangeScope(Scanner* scanner, SourceRange* range, - PositionKind pre_kind = PEEK_POSITION_BEG, - PositionKind post_kind = POSITION_END) - : scanner_(scanner), range_(range), post_kind_(post_kind) { - range_->start = GetPosition(pre_kind); + SourceRangeScope(const Scanner* scanner, SourceRange* range) + : scanner_(scanner), range_(range) { + range_->start = scanner->peek_location().beg_pos; DCHECK_NE(range_->start, kNoSourcePosition); + DCHECK_EQ(range_->end, kNoSourcePosition); } - ~SourceRangeScope() { Finalize(); } - - const SourceRange& Finalize() { - if (is_finalized_) return *range_; - is_finalized_ = true; - range_->end = GetPosition(post_kind_); + ~SourceRangeScope() { + DCHECK_EQ(kNoSourcePosition, range_->end); + range_->end = scanner_->location().end_pos; DCHECK_NE(range_->end, kNoSourcePosition); - return *range_; } private: - int32_t GetPosition(PositionKind kind) { - switch (kind) { - case POSITION_BEG: - return scanner_->location().beg_pos; - case POSITION_END: - return scanner_->location().end_pos; - case PEEK_POSITION_BEG: - return scanner_->peek_location().beg_pos; - case PEEK_POSITION_END: - return scanner_->peek_location().end_pos; - default: - UNREACHABLE(); - } - } - - Scanner* scanner_; + const Scanner* scanner_; SourceRange* range_; - PositionKind post_kind_; - bool is_finalized_ = false; DISALLOW_IMPLICIT_CONSTRUCTORS(SourceRangeScope); }; // ---------------------------------------------------------------------------- -// The CHECK_OK macro is a convenient macro to enforce error -// handling for functions that may fail (by returning !*ok). +// The RETURN_IF_PARSE_ERROR macro is a convenient macro to enforce error +// handling for functions that may fail (by returning if there was an parser +// error). // -// CAUTION: This macro appends extra statements after a call, -// thus it must never be used where only a single statement -// is correct (e.g. an if statement branch w/o braces)! - -#define CHECK_OK_CUSTOM(x, ...) ok); \ - if (!*ok) return impl()->x(__VA_ARGS__); \ - ((void)0 -#define DUMMY ) // to make indentation work -#undef DUMMY - -// Used in functions where the return type is ExpressionT. -#define CHECK_OK CHECK_OK_CUSTOM(NullExpression) +// Usage: +// foo = ParseFoo(); // may fail +// RETURN_IF_PARSE_ERROR +// +// SAFE_USE(foo); -#define CHECK_OK_VOID ok); \ - if (!*ok) return; \ - ((void)0 -#define DUMMY ) // to make indentation work -#undef DUMMY +#define RETURN_IF_PARSE_ERROR \ + if (has_error()) return impl()->NullStatement(); // Common base class template shared between parser and pre-parser. // The Impl parameter is the actual class of the parser/pre-parser, @@ -217,8 +182,10 @@ enum class ParsePropertyKind : uint8_t { kAccessorSetter, kValue, kShorthand, + kAssign, kMethod, kClassField, + kShorthandOrClassField, kSpread, kNotSet }; @@ -228,25 +195,43 @@ class ParserBase { public: // Shorten type names defined by ParserTypes<Impl>. typedef ParserTypes<Impl> Types; - typedef typename Types::Identifier IdentifierT; - typedef typename Types::Expression ExpressionT; - typedef typename Types::FunctionLiteral FunctionLiteralT; - typedef typename Types::ObjectLiteralProperty ObjectLiteralPropertyT; + typedef typename v8::internal::ExpressionScope<Types> ExpressionScope; + typedef typename v8::internal::ExpressionParsingScope<Types> + ExpressionParsingScope; + typedef typename v8::internal::AccumulationScope<Types> AccumulationScope; + typedef typename v8::internal::ArrowHeadParsingScope<Types> + ArrowHeadParsingScope; + typedef typename v8::internal::VariableDeclarationParsingScope<Types> + VariableDeclarationParsingScope; + typedef typename v8::internal::ParameterDeclarationParsingScope<Types> + ParameterDeclarationParsingScope; + + // Return types for traversing functions. + typedef typename Types::Block BlockT; + typedef typename Types::BreakableStatement BreakableStatementT; typedef typename Types::ClassLiteralProperty ClassLiteralPropertyT; - typedef typename Types::Suspend SuspendExpressionT; - typedef typename Types::RewritableExpression RewritableExpressionT; + typedef typename Types::ClassPropertyList ClassPropertyListT; + typedef typename Types::Expression ExpressionT; typedef typename Types::ExpressionList ExpressionListT; typedef typename Types::FormalParameters FormalParametersT; + typedef typename Types::ForStatement ForStatementT; + typedef typename Types::FunctionLiteral FunctionLiteralT; + typedef typename Types::Identifier IdentifierT; + typedef typename Types::IterationStatement IterationStatementT; + typedef typename Types::ObjectLiteralProperty ObjectLiteralPropertyT; + typedef typename Types::ObjectPropertyList ObjectPropertyListT; typedef typename Types::Statement StatementT; typedef typename Types::StatementList StatementListT; - typedef typename Types::Block BlockT; - typedef typename Types::ForStatement ForStatementT; - typedef typename v8::internal::ExpressionClassifier<Types> - ExpressionClassifier; + typedef typename Types::Suspend SuspendExpressionT; + // For constructing objects returned by the traversing functions. + typedef typename Types::Factory FactoryT; + // Other implementation-specific tasks. typedef typename Types::FuncNameInferrer FuncNameInferrer; typedef typename Types::FuncNameInferrer::State FuncNameInferrerState; typedef typename Types::SourceRange SourceRange; typedef typename Types::SourceRangeScope SourceRangeScope; + typedef typename Types::Target TargetT; + typedef typename Types::TargetScope TargetScopeT; // All implementation-specific methods must be called through this. Impl* impl() { return static_cast<Impl*>(this); } @@ -261,7 +246,7 @@ class ParserBase { original_scope_(nullptr), function_state_(nullptr), extension_(extension), - fni_(ast_value_factory, zone), + fni_(ast_value_factory), ast_value_factory_(ast_value_factory), ast_node_factory_(ast_value_factory, zone), runtime_call_stats_(runtime_call_stats), @@ -271,34 +256,38 @@ class ParserBase { stack_limit_(stack_limit), pending_error_handler_(pending_error_handler), zone_(zone), - classifier_(nullptr), + expression_scope_(nullptr), scanner_(scanner), - default_eager_compile_hint_(FunctionLiteral::kShouldLazyCompile), function_literal_id_(0), script_id_(script_id), + default_eager_compile_hint_(FunctionLiteral::kShouldLazyCompile), allow_natives_(false), - allow_harmony_do_expressions_(false), allow_harmony_public_fields_(false), allow_harmony_static_fields_(false), allow_harmony_dynamic_import_(false), allow_harmony_import_meta_(false), allow_harmony_private_fields_(false), - allow_eval_cache_(true) {} + allow_harmony_private_methods_(false), + allow_eval_cache_(true) { + pointer_buffer_.reserve(32); + variable_buffer_.reserve(32); + } #define ALLOW_ACCESSORS(name) \ bool allow_##name() const { return allow_##name##_; } \ void set_allow_##name(bool allow) { allow_##name##_ = allow; } ALLOW_ACCESSORS(natives); - ALLOW_ACCESSORS(harmony_do_expressions); ALLOW_ACCESSORS(harmony_public_fields); ALLOW_ACCESSORS(harmony_static_fields); ALLOW_ACCESSORS(harmony_dynamic_import); ALLOW_ACCESSORS(harmony_import_meta); + ALLOW_ACCESSORS(harmony_private_methods); ALLOW_ACCESSORS(eval_cache); #undef ALLOW_ACCESSORS + V8_INLINE bool has_error() const { return scanner()->has_parser_error(); } bool allow_harmony_numeric_separator() const { return scanner()->allow_harmony_numeric_separator(); } @@ -326,6 +315,10 @@ class ParserBase { return default_eager_compile_hint_; } + int loop_nesting_depth() const { + return function_state_->loop_nesting_depth(); + } + int GetNextFunctionLiteralId() { return ++function_literal_id_; } int GetLastFunctionLiteralId() const { return function_literal_id_; } @@ -340,14 +333,9 @@ class ParserBase { Zone* zone() const { return zone_; } protected: - friend class v8::internal::ExpressionClassifier<ParserTypes<Impl>>; - - enum AllowRestrictedIdentifiers { - kAllowRestrictedIdentifiers, - kDontAllowRestrictedIdentifiers - }; - - enum LazyParsingResult { kLazyParsingComplete, kLazyParsingAborted }; + friend class v8::internal::ExpressionScope<ParserTypes<Impl>>; + friend class v8::internal::ExpressionParsingScope<ParserTypes<Impl>>; + friend class v8::internal::ArrowHeadParsingScope<ParserTypes<Impl>>; enum VariableDeclarationContext { kStatementListItem, @@ -356,7 +344,6 @@ class ParserBase { }; class ClassLiteralChecker; - class ObjectLiteralChecker; // --------------------------------------------------------------------------- // BlockState and FunctionState implement the parser's scope stack. @@ -403,33 +390,6 @@ class ParserBase { FunctionKind kind() const { return scope()->function_kind(); } - void RewindDestructuringAssignments(int pos) { - destructuring_assignments_to_rewrite_.Rewind(pos); - } - - void AdoptDestructuringAssignmentsFromParentState(int pos) { - const auto& outer_assignments = - outer_function_state_->destructuring_assignments_to_rewrite_; - DCHECK_GE(outer_assignments.size(), pos); - auto it = outer_assignments.begin(); - it.Advance(pos); - for (; it != outer_assignments.end(); ++it) { - auto expr = *it; - expr->set_scope(scope_); - destructuring_assignments_to_rewrite_.push_back(expr); - } - outer_function_state_->RewindDestructuringAssignments(pos); - } - - const ZoneChunkList<RewritableExpressionT>& - destructuring_assignments_to_rewrite() const { - return destructuring_assignments_to_rewrite_; - } - - ZoneList<typename ExpressionClassifier::Error>* GetReportedErrorList() { - return &reported_errors_; - } - bool next_function_is_likely_called() const { return next_function_is_likely_called_; } @@ -450,41 +410,50 @@ class ParserBase { class FunctionOrEvalRecordingScope { public: explicit FunctionOrEvalRecordingScope(FunctionState* state) - : state_(state) { - prev_value_ = state->contains_function_or_eval_; + : state_and_prev_value_(state, state->contains_function_or_eval_) { state->contains_function_or_eval_ = false; } ~FunctionOrEvalRecordingScope() { - bool found = state_->contains_function_or_eval_; + bool found = state_and_prev_value_->contains_function_or_eval_; if (!found) { - state_->contains_function_or_eval_ = prev_value_; + state_and_prev_value_->contains_function_or_eval_ = + state_and_prev_value_.GetPayload(); } } private: - FunctionState* state_; - bool prev_value_; + PointerWithPayload<FunctionState, bool, 1> state_and_prev_value_; }; - private: - void AddDestructuringAssignment(RewritableExpressionT expr) { - destructuring_assignments_to_rewrite_.push_back(expr); - } + class LoopScope { + public: + explicit LoopScope(FunctionState* function_state) + : function_state_(function_state) { + function_state_->loop_nesting_depth_++; + } + ~LoopScope() { function_state_->loop_nesting_depth_--; } + + private: + FunctionState* function_state_; + }; + + int loop_nesting_depth() const { return loop_nesting_depth_; } + + private: // Properties count estimation. int expected_property_count_; // How many suspends are needed for this function. int suspend_count_; + // How deeply nested we currently are in this function. + int loop_nesting_depth_ = 0; + FunctionState** function_state_stack_; FunctionState* outer_function_state_; DeclarationScope* scope_; - ZoneChunkList<RewritableExpressionT> destructuring_assignments_to_rewrite_; - - ZoneList<typename ExpressionClassifier::Error> reported_errors_; - // A reason, if any, why this function should not be optimized. BailoutReason dont_optimize_reason_; @@ -503,12 +472,10 @@ class ParserBase { }; struct DeclarationDescriptor { - enum Kind { NORMAL, PARAMETER, FOR_EACH }; - Scope* scope; VariableMode mode; + VariableKind kind; int declaration_pos; int initialization_pos; - Kind declaration_kind; }; struct DeclarationParsingResult { @@ -538,18 +505,12 @@ class ParserBase { struct CatchInfo { public: explicit CatchInfo(ParserBase* parser) - : name(parser->impl()->NullIdentifier()), - pattern(parser->impl()->NullExpression()), - scope(nullptr), - init_block(parser->impl()->NullStatement()), - inner_block(parser->impl()->NullStatement()), - bound_names(1, parser->zone()) {} - IdentifierT name; + : pattern(parser->impl()->NullExpression()), + variable(nullptr), + scope(nullptr) {} ExpressionT pattern; + Variable* variable; Scope* scope; - BlockT init_block; - BlockT inner_block; - ZonePtrList<const AstRawString> bound_names; }; struct ForInfo { @@ -578,29 +539,102 @@ class ParserBase { has_name_static_property(false), has_static_computed_names(false), has_static_class_fields(false), - has_instance_class_fields(false), + has_instance_members(false), is_anonymous(false), static_fields_scope(nullptr), - instance_fields_scope(nullptr), + instance_members_scope(nullptr), computed_field_count(0) {} Variable* variable; ExpressionT extends; - typename Types::ClassPropertyList properties; - typename Types::ClassPropertyList static_fields; - typename Types::ClassPropertyList instance_fields; + ClassPropertyListT properties; + ClassPropertyListT static_fields; + ClassPropertyListT instance_fields; FunctionLiteralT constructor; bool has_seen_constructor; bool has_name_static_property; bool has_static_computed_names; bool has_static_class_fields; - bool has_instance_class_fields; + bool has_instance_members; bool is_anonymous; DeclarationScope* static_fields_scope; - DeclarationScope* instance_fields_scope; + DeclarationScope* instance_members_scope; int computed_field_count; }; + enum class PropertyPosition { kObjectLiteral, kClassLiteral }; + struct ParsePropertyInfo { + public: + explicit ParsePropertyInfo(ParserBase* parser, + AccumulationScope* accumulation_scope = nullptr) + : accumulation_scope(accumulation_scope), + name(parser->impl()->NullIdentifier()), + position(PropertyPosition::kClassLiteral), + function_flags(ParseFunctionFlag::kIsNormal), + kind(ParsePropertyKind::kNotSet), + is_computed_name(false), + is_private(false), + is_static(false), + is_rest(false) {} + + bool ParsePropertyKindFromToken(Token::Value token) { + // 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 = ParsePropertyKind::kValue; + return true; + case Token::COMMA: + kind = ParsePropertyKind::kShorthand; + return true; + case Token::RBRACE: + kind = ParsePropertyKind::kShorthandOrClassField; + return true; + case Token::ASSIGN: + kind = ParsePropertyKind::kAssign; + return true; + case Token::LPAREN: + kind = ParsePropertyKind::kMethod; + return true; + case Token::MUL: + case Token::SEMICOLON: + kind = ParsePropertyKind::kClassField; + return true; + default: + break; + } + return false; + } + + AccumulationScope* accumulation_scope; + IdentifierT name; + PropertyPosition position; + ParseFunctionFlags function_flags; + ParsePropertyKind kind; + bool is_computed_name; + bool is_private; + bool is_static; + bool is_rest; + }; + + ClassLiteralProperty::Kind ClassPropertyKindFor(ParsePropertyKind kind) { + switch (kind) { + case ParsePropertyKind::kAccessorGetter: + return ClassLiteralProperty::GETTER; + case ParsePropertyKind::kAccessorSetter: + return ClassLiteralProperty::SETTER; + case ParsePropertyKind::kMethod: + return ClassLiteralProperty::METHOD; + case ParsePropertyKind::kClassField: + return ClassLiteralProperty::FIELD; + default: + // Only returns for deterministic kinds + UNREACHABLE(); + } + } + const AstRawString* ClassFieldVariableName(AstValueFactory* ast_value_factory, int index) { std::string name = ".class-field-" + std::to_string(index); @@ -667,6 +701,22 @@ class ParserBase { return scope()->GetClosureScope(); } + VariableProxy* NewRawVariable(const AstRawString* name, int pos) { + return factory()->ast_node_factory()->NewVariableProxy( + name, NORMAL_VARIABLE, pos); + } + + VariableProxy* NewUnresolved(const AstRawString* name) { + return scope()->NewUnresolved(factory()->ast_node_factory(), name, + scanner()->location().beg_pos); + } + + VariableProxy* NewUnresolved(const AstRawString* name, int begin_pos, + VariableKind kind = NORMAL_VARIABLE) { + return scope()->NewUnresolved(factory()->ast_node_factory(), name, + begin_pos, kind); + } + Scanner* scanner() const { return scanner_; } AstValueFactory* ast_value_factory() const { return ast_value_factory_; } int position() const { return scanner_->location().beg_pos; } @@ -676,14 +726,18 @@ class ParserBase { bool stack_overflow() const { return pending_error_handler()->stack_overflow(); } - void set_stack_overflow() { pending_error_handler()->set_stack_overflow(); } + void set_stack_overflow() { + scanner_->set_parser_error(); + pending_error_handler()->set_stack_overflow(); + } + void CheckStackOverflow() { + // Any further calls to Next or peek will return the illegal token. + if (GetCurrentStackPosition() < stack_limit_) set_stack_overflow(); + } int script_id() { return script_id_; } void set_script_id(int id) { script_id_ = id; } - V8_INLINE Token::Value peek() { - if (stack_overflow()) return Token::ILLEGAL; - return scanner()->peek(); - } + V8_INLINE Token::Value peek() { return scanner()->peek(); } // Returns the position past the following semicolon (if it exists), and the // position past the end of the current token otherwise. @@ -691,33 +745,19 @@ class ParserBase { return (peek() == Token::SEMICOLON) ? peek_end_position() : end_position(); } - V8_INLINE Token::Value PeekAhead() { - if (stack_overflow()) return Token::ILLEGAL; - return scanner()->PeekAhead(); - } + V8_INLINE Token::Value PeekAhead() { return scanner()->PeekAhead(); } - V8_INLINE Token::Value Next() { - if (stack_overflow()) return Token::ILLEGAL; - { - if (GetCurrentStackPosition() < stack_limit_) { - // Any further calls to Next or peek will return the illegal token. - // The current call must return the next token, which might already - // have been peek'ed. - set_stack_overflow(); - } - } - return scanner()->Next(); - } + V8_INLINE Token::Value Next() { return scanner()->Next(); } - void Consume(Token::Value token) { - Token::Value next = Next(); + V8_INLINE void Consume(Token::Value token) { + Token::Value next = scanner()->Next(); USE(next); USE(token); - DCHECK_EQ(next, token); + DCHECK_IMPLIES(!has_error(), next == token); } - bool Check(Token::Value token) { - Token::Value next = peek(); + V8_INLINE bool Check(Token::Value token) { + Token::Value next = scanner()->peek(); if (next == token) { Consume(next); return true; @@ -725,28 +765,26 @@ class ParserBase { return false; } - void Expect(Token::Value token, bool* ok) { + void Expect(Token::Value token) { Token::Value next = Next(); - if (next != token) { + if (V8_UNLIKELY(next != token)) { ReportUnexpectedToken(next); - *ok = false; } } - void ExpectSemicolon(bool* ok) { + void ExpectSemicolon() { // Check for automatic semicolon insertion according to // the rules given in ECMA-262, section 7.9, page 21. Token::Value tok = peek(); - if (tok == Token::SEMICOLON) { + if (V8_LIKELY(tok == Token::SEMICOLON)) { Next(); return; } - if (scanner()->HasLineTerminatorBeforeNext() || tok == Token::RBRACE || - tok == Token::EOS) { + if (V8_LIKELY(scanner()->HasLineTerminatorBeforeNext() || + Token::IsAutoSemicolon(tok))) { return; } - *ok = false; if (scanner()->current_token() == Token::AWAIT && !is_async_function()) { ReportMessageAt(scanner()->location(), MessageTemplate::kAwaitNotInAsyncFunction, kSyntaxError); @@ -756,38 +794,28 @@ class ParserBase { ReportUnexpectedToken(Next()); } - // Dummy functions, just useful as arguments to CHECK_OK_CUSTOM. - static void Void() {} - template <typename T> - static T Return(T result) { - return result; - } - bool peek_any_identifier() { return Token::IsAnyIdentifier(peek()); } - bool CheckContextualKeyword(Token::Value token) { - if (PeekContextualKeyword(token)) { + bool PeekContextualKeyword(const AstRawString* name) { + return peek() == Token::IDENTIFIER && + scanner()->NextSymbol(ast_value_factory()) == name; + } + + bool CheckContextualKeyword(const AstRawString* name) { + if (PeekContextualKeyword(name)) { Consume(Token::IDENTIFIER); return true; } return false; } - bool PeekContextualKeyword(Token::Value token) { - DCHECK(Token::IsContextualKeyword(token)); - return peek() == Token::IDENTIFIER && - scanner()->next_contextual_token() == token; - } - - void ExpectMetaProperty(Token::Value property_name, const char* full_name, - int pos, bool* ok); + void ExpectMetaProperty(const AstRawString* property_name, + const char* full_name, int pos); - void ExpectContextualKeyword(Token::Value token, bool* ok) { - DCHECK(Token::IsContextualKeyword(token)); - Expect(Token::IDENTIFIER, CHECK_OK_CUSTOM(Void)); - if (scanner()->current_contextual_token() != token) { + void ExpectContextualKeyword(const AstRawString* name) { + Expect(Token::IDENTIFIER); + if (V8_UNLIKELY(scanner()->CurrentSymbol(ast_value_factory()) != name)) { ReportUnexpectedToken(scanner()->current_token()); - *ok = false; } } @@ -795,7 +823,7 @@ class ParserBase { if (Check(Token::IN)) { *visit_mode = ForEachStatement::ENUMERATE; return true; - } else if (CheckContextualKeyword(Token::OF)) { + } else if (CheckContextualKeyword(ast_value_factory()->of_string())) { *visit_mode = ForEachStatement::ITERATE; return true; } @@ -803,23 +831,23 @@ class ParserBase { } bool PeekInOrOf() { - return peek() == Token::IN || PeekContextualKeyword(Token::OF); + return peek() == Token::IN || + PeekContextualKeyword(ast_value_factory()->of_string()); } // Checks whether an octal literal was last seen between beg_pos and end_pos. // Only called for strict mode strings. - void CheckStrictOctalLiteral(int beg_pos, int end_pos, bool* ok) { + void CheckStrictOctalLiteral(int beg_pos, int end_pos) { Scanner::Location octal = scanner()->octal_position(); if (octal.IsValid() && beg_pos <= octal.beg_pos && octal.end_pos <= end_pos) { - MessageTemplate::Template message = scanner()->octal_message(); + MessageTemplate message = scanner()->octal_message(); DCHECK_NE(message, MessageTemplate::kNone); impl()->ReportMessageAt(octal, message); scanner()->clear_octal_position(); if (message == MessageTemplate::kStrictDecimalWithLeadingZero) { impl()->CountUsage(v8::Isolate::kDecimalWithLeadingZeroInStrictMode); } - *ok = false; } } @@ -827,29 +855,29 @@ class ParserBase { // appears in the current template literal token. In the presence of such, // either returns false or reports an error, depending on should_throw. // Otherwise returns true. - inline bool CheckTemplateEscapes(bool should_throw, bool* ok) { - DCHECK(scanner()->current_token() == Token::TEMPLATE_SPAN || - scanner()->current_token() == Token::TEMPLATE_TAIL); - if (!scanner()->has_invalid_template_escape()) { - return true; - } + inline bool CheckTemplateEscapes(bool should_throw) { + DCHECK(Token::IsTemplate(scanner()->current_token())); + if (!scanner()->has_invalid_template_escape()) return true; // Handle error case(s) if (should_throw) { impl()->ReportMessageAt(scanner()->invalid_template_escape_location(), scanner()->invalid_template_escape_message()); - *ok = false; } - return false; + scanner()->clear_invalid_template_escape_message(); + return should_throw; } - void CheckDestructuringElement(ExpressionT element, int beg_pos, int end_pos); + ExpressionT ParsePossibleDestructuringSubPattern(AccumulationScope* scope); + void ClassifyParameter(IdentifierT parameter, int beg_pos, int end_pos); + void ClassifyArrowParameter(AccumulationScope* accumulation_scope, + int position, ExpressionT parameter); // Checking the name of a function literal. This has to be done after parsing // the function, since the function can declare itself strict. void CheckFunctionName(LanguageMode language_mode, IdentifierT function_name, FunctionNameValidity function_name_validity, - const Scanner::Location& function_name_loc, bool* ok) { + const Scanner::Location& function_name_loc) { if (impl()->IsNull(function_name)) return; if (function_name_validity == kSkipFunctionNameCheck) return; // The function name needs to be checked in strict mode. @@ -858,24 +886,15 @@ class ParserBase { if (impl()->IsEvalOrArguments(function_name)) { impl()->ReportMessageAt(function_name_loc, MessageTemplate::kStrictEvalArguments); - *ok = false; return; } if (function_name_validity == kFunctionNameIsStrictReserved) { impl()->ReportMessageAt(function_name_loc, MessageTemplate::kUnexpectedStrictReserved); - *ok = false; return; } } - // Determine precedence of given token. - static int Precedence(Token::Value token, bool accept_IN) { - if (token == Token::IN && !accept_IN) - return 0; // 0 precedence will terminate binary expression parsing - return Token::Precedence(token); - } - typename Types::Factory* factory() { return &ast_node_factory_; } DeclarationScope* GetReceiverScope() const { @@ -907,405 +926,280 @@ class ParserBase { } // Report syntax errors. - void ReportMessage(MessageTemplate::Template message) { + V8_NOINLINE void ReportMessage(MessageTemplate message) { Scanner::Location source_location = scanner()->location(); impl()->ReportMessageAt(source_location, message, static_cast<const char*>(nullptr), kSyntaxError); } template <typename T> - void ReportMessage(MessageTemplate::Template message, T arg, - ParseErrorType error_type = kSyntaxError) { + V8_NOINLINE void ReportMessage(MessageTemplate message, T arg, + ParseErrorType error_type = kSyntaxError) { Scanner::Location source_location = scanner()->location(); impl()->ReportMessageAt(source_location, message, arg, error_type); } - void ReportMessageAt(Scanner::Location location, - MessageTemplate::Template message, - ParseErrorType error_type) { + V8_NOINLINE void ReportMessageAt(Scanner::Location location, + MessageTemplate message, + ParseErrorType error_type) { impl()->ReportMessageAt(location, message, static_cast<const char*>(nullptr), error_type); } - void GetUnexpectedTokenMessage( - Token::Value token, MessageTemplate::Template* message, - Scanner::Location* location, const char** arg, - MessageTemplate::Template default_ = MessageTemplate::kUnexpectedToken); + V8_NOINLINE void ReportUnexpectedToken(Token::Value token); - void ReportUnexpectedToken(Token::Value token); - void ReportUnexpectedTokenAt( - Scanner::Location location, Token::Value token, - MessageTemplate::Template message = MessageTemplate::kUnexpectedToken); - - void ReportClassifierError( - const typename ExpressionClassifier::Error& error) { - if (classifier()->does_error_reporting()) { - impl()->ReportMessageAt(error.location, error.message, error.arg); - } else { - impl()->ReportUnidentifiableError(); - } + void ValidateFormalParameters(LanguageMode language_mode, + const FormalParametersT& parameters, + bool allow_duplicates) { + if (!allow_duplicates) parameters.ValidateDuplicate(impl()); + if (is_strict(language_mode)) parameters.ValidateStrictMode(impl()); } - void ValidateExpression(bool* ok) { - if (!classifier()->is_valid_expression()) { - ReportClassifierError(classifier()->expression_error()); - *ok = false; - } + V8_INLINE IdentifierT ParseAndClassifyIdentifier(Token::Value token); + // Parses an identifier or a strict mode future reserved word. Allows passing + // in function_kind for the case of parsing the identifier in a function + // expression, where the relevant "function_kind" bit is of the function being + // parsed, not the containing function. + V8_INLINE IdentifierT ParseIdentifier(FunctionKind function_kind); + V8_INLINE IdentifierT ParseIdentifier() { + return ParseIdentifier(function_state_->kind()); } + // Same as above but additionally disallows 'eval' and 'arguments' in strict + // mode. + IdentifierT ParseNonRestrictedIdentifier(); - void ValidateFormalParameterInitializer(bool* ok) { - if (!classifier()->is_valid_formal_parameter_initializer()) { - ReportClassifierError(classifier()->formal_parameter_initializer_error()); - *ok = false; - } - } + V8_INLINE IdentifierT ParsePropertyName(); - void ValidateBindingPattern(bool* ok) { - if (!classifier()->is_valid_binding_pattern()) { - ReportClassifierError(classifier()->binding_pattern_error()); - *ok = false; - } - } + ExpressionT ParsePropertyOrPrivatePropertyName(); - void ValidateAssignmentPattern(bool* ok) { - if (!classifier()->is_valid_assignment_pattern()) { - ReportClassifierError(classifier()->assignment_pattern_error()); - *ok = false; - } - } + ExpressionT ParseRegExpLiteral(); - void ValidateFormalParameters(LanguageMode language_mode, - bool allow_duplicates, bool* ok) { - if (!allow_duplicates && - !classifier()->is_valid_formal_parameter_list_without_duplicates()) { - ReportClassifierError(classifier()->duplicate_formal_parameter_error()); - *ok = false; - } else if (is_strict(language_mode) && - !classifier()->is_valid_strict_mode_formal_parameters()) { - ReportClassifierError(classifier()->strict_mode_formal_parameter_error()); - *ok = false; - } - } - - bool IsValidArrowFormalParametersStart(Token::Value token) { - return Token::IsAnyIdentifier(token) || token == Token::LPAREN; - } - - void ValidateArrowFormalParameters(ExpressionT expr, - bool parenthesized_formals, bool is_async, - bool* ok) { - if (classifier()->is_valid_binding_pattern()) { - // A simple arrow formal parameter: IDENTIFIER => BODY. - if (!impl()->IsIdentifier(expr)) { - impl()->ReportMessageAt(scanner()->location(), - MessageTemplate::kUnexpectedToken, - Token::String(scanner()->current_token())); - *ok = false; - } - } else if (!classifier()->is_valid_arrow_formal_parameters()) { - // If after parsing the expr, we see an error but the expression is - // neither a valid binding pattern nor a valid parenthesized formal - // parameter list, show the "arrow formal parameters" error if the formals - // started with a parenthesis, and the binding pattern error otherwise. - const typename ExpressionClassifier::Error& error = - parenthesized_formals ? classifier()->arrow_formal_parameters_error() - : classifier()->binding_pattern_error(); - ReportClassifierError(error); - *ok = false; - } - if (is_async && !classifier()->is_valid_async_arrow_formal_parameters()) { - const typename ExpressionClassifier::Error& error = - classifier()->async_arrow_formal_parameters_error(); - ReportClassifierError(error); - *ok = false; - } - } - - void ValidateLetPattern(bool* ok) { - if (!classifier()->is_valid_let_pattern()) { - ReportClassifierError(classifier()->let_pattern_error()); - *ok = false; - } - } - - void BindingPatternUnexpectedToken() { - MessageTemplate::Template message = MessageTemplate::kUnexpectedToken; - const char* arg; - Scanner::Location location = scanner()->peek_location(); - GetUnexpectedTokenMessage(peek(), &message, &location, &arg); - classifier()->RecordBindingPatternError(location, message, arg); - } - - void ArrowFormalParametersUnexpectedToken() { - MessageTemplate::Template message = MessageTemplate::kUnexpectedToken; - const char* arg; - Scanner::Location location = scanner()->peek_location(); - GetUnexpectedTokenMessage(peek(), &message, &location, &arg); - classifier()->RecordArrowFormalParametersError(location, message, arg); - } - - // Recursive descent functions. - // All ParseXXX functions take as the last argument an *ok parameter - // which is set to false if parsing failed; it is unchanged otherwise. - // By making the 'exception handling' explicit, we are forced to check - // for failure at the call sites. The family of CHECK_OK* macros can - // be useful for this. - - // Parses an identifier that is valid for the current scope, in particular it - // fails on strict mode future reserved keywords in a strict scope. If - // allow_eval_or_arguments is kAllowEvalOrArguments, we allow "eval" or - // "arguments" as identifier even in strict mode (this is needed in cases like - // "var foo = eval;"). - IdentifierT ParseIdentifier(AllowRestrictedIdentifiers, bool* ok); - IdentifierT ParseAndClassifyIdentifier(bool* ok); - // Parses an identifier or a strict mode future reserved word, and indicate - // whether it is strict mode future reserved. Allows passing in function_kind - // for the case of parsing the identifier in a function expression, where the - // relevant "function_kind" bit is of the function being parsed, not the - // containing function. - IdentifierT ParseIdentifierOrStrictReservedWord(FunctionKind function_kind, - bool* is_strict_reserved, - bool* is_await, bool* ok); - IdentifierT ParseIdentifierOrStrictReservedWord(bool* is_strict_reserved, - bool* is_await, bool* ok) { - return ParseIdentifierOrStrictReservedWord( - function_state_->kind(), is_strict_reserved, is_await, ok); - } - - V8_INLINE IdentifierT ParseIdentifierName(bool* ok); - - ExpressionT ParseIdentifierNameOrPrivateName(bool* ok); - - ExpressionT ParseRegExpLiteral(bool* ok); - - ExpressionT ParseBindingPattern(bool* ok); - ExpressionT ParsePrimaryExpression(bool* is_async, bool* ok); - - // Use when parsing an expression that is known to not be a pattern or part - // of a pattern. - 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. - // It should be used whenever we're parsing something with the "cover" - // grammar that recognizes both patterns and non-patterns (which roughly - // corresponds to what's inside the parentheses generated by the symbol + ExpressionT ParseBindingPattern(); + ExpressionT ParsePrimaryExpression(); + + // Use when parsing an expression that is known to not be a pattern or part of + // a pattern. + V8_INLINE ExpressionT ParseExpression(); + V8_INLINE ExpressionT ParseAssignmentExpression(); + + // These methods do not wrap the parsing of the expression inside a new + // expression_scope; they use the outer expression_scope instead. They should + // be used whenever we're parsing something with the "cover" grammar that + // recognizes both patterns and non-patterns (which roughly corresponds to + // what's inside the parentheses generated by the symbol // "CoverParenthesizedExpressionAndArrowParameterList" in the ES 2017 // specification). - ExpressionT ParseExpressionCoverGrammar(bool accept_IN, bool* ok); + ExpressionT ParseExpressionCoverGrammar(); + ExpressionT ParseAssignmentExpressionCoverGrammar(); + + ExpressionT ParseArrowParametersWithRest(ExpressionListT* list, + AccumulationScope* scope); - ExpressionT ParseArrayLiteral(bool* ok); + ExpressionT ParseArrayLiteral(); inline static bool IsAccessor(ParsePropertyKind kind) { return IsInRange(kind, ParsePropertyKind::kAccessorGetter, ParsePropertyKind::kAccessorSetter); } - ExpressionT ParsePropertyName(IdentifierT* name, ParsePropertyKind* kind, - ParseFunctionFlags* flags, - bool* is_computed_name, bool* ok); - ExpressionT ParseObjectLiteral(bool* ok); + ExpressionT ParseProperty(ParsePropertyInfo* prop_info); + ExpressionT ParseObjectLiteral(); 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, int beg_pos, - bool is_static, bool* ok); + ClassInfo* class_info, ParsePropertyInfo* prop_info, bool has_extends); + void CheckClassFieldName(IdentifierT name, bool is_static); + void CheckClassMethodName(IdentifierT name, ParsePropertyKind type, + ParseFunctionFlags flags, bool is_static, + bool* has_seen_constructor); + ExpressionT ParseMemberInitializer(ClassInfo* class_info, int beg_pos, + bool is_static); ObjectLiteralPropertyT ParseObjectPropertyDefinition( - ObjectLiteralChecker* checker, bool* is_computed_name, - bool* is_rest_property, bool* ok); - ExpressionListT ParseArguments(Scanner::Location* first_spread_pos, - bool maybe_arrow, - bool* is_simple_parameter_list, bool* ok); - ExpressionListT ParseArguments(Scanner::Location* first_spread_pos, - bool* ok) { - 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 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 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); - - // `rewritable_length`: length of the destructuring_assignments_to_rewrite() - // queue in the parent function state, prior to parsing of formal parameters. - // If the arrow function is lazy, any items added during formal parameter - // parsing are removed from the queue. - ExpressionT ParseArrowFunctionLiteral(bool accept_IN, - const FormalParametersT& parameters, - int rewritable_length, bool* ok); - void ParseAsyncFunctionBody(Scope* scope, StatementListT body, bool* ok); - ExpressionT ParseAsyncFunctionLiteral(bool* ok); + ParsePropertyInfo* prop_info, bool* has_seen_proto); + void ParseArguments( + ExpressionListT* args, bool* has_spread, + ParsingArrowHeadFlag maybe_arrow = kCertainlyNotArrowHead); + + ExpressionT ParseYieldExpression(); + V8_INLINE ExpressionT ParseConditionalExpression(); + ExpressionT ParseConditionalContinuation(ExpressionT expression, int pos); + ExpressionT ParseBinaryContinuation(ExpressionT x, int prec, int prec1); + V8_INLINE ExpressionT ParseBinaryExpression(int prec); + ExpressionT ParseUnaryOrPrefixExpression(); + ExpressionT ParseAwaitExpression(); + V8_INLINE ExpressionT ParseUnaryExpression(); + V8_INLINE ExpressionT ParsePostfixExpression(); + V8_INLINE ExpressionT ParseLeftHandSideExpression(); + ExpressionT ParseLeftHandSideContinuation(ExpressionT expression); + ExpressionT ParseMemberWithPresentNewPrefixesExpression(); + V8_INLINE ExpressionT ParseMemberWithNewPrefixesExpression(); + ExpressionT ParseFunctionExpression(); + V8_INLINE ExpressionT ParseMemberExpression(); + V8_INLINE ExpressionT + ParseMemberExpressionContinuation(ExpressionT expression) { + if (!Token::IsMember(peek())) return expression; + return DoParseMemberExpressionContinuation(expression); + } + ExpressionT DoParseMemberExpressionContinuation(ExpressionT expression); + + ExpressionT ParseArrowFunctionLiteral(const FormalParametersT& parameters); + void ParseAsyncFunctionBody(Scope* scope, StatementListT* body); + ExpressionT ParseAsyncFunctionLiteral(); ExpressionT ParseClassLiteral(IdentifierT name, Scanner::Location class_name_location, bool name_is_strict_reserved, - int class_token_pos, bool* ok); - ExpressionT ParseTemplateLiteral(ExpressionT tag, int start, bool tagged, - bool* ok); - ExpressionT ParseSuperExpression(bool is_new, bool* ok); - ExpressionT ParseImportExpressions(bool* ok); - ExpressionT ParseNewTargetExpression(bool* ok); - - V8_INLINE void ParseFormalParameter(FormalParametersT* parameters, bool* ok); - void ParseFormalParameterList(FormalParametersT* parameters, bool* ok); + int class_token_pos); + ExpressionT ParseTemplateLiteral(ExpressionT tag, int start, bool tagged); + ExpressionT ParseSuperExpression(bool is_new); + ExpressionT ParseImportExpressions(); + ExpressionT ParseNewTargetExpression(); + + V8_INLINE void ParseFormalParameter(FormalParametersT* parameters); + void ParseFormalParameterList(FormalParametersT* parameters); void CheckArityRestrictions(int param_count, FunctionKind function_type, bool has_rest, int formals_start_pos, - int formals_end_pos, bool* ok); + int formals_end_pos); - BlockT ParseVariableDeclarations(VariableDeclarationContext var_context, - DeclarationParsingResult* parsing_result, - ZonePtrList<const AstRawString>* names, - bool* ok); + void ParseVariableDeclarations(VariableDeclarationContext var_context, + DeclarationParsingResult* parsing_result, + ZonePtrList<const AstRawString>* names); StatementT ParseAsyncFunctionDeclaration( - ZonePtrList<const AstRawString>* names, bool default_export, bool* ok); - StatementT ParseFunctionDeclaration(bool* ok); + ZonePtrList<const AstRawString>* names, bool default_export); + StatementT ParseFunctionDeclaration(); StatementT ParseHoistableDeclaration(ZonePtrList<const AstRawString>* names, - bool default_export, bool* ok); + bool default_export); StatementT ParseHoistableDeclaration(int pos, ParseFunctionFlags flags, ZonePtrList<const AstRawString>* names, - bool default_export, bool* ok); + bool default_export); StatementT ParseClassDeclaration(ZonePtrList<const AstRawString>* names, - bool default_export, bool* ok); - StatementT ParseNativeDeclaration(bool* ok); + bool default_export); + StatementT ParseNativeDeclaration(); // 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, + void ParseFunctionBody(StatementListT* body, IdentifierT function_name, int pos, const FormalParametersT& parameters, FunctionKind kind, 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 - // definition of "long and trivial" is: - // - over kLazyParseTrialLimit statements - // - all starting with an identifier (i.e., no if, for, while, etc.) - static const int kLazyParseTrialLimit = 200; + FunctionBodyType body_type); // TODO(nikolaos, marja): The first argument should not really be passed // by value. The method is expected to add the parsed statements to the // list. This works because in the case of the parser, StatementListT is // a pointer whereas the preparser does not really modify the body. - V8_INLINE void ParseStatementList(StatementListT body, Token::Value end_token, - bool* ok) { - LazyParsingResult result = ParseStatementList(body, end_token, false, ok); - USE(result); - DCHECK_EQ(result, kLazyParsingComplete); - } - V8_INLINE LazyParsingResult ParseStatementList(StatementListT body, - Token::Value end_token, - bool may_abort, bool* ok); - StatementT ParseStatementListItem(bool* ok); + V8_INLINE void ParseStatementList(StatementListT* body, + Token::Value end_token); + StatementT ParseStatementListItem(); StatementT ParseStatement(ZonePtrList<const AstRawString>* labels, - ZonePtrList<const AstRawString>* own_labels, - bool* ok) { + ZonePtrList<const AstRawString>* own_labels) { return ParseStatement(labels, own_labels, - kDisallowLabelledFunctionStatement, ok); + kDisallowLabelledFunctionStatement); } StatementT ParseStatement(ZonePtrList<const AstRawString>* labels, ZonePtrList<const AstRawString>* own_labels, - AllowLabelledFunctionStatement allow_function, - bool* ok); - BlockT ParseBlock(ZonePtrList<const AstRawString>* labels, bool* ok); + AllowLabelledFunctionStatement allow_function); + BlockT ParseBlock(ZonePtrList<const AstRawString>* labels); // Parse a SubStatement in strict mode, or with an extra block scope in // sloppy mode to handle // ES#sec-functiondeclarations-in-ifstatement-statement-clauses - StatementT ParseScopedStatement(ZonePtrList<const AstRawString>* labels, - bool* ok); + StatementT ParseScopedStatement(ZonePtrList<const AstRawString>* labels); StatementT ParseVariableStatement(VariableDeclarationContext var_context, - ZonePtrList<const AstRawString>* names, - bool* ok); + ZonePtrList<const AstRawString>* names); // Magical syntax support. - ExpressionT ParseV8Intrinsic(bool* ok); + ExpressionT ParseV8Intrinsic(); - ExpressionT ParseDoExpression(bool* ok); - - StatementT ParseDebuggerStatement(bool* ok); + StatementT ParseDebuggerStatement(); StatementT ParseExpressionOrLabelledStatement( ZonePtrList<const AstRawString>* labels, ZonePtrList<const AstRawString>* own_labels, - AllowLabelledFunctionStatement allow_function, bool* ok); - StatementT ParseIfStatement(ZonePtrList<const AstRawString>* labels, - bool* ok); - StatementT ParseContinueStatement(bool* ok); - StatementT ParseBreakStatement(ZonePtrList<const AstRawString>* labels, - bool* ok); - StatementT ParseReturnStatement(bool* ok); - StatementT ParseWithStatement(ZonePtrList<const AstRawString>* labels, - bool* ok); + AllowLabelledFunctionStatement allow_function); + StatementT ParseIfStatement(ZonePtrList<const AstRawString>* labels); + StatementT ParseContinueStatement(); + StatementT ParseBreakStatement(ZonePtrList<const AstRawString>* labels); + StatementT ParseReturnStatement(); + StatementT ParseWithStatement(ZonePtrList<const AstRawString>* labels); StatementT ParseDoWhileStatement(ZonePtrList<const AstRawString>* labels, - ZonePtrList<const AstRawString>* own_labels, - bool* ok); + ZonePtrList<const AstRawString>* own_labels); StatementT ParseWhileStatement(ZonePtrList<const AstRawString>* labels, - ZonePtrList<const AstRawString>* own_labels, - bool* ok); - StatementT ParseThrowStatement(bool* ok); - StatementT ParseSwitchStatement(ZonePtrList<const AstRawString>* labels, - bool* ok); - V8_INLINE StatementT ParseTryStatement(bool* ok); + ZonePtrList<const AstRawString>* own_labels); + StatementT ParseThrowStatement(); + StatementT ParseSwitchStatement(ZonePtrList<const AstRawString>* labels); + V8_INLINE StatementT ParseTryStatement(); StatementT ParseForStatement(ZonePtrList<const AstRawString>* labels, - ZonePtrList<const AstRawString>* own_labels, - bool* ok); + ZonePtrList<const AstRawString>* own_labels); StatementT ParseForEachStatementWithDeclarations( int stmt_pos, ForInfo* for_info, ZonePtrList<const AstRawString>* labels, - ZonePtrList<const AstRawString>* own_labels, Scope* inner_block_scope, - bool* ok); + ZonePtrList<const AstRawString>* own_labels, Scope* inner_block_scope); StatementT ParseForEachStatementWithoutDeclarations( int stmt_pos, ExpressionT expression, int lhs_beg_pos, int lhs_end_pos, ForInfo* for_info, ZonePtrList<const AstRawString>* labels, - ZonePtrList<const AstRawString>* own_labels, bool* ok); + ZonePtrList<const AstRawString>* own_labels); // Parse a C-style for loop: 'for (<init>; <cond>; <next>) { ... }' // "for (<init>;" is assumed to have been parser already. ForStatementT ParseStandardForLoop( int stmt_pos, ZonePtrList<const AstRawString>* labels, ZonePtrList<const AstRawString>* own_labels, ExpressionT* cond, - StatementT* next, StatementT* body, bool* ok); + StatementT* next, StatementT* body); // Same as the above, but handles those cases where <init> is a // lexical variable declaration. StatementT ParseStandardForLoopWithLexicalDeclarations( int stmt_pos, StatementT init, ForInfo* for_info, ZonePtrList<const AstRawString>* labels, - ZonePtrList<const AstRawString>* own_labels, bool* ok); - StatementT ParseForAwaitStatement(ZonePtrList<const AstRawString>* labels, - ZonePtrList<const AstRawString>* own_labels, - bool* ok); + ZonePtrList<const AstRawString>* own_labels); + StatementT ParseForAwaitStatement( + ZonePtrList<const AstRawString>* labels, + ZonePtrList<const AstRawString>* own_labels); + + V8_INLINE bool IsLet(const AstRawString* identifier) const { + return identifier == ast_value_factory()->let_string(); + } + + void DesugarBindingInForEachStatement(ForInfo* for_info, BlockT* body_block, + ExpressionT* each_variable) { + // Annex B.3.5 prohibits the form + // `try {} catch(e) { for (var e of {}); }` + // So if we are parsing a statement like `for (var ... of ...)` + // we need to walk up the scope chain and look for catch scopes + // which have a simple binding, then compare their binding against + // all of the names declared in the init of the for-of we're + // parsing. + bool is_for_var_of = + for_info->mode == ForEachStatement::ITERATE && + for_info->parsing_result.descriptor.mode == VariableMode::kVar; + + if (is_for_var_of) { + Scope* scope = this->scope(); + while (scope != nullptr && !scope->is_declaration_scope()) { + if (scope->is_catch_scope()) { + auto name = scope->catch_variable()->raw_name(); + // If it's a simple binding and the name is declared in the for loop. + if (name != ast_value_factory()->dot_catch_string() && + for_info->bound_names.Contains(name)) { + impl()->ReportMessageAt(for_info->parsing_result.bindings_loc, + MessageTemplate::kVarRedeclaration, name); + } + } + scope = scope->outer_scope(); + } + } + + impl()->DesugarBindingInForEachStatement(for_info, body_block, + each_variable); + } bool IsNextLetKeyword(); - bool IsTrivialExpression(); // Checks if the expression is a valid reference expression (e.g., on the // left-hand side of assignments). Although ruled out by ECMA as early errors, // we allow calls for web compatibility and rewrite them to a runtime throw. - ExpressionT CheckAndRewriteReferenceExpression( - ExpressionT expression, int beg_pos, int end_pos, - MessageTemplate::Template message, bool* ok); - ExpressionT CheckAndRewriteReferenceExpression( - ExpressionT expression, int beg_pos, int end_pos, - MessageTemplate::Template message, ParseErrorType type, bool* ok); + ExpressionT RewriteInvalidReferenceExpression( + ExpressionT expression, int beg_pos, int end_pos, MessageTemplate message, + ParseErrorType type = kReferenceError); bool IsValidReferenceExpression(ExpressionT expression); @@ -1318,33 +1212,6 @@ class ParserBase { return true; } - bool IsValidPattern(ExpressionT expression) { - return expression->IsObjectLiteral() || expression->IsArrayLiteral(); - } - - // Due to hoisting, the value of a 'var'-declared variable may actually change - // even if the code contains only the "initial" assignment, namely when that - // assignment occurs inside a loop. For example: - // - // let i = 10; - // do { var x = i } while (i--): - // - // As a simple and very conservative approximation of this, we explicitly mark - // as maybe-assigned any non-lexical variable whose initializing "declaration" - // does not syntactically occur in the function scope. (In the example above, - // it occurs in a block scope.) - // - // Note that non-lexical variables include temporaries, which may also get - // assigned inside a loop due to the various rewritings that the parser - // performs. - // - // This also handles marking of loop variables in for-in and for-of loops, - // as determined by declaration_kind. - // - static void MarkLoopVariableAsAssigned( - Scope* scope, Variable* var, - typename DeclarationDescriptor::Kind declaration_kind); - FunctionKind FunctionKindForImpl(bool is_method, ParseFunctionFlags flags) { static const FunctionKind kFunctionKinds[][2][2] = { { @@ -1422,106 +1289,64 @@ class ParserBase { return factory()->NewReturnStatement(expr, pos, end_pos); } - // Validation per ES6 object literals. - class ObjectLiteralChecker { - public: - explicit ObjectLiteralChecker(ParserBase* parser) - : parser_(parser), has_seen_proto_(false) {} + ModuleDescriptor* module() const { + return scope()->AsModuleScope()->module(); + } + Scope* scope() const { return scope_; } - void CheckDuplicateProto(Token::Value property); + // Stack of expression expression_scopes. + // The top of the stack is always pointed to by expression_scope(). + V8_INLINE ExpressionScope* expression_scope() const { + DCHECK_NOT_NULL(expression_scope_); + return expression_scope_; + } - private: - bool IsProto() const { - return this->scanner()->CurrentMatchesContextualEscaped( - Token::PROTO_UNDERSCORED); + class AcceptINScope final { + public: + AcceptINScope(ParserBase* parser, bool accept_IN) + : parser_(parser), previous_accept_IN_(parser->accept_IN_) { + parser_->accept_IN_ = accept_IN; } - ParserBase* parser() const { return parser_; } - Scanner* scanner() const { return parser_->scanner(); } + ~AcceptINScope() { parser_->accept_IN_ = previous_accept_IN_; } + private: ParserBase* parser_; - bool has_seen_proto_; + bool previous_accept_IN_; }; - // Validation per ES6 class literals. - class ClassLiteralChecker { + class ParameterParsingScope { public: - explicit ClassLiteralChecker(ParserBase* parser) - : parser_(parser), has_seen_constructor_(false) {} - - void CheckClassMethodName(Token::Value property, ParsePropertyKind type, - ParseFunctionFlags flags, bool is_static, - bool* ok); - void CheckClassFieldName(bool is_static, bool* ok); - - private: - bool IsConstructor() { - return this->scanner()->CurrentMatchesContextualEscaped( - Token::CONSTRUCTOR); - } - bool IsPrivateConstructor() { - return this->scanner()->CurrentMatchesContextualEscaped( - Token::PRIVATE_CONSTRUCTOR); - } - bool IsPrototype() { - return this->scanner()->CurrentMatchesContextualEscaped(Token::PROTOTYPE); + ParameterParsingScope(Impl* parser, FormalParametersT* parameters) + : parser_(parser), parent_parameters_(parser_->parameters_) { + parser_->parameters_ = parameters; } - ParserBase* parser() const { return parser_; } - Scanner* scanner() const { return parser_->scanner(); } + ~ParameterParsingScope() { parser_->parameters_ = parent_parameters_; } - ParserBase* parser_; - bool has_seen_constructor_; + private: + Impl* parser_; + FormalParametersT* parent_parameters_; }; - ModuleDescriptor* module() const { - return scope()->AsModuleScope()->module(); - } - Scope* scope() const { return scope_; } - - // Stack of expression classifiers. - // The top of the stack is always pointed to by classifier(). - V8_INLINE ExpressionClassifier* classifier() const { - DCHECK_NOT_NULL(classifier_); - return classifier_; - } - - // Accumulates the classifier that is on top of the stack (inner) to - // the one that is right below (outer) and pops the inner. - V8_INLINE void Accumulate(unsigned productions) { - DCHECK_NOT_NULL(classifier_); - ExpressionClassifier* previous = classifier_->previous(); - DCHECK_NOT_NULL(previous); - previous->Accumulate(classifier_, productions); - classifier_ = previous; - } + class FunctionBodyParsingScope { + public: + explicit FunctionBodyParsingScope(Impl* parser) + : parser_(parser), expression_scope_(parser_->expression_scope_) { + parser_->expression_scope_ = nullptr; + } - V8_INLINE void AccumulateNonBindingPatternErrors() { - this->Accumulate(ExpressionClassifier::AllProductions & - ~(ExpressionClassifier::BindingPatternProduction | - ExpressionClassifier::LetPatternProduction)); - } + ~FunctionBodyParsingScope() { + parser_->expression_scope_ = expression_scope_; + } - // Pops and discards the classifier that is on top of the stack - // without accumulating. - V8_INLINE void DiscardExpressionClassifier() { - DCHECK_NOT_NULL(classifier_); - classifier_->Discard(); - classifier_ = classifier_->previous(); - } + private: + Impl* parser_; + ExpressionScope* expression_scope_; + }; - // Accumulate errors that can be arbitrarily deep in an expression. - // These correspond to the ECMAScript spec's 'Contains' operation - // on productions. This includes: - // - // - YieldExpression is disallowed in arrow parameters in a generator. - // - AwaitExpression is disallowed in arrow parameters in an async function. - // - AwaitExpression is disallowed in async arrow parameters. - // - V8_INLINE void AccumulateFormalParameterContainmentErrors() { - Accumulate(ExpressionClassifier::FormalParameterInitializerProduction | - ExpressionClassifier::AsyncArrowFormalParametersProduction); - } + std::vector<void*>* pointer_buffer() { return &pointer_buffer_; } + std::vector<void*>* variable_buffer() { return &variable_buffer_; } // Parser base's protected field members. @@ -1543,22 +1368,60 @@ class ParserBase { private: Zone* zone_; - ExpressionClassifier* classifier_; + ExpressionScope* expression_scope_; - Scanner* scanner_; + std::vector<void*> pointer_buffer_; + std::vector<void*> variable_buffer_; - FunctionLiteral::EagerCompileHint default_eager_compile_hint_; + Scanner* scanner_; int function_literal_id_; int script_id_; + FunctionLiteral::EagerCompileHint default_eager_compile_hint_; + + // This struct is used to move information about the next arrow function from + // the place where the arrow head was parsed to where the body will be parsed. + // Nothing can be parsed between the head and the body, so it will be consumed + // immediately after it's produced. + // Preallocating the struct as part of the parser minimizes the cost of + // supporting arrow functions on non-arrow expressions. + struct NextArrowFunctionInfo { + Scanner::Location strict_parameter_error_location = + Scanner::Location::invalid(); + MessageTemplate strict_parameter_error_message = MessageTemplate::kNone; + DeclarationScope* scope = nullptr; + + bool HasInitialState() const { return scope == nullptr; } + + void Reset() { + scope = nullptr; + ClearStrictParameterError(); + DCHECK(HasInitialState()); + } + + // Tracks strict-mode parameter violations of sloppy-mode arrow heads in + // case the function ends up becoming strict mode. Only one global place to + // track this is necessary since arrow functions with none-simple parameters + // cannot become strict-mode later on. + void ClearStrictParameterError() { + strict_parameter_error_location = Scanner::Location::invalid(); + strict_parameter_error_message = MessageTemplate::kNone; + } + }; + + FormalParametersT* parameters_; + NextArrowFunctionInfo next_arrow_function_info_; + + bool accept_IN_ = true; + bool allow_natives_; - bool allow_harmony_do_expressions_; bool allow_harmony_public_fields_; bool allow_harmony_static_fields_; bool allow_harmony_dynamic_import_; bool allow_harmony_import_meta_; bool allow_harmony_private_fields_; + bool allow_harmony_private_methods_; bool allow_eval_cache_; }; @@ -1572,8 +1435,6 @@ ParserBase<Impl>::FunctionState::FunctionState( function_state_stack_(function_state_stack), outer_function_state_(*function_state_stack), scope_(scope), - destructuring_assignments_to_rewrite_(scope->zone()), - reported_errors_(16, scope->zone()), dont_optimize_reason_(BailoutReason::kNoReason), next_function_is_likely_called_(false), previous_function_was_likely_called_(false), @@ -1592,218 +1453,109 @@ ParserBase<Impl>::FunctionState::~FunctionState() { } template <typename Impl> -void ParserBase<Impl>::GetUnexpectedTokenMessage( - Token::Value token, MessageTemplate::Template* message, - Scanner::Location* location, const char** arg, - MessageTemplate::Template default_) { - *arg = nullptr; - switch (token) { - case Token::EOS: - *message = MessageTemplate::kUnexpectedEOS; - break; - case Token::SMI: - case Token::NUMBER: - case Token::BIGINT: - *message = MessageTemplate::kUnexpectedTokenNumber; - break; - case Token::STRING: - *message = MessageTemplate::kUnexpectedTokenString; - break; - case Token::PRIVATE_NAME: - case Token::IDENTIFIER: - *message = MessageTemplate::kUnexpectedTokenIdentifier; - break; - case Token::AWAIT: - case Token::ENUM: - *message = MessageTemplate::kUnexpectedReserved; - break; - case Token::LET: - case Token::STATIC: - case Token::YIELD: - case Token::FUTURE_STRICT_RESERVED_WORD: - *message = is_strict(language_mode()) - ? MessageTemplate::kUnexpectedStrictReserved - : MessageTemplate::kUnexpectedTokenIdentifier; - break; - case Token::TEMPLATE_SPAN: - case Token::TEMPLATE_TAIL: - *message = MessageTemplate::kUnexpectedTemplateString; - break; - case Token::ESCAPED_STRICT_RESERVED_WORD: - case Token::ESCAPED_KEYWORD: - *message = MessageTemplate::kInvalidEscapedReservedWord; - break; - case Token::ILLEGAL: - if (scanner()->has_error()) { - *message = scanner()->error(); - *location = scanner()->error_location(); - } else { - *message = MessageTemplate::kInvalidOrUnexpectedToken; - } - break; - case Token::REGEXP_LITERAL: - *message = MessageTemplate::kUnexpectedTokenRegExp; - break; - default: - const char* name = Token::String(token); - DCHECK_NOT_NULL(name); - *arg = name; - break; - } -} - -template <typename Impl> void ParserBase<Impl>::ReportUnexpectedToken(Token::Value token) { - return ReportUnexpectedTokenAt(scanner_->location(), token); -} - -template <typename Impl> -void ParserBase<Impl>::ReportUnexpectedTokenAt( - Scanner::Location source_location, Token::Value token, - MessageTemplate::Template message) { - const char* arg; - GetUnexpectedTokenMessage(token, &message, &source_location, &arg); - impl()->ReportMessageAt(source_location, message, arg); -} - -template <typename Impl> -typename ParserBase<Impl>::IdentifierT ParserBase<Impl>::ParseIdentifier( - AllowRestrictedIdentifiers allow_restricted_identifiers, bool* ok) { - ExpressionClassifier classifier(this); - auto result = ParseAndClassifyIdentifier(CHECK_OK_CUSTOM(NullIdentifier)); - - if (allow_restricted_identifiers == kDontAllowRestrictedIdentifiers) { - ValidateAssignmentPattern(CHECK_OK_CUSTOM(NullIdentifier)); - ValidateBindingPattern(CHECK_OK_CUSTOM(NullIdentifier)); - } - - return result; + return impl()->ReportUnexpectedTokenAt(scanner_->location(), token); } template <typename Impl> typename ParserBase<Impl>::IdentifierT -ParserBase<Impl>::ParseAndClassifyIdentifier(bool* ok) { - Token::Value next = Next(); +ParserBase<Impl>::ParseAndClassifyIdentifier(Token::Value next) { + DCHECK_EQ(scanner()->current_token(), next); STATIC_ASSERT(Token::IDENTIFIER + 1 == Token::ASYNC); - if (IsInRange(next, Token::IDENTIFIER, Token::ASYNC)) { + if (V8_LIKELY(IsInRange(next, Token::IDENTIFIER, Token::ASYNC))) { IdentifierT name = impl()->GetSymbol(); - - // When this function is used to read a formal parameter, we don't always - // know whether the function is going to be strict or sloppy. Indeed for - // arrow functions we don't always know that the identifier we are reading - // is actually a formal parameter. Therefore besides the errors that we - // must detect because we know we're in strict mode, we also record any - // error that we might make in the future once we know the language mode. - if (impl()->IsEvalOrArguments(name)) { - 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); - } + if (V8_UNLIKELY(impl()->IsArguments(name) && + scope()->ShouldBanArguments())) { + ReportMessage(MessageTemplate::kArgumentsDisallowedInInitializer); + return impl()->EmptyIdentifierString(); } + return name; + } - if (classifier()->duplicate_finder() != nullptr && - scanner()->IsDuplicateSymbol(classifier()->duplicate_finder(), - ast_value_factory())) { - classifier()->RecordDuplicateFormalParameterError(scanner()->location()); - } + if (!Token::IsValidIdentifier(next, language_mode(), is_generator(), + parsing_module_ || is_async_function())) { + ReportUnexpectedToken(next); + return impl()->EmptyIdentifierString(); + } - return name; - } else if (next == Token::AWAIT && !parsing_module_ && !is_async_function()) { - classifier()->RecordAsyncArrowFormalParametersError( + if (next == Token::AWAIT) { + expression_scope()->RecordAsyncArrowParametersError( scanner()->location(), MessageTemplate::kAwaitBindingIdentifier); return impl()->GetSymbol(); - } else if (is_sloppy(language_mode()) && - (Token::IsStrictReservedWord(next) || - (next == Token::YIELD && !is_generator()))) { - classifier()->RecordStrictModeFormalParameterError( - scanner()->location(), MessageTemplate::kUnexpectedStrictReserved); - if (scanner()->IsLet()) { - classifier()->RecordLetPatternError( - scanner()->location(), MessageTemplate::kLetInLexicalBinding); - } - return impl()->GetSymbol(); - } else { - ReportUnexpectedToken(next); - *ok = false; - return impl()->NullIdentifier(); } + + DCHECK(Token::IsStrictReservedWord(next)); + expression_scope()->RecordStrictModeParameterError( + scanner()->location(), MessageTemplate::kUnexpectedStrictReserved); + return impl()->GetSymbol(); } template <class Impl> -typename ParserBase<Impl>::IdentifierT -ParserBase<Impl>::ParseIdentifierOrStrictReservedWord( - FunctionKind function_kind, bool* is_strict_reserved, bool* is_await, - bool* ok) { +typename ParserBase<Impl>::IdentifierT ParserBase<Impl>::ParseIdentifier( + FunctionKind function_kind) { Token::Value next = Next(); - if (next == Token::IDENTIFIER || (next == Token::AWAIT && !parsing_module_ && - !IsAsyncFunction(function_kind)) || - next == Token::ASYNC) { - *is_strict_reserved = false; - *is_await = next == Token::AWAIT; - } else if (Token::IsStrictReservedWord(next) || - (next == Token::YIELD && !IsGeneratorFunction(function_kind))) { - *is_strict_reserved = true; - } else { + + if (!Token::IsValidIdentifier( + next, language_mode(), IsGeneratorFunction(function_kind), + parsing_module_ || IsAsyncFunction(function_kind))) { ReportUnexpectedToken(next); - *ok = false; - return impl()->NullIdentifier(); + return impl()->EmptyIdentifierString(); } return impl()->GetSymbol(); } template <typename Impl> -typename ParserBase<Impl>::IdentifierT ParserBase<Impl>::ParseIdentifierName( - bool* ok) { - Token::Value next = Next(); - if (!Token::IsAnyIdentifier(next) && next != Token::ESCAPED_KEYWORD && - !Token::IsKeyword(next)) { - ReportUnexpectedToken(next); - *ok = false; - return impl()->NullIdentifier(); +typename ParserBase<Impl>::IdentifierT +ParserBase<Impl>::ParseNonRestrictedIdentifier() { + IdentifierT result = ParseIdentifier(); + + if (is_strict(language_mode()) && + V8_UNLIKELY(impl()->IsEvalOrArguments(result))) { + impl()->ReportMessageAt(scanner()->location(), + MessageTemplate::kStrictEvalArguments); } - return impl()->GetSymbol(); + return result; +} + +template <typename Impl> +typename ParserBase<Impl>::IdentifierT ParserBase<Impl>::ParsePropertyName() { + Token::Value next = Next(); + if (V8_LIKELY(Token::IsPropertyName(next))) return impl()->GetSymbol(); + + ReportUnexpectedToken(next); + return impl()->EmptyIdentifierString(); } template <typename Impl> typename ParserBase<Impl>::ExpressionT -ParserBase<Impl>::ParseIdentifierNameOrPrivateName(bool* ok) { +ParserBase<Impl>::ParsePropertyOrPrivatePropertyName() { int pos = position(); IdentifierT name; ExpressionT key; - if (allow_harmony_private_fields() && peek() == Token::PRIVATE_NAME) { - Consume(Token::PRIVATE_NAME); + Token::Value next = Next(); + if (V8_LIKELY(Token::IsPropertyName(next))) { name = impl()->GetSymbol(); - auto key_proxy = - impl()->ExpressionFromIdentifier(name, pos, InferName::kNo); - key_proxy->set_is_private_field(); - key = key_proxy; - } else { - name = ParseIdentifierName(CHECK_OK); key = factory()->NewStringLiteral(name, pos); + } else if (allow_harmony_private_fields() && next == Token::PRIVATE_NAME) { + name = impl()->GetSymbol(); + key = impl()->ExpressionFromIdentifier(name, pos, InferName::kNo); + } else { + ReportUnexpectedToken(next); + return impl()->FailureExpression(); } impl()->PushLiteralName(name); return key; } template <typename Impl> -typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseRegExpLiteral( - bool* ok) { +typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseRegExpLiteral() { int pos = peek_position(); if (!scanner()->ScanRegExpPattern()) { Next(); ReportMessage(MessageTemplate::kUnterminatedRegExp); - *ok = false; - return impl()->NullExpression(); + return impl()->FailureExpression(); } IdentifierT js_pattern = impl()->GetNextSymbol(); @@ -1811,8 +1563,7 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseRegExpLiteral( if (flags.IsNothing()) { Next(); ReportMessage(MessageTemplate::kMalformedRegExpFlags); - *ok = false; - return impl()->NullExpression(); + return impl()->FailureExpression(); } int js_flags = flags.FromJust(); Next(); @@ -1820,8 +1571,7 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseRegExpLiteral( } template <typename Impl> -typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseBindingPattern( - bool* ok) { +typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseBindingPattern() { // Pattern :: // Identifier // ArrayLiteral @@ -1832,29 +1582,35 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseBindingPattern( 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(); + IdentifierT name = ParseAndClassifyIdentifier(Next()); + if (V8_UNLIKELY(is_strict(language_mode()) && + impl()->IsEvalOrArguments(name))) { + impl()->ReportMessageAt(scanner()->location(), + MessageTemplate::kStrictEvalArguments); + return impl()->FailureExpression(); } + return impl()->ExpressionFromIdentifier(name, beg_pos); + } + + CheckStackOverflow(); + + if (token == Token::LBRACK) { + result = ParseArrayLiteral(); + } else if (token == Token::LBRACE) { + result = ParseObjectLiteral(); + } else { + ReportUnexpectedToken(Next()); + return impl()->FailureExpression(); } - ValidateBindingPattern(CHECK_OK); return result; } template <typename Impl> -typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParsePrimaryExpression( - bool* is_async, bool* ok) { +typename ParserBase<Impl>::ExpressionT +ParserBase<Impl>::ParsePrimaryExpression() { + CheckStackOverflow(); + // PrimaryExpression :: // 'this' // 'null' @@ -1874,136 +1630,115 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParsePrimaryExpression( int beg_pos = peek_position(); Token::Value token = peek(); - switch (token) { - case Token::THIS: { - BindingPatternUnexpectedToken(); - Consume(Token::THIS); - return impl()->ThisExpression(beg_pos); + + if (Token::IsAnyIdentifier(token)) { + Consume(token); + + FunctionKind kind = FunctionKind::kArrowFunction; + + if (V8_UNLIKELY(token == Token::ASYNC && + !scanner()->HasLineTerminatorBeforeNext())) { + // async function ... + if (peek() == Token::FUNCTION) return ParseAsyncFunctionLiteral(); + + // async Identifier => ... + if (peek_any_identifier() && PeekAhead() == Token::ARROW) { + token = Next(); + beg_pos = position(); + kind = FunctionKind::kAsyncArrowFunction; + } } - case Token::NULL_LITERAL: - case Token::TRUE_LITERAL: - case Token::FALSE_LITERAL: - case Token::SMI: - case Token::NUMBER: - 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); + if (V8_UNLIKELY(peek() == Token::ARROW)) { + ArrowHeadParsingScope parsing_scope(impl(), kind); + IdentifierT name = ParseAndClassifyIdentifier(token); + ClassifyParameter(name, beg_pos, end_position()); + ExpressionT result = + impl()->ExpressionFromIdentifier(name, beg_pos, InferName::kNo); + next_arrow_function_info_.scope = parsing_scope.ValidateAndCreateScope(); + return result; } - case Token::ASYNC: - if (!scanner()->HasLineTerminatorAfterNext() && - PeekAhead() == Token::FUNCTION) { - BindingPatternUnexpectedToken(); - Consume(Token::ASYNC); - return ParseAsyncFunctionLiteral(ok); - } - // CoverCallExpressionAndAsyncArrowHead - *is_async = true; - V8_FALLTHROUGH; - case Token::IDENTIFIER: - case Token::LET: - case Token::STATIC: - case Token::YIELD: - case Token::AWAIT: - 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); + IdentifierT name = ParseAndClassifyIdentifier(token); + return impl()->ExpressionFromIdentifier(name, beg_pos); + } + + if (Token::IsLiteral(token)) { + return impl()->ExpressionFromLiteral(Next(), beg_pos); + } + + switch (token) { + case Token::THIS: { + Consume(Token::THIS); + return impl()->ThisExpression(beg_pos); } case Token::ASSIGN_DIV: case Token::DIV: - classifier()->RecordBindingPatternError( - scanner()->peek_location(), MessageTemplate::kUnexpectedTokenRegExp); - return ParseRegExpLiteral(ok); + return ParseRegExpLiteral(); case Token::LBRACK: - return ParseArrayLiteral(ok); + return ParseArrayLiteral(); case Token::LBRACE: - return ParseObjectLiteral(ok); + return ParseObjectLiteral(); case Token::LPAREN: { - // Arrow function formal parameters are either a single identifier or a - // list of BindingPattern productions enclosed in parentheses. - // Parentheses are not valid on the LHS of a BindingPattern, so we use - // the is_valid_binding_pattern() check to detect multiple levels of - // parenthesization. - bool pattern_error = !classifier()->is_valid_binding_pattern(); - classifier()->RecordPatternError(scanner()->peek_location(), - MessageTemplate::kUnexpectedToken, - Token::String(Token::LPAREN)); - if (pattern_error) ArrowFormalParametersUnexpectedToken(); Consume(Token::LPAREN); if (Check(Token::RPAREN)) { - // ()=>x. The continuation that looks for the => is in - // ParseAssignmentExpression. - classifier()->RecordExpressionError(scanner()->location(), - MessageTemplate::kUnexpectedToken, - Token::String(Token::RPAREN)); + // ()=>x. The continuation that consumes the => is in + // ParseAssignmentExpressionCoverGrammar. + if (peek() != Token::ARROW) ReportUnexpectedToken(Token::RPAREN); + next_arrow_function_info_.scope = + NewFunctionScope(FunctionKind::kArrowFunction); return factory()->NewEmptyParentheses(beg_pos); } + Scope::Snapshot scope_snapshot(scope()); + ArrowHeadParsingScope maybe_arrow(impl(), FunctionKind::kArrowFunction); // Heuristically try to detect immediately called functions before // seeing the call parentheses. if (peek() == Token::FUNCTION || (peek() == Token::ASYNC && PeekAhead() == Token::FUNCTION)) { function_state_->set_next_function_is_likely_called(); } - ExpressionT expr = ParseExpressionCoverGrammar(true, CHECK_OK); - Expect(Token::RPAREN, ok); + AcceptINScope scope(this, true); + ExpressionT expr = ParseExpressionCoverGrammar(); + expr->mark_parenthesized(); + Expect(Token::RPAREN); + + if (peek() == Token::ARROW) { + next_arrow_function_info_.scope = maybe_arrow.ValidateAndCreateScope(); + scope_snapshot.Reparent(next_arrow_function_info_.scope); + } else { + maybe_arrow.ValidateExpression(); + } + return expr; } case Token::CLASS: { - BindingPatternUnexpectedToken(); Consume(Token::CLASS); int class_token_pos = position(); IdentifierT name = impl()->NullIdentifier(); bool is_strict_reserved_name = false; Scanner::Location class_name_location = Scanner::Location::invalid(); if (peek_any_identifier()) { - bool is_await = false; - name = ParseIdentifierOrStrictReservedWord(&is_strict_reserved_name, - &is_await, CHECK_OK); + name = ParseAndClassifyIdentifier(Next()); class_name_location = scanner()->location(); - if (is_await) { - classifier()->RecordAsyncArrowFormalParametersError( - scanner()->location(), MessageTemplate::kAwaitBindingIdentifier); - } + is_strict_reserved_name = + Token::IsStrictReservedWord(scanner()->current_token()); } return ParseClassLiteral(name, class_name_location, - is_strict_reserved_name, class_token_pos, ok); + is_strict_reserved_name, class_token_pos); } case Token::TEMPLATE_SPAN: case Token::TEMPLATE_TAIL: - BindingPatternUnexpectedToken(); - return ParseTemplateLiteral(impl()->NullExpression(), beg_pos, false, ok); + return ParseTemplateLiteral(impl()->NullExpression(), beg_pos, false); case Token::MOD: if (allow_natives() || extension_ != nullptr) { - BindingPatternUnexpectedToken(); - return ParseV8Intrinsic(ok); - } - break; - - case Token::DO: - if (allow_harmony_do_expressions()) { - BindingPatternUnexpectedToken(); - return ParseDoExpression(ok); + return ParseV8Intrinsic(); } break; @@ -2012,72 +1747,49 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParsePrimaryExpression( } ReportUnexpectedToken(Next()); - *ok = false; - return impl()->NullExpression(); + return impl()->FailureExpression(); } template <typename Impl> -typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseExpression( - bool* ok) { - ExpressionClassifier classifier(this); - ExpressionT result = ParseExpressionCoverGrammar(true, CHECK_OK); - ValidateExpression(ok); +typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseExpression() { + ExpressionParsingScope expression_scope(impl()); + AcceptINScope scope(this, true); + ExpressionT result = ParseExpressionCoverGrammar(); + expression_scope.ValidateExpression(); return result; } template <typename Impl> typename ParserBase<Impl>::ExpressionT -ParserBase<Impl>::ParseExpressionCoverGrammar(bool accept_IN, bool* ok) { +ParserBase<Impl>::ParseAssignmentExpression() { + ExpressionParsingScope expression_scope(impl()); + ExpressionT result = ParseAssignmentExpressionCoverGrammar(); + expression_scope.ValidateExpression(); + return result; +} + +template <typename Impl> +typename ParserBase<Impl>::ExpressionT +ParserBase<Impl>::ParseExpressionCoverGrammar() { // Expression :: // AssignmentExpression // Expression ',' AssignmentExpression - ExpressionT result = impl()->NullExpression(); + ExpressionListT list(pointer_buffer()); + ExpressionT expression; + AccumulationScope accumulation_scope(expression_scope()); while (true) { - int comma_pos = position(); - ExpressionClassifier binding_classifier(this); - ExpressionT right; - if (Check(Token::ELLIPSIS)) { - // 'x, y, ...z' in CoverParenthesizedExpressionAndArrowParameterList only - // as the formal parameters of'(x, y, ...z) => foo', and is not itself a - // valid expression. - classifier()->RecordExpressionError(scanner()->location(), - MessageTemplate::kUnexpectedToken, - Token::String(Token::ELLIPSIS)); - int ellipsis_pos = position(); - int pattern_pos = peek_position(); - ExpressionT pattern = ParseBindingPattern(CHECK_OK); - if (peek() == Token::ASSIGN) { - ReportMessage(MessageTemplate::kRestDefaultInitializer); - *ok = false; - return result; - } - right = factory()->NewSpread(pattern, ellipsis_pos, pattern_pos); - } else { - right = ParseAssignmentExpression(accept_IN, CHECK_OK); - } - // No need to accumulate binding pattern-related errors, since - // an Expression can't be a binding pattern anyway. - AccumulateNonBindingPatternErrors(); - if (!impl()->IsIdentifier(right)) classifier()->RecordNonSimpleParameter(); - if (impl()->IsNull(result)) { - // First time through the loop. - result = right; - } else if (impl()->CollapseNaryExpression(&result, right, Token::COMMA, - comma_pos, - SourceRange::Empty())) { - // Do nothing, "result" is already updated. - } else { - result = - factory()->NewBinaryOperation(Token::COMMA, result, right, comma_pos); + if (V8_UNLIKELY(peek() == Token::ELLIPSIS)) { + return ParseArrowParametersWithRest(&list, &accumulation_scope); } - if (!Check(Token::COMMA)) break; + int expr_pos = peek_position(); + expression = ParseAssignmentExpressionCoverGrammar(); - if (right->IsSpread()) { - classifier()->RecordArrowFormalParametersError( - scanner()->location(), MessageTemplate::kParamAfterRest); - } + ClassifyArrowParameter(&accumulation_scope, expr_pos, expression); + list.Add(expression); + + if (!Check(Token::COMMA)) break; if (peek() == Token::RPAREN && PeekAhead() == Token::ARROW) { // a trailing comma is allowed at the end of an arrow parameter list @@ -2092,19 +1804,64 @@ ParserBase<Impl>::ParseExpressionCoverGrammar(bool accept_IN, bool* ok) { } } - return result; + // Return the single element if the list is empty. We need to do this because + // callers of this function care about the type of the result if there was + // only a single assignment expression. The preparser would lose this + // information otherwise. + if (list.length() == 1) return expression; + return impl()->ExpressionListToExpression(list); } template <typename Impl> -typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseArrayLiteral( - bool* ok) { +typename ParserBase<Impl>::ExpressionT +ParserBase<Impl>::ParseArrowParametersWithRest( + typename ParserBase<Impl>::ExpressionListT* list, + AccumulationScope* accumulation_scope) { + Consume(Token::ELLIPSIS); + + Scanner::Location ellipsis = scanner()->location(); + int pattern_pos = peek_position(); + ExpressionT pattern = ParseBindingPattern(); + ClassifyArrowParameter(accumulation_scope, pattern_pos, pattern); + + expression_scope()->RecordNonSimpleParameter(); + + if (V8_UNLIKELY(peek() == Token::ASSIGN)) { + ReportMessage(MessageTemplate::kRestDefaultInitializer); + return impl()->FailureExpression(); + } + + ExpressionT spread = + factory()->NewSpread(pattern, ellipsis.beg_pos, pattern_pos); + if (V8_UNLIKELY(peek() == Token::COMMA)) { + ReportMessage(MessageTemplate::kParamAfterRest); + return impl()->FailureExpression(); + } + + // 'x, y, ...z' in CoverParenthesizedExpressionAndArrowParameterList only + // as the formal parameters of'(x, y, ...z) => foo', and is not itself a + // valid expression. + if (peek() != Token::RPAREN || PeekAhead() != Token::ARROW) { + impl()->ReportUnexpectedTokenAt(ellipsis, Token::ELLIPSIS); + return impl()->FailureExpression(); + } + + list->Add(spread); + return impl()->ExpressionListToExpression(*list); +} + +template <typename Impl> +typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseArrayLiteral() { // ArrayLiteral :: // '[' Expression? (',' Expression?)* ']' int pos = peek_position(); - ExpressionListT values = impl()->NewExpressionList(4); + ExpressionListT values(pointer_buffer()); int first_spread_index = -1; Consume(Token::LBRACK); + + AccumulationScope accumulation_scope(expression_scope()); + while (!Check(Token::RBRACK)) { ExpressionT elem; if (peek() == Token::COMMA) { @@ -2112,115 +1869,80 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseArrayLiteral( } else if (Check(Token::ELLIPSIS)) { int start_pos = position(); int expr_pos = peek_position(); - ExpressionT argument = ParseAssignmentExpression(true, CHECK_OK); + AcceptINScope scope(this, true); + ExpressionT argument = + ParsePossibleDestructuringSubPattern(&accumulation_scope); elem = factory()->NewSpread(argument, start_pos, expr_pos); if (first_spread_index < 0) { - first_spread_index = values->length(); + first_spread_index = values.length(); } if (argument->IsAssignment()) { - classifier()->RecordPatternError( + expression_scope()->RecordPatternError( Scanner::Location(start_pos, end_position()), MessageTemplate::kInvalidDestructuringTarget); - } else { - CheckDestructuringElement(argument, start_pos, end_position()); } if (peek() == Token::COMMA) { - classifier()->RecordPatternError( + expression_scope()->RecordPatternError( Scanner::Location(start_pos, end_position()), MessageTemplate::kElementAfterRest); } } else { - int beg_pos = peek_position(); - elem = ParseAssignmentExpression(true, CHECK_OK); - CheckDestructuringElement(elem, beg_pos, end_position()); + AcceptINScope scope(this, true); + elem = ParsePossibleDestructuringSubPattern(&accumulation_scope); } - values->Add(elem, zone_); + values.Add(elem); if (peek() != Token::RBRACK) { - Expect(Token::COMMA, CHECK_OK); + Expect(Token::COMMA); + if (elem->IsFailureExpression()) return elem; } } return factory()->NewArrayLiteral(values, first_spread_index, pos); } -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 = ParsePropertyKind::kValue; - return true; - case Token::COMMA: - case Token::RBRACE: - case Token::ASSIGN: - *kind = ParsePropertyKind::kShorthand; - return true; - case Token::LPAREN: - *kind = ParsePropertyKind::kMethod; - return true; - case Token::MUL: - case Token::SEMICOLON: - *kind = ParsePropertyKind::kClassField; - return true; - case Token::PRIVATE_NAME: - *kind = ParsePropertyKind::kClassField; - return true; - default: - break; - } - return false; -} - -inline bool ParseAsAccessor(Token::Value token, Token::Value contextual_token, - ParsePropertyKind* kind) { - if (ParsePropertyKindFromToken(token, kind)) return false; - - if (contextual_token == Token::GET) { - *kind = ParsePropertyKind::kAccessorGetter; - } else if (contextual_token == Token::SET) { - *kind = ParsePropertyKind::kAccessorSetter; - } else { - return false; - } - - return true; -} - 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); +typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseProperty( + ParsePropertyInfo* prop_info) { + DCHECK_EQ(prop_info->kind, ParsePropertyKind::kNotSet); + DCHECK_EQ(prop_info->function_flags, ParseFunctionFlag::kIsNormal); + DCHECK(!prop_info->is_computed_name); if (Check(Token::ASYNC)) { Token::Value token = peek(); - if ((token != Token::MUL && ParsePropertyKindFromToken(token, kind)) || + if ((token != Token::MUL && prop_info->ParsePropertyKindFromToken(token)) || scanner()->HasLineTerminatorBeforeNext()) { - *name = impl()->GetSymbol(); - impl()->PushLiteralName(*name); - return factory()->NewStringLiteral(*name, position()); + prop_info->name = impl()->GetSymbol(); + impl()->PushLiteralName(prop_info->name); + return factory()->NewStringLiteral(prop_info->name, position()); } - *flags = ParseFunctionFlag::kIsAsync; - *kind = ParsePropertyKind::kMethod; + prop_info->function_flags = ParseFunctionFlag::kIsAsync; + prop_info->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()); + prop_info->function_flags |= ParseFunctionFlag::kIsGenerator; + prop_info->kind = ParsePropertyKind::kMethod; + } + + if (prop_info->kind == ParsePropertyKind::kNotSet && + Check(Token::IDENTIFIER)) { + IdentifierT symbol = impl()->GetSymbol(); + if (!prop_info->ParsePropertyKindFromToken(peek())) { + if (impl()->IdentifierEquals(symbol, ast_value_factory()->get_string())) { + prop_info->kind = ParsePropertyKind::kAccessorGetter; + } else if (impl()->IdentifierEquals(symbol, + ast_value_factory()->set_string())) { + prop_info->kind = ParsePropertyKind::kAccessorSetter; + } + } + if (!IsAccessor(prop_info->kind)) { + prop_info->name = symbol; + impl()->PushLiteralName(prop_info->name); + return factory()->NewStringLiteral(prop_info->name, position()); + } } int pos = peek_position(); @@ -2237,10 +1959,27 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParsePropertyName( bool is_array_index; uint32_t index; switch (peek()) { + case Token::PRIVATE_NAME: + prop_info->is_private = true; + is_array_index = false; + Consume(Token::PRIVATE_NAME); + if (prop_info->kind == ParsePropertyKind::kNotSet) { + prop_info->ParsePropertyKindFromToken(peek()); + } + prop_info->name = impl()->GetSymbol(); + if (prop_info->position == PropertyPosition::kObjectLiteral || + (!allow_harmony_private_methods() && + (IsAccessor(prop_info->kind) || + prop_info->kind == ParsePropertyKind::kMethod))) { + ReportUnexpectedToken(Next()); + return impl()->FailureExpression(); + } + break; + case Token::STRING: Consume(Token::STRING); - *name = impl()->GetSymbol(); - is_array_index = impl()->IsArrayIndex(*name, &index); + prop_info->name = impl()->GetSymbol(); + is_array_index = impl()->IsArrayIndex(prop_info->name, &index); break; case Token::SMI: @@ -2248,82 +1987,76 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParsePropertyName( index = scanner()->smi_value(); is_array_index = true; // Token::SMI were scanned from their canonical representation. - *name = impl()->GetSymbol(); + prop_info->name = impl()->GetSymbol(); break; case Token::NUMBER: { Consume(Token::NUMBER); - *name = impl()->GetNumberAsSymbol(); - is_array_index = impl()->IsArrayIndex(*name, &index); + prop_info->name = impl()->GetNumberAsSymbol(); + is_array_index = impl()->IsArrayIndex(prop_info->name, &index); break; } case Token::LBRACK: { - *name = impl()->NullIdentifier(); - *is_computed_name = true; + prop_info->name = impl()->NullIdentifier(); + prop_info->is_computed_name = true; Consume(Token::LBRACK); - ExpressionClassifier computed_name_classifier(this); - ExpressionT expression = ParseAssignmentExpression(true, CHECK_OK); - ValidateExpression(CHECK_OK); - AccumulateFormalParameterContainmentErrors(); - Expect(Token::RBRACK, CHECK_OK); - if (*kind == ParsePropertyKind::kNotSet) { - ParsePropertyKindFromToken(peek(), kind); + AcceptINScope scope(this, true); + ExpressionT expression = ParseAssignmentExpression(); + Expect(Token::RBRACK); + if (prop_info->kind == ParsePropertyKind::kNotSet) { + prop_info->ParsePropertyKindFromToken(peek()); } return expression; } case Token::ELLIPSIS: - if (*kind == ParsePropertyKind::kNotSet) { - *name = impl()->NullIdentifier(); + if (prop_info->kind == ParsePropertyKind::kNotSet) { + prop_info->name = impl()->NullIdentifier(); Consume(Token::ELLIPSIS); - ExpressionT expression = ParseAssignmentExpression(true, CHECK_OK); - *kind = ParsePropertyKind::kSpread; - - if (!impl()->IsIdentifier(expression)) { - classifier()->RecordBindingPatternError( - scanner()->location(), + AcceptINScope scope(this, true); + int start_pos = peek_position(); + ExpressionT expression = + ParsePossibleDestructuringSubPattern(prop_info->accumulation_scope); + prop_info->kind = ParsePropertyKind::kSpread; + + if (!IsValidReferenceExpression(expression)) { + expression_scope()->RecordDeclarationError( + Scanner::Location(start_pos, end_position()), MessageTemplate::kInvalidRestBindingPattern); - } - - if (!expression->IsValidReferenceExpression()) { - classifier()->RecordAssignmentPatternError( - scanner()->location(), + expression_scope()->RecordPatternError( + Scanner::Location(start_pos, end_position()), MessageTemplate::kInvalidRestAssignmentPattern); } if (peek() != Token::RBRACE) { - classifier()->RecordPatternError(scanner()->location(), - MessageTemplate::kElementAfterRest); + expression_scope()->RecordPatternError( + scanner()->location(), MessageTemplate::kElementAfterRest); } return expression; } V8_FALLTHROUGH; default: - *name = ParseIdentifierName(CHECK_OK); + prop_info->name = ParsePropertyName(); is_array_index = false; break; } - if (*kind == ParsePropertyKind::kNotSet) { - ParsePropertyKindFromToken(peek(), kind); + if (prop_info->kind == ParsePropertyKind::kNotSet) { + prop_info->ParsePropertyKindFromToken(peek()); } - impl()->PushLiteralName(*name); + impl()->PushLiteralName(prop_info->name); return is_array_index ? factory()->NewNumberLiteral(index, pos) - : factory()->NewStringLiteral(*name, pos); + : factory()->NewStringLiteral(prop_info->name, pos); } template <typename Impl> typename ParserBase<Impl>::ClassLiteralPropertyT -ParserBase<Impl>::ParseClassPropertyDefinition( - ClassLiteralChecker* checker, ClassInfo* class_info, IdentifierT* name, - bool has_extends, bool* is_computed_name, - ClassLiteralProperty::Kind* property_kind, bool* is_static, bool* ok) { +ParserBase<Impl>::ParseClassPropertyDefinition(ClassInfo* class_info, + ParsePropertyInfo* prop_info, + bool has_extends) { DCHECK_NOT_NULL(class_info); - ParseFunctionFlags function_flags = ParseFunctionFlag::kIsNormal; - *is_static = false; - *property_kind = ClassLiteralProperty::METHOD; - ParsePropertyKind kind = ParsePropertyKind::kNotSet; + DCHECK_EQ(prop_info->position, PropertyPosition::kClassLiteral); Token::Value name_token = peek(); DCHECK_IMPLIES(name_token == Token::PRIVATE_NAME, @@ -2331,48 +2064,39 @@ ParserBase<Impl>::ParseClassPropertyDefinition( 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 = ParsePropertyKind::kMethod; - *name = impl()->GetSymbol(); // TODO(bakkot) specialize on 'static' - name_expression = factory()->NewStringLiteral(*name, position()); + prop_info->kind = ParsePropertyKind::kMethod; + // TODO(bakkot) specialize on 'static' + prop_info->name = impl()->GetSymbol(); + name_expression = + factory()->NewStringLiteral(prop_info->name, position()); } else if (peek() == Token::ASSIGN || peek() == Token::SEMICOLON || peek() == Token::RBRACE) { - *name = impl()->GetSymbol(); // TODO(bakkot) specialize on 'static' - name_expression = factory()->NewStringLiteral(*name, position()); - } else if (peek() == Token::PRIVATE_NAME) { - DCHECK(allow_harmony_private_fields()); - // TODO(gsathya): Make a better error message for this. - ReportUnexpectedToken(Next()); - *ok = false; - return impl()->NullLiteralProperty(); - } else { - *is_static = true; + // TODO(bakkot) specialize on 'static' + prop_info->name = impl()->GetSymbol(); name_expression = - ParsePropertyName(name, &kind, &function_flags, is_computed_name, - CHECK_OK_CUSTOM(NullLiteralProperty)); + factory()->NewStringLiteral(prop_info->name, position()); + } else { + prop_info->is_static = true; + name_expression = ParseProperty(prop_info); } - } 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, &function_flags, is_computed_name, - CHECK_OK_CUSTOM(NullLiteralProperty)); + name_expression = ParseProperty(prop_info); } - if (!class_info->has_name_static_property && *is_static && - impl()->IsName(*name)) { + if (!class_info->has_name_static_property && prop_info->is_static && + impl()->IsName(prop_info->name)) { class_info->has_name_static_property = true; } - switch (kind) { + switch (prop_info->kind) { + case ParsePropertyKind::kAssign: case ParsePropertyKind::kClassField: + case ParsePropertyKind::kShorthandOrClassField: 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 @@ -2381,34 +2105,33 @@ ParserBase<Impl>::ParseClassPropertyDefinition( // 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 - : ClassLiteralProperty::PUBLIC_FIELD; - if (*is_static && !allow_harmony_static_fields()) { + prop_info->kind = ParsePropertyKind::kClassField; + DCHECK_IMPLIES(prop_info->is_computed_name, !prop_info->is_private); + + if (prop_info->is_static && !allow_harmony_static_fields()) { ReportUnexpectedToken(Next()); - *ok = false; return impl()->NullLiteralProperty(); } - if (!*is_computed_name) { - checker->CheckClassFieldName(*is_static, - CHECK_OK_CUSTOM(NullLiteralProperty)); + + if (!prop_info->is_computed_name) { + CheckClassFieldName(prop_info->name, prop_info->is_static); } - ExpressionT initializer = - ParseClassFieldInitializer(class_info, property_beg_pos, *is_static, - CHECK_OK_CUSTOM(NullLiteralProperty)); - ExpectSemicolon(CHECK_OK_CUSTOM(NullLiteralProperty)); + + ExpressionT initializer = ParseMemberInitializer( + class_info, property_beg_pos, prop_info->is_static); + ExpectSemicolon(); + ClassLiteralPropertyT result = factory()->NewClassLiteralProperty( - name_expression, initializer, *property_kind, *is_static, - *is_computed_name); - impl()->SetFunctionNameFromPropertyName(result, *name); + name_expression, initializer, ClassLiteralProperty::FIELD, + prop_info->is_static, prop_info->is_computed_name, + prop_info->is_private); + impl()->SetFunctionNameFromPropertyName(result, prop_info->name); + return result; } else { ReportUnexpectedToken(Next()); - *ok = false; return impl()->NullLiteralProperty(); } @@ -2421,89 +2144,89 @@ ParserBase<Impl>::ParseClassPropertyDefinition( // async '*' PropertyName '(' StrictFormalParameters ')' // '{' FunctionBody '}' - if (!*is_computed_name) { - checker->CheckClassMethodName(name_token, ParsePropertyKind::kMethod, - function_flags, *is_static, - CHECK_OK_CUSTOM(NullLiteralProperty)); + if (!prop_info->is_computed_name) { + CheckClassMethodName(prop_info->name, ParsePropertyKind::kMethod, + prop_info->function_flags, prop_info->is_static, + &class_info->has_seen_constructor); } - FunctionKind kind = MethodKindFor(function_flags); + FunctionKind kind = MethodKindFor(prop_info->function_flags); - if (!*is_static && impl()->IsConstructor(*name)) { + if (!prop_info->is_static && impl()->IsConstructor(prop_info->name)) { class_info->has_seen_constructor = true; kind = has_extends ? FunctionKind::kDerivedConstructor : FunctionKind::kBaseConstructor; } ExpressionT value = impl()->ParseFunctionLiteral( - *name, scanner()->location(), kSkipFunctionNameCheck, kind, + prop_info->name, scanner()->location(), kSkipFunctionNameCheck, kind, name_token_position, FunctionLiteral::kAccessorOrMethod, - language_mode(), nullptr, CHECK_OK_CUSTOM(NullLiteralProperty)); + language_mode(), nullptr); - *property_kind = ClassLiteralProperty::METHOD; ClassLiteralPropertyT result = factory()->NewClassLiteralProperty( - name_expression, value, *property_kind, *is_static, - *is_computed_name); - impl()->SetFunctionNameFromPropertyName(result, *name); + name_expression, value, ClassLiteralProperty::METHOD, + prop_info->is_static, prop_info->is_computed_name, + prop_info->is_private); + impl()->SetFunctionNameFromPropertyName(result, prop_info->name); return result; } case ParsePropertyKind::kAccessorGetter: case ParsePropertyKind::kAccessorSetter: { - DCHECK_EQ(function_flags, ParseFunctionFlag::kIsNormal); - bool is_get = kind == ParsePropertyKind::kAccessorGetter; + DCHECK_EQ(prop_info->function_flags, ParseFunctionFlag::kIsNormal); + bool is_get = prop_info->kind == ParsePropertyKind::kAccessorGetter; - if (!*is_computed_name) { - checker->CheckClassMethodName(name_token, kind, - ParseFunctionFlag::kIsNormal, *is_static, - CHECK_OK_CUSTOM(NullLiteralProperty)); + if (!prop_info->is_computed_name) { + CheckClassMethodName(prop_info->name, prop_info->kind, + ParseFunctionFlag::kIsNormal, prop_info->is_static, + &class_info->has_seen_constructor); // Make sure the name expression is a string since we need a Name for // Runtime_DefineAccessorPropertyUnchecked and since we can determine // this statically we can skip the extra runtime check. - name_expression = - factory()->NewStringLiteral(*name, name_expression->position()); + name_expression = factory()->NewStringLiteral( + prop_info->name, name_expression->position()); } FunctionKind kind = is_get ? FunctionKind::kGetterFunction : FunctionKind::kSetterFunction; FunctionLiteralT value = impl()->ParseFunctionLiteral( - *name, scanner()->location(), kSkipFunctionNameCheck, kind, + prop_info->name, scanner()->location(), kSkipFunctionNameCheck, kind, name_token_position, FunctionLiteral::kAccessorOrMethod, - language_mode(), nullptr, CHECK_OK_CUSTOM(NullLiteralProperty)); + language_mode(), nullptr); - *property_kind = + ClassLiteralProperty::Kind property_kind = is_get ? ClassLiteralProperty::GETTER : ClassLiteralProperty::SETTER; ClassLiteralPropertyT result = factory()->NewClassLiteralProperty( - name_expression, value, *property_kind, *is_static, - *is_computed_name); + name_expression, value, property_kind, prop_info->is_static, + prop_info->is_computed_name, prop_info->is_private); const AstRawString* prefix = is_get ? ast_value_factory()->get_space_string() : ast_value_factory()->set_space_string(); - impl()->SetFunctionNameFromPropertyName(result, *name, prefix); + impl()->SetFunctionNameFromPropertyName(result, prop_info->name, prefix); return result; } + case ParsePropertyKind::kValue: + case ParsePropertyKind::kShorthand: case ParsePropertyKind::kSpread: - ReportUnexpectedTokenAt( + impl()->ReportUnexpectedTokenAt( Scanner::Location(name_token_position, name_expression->position()), name_token); - *ok = false; return impl()->NullLiteralProperty(); } UNREACHABLE(); } template <typename Impl> -typename ParserBase<Impl>::ExpressionT -ParserBase<Impl>::ParseClassFieldInitializer(ClassInfo* class_info, int beg_pos, - bool is_static, bool* ok) { - DeclarationScope* initializer_scope = is_static - ? class_info->static_fields_scope - : class_info->instance_fields_scope; +typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseMemberInitializer( + ClassInfo* class_info, int beg_pos, bool is_static) { + DeclarationScope* initializer_scope = + is_static ? class_info->static_fields_scope + : class_info->instance_members_scope; if (initializer_scope == nullptr) { initializer_scope = - NewFunctionScope(FunctionKind::kClassFieldsInitializerFunction); + NewFunctionScope(FunctionKind::kClassMembersInitializerFunction); // TODO(gsathya): Make scopes be non contiguous. initializer_scope->set_start_position(beg_pos); initializer_scope->SetLanguageMode(LanguageMode::kStrict); @@ -2513,11 +2236,9 @@ ParserBase<Impl>::ParseClassFieldInitializer(ClassInfo* class_info, int beg_pos, if (Check(Token::ASSIGN)) { FunctionState initializer_state(&function_state_, &scope_, initializer_scope); - ExpressionClassifier expression_classifier(this); - initializer = - ParseAssignmentExpression(true, CHECK_OK_CUSTOM(NullExpression)); - ValidateExpression(CHECK_OK_CUSTOM(NullExpression)); + AcceptINScope scope(this, true); + initializer = ParseAssignmentExpression(); } else { initializer = factory()->NewUndefinedLiteral(kNoSourcePosition); } @@ -2527,8 +2248,8 @@ ParserBase<Impl>::ParseClassFieldInitializer(ClassInfo* class_info, int beg_pos, class_info->static_fields_scope = initializer_scope; class_info->has_static_class_fields = true; } else { - class_info->instance_fields_scope = initializer_scope; - class_info->has_instance_class_fields = true; + class_info->instance_members_scope = initializer_scope; + class_info->has_instance_members = true; } return initializer; @@ -2536,30 +2257,25 @@ ParserBase<Impl>::ParseClassFieldInitializer(ClassInfo* class_info, int beg_pos, template <typename Impl> typename ParserBase<Impl>::ObjectLiteralPropertyT -ParserBase<Impl>::ParseObjectPropertyDefinition(ObjectLiteralChecker* checker, - bool* is_computed_name, - bool* is_rest_property, - bool* ok) { - ParseFunctionFlags function_flags = ParseFunctionFlag::kIsNormal; - ParsePropertyKind kind = ParsePropertyKind::kNotSet; - - IdentifierT name = impl()->NullIdentifier(); +ParserBase<Impl>::ParseObjectPropertyDefinition(ParsePropertyInfo* prop_info, + bool* has_seen_proto) { + DCHECK_EQ(prop_info->position, PropertyPosition::kObjectLiteral); Token::Value name_token = peek(); - int next_beg_pos = peek_position(); - int next_end_pos = peek_end_position(); + Scanner::Location next_loc = scanner()->peek_location(); - ExpressionT name_expression = - ParsePropertyName(&name, &kind, &function_flags, is_computed_name, - CHECK_OK_CUSTOM(NullLiteralProperty)); + ExpressionT name_expression = ParseProperty(prop_info); + IdentifierT name = prop_info->name; + ParseFunctionFlags function_flags = prop_info->function_flags; + ParsePropertyKind kind = prop_info->kind; - switch (kind) { + switch (prop_info->kind) { case ParsePropertyKind::kSpread: DCHECK_EQ(function_flags, ParseFunctionFlag::kIsNormal); - DCHECK(!*is_computed_name); + DCHECK(!prop_info->is_computed_name); DCHECK_EQ(Token::ELLIPSIS, name_token); - *is_computed_name = true; - *is_rest_property = true; + prop_info->is_computed_name = true; + prop_info->is_rest = true; return factory()->NewObjectLiteralProperty( factory()->NewTheHoleLiteral(), name_expression, @@ -2568,21 +2284,27 @@ ParserBase<Impl>::ParseObjectPropertyDefinition(ObjectLiteralChecker* checker, case ParsePropertyKind::kValue: { DCHECK_EQ(function_flags, ParseFunctionFlag::kIsNormal); - if (!*is_computed_name) { - checker->CheckDuplicateProto(name_token); + if (!prop_info->is_computed_name && + impl()->IdentifierEquals(name, ast_value_factory()->proto_string())) { + if (*has_seen_proto) { + expression_scope()->RecordExpressionError( + scanner()->location(), MessageTemplate::kDuplicateProto); + } + *has_seen_proto = true; } Consume(Token::COLON); - int beg_pos = peek_position(); + AcceptINScope scope(this, true); ExpressionT value = - ParseAssignmentExpression(true, CHECK_OK_CUSTOM(NullLiteralProperty)); - CheckDestructuringElement(value, beg_pos, end_position()); + ParsePossibleDestructuringSubPattern(prop_info->accumulation_scope); ObjectLiteralPropertyT result = factory()->NewObjectLiteralProperty( - name_expression, value, *is_computed_name); + name_expression, value, prop_info->is_computed_name); impl()->SetFunctionNameFromPropertyName(result, name); return result; } + case ParsePropertyKind::kAssign: + case ParsePropertyKind::kShorthandOrClassField: case ParsePropertyKind::kShorthand: { // PropertyDefinition // IdentifierReference @@ -2592,56 +2314,43 @@ ParserBase<Impl>::ParseObjectPropertyDefinition(ObjectLiteralChecker* checker, // IdentifierReference Initializer? DCHECK_EQ(function_flags, ParseFunctionFlag::kIsNormal); - if (!Token::IsIdentifier(name_token, language_mode(), - this->is_generator(), - parsing_module_ || is_async_function())) { + if (!Token::IsValidIdentifier(name_token, language_mode(), is_generator(), + parsing_module_ || is_async_function())) { ReportUnexpectedToken(Next()); - *ok = false; return impl()->NullLiteralProperty(); } - DCHECK(!*is_computed_name); - - if (classifier()->duplicate_finder() != nullptr && - scanner()->IsDuplicateSymbol(classifier()->duplicate_finder(), - ast_value_factory())) { - classifier()->RecordDuplicateFormalParameterError( - scanner()->location()); - } - - if (impl()->IsEvalOrArguments(name) && is_strict(language_mode())) { - classifier()->RecordBindingPatternError( - scanner()->location(), MessageTemplate::kStrictEvalArguments); - } + DCHECK(!prop_info->is_computed_name); if (name_token == Token::LET) { - classifier()->RecordLetPatternError( + expression_scope()->RecordLexicalDeclarationError( scanner()->location(), MessageTemplate::kLetInLexicalBinding); } if (name_token == Token::AWAIT) { DCHECK(!is_async_function()); - classifier()->RecordAsyncArrowFormalParametersError( - Scanner::Location(next_beg_pos, next_end_pos), - MessageTemplate::kAwaitBindingIdentifier); + expression_scope()->RecordAsyncArrowParametersError( + next_loc, MessageTemplate::kAwaitBindingIdentifier); + } + ExpressionT lhs = + impl()->ExpressionFromIdentifier(name, next_loc.beg_pos); + if (!IsAssignableIdentifier(lhs)) { + expression_scope()->RecordPatternError( + next_loc, MessageTemplate::kStrictEvalArguments); } - ExpressionT lhs = impl()->ExpressionFromIdentifier(name, next_beg_pos); - CheckDestructuringElement(lhs, next_beg_pos, next_end_pos); ExpressionT value; if (peek() == Token::ASSIGN) { Consume(Token::ASSIGN); - ExpressionClassifier rhs_classifier(this); - ExpressionT rhs = ParseAssignmentExpression( - true, CHECK_OK_CUSTOM(NullLiteralProperty)); - ValidateExpression(CHECK_OK_CUSTOM(NullLiteralProperty)); - AccumulateFormalParameterContainmentErrors(); - value = factory()->NewAssignment(Token::ASSIGN, lhs, rhs, - kNoSourcePosition); - classifier()->RecordExpressionError( - Scanner::Location(next_beg_pos, end_position()), + { + AcceptINScope scope(this, true); + ExpressionT rhs = ParseAssignmentExpression(); + value = factory()->NewAssignment(Token::ASSIGN, lhs, rhs, + kNoSourcePosition); + impl()->SetFunctionNameFromIdentifierRef(rhs, lhs); + } + expression_scope()->RecordExpressionError( + Scanner::Location(next_loc.beg_pos, end_position()), MessageTemplate::kInvalidCoverInitializedName); - - impl()->SetFunctionNameFromIdentifierRef(rhs, lhs); } else { value = lhs; } @@ -2657,20 +2366,20 @@ ParserBase<Impl>::ParseObjectPropertyDefinition(ObjectLiteralChecker* checker, // PropertyName '(' StrictFormalParameters ')' '{' FunctionBody '}' // '*' PropertyName '(' StrictFormalParameters ')' '{' FunctionBody '}' - classifier()->RecordPatternError( - Scanner::Location(next_beg_pos, end_position()), + expression_scope()->RecordPatternError( + Scanner::Location(next_loc.beg_pos, end_position()), MessageTemplate::kInvalidDestructuringTarget); FunctionKind kind = MethodKindFor(function_flags); ExpressionT value = impl()->ParseFunctionLiteral( name, scanner()->location(), kSkipFunctionNameCheck, kind, - next_beg_pos, FunctionLiteral::kAccessorOrMethod, language_mode(), - nullptr, CHECK_OK_CUSTOM(NullLiteralProperty)); + next_loc.beg_pos, FunctionLiteral::kAccessorOrMethod, language_mode(), + nullptr); ObjectLiteralPropertyT result = factory()->NewObjectLiteralProperty( name_expression, value, ObjectLiteralProperty::COMPUTED, - *is_computed_name); + prop_info->is_computed_name); impl()->SetFunctionNameFromPropertyName(result, name); return result; } @@ -2680,11 +2389,11 @@ ParserBase<Impl>::ParseObjectPropertyDefinition(ObjectLiteralChecker* checker, DCHECK_EQ(function_flags, ParseFunctionFlag::kIsNormal); bool is_get = kind == ParsePropertyKind::kAccessorGetter; - classifier()->RecordPatternError( - Scanner::Location(next_beg_pos, end_position()), + expression_scope()->RecordPatternError( + Scanner::Location(next_loc.beg_pos, end_position()), MessageTemplate::kInvalidDestructuringTarget); - if (!*is_computed_name) { + if (!prop_info->is_computed_name) { // Make sure the name expression is a string since we need a Name for // Runtime_DefineAccessorPropertyUnchecked and since we can determine // this statically we can skip the extra runtime check. @@ -2697,14 +2406,14 @@ ParserBase<Impl>::ParseObjectPropertyDefinition(ObjectLiteralChecker* checker, FunctionLiteralT value = impl()->ParseFunctionLiteral( name, scanner()->location(), kSkipFunctionNameCheck, kind, - next_beg_pos, FunctionLiteral::kAccessorOrMethod, language_mode(), - nullptr, CHECK_OK_CUSTOM(NullLiteralProperty)); + next_loc.beg_pos, FunctionLiteral::kAccessorOrMethod, language_mode(), + nullptr); ObjectLiteralPropertyT result = factory()->NewObjectLiteralProperty( name_expression, value, is_get ? ObjectLiteralProperty::GETTER : ObjectLiteralProperty::SETTER, - *is_computed_name); + prop_info->is_computed_name); const AstRawString* prefix = is_get ? ast_value_factory()->get_space_string() : ast_value_factory()->set_space_string(); @@ -2715,42 +2424,41 @@ ParserBase<Impl>::ParseObjectPropertyDefinition(ObjectLiteralChecker* checker, case ParsePropertyKind::kClassField: case ParsePropertyKind::kNotSet: ReportUnexpectedToken(Next()); - *ok = false; return impl()->NullLiteralProperty(); } UNREACHABLE(); } template <typename Impl> -typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseObjectLiteral( - bool* ok) { +typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseObjectLiteral() { // ObjectLiteral :: // '{' (PropertyDefinition (',' PropertyDefinition)* ','? )? '}' int pos = peek_position(); - typename Types::ObjectPropertyList properties = - impl()->NewObjectPropertyList(4); + ObjectPropertyListT properties(pointer_buffer()); int number_of_boilerplate_properties = 0; bool has_computed_names = false; bool has_rest_property = false; - ObjectLiteralChecker checker(this); + bool has_seen_proto = false; Consume(Token::LBRACE); + AccumulationScope accumulation_scope(expression_scope()); while (!Check(Token::RBRACE)) { FuncNameInferrerState fni_state(&fni_); - bool is_computed_name = false; - bool is_rest_property = false; - ObjectLiteralPropertyT property = ParseObjectPropertyDefinition( - &checker, &is_computed_name, &is_rest_property, CHECK_OK); + ParsePropertyInfo prop_info(this, &accumulation_scope); + prop_info.position = PropertyPosition::kObjectLiteral; + ObjectLiteralPropertyT property = + ParseObjectPropertyDefinition(&prop_info, &has_seen_proto); + if (impl()->IsNull(property)) return impl()->FailureExpression(); - if (is_computed_name) { + if (prop_info.is_computed_name) { has_computed_names = true; } - if (is_rest_property) { + if (prop_info.is_rest) { has_rest_property = true; } @@ -2760,11 +2468,10 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseObjectLiteral( number_of_boilerplate_properties++; } - properties->Add(property, zone()); + properties.Add(property); if (peek() != Token::RBRACE) { - // Need {} because of the CHECK_OK macro. - Expect(Token::COMMA, CHECK_OK); + Expect(Token::COMMA); } fni_.Infer(); @@ -2775,8 +2482,8 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseObjectLiteral( // this runtime function. Here, we make sure that the number of // properties is less than number of arguments allowed for a runtime // call. - if (has_rest_property && properties->length() > Code::kMaxArguments) { - this->classifier()->RecordPatternError(Scanner::Location(pos, position()), + if (has_rest_property && properties.length() > Code::kMaxArguments) { + expression_scope()->RecordPatternError(Scanner::Location(pos, position()), MessageTemplate::kTooManyArguments); } @@ -2785,78 +2492,61 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseObjectLiteral( } template <typename Impl> -typename ParserBase<Impl>::ExpressionListT ParserBase<Impl>::ParseArguments( - Scanner::Location* first_spread_arg_loc, bool maybe_arrow, - bool* is_simple_parameter_list, bool* ok) { +void ParserBase<Impl>::ParseArguments( + typename ParserBase<Impl>::ExpressionListT* args, bool* has_spread, + ParsingArrowHeadFlag maybe_arrow) { // Arguments :: // '(' (AssignmentExpression)*[','] ')' - Scanner::Location spread_arg = Scanner::Location::invalid(); - ExpressionListT result = impl()->NewExpressionList(4); - Expect(Token::LPAREN, CHECK_OK_CUSTOM(NullExpressionList)); + *has_spread = false; + Consume(Token::LPAREN); + AccumulationScope accumulation_scope(expression_scope()); + 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 = false; + AcceptINScope scope(this, true); + ExpressionT argument = ParseAssignmentExpressionCoverGrammar(); - if (!maybe_arrow) { - ValidateExpression(CHECK_OK_CUSTOM(NullExpressionList)); + if (V8_UNLIKELY(maybe_arrow == kMaybeArrowHead)) { + ClassifyArrowParameter(&accumulation_scope, expr_pos, argument); + if (is_spread) { + expression_scope()->RecordNonSimpleParameter(); + if (argument->IsAssignment()) { + expression_scope()->RecordAsyncArrowParametersError( + scanner()->location(), MessageTemplate::kRestDefaultInitializer); + } + if (peek() == Token::COMMA) { + expression_scope()->RecordAsyncArrowParametersError( + scanner()->peek_location(), MessageTemplate::kParamAfterRest); + } + } } if (is_spread) { - *is_simple_parameter_list = false; - if (!spread_arg.IsValid()) { - spread_arg.beg_pos = start_pos; - spread_arg.end_pos = peek_position(); - } - if (argument->IsAssignment()) { - classifier()->RecordAsyncArrowFormalParametersError( - scanner()->location(), MessageTemplate::kRestDefaultInitializer); - } + *has_spread = true; argument = factory()->NewSpread(argument, start_pos, expr_pos); } - result->Add(argument, zone_); - - if (peek() != Token::COMMA) break; - - Next(); - - if (argument->IsSpread()) { - classifier()->RecordAsyncArrowFormalParametersError( - scanner()->location(), MessageTemplate::kParamAfterRest); - } + args->Add(argument); + if (!Check(Token::COMMA)) break; } - if (result->length() > Code::kMaxArguments) { + if (args->length() > Code::kMaxArguments) { ReportMessage(MessageTemplate::kTooManyArguments); - *ok = false; - return impl()->NullExpressionList(); + return; } Scanner::Location location = scanner_->location(); - if (Token::RPAREN != Next()) { + if (!Check(Token::RPAREN)) { impl()->ReportMessageAt(location, MessageTemplate::kUnterminatedArgList); - *ok = false; - return impl()->NullExpressionList(); - } - *first_spread_arg_loc = spread_arg; - - if (!maybe_arrow || peek() != Token::ARROW) { - if (maybe_arrow) { - ValidateExpression(CHECK_OK_CUSTOM(NullExpressionList)); - } } - - return result; } // Precedence = 2 template <typename Impl> typename ParserBase<Impl>::ExpressionT -ParserBase<Impl>::ParseAssignmentExpression(bool accept_IN, bool* ok) { +ParserBase<Impl>::ParseAssignmentExpressionCoverGrammar() { // AssignmentExpression :: // ConditionalExpression // ArrowFunction @@ -2865,188 +2555,120 @@ ParserBase<Impl>::ParseAssignmentExpression(bool accept_IN, bool* ok) { int lhs_beg_pos = peek_position(); if (peek() == Token::YIELD && is_generator()) { - return ParseYieldExpression(accept_IN, ok); + return ParseYieldExpression(); } FuncNameInferrerState fni_state(&fni_); - ExpressionClassifier arrow_formals_classifier( - this, classifier()->duplicate_finder()); - Scope::Snapshot scope_snapshot(scope()); - int rewritable_length = static_cast<int>( - function_state_->destructuring_assignments_to_rewrite().size()); + DCHECK_IMPLIES(!has_error(), next_arrow_function_info_.HasInitialState()); - bool is_async = peek() == Token::ASYNC && - !scanner()->HasLineTerminatorAfterNext() && - IsValidArrowFormalParametersStart(PeekAhead()); + ExpressionT expression = ParseConditionalExpression(); - bool parenthesized_formals = peek() == Token::LPAREN; - if (!is_async && !parenthesized_formals) { - ArrowFormalParametersUnexpectedToken(); - } - - // Parse a simple, faster sub-grammar (primary expression) if it's evident - // that we have only a trivial expression to parse. - ExpressionT expression; - if (IsTrivialExpression()) { - expression = ParsePrimaryExpression(&is_async, CHECK_OK); - } else { - expression = ParseConditionalExpression(accept_IN, CHECK_OK); - } + Token::Value op = peek(); - if (is_async && impl()->IsIdentifier(expression) && peek_any_identifier() && - PeekAhead() == Token::ARROW) { - // async Identifier => AsyncConciseBody - IdentifierT name = ParseAndClassifyIdentifier(CHECK_OK); - expression = - impl()->ExpressionFromIdentifier(name, position(), InferName::kNo); - // Remove `async` keyword from inferred name stack. - fni_.RemoveAsyncKeywordFromEnd(); - } - - if (peek() == Token::ARROW) { - Scanner::Location arrow_loc = scanner()->peek_location(); - ValidateArrowFormalParameters(expression, parenthesized_formals, is_async, - CHECK_OK); - // This reads strangely, but is correct: it checks whether any - // sub-expression of the parameter list failed to be a valid formal - // parameter initializer. Since YieldExpressions are banned anywhere - // in an arrow parameter list, this is correct. - // TODO(adamk): Rename "FormalParameterInitializerError" to refer to - // "YieldExpression", which is its only use. - ValidateFormalParameterInitializer(CHECK_OK); + if (!Token::IsArrowOrAssignmentOp(op)) return expression; + // Arrow functions. + if (V8_UNLIKELY(op == Token::ARROW)) { Scanner::Location loc(lhs_beg_pos, end_position()); - DeclarationScope* scope = - NewFunctionScope(is_async ? FunctionKind::kAsyncArrowFunction - : FunctionKind::kArrowFunction); - // Because the arrow's parameters were parsed in the outer scope, - // we need to fix up the scope chain appropriately. - scope_snapshot.Reparent(scope); - - FormalParametersT parameters(scope); - if (!classifier()->is_simple_parameter_list()) { - scope->SetHasNonSimpleParameters(); - parameters.is_simple = false; + if (!impl()->IsIdentifier(expression) && !expression->is_parenthesized()) { + impl()->ReportMessageAt( + Scanner::Location(expression->position(), position()), + MessageTemplate::kMalformedArrowFunParamList); + return impl()->FailureExpression(); } + DeclarationScope* scope = next_arrow_function_info_.scope; scope->set_start_position(lhs_beg_pos); - Scanner::Location duplicate_loc = Scanner::Location::invalid(); - impl()->DeclareArrowFunctionFormalParameters(¶meters, expression, loc, - &duplicate_loc, CHECK_OK); - if (duplicate_loc.IsValid()) { - classifier()->RecordDuplicateFormalParameterError(duplicate_loc); - } - expression = ParseArrowFunctionLiteral(accept_IN, parameters, - rewritable_length, CHECK_OK); - Accumulate(ExpressionClassifier::AsyncArrowFormalParametersProduction); - classifier()->RecordPatternError(arrow_loc, - MessageTemplate::kUnexpectedToken, - Token::String(Token::ARROW)); - fni_.Infer(); - - return expression; - } + FormalParametersT parameters(scope); + parameters.set_strict_parameter_error( + next_arrow_function_info_.strict_parameter_error_location, + next_arrow_function_info_.strict_parameter_error_message); + parameters.is_simple = scope->has_simple_parameters(); + next_arrow_function_info_.Reset(); - // "expression" was not itself an arrow function parameter list, but it might - // form part of one. Propagate speculative formal parameter error locations - // (including those for binding patterns, since formal parameters can - // themselves contain binding patterns). - unsigned productions = ExpressionClassifier::AllProductions & - ~ExpressionClassifier::ArrowFormalParametersProduction; + impl()->DeclareArrowFunctionFormalParameters(¶meters, expression, loc); - // Parenthesized identifiers and property references are allowed as part - // of a larger assignment pattern, even though parenthesized patterns - // themselves are not allowed, e.g., "[(x)] = []". Only accumulate - // assignment pattern errors if the parsed expression is more complex. - if (IsValidReferenceExpression(expression)) { - productions &= ~ExpressionClassifier::AssignmentPatternProduction; - } + expression = ParseArrowFunctionLiteral(parameters); - const bool is_destructuring_assignment = - IsValidPattern(expression) && peek() == Token::ASSIGN; - if (is_destructuring_assignment) { - // This is definitely not an expression so don't accumulate - // expression-related errors. - productions &= ~ExpressionClassifier::ExpressionProduction; + return expression; } - Accumulate(productions); - if (!Token::IsAssignmentOp(peek())) return expression; - - if (is_destructuring_assignment) { - ValidateAssignmentPattern(CHECK_OK); + if (V8_LIKELY(impl()->IsAssignableIdentifier(expression))) { + if (expression->is_parenthesized()) { + expression_scope()->RecordDeclarationError( + Scanner::Location(lhs_beg_pos, end_position()), + MessageTemplate::kInvalidDestructuringTarget); + } + expression_scope()->MarkIdentifierAsAssigned(); + } else if (expression->IsProperty()) { + expression_scope()->RecordDeclarationError( + Scanner::Location(lhs_beg_pos, end_position()), + MessageTemplate::kInvalidPropertyBindingPattern); + } else if (expression->IsPattern() && op == Token::ASSIGN) { + // Destructuring assignmment. + if (expression->is_parenthesized()) { + expression_scope()->RecordPatternError( + Scanner::Location(lhs_beg_pos, end_position()), + MessageTemplate::kInvalidDestructuringTarget); + } + expression_scope()->ValidateAsPattern(expression, lhs_beg_pos, + end_position()); } else { - expression = CheckAndRewriteReferenceExpression( + DCHECK(!IsValidReferenceExpression(expression)); + expression = RewriteInvalidReferenceExpression( expression, lhs_beg_pos, end_position(), - MessageTemplate::kInvalidLhsInAssignment, CHECK_OK); + MessageTemplate::kInvalidLhsInAssignment); } - impl()->MarkExpressionAsAssigned(expression); + Consume(op); + int op_position = position(); - Token::Value op = Next(); // Get assignment operator. - if (op != Token::ASSIGN) { - classifier()->RecordPatternError(scanner()->location(), - MessageTemplate::kUnexpectedToken, - Token::String(op)); - } - int pos = position(); - - ExpressionClassifier rhs_classifier(this); + ExpressionT right = ParseAssignmentExpression(); - ExpressionT right = ParseAssignmentExpression(accept_IN, CHECK_OK); - ValidateExpression(CHECK_OK); - AccumulateFormalParameterContainmentErrors(); - - // We try to estimate the set of properties set by constructors. We define a - // new property whenever there is an assignment to a property of 'this'. We - // should probably only add properties if we haven't seen them - // before. Otherwise we'll probably overestimate the number of properties. - if (op == Token::ASSIGN && impl()->IsThisProperty(expression)) { - function_state_->AddProperty(); - } - - impl()->CheckAssigningFunctionLiteralToProperty(expression, right); + if (op == Token::ASSIGN) { + // We try to estimate the set of properties set by constructors. We define a + // new property whenever there is an assignment to a property of 'this'. We + // should probably only add properties if we haven't seen them before. + // Otherwise we'll probably overestimate the number of properties. + if (impl()->IsThisProperty(expression)) function_state_->AddProperty(); + + impl()->CheckAssigningFunctionLiteralToProperty(expression, right); + + // Check if the right hand side is a call to avoid inferring a + // name if we're dealing with "a = function(){...}();"-like + // expression. + if (right->IsCall() || right->IsCallNew()) { + fni_.RemoveLastFunction(); + } else { + fni_.Infer(); + } - // 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(); + impl()->SetFunctionNameFromIdentifierRef(right, expression); } else { + expression_scope()->RecordPatternError( + Scanner::Location(lhs_beg_pos, end_position()), + MessageTemplate::kInvalidDestructuringTarget); fni_.RemoveLastFunction(); } - if (op == Token::ASSIGN) { - impl()->SetFunctionNameFromIdentifierRef(right, expression); - } - - DCHECK_NE(op, Token::INIT); - ExpressionT result = factory()->NewAssignment(op, expression, right, pos); - - if (is_destructuring_assignment) { - DCHECK_NE(op, Token::ASSIGN_EXP); - auto rewritable = factory()->NewRewritableExpression(result, scope()); - impl()->QueueDestructuringAssignmentForRewriting(rewritable); - result = rewritable; - } - - return result; + return factory()->NewAssignment(op, expression, right, op_position); } template <typename Impl> -typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseYieldExpression( - bool accept_IN, bool* ok) { +typename ParserBase<Impl>::ExpressionT +ParserBase<Impl>::ParseYieldExpression() { // YieldExpression :: // 'yield' ([no line terminator] '*'? AssignmentExpression)? int pos = peek_position(); - classifier()->RecordPatternError( - scanner()->peek_location(), MessageTemplate::kInvalidDestructuringTarget); - classifier()->RecordFormalParameterInitializerError( + expression_scope()->RecordParameterInitializerError( scanner()->peek_location(), MessageTemplate::kYieldInParameter); - Expect(Token::YIELD, CHECK_OK); + Consume(Token::YIELD); + + CheckStackOverflow(); + // The following initialization is necessary. ExpressionT expression = impl()->NullExpression(); bool delegating = false; // yield* @@ -3069,8 +2691,7 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseYieldExpression( // Delegating yields require an RHS; fall through. V8_FALLTHROUGH; default: - expression = ParseAssignmentExpression(accept_IN, CHECK_OK); - ValidateExpression(CHECK_OK); + expression = ParseAssignmentExpressionCoverGrammar(); break; } } @@ -3099,80 +2720,65 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseYieldExpression( // Precedence = 3 template <typename Impl> typename ParserBase<Impl>::ExpressionT -ParserBase<Impl>::ParseConditionalExpression(bool accept_IN, - bool* ok) { +ParserBase<Impl>::ParseConditionalExpression() { // ConditionalExpression :: // LogicalOrExpression // LogicalOrExpression '?' AssignmentExpression ':' AssignmentExpression int pos = peek_position(); // We start using the binary expression parser for prec >= 4 only! - ExpressionT expression = ParseBinaryExpression(4, accept_IN, CHECK_OK); + ExpressionT expression = ParseBinaryExpression(4); return peek() == Token::CONDITIONAL - ? ParseConditionalContinuation(expression, accept_IN, pos, ok) + ? ParseConditionalContinuation(expression, pos) : expression; } template <typename Impl> typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseConditionalContinuation(ExpressionT expression, - bool accept_IN, int pos, - bool* ok) { + int pos) { SourceRange then_range, else_range; - ValidateExpression(CHECK_OK); - BindingPatternUnexpectedToken(); - ArrowFormalParametersUnexpectedToken(); ExpressionT left; { SourceRangeScope range_scope(scanner(), &then_range); Consume(Token::CONDITIONAL); - ExpressionClassifier classifier(this); // In parsing the first assignment expression in conditional // expressions we always accept the 'in' keyword; see ECMA-262, // section 11.12, page 58. - left = ParseAssignmentExpression(true, CHECK_OK); - AccumulateNonBindingPatternErrors(); + AcceptINScope scope(this, true); + left = ParseAssignmentExpression(); } - ValidateExpression(CHECK_OK); ExpressionT right; { SourceRangeScope range_scope(scanner(), &else_range); - Expect(Token::COLON, CHECK_OK); - ExpressionClassifier classifier(this); - right = ParseAssignmentExpression(accept_IN, CHECK_OK); - AccumulateNonBindingPatternErrors(); + Expect(Token::COLON); + right = ParseAssignmentExpression(); } - ValidateExpression(CHECK_OK); ExpressionT expr = factory()->NewConditional(expression, left, right, pos); impl()->RecordConditionalSourceRange(expr, then_range, else_range); return expr; } - // Precedence >= 4 template <typename Impl> -typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseBinaryExpression( - int prec, bool accept_IN, bool* ok) { - DCHECK_GE(prec, 4); - SourceRange right_range; - ExpressionT x = ParseUnaryExpression(CHECK_OK); - for (int prec1 = Precedence(peek(), accept_IN); prec1 >= prec; prec1--) { +typename ParserBase<Impl>::ExpressionT +ParserBase<Impl>::ParseBinaryContinuation(ExpressionT x, int prec, int prec1) { + do { // prec1 >= 4 - while (Precedence(peek(), accept_IN) == prec1) { - ValidateExpression(CHECK_OK); - BindingPatternUnexpectedToken(); - ArrowFormalParametersUnexpectedToken(); - - SourceRangeScope right_range_scope(scanner(), &right_range); - Token::Value op = Next(); - int pos = position(); - - const bool is_right_associative = op == Token::EXP; - const int next_prec = is_right_associative ? prec1 : prec1 + 1; - ExpressionT y = ParseBinaryExpression(next_prec, accept_IN, CHECK_OK); - right_range_scope.Finalize(); - ValidateExpression(CHECK_OK); + while (Token::Precedence(peek(), accept_IN_) == prec1) { + SourceRange right_range; + int pos = peek_position(); + ExpressionT y; + Token::Value op; + { + SourceRangeScope right_range_scope(scanner(), &right_range); + op = Next(); + + const bool is_right_associative = op == Token::EXP; + const int next_prec = is_right_associative ? prec1 : prec1 + 1; + y = ParseBinaryExpression(next_prec); + } // For now we distinguish between comparisons and other binary // operations. (We could combine the two and get rid of this @@ -3200,16 +2806,28 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseBinaryExpression( } } } - } + --prec1; + } while (prec1 >= prec); + return x; } +// Precedence >= 4 template <typename Impl> -typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseUnaryOpExpression( - bool* ok) { - BindingPatternUnexpectedToken(); - ArrowFormalParametersUnexpectedToken(); +typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseBinaryExpression( + int prec) { + DCHECK_GE(prec, 4); + ExpressionT x = ParseUnaryExpression(); + int prec1 = Token::Precedence(peek(), accept_IN_); + if (prec1 >= prec) { + return ParseBinaryContinuation(x, prec, prec1); + } + return x; +} +template <typename Impl> +typename ParserBase<Impl>::ExpressionT +ParserBase<Impl>::ParseUnaryOrPrefixExpression() { Token::Value op = Next(); int pos = position(); @@ -3218,66 +2836,64 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseUnaryOpExpression( function_state_->set_next_function_is_likely_called(); } - ExpressionT expression = ParseUnaryExpression(CHECK_OK); - ValidateExpression(CHECK_OK); + CheckStackOverflow(); + + int expression_position = peek_position(); + ExpressionT expression = ParseUnaryExpression(); + + if (Token::IsUnaryOp(op)) { + if (op == Token::DELETE) { + if (impl()->IsIdentifier(expression) && is_strict(language_mode())) { + // "delete identifier" is a syntax error in strict mode. + ReportMessage(MessageTemplate::kStrictDelete); + return impl()->FailureExpression(); + } - if (op == Token::DELETE) { - if (impl()->IsIdentifier(expression) && is_strict(language_mode())) { - // "delete identifier" is a syntax error in strict mode. - ReportMessage(MessageTemplate::kStrictDelete); - *ok = false; - return impl()->NullExpression(); + if (impl()->IsPropertyWithPrivateFieldKey(expression)) { + ReportMessage(MessageTemplate::kDeletePrivateField); + return impl()->FailureExpression(); + } } - if (impl()->IsPropertyWithPrivateFieldKey(expression)) { - ReportMessage(MessageTemplate::kDeletePrivateField); - *ok = false; - return impl()->NullExpression(); + if (peek() == Token::EXP) { + impl()->ReportMessageAt( + Scanner::Location(pos, peek_end_position()), + MessageTemplate::kUnexpectedTokenUnaryExponentiation); + return impl()->FailureExpression(); } - } - if (peek() == Token::EXP) { - ReportUnexpectedToken(Next()); - *ok = false; - return impl()->NullExpression(); + // Allow the parser's implementation to rewrite the expression. + return impl()->BuildUnaryExpression(expression, op, pos); } - // Allow the parser's implementation to rewrite the expression. - return impl()->BuildUnaryExpression(expression, op, pos); -} + DCHECK(Token::IsCountOp(op)); -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); + if (V8_LIKELY(IsValidReferenceExpression(expression))) { + if (impl()->IsIdentifier(expression)) { + expression_scope()->MarkIdentifierAsAssigned(); + } + } else { + expression = RewriteInvalidReferenceExpression( + expression, expression_position, end_position(), + MessageTemplate::kInvalidLhsInPrefixOp); + } return factory()->NewCountOperation(op, true /* prefix */, expression, position()); } template <typename Impl> -typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseAwaitExpression( - bool* ok) { - classifier()->RecordFormalParameterInitializerError( +typename ParserBase<Impl>::ExpressionT +ParserBase<Impl>::ParseAwaitExpression() { + expression_scope()->RecordParameterInitializerError( scanner()->peek_location(), MessageTemplate::kAwaitExpressionFormalParameter); int await_pos = peek_position(); Consume(Token::AWAIT); - ExpressionT value = ParseUnaryExpression(CHECK_OK); + CheckStackOverflow(); - classifier()->RecordBindingPatternError( - Scanner::Location(await_pos, end_position()), - MessageTemplate::kInvalidDestructuringTarget); + ExpressionT value = ParseUnaryExpression(); ExpressionT expr = factory()->NewAwait(value, await_pos); function_state_->AddSuspend(); @@ -3286,8 +2902,8 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseAwaitExpression( } template <typename Impl> -typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseUnaryExpression( - bool* ok) { +typename ParserBase<Impl>::ExpressionT +ParserBase<Impl>::ParseUnaryExpression() { // UnaryExpression :: // PostfixExpression // 'delete' UnaryExpression @@ -3302,31 +2918,30 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseUnaryExpression( // [+Await] AwaitExpression[?Yield] Token::Value op = peek(); - if (Token::IsUnaryOp(op)) return ParseUnaryOpExpression(ok); - if (Token::IsCountOp(op)) return ParsePrefixExpression(ok); + if (Token::IsUnaryOrCountOp(op)) return ParseUnaryOrPrefixExpression(); if (is_async_function() && op == Token::AWAIT) { - return ParseAwaitExpression(ok); + return ParseAwaitExpression(); } - return ParsePostfixExpression(ok); + return ParsePostfixExpression(); } template <typename Impl> -typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParsePostfixExpression( - bool* ok) { +typename ParserBase<Impl>::ExpressionT +ParserBase<Impl>::ParsePostfixExpression() { // PostfixExpression :: // LeftHandSideExpression ('++' | '--')? int lhs_beg_pos = peek_position(); - ExpressionT expression = ParseLeftHandSideExpression(CHECK_OK); + ExpressionT expression = ParseLeftHandSideExpression(); if (!scanner()->HasLineTerminatorBeforeNext() && Token::IsCountOp(peek())) { - BindingPatternUnexpectedToken(); - ArrowFormalParametersUnexpectedToken(); - - expression = CheckAndRewriteReferenceExpression( - expression, lhs_beg_pos, end_position(), - MessageTemplate::kInvalidLhsInPostfixOp, CHECK_OK); - impl()->MarkExpressionAsAssigned(expression); - ValidateExpression(CHECK_OK); + if (V8_UNLIKELY(!IsValidReferenceExpression(expression))) { + expression = RewriteInvalidReferenceExpression( + expression, lhs_beg_pos, end_position(), + MessageTemplate::kInvalidLhsInPostfixOp); + } + if (impl()->IsIdentifier(expression)) { + expression_scope()->MarkIdentifierAsAssigned(); + } Token::Value next = Next(); expression = @@ -3340,36 +2955,83 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParsePostfixExpression( template <typename Impl> typename ParserBase<Impl>::ExpressionT -ParserBase<Impl>::ParseLeftHandSideExpression(bool* ok) { +ParserBase<Impl>::ParseLeftHandSideExpression() { // LeftHandSideExpression :: // (NewExpression | MemberExpression) ... - bool is_async = false; - ExpressionT result = - ParseMemberWithNewPrefixesExpression(&is_async, CHECK_OK); + ExpressionT result = ParseMemberWithNewPrefixesExpression(); + if (!Token::IsPropertyOrCall(peek())) return result; + return ParseLeftHandSideContinuation(result); +} - while (true) { +template <typename Impl> +typename ParserBase<Impl>::ExpressionT +ParserBase<Impl>::ParseLeftHandSideContinuation(ExpressionT result) { + DCHECK(Token::IsPropertyOrCall(peek())); + + if (V8_UNLIKELY(peek() == Token::LPAREN && impl()->IsIdentifier(result) && + scanner()->current_token() == Token::ASYNC && + !scanner()->HasLineTerminatorBeforeNext())) { + DCHECK(impl()->IsAsync(impl()->AsIdentifier(result))); + int pos = position(); + + ArrowHeadParsingScope maybe_arrow(impl(), + FunctionKind::kAsyncArrowFunction); + Scope::Snapshot scope_snapshot(scope()); + + ExpressionListT args(pointer_buffer()); + bool has_spread; + ParseArguments(&args, &has_spread, kMaybeArrowHead); + if (V8_LIKELY(peek() == Token::ARROW)) { + fni_.RemoveAsyncKeywordFromEnd(); + next_arrow_function_info_.scope = maybe_arrow.ValidateAndCreateScope(); + scope_snapshot.Reparent(next_arrow_function_info_.scope); + // async () => ... + if (!args.length()) return factory()->NewEmptyParentheses(pos); + // async ( Arguments ) => ... + ExpressionT result = impl()->ExpressionListToExpression(args); + result->mark_parenthesized(); + return result; + } + + if (has_spread) { + result = impl()->SpreadCall(result, args, pos, Call::NOT_EVAL); + } else { + result = factory()->NewCall(result, args, pos, Call::NOT_EVAL); + } + + maybe_arrow.ValidateExpression(); + + fni_.RemoveLastFunction(); + if (!Token::IsPropertyOrCall(peek())) return result; + } + + do { switch (peek()) { + /* Property */ case Token::LBRACK: { - ValidateExpression(CHECK_OK); - BindingPatternUnexpectedToken(); - ArrowFormalParametersUnexpectedToken(); Consume(Token::LBRACK); int pos = position(); - ExpressionT index = ParseExpressionCoverGrammar(true, CHECK_OK); - ValidateExpression(CHECK_OK); + AcceptINScope scope(this, true); + ExpressionT index = ParseExpressionCoverGrammar(); result = factory()->NewProperty(result, index, pos); - Expect(Token::RBRACK, CHECK_OK); + Expect(Token::RBRACK); break; } + /* Property */ + case Token::PERIOD: { + Consume(Token::PERIOD); + int pos = position(); + ExpressionT key = ParsePropertyOrPrivatePropertyName(); + result = factory()->NewProperty(result, key, pos); + break; + } + + /* Call */ case Token::LPAREN: { int pos; - ValidateExpression(CHECK_OK); - BindingPatternUnexpectedToken(); - if (scanner()->current_token() == Token::IDENTIFIER || - scanner()->current_token() == Token::SUPER || - scanner()->current_token() == Token::ASYNC) { + if (Token::IsCallable(scanner()->current_token())) { // For call of an identifier we want to report position of // the identifier as position of the call in the stack trace. pos = position(); @@ -3385,43 +3047,16 @@ ParserBase<Impl>::ParseLeftHandSideExpression(bool* ok) { // function literal eagerly, we can also compile it eagerly. if (result->IsFunctionLiteral()) { result->AsFunctionLiteral()->SetShouldEagerCompile(); - result->AsFunctionLiteral()->mark_as_iife(); - } - } - Scanner::Location spread_pos; - ExpressionListT args; - if (V8_UNLIKELY(is_async && impl()->IsIdentifier(result))) { - ExpressionClassifier async_classifier(this); - bool is_simple_parameter_list = true; - args = ParseArguments(&spread_pos, true, &is_simple_parameter_list, - CHECK_OK); - if (peek() == Token::ARROW) { - fni_.RemoveAsyncKeywordFromEnd(); - ValidateBindingPattern(CHECK_OK); - ValidateFormalParameterInitializer(CHECK_OK); - if (!classifier()->is_valid_async_arrow_formal_parameters()) { - ReportClassifierError( - classifier()->async_arrow_formal_parameters_error()); - *ok = false; - return impl()->NullExpression(); - } - if (args->length()) { - // async ( Arguments ) => ... - if (!is_simple_parameter_list) { - async_classifier.previous()->RecordNonSimpleParameter(); - } - return impl()->ExpressionListToExpression(args); + if (scope()->is_script_scope()) { + // A non-top-level iife is likely to be executed multiple times + // and so shouldn`t be optimized as one-shot. + result->AsFunctionLiteral()->mark_as_oneshot_iife(); } - // async () => ... - return factory()->NewEmptyParentheses(pos); - } else { - AccumulateFormalParameterContainmentErrors(); } - } else { - args = ParseArguments(&spread_pos, CHECK_OK); } - - ArrowFormalParametersUnexpectedToken(); + bool has_spread; + ExpressionListT args(pointer_buffer()); + ParseArguments(&args, &has_spread); // Keep track of eval() calls since they disable all local variable // optimizations. @@ -3433,7 +3068,7 @@ ParserBase<Impl>::ParseLeftHandSideExpression(bool* ok) { Call::PossiblyEval is_possibly_eval = CheckPossibleEvalCall(result, scope()); - if (spread_pos.IsValid()) { + if (has_spread) { result = impl()->SpreadCall(result, args, pos, is_possibly_eval); } else { result = factory()->NewCall(result, args, pos, is_possibly_eval); @@ -3443,36 +3078,19 @@ ParserBase<Impl>::ParseLeftHandSideExpression(bool* ok) { break; } - case Token::PERIOD: { - ValidateExpression(CHECK_OK); - BindingPatternUnexpectedToken(); - ArrowFormalParametersUnexpectedToken(); - Consume(Token::PERIOD); - int pos = position(); - ExpressionT key = ParseIdentifierNameOrPrivateName(CHECK_OK); - result = factory()->NewProperty(result, key, pos); - break; - } - - case Token::TEMPLATE_SPAN: - case Token::TEMPLATE_TAIL: { - ValidateExpression(CHECK_OK); - BindingPatternUnexpectedToken(); - ArrowFormalParametersUnexpectedToken(); - result = ParseTemplateLiteral(result, position(), true, CHECK_OK); - break; - } - + /* Call */ default: - return result; + DCHECK(Token::IsTemplate(peek())); + result = ParseTemplateLiteral(result, position(), true); + break; } - } + } while (Token::IsPropertyOrCall(peek())); + return result; } template <typename Impl> typename ParserBase<Impl>::ExpressionT -ParserBase<Impl>::ParseMemberWithPresentNewPrefixesExpression(bool* is_async, - bool* ok) { +ParserBase<Impl>::ParseMemberWithPresentNewPrefixesExpression() { // NewExpression :: // ('new')+ MemberExpression // @@ -3492,57 +3110,94 @@ ParserBase<Impl>::ParseMemberWithPresentNewPrefixesExpression(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; + + CheckStackOverflow(); + if (peek() == Token::SUPER) { const bool is_new = true; - result = ParseSuperExpression(is_new, CHECK_OK); + result = ParseSuperExpression(is_new); } 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(); + return impl()->FailureExpression(); } else if (peek() == Token::PERIOD) { - *is_async = false; - result = ParseNewTargetExpression(CHECK_OK); - return ParseMemberExpressionContinuation(result, is_async, ok); + result = ParseNewTargetExpression(); + return ParseMemberExpressionContinuation(result); } else { - result = ParseMemberWithNewPrefixesExpression(is_async, CHECK_OK); + result = ParseMemberWithNewPrefixesExpression(); } - ValidateExpression(CHECK_OK); if (peek() == Token::LPAREN) { // NewExpression with arguments. - Scanner::Location spread_pos; - ExpressionListT args = ParseArguments(&spread_pos, CHECK_OK); + { + ExpressionListT args(pointer_buffer()); + bool has_spread; + ParseArguments(&args, &has_spread); - if (spread_pos.IsValid()) { - result = impl()->SpreadCallNew(result, args, new_pos); - } else { - result = factory()->NewCallNew(result, args, new_pos); + if (has_spread) { + 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. - return ParseMemberExpressionContinuation(result, is_async, ok); + return ParseMemberExpressionContinuation(result); } // NewExpression without arguments. - return factory()->NewCallNew(result, impl()->NewExpressionList(0), new_pos); + ExpressionListT args(pointer_buffer()); + return factory()->NewCallNew(result, args, new_pos); +} + +template <typename Impl> +typename ParserBase<Impl>::ExpressionT +ParserBase<Impl>::ParseMemberWithNewPrefixesExpression() { + return peek() == Token::NEW ? ParseMemberWithPresentNewPrefixesExpression() + : ParseMemberExpression(); } 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); +ParserBase<Impl>::ParseFunctionExpression() { + Consume(Token::FUNCTION); + int function_token_position = position(); + + FunctionKind function_kind = Check(Token::MUL) + ? FunctionKind::kGeneratorFunction + : FunctionKind::kNormalFunction; + IdentifierT name = impl()->NullIdentifier(); + bool is_strict_reserved_name = Token::IsStrictReservedWord(peek()); + Scanner::Location function_name_location = Scanner::Location::invalid(); + FunctionLiteral::FunctionType function_type = + FunctionLiteral::kAnonymousExpression; + if (impl()->ParsingDynamicFunctionDeclaration()) { + // We don't want dynamic functions to actually declare their name + // "anonymous". We just want that name in the toString(). + Consume(Token::IDENTIFIER); + DCHECK_IMPLIES(!has_error(), + scanner()->CurrentSymbol(ast_value_factory()) == + ast_value_factory()->anonymous_string()); + } else if (peek_any_identifier()) { + name = ParseIdentifier(function_kind); + function_name_location = scanner()->location(); + function_type = FunctionLiteral::kNamedExpression; + } + FunctionLiteralT result = impl()->ParseFunctionLiteral( + name, function_name_location, + is_strict_reserved_name ? kFunctionNameIsStrictReserved + : kFunctionNameValidityUnknown, + function_kind, function_token_position, function_type, language_mode(), + nullptr); + // TODO(verwaest): FailureFunctionLiteral? + if (impl()->IsNull(result)) return impl()->FailureExpression(); + return result; } template <typename Impl> -typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseMemberExpression( - bool* is_async, bool* ok) { +typename ParserBase<Impl>::ExpressionT +ParserBase<Impl>::ParseMemberExpression() { // MemberExpression :: // (PrimaryExpression | FunctionLiteral | ClassLiteral) // ('[' Expression ']' | '.' Identifier | Arguments | TemplateLiteral)* @@ -3558,99 +3213,60 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseMemberExpression( // Parse the initial primary or function expression. ExpressionT result; if (peek() == Token::FUNCTION) { - BindingPatternUnexpectedToken(); - ArrowFormalParametersUnexpectedToken(); - - Consume(Token::FUNCTION); - int function_token_position = position(); - - FunctionKind function_kind = Check(Token::MUL) - ? FunctionKind::kGeneratorFunction - : FunctionKind::kNormalFunction; - IdentifierT name = impl()->NullIdentifier(); - bool is_strict_reserved_name = false; - Scanner::Location function_name_location = Scanner::Location::invalid(); - FunctionLiteral::FunctionType function_type = - FunctionLiteral::kAnonymousExpression; - if (impl()->ParsingDynamicFunctionDeclaration()) { - // We don't want dynamic functions to actually declare their name - // "anonymous". We just want that name in the toString(). - if (stack_overflow()) { - *ok = false; - return impl()->NullExpression(); - } - Consume(Token::IDENTIFIER); - DCHECK(scanner()->CurrentMatchesContextual(Token::ANONYMOUS)); - } else if (peek_any_identifier()) { - bool is_await = false; - name = ParseIdentifierOrStrictReservedWord( - function_kind, &is_strict_reserved_name, &is_await, CHECK_OK); - function_name_location = scanner()->location(); - function_type = FunctionLiteral::kNamedExpression; - } - result = impl()->ParseFunctionLiteral( - name, function_name_location, - is_strict_reserved_name ? kFunctionNameIsStrictReserved - : kFunctionNameValidityUnknown, - function_kind, function_token_position, function_type, language_mode(), - nullptr, CHECK_OK); + result = ParseFunctionExpression(); } else if (peek() == Token::SUPER) { const bool is_new = false; - result = ParseSuperExpression(is_new, CHECK_OK); + result = ParseSuperExpression(is_new); } else if (allow_harmony_dynamic_import() && peek() == Token::IMPORT) { - result = ParseImportExpressions(CHECK_OK); + result = ParseImportExpressions(); } else { - result = ParsePrimaryExpression(is_async, CHECK_OK); + result = ParsePrimaryExpression(); } - return ParseMemberExpressionContinuation(result, is_async, ok); + return ParseMemberExpressionContinuation(result); } template <typename Impl> -typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseImportExpressions( - bool* ok) { +typename ParserBase<Impl>::ExpressionT +ParserBase<Impl>::ParseImportExpressions() { DCHECK(allow_harmony_dynamic_import()); - classifier()->RecordPatternError(scanner()->peek_location(), - MessageTemplate::kUnexpectedToken, - Token::String(Token::IMPORT)); - Consume(Token::IMPORT); int pos = position(); if (allow_harmony_import_meta() && peek() == Token::PERIOD) { - ExpectMetaProperty(Token::META, "import.meta", pos, CHECK_OK); + ExpectMetaProperty(ast_value_factory()->meta_string(), "import.meta", pos); if (!parsing_module_) { impl()->ReportMessageAt(scanner()->location(), MessageTemplate::kImportMetaOutsideModule); - *ok = false; - return impl()->NullExpression(); + return impl()->FailureExpression(); } return impl()->ImportMetaExpression(pos); } - Expect(Token::LPAREN, CHECK_OK); + Expect(Token::LPAREN); if (peek() == Token::RPAREN) { impl()->ReportMessageAt(scanner()->location(), MessageTemplate::kImportMissingSpecifier); - *ok = false; - return impl()->NullExpression(); + return impl()->FailureExpression(); } - ExpressionT arg = ParseAssignmentExpression(true, CHECK_OK); - Expect(Token::RPAREN, CHECK_OK); + AcceptINScope scope(this, true); + ExpressionT arg = ParseAssignmentExpressionCoverGrammar(); + Expect(Token::RPAREN); + return factory()->NewImportCallExpression(arg, pos); } template <typename Impl> typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseSuperExpression( - bool is_new, bool* ok) { - Expect(Token::SUPER, CHECK_OK); + bool is_new) { + Consume(Token::SUPER); int pos = position(); DeclarationScope* scope = GetReceiverScope(); FunctionKind kind = scope->function_kind(); if (IsConciseMethod(kind) || IsAccessorFunction(kind) || IsClassConstructor(kind)) { - if (peek() == Token::PERIOD || peek() == Token::LBRACK) { + if (Token::IsProperty(peek())) { scope->RecordSuperPropertyUsage(); return impl()->NewSuperPropertyReference(pos); } @@ -3665,39 +3281,31 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseSuperExpression( impl()->ReportMessageAt(scanner()->location(), MessageTemplate::kUnexpectedSuper); - *ok = false; - return impl()->NullExpression(); + return impl()->FailureExpression(); } template <typename Impl> -void ParserBase<Impl>::ExpectMetaProperty(Token::Value property_name, - const char* full_name, int pos, - bool* ok) { +void ParserBase<Impl>::ExpectMetaProperty(const AstRawString* property_name, + const char* full_name, int pos) { Consume(Token::PERIOD); - ExpectContextualKeyword(property_name, CHECK_OK_CUSTOM(Void)); - if (scanner()->literal_contains_escapes()) { + ExpectContextualKeyword(property_name); + if (V8_UNLIKELY(scanner()->literal_contains_escapes())) { impl()->ReportMessageAt(Scanner::Location(pos, end_position()), MessageTemplate::kInvalidEscapedMetaProperty, full_name); - *ok = false; } } template <typename Impl> typename ParserBase<Impl>::ExpressionT -ParserBase<Impl>::ParseNewTargetExpression(bool* ok) { +ParserBase<Impl>::ParseNewTargetExpression() { int pos = position(); - ExpectMetaProperty(Token::TARGET, "new.target", pos, CHECK_OK); - - classifier()->RecordAssignmentPatternError( - Scanner::Location(pos, end_position()), - MessageTemplate::kInvalidDestructuringTarget); + ExpectMetaProperty(ast_value_factory()->target_string(), "new.target", pos); if (!GetReceiverScope()->is_function_scope()) { impl()->ReportMessageAt(scanner()->location(), MessageTemplate::kUnexpectedNewTarget); - *ok = false; - return impl()->NullExpression(); + return impl()->FailureExpression(); } return impl()->NewTargetExpression(pos); @@ -3705,45 +3313,31 @@ ParserBase<Impl>::ParseNewTargetExpression(bool* ok) { template <typename Impl> typename ParserBase<Impl>::ExpressionT -ParserBase<Impl>::ParseMemberExpressionContinuation(ExpressionT expression, - bool* is_async, bool* ok) { +ParserBase<Impl>::DoParseMemberExpressionContinuation(ExpressionT expression) { + DCHECK(Token::IsMember(peek())); // Parses this part of MemberExpression: // ('[' Expression ']' | '.' Identifier | TemplateLiteral)* - while (true) { + do { switch (peek()) { case Token::LBRACK: { - *is_async = false; - ValidateExpression(CHECK_OK); - BindingPatternUnexpectedToken(); - ArrowFormalParametersUnexpectedToken(); - Consume(Token::LBRACK); int pos = position(); - ExpressionT index = ParseExpressionCoverGrammar(true, CHECK_OK); - ValidateExpression(CHECK_OK); + AcceptINScope scope(this, true); + ExpressionT index = ParseExpressionCoverGrammar(); expression = factory()->NewProperty(expression, index, pos); impl()->PushPropertyName(index); - Expect(Token::RBRACK, CHECK_OK); + Expect(Token::RBRACK); break; } case Token::PERIOD: { - *is_async = false; - ValidateExpression(CHECK_OK); - BindingPatternUnexpectedToken(); - ArrowFormalParametersUnexpectedToken(); - Consume(Token::PERIOD); int pos = peek_position(); - ExpressionT key = ParseIdentifierNameOrPrivateName(CHECK_OK); + ExpressionT key = ParsePropertyOrPrivatePropertyName(); expression = factory()->NewProperty(expression, key, pos); break; } - case Token::TEMPLATE_SPAN: - case Token::TEMPLATE_TAIL: { - *is_async = false; - ValidateExpression(CHECK_OK); - BindingPatternUnexpectedToken(); - ArrowFormalParametersUnexpectedToken(); + default: { + DCHECK(Token::IsTemplate(peek())); int pos; if (scanner()->current_token() == Token::IDENTIFIER) { pos = position(); @@ -3755,61 +3349,47 @@ ParserBase<Impl>::ParseMemberExpressionContinuation(ExpressionT expression, expression->AsFunctionLiteral()->SetShouldEagerCompile(); } } - expression = ParseTemplateLiteral(expression, pos, true, CHECK_OK); + expression = ParseTemplateLiteral(expression, pos, true); break; } - case Token::ILLEGAL: { - ReportUnexpectedTokenAt(scanner()->peek_location(), Token::ILLEGAL); - *ok = false; - return impl()->NullExpression(); - } - default: - return expression; } - } - DCHECK(false); - return impl()->NullExpression(); + } while (Token::IsMember(peek())); + return expression; } template <typename Impl> -void ParserBase<Impl>::ParseFormalParameter(FormalParametersT* parameters, - bool* ok) { +void ParserBase<Impl>::ParseFormalParameter(FormalParametersT* parameters) { // FormalParameter[Yield,GeneratorParameter] : // BindingElement[?Yield, ?GeneratorParameter] - bool is_rest = parameters->has_rest; - FuncNameInferrerState fni_state(&fni_); - ExpressionT pattern = ParseBindingPattern(CHECK_OK_CUSTOM(Void)); - if (!impl()->IsIdentifier(pattern)) { + int pos = peek_position(); + ExpressionT pattern = ParseBindingPattern(); + if (impl()->IsIdentifier(pattern)) { + ClassifyParameter(impl()->AsIdentifier(pattern), pos, end_position()); + } else { parameters->is_simple = false; - ValidateFormalParameterInitializer(CHECK_OK_CUSTOM(Void)); } ExpressionT initializer = impl()->NullExpression(); if (Check(Token::ASSIGN)) { - if (is_rest) { + parameters->is_simple = false; + + if (parameters->has_rest) { ReportMessage(MessageTemplate::kRestDefaultInitializer); - *ok = false; return; } - ExpressionClassifier init_classifier(this); - initializer = ParseAssignmentExpression(true, CHECK_OK_CUSTOM(Void)); - ValidateExpression(CHECK_OK_CUSTOM(Void)); - ValidateFormalParameterInitializer(CHECK_OK_CUSTOM(Void)); - parameters->is_simple = false; - DiscardExpressionClassifier(); - classifier()->RecordNonSimpleParameter(); + AcceptINScope scope(this, true); + initializer = ParseAssignmentExpression(); impl()->SetFunctionNameFromIdentifierRef(initializer, pattern); } impl()->AddFormalParameter(parameters, pattern, initializer, end_position(), - is_rest); + parameters->has_rest); } template <typename Impl> -void ParserBase<Impl>::ParseFormalParameterList(FormalParametersT* parameters, - bool* ok) { +void ParserBase<Impl>::ParseFormalParameterList(FormalParametersT* parameters) { // FormalParameters[Yield] : // [empty] // FunctionRestParameter[?Yield] @@ -3820,26 +3400,25 @@ void ParserBase<Impl>::ParseFormalParameterList(FormalParametersT* parameters, // FormalParameterList[Yield] : // FormalParameter[?Yield] // FormalParameterList[?Yield] , FormalParameter[?Yield] + ParameterParsingScope scope(impl(), parameters); DCHECK_EQ(0, parameters->arity); if (peek() != Token::RPAREN) { while (true) { - if (parameters->arity > Code::kMaxArguments) { + // Add one since we're going to be adding a parameter. + if (parameters->arity + 1 > Code::kMaxArguments) { ReportMessage(MessageTemplate::kTooManyParameters); - *ok = false; return; } parameters->has_rest = Check(Token::ELLIPSIS); - ParseFormalParameter(parameters, CHECK_OK_CUSTOM(Void)); + ParseFormalParameter(parameters); if (parameters->has_rest) { parameters->is_simple = false; - classifier()->RecordNonSimpleParameter(); if (peek() == Token::COMMA) { impl()->ReportMessageAt(scanner()->peek_location(), MessageTemplate::kParamAfterRest); - *ok = false; return; } break; @@ -3852,15 +3431,14 @@ void ParserBase<Impl>::ParseFormalParameterList(FormalParametersT* parameters, } } - impl()->DeclareFormalParameters(parameters->scope, parameters->params, - parameters->is_simple); + impl()->DeclareFormalParameters(parameters); } template <typename Impl> -typename ParserBase<Impl>::BlockT ParserBase<Impl>::ParseVariableDeclarations( +void ParserBase<Impl>::ParseVariableDeclarations( VariableDeclarationContext var_context, DeclarationParsingResult* parsing_result, - ZonePtrList<const AstRawString>* names, bool* ok) { + ZonePtrList<const AstRawString>* names) { // VariableDeclarations :: // ('var' | 'const' | 'let') (Identifier ('=' AssignmentExpression)?)+[','] // @@ -3869,15 +3447,10 @@ typename ParserBase<Impl>::BlockT ParserBase<Impl>::ParseVariableDeclarations( // declaration syntax. DCHECK_NOT_NULL(parsing_result); - parsing_result->descriptor.declaration_kind = DeclarationDescriptor::NORMAL; + parsing_result->descriptor.kind = NORMAL_VARIABLE; parsing_result->descriptor.declaration_pos = peek_position(); parsing_result->descriptor.initialization_pos = peek_position(); - BlockT init_block = impl()->NullStatement(); - if (var_context != kForStatement) { - init_block = factory()->NewBlock(1, true); - } - switch (peek()) { case Token::VAR: parsing_result->descriptor.mode = VariableMode::kVar; @@ -3898,40 +3471,29 @@ typename ParserBase<Impl>::BlockT ParserBase<Impl>::ParseVariableDeclarations( break; } - parsing_result->descriptor.scope = scope(); + VariableDeclarationParsingScope declaration( + impl(), parsing_result->descriptor.mode, names); int bindings_start = peek_position(); do { // Parse binding pattern. FuncNameInferrerState fni_state(&fni_); - ExpressionT pattern = impl()->NullExpression(); int decl_pos = peek_position(); - { - ExpressionClassifier pattern_classifier(this); - pattern = ParseBindingPattern(CHECK_OK_CUSTOM(NullStatement)); + ExpressionT pattern = ParseBindingPattern(); - if (IsLexicalVariableMode(parsing_result->descriptor.mode)) { - ValidateLetPattern(CHECK_OK_CUSTOM(NullStatement)); - } - } Scanner::Location variable_loc = scanner()->location(); - bool single_name = impl()->IsIdentifier(pattern); - if (single_name) { - impl()->PushVariableName(impl()->AsIdentifier(pattern)); - } - ExpressionT value = impl()->NullExpression(); int initializer_position = kNoSourcePosition; int value_beg_position = kNoSourcePosition; if (Check(Token::ASSIGN)) { value_beg_position = peek_position(); - ExpressionClassifier classifier(this); - value = ParseAssignmentExpression(var_context != kForStatement, - CHECK_OK_CUSTOM(NullStatement)); - ValidateExpression(CHECK_OK_CUSTOM(NullStatement)); + { + AcceptINScope scope(this, var_context != kForStatement); + value = ParseAssignmentExpression(); + } variable_loc.end_pos = end_position(); if (!parsing_result->first_initializer_loc.IsValid()) { @@ -3939,7 +3501,7 @@ typename ParserBase<Impl>::BlockT ParserBase<Impl>::ParseVariableDeclarations( } // Don't infer if it is "a = function(){...}();"-like expression. - if (single_name) { + if (impl()->IsIdentifier(pattern)) { if (!value->IsCall() && !value->IsCallNew()) { fni_.Infer(); } else { @@ -3960,8 +3522,7 @@ typename ParserBase<Impl>::BlockT ParserBase<Impl>::ParseVariableDeclarations( Scanner::Location(decl_pos, end_position()), MessageTemplate::kDeclarationMissingInitializer, !impl()->IsIdentifier(pattern) ? "destructuring" : "const"); - *ok = false; - return impl()->NullStatement(); + return; } // 'let x' initializes 'x' to undefined. if (parsing_result->descriptor.mode == VariableMode::kLet) { @@ -3976,61 +3537,50 @@ typename ParserBase<Impl>::BlockT ParserBase<Impl>::ParseVariableDeclarations( typename DeclarationParsingResult::Declaration decl( pattern, initializer_position, value); decl.value_beg_position = value_beg_position; - if (var_context == kForStatement) { - // Save the declaration for further handling in ParseForStatement. - parsing_result->declarations.push_back(decl); - } else { - // Immediately declare the variable otherwise. This avoids O(N^2) - // behavior (where N is the number of variables in a single - // declaration) in the PatternRewriter having to do with removing - // and adding VariableProxies to the Scope (see bug 4699). - impl()->DeclareAndInitializeVariables( - init_block, &parsing_result->descriptor, &decl, names, - CHECK_OK_CUSTOM(NullStatement)); - } + parsing_result->declarations.push_back(decl); } while (Check(Token::COMMA)); parsing_result->bindings_loc = Scanner::Location(bindings_start, end_position()); - - DCHECK(*ok); - return init_block; } template <typename Impl> typename ParserBase<Impl>::StatementT -ParserBase<Impl>::ParseFunctionDeclaration(bool* ok) { +ParserBase<Impl>::ParseFunctionDeclaration() { Consume(Token::FUNCTION); + int pos = position(); ParseFunctionFlags flags = ParseFunctionFlag::kIsNormal; if (Check(Token::MUL)) { impl()->ReportMessageAt( scanner()->location(), MessageTemplate::kGeneratorInSingleStatementContext); - *ok = false; return impl()->NullStatement(); } - return ParseHoistableDeclaration(pos, flags, nullptr, false, ok); + return ParseHoistableDeclaration(pos, flags, nullptr, false); } template <typename Impl> typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseHoistableDeclaration( - ZonePtrList<const AstRawString>* names, bool default_export, bool* ok) { - Expect(Token::FUNCTION, CHECK_OK_CUSTOM(NullStatement)); + ZonePtrList<const AstRawString>* names, bool default_export) { + Consume(Token::FUNCTION); + int pos = position(); ParseFunctionFlags flags = ParseFunctionFlag::kIsNormal; if (Check(Token::MUL)) { flags |= ParseFunctionFlag::kIsGenerator; } - return ParseHoistableDeclaration(pos, flags, names, default_export, ok); + return ParseHoistableDeclaration(pos, flags, names, default_export); } template <typename Impl> typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseHoistableDeclaration( int pos, ParseFunctionFlags flags, ZonePtrList<const AstRawString>* names, - bool default_export, bool* ok) { + bool default_export) { + CheckStackOverflow(); + // FunctionDeclaration :: // 'function' Identifier '(' FormalParameters ')' '{' FunctionBody '}' // 'function' '(' FormalParameters ')' '{' FunctionBody '}' @@ -4057,10 +3607,8 @@ ParserBase<Impl>::ParseHoistableDeclaration( impl()->GetDefaultStrings(&name, &variable_name); name_validity = kSkipFunctionNameCheck; } else { - bool is_strict_reserved; - bool is_await = false; - name = ParseIdentifierOrStrictReservedWord(&is_strict_reserved, &is_await, - CHECK_OK_CUSTOM(NullStatement)); + bool is_strict_reserved = Token::IsStrictReservedWord(peek()); + name = ParseIdentifier(); name_validity = is_strict_reserved ? kFunctionNameIsStrictReserved : kFunctionNameValidityUnknown; variable_name = name; @@ -4073,8 +3621,7 @@ ParserBase<Impl>::ParseHoistableDeclaration( FunctionLiteralT function = impl()->ParseFunctionLiteral( name, scanner()->location(), name_validity, kind, pos, - FunctionLiteral::kDeclaration, language_mode(), nullptr, - CHECK_OK_CUSTOM(NullStatement)); + FunctionLiteral::kDeclaration, language_mode(), nullptr); // In ES6, a function behaves as a lexical binding, except in // a script scope, or the initial scope of eval or another function. @@ -4092,12 +3639,13 @@ ParserBase<Impl>::ParseHoistableDeclaration( flags == ParseFunctionFlag::kIsNormal; return impl()->DeclareFunction(variable_name, function, mode, pos, - is_sloppy_block_function, names, ok); + end_position(), is_sloppy_block_function, + names); } template <typename Impl> typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseClassDeclaration( - ZonePtrList<const AstRawString>* names, bool default_export, bool* ok) { + ZonePtrList<const AstRawString>* names, bool default_export) { // ClassDeclaration :: // 'class' Identifier ('extends' LeftHandExpression)? '{' ClassBody '}' // 'class' ('extends' LeftHandExpression)? '{' ClassBody '}' @@ -4118,24 +3666,22 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseClassDeclaration( int class_token_pos = position(); IdentifierT name = impl()->NullIdentifier(); - bool is_strict_reserved = false; + bool is_strict_reserved = Token::IsStrictReservedWord(peek()); IdentifierT variable_name = impl()->NullIdentifier(); if (default_export && (peek() == Token::EXTENDS || peek() == Token::LBRACE)) { impl()->GetDefaultStrings(&name, &variable_name); } else { - bool is_await = false; - name = ParseIdentifierOrStrictReservedWord(&is_strict_reserved, &is_await, - CHECK_OK_CUSTOM(NullStatement)); + name = ParseIdentifier(); variable_name = name; } - ExpressionClassifier no_classifier(this); - ExpressionT value = - ParseClassLiteral(name, scanner()->location(), is_strict_reserved, - class_token_pos, CHECK_OK_CUSTOM(NullStatement)); + ExpressionParsingScope no_expression_scope(impl()); + ExpressionT value = ParseClassLiteral(name, scanner()->location(), + is_strict_reserved, class_token_pos); + no_expression_scope.ValidateExpression(); int end_pos = position(); return impl()->DeclareClass(variable_name, value, names, class_token_pos, - end_pos, ok); + end_pos); } // Language extension which is only enabled for source files loaded @@ -4143,86 +3689,84 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseClassDeclaration( // declaration is resolved by looking up the function through a // callback provided by the extension. template <typename Impl> -typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseNativeDeclaration( - bool* ok) { +typename ParserBase<Impl>::StatementT +ParserBase<Impl>::ParseNativeDeclaration() { function_state_->DisableOptimization(BailoutReason::kNativeFunctionLiteral); int pos = peek_position(); - Expect(Token::FUNCTION, CHECK_OK_CUSTOM(NullStatement)); + Consume(Token::FUNCTION); // Allow "eval" or "arguments" for backward compatibility. - IdentifierT name = ParseIdentifier(kAllowRestrictedIdentifiers, - CHECK_OK_CUSTOM(NullStatement)); - Expect(Token::LPAREN, CHECK_OK_CUSTOM(NullStatement)); + IdentifierT name = ParseIdentifier(); + Expect(Token::LPAREN); if (peek() != Token::RPAREN) { do { - ParseIdentifier(kAllowRestrictedIdentifiers, - CHECK_OK_CUSTOM(NullStatement)); + ParseIdentifier(); } while (Check(Token::COMMA)); } - Expect(Token::RPAREN, CHECK_OK_CUSTOM(NullStatement)); - Expect(Token::SEMICOLON, CHECK_OK_CUSTOM(NullStatement)); - return impl()->DeclareNative(name, pos, ok); + Expect(Token::RPAREN); + Expect(Token::SEMICOLON); + return impl()->DeclareNative(name, pos); } template <typename Impl> typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseAsyncFunctionDeclaration( - ZonePtrList<const AstRawString>* names, bool default_export, bool* ok) { + ZonePtrList<const AstRawString>* names, bool default_export) { // AsyncFunctionDeclaration :: // async [no LineTerminator here] function BindingIdentifier[Await] // ( FormalParameters[Await] ) { AsyncFunctionBody } DCHECK_EQ(scanner()->current_token(), Token::ASYNC); int pos = position(); - if (scanner()->HasLineTerminatorBeforeNext()) { - *ok = false; - impl()->ReportUnexpectedToken(scanner()->current_token()); - return impl()->NullStatement(); - } - Expect(Token::FUNCTION, CHECK_OK_CUSTOM(NullStatement)); + DCHECK(!scanner()->HasLineTerminatorBeforeNext()); + Consume(Token::FUNCTION); ParseFunctionFlags flags = ParseFunctionFlag::kIsAsync; - return ParseHoistableDeclaration(pos, flags, names, default_export, ok); + return ParseHoistableDeclaration(pos, flags, names, default_export); } 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, FunctionBodyType body_type, - bool accept_IN, bool* ok) { - DeclarationScope* function_scope = scope()->AsDeclarationScope(); + StatementListT* body, IdentifierT function_name, int pos, + const FormalParametersT& parameters, FunctionKind kind, + FunctionLiteral::FunctionType function_type, FunctionBodyType body_type) { + FunctionBodyParsingScope body_parsing_scope(impl()); + + if (IsResumableFunction(kind)) impl()->PrepareGeneratorVariables(); + + DeclarationScope* function_scope = parameters.scope; DeclarationScope* inner_scope = function_scope; - BlockT inner_block = impl()->NullStatement(); - StatementListT body = result; - if (!parameters.is_simple) { + // Building the parameter initialization block declares the parameters. + // TODO(verwaest): Rely on ArrowHeadParsingScope instead. + if (V8_UNLIKELY(!parameters.is_simple)) { + if (has_error()) return; + BlockT init_block = impl()->BuildParameterInitializationBlock(parameters); + if (IsAsyncFunction(kind) && !IsAsyncGeneratorFunction(kind)) { + init_block = impl()->BuildRejectPromiseOnException(init_block); + } + body->Add(init_block); + if (has_error()) return; + inner_scope = NewVarblockScope(); inner_scope->set_start_position(scanner()->location().beg_pos); - inner_block = factory()->NewBlock(8, true); - inner_block->set_scope(inner_scope); - body = inner_block->statements(); } + StatementListT inner_body(pointer_buffer()); + { BlockState block_state(&scope_, inner_scope); - if (IsResumableFunction(kind)) impl()->PrepareGeneratorVariables(); - if (body_type == FunctionBodyType::kExpression) { - ExpressionClassifier classifier(this); - ExpressionT expression = - ParseAssignmentExpression(accept_IN, CHECK_OK_VOID); - ValidateExpression(CHECK_OK_VOID); + ExpressionT expression = ParseAssignmentExpression(); if (IsAsyncFunction(kind)) { BlockT block = factory()->NewBlock(1, true); - impl()->RewriteAsyncFunctionBody(body, block, expression, - CHECK_OK_VOID); + impl()->RewriteAsyncFunctionBody(&inner_body, block, expression); } else { - body->Add(BuildReturnStatement(expression, expression->position()), - zone()); + inner_body.Add( + BuildReturnStatement(expression, expression->position())); } } else { - DCHECK(accept_IN); + 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. @@ -4231,63 +3775,64 @@ void ParserBase<Impl>::ParseFunctionBody( : Token::RBRACE; if (IsAsyncGeneratorFunction(kind)) { - impl()->ParseAndRewriteAsyncGeneratorFunctionBody(pos, kind, body, - CHECK_OK_VOID); + impl()->ParseAndRewriteAsyncGeneratorFunctionBody(pos, kind, + &inner_body); } else if (IsGeneratorFunction(kind)) { - impl()->ParseAndRewriteGeneratorFunctionBody(pos, kind, body, - CHECK_OK_VOID); + impl()->ParseAndRewriteGeneratorFunctionBody(pos, kind, &inner_body); } else if (IsAsyncFunction(kind)) { - ParseAsyncFunctionBody(inner_scope, body, CHECK_OK_VOID); + ParseAsyncFunctionBody(inner_scope, &inner_body); } else { - ParseStatementList(body, closing_token, CHECK_OK_VOID); + ParseStatementList(&inner_body, closing_token); } if (IsDerivedConstructor(kind)) { - body->Add(factory()->NewReturnStatement(impl()->ThisExpression(), - kNoSourcePosition), - zone()); + inner_body.Add(factory()->NewReturnStatement(impl()->ThisExpression(), + kNoSourcePosition)); } - Expect(closing_token, CHECK_OK_VOID); + Expect(closing_token); } } scope()->set_end_position(end_position()); - if (!parameters.is_simple) { + bool allow_duplicate_parameters = false; + + if (V8_LIKELY(parameters.is_simple)) { + DCHECK_EQ(inner_scope, function_scope); + if (is_sloppy(function_scope->language_mode())) { + impl()->InsertSloppyBlockFunctionVarBindings(function_scope); + } + allow_duplicate_parameters = + is_sloppy(function_scope->language_mode()) && !IsConciseMethod(kind); + } else { DCHECK_NOT_NULL(inner_scope); DCHECK_EQ(function_scope, scope()); DCHECK_EQ(function_scope, inner_scope->outer_scope()); impl()->SetLanguageMode(function_scope, inner_scope->language_mode()); - BlockT init_block = - impl()->BuildParameterInitializationBlock(parameters, CHECK_OK_VOID); if (is_sloppy(inner_scope->language_mode())) { impl()->InsertSloppyBlockFunctionVarBindings(inner_scope); } - // TODO(littledan): Merge the two rejection blocks into one - if (IsAsyncFunction(kind) && !IsAsyncGeneratorFunction(kind)) { - init_block = impl()->BuildRejectPromiseOnException(init_block); - } - inner_scope->set_end_position(end_position()); if (inner_scope->FinalizeBlockScope() != nullptr) { - impl()->CheckConflictingVarDeclarations(inner_scope, CHECK_OK_VOID); + BlockT inner_block = factory()->NewBlock(true, inner_body); + inner_body.Rewind(); + inner_body.Add(inner_block); + inner_block->set_scope(inner_scope); + const AstRawString* conflict = inner_scope->FindVariableDeclaredIn( + function_scope, VariableMode::kLastLexicalVariableMode); + if (conflict != nullptr) { + impl()->ReportVarRedeclarationIn(conflict, inner_scope); + } + impl()->CheckConflictingVarDeclarations(inner_scope); impl()->InsertShadowingVarBindingInitializers(inner_block); - } else { - inner_block->set_scope(nullptr); - } - inner_scope = nullptr; - - result->Add(init_block, zone()); - result->Add(inner_block, zone()); - } else { - DCHECK_EQ(inner_scope, function_scope); - if (is_sloppy(function_scope->language_mode())) { - impl()->InsertSloppyBlockFunctionVarBindings(function_scope); } } + ValidateFormalParameters(language_mode(), parameters, + allow_duplicate_parameters); + if (!IsArrowFunction(kind)) { // Declare arguments after parsing the function since lexical 'arguments' // masks the arguments object. Declare arguments before declaring the @@ -4296,6 +3841,8 @@ void ParserBase<Impl>::ParseFunctionBody( } impl()->DeclareFunctionNameVar(function_name, function_type, function_scope); + + inner_body.MergeInto(body); } template <typename Impl> @@ -4303,26 +3850,23 @@ void ParserBase<Impl>::CheckArityRestrictions(int param_count, FunctionKind function_kind, bool has_rest, int formals_start_pos, - int formals_end_pos, bool* ok) { + int formals_end_pos) { if (IsGetterFunction(function_kind)) { if (param_count != 0) { impl()->ReportMessageAt( Scanner::Location(formals_start_pos, formals_end_pos), MessageTemplate::kBadGetterArity); - *ok = false; } } else if (IsSetterFunction(function_kind)) { if (param_count != 1) { impl()->ReportMessageAt( Scanner::Location(formals_start_pos, formals_end_pos), MessageTemplate::kBadSetterArity); - *ok = false; } if (has_rest) { impl()->ReportMessageAt( Scanner::Location(formals_start_pos, formals_end_pos), MessageTemplate::kBadSetterRestParameter); - *ok = false; } } } @@ -4353,25 +3897,9 @@ bool ParserBase<Impl>::IsNextLetKeyword() { } template <typename Impl> -bool ParserBase<Impl>::IsTrivialExpression() { - 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 || - Token::IsAssignmentOp(peek_ahead)) { - return true; - } - } - return false; -} - -template <typename Impl> typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseArrowFunctionLiteral( - bool accept_IN, const FormalParametersT& formal_parameters, - int rewritable_length, bool* ok) { + const FormalParametersT& formal_parameters) { const RuntimeCallCounterId counters[2][2] = { {RuntimeCallCounterId::kParseBackgroundArrowFunctionLiteral, RuntimeCallCounterId::kParseArrowFunctionLiteral}, @@ -4383,16 +3911,15 @@ ParserBase<Impl>::ParseArrowFunctionLiteral( base::ElapsedTimer timer; if (V8_UNLIKELY(FLAG_log_function_events)) timer.Start(); - if (peek() == Token::ARROW && scanner_->HasLineTerminatorBeforeNext()) { + DCHECK_IMPLIES(!has_error(), peek() == Token::ARROW); + if (scanner_->HasLineTerminatorBeforeNext()) { // ASI inserts `;` after arrow parameters if a line terminator is found. // `=> ...` is never a valid expression, so report as syntax error. // If next token is not `=>`, it's a syntax error anyways. - ReportUnexpectedTokenAt(scanner_->peek_location(), Token::ARROW); - *ok = false; - return impl()->NullExpression(); + impl()->ReportUnexpectedTokenAt(scanner_->peek_location(), Token::ARROW); + return impl()->FailureExpression(); } - StatementListT body = impl()->NullStatementList(); int expected_property_count = -1; int suspend_count = 0; int function_literal_id = GetNextFunctionLiteralId(); @@ -4407,93 +3934,102 @@ ParserBase<Impl>::ParseArrowFunctionLiteral( bool is_lazy_top_level_function = can_preparse && impl()->AllowsLazyParsingWithoutUnresolvedVariables(); bool has_braces = true; - ProducedPreParsedScopeData* produced_preparsed_scope_data = nullptr; + ProducedPreparseData* produced_preparse_data = nullptr; + StatementListT body(pointer_buffer()); { FunctionState function_state(&function_state_, &scope_, formal_parameters.scope); - // Move any queued destructuring assignments which appeared - // in this function's parameter list into its own function_state. - function_state.AdoptDestructuringAssignmentsFromParentState( - rewritable_length); - - Expect(Token::ARROW, CHECK_OK); + Consume(Token::ARROW); if (peek() == Token::LBRACE) { // Multiple statement body DCHECK_EQ(scope(), formal_parameters.scope); + if (is_lazy_top_level_function) { // FIXME(marja): Arrow function parameters will be parsed even if the // body is preparsed; move relevant parts of parameter handling to // simulate consistent parameter handling. + // Building the parameter initialization block declares the parameters. + // TODO(verwaest): Rely on ArrowHeadParsingScope instead. + if (!formal_parameters.is_simple) { + impl()->BuildParameterInitializationBlock(formal_parameters); + if (has_error()) return impl()->FailureExpression(); + } + // For arrow functions, we don't need to retrieve data about function // parameters. int dummy_num_parameters = -1; DCHECK_NE(kind & FunctionKind::kArrowFunction, 0); - 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, &hint, CHECK_OK); + &produced_preparse_data); - DCHECK_NULL(produced_preparsed_scope_data); + DCHECK_NULL(produced_preparse_data); 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); + // Validate parameter names. We can do this only after preparsing the + // function, since the function can declare itself strict. + ValidateFormalParameters(language_mode(), formal_parameters, false); } else { // In case we did not sucessfully preparse the function because of an // unidentified error we do a full reparse to return the error. + // Parse again in the outer scope, since the language mode may change. + BlockState block_state(&scope_, scope()->outer_scope()); + ExpressionT expression = ParseConditionalExpression(); + // Reparsing the head may have caused a stack overflow. + if (has_error()) return impl()->FailureExpression(); + + DeclarationScope* function_scope = next_arrow_function_info_.scope; + FunctionState function_state(&function_state_, &scope_, + function_scope); + Scanner::Location loc(function_scope->start_position(), + end_position()); + FormalParametersT parameters(function_scope); + parameters.is_simple = function_scope->has_simple_parameters(); + impl()->DeclareArrowFunctionFormalParameters(¶meters, expression, + loc); + next_arrow_function_info_.Reset(); + + Consume(Token::ARROW); Consume(Token::LBRACE); - body = impl()->NewStatementList(8); - ParseFunctionBody(body, impl()->NullIdentifier(), kNoSourcePosition, - formal_parameters, kind, + + AcceptINScope scope(this, true); + ParseFunctionBody(&body, impl()->NullIdentifier(), kNoSourcePosition, + parameters, kind, FunctionLiteral::kAnonymousExpression, - FunctionBodyType::kBlock, true, ok); - CHECK(!*ok); - return impl()->NullExpression(); + FunctionBodyType::kBlock); + CHECK(has_error()); + return impl()->FailureExpression(); } } else { Consume(Token::LBRACE); - body = impl()->NewStatementList(8); - ParseFunctionBody(body, impl()->NullIdentifier(), kNoSourcePosition, + AcceptINScope scope(this, true); + ParseFunctionBody(&body, impl()->NullIdentifier(), kNoSourcePosition, formal_parameters, kind, FunctionLiteral::kAnonymousExpression, - FunctionBodyType::kBlock, true, CHECK_OK); + FunctionBodyType::kBlock); expected_property_count = function_state.expected_property_count(); } } else { // Single-expression body has_braces = false; - body = impl()->NewStatementList(1); - ParseFunctionBody(body, impl()->NullIdentifier(), kNoSourcePosition, + ParseFunctionBody(&body, impl()->NullIdentifier(), kNoSourcePosition, formal_parameters, kind, FunctionLiteral::kAnonymousExpression, - FunctionBodyType::kExpression, accept_IN, CHECK_OK); + FunctionBodyType::kExpression); expected_property_count = function_state.expected_property_count(); } 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 - // that duplicates are not allowed. Of course, the arrow function may - // itself be strict as well. - const bool allow_duplicate_parameters = false; - ValidateFormalParameters(language_mode(), allow_duplicate_parameters, - CHECK_OK); - // Validate strict mode. if (is_strict(language_mode())) { CheckStrictOctalLiteral(formal_parameters.scope->start_position(), - end_position(), CHECK_OK); + end_position()); } - impl()->CheckConflictingVarDeclarations(formal_parameters.scope, CHECK_OK); - - impl()->RewriteDestructuringAssignments(); suspend_count = function_state.suspend_count(); } @@ -4504,7 +4040,7 @@ ParserBase<Impl>::ParseArrowFunctionLiteral( FunctionLiteral::kNoDuplicateParameters, FunctionLiteral::kAnonymousExpression, eager_compile_hint, formal_parameters.scope->start_position(), has_braces, - function_literal_id, produced_preparsed_scope_data); + function_literal_id, produced_preparse_data); function_literal->set_suspend_count(suspend_count); function_literal->set_function_token_position( @@ -4528,7 +4064,7 @@ ParserBase<Impl>::ParseArrowFunctionLiteral( template <typename Impl> typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseClassLiteral( IdentifierT name, Scanner::Location class_name_location, - bool name_is_strict_reserved, int class_token_pos, bool* ok) { + bool name_is_strict_reserved, int class_token_pos) { bool is_anonymous = impl()->IsNull(name); // All parts of a ClassDeclaration and ClassExpression are strict code. @@ -4536,14 +4072,12 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseClassLiteral( if (name_is_strict_reserved) { impl()->ReportMessageAt(class_name_location, MessageTemplate::kUnexpectedStrictReserved); - *ok = false; - return impl()->NullExpression(); + return impl()->FailureExpression(); } if (impl()->IsEvalOrArguments(name)) { impl()->ReportMessageAt(class_name_location, MessageTemplate::kStrictEvalArguments); - *ok = false; - return impl()->NullExpression(); + return impl()->FailureExpression(); } } @@ -4553,77 +4087,79 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseClassLiteral( ClassInfo class_info(this); class_info.is_anonymous = is_anonymous; - impl()->DeclareClassVariable(name, &class_info, class_token_pos, CHECK_OK); + impl()->DeclareClassVariable(name, &class_info, class_token_pos); scope()->set_start_position(end_position()); if (Check(Token::EXTENDS)) { FuncNameInferrerState fni_state(&fni_); - ExpressionClassifier extends_classifier(this); - class_info.extends = ParseLeftHandSideExpression(CHECK_OK); - ValidateExpression(CHECK_OK); - AccumulateFormalParameterContainmentErrors(); + ExpressionParsingScope scope(impl()); + class_info.extends = ParseLeftHandSideExpression(); + scope.ValidateExpression(); } - ClassLiteralChecker checker(this); - - Expect(Token::LBRACE, CHECK_OK); + Expect(Token::LBRACE); const bool has_extends = !impl()->IsNull(class_info.extends); while (peek() != Token::RBRACE) { if (Check(Token::SEMICOLON)) continue; FuncNameInferrerState fni_state(&fni_); - bool is_computed_name = false; // Classes do not care about computed - // property names here. - bool is_static; - ClassLiteralProperty::Kind property_kind; - ExpressionClassifier property_classifier(this); - IdentifierT property_name; // If we haven't seen the constructor yet, it potentially is the next // property. bool is_constructor = !class_info.has_seen_constructor; - ClassLiteralPropertyT property = ParseClassPropertyDefinition( - &checker, &class_info, &property_name, has_extends, &is_computed_name, - &property_kind, &is_static, CHECK_OK); - if (!class_info.has_static_computed_names && is_static && - is_computed_name) { + ParsePropertyInfo prop_info(this); + prop_info.position = PropertyPosition::kClassLiteral; + ClassLiteralPropertyT property = + ParseClassPropertyDefinition(&class_info, &prop_info, has_extends); + + if (has_error()) return impl()->FailureExpression(); + + ClassLiteralProperty::Kind property_kind = + ClassPropertyKindFor(prop_info.kind); + if (!class_info.has_static_computed_names && prop_info.is_static && + prop_info.is_computed_name) { class_info.has_static_computed_names = true; } - if (is_computed_name && - property_kind == ClassLiteralProperty::PUBLIC_FIELD) { - class_info.computed_field_count++; - } is_constructor &= class_info.has_seen_constructor; - ValidateExpression(CHECK_OK); - AccumulateFormalParameterContainmentErrors(); - impl()->DeclareClassProperty(name, property, property_name, property_kind, - is_static, is_constructor, is_computed_name, - &class_info, CHECK_OK); + if (V8_UNLIKELY(property_kind == ClassLiteralProperty::FIELD)) { + if (prop_info.is_computed_name) { + DCHECK(!prop_info.is_private); + class_info.computed_field_count++; + } + + impl()->DeclareClassField(property, prop_info.name, prop_info.is_static, + prop_info.is_computed_name, + prop_info.is_private, &class_info); + } else { + impl()->DeclareClassProperty(name, property, is_constructor, &class_info); + } impl()->InferFunctionName(); } - Expect(Token::RBRACE, CHECK_OK); + Expect(Token::RBRACE); 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); + class_token_pos, end_pos); } template <typename Impl> -void ParserBase<Impl>::ParseAsyncFunctionBody(Scope* scope, StatementListT body, - bool* ok) { - BlockT block = factory()->NewBlock(8, true); - - ParseStatementList(block->statements(), Token::RBRACE, CHECK_OK_VOID); +void ParserBase<Impl>::ParseAsyncFunctionBody(Scope* scope, + StatementListT* body) { + BlockT block = impl()->NullBlock(); + { + StatementListT statements(pointer_buffer()); + ParseStatementList(&statements, Token::RBRACE); + block = factory()->NewBlock(true, statements); + } impl()->RewriteAsyncFunctionBody( - body, block, factory()->NewUndefinedLiteral(kNoSourcePosition), - CHECK_OK_VOID); + body, block, factory()->NewUndefinedLiteral(kNoSourcePosition)); scope->set_end_position(end_position()); } template <typename Impl> typename ParserBase<Impl>::ExpressionT -ParserBase<Impl>::ParseAsyncFunctionLiteral(bool* ok) { +ParserBase<Impl>::ParseAsyncFunctionLiteral() { // AsyncFunctionLiteral :: // async [no LineTerminator here] function ( FormalParameters[Await] ) // { AsyncFunctionBody } @@ -4632,43 +4168,41 @@ ParserBase<Impl>::ParseAsyncFunctionLiteral(bool* ok) { // ( FormalParameters[Await] ) { AsyncFunctionBody } DCHECK_EQ(scanner()->current_token(), Token::ASYNC); int pos = position(); - Expect(Token::FUNCTION, CHECK_OK); - bool is_strict_reserved = false; + Consume(Token::FUNCTION); IdentifierT name = impl()->NullIdentifier(); FunctionLiteral::FunctionType type = FunctionLiteral::kAnonymousExpression; ParseFunctionFlags flags = ParseFunctionFlag::kIsAsync; if (Check(Token::MUL)) flags |= ParseFunctionFlag::kIsGenerator; const FunctionKind kind = FunctionKindFor(flags); + bool is_strict_reserved = Token::IsStrictReservedWord(peek()); if (impl()->ParsingDynamicFunctionDeclaration()) { // We don't want dynamic functions to actually declare their name // "anonymous". We just want that name in the toString(). - if (stack_overflow()) { - *ok = false; - return impl()->NullExpression(); - } + + // Consuming token we did not peek yet, which could lead to a ILLEGAL token + // in the case of a stackoverflow. Consume(Token::IDENTIFIER); - DCHECK(scanner()->CurrentMatchesContextual(Token::ANONYMOUS)); + DCHECK_IMPLIES(!has_error(), + scanner()->CurrentSymbol(ast_value_factory()) == + ast_value_factory()->anonymous_string()); } else if (peek_any_identifier()) { type = FunctionLiteral::kNamedExpression; - bool is_await = false; - name = ParseIdentifierOrStrictReservedWord(kind, &is_strict_reserved, - &is_await, CHECK_OK); - // If the function name is "await", ParseIdentifierOrStrictReservedWord - // recognized the error. - DCHECK(!is_await); - } - return impl()->ParseFunctionLiteral( + name = ParseIdentifier(kind); + } + FunctionLiteralT result = impl()->ParseFunctionLiteral( name, scanner()->location(), is_strict_reserved ? kFunctionNameIsStrictReserved : kFunctionNameValidityUnknown, - kind, pos, type, language_mode(), nullptr, ok); + kind, pos, type, language_mode(), nullptr); + if (impl()->IsNull(result)) return impl()->FailureExpression(); + return result; } template <typename Impl> typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseTemplateLiteral( - ExpressionT tag, int start, bool tagged, bool* ok) { + ExpressionT tag, int start, bool tagged) { // A TemplateLiteral is made up of 0 or more TEMPLATE_SPAN tokens (literal // text followed by a substitution expression), finalized by a single // TEMPLATE_TAIL. @@ -4696,7 +4230,7 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseTemplateLiteral( Consume(Token::TEMPLATE_TAIL); int pos = position(); typename Impl::TemplateLiteralState ts = impl()->OpenTemplateLiteral(pos); - bool is_valid = CheckTemplateEscapes(forbid_illegal_escapes, CHECK_OK); + bool is_valid = CheckTemplateEscapes(forbid_illegal_escapes); impl()->AddTemplateSpan(&ts, is_valid, true); return impl()->CloseTemplateLiteral(&ts, start, tag); } @@ -4704,7 +4238,7 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseTemplateLiteral( Consume(Token::TEMPLATE_SPAN); int pos = position(); typename Impl::TemplateLiteralState ts = impl()->OpenTemplateLiteral(pos); - bool is_valid = CheckTemplateEscapes(forbid_illegal_escapes, CHECK_OK); + bool is_valid = CheckTemplateEscapes(forbid_illegal_escapes); impl()->AddTemplateSpan(&ts, is_valid, false); Token::Value next; @@ -4714,29 +4248,16 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseTemplateLiteral( do { next = peek(); - if (next == Token::EOS) { - impl()->ReportMessageAt(Scanner::Location(start, peek_position()), - MessageTemplate::kUnterminatedTemplate); - *ok = false; - return impl()->NullExpression(); - } else if (next == Token::ILLEGAL) { - impl()->ReportMessageAt( - Scanner::Location(position() + 1, peek_position()), - MessageTemplate::kUnexpectedToken, "ILLEGAL", kSyntaxError); - *ok = false; - return impl()->NullExpression(); - } int expr_pos = peek_position(); - ExpressionT expression = ParseExpressionCoverGrammar(true, CHECK_OK); - ValidateExpression(CHECK_OK); + AcceptINScope scope(this, true); + ExpressionT expression = ParseExpressionCoverGrammar(); impl()->AddTemplateExpression(&ts, expression); if (peek() != Token::RBRACE) { impl()->ReportMessageAt(Scanner::Location(expr_pos, peek_position()), MessageTemplate::kUnterminatedTemplateExpr); - *ok = false; - return impl()->NullExpression(); + return impl()->FailureExpression(); } // If we didn't die parsing that expression, our next token should be a @@ -4745,53 +4266,34 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseTemplateLiteral( Next(); pos = position(); - if (next == Token::EOS) { - impl()->ReportMessageAt(Scanner::Location(start, pos), - MessageTemplate::kUnterminatedTemplate); - *ok = false; - return impl()->NullExpression(); - } else if (next == Token::ILLEGAL) { - impl()->ReportMessageAt( - Scanner::Location(position() + 1, peek_position()), - MessageTemplate::kUnexpectedToken, "ILLEGAL", kSyntaxError); - *ok = false; - return impl()->NullExpression(); - } - - bool is_valid = CheckTemplateEscapes(forbid_illegal_escapes, CHECK_OK); + bool is_valid = CheckTemplateEscapes(forbid_illegal_escapes); impl()->AddTemplateSpan(&ts, is_valid, next == Token::TEMPLATE_TAIL); } while (next == Token::TEMPLATE_SPAN); - DCHECK_EQ(next, Token::TEMPLATE_TAIL); + DCHECK_IMPLIES(!has_error(), next == Token::TEMPLATE_TAIL); // Once we've reached a TEMPLATE_TAIL, we can close the TemplateLiteral. return impl()->CloseTemplateLiteral(&ts, start, tag); } template <typename Impl> typename ParserBase<Impl>::ExpressionT -ParserBase<Impl>::CheckAndRewriteReferenceExpression( - ExpressionT expression, int beg_pos, int end_pos, - MessageTemplate::Template message, bool* ok) { - return CheckAndRewriteReferenceExpression(expression, beg_pos, end_pos, - message, kReferenceError, ok); -} +ParserBase<Impl>::RewriteInvalidReferenceExpression(ExpressionT expression, + int beg_pos, int end_pos, + MessageTemplate message, + ParseErrorType type) { + DCHECK(!IsValidReferenceExpression(expression)); + if (impl()->IsIdentifier(expression)) { + DCHECK(is_strict(language_mode())); + DCHECK(impl()->IsEvalOrArguments(impl()->AsIdentifier(expression))); -template <typename Impl> -typename ParserBase<Impl>::ExpressionT -ParserBase<Impl>::CheckAndRewriteReferenceExpression( - ExpressionT expression, int beg_pos, int end_pos, - MessageTemplate::Template message, ParseErrorType type, bool* ok) { - if (impl()->IsIdentifier(expression) && is_strict(language_mode()) && - impl()->IsEvalOrArguments(impl()->AsIdentifier(expression))) { ReportMessageAt(Scanner::Location(beg_pos, end_pos), MessageTemplate::kStrictEvalArguments, kSyntaxError); - *ok = false; - return impl()->NullExpression(); - } - if (expression->IsValidReferenceExpression()) { - return expression; + return impl()->FailureExpression(); } if (expression->IsCall() && !expression->AsCall()->is_tagged_template()) { + expression_scope()->RecordPatternError( + Scanner::Location(beg_pos, end_pos), + MessageTemplate::kInvalidDestructuringTarget); // If it is a call, make it a runtime error for legacy web compatibility. // Bug: https://bugs.chromium.org/p/v8/issues/detail?id=4480 // Rewrite `expr' to `expr[throw ReferenceError]'. @@ -4803,8 +4305,35 @@ ParserBase<Impl>::CheckAndRewriteReferenceExpression( return factory()->NewProperty(expression, error, beg_pos); } ReportMessageAt(Scanner::Location(beg_pos, end_pos), message, type); - *ok = false; - return impl()->NullExpression(); + return impl()->FailureExpression(); +} + +template <typename Impl> +void ParserBase<Impl>::ClassifyParameter(IdentifierT parameter, int begin, + int end) { + if (impl()->IsEvalOrArguments(parameter)) { + expression_scope()->RecordStrictModeParameterError( + Scanner::Location(begin, end), MessageTemplate::kStrictEvalArguments); + } +} + +template <typename Impl> +void ParserBase<Impl>::ClassifyArrowParameter( + AccumulationScope* accumulation_scope, int position, + ExpressionT parameter) { + accumulation_scope->Accumulate(); + if (parameter->is_parenthesized() || + !(impl()->IsIdentifier(parameter) || parameter->IsPattern() || + parameter->IsAssignment())) { + expression_scope()->RecordDeclarationError( + Scanner::Location(position, end_position()), + MessageTemplate::kInvalidDestructuringTarget); + } else if (impl()->IsIdentifier(parameter)) { + ClassifyParameter(impl()->AsIdentifier(parameter), position, + end_position()); + } else { + expression_scope()->RecordNonSimpleParameter(); + } } template <typename Impl> @@ -4813,147 +4342,133 @@ bool ParserBase<Impl>::IsValidReferenceExpression(ExpressionT expression) { } template <typename Impl> -void ParserBase<Impl>::CheckDestructuringElement(ExpressionT expression, - int begin, int end) { - if (!IsValidPattern(expression) && !expression->IsAssignment() && - !IsValidReferenceExpression(expression)) { - classifier()->RecordAssignmentPatternError( - Scanner::Location(begin, end), +typename ParserBase<Impl>::ExpressionT +ParserBase<Impl>::ParsePossibleDestructuringSubPattern( + AccumulationScope* scope) { + if (scope) scope->Accumulate(); + int begin = peek_position(); + ExpressionT result = ParseAssignmentExpressionCoverGrammar(); + + if (IsValidReferenceExpression(result)) { + // Parenthesized identifiers and property references are allowed as part of + // a larger assignment pattern, even though parenthesized patterns + // themselves are not allowed, e.g., "[(x)] = []". Only accumulate + // assignment pattern errors if the parsed expression is more complex. + if (impl()->IsIdentifier(result)) { + if (result->is_parenthesized()) { + expression_scope()->RecordDeclarationError( + Scanner::Location(begin, end_position()), + MessageTemplate::kInvalidDestructuringTarget); + } + IdentifierT identifier = impl()->AsIdentifier(result); + ClassifyParameter(identifier, begin, end_position()); + } else { + DCHECK(result->IsProperty()); + expression_scope()->RecordDeclarationError( + Scanner::Location(begin, end_position()), + MessageTemplate::kInvalidPropertyBindingPattern); + if (scope != nullptr) scope->ValidateExpression(); + } + } else if (result->is_parenthesized() || + (!result->IsPattern() && !result->IsAssignment())) { + expression_scope()->RecordPatternError( + Scanner::Location(begin, end_position()), MessageTemplate::kInvalidDestructuringTarget); } + + return result; } template <typename Impl> -typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseV8Intrinsic( - bool* ok) { +typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseV8Intrinsic() { // CallRuntime :: // '%' Identifier Arguments int pos = peek_position(); - Expect(Token::MOD, CHECK_OK); + Consume(Token::MOD); // Allow "eval" or "arguments" for backward compatibility. - IdentifierT name = ParseIdentifier(kAllowRestrictedIdentifiers, CHECK_OK); - Scanner::Location spread_pos; - ExpressionClassifier classifier(this); - ExpressionListT args = ParseArguments(&spread_pos, CHECK_OK); - - if (spread_pos.IsValid()) { - *ok = false; - ReportMessageAt(spread_pos, MessageTemplate::kIntrinsicWithSpread, - kSyntaxError); - return impl()->NullExpression(); + IdentifierT name = ParseIdentifier(); + if (peek() != Token::LPAREN) { + impl()->ReportUnexpectedToken(peek()); + return impl()->FailureExpression(); } + bool has_spread; + ExpressionListT args(pointer_buffer()); + ParseArguments(&args, &has_spread); - return impl()->NewV8Intrinsic(name, args, pos, ok); -} - -template <typename Impl> -typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseDoExpression( - bool* ok) { - // AssignmentExpression :: - // do '{' StatementList '}' + if (has_spread) { + ReportMessageAt(Scanner::Location(pos, position()), + MessageTemplate::kIntrinsicWithSpread, kSyntaxError); + return impl()->FailureExpression(); + } - int pos = peek_position(); - Expect(Token::DO, CHECK_OK); - BlockT block = ParseBlock(nullptr, CHECK_OK); - return impl()->RewriteDoExpression(block, pos, ok); + return impl()->NewV8Intrinsic(name, args, pos); } -// Redefinition of CHECK_OK for parsing statements. -#undef CHECK_OK -#define CHECK_OK CHECK_OK_CUSTOM(NullStatement) - template <typename Impl> -typename ParserBase<Impl>::LazyParsingResult -ParserBase<Impl>::ParseStatementList(StatementListT body, - Token::Value end_token, bool may_abort, - bool* ok) { +void ParserBase<Impl>::ParseStatementList(StatementListT* body, + Token::Value end_token) { // StatementList :: // (StatementListItem)* <end_token> + DCHECK_NOT_NULL(body); - // Allocate a target stack to use for this set of source - // elements. This way, all scripts and functions get their own - // target stack thus avoiding illegal breaks and continues across - // functions. - typename Types::TargetScope target_scope(this); - int count_statements = 0; + while (peek() == Token::STRING) { + bool use_strict = false; + bool use_asm = false; - DCHECK(!impl()->IsNull(body)); - bool directive_prologue = true; // Parsing directive prologue. + Scanner::Location token_loc = scanner()->peek_location(); - while (peek() != end_token) { - if (directive_prologue && peek() != Token::STRING) { - directive_prologue = false; + if (scanner()->NextLiteralEquals("use strict")) { + use_strict = true; + } else if (scanner()->NextLiteralEquals("use asm")) { + use_asm = true; } - bool starts_with_identifier = peek() == Token::IDENTIFIER; - Scanner::Location token_loc = scanner()->peek_location(); - StatementT stat = - ParseStatementListItem(CHECK_OK_CUSTOM(Return, kLazyParsingComplete)); - - if (impl()->IsNull(stat) || stat->IsEmptyStatement()) { - directive_prologue = false; // End of directive prologue. - continue; - } - - if (directive_prologue) { - // The length of the token is used to distinguish between strings literals - // that evaluate equal to directives but contain either escape sequences - // (e.g., "use \x73trict") or line continuations (e.g., "use \(newline) - // strict"). - if (impl()->IsUseStrictDirective(stat) && - token_loc.end_pos - token_loc.beg_pos == sizeof("use strict") + 1) { - // Directive "use strict" (ES5 14.1). - RaiseLanguageMode(LanguageMode::kStrict); - if (!scope()->HasSimpleParameters()) { - // TC39 deemed "use strict" directives to be an error when occurring - // in the body of a function with non-simple parameter list, on - // 29/7/2015. https://goo.gl/ueA7Ln - impl()->ReportMessageAt( - token_loc, MessageTemplate::kIllegalLanguageModeDirective, - "use strict"); - *ok = false; - return kLazyParsingComplete; - } - } else if (impl()->IsUseAsmDirective(stat) && - token_loc.end_pos - token_loc.beg_pos == - sizeof("use asm") + 1) { - // Directive "use asm". - impl()->SetAsmModule(); - } else if (impl()->IsStringLiteral(stat)) { - // Possibly an unknown directive. - // Should not change mode, but will increment usage counters - // as appropriate. Ditto usages below. - RaiseLanguageMode(LanguageMode::kSloppy); - } else { - // End of the directive prologue. - directive_prologue = false; - RaiseLanguageMode(LanguageMode::kSloppy); + StatementT stat = ParseStatementListItem(); + if (impl()->IsNull(stat)) return; + + body->Add(stat); + + if (!impl()->IsStringLiteral(stat)) break; + + if (use_strict) { + // Directive "use strict" (ES5 14.1). + RaiseLanguageMode(LanguageMode::kStrict); + if (!scope()->HasSimpleParameters()) { + // TC39 deemed "use strict" directives to be an error when occurring + // in the body of a function with non-simple parameter list, on + // 29/7/2015. https://goo.gl/ueA7Ln + impl()->ReportMessageAt(token_loc, + MessageTemplate::kIllegalLanguageModeDirective, + "use strict"); + return; } + } else if (use_asm) { + // Directive "use asm". + impl()->SetAsmModule(); } else { + // Possibly an unknown directive. + // Should not change mode, but will increment usage counters + // as appropriate. Ditto usages below. RaiseLanguageMode(LanguageMode::kSloppy); } + } - // If we're allowed to abort, we will do so when we see a "long and - // trivial" function. Our current definition of "long and trivial" is: - // - over kLazyParseTrialLimit statements - // - all starting with an identifier (i.e., no if, for, while, etc.) - if (may_abort) { - if (!starts_with_identifier) { - may_abort = false; - } else if (++count_statements > kLazyParseTrialLimit) { - return kLazyParsingAborted; - } - } - - body->Add(stat, zone()); + // Allocate a target stack to use for this set of source elements. This way, + // all scripts and functions get their own target stack thus avoiding illegal + // breaks and continues across functions. + TargetScopeT target_scope(this); + while (peek() != end_token) { + StatementT stat = ParseStatementListItem(); + if (impl()->IsNull(stat)) return; + if (stat->IsEmptyStatement()) continue; + body->Add(stat); } - return kLazyParsingComplete; } template <typename Impl> -typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseStatementListItem( - bool* ok) { +typename ParserBase<Impl>::StatementT +ParserBase<Impl>::ParseStatementListItem() { // ECMA 262 6th Edition // StatementListItem[Yield, Return] : // Statement[?Yield, ?Return] @@ -4973,36 +4488,36 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseStatementListItem( switch (peek()) { case Token::FUNCTION: - return ParseHoistableDeclaration(nullptr, false, ok); + return ParseHoistableDeclaration(nullptr, false); case Token::CLASS: Consume(Token::CLASS); - return ParseClassDeclaration(nullptr, false, ok); + return ParseClassDeclaration(nullptr, false); case Token::VAR: case Token::CONST: - return ParseVariableStatement(kStatementListItem, nullptr, ok); + return ParseVariableStatement(kStatementListItem, nullptr); case Token::LET: if (IsNextLetKeyword()) { - return ParseVariableStatement(kStatementListItem, nullptr, ok); + return ParseVariableStatement(kStatementListItem, nullptr); } break; case Token::ASYNC: if (PeekAhead() == Token::FUNCTION && !scanner()->HasLineTerminatorAfterNext()) { Consume(Token::ASYNC); - return ParseAsyncFunctionDeclaration(nullptr, false, ok); + return ParseAsyncFunctionDeclaration(nullptr, false); } break; default: break; } - return ParseStatement(nullptr, nullptr, kAllowLabelledFunctionStatement, ok); + return ParseStatement(nullptr, nullptr, kAllowLabelledFunctionStatement); } template <typename Impl> typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseStatement( ZonePtrList<const AstRawString>* labels, ZonePtrList<const AstRawString>* own_labels, - AllowLabelledFunctionStatement allow_function, bool* ok) { + AllowLabelledFunctionStatement allow_function) { // Statement :: // Block // VariableStatement @@ -5031,46 +4546,48 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseStatement( // parsed into an empty statement. switch (peek()) { case Token::LBRACE: - return ParseBlock(labels, ok); + return ParseBlock(labels); case Token::SEMICOLON: Next(); - return factory()->NewEmptyStatement(kNoSourcePosition); + return factory()->EmptyStatement(); case Token::IF: - return ParseIfStatement(labels, ok); + return ParseIfStatement(labels); case Token::DO: - return ParseDoWhileStatement(labels, own_labels, ok); + return ParseDoWhileStatement(labels, own_labels); case Token::WHILE: - return ParseWhileStatement(labels, own_labels, ok); + return ParseWhileStatement(labels, own_labels); case Token::FOR: if (V8_UNLIKELY(is_async_function() && PeekAhead() == Token::AWAIT)) { - return ParseForAwaitStatement(labels, own_labels, ok); + return ParseForAwaitStatement(labels, own_labels); } - return ParseForStatement(labels, own_labels, ok); + return ParseForStatement(labels, own_labels); case Token::CONTINUE: - return ParseContinueStatement(ok); + return ParseContinueStatement(); case Token::BREAK: - return ParseBreakStatement(labels, ok); + return ParseBreakStatement(labels); case Token::RETURN: - return ParseReturnStatement(ok); + return ParseReturnStatement(); case Token::THROW: - return ParseThrowStatement(ok); + return ParseThrowStatement(); case Token::TRY: { // It is somewhat complicated to have labels on try-statements. // When breaking out of a try-finally statement, one must take // great care not to treat it as a fall-through. It is much easier // just to wrap the entire try-statement in a statement block and // put the labels there. - if (labels == nullptr) return ParseTryStatement(ok); - BlockT result = factory()->NewBlock(1, false, labels); - typename Types::Target target(this, result); - StatementT statement = ParseTryStatement(CHECK_OK); - result->statements()->Add(statement, zone()); + if (labels == nullptr) return ParseTryStatement(); + StatementListT statements(pointer_buffer()); + BlockT result = factory()->NewBlock(false, labels); + TargetT target(this, result); + StatementT statement = ParseTryStatement(); + statements.Add(statement); + result->InitializeStatements(statements, zone()); return result; } case Token::WITH: - return ParseWithStatement(labels, ok); + return ParseWithStatement(labels); case Token::SWITCH: - return ParseSwitchStatement(labels, ok); + return ParseSwitchStatement(labels); case Token::FUNCTION: // FunctionDeclaration only allowed as a StatementListItem, not in // an arbitrary Statement position. Exceptions such as @@ -5081,72 +4598,77 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseStatement( is_strict(language_mode()) ? MessageTemplate::kStrictFunction : MessageTemplate::kSloppyFunction); - *ok = false; return impl()->NullStatement(); case Token::DEBUGGER: - return ParseDebuggerStatement(ok); + return ParseDebuggerStatement(); case Token::VAR: - return ParseVariableStatement(kStatement, nullptr, ok); + return ParseVariableStatement(kStatement, nullptr); case Token::ASYNC: if (!scanner()->HasLineTerminatorAfterNext() && PeekAhead() == Token::FUNCTION) { impl()->ReportMessageAt( scanner()->peek_location(), MessageTemplate::kAsyncFunctionInSingleStatementContext); - *ok = false; return impl()->NullStatement(); } V8_FALLTHROUGH; default: return ParseExpressionOrLabelledStatement(labels, own_labels, - allow_function, ok); + allow_function); } } template <typename Impl> typename ParserBase<Impl>::BlockT ParserBase<Impl>::ParseBlock( - ZonePtrList<const AstRawString>* labels, bool* ok) { + ZonePtrList<const AstRawString>* labels) { // Block :: // '{' StatementList '}' - // Construct block expecting 16 statements. - BlockT body = factory()->NewBlock(16, false, labels); - // Parse the statements and collect escaping labels. - Expect(Token::LBRACE, CHECK_OK_CUSTOM(NullStatement)); + BlockT body = factory()->NewBlock(false, labels); + StatementListT statements(pointer_buffer()); + + CheckStackOverflow(); + { BlockState block_state(zone(), &scope_); - scope()->set_start_position(scanner()->location().beg_pos); - typename Types::Target target(this, body); + scope()->set_start_position(peek_position()); + TargetT target(this, body); + + Expect(Token::LBRACE); while (peek() != Token::RBRACE) { - StatementT stat = ParseStatementListItem(CHECK_OK_CUSTOM(NullStatement)); - if (!impl()->IsNull(stat) && !stat->IsEmptyStatement()) { - body->statements()->Add(stat, zone()); - } + StatementT stat = ParseStatementListItem(); + if (impl()->IsNull(stat)) return body; + if (stat->IsEmptyStatement()) continue; + statements.Add(stat); } - Expect(Token::RBRACE, CHECK_OK_CUSTOM(NullStatement)); + Expect(Token::RBRACE); + int end_pos = end_position(); scope()->set_end_position(end_pos); + impl()->RecordBlockSourceRange(body, end_pos); body->set_scope(scope()->FinalizeBlockScope()); } + + body->InitializeStatements(statements, zone_); return body; } template <typename Impl> typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseScopedStatement( - ZonePtrList<const AstRawString>* labels, bool* ok) { + ZonePtrList<const AstRawString>* labels) { if (is_strict(language_mode()) || peek() != Token::FUNCTION) { - return ParseStatement(labels, nullptr, ok); + return ParseStatement(labels, nullptr); } else { // Make a block around the statement for a lexical binding // is introduced by a FunctionDeclaration. BlockState block_state(zone(), &scope_); scope()->set_start_position(scanner()->location().beg_pos); BlockT block = factory()->NewBlock(1, false); - StatementT body = ParseFunctionDeclaration(CHECK_OK); + StatementT body = ParseFunctionDeclaration(); block->statements()->Add(body, zone()); scope()->set_end_position(end_position()); block->set_scope(scope()->FinalizeBlockScope()); @@ -5157,7 +4679,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseScopedStatement( template <typename Impl> typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseVariableStatement( VariableDeclarationContext var_context, - ZonePtrList<const AstRawString>* names, bool* ok) { + ZonePtrList<const AstRawString>* names) { // VariableStatement :: // VariableDeclarations ';' @@ -5174,15 +4696,14 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseVariableStatement( // is inside an initializer block, it is ignored. DeclarationParsingResult parsing_result; - StatementT result = - ParseVariableDeclarations(var_context, &parsing_result, names, CHECK_OK); - ExpectSemicolon(ok); - return result; + ParseVariableDeclarations(var_context, &parsing_result, names); + ExpectSemicolon(); + return impl()->BuildInitializationBlock(&parsing_result); } template <typename Impl> -typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseDebuggerStatement( - bool* ok) { +typename ParserBase<Impl>::StatementT +ParserBase<Impl>::ParseDebuggerStatement() { // In ECMA-262 'debugger' is defined as a reserved keyword. In some browser // contexts this is used as a statement which invokes the debugger as i a // break point is present. @@ -5190,8 +4711,8 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseDebuggerStatement( // 'debugger' ';' int pos = peek_position(); - Expect(Token::DEBUGGER, CHECK_OK); - ExpectSemicolon(CHECK_OK); + Consume(Token::DEBUGGER); + ExpectSemicolon(); return factory()->NewDebuggerStatement(pos); } @@ -5200,7 +4721,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseExpressionOrLabelledStatement( ZonePtrList<const AstRawString>* labels, ZonePtrList<const AstRawString>* own_labels, - AllowLabelledFunctionStatement allow_function, bool* ok) { + AllowLabelledFunctionStatement allow_function) { // ExpressionStatement | LabelledStatement :: // Expression ';' // Identifier ':' Statement @@ -5216,7 +4737,6 @@ ParserBase<Impl>::ParseExpressionOrLabelledStatement( UNREACHABLE(); // Always handled by the callers. case Token::CLASS: ReportUnexpectedToken(Next()); - *ok = false; return impl()->NullStatement(); case Token::LET: { Token::Value next_next = PeekAhead(); @@ -5230,7 +4750,6 @@ ParserBase<Impl>::ParseExpressionOrLabelledStatement( } impl()->ReportMessageAt(scanner()->peek_location(), MessageTemplate::kUnexpectedLexicalDeclaration); - *ok = false; return impl()->NullStatement(); } default: @@ -5238,20 +4757,20 @@ ParserBase<Impl>::ParseExpressionOrLabelledStatement( } bool starts_with_identifier = peek_any_identifier(); - ExpressionT expr = ParseExpression(CHECK_OK); + ExpressionT expr = ParseExpression(); if (peek() == Token::COLON && starts_with_identifier && impl()->IsIdentifier(expr)) { // The whole expression was a single identifier, and not, e.g., // something starting with an identifier or a parenthesized identifier. impl()->DeclareLabel(&labels, &own_labels, - impl()->AsIdentifierExpression(expr), CHECK_OK); + impl()->AsIdentifierExpression(expr)); Consume(Token::COLON); // ES#sec-labelled-function-declarations Labelled Function Declarations if (peek() == Token::FUNCTION && is_sloppy(language_mode()) && allow_function == kAllowLabelledFunctionStatement) { - return ParseFunctionDeclaration(ok); + return ParseFunctionDeclaration(); } - return ParseStatement(labels, own_labels, allow_function, ok); + return ParseStatement(labels, own_labels, allow_function); } // If we have an extension, we allow a native function declaration. @@ -5260,39 +4779,46 @@ ParserBase<Impl>::ParseExpressionOrLabelledStatement( if (extension_ != nullptr && peek() == Token::FUNCTION && !scanner()->HasLineTerminatorBeforeNext() && impl()->IsNative(expr) && !scanner()->literal_contains_escapes()) { - return ParseNativeDeclaration(ok); + return ParseNativeDeclaration(); } // Parsed expression statement, followed by semicolon. - ExpectSemicolon(CHECK_OK); + ExpectSemicolon(); + if (expr->IsFailureExpression()) return impl()->NullStatement(); return factory()->NewExpressionStatement(expr, pos); } template <typename Impl> typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseIfStatement( - ZonePtrList<const AstRawString>* labels, bool* ok) { + ZonePtrList<const AstRawString>* labels) { // IfStatement :: // 'if' '(' Expression ')' Statement ('else' Statement)? int pos = peek_position(); - Expect(Token::IF, CHECK_OK); - Expect(Token::LPAREN, CHECK_OK); - ExpressionT condition = ParseExpression(CHECK_OK); - Expect(Token::RPAREN, CHECK_OK); + Consume(Token::IF); + Expect(Token::LPAREN); + ExpressionT condition = ParseExpression(); + Expect(Token::RPAREN); SourceRange then_range, else_range; StatementT then_statement = impl()->NullStatement(); { SourceRangeScope range_scope(scanner(), &then_range); - then_statement = ParseScopedStatement(labels, CHECK_OK); + // Make a copy of {labels} to avoid conflicts with any + // labels that may be applied to the else clause below. + auto labels_copy = + labels == nullptr + ? labels + : new (zone()) ZonePtrList<const AstRawString>(*labels, zone()); + then_statement = ParseScopedStatement(labels_copy); } StatementT else_statement = impl()->NullStatement(); if (Check(Token::ELSE)) { - else_statement = ParseScopedStatement(labels, CHECK_OK); + else_statement = ParseScopedStatement(labels); else_range = SourceRange::ContinuationOf(then_range, end_position()); } else { - else_statement = factory()->NewEmptyStatement(kNoSourcePosition); + else_statement = factory()->EmptyStatement(); } StatementT stmt = factory()->NewIfStatement(condition, then_statement, else_statement, pos); @@ -5301,37 +4827,34 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseIfStatement( } template <typename Impl> -typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseContinueStatement( - bool* ok) { +typename ParserBase<Impl>::StatementT +ParserBase<Impl>::ParseContinueStatement() { // ContinueStatement :: // 'continue' Identifier? ';' int pos = peek_position(); - Expect(Token::CONTINUE, CHECK_OK); + Consume(Token::CONTINUE); IdentifierT label = impl()->NullIdentifier(); Token::Value tok = peek(); - if (!scanner()->HasLineTerminatorBeforeNext() && tok != Token::SEMICOLON && - tok != Token::RBRACE && tok != Token::EOS) { + if (!scanner()->HasLineTerminatorBeforeNext() && + !Token::IsAutoSemicolon(tok)) { // ECMA allows "eval" or "arguments" as labels even in strict mode. - label = ParseIdentifier(kAllowRestrictedIdentifiers, CHECK_OK); + label = ParseIdentifier(); } - typename Types::IterationStatement target = - impl()->LookupContinueTarget(label, CHECK_OK); + IterationStatementT target = impl()->LookupContinueTarget(label); if (impl()->IsNull(target)) { // Illegal continue statement. - MessageTemplate::Template message = MessageTemplate::kIllegalContinue; - typename Types::BreakableStatement breakable_target = - impl()->LookupBreakTarget(label, CHECK_OK); + MessageTemplate message = MessageTemplate::kIllegalContinue; + BreakableStatementT breakable_target = impl()->LookupBreakTarget(label); if (impl()->IsNull(label)) { message = MessageTemplate::kNoIterationStatement; } else if (impl()->IsNull(breakable_target)) { message = MessageTemplate::kUnknownLabel; } ReportMessage(message, label); - *ok = false; return impl()->NullStatement(); } - ExpectSemicolon(CHECK_OK); + ExpectSemicolon(); StatementT stmt = factory()->NewContinueStatement(target, pos); impl()->RecordJumpStatementSourceRange(stmt, end_position()); return stmt; @@ -5339,53 +4862,50 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseContinueStatement( template <typename Impl> typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseBreakStatement( - ZonePtrList<const AstRawString>* labels, bool* ok) { + ZonePtrList<const AstRawString>* labels) { // BreakStatement :: // 'break' Identifier? ';' int pos = peek_position(); - Expect(Token::BREAK, CHECK_OK); + Consume(Token::BREAK); IdentifierT label = impl()->NullIdentifier(); Token::Value tok = peek(); - if (!scanner()->HasLineTerminatorBeforeNext() && tok != Token::SEMICOLON && - tok != Token::RBRACE && tok != Token::EOS) { + if (!scanner()->HasLineTerminatorBeforeNext() && + !Token::IsAutoSemicolon(tok)) { // ECMA allows "eval" or "arguments" as labels even in strict mode. - label = ParseIdentifier(kAllowRestrictedIdentifiers, CHECK_OK); + label = ParseIdentifier(); } // Parse labeled break statements that target themselves into // empty statements, e.g. 'l1: l2: l3: break l2;' if (!impl()->IsNull(label) && impl()->ContainsLabel(labels, label)) { - ExpectSemicolon(CHECK_OK); - return factory()->NewEmptyStatement(pos); + ExpectSemicolon(); + return factory()->EmptyStatement(); } - typename Types::BreakableStatement target = - impl()->LookupBreakTarget(label, CHECK_OK); + BreakableStatementT target = impl()->LookupBreakTarget(label); if (impl()->IsNull(target)) { // Illegal break statement. - MessageTemplate::Template message = MessageTemplate::kIllegalBreak; + MessageTemplate message = MessageTemplate::kIllegalBreak; if (!impl()->IsNull(label)) { message = MessageTemplate::kUnknownLabel; } ReportMessage(message, label); - *ok = false; return impl()->NullStatement(); } - ExpectSemicolon(CHECK_OK); + ExpectSemicolon(); StatementT stmt = factory()->NewBreakStatement(target, pos); impl()->RecordJumpStatementSourceRange(stmt, end_position()); return stmt; } template <typename Impl> -typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseReturnStatement( - bool* ok) { +typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseReturnStatement() { // ReturnStatement :: // 'return' [no line terminator] Expression? ';' // Consume the return token. It is necessary to do that before // reporting any errors on it, because of the way errors are // reported (underlining). - Expect(Token::RETURN, CHECK_OK); + Consume(Token::RETURN); Scanner::Location loc = scanner()->location(); switch (GetDeclarationScope()->scope_type()) { @@ -5393,7 +4913,6 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseReturnStatement( case EVAL_SCOPE: case MODULE_SCOPE: impl()->ReportMessageAt(loc, MessageTemplate::kIllegalReturn); - *ok = false; return impl()->NullStatement(); default: break; @@ -5401,15 +4920,15 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseReturnStatement( Token::Value tok = peek(); ExpressionT return_value = impl()->NullExpression(); - if (scanner()->HasLineTerminatorBeforeNext() || tok == Token::SEMICOLON || - tok == Token::RBRACE || tok == Token::EOS) { + if (scanner()->HasLineTerminatorBeforeNext() || Token::IsAutoSemicolon(tok)) { if (IsDerivedConstructor(function_state_->kind())) { return_value = impl()->ThisExpression(loc.beg_pos); } } else { - return_value = ParseExpression(CHECK_OK); + return_value = ParseExpression(); } - ExpectSemicolon(CHECK_OK); + ExpectSemicolon(); + return_value = impl()->RewriteReturn(return_value, loc.beg_pos); int continuation_pos = end_position(); StatementT stmt = @@ -5420,29 +4939,28 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseReturnStatement( template <typename Impl> typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseWithStatement( - ZonePtrList<const AstRawString>* labels, bool* ok) { + ZonePtrList<const AstRawString>* labels) { // WithStatement :: // 'with' '(' Expression ')' Statement - Expect(Token::WITH, CHECK_OK); + Consume(Token::WITH); int pos = position(); if (is_strict(language_mode())) { ReportMessage(MessageTemplate::kStrictWith); - *ok = false; return impl()->NullStatement(); } - Expect(Token::LPAREN, CHECK_OK); - ExpressionT expr = ParseExpression(CHECK_OK); - Expect(Token::RPAREN, CHECK_OK); + Expect(Token::LPAREN); + ExpressionT expr = ParseExpression(); + Expect(Token::RPAREN); Scope* with_scope = NewScope(WITH_SCOPE); StatementT body = impl()->NullStatement(); { BlockState block_state(&scope_, with_scope); with_scope->set_start_position(scanner()->peek_location().beg_pos); - body = ParseStatement(labels, nullptr, CHECK_OK); + body = ParseStatement(labels, nullptr); with_scope->set_end_position(end_position()); } return factory()->NewWithStatement(with_scope, expr, body, pos); @@ -5451,27 +4969,30 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseWithStatement( template <typename Impl> typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseDoWhileStatement( ZonePtrList<const AstRawString>* labels, - ZonePtrList<const AstRawString>* own_labels, bool* ok) { + ZonePtrList<const AstRawString>* own_labels) { // DoStatement :: // 'do' Statement 'while' '(' Expression ')' ';' + typename FunctionState::LoopScope loop_scope(function_state_); auto loop = factory()->NewDoWhileStatement(labels, own_labels, peek_position()); - typename Types::Target target(this, loop); + TargetT target(this, loop); SourceRange body_range; StatementT body = impl()->NullStatement(); - Expect(Token::DO, CHECK_OK); + Consume(Token::DO); + + CheckStackOverflow(); { SourceRangeScope range_scope(scanner(), &body_range); - body = ParseStatement(nullptr, nullptr, CHECK_OK); + body = ParseStatement(nullptr, nullptr); } - Expect(Token::WHILE, CHECK_OK); - Expect(Token::LPAREN, CHECK_OK); + Expect(Token::WHILE); + Expect(Token::LPAREN); - ExpressionT cond = ParseExpression(CHECK_OK); - Expect(Token::RPAREN, CHECK_OK); + ExpressionT cond = ParseExpression(); + Expect(Token::RPAREN); // Allow do-statements to be terminated with and without // semi-colons. This allows code such as 'do;while(0)return' to @@ -5488,23 +5009,24 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseDoWhileStatement( template <typename Impl> typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseWhileStatement( ZonePtrList<const AstRawString>* labels, - ZonePtrList<const AstRawString>* own_labels, bool* ok) { + ZonePtrList<const AstRawString>* own_labels) { // WhileStatement :: // 'while' '(' Expression ')' Statement + typename FunctionState::LoopScope loop_scope(function_state_); auto loop = factory()->NewWhileStatement(labels, own_labels, peek_position()); - typename Types::Target target(this, loop); + TargetT target(this, loop); SourceRange body_range; StatementT body = impl()->NullStatement(); - Expect(Token::WHILE, CHECK_OK); - Expect(Token::LPAREN, CHECK_OK); - ExpressionT cond = ParseExpression(CHECK_OK); - Expect(Token::RPAREN, CHECK_OK); + Consume(Token::WHILE); + Expect(Token::LPAREN); + ExpressionT cond = ParseExpression(); + Expect(Token::RPAREN); { SourceRangeScope range_scope(scanner(), &body_range); - body = ParseStatement(nullptr, nullptr, CHECK_OK); + body = ParseStatement(nullptr, nullptr); } loop->Initialize(cond, body); @@ -5514,20 +5036,18 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseWhileStatement( } template <typename Impl> -typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseThrowStatement( - bool* ok) { +typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseThrowStatement() { // ThrowStatement :: // 'throw' Expression ';' - Expect(Token::THROW, CHECK_OK); + Consume(Token::THROW); int pos = position(); if (scanner()->HasLineTerminatorBeforeNext()) { ReportMessage(MessageTemplate::kNewlineAfterThrow); - *ok = false; return impl()->NullStatement(); } - ExpressionT exception = ParseExpression(CHECK_OK); - ExpectSemicolon(CHECK_OK); + ExpressionT exception = ParseExpression(); + ExpectSemicolon(); StatementT stmt = impl()->NewThrowStatement(exception, pos); impl()->RecordThrowSourceRange(stmt, end_position()); @@ -5537,7 +5057,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseThrowStatement( template <typename Impl> typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseSwitchStatement( - ZonePtrList<const AstRawString>* labels, bool* ok) { + ZonePtrList<const AstRawString>* labels) { // SwitchStatement :: // 'switch' '(' Expression ')' '{' CaseClause* '}' // CaseClause :: @@ -5546,10 +5066,10 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseSwitchStatement( int switch_pos = peek_position(); - Expect(Token::SWITCH, CHECK_OK); - Expect(Token::LPAREN, CHECK_OK); - ExpressionT tag = ParseExpression(CHECK_OK); - Expect(Token::RPAREN, CHECK_OK); + Consume(Token::SWITCH); + Expect(Token::LPAREN); + ExpressionT tag = ParseExpression(); + Expect(Token::RPAREN); auto switch_statement = factory()->NewSwitchStatement(labels, tag, switch_pos); @@ -5558,38 +5078,41 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseSwitchStatement( BlockState cases_block_state(zone(), &scope_); scope()->set_start_position(switch_pos); scope()->SetNonlinear(); - typename Types::Target target(this, switch_statement); + TargetT target(this, switch_statement); bool default_seen = false; - Expect(Token::LBRACE, CHECK_OK); + Expect(Token::LBRACE); while (peek() != Token::RBRACE) { // An empty label indicates the default case. ExpressionT label = impl()->NullExpression(); + StatementListT statements(pointer_buffer()); SourceRange clause_range; - SourceRangeScope range_scope(scanner(), &clause_range); - if (Check(Token::CASE)) { - label = ParseExpression(CHECK_OK); - } else { - Expect(Token::DEFAULT, CHECK_OK); - if (default_seen) { - ReportMessage(MessageTemplate::kMultipleDefaultsInSwitch); - *ok = false; - return impl()->NullStatement(); + { + SourceRangeScope range_scope(scanner(), &clause_range); + if (Check(Token::CASE)) { + label = ParseExpression(); + } else { + Expect(Token::DEFAULT); + if (default_seen) { + ReportMessage(MessageTemplate::kMultipleDefaultsInSwitch); + return impl()->NullStatement(); + } + default_seen = true; + } + Expect(Token::COLON); + while (peek() != Token::CASE && peek() != Token::DEFAULT && + peek() != Token::RBRACE) { + StatementT stat = ParseStatementListItem(); + if (impl()->IsNull(stat)) return stat; + if (stat->IsEmptyStatement()) continue; + statements.Add(stat); } - default_seen = true; - } - Expect(Token::COLON, CHECK_OK); - StatementListT statements = impl()->NewStatementList(5); - while (peek() != Token::CASE && peek() != Token::DEFAULT && - peek() != Token::RBRACE) { - StatementT stat = ParseStatementListItem(CHECK_OK); - statements->Add(stat, zone()); } auto clause = factory()->NewCaseClause(label, statements); - impl()->RecordCaseClauseSourceRange(clause, range_scope.Finalize()); + impl()->RecordCaseClauseSourceRange(clause, clause_range); switch_statement->cases()->Add(clause, zone()); } - Expect(Token::RBRACE, CHECK_OK); + Expect(Token::RBRACE); int end_pos = end_position(); scope()->set_end_position(end_pos); @@ -5603,8 +5126,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseSwitchStatement( } template <typename Impl> -typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseTryStatement( - bool* ok) { +typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseTryStatement() { // TryStatement :: // 'try' Block Catch // 'try' Block Finally @@ -5616,22 +5138,21 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseTryStatement( // Finally :: // 'finally' Block - Expect(Token::TRY, CHECK_OK); + Consume(Token::TRY); int pos = position(); - BlockT try_block = ParseBlock(nullptr, CHECK_OK); + BlockT try_block = ParseBlock(nullptr); CatchInfo catch_info(this); if (peek() != Token::CATCH && peek() != Token::FINALLY) { ReportMessage(MessageTemplate::kNoCatchOrFinally); - *ok = false; return impl()->NullStatement(); } SourceRange catch_range, finally_range; - BlockT catch_block = impl()->NullStatement(); + BlockT catch_block = impl()->NullBlock(); { SourceRangeScope catch_range_scope(scanner(), &catch_range); if (Check(Token::CATCH)) { @@ -5644,57 +5165,74 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseTryStatement( { BlockState catch_block_state(&scope_, catch_info.scope); - - catch_block = factory()->NewBlock(16, false); + StatementListT catch_statements(pointer_buffer()); // Create a block scope to hold any lexical declarations created // as part of destructuring the catch parameter. { BlockState catch_variable_block_state(zone(), &scope_); - scope()->set_start_position(scanner()->location().beg_pos); + scope()->set_start_position(position()); - // This does not simply call ParsePrimaryExpression to avoid - // ExpressionFromIdentifier from being called in the first - // branch, which would introduce an unresolved symbol and mess - // with arrow function names. if (peek_any_identifier()) { - catch_info.name = - ParseIdentifier(kDontAllowRestrictedIdentifiers, CHECK_OK); + IdentifierT identifier = ParseNonRestrictedIdentifier(); + RETURN_IF_PARSE_ERROR; + catch_info.variable = impl()->DeclareCatchVariableName( + catch_info.scope, identifier); } else { - ExpressionClassifier pattern_classifier(this); - catch_info.pattern = ParseBindingPattern(CHECK_OK); + catch_info.variable = catch_info.scope->DeclareCatchVariableName( + ast_value_factory()->dot_catch_string()); + VariableDeclarationParsingScope destructuring( + impl(), VariableMode::kLet, nullptr); + catch_info.pattern = ParseBindingPattern(); + RETURN_IF_PARSE_ERROR; + catch_statements.Add(impl()->RewriteCatchPattern(&catch_info)); } - Expect(Token::RPAREN, CHECK_OK); - impl()->RewriteCatchPattern(&catch_info, CHECK_OK); - if (!impl()->IsNull(catch_info.init_block)) { - catch_block->statements()->Add(catch_info.init_block, zone()); + Expect(Token::RPAREN); + + BlockT inner_block = ParseBlock(nullptr); + catch_statements.Add(inner_block); + + // Check for `catch(e) { let e; }` and similar errors. + Scope* inner_scope = inner_block->scope(); + if (inner_scope != nullptr) { + const AstRawString* conflict = nullptr; + if (impl()->IsNull(catch_info.pattern)) { + const AstRawString* name = catch_info.variable->raw_name(); + if (inner_scope->LookupLocal(name)) conflict = name; + } else { + conflict = inner_scope->FindVariableDeclaredIn( + scope(), VariableMode::kVar); + } + if (conflict != nullptr) { + impl()->ReportVarRedeclarationIn(conflict, inner_scope); + } } - 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(end_position()); + catch_block = factory()->NewBlock(false, catch_statements); catch_block->set_scope(scope()->FinalizeBlockScope()); } } catch_info.scope->set_end_position(end_position()); } else { - catch_block = ParseBlock(nullptr, CHECK_OK); + catch_block = ParseBlock(nullptr); } } } - BlockT finally_block = impl()->NullStatement(); - DCHECK(peek() == Token::FINALLY || !impl()->IsNull(catch_block)); + BlockT finally_block = impl()->NullBlock(); + DCHECK(has_error() || peek() == Token::FINALLY || + !impl()->IsNull(catch_block)); { SourceRangeScope range_scope(scanner(), &finally_range); if (Check(Token::FINALLY)) { - finally_block = ParseBlock(nullptr, CHECK_OK); + finally_block = ParseBlock(nullptr); } } + RETURN_IF_PARSE_ERROR; return impl()->RewriteTryStatement(try_block, catch_block, catch_range, finally_block, finally_range, catch_info, pos); @@ -5703,7 +5241,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseTryStatement( template <typename Impl> typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForStatement( ZonePtrList<const AstRawString>* labels, - ZonePtrList<const AstRawString>* own_labels, bool* ok) { + ZonePtrList<const AstRawString>* own_labels) { // Either a standard for loop // for (<init>; <cond>; <next>) { ... } // or a for-each loop @@ -5711,18 +5249,19 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForStatement( // // We parse a declaration/expression after the 'for (' and then read the first // expression/declaration before we know if this is a for or a for-each. + typename FunctionState::LoopScope loop_scope(function_state_); int stmt_pos = peek_position(); ForInfo for_info(this); - Expect(Token::FOR, CHECK_OK); - Expect(Token::LPAREN, CHECK_OK); + Consume(Token::FOR); + Expect(Token::LPAREN); if (peek() == Token::CONST || (peek() == Token::LET && IsNextLetKeyword())) { // The initializer contains lexical declarations, // so create an in-between scope. BlockState for_state(zone(), &scope_); - scope()->set_start_position(scanner()->location().beg_pos); + scope()->set_start_position(position()); // Also record whether inner functions or evals are found inside // this loop, as this information is used to simplify the desugaring @@ -5736,78 +5275,94 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForStatement( { BlockState inner_state(&scope_, inner_block_scope); ParseVariableDeclarations(kForStatement, &for_info.parsing_result, - nullptr, CHECK_OK); + &for_info.bound_names); } DCHECK(IsLexicalVariableMode(for_info.parsing_result.descriptor.mode)); - for_info.position = scanner()->location().beg_pos; + for_info.position = position(); if (CheckInOrOf(&for_info.mode)) { scope()->set_is_hidden(); return ParseForEachStatementWithDeclarations( - stmt_pos, &for_info, labels, own_labels, inner_block_scope, ok); + stmt_pos, &for_info, labels, own_labels, inner_block_scope); } - Expect(Token::SEMICOLON, CHECK_OK); + Expect(Token::SEMICOLON); - StatementT init = impl()->BuildInitializationBlock( - &for_info.parsing_result, &for_info.bound_names, CHECK_OK); + // Parse the remaining code in the inner block scope since the declaration + // above was parsed there. We'll finalize the unnecessary outer block scope + // after parsing the rest of the loop. + StatementT result = impl()->NullStatement(); + inner_block_scope->set_start_position(scope()->start_position()); + { + BlockState inner_state(&scope_, inner_block_scope); + StatementT init = + impl()->BuildInitializationBlock(&for_info.parsing_result); - Scope* finalized = inner_block_scope->FinalizeBlockScope(); - // No variable declarations will have been created in inner_block_scope. + result = ParseStandardForLoopWithLexicalDeclarations( + stmt_pos, init, &for_info, labels, own_labels); + } + Scope* finalized = scope()->FinalizeBlockScope(); DCHECK_NULL(finalized); USE(finalized); - return ParseStandardForLoopWithLexicalDeclarations( - stmt_pos, init, &for_info, labels, own_labels, ok); + return result; } StatementT init = impl()->NullStatement(); if (peek() == Token::VAR) { - ParseVariableDeclarations(kForStatement, &for_info.parsing_result, nullptr, - CHECK_OK); + ParseVariableDeclarations(kForStatement, &for_info.parsing_result, + &for_info.bound_names); DCHECK_EQ(for_info.parsing_result.descriptor.mode, VariableMode::kVar); for_info.position = scanner()->location().beg_pos; if (CheckInOrOf(&for_info.mode)) { return ParseForEachStatementWithDeclarations(stmt_pos, &for_info, labels, - own_labels, nullptr, ok); + own_labels, scope()); } - init = impl()->BuildInitializationBlock(&for_info.parsing_result, nullptr, - CHECK_OK); + init = impl()->BuildInitializationBlock(&for_info.parsing_result); } else if (peek() != Token::SEMICOLON) { // The initializer does not contain declarations. int lhs_beg_pos = peek_position(); - ExpressionClassifier classifier(this); - ExpressionT expression = ParseExpressionCoverGrammar(false, CHECK_OK); - int lhs_end_pos = end_position(); - - bool is_for_each = CheckInOrOf(&for_info.mode); - bool is_destructuring = is_for_each && (expression->IsArrayLiteral() || - expression->IsObjectLiteral()); - - if (is_destructuring) { - ValidateAssignmentPattern(CHECK_OK); - } else { - ValidateExpression(CHECK_OK); + int lhs_end_pos; + bool is_for_each; + ExpressionT expression; + { + ExpressionParsingScope parsing_scope(impl()); + AcceptINScope scope(this, false); + expression = ParseExpressionCoverGrammar(); + // Initializer is reference followed by in/of. + lhs_end_pos = end_position(); + is_for_each = CheckInOrOf(&for_info.mode); + if (is_for_each) { + if (expression->IsPattern()) { + parsing_scope.ValidatePattern(expression, lhs_beg_pos, lhs_end_pos); + } else { + expression = parsing_scope.ValidateAndRewriteReference( + expression, lhs_beg_pos, lhs_end_pos); + } + } else { + parsing_scope.ValidateExpression(); + } } if (is_for_each) { return ParseForEachStatementWithoutDeclarations( stmt_pos, expression, lhs_beg_pos, lhs_end_pos, &for_info, labels, - own_labels, ok); + own_labels); } // Initializer is just an expression. init = factory()->NewExpressionStatement(expression, lhs_beg_pos); } - Expect(Token::SEMICOLON, CHECK_OK); + Expect(Token::SEMICOLON); // Standard 'for' loop, we have parsed the initializer at this point. ExpressionT cond = impl()->NullExpression(); StatementT next = impl()->NullStatement(); StatementT body = impl()->NullStatement(); - ForStatementT loop = ParseStandardForLoop(stmt_pos, labels, own_labels, &cond, - &next, &body, CHECK_OK); + ForStatementT loop = + ParseStandardForLoop(stmt_pos, labels, own_labels, &cond, &next, &body); + RETURN_IF_PARSE_ERROR; loop->Initialize(init, cond, next, body); return loop; } @@ -5816,14 +5371,12 @@ template <typename Impl> typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForEachStatementWithDeclarations( int stmt_pos, ForInfo* for_info, ZonePtrList<const AstRawString>* labels, - ZonePtrList<const AstRawString>* own_labels, Scope* inner_block_scope, - bool* ok) { + ZonePtrList<const AstRawString>* own_labels, Scope* inner_block_scope) { // Just one declaration followed by in/of. if (for_info->parsing_result.declarations.size() != 1) { impl()->ReportMessageAt(for_info->parsing_result.bindings_loc, MessageTemplate::kForInOfLoopMultiBindings, ForEachStatement::VisitModeString(for_info->mode)); - *ok = false; return impl()->NullStatement(); } if (for_info->parsing_result.first_initializer_loc.IsValid() && @@ -5835,80 +5388,66 @@ ParserBase<Impl>::ParseForEachStatementWithDeclarations( impl()->ReportMessageAt(for_info->parsing_result.first_initializer_loc, MessageTemplate::kForInOfLoopInitializer, ForEachStatement::VisitModeString(for_info->mode)); - *ok = false; return impl()->NullStatement(); } - // Reset the declaration_kind to ensure proper processing during declaration. - for_info->parsing_result.descriptor.declaration_kind = - DeclarationDescriptor::FOR_EACH; - BlockT init_block = impl()->RewriteForVarInLegacy(*for_info); auto loop = factory()->NewForEachStatement(for_info->mode, labels, own_labels, stmt_pos); - typename Types::Target target(this, loop); + TargetT target(this, loop); ExpressionT enumerable = impl()->NullExpression(); if (for_info->mode == ForEachStatement::ITERATE) { - ExpressionClassifier classifier(this); - enumerable = ParseAssignmentExpression(true, CHECK_OK); - ValidateExpression(CHECK_OK); + AcceptINScope scope(this, true); + enumerable = ParseAssignmentExpression(); } else { - enumerable = ParseExpression(CHECK_OK); + enumerable = ParseExpression(); } - Expect(Token::RPAREN, CHECK_OK); + Expect(Token::RPAREN); - Scope* for_scope = nullptr; - if (inner_block_scope != nullptr) { - for_scope = inner_block_scope->outer_scope(); - DCHECK_EQ(for_scope, scope()); - inner_block_scope->set_start_position(scanner()->location().beg_pos); + if (IsLexicalVariableMode(for_info->parsing_result.descriptor.mode)) { + inner_block_scope->set_start_position(position()); } ExpressionT each_variable = impl()->NullExpression(); - BlockT body_block = impl()->NullStatement(); + BlockT body_block = impl()->NullBlock(); { - BlockState block_state( - &scope_, inner_block_scope != nullptr ? inner_block_scope : scope_); + BlockState block_state(&scope_, inner_block_scope); SourceRange body_range; - SourceRangeScope range_scope(scanner(), &body_range); - - StatementT body = ParseStatement(nullptr, nullptr, CHECK_OK); - impl()->RecordIterationStatementSourceRange(loop, range_scope.Finalize()); + StatementT body = impl()->NullStatement(); + { + SourceRangeScope range_scope(scanner(), &body_range); + body = ParseStatement(nullptr, nullptr); + } + impl()->RecordIterationStatementSourceRange(loop, body_range); - impl()->DesugarBindingInForEachStatement(for_info, &body_block, - &each_variable, CHECK_OK); + DesugarBindingInForEachStatement(for_info, &body_block, &each_variable); body_block->statements()->Add(body, zone()); - if (inner_block_scope != nullptr) { - inner_block_scope->set_end_position(end_position()); - body_block->set_scope(inner_block_scope->FinalizeBlockScope()); + if (IsLexicalVariableMode(for_info->parsing_result.descriptor.mode)) { + scope()->set_end_position(end_position()); + body_block->set_scope(scope()->FinalizeBlockScope()); } } - StatementT final_loop = impl()->InitializeForEachStatement( - loop, each_variable, enumerable, body_block); + loop->Initialize(each_variable, enumerable, body_block); - init_block = - impl()->CreateForEachStatementTDZ(init_block, *for_info, CHECK_OK); - - if (for_scope != nullptr) { - for_scope->set_end_position(end_position()); - for_scope = for_scope->FinalizeBlockScope(); - } + init_block = impl()->CreateForEachStatementTDZ(init_block, *for_info); // Parsed for-in loop w/ variable declarations. if (!impl()->IsNull(init_block)) { - init_block->statements()->Add(final_loop, zone()); - init_block->set_scope(for_scope); + init_block->statements()->Add(loop, zone()); + if (IsLexicalVariableMode(for_info->parsing_result.descriptor.mode)) { + scope()->set_end_position(end_position()); + init_block->set_scope(scope()->FinalizeBlockScope()); + } return init_block; } - DCHECK_NULL(for_scope); - return final_loop; + return loop; } template <typename Impl> @@ -5916,38 +5455,31 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForEachStatementWithoutDeclarations( int stmt_pos, ExpressionT expression, int lhs_beg_pos, int lhs_end_pos, ForInfo* for_info, ZonePtrList<const AstRawString>* labels, - ZonePtrList<const AstRawString>* own_labels, bool* ok) { - // Initializer is reference followed by in/of. - if (!expression->IsArrayLiteral() && !expression->IsObjectLiteral()) { - expression = CheckAndRewriteReferenceExpression( - expression, lhs_beg_pos, lhs_end_pos, MessageTemplate::kInvalidLhsInFor, - kSyntaxError, CHECK_OK); - } - + ZonePtrList<const AstRawString>* own_labels) { auto loop = factory()->NewForEachStatement(for_info->mode, labels, own_labels, stmt_pos); - typename Types::Target target(this, loop); + TargetT target(this, loop); ExpressionT enumerable = impl()->NullExpression(); if (for_info->mode == ForEachStatement::ITERATE) { - ExpressionClassifier classifier(this); - enumerable = ParseAssignmentExpression(true, CHECK_OK); - ValidateExpression(CHECK_OK); + AcceptINScope scope(this, true); + enumerable = ParseAssignmentExpression(); } else { - enumerable = ParseExpression(CHECK_OK); + enumerable = ParseExpression(); } - Expect(Token::RPAREN, CHECK_OK); + Expect(Token::RPAREN); StatementT body = impl()->NullStatement(); + SourceRange body_range; { - SourceRange body_range; SourceRangeScope range_scope(scanner(), &body_range); - - body = ParseStatement(nullptr, nullptr, CHECK_OK); - impl()->RecordIterationStatementSourceRange(loop, range_scope.Finalize()); + body = ParseStatement(nullptr, nullptr); } - return impl()->InitializeForEachStatement(loop, expression, enumerable, body); + impl()->RecordIterationStatementSourceRange(loop, body_range); + RETURN_IF_PARSE_ERROR; + loop->Initialize(expression, enumerable, body); + return loop; } template <typename Impl> @@ -5955,7 +5487,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseStandardForLoopWithLexicalDeclarations( int stmt_pos, StatementT init, ForInfo* for_info, ZonePtrList<const AstRawString>* labels, - ZonePtrList<const AstRawString>* own_labels, bool* ok) { + ZonePtrList<const AstRawString>* own_labels) { // The condition and the next statement of the for loop must be parsed // in a new scope. Scope* inner_scope = NewScope(BLOCK_SCOPE); @@ -5966,8 +5498,9 @@ ParserBase<Impl>::ParseStandardForLoopWithLexicalDeclarations( { BlockState block_state(&scope_, inner_scope); scope()->set_start_position(scanner()->location().beg_pos); - loop = ParseStandardForLoop(stmt_pos, labels, own_labels, &cond, &next, - &body, CHECK_OK); + loop = + ParseStandardForLoop(stmt_pos, labels, own_labels, &cond, &next, &body); + RETURN_IF_PARSE_ERROR; scope()->set_end_position(end_position()); } @@ -5976,7 +5509,7 @@ ParserBase<Impl>::ParseStandardForLoopWithLexicalDeclarations( function_state_->contains_function_or_eval()) { scope()->set_is_hidden(); return impl()->DesugarLexicalBindingsInForStatement( - loop, init, cond, next, body, inner_scope, *for_info, ok); + loop, init, cond, next, body, inner_scope, *for_info); } else { inner_scope = inner_scope->FinalizeBlockScope(); DCHECK_NULL(inner_scope); @@ -6012,25 +5545,25 @@ template <typename Impl> typename ParserBase<Impl>::ForStatementT ParserBase<Impl>::ParseStandardForLoop( int stmt_pos, ZonePtrList<const AstRawString>* labels, ZonePtrList<const AstRawString>* own_labels, ExpressionT* cond, - StatementT* next, StatementT* body, bool* ok) { + StatementT* next, StatementT* body) { ForStatementT loop = factory()->NewForStatement(labels, own_labels, stmt_pos); - typename Types::Target target(this, loop); + TargetT target(this, loop); if (peek() != Token::SEMICOLON) { - *cond = ParseExpression(CHECK_OK); + *cond = ParseExpression(); } - Expect(Token::SEMICOLON, CHECK_OK); + Expect(Token::SEMICOLON); if (peek() != Token::RPAREN) { - ExpressionT exp = ParseExpression(CHECK_OK); + ExpressionT exp = ParseExpression(); *next = factory()->NewExpressionStatement(exp, exp->position()); } - Expect(Token::RPAREN, CHECK_OK); + Expect(Token::RPAREN); SourceRange body_range; { SourceRangeScope range_scope(scanner(), &body_range); - *body = ParseStatement(nullptr, nullptr, CHECK_OK); + *body = ParseStatement(nullptr, nullptr); } impl()->RecordIterationStatementSourceRange(loop, body_range); @@ -6038,22 +5571,12 @@ typename ParserBase<Impl>::ForStatementT ParserBase<Impl>::ParseStandardForLoop( } template <typename Impl> -void ParserBase<Impl>::MarkLoopVariableAsAssigned( - Scope* scope, Variable* var, - typename DeclarationDescriptor::Kind declaration_kind) { - if (!IsLexicalVariableMode(var->mode()) && - (!scope->is_function_scope() || - declaration_kind == DeclarationDescriptor::FOR_EACH)) { - var->set_maybe_assigned(); - } -} - -template <typename Impl> typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForAwaitStatement( ZonePtrList<const AstRawString>* labels, - ZonePtrList<const AstRawString>* own_labels, bool* ok) { + ZonePtrList<const AstRawString>* own_labels) { // for await '(' ForDeclaration of AssignmentExpression ')' DCHECK(is_async_function()); + typename FunctionState::LoopScope loop_scope(function_state_); int stmt_pos = peek_position(); @@ -6062,14 +5585,19 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForAwaitStatement( // Create an in-between scope for let-bound iteration variables. BlockState for_state(zone(), &scope_); - Expect(Token::FOR, CHECK_OK); - Expect(Token::AWAIT, CHECK_OK); - Expect(Token::LPAREN, CHECK_OK); + Expect(Token::FOR); + Expect(Token::AWAIT); + Expect(Token::LPAREN); scope()->set_start_position(scanner()->location().beg_pos); scope()->set_is_hidden(); - auto loop = factory()->NewForOfStatement(labels, own_labels, stmt_pos); - typename Types::Target target(this, loop); + auto loop = factory()->NewForOfStatement(labels, own_labels, stmt_pos, + IteratorType::kAsync); + // Two suspends: one for next() and one for return() + function_state_->AddSuspend(); + function_state_->AddSuspend(); + + TargetT target(this, loop); ExpressionT each_variable = impl()->NullExpression(); @@ -6088,7 +5616,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForAwaitStatement( { BlockState inner_state(&scope_, inner_block_scope); ParseVariableDeclarations(kForStatement, &for_info.parsing_result, - nullptr, CHECK_OK); + &for_info.bound_names); } for_info.position = scanner()->location().beg_pos; @@ -6097,7 +5625,6 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForAwaitStatement( impl()->ReportMessageAt(for_info.parsing_result.bindings_loc, MessageTemplate::kForInOfLoopMultiBindings, "for-await-of"); - *ok = false; return impl()->NullStatement(); } @@ -6106,7 +5633,6 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForAwaitStatement( impl()->ReportMessageAt(for_info.parsing_result.first_initializer_loc, MessageTemplate::kForInOfLoopInitializer, "for-await-of"); - *ok = false; return impl()->NullStatement(); } } else { @@ -6115,33 +5641,29 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForAwaitStatement( // Statement int lhs_beg_pos = peek_position(); BlockState inner_state(&scope_, inner_block_scope); - ExpressionClassifier classifier(this); - ExpressionT lhs = each_variable = ParseLeftHandSideExpression(CHECK_OK); + ExpressionParsingScope parsing_scope(impl()); + ExpressionT lhs = each_variable = ParseLeftHandSideExpression(); int lhs_end_pos = end_position(); - if (lhs->IsArrayLiteral() || lhs->IsObjectLiteral()) { - ValidateAssignmentPattern(CHECK_OK); + if (lhs->IsPattern()) { + parsing_scope.ValidatePattern(lhs, lhs_beg_pos, lhs_end_pos); } else { - ValidateExpression(CHECK_OK); - each_variable = CheckAndRewriteReferenceExpression( - lhs, lhs_beg_pos, lhs_end_pos, MessageTemplate::kInvalidLhsInFor, - kSyntaxError, CHECK_OK); + each_variable = parsing_scope.ValidateAndRewriteReference( + lhs, lhs_beg_pos, lhs_end_pos); } } - ExpectContextualKeyword(Token::OF, CHECK_OK); - int each_keyword_pos = scanner()->location().beg_pos; + ExpectContextualKeyword(ast_value_factory()->of_string()); const bool kAllowIn = true; ExpressionT iterable = impl()->NullExpression(); { - ExpressionClassifier classifier(this); - iterable = ParseAssignmentExpression(kAllowIn, CHECK_OK); - ValidateExpression(CHECK_OK); + AcceptINScope scope(this, kAllowIn); + iterable = ParseAssignmentExpression(); } - Expect(Token::RPAREN, CHECK_OK); + Expect(Token::RPAREN); StatementT body = impl()->NullStatement(); { @@ -6149,16 +5671,16 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForAwaitStatement( scope()->set_start_position(scanner()->location().beg_pos); SourceRange body_range; - SourceRangeScope range_scope(scanner(), &body_range); - - body = ParseStatement(nullptr, nullptr, CHECK_OK); - scope()->set_end_position(end_position()); - impl()->RecordIterationStatementSourceRange(loop, range_scope.Finalize()); + { + SourceRangeScope range_scope(scanner(), &body_range); + body = ParseStatement(nullptr, nullptr); + scope()->set_end_position(end_position()); + } + impl()->RecordIterationStatementSourceRange(loop, body_range); if (has_declarations) { - BlockT body_block = impl()->NullStatement(); - impl()->DesugarBindingInForEachStatement(&for_info, &body_block, - &each_variable, CHECK_OK); + BlockT body_block = impl()->NullBlock(); + DesugarBindingInForEachStatement(&for_info, &body_block, &each_variable); body_block->statements()->Add(body, zone()); body_block->set_scope(scope()->FinalizeBlockScope()); body = body_block; @@ -6168,103 +5690,85 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForAwaitStatement( USE(block_scope); } } - const bool finalize = true; - StatementT final_loop = impl()->InitializeForOfStatement( - loop, each_variable, iterable, body, finalize, IteratorType::kAsync, - each_keyword_pos); + + loop->Initialize(each_variable, iterable, body); if (!has_declarations) { Scope* for_scope = scope()->FinalizeBlockScope(); DCHECK_NULL(for_scope); USE(for_scope); - return final_loop; + return loop; } - BlockT init_block = impl()->CreateForEachStatementTDZ(impl()->NullStatement(), - for_info, CHECK_OK); + BlockT init_block = + impl()->CreateForEachStatementTDZ(impl()->NullBlock(), for_info); scope()->set_end_position(end_position()); Scope* for_scope = scope()->FinalizeBlockScope(); // Parsed for-in loop w/ variable declarations. if (!impl()->IsNull(init_block)) { - init_block->statements()->Add(final_loop, zone()); + init_block->statements()->Add(loop, zone()); init_block->set_scope(for_scope); return init_block; } DCHECK_NULL(for_scope); - return final_loop; -} - -template <typename Impl> -void ParserBase<Impl>::ObjectLiteralChecker::CheckDuplicateProto( - Token::Value property) { - if (property == Token::SMI || property == Token::NUMBER) return; - - if (IsProto()) { - if (has_seen_proto_) { - this->parser()->classifier()->RecordExpressionError( - this->scanner()->location(), MessageTemplate::kDuplicateProto); - return; - } - has_seen_proto_ = true; - } + return loop; } template <typename Impl> -void ParserBase<Impl>::ClassLiteralChecker::CheckClassMethodName( - Token::Value property, ParsePropertyKind type, ParseFunctionFlags flags, - bool is_static, bool* ok) { +void ParserBase<Impl>::CheckClassMethodName(IdentifierT name, + ParsePropertyKind type, + ParseFunctionFlags flags, + bool is_static, + bool* has_seen_constructor) { DCHECK(type == ParsePropertyKind::kMethod || IsAccessor(type)); - if (property == Token::SMI || property == Token::NUMBER) return; + AstValueFactory* avf = ast_value_factory(); if (is_static) { - if (IsPrototype()) { - this->parser()->ReportMessage(MessageTemplate::kStaticPrototype); - *ok = false; + if (impl()->IdentifierEquals(name, avf->prototype_string())) { + ReportMessage(MessageTemplate::kStaticPrototype); return; } - } else if (IsConstructor()) { + } else if (impl()->IdentifierEquals(name, + avf->private_constructor_string())) { + ReportMessage(MessageTemplate::kConstructorIsPrivate); + return; + } else if (impl()->IdentifierEquals(name, avf->constructor_string())) { if (flags != ParseFunctionFlag::kIsNormal || IsAccessor(type)) { - MessageTemplate::Template msg = - (flags & ParseFunctionFlag::kIsGenerator) != 0 - ? MessageTemplate::kConstructorIsGenerator - : (flags & ParseFunctionFlag::kIsAsync) != 0 - ? MessageTemplate::kConstructorIsAsync - : MessageTemplate::kConstructorIsAccessor; - this->parser()->ReportMessage(msg); - *ok = false; + MessageTemplate msg = (flags & ParseFunctionFlag::kIsGenerator) != 0 + ? MessageTemplate::kConstructorIsGenerator + : (flags & ParseFunctionFlag::kIsAsync) != 0 + ? MessageTemplate::kConstructorIsAsync + : MessageTemplate::kConstructorIsAccessor; + ReportMessage(msg); return; } - if (has_seen_constructor_) { - this->parser()->ReportMessage(MessageTemplate::kDuplicateConstructor); - *ok = false; + if (*has_seen_constructor) { + ReportMessage(MessageTemplate::kDuplicateConstructor); return; } - has_seen_constructor_ = true; + *has_seen_constructor = true; return; } } template <typename Impl> -void ParserBase<Impl>::ClassLiteralChecker::CheckClassFieldName(bool is_static, - bool* ok) { - if (is_static && IsPrototype()) { - this->parser()->ReportMessage(MessageTemplate::kStaticPrototype); - *ok = false; +void ParserBase<Impl>::CheckClassFieldName(IdentifierT name, bool is_static) { + AstValueFactory* avf = ast_value_factory(); + if (is_static && impl()->IdentifierEquals(name, avf->prototype_string())) { + ReportMessage(MessageTemplate::kStaticPrototype); return; } - if (IsConstructor() || IsPrivateConstructor()) { - this->parser()->ReportMessage(MessageTemplate::kConstructorClassField); - *ok = false; + if (impl()->IdentifierEquals(name, avf->constructor_string()) || + impl()->IdentifierEquals(name, avf->private_constructor_string())) { + ReportMessage(MessageTemplate::kConstructorClassField); return; } } -#undef CHECK_OK -#undef CHECK_OK_CUSTOM -#undef CHECK_OK_VOID +#undef RETURN_IF_PARSE_ERROR } // namespace internal } // namespace v8 diff --git a/deps/v8/src/parsing/parser.cc b/deps/v8/src/parsing/parser.cc index 5687633f2f..d6d55af2b6 100644 --- a/deps/v8/src/parsing/parser.cc +++ b/deps/v8/src/parsing/parser.cc @@ -12,14 +12,14 @@ #include "src/ast/ast.h" #include "src/ast/source-range-ast-visitor.h" #include "src/bailout-reason.h" +#include "src/base/overflowing-math.h" #include "src/base/platform/platform.h" #include "src/char-predicates-inl.h" #include "src/compiler-dispatcher/compiler-dispatcher.h" #include "src/conversions-inl.h" #include "src/log.h" -#include "src/messages.h" +#include "src/message-template.h" #include "src/objects/scope-info.h" -#include "src/parsing/duplicate-finder.h" #include "src/parsing/expression-scope-reparenter.h" #include "src/parsing/parse-info.h" #include "src/parsing/rewriter.h" @@ -43,31 +43,31 @@ FunctionLiteral* Parser::DefaultConstructor(const AstRawString* name, // Set start and end position to the same value function_scope->set_start_position(pos); function_scope->set_end_position(pos); - ZonePtrList<Statement>* body = nullptr; + ScopedPtrList<Statement> body(pointer_buffer()); { FunctionState function_state(&function_state_, &scope_, function_scope); - body = new (zone()) ZonePtrList<Statement>(call_super ? 2 : 1, zone()); if (call_super) { // Create a SuperCallReference and handle in BytecodeGenerator. auto constructor_args_name = ast_value_factory()->empty_string(); - bool is_duplicate; bool is_rest = true; bool is_optional = false; Variable* constructor_args = function_scope->DeclareParameter( constructor_args_name, VariableMode::kTemporary, is_optional, is_rest, - &is_duplicate, ast_value_factory(), pos); + ast_value_factory(), pos); - ZonePtrList<Expression>* args = - new (zone()) ZonePtrList<Expression>(1, zone()); - Spread* spread_args = factory()->NewSpread( - factory()->NewVariableProxy(constructor_args), pos, pos); + Expression* call; + { + ScopedPtrList<Expression> args(pointer_buffer()); + Spread* spread_args = factory()->NewSpread( + factory()->NewVariableProxy(constructor_args), pos, pos); - args->Add(spread_args, zone()); - Expression* super_call_ref = NewSuperCallReference(pos); - Expression* call = factory()->NewCall(super_call_ref, args, pos); - body->Add(factory()->NewReturnStatement(call, pos), zone()); + args.Add(spread_args); + Expression* super_call_ref = NewSuperCallReference(pos); + call = factory()->NewCall(super_call_ref, args, pos); + } + body.Add(factory()->NewReturnStatement(call, pos)); } expected_property_count = function_state.expected_property_count(); @@ -81,28 +81,65 @@ FunctionLiteral* Parser::DefaultConstructor(const AstRawString* name, return function_literal; } -// ---------------------------------------------------------------------------- -// The CHECK_OK macro is a convenient macro to enforce error -// handling for functions that may fail (by returning !*ok). -// -// CAUTION: This macro appends extra statements after a call, -// thus it must never be used where only a single statement -// is correct (e.g. an if statement branch w/o braces)! - -#define CHECK_OK_VALUE(x) ok); \ - if (!*ok) return x; \ - ((void)0 -#define DUMMY ) // to make indentation work -#undef DUMMY - -#define CHECK_OK CHECK_OK_VALUE(nullptr) -#define CHECK_OK_VOID CHECK_OK_VALUE(this->Void()) - -#define CHECK_FAILED /**/); \ - if (failed_) return nullptr; \ - ((void)0 -#define DUMMY ) // to make indentation work -#undef DUMMY +void Parser::ReportUnexpectedTokenAt(Scanner::Location location, + Token::Value token, + MessageTemplate message) { + const char* arg = nullptr; + switch (token) { + case Token::EOS: + message = MessageTemplate::kUnexpectedEOS; + break; + case Token::SMI: + case Token::NUMBER: + case Token::BIGINT: + message = MessageTemplate::kUnexpectedTokenNumber; + break; + case Token::STRING: + message = MessageTemplate::kUnexpectedTokenString; + break; + case Token::PRIVATE_NAME: + case Token::IDENTIFIER: + message = MessageTemplate::kUnexpectedTokenIdentifier; + break; + case Token::AWAIT: + case Token::ENUM: + message = MessageTemplate::kUnexpectedReserved; + break; + case Token::LET: + case Token::STATIC: + case Token::YIELD: + case Token::FUTURE_STRICT_RESERVED_WORD: + message = is_strict(language_mode()) + ? MessageTemplate::kUnexpectedStrictReserved + : MessageTemplate::kUnexpectedTokenIdentifier; + break; + case Token::TEMPLATE_SPAN: + case Token::TEMPLATE_TAIL: + message = MessageTemplate::kUnexpectedTemplateString; + break; + case Token::ESCAPED_STRICT_RESERVED_WORD: + case Token::ESCAPED_KEYWORD: + message = MessageTemplate::kInvalidEscapedReservedWord; + break; + case Token::ILLEGAL: + if (scanner()->has_error()) { + message = scanner()->error(); + location = scanner()->error_location(); + } else { + message = MessageTemplate::kInvalidOrUnexpectedToken; + } + break; + case Token::REGEXP_LITERAL: + message = MessageTemplate::kUnexpectedTokenRegExp; + break; + default: + const char* name = Token::String(token); + DCHECK_NOT_NULL(name); + arg = name; + break; + } + ReportMessageAt(location, message, arg); +} // ---------------------------------------------------------------------------- // Implementation of Parser @@ -124,7 +161,7 @@ bool Parser::ShortcutNumericLiteralBinaryExpression(Expression** x, *x = factory()->NewNumberLiteral(x_val * y_val, pos); return true; case Token::DIV: - *x = factory()->NewNumberLiteral(x_val / y_val, pos); + *x = factory()->NewNumberLiteral(base::Divide(x_val, y_val), pos); return true; case Token::BIT_OR: { int value = DoubleToInt32(x_val) | DoubleToInt32(y_val); @@ -200,6 +237,7 @@ bool Parser::CollapseNaryExpression(Expression** x, Expression* y, // TODO(leszeks): Do some literal collapsing here if we're appending Smi or // String literals. nary->AddSubsequent(y, pos); + nary->clear_parenthesized(); AppendNaryOperationSourceRange(nary, range); return true; @@ -232,12 +270,11 @@ Expression* Parser::BuildUnaryExpression(Expression* expression, } Expression* Parser::NewThrowError(Runtime::FunctionId id, - MessageTemplate::Template message, + MessageTemplate message, const AstRawString* arg, int pos) { - ZonePtrList<Expression>* args = - new (zone()) ZonePtrList<Expression>(2, zone()); - args->Add(factory()->NewSmiLiteral(message, pos), zone()); - args->Add(factory()->NewStringLiteral(arg, pos), zone()); + ScopedPtrList<Expression> args(pointer_buffer()); + args.Add(factory()->NewSmiLiteral(static_cast<int>(message), pos)); + args.Add(factory()->NewStringLiteral(arg, pos)); CallRuntime* call_constructor = factory()->NewCallRuntime(id, args, pos); return factory()->NewThrow(call_constructor, pos); } @@ -271,12 +308,12 @@ Expression* Parser::NewTargetExpression(int pos) { } Expression* Parser::ImportMetaExpression(int pos) { - return factory()->NewCallRuntime( - Runtime::kInlineGetImportMetaObject, - new (zone()) ZonePtrList<Expression>(0, zone()), pos); + ScopedPtrList<Expression> args(pointer_buffer()); + return factory()->NewCallRuntime(Runtime::kInlineGetImportMetaObject, args, + pos); } -Literal* Parser::ExpressionFromLiteral(Token::Value token, int pos) { +Expression* Parser::ExpressionFromLiteral(Token::Value token, int pos) { switch (token) { case Token::NULL_LITERAL: return factory()->NewNullLiteral(pos); @@ -295,15 +332,18 @@ Literal* Parser::ExpressionFromLiteral(Token::Value token, int pos) { case Token::BIGINT: return factory()->NewBigIntLiteral( AstBigInt(scanner()->CurrentLiteralAsCString(zone())), pos); + case Token::STRING: { + return factory()->NewStringLiteral(GetSymbol(), pos); + } default: DCHECK(false); } - return nullptr; + return FailureExpression(); } Expression* Parser::NewV8Intrinsic(const AstRawString* name, - ZonePtrList<Expression>* args, int pos, - bool* ok) { + const ScopedPtrList<Expression>& args, + int pos) { if (extension_ != nullptr) { // The extension structures are only accessible while parsing the // very first time, not when reparsing because of lazy compilation. @@ -318,26 +358,11 @@ Expression* Parser::NewV8Intrinsic(const AstRawString* name, // Check for possible name clash. DCHECK_EQ(Context::kNotFound, Context::IntrinsicIndexForName(name->raw_data(), name->length())); - // Check for built-in IS_VAR macro. - if (function->function_id == Runtime::kIS_VAR) { - DCHECK_EQ(Runtime::RUNTIME, function->intrinsic_type); - // %IS_VAR(x) evaluates to x if x is a variable, - // leads to a parse error otherwise. Could be implemented as an - // inline function %_IS_VAR(x) to eliminate this special case. - if (args->length() == 1 && args->at(0)->AsVariableProxy() != nullptr) { - return args->at(0); - } else { - ReportMessage(MessageTemplate::kNotIsvar); - *ok = false; - return nullptr; - } - } // Check that the expected number of arguments are being passed. - if (function->nargs != -1 && function->nargs != args->length()) { + if (function->nargs != -1 && function->nargs != args.length()) { ReportMessage(MessageTemplate::kRuntimeWrongNumArgs); - *ok = false; - return nullptr; + return FailureExpression(); } return factory()->NewCallRuntime(function, args, pos); @@ -349,8 +374,7 @@ Expression* Parser::NewV8Intrinsic(const AstRawString* name, // Check that the function is defined. if (context_index == Context::kNotFound) { ReportMessage(MessageTemplate::kNotDefined, name); - *ok = false; - return nullptr; + return FailureExpression(); } return factory()->NewCallRuntime(context_index, args, pos); @@ -363,15 +387,16 @@ Parser::Parser(ParseInfo* info) info->runtime_call_stats(), info->logger(), info->script().is_null() ? -1 : info->script()->id(), info->is_module(), true), - scanner_(info->unicode_cache(), info->character_stream(), - info->is_module()), + info_(info), + scanner_(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), - consumed_preparsed_scope_data_(info->consumed_preparsed_scope_data()), + consumed_preparse_data_(info->consumed_preparse_data()), + preparse_data_buffer_(), parameters_end_pos_(info->parameters_end_pos()) { // Even though we were passed ParseInfo, we should not store it in // Parser - this makes sure that Isolate is not accidentally accessed via @@ -387,21 +412,22 @@ Parser::Parser(ParseInfo* info) // of functions without an outer context when setting a breakpoint through // Debug::FindSharedFunctionInfoInScript // We also compile eagerly for kProduceExhaustiveCodeCache. - bool can_compile_lazily = FLAG_lazy && !info->is_eager(); + bool can_compile_lazily = info->allow_lazy_compile() && !info->is_eager(); set_default_eager_compile_hint(can_compile_lazily ? FunctionLiteral::kShouldLazyCompile : FunctionLiteral::kShouldEagerCompile); - allow_lazy_ = FLAG_lazy && info->allow_lazy_parsing() && !info->is_native() && - info->extension() == nullptr && can_compile_lazily; - set_allow_natives(FLAG_allow_natives_syntax || info->is_native()); - set_allow_harmony_do_expressions(FLAG_harmony_do_expressions); - set_allow_harmony_public_fields(FLAG_harmony_public_fields); - 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_numeric_separator(FLAG_harmony_numeric_separator); - set_allow_harmony_private_fields(FLAG_harmony_private_fields); + allow_lazy_ = info->allow_lazy_compile() && info->allow_lazy_parsing() && + !info->is_native() && info->extension() == nullptr && + can_compile_lazily; + set_allow_natives(info->allow_natives_syntax() || info->is_native()); + set_allow_harmony_public_fields(info->allow_harmony_public_fields()); + set_allow_harmony_static_fields(info->allow_harmony_static_fields()); + set_allow_harmony_dynamic_import(info->allow_harmony_dynamic_import()); + set_allow_harmony_import_meta(info->allow_harmony_import_meta()); + set_allow_harmony_numeric_separator(info->allow_harmony_numeric_separator()); + set_allow_harmony_private_fields(info->allow_harmony_private_fields()); + set_allow_harmony_private_methods(info->allow_harmony_private_methods()); for (int feature = 0; feature < v8::Isolate::kUseCounterFeatureCount; ++feature) { use_counts_[feature] = 0; @@ -411,8 +437,6 @@ Parser::Parser(ParseInfo* 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); original_scope_ = script_scope; @@ -420,14 +444,15 @@ void Parser::InitializeEmptyScopeChain(ParseInfo* info) { void Parser::DeserializeScopeChain( Isolate* isolate, ParseInfo* info, - MaybeHandle<ScopeInfo> maybe_outer_scope_info) { + MaybeHandle<ScopeInfo> maybe_outer_scope_info, + Scope::DeserializationMode mode) { InitializeEmptyScopeChain(info); Handle<ScopeInfo> outer_scope_info; if (maybe_outer_scope_info.ToHandle(&outer_scope_info)) { DCHECK(ThreadId::Current().Equals(isolate->thread_id())); original_scope_ = Scope::DeserializeScopeChain( isolate, zone(), *outer_scope_info, info->script_scope(), - ast_value_factory(), Scope::DeserializationMode::kScopesOnly); + ast_value_factory(), mode); } } @@ -436,10 +461,11 @@ namespace { void MaybeResetCharacterStream(ParseInfo* info, FunctionLiteral* literal) { // Don't reset the character stream if there is an asm.js module since it will // be used again by the asm-parser. - if (!FLAG_stress_validate_asm && - (literal == nullptr || !literal->scope()->ContainsAsmModule())) { - info->ResetCharacterStream(); + if (info->contains_asm_module()) { + if (FLAG_stress_validate_asm) return; + if (literal != nullptr && literal->scope()->ContainsAsmModule()) return; } + info->ResetCharacterStream(); } void MaybeProcessSourceRanges(ParseInfo* parse_info, Expression* root, @@ -469,9 +495,13 @@ FunctionLiteral* Parser::ParseProgram(Isolate* isolate, ParseInfo* info) { if (V8_UNLIKELY(FLAG_log_function_events)) timer.Start(); // Initialize parser state. - DeserializeScopeChain(isolate, info, info->maybe_outer_scope_info()); + DeserializeScopeChain(isolate, info, info->maybe_outer_scope_info(), + Scope::DeserializationMode::kIncludingVariables); scanner_.Initialize(); + if (FLAG_harmony_hashbang && !info->is_eval()) { + scanner_.SkipHashBang(); + } FunctionLiteral* result = DoParseProgram(isolate, info); MaybeResetCharacterStream(info, result); MaybeProcessSourceRanges(info, result, stack_limit_); @@ -481,7 +511,7 @@ FunctionLiteral* Parser::ParseProgram(Isolate* isolate, ParseInfo* info) { if (V8_UNLIKELY(FLAG_log_function_events) && result != nullptr) { double ms = timer.Elapsed().InMillisecondsF(); const char* event_name = "parse-eval"; - Script* script = *info->script(); + Script script = *info->script(); int start = -1; int end = -1; if (!info->is_eval()) { @@ -524,78 +554,80 @@ FunctionLiteral* Parser::DoParseProgram(Isolate* isolate, ParseInfo* info) { scope->set_start_position(0); FunctionState function_state(&function_state_, &scope_, scope); - ZonePtrList<Statement>* body = - new (zone()) ZonePtrList<Statement>(16, zone()); - bool ok = true; + ScopedPtrList<Statement> body(pointer_buffer()); int beg_pos = scanner()->location().beg_pos; if (parsing_module_) { DCHECK(info->is_module()); // Declare the special module parameter. auto name = ast_value_factory()->empty_string(); - bool is_duplicate = false; bool is_rest = false; bool is_optional = false; + VariableMode mode = VariableMode::kVar; + bool was_added; + scope->DeclareLocal(name, mode, PARAMETER_VARIABLE, &was_added, + Variable::DefaultInitializationFlag(mode)); + DCHECK(was_added); auto var = scope->DeclareParameter(name, VariableMode::kVar, is_optional, - is_rest, &is_duplicate, - ast_value_factory(), beg_pos); - DCHECK(!is_duplicate); + is_rest, ast_value_factory(), beg_pos); var->AllocateTo(VariableLocation::PARAMETER, 0); PrepareGeneratorVariables(); Expression* initial_yield = BuildInitialYield(kNoSourcePosition, kGeneratorFunction); - body->Add( - factory()->NewExpressionStatement(initial_yield, kNoSourcePosition), - zone()); - - ParseModuleItemList(body, &ok); - ok = ok && module()->Validate(this->scope()->AsModuleScope(), - pending_error_handler(), zone()); + body.Add( + factory()->NewExpressionStatement(initial_yield, kNoSourcePosition)); + + ParseModuleItemList(&body); + if (!has_error() && + !module()->Validate(this->scope()->AsModuleScope(), + pending_error_handler(), zone())) { + scanner()->set_parser_error(); + } } else if (info->is_wrapped_as_function()) { - ParseWrapped(isolate, info, body, scope, zone(), &ok); + ParseWrapped(isolate, info, &body, scope, zone()); } else { // Don't count the mode in the use counters--give the program a chance // to enable script-wide strict mode below. this->scope()->SetLanguageMode(info->language_mode()); - ParseStatementList(body, Token::EOS, &ok); + ParseStatementList(&body, Token::EOS); } // The parser will peek but not consume EOS. Our scope logically goes all // the way to the EOS, though. - scope->set_end_position(scanner()->peek_location().beg_pos); + scope->set_end_position(peek_position()); - if (ok && is_strict(language_mode())) { - CheckStrictOctalLiteral(beg_pos, scanner()->location().end_pos, &ok); + if (is_strict(language_mode())) { + CheckStrictOctalLiteral(beg_pos, end_position()); } - if (ok && is_sloppy(language_mode())) { + if (is_sloppy(language_mode())) { // TODO(littledan): Function bindings on the global object that modify // pre-existing bindings should be made writable, enumerable and // nonconfigurable if possible, whereas this code will leave attributes // unchanged if the property already exists. InsertSloppyBlockFunctionVarBindings(scope); } - if (ok) { - CheckConflictingVarDeclarations(scope, &ok); + // Internalize the ast strings in the case of eval so we can check for + // conflicting var declarations with outer scope-info-backed scopes. + if (info->is_eval()) { + DCHECK(parsing_on_main_thread_); + info->ast_value_factory()->Internalize(isolate); } + CheckConflictingVarDeclarations(scope); - if (ok && info->parse_restriction() == ONLY_SINGLE_FUNCTION_LITERAL) { - if (body->length() != 1 || - !body->at(0)->IsExpressionStatement() || - !body->at(0)->AsExpressionStatement()-> - expression()->IsFunctionLiteral()) { + if (info->parse_restriction() == ONLY_SINGLE_FUNCTION_LITERAL) { + if (body.length() != 1 || !body.at(0)->IsExpressionStatement() || + !body.at(0) + ->AsExpressionStatement() + ->expression() + ->IsFunctionLiteral()) { ReportMessage(MessageTemplate::kSingleFunctionLiteral); - ok = false; } } - if (ok) { - RewriteDestructuringAssignments(); - int parameter_count = parsing_module_ ? 1 : 0; - result = factory()->NewScriptOrEvalFunctionLiteral( - scope, body, function_state.expected_property_count(), - parameter_count); - result->set_suspend_count(function_state.suspend_count()); - } + int parameter_count = parsing_module_ ? 1 : 0; + result = factory()->NewScriptOrEvalFunctionLiteral( + scope, body, function_state.expected_property_count(), parameter_count); + result->set_suspend_count(function_state.suspend_count()); } info->set_max_function_literal_id(GetLastFunctionLiteralId()); @@ -603,6 +635,7 @@ FunctionLiteral* Parser::DoParseProgram(Isolate* isolate, ParseInfo* info) { // Make sure the target stack is empty. DCHECK_NULL(target_stack_); + if (has_error()) return nullptr; return result; } @@ -623,8 +656,8 @@ ZonePtrList<const AstRawString>* Parser::PrepareWrappedArguments( } void Parser::ParseWrapped(Isolate* isolate, ParseInfo* info, - ZonePtrList<Statement>* body, - DeclarationScope* outer_scope, Zone* zone, bool* ok) { + ScopedPtrList<Statement>* body, + DeclarationScope* outer_scope, Zone* zone) { DCHECK_EQ(parsing_on_main_thread_, isolate != nullptr); DCHECK(info->is_wrapped_as_function()); ParsingModeScope parsing_mode(this, PARSE_EAGERLY); @@ -642,11 +675,11 @@ void Parser::ParseWrapped(Isolate* isolate, ParseInfo* info, FunctionLiteral* function_literal = ParseFunctionLiteral( function_name, location, kSkipFunctionNameCheck, kNormalFunction, kNoSourcePosition, FunctionLiteral::kWrapped, LanguageMode::kSloppy, - arguments_for_wrapped_function, CHECK_OK_VOID); + arguments_for_wrapped_function); Statement* return_statement = factory()->NewReturnStatement( function_literal, kNoSourcePosition, kNoSourcePosition); - body->Add(return_statement, zone); + body->Add(return_statement); } FunctionLiteral* Parser::ParseFunction(Isolate* isolate, ParseInfo* info, @@ -660,7 +693,8 @@ FunctionLiteral* Parser::ParseFunction(Isolate* isolate, ParseInfo* info, base::ElapsedTimer timer; if (V8_UNLIKELY(FLAG_log_function_events)) timer.Start(); - DeserializeScopeChain(isolate, info, info->maybe_outer_scope_info()); + DeserializeScopeChain(isolate, info, info->maybe_outer_scope_info(), + Scope::DeserializationMode::kIncludingVariables); DCHECK_EQ(factory()->zone(), info->zone()); // Initialize parser state. @@ -736,7 +770,6 @@ FunctionLiteral* Parser::DoParseFunction(Isolate* isolate, ParseInfo* info, is_strict(info->language_mode())); FunctionLiteral::FunctionType function_type = ComputeFunctionType(info); FunctionKind kind = info->function_kind(); - bool ok = true; if (IsArrowFunction(kind)) { if (IsAsyncFunction(kind)) { @@ -759,70 +792,56 @@ FunctionLiteral* Parser::DoParseFunction(Isolate* isolate, ParseInfo* info, SetLanguageMode(scope, info->language_mode()); scope->set_start_position(info->start_position()); - ExpressionClassifier formals_classifier(this); ParserFormalParameters formals(scope); - // The outer FunctionState should not contain destructuring assignments. - DCHECK_EQ(0, - function_state.destructuring_assignments_to_rewrite().size()); { + ParameterDeclarationParsingScope formals_scope(this); // Parsing patterns as variable reference expression creates // NewUnresolved references in current scope. Enter arrow function // scope for formal parameter parsing. BlockState block_state(&scope_, scope); if (Check(Token::LPAREN)) { // '(' StrictFormalParameters ')' - ParseFormalParameterList(&formals, &ok); - if (ok) ok = Check(Token::RPAREN); + ParseFormalParameterList(&formals); + Expect(Token::RPAREN); } else { // BindingIdentifier - ParseFormalParameter(&formals, &ok); - if (ok) { - DeclareFormalParameters(formals.scope, formals.params, - formals.is_simple); - } + ParameterParsingScope scope(impl(), &formals); + ParseFormalParameter(&formals); + DeclareFormalParameters(&formals); } + formals.duplicate_loc = formals_scope.duplicate_location(); } - if (ok) { - if (GetLastFunctionLiteralId() != info->function_literal_id() - 1) { - // If there were FunctionLiterals in the parameters, we need to - // renumber them to shift down so the next function literal id for - // the arrow function is the one requested. - AstFunctionLiteralIdReindexer reindexer( - stack_limit_, - (info->function_literal_id() - 1) - GetLastFunctionLiteralId()); - for (auto p : formals.params) { - if (p->pattern != nullptr) reindexer.Reindex(p->pattern); - if (p->initializer != nullptr) reindexer.Reindex(p->initializer); + if (GetLastFunctionLiteralId() != info->function_literal_id() - 1) { + if (has_error()) return nullptr; + // If there were FunctionLiterals in the parameters, we need to + // renumber them to shift down so the next function literal id for + // the arrow function is the one requested. + AstFunctionLiteralIdReindexer reindexer( + stack_limit_, + (info->function_literal_id() - 1) - GetLastFunctionLiteralId()); + for (auto p : formals.params) { + if (p->pattern != nullptr) reindexer.Reindex(p->pattern); + if (p->initializer() != nullptr) { + reindexer.Reindex(p->initializer()); } - ResetFunctionLiteralId(); - SkipFunctionLiterals(info->function_literal_id() - 1); } + ResetFunctionLiteralId(); + SkipFunctionLiterals(info->function_literal_id() - 1); + } - // Pass `accept_IN=true` to ParseArrowFunctionLiteral --- This should - // not be observable, or else the preparser would have failed. - const bool accept_IN = true; - // Any destructuring assignments in the current FunctionState - // actually belong to the arrow function itself. - const int rewritable_length = 0; - Expression* expression = ParseArrowFunctionLiteral( - accept_IN, formals, rewritable_length, &ok); - if (ok) { - // Scanning must end at the same position that was recorded - // previously. If not, parsing has been interrupted due to a stack - // overflow, at which point the partially parsed arrow function - // concise body happens to be a valid expression. This is a problem - // only for arrow functions with single expression bodies, since there - // is no end token such as "}" for normal functions. - if (scanner()->location().end_pos == info->end_position()) { - // The pre-parser saw an arrow function here, so the full parser - // must produce a FunctionLiteral. - DCHECK(expression->IsFunctionLiteral()); - result = expression->AsFunctionLiteral(); - } else { - ok = false; - } - } + Expression* expression = ParseArrowFunctionLiteral(formals); + // Scanning must end at the same position that was recorded + // previously. If not, parsing has been interrupted due to a stack + // overflow, at which point the partially parsed arrow function + // concise body happens to be a valid expression. This is a problem + // only for arrow functions with single expression bodies, since there + // is no end token such as "}" for normal functions. + if (scanner()->location().end_pos == info->end_position()) { + // The pre-parser saw an arrow function here, so the full parser + // must produce a FunctionLiteral. + DCHECK(expression->IsFunctionLiteral()); + result = expression->AsFunctionLiteral(); } } else if (IsDefaultConstructor(kind)) { DCHECK_EQ(scope(), outer); @@ -836,15 +855,15 @@ FunctionLiteral* Parser::DoParseFunction(Isolate* isolate, ParseInfo* info, result = ParseFunctionLiteral( raw_name, Scanner::Location::invalid(), kSkipFunctionNameCheck, kind, kNoSourcePosition, function_type, info->language_mode(), - arguments_for_wrapped_function, &ok); + arguments_for_wrapped_function); } - if (ok) { - result->set_requires_instance_fields_initializer( - info->requires_instance_fields_initializer()); + if (has_error()) return nullptr; + result->set_requires_instance_members_initializer( + info->requires_instance_members_initializer()); + if (info->is_oneshot_iife()) { + result->mark_as_oneshot_iife(); } - // Make sure the results agree. - DCHECK(ok == (result != nullptr)); } // Make sure the target stack is empty. @@ -854,7 +873,7 @@ FunctionLiteral* Parser::DoParseFunction(Isolate* isolate, ParseInfo* info, return result; } -Statement* Parser::ParseModuleItem(bool* ok) { +Statement* Parser::ParseModuleItem() { // ecma262/#prod-ModuleItem // ModuleItem : // ImportDeclaration @@ -864,7 +883,7 @@ Statement* Parser::ParseModuleItem(bool* ok) { Token::Value next = peek(); if (next == Token::EXPORT) { - return ParseExportDeclaration(ok); + return ParseExportDeclaration(); } if (next == Token::IMPORT) { @@ -873,15 +892,15 @@ Statement* Parser::ParseModuleItem(bool* ok) { Token::Value peek_ahead = PeekAhead(); if ((!allow_harmony_dynamic_import() || peek_ahead != Token::LPAREN) && (!allow_harmony_import_meta() || peek_ahead != Token::PERIOD)) { - ParseImportDeclaration(CHECK_OK); - return factory()->NewEmptyStatement(kNoSourcePosition); + ParseImportDeclaration(); + return factory()->EmptyStatement(); } } - return ParseStatementListItem(ok); + return ParseStatementListItem(); } -void Parser::ParseModuleItemList(ZonePtrList<Statement>* body, bool* ok) { +void Parser::ParseModuleItemList(ScopedPtrList<Statement>* body) { // ecma262/#prod-Module // Module : // ModuleBody? @@ -892,24 +911,23 @@ void Parser::ParseModuleItemList(ZonePtrList<Statement>* body, bool* ok) { DCHECK(scope()->is_module_scope()); while (peek() != Token::EOS) { - Statement* stat = ParseModuleItem(CHECK_OK_VOID); - if (stat && !stat->IsEmpty()) { - body->Add(stat, zone()); - } + Statement* stat = ParseModuleItem(); + if (stat == nullptr) return; + if (stat->IsEmptyStatement()) continue; + body->Add(stat); } } - -const AstRawString* Parser::ParseModuleSpecifier(bool* ok) { +const AstRawString* Parser::ParseModuleSpecifier() { // ModuleSpecifier : // StringLiteral - Expect(Token::STRING, CHECK_OK); + Expect(Token::STRING); return GetSymbol(); } ZoneChunkList<Parser::ExportClauseData>* Parser::ParseExportClause( - Scanner::Location* reserved_loc, bool* ok) { + Scanner::Location* reserved_loc) { // ExportClause : // '{' '}' // '{' ExportsList '}' @@ -925,22 +943,22 @@ ZoneChunkList<Parser::ExportClauseData>* Parser::ParseExportClause( ZoneChunkList<ExportClauseData>* export_data = new (zone()) ZoneChunkList<ExportClauseData>(zone()); - Expect(Token::LBRACE, CHECK_OK); + Expect(Token::LBRACE); Token::Value name_tok; while ((name_tok = peek()) != Token::RBRACE) { // Keep track of the first reserved word encountered in case our // caller needs to report an error. if (!reserved_loc->IsValid() && - !Token::IsIdentifier(name_tok, LanguageMode::kStrict, false, - parsing_module_)) { + !Token::IsValidIdentifier(name_tok, LanguageMode::kStrict, false, + parsing_module_)) { *reserved_loc = scanner()->location(); } - const AstRawString* local_name = ParseIdentifierName(CHECK_OK); + const AstRawString* local_name = ParsePropertyName(); const AstRawString* export_name = nullptr; Scanner::Location location = scanner()->location(); - if (CheckContextualKeyword(Token::AS)) { - export_name = ParseIdentifierName(CHECK_OK); + if (CheckContextualKeyword(ast_value_factory()->as_string())) { + export_name = ParsePropertyName(); // Set the location to the whole "a as b" string, so that it makes sense // both for errors due to "a" and for errors due to "b". location.end_pos = scanner()->location().end_pos; @@ -950,15 +968,17 @@ ZoneChunkList<Parser::ExportClauseData>* Parser::ParseExportClause( } export_data->push_back({export_name, local_name, location}); if (peek() == Token::RBRACE) break; - Expect(Token::COMMA, CHECK_OK); + if (V8_UNLIKELY(!Check(Token::COMMA))) { + ReportUnexpectedToken(Next()); + break; + } } - Expect(Token::RBRACE, CHECK_OK); + Expect(Token::RBRACE); return export_data; } -ZonePtrList<const Parser::NamedImport>* Parser::ParseNamedImports(int pos, - bool* ok) { +ZonePtrList<const Parser::NamedImport>* Parser::ParseNamedImports(int pos) { // NamedImports : // '{' '}' // '{' ImportsList '}' @@ -972,47 +992,45 @@ ZonePtrList<const Parser::NamedImport>* Parser::ParseNamedImports(int pos, // BindingIdentifier // IdentifierName 'as' BindingIdentifier - Expect(Token::LBRACE, CHECK_OK); + Expect(Token::LBRACE); auto result = new (zone()) ZonePtrList<const NamedImport>(1, zone()); while (peek() != Token::RBRACE) { - const AstRawString* import_name = ParseIdentifierName(CHECK_OK); + const AstRawString* import_name = ParsePropertyName(); const AstRawString* local_name = import_name; Scanner::Location location = scanner()->location(); // In the presence of 'as', the left-side of the 'as' can // be any IdentifierName. But without 'as', it must be a valid // BindingIdentifier. - if (CheckContextualKeyword(Token::AS)) { - local_name = ParseIdentifierName(CHECK_OK); + if (CheckContextualKeyword(ast_value_factory()->as_string())) { + local_name = ParsePropertyName(); } - if (!Token::IsIdentifier(scanner()->current_token(), LanguageMode::kStrict, - false, parsing_module_)) { - *ok = false; + if (!Token::IsValidIdentifier(scanner()->current_token(), + LanguageMode::kStrict, false, + parsing_module_)) { ReportMessage(MessageTemplate::kUnexpectedReserved); return nullptr; } else if (IsEvalOrArguments(local_name)) { - *ok = false; ReportMessage(MessageTemplate::kStrictEvalArguments); return nullptr; } DeclareVariable(local_name, VariableMode::kConst, kNeedsInitialization, - position(), CHECK_OK); + position()); NamedImport* import = new (zone()) NamedImport(import_name, local_name, location); result->Add(import, zone()); if (peek() == Token::RBRACE) break; - Expect(Token::COMMA, CHECK_OK); + Expect(Token::COMMA); } - Expect(Token::RBRACE, CHECK_OK); + Expect(Token::RBRACE); return result; } - -void Parser::ParseImportDeclaration(bool* ok) { +void Parser::ParseImportDeclaration() { // ImportDeclaration : // 'import' ImportClause 'from' ModuleSpecifier ';' // 'import' ModuleSpecifier ';' @@ -1028,15 +1046,15 @@ void Parser::ParseImportDeclaration(bool* ok) { // '*' 'as' ImportedBinding int pos = peek_position(); - Expect(Token::IMPORT, CHECK_OK_VOID); + Expect(Token::IMPORT); Token::Value tok = peek(); // 'import' ModuleSpecifier ';' if (tok == Token::STRING) { Scanner::Location specifier_loc = scanner()->peek_location(); - const AstRawString* module_specifier = ParseModuleSpecifier(CHECK_OK_VOID); - ExpectSemicolon(CHECK_OK_VOID); + const AstRawString* module_specifier = ParseModuleSpecifier(); + ExpectSemicolon(); module()->AddEmptyImport(module_specifier, specifier_loc); return; } @@ -1045,11 +1063,10 @@ void Parser::ParseImportDeclaration(bool* ok) { const AstRawString* import_default_binding = nullptr; Scanner::Location import_default_binding_loc; if (tok != Token::MUL && tok != Token::LBRACE) { - import_default_binding = - ParseIdentifier(kDontAllowRestrictedIdentifiers, CHECK_OK_VOID); + import_default_binding = ParseNonRestrictedIdentifier(); import_default_binding_loc = scanner()->location(); DeclareVariable(import_default_binding, VariableMode::kConst, - kNeedsInitialization, pos, CHECK_OK_VOID); + kNeedsInitialization, pos); } // Parse NameSpaceImport or NamedImports if present. @@ -1060,30 +1077,28 @@ void Parser::ParseImportDeclaration(bool* ok) { switch (peek()) { case Token::MUL: { Consume(Token::MUL); - ExpectContextualKeyword(Token::AS, CHECK_OK_VOID); - module_namespace_binding = - ParseIdentifier(kDontAllowRestrictedIdentifiers, CHECK_OK_VOID); + ExpectContextualKeyword(ast_value_factory()->as_string()); + module_namespace_binding = ParseNonRestrictedIdentifier(); module_namespace_binding_loc = scanner()->location(); DeclareVariable(module_namespace_binding, VariableMode::kConst, - kCreatedInitialized, pos, CHECK_OK_VOID); + kCreatedInitialized, pos); break; } case Token::LBRACE: - named_imports = ParseNamedImports(pos, CHECK_OK_VOID); + named_imports = ParseNamedImports(pos); break; default: - *ok = false; ReportUnexpectedToken(scanner()->current_token()); return; } } - ExpectContextualKeyword(Token::FROM, CHECK_OK_VOID); + ExpectContextualKeyword(ast_value_factory()->from_string()); Scanner::Location specifier_loc = scanner()->peek_location(); - const AstRawString* module_specifier = ParseModuleSpecifier(CHECK_OK_VOID); - ExpectSemicolon(CHECK_OK_VOID); + const AstRawString* module_specifier = ParseModuleSpecifier(); + ExpectSemicolon(); // Now that we have all the information, we can make the appropriate // declarations. @@ -1119,42 +1134,40 @@ void Parser::ParseImportDeclaration(bool* ok) { } } - -Statement* Parser::ParseExportDefault(bool* ok) { +Statement* Parser::ParseExportDefault() { // Supports the following productions, starting after the 'default' token: // 'export' 'default' HoistableDeclaration // 'export' 'default' ClassDeclaration // 'export' 'default' AssignmentExpression[In] ';' - Expect(Token::DEFAULT, CHECK_OK); + Expect(Token::DEFAULT); Scanner::Location default_loc = scanner()->location(); ZonePtrList<const AstRawString> local_names(1, zone()); Statement* result = nullptr; switch (peek()) { case Token::FUNCTION: - result = ParseHoistableDeclaration(&local_names, true, CHECK_OK); + result = ParseHoistableDeclaration(&local_names, true); break; case Token::CLASS: Consume(Token::CLASS); - result = ParseClassDeclaration(&local_names, true, CHECK_OK); + result = ParseClassDeclaration(&local_names, true); break; case Token::ASYNC: if (PeekAhead() == Token::FUNCTION && !scanner()->HasLineTerminatorAfterNext()) { Consume(Token::ASYNC); - result = ParseAsyncFunctionDeclaration(&local_names, true, CHECK_OK); + result = ParseAsyncFunctionDeclaration(&local_names, true); break; } V8_FALLTHROUGH; default: { int pos = position(); - ExpressionClassifier classifier(this); - Expression* value = ParseAssignmentExpression(true, CHECK_OK); - ValidateExpression(CHECK_OK); + AcceptINScope scope(this, true); + Expression* value = ParseAssignmentExpression(); SetFunctionName(value, ast_value_factory()->default_string()); const AstRawString* local_name = @@ -1163,57 +1176,98 @@ Statement* Parser::ParseExportDefault(bool* ok) { // It's fine to declare this as VariableMode::kConst because the user has // no way of writing to it. - Declaration* decl = - DeclareVariable(local_name, VariableMode::kConst, pos, CHECK_OK); - decl->proxy()->var()->set_initializer_position(position()); + VariableProxy* proxy = + DeclareVariable(local_name, VariableMode::kConst, pos); + proxy->var()->set_initializer_position(position()); Assignment* assignment = factory()->NewAssignment( - Token::INIT, decl->proxy(), value, kNoSourcePosition); + Token::INIT, proxy, value, kNoSourcePosition); result = IgnoreCompletion( factory()->NewExpressionStatement(assignment, kNoSourcePosition)); - ExpectSemicolon(CHECK_OK); + ExpectSemicolon(); break; } } - DCHECK_EQ(local_names.length(), 1); - module()->AddExport(local_names.first(), - ast_value_factory()->default_string(), default_loc, - zone()); + if (result != nullptr) { + DCHECK_EQ(local_names.length(), 1); + module()->AddExport(local_names.first(), + ast_value_factory()->default_string(), default_loc, + zone()); + } - DCHECK_NOT_NULL(result); return result; } -Statement* Parser::ParseExportDeclaration(bool* ok) { +const AstRawString* Parser::NextInternalNamespaceExportName() { + const char* prefix = ".ns-export"; + std::string s(prefix); + s.append(std::to_string(number_of_named_namespace_exports_++)); + return ast_value_factory()->GetOneByteString(s.c_str()); +} + +void Parser::ParseExportStar() { + int pos = position(); + Consume(Token::MUL); + + if (!FLAG_harmony_namespace_exports || + !PeekContextualKeyword(ast_value_factory()->as_string())) { + // 'export' '*' 'from' ModuleSpecifier ';' + Scanner::Location loc = scanner()->location(); + ExpectContextualKeyword(ast_value_factory()->from_string()); + Scanner::Location specifier_loc = scanner()->peek_location(); + const AstRawString* module_specifier = ParseModuleSpecifier(); + ExpectSemicolon(); + module()->AddStarExport(module_specifier, loc, specifier_loc, zone()); + return; + } + if (!FLAG_harmony_namespace_exports) return; + + // 'export' '*' 'as' IdentifierName 'from' ModuleSpecifier ';' + // + // Desugaring: + // export * as x from "..."; + // ~> + // import * as .x from "..."; export {.x as x}; + + ExpectContextualKeyword(ast_value_factory()->as_string()); + const AstRawString* export_name = ParsePropertyName(); + Scanner::Location export_name_loc = scanner()->location(); + const AstRawString* local_name = NextInternalNamespaceExportName(); + Scanner::Location local_name_loc = Scanner::Location::invalid(); + DeclareVariable(local_name, VariableMode::kConst, kCreatedInitialized, pos); + + ExpectContextualKeyword(ast_value_factory()->from_string()); + Scanner::Location specifier_loc = scanner()->peek_location(); + const AstRawString* module_specifier = ParseModuleSpecifier(); + ExpectSemicolon(); + + module()->AddStarImport(local_name, module_specifier, local_name_loc, + specifier_loc, zone()); + module()->AddExport(local_name, export_name, export_name_loc, zone()); +} + +Statement* Parser::ParseExportDeclaration() { // ExportDeclaration: // 'export' '*' 'from' ModuleSpecifier ';' + // 'export' '*' 'as' IdentifierName 'from' ModuleSpecifier ';' // 'export' ExportClause ('from' ModuleSpecifier)? ';' // 'export' VariableStatement // 'export' Declaration // 'export' 'default' ... (handled in ParseExportDefault) - Expect(Token::EXPORT, CHECK_OK); - int pos = position(); - + Expect(Token::EXPORT); Statement* result = nullptr; ZonePtrList<const AstRawString> names(1, zone()); Scanner::Location loc = scanner()->peek_location(); switch (peek()) { case Token::DEFAULT: - return ParseExportDefault(ok); - - case Token::MUL: { - Consume(Token::MUL); - loc = scanner()->location(); - ExpectContextualKeyword(Token::FROM, CHECK_OK); - Scanner::Location specifier_loc = scanner()->peek_location(); - const AstRawString* module_specifier = ParseModuleSpecifier(CHECK_OK); - ExpectSemicolon(CHECK_OK); - module()->AddStarExport(module_specifier, loc, specifier_loc, zone()); - return factory()->NewEmptyStatement(pos); - } + return ParseExportDefault(); + + case Token::MUL: + ParseExportStar(); + return factory()->EmptyStatement(); case Token::LBRACE: { // There are two cases here: @@ -1229,19 +1283,18 @@ Statement* Parser::ParseExportDeclaration(bool* ok) { // non-FromClause case. Scanner::Location reserved_loc = Scanner::Location::invalid(); ZoneChunkList<ExportClauseData>* export_data = - ParseExportClause(&reserved_loc, CHECK_OK); + ParseExportClause(&reserved_loc); const AstRawString* module_specifier = nullptr; Scanner::Location specifier_loc; - if (CheckContextualKeyword(Token::FROM)) { + if (CheckContextualKeyword(ast_value_factory()->from_string())) { specifier_loc = scanner()->peek_location(); - module_specifier = ParseModuleSpecifier(CHECK_OK); + module_specifier = ParseModuleSpecifier(); } else if (reserved_loc.IsValid()) { // No FromClause, so reserved words are invalid in ExportClause. - *ok = false; ReportMessageAt(reserved_loc, MessageTemplate::kUnexpectedReserved); return nullptr; } - ExpectSemicolon(CHECK_OK); + ExpectSemicolon(); if (module_specifier == nullptr) { for (const ExportClauseData& data : *export_data) { module()->AddExport(data.local_name, data.export_name, data.location, @@ -1256,33 +1309,34 @@ Statement* Parser::ParseExportDeclaration(bool* ok) { zone()); } } - return factory()->NewEmptyStatement(pos); + return factory()->EmptyStatement(); } case Token::FUNCTION: - result = ParseHoistableDeclaration(&names, false, CHECK_OK); + result = ParseHoistableDeclaration(&names, false); break; case Token::CLASS: Consume(Token::CLASS); - result = ParseClassDeclaration(&names, false, CHECK_OK); + result = ParseClassDeclaration(&names, false); break; case Token::VAR: case Token::LET: case Token::CONST: - result = ParseVariableStatement(kStatementListItem, &names, CHECK_OK); + result = ParseVariableStatement(kStatementListItem, &names); break; case Token::ASYNC: - // TODO(neis): Why don't we have the same check here as in - // ParseStatementListItem? Consume(Token::ASYNC); - result = ParseAsyncFunctionDeclaration(&names, false, CHECK_OK); - break; + if (peek() == Token::FUNCTION && + !scanner()->HasLineTerminatorBeforeNext()) { + result = ParseAsyncFunctionDeclaration(&names, false); + break; + } + V8_FALLTHROUGH; default: - *ok = false; ReportUnexpectedToken(scanner()->current_token()); return nullptr; } @@ -1293,126 +1347,117 @@ Statement* Parser::ParseExportDeclaration(bool* ok) { descriptor->AddExport(names[i], names[i], loc, zone()); } - DCHECK_NOT_NULL(result); return result; } -VariableProxy* Parser::NewUnresolved(const AstRawString* name, int begin_pos, - VariableKind kind) { - return scope()->NewUnresolved(factory(), name, begin_pos, kind); -} - -VariableProxy* Parser::NewUnresolved(const AstRawString* name) { - return scope()->NewUnresolved(factory(), name, scanner()->location().beg_pos); -} - -Declaration* Parser::DeclareVariable(const AstRawString* name, - VariableMode mode, int pos, bool* ok) { +VariableProxy* Parser::DeclareVariable(const AstRawString* name, + VariableMode mode, int pos) { return DeclareVariable(name, mode, Variable::DefaultInitializationFlag(mode), - pos, ok); + pos); } -Declaration* Parser::DeclareVariable(const AstRawString* name, - VariableMode mode, InitializationFlag init, - int pos, bool* ok) { +VariableProxy* Parser::DeclareVariable(const AstRawString* name, + VariableMode mode, + InitializationFlag init, int pos) { DCHECK_NOT_NULL(name); - VariableProxy* proxy = factory()->NewVariableProxy( - name, NORMAL_VARIABLE, scanner()->location().beg_pos); + VariableProxy* proxy = + factory()->NewVariableProxy(name, NORMAL_VARIABLE, position()); + bool was_added; + DeclareVariable(proxy, NORMAL_VARIABLE, mode, init, scope(), &was_added, pos, + end_position()); + return proxy; +} + +void Parser::DeclareVariable(VariableProxy* proxy, VariableKind kind, + VariableMode mode, InitializationFlag init, + Scope* scope, bool* was_added, int begin, + int end) { Declaration* declaration; - if (mode == VariableMode::kVar && !scope()->is_declaration_scope()) { - DCHECK(scope()->is_block_scope() || scope()->is_with_scope()); - declaration = factory()->NewNestedVariableDeclaration(proxy, scope(), pos); + if (mode == VariableMode::kVar && !scope->is_declaration_scope()) { + DCHECK(scope->is_block_scope() || scope->is_with_scope()); + declaration = factory()->NewNestedVariableDeclaration(scope, begin); } else { - declaration = factory()->NewVariableDeclaration(proxy, pos); + declaration = factory()->NewVariableDeclaration(begin); } - Declare(declaration, DeclarationDescriptor::NORMAL, mode, init, ok, nullptr, - scanner()->location().end_pos); - if (!*ok) return nullptr; - return declaration; + return Declare(declaration, proxy, kind, mode, init, scope, was_added, end); } -Variable* Parser::Declare(Declaration* declaration, - DeclarationDescriptor::Kind declaration_kind, - VariableMode mode, InitializationFlag init, bool* ok, - Scope* scope, int var_end_pos) { - if (scope == nullptr) { - scope = this->scope(); - } +void Parser::Declare(Declaration* declaration, VariableProxy* proxy, + VariableKind variable_kind, VariableMode mode, + InitializationFlag init, Scope* scope, bool* was_added, + int var_end_pos) { + bool local_ok = true; bool sloppy_mode_block_scope_function_redefinition = false; - Variable* variable = scope->DeclareVariable( - declaration, mode, init, &sloppy_mode_block_scope_function_redefinition, - ok); - if (!*ok) { + scope->DeclareVariable( + declaration, proxy, mode, variable_kind, init, was_added, + &sloppy_mode_block_scope_function_redefinition, &local_ok); + if (!local_ok) { // If we only have the start position of a proxy, we can't highlight the // whole variable name. Pretend its length is 1 so that we highlight at // least the first character. - Scanner::Location loc(declaration->proxy()->position(), - var_end_pos != kNoSourcePosition - ? var_end_pos - : declaration->proxy()->position() + 1); - if (declaration_kind == DeclarationDescriptor::PARAMETER) { + Scanner::Location loc(proxy->position(), var_end_pos != kNoSourcePosition + ? var_end_pos + : proxy->position() + 1); + if (variable_kind == PARAMETER_VARIABLE) { ReportMessageAt(loc, MessageTemplate::kParamDupe); } else { ReportMessageAt(loc, MessageTemplate::kVarRedeclaration, - declaration->proxy()->raw_name()); + declaration->var()->raw_name()); } - return nullptr; - } - if (sloppy_mode_block_scope_function_redefinition) { + } else if (sloppy_mode_block_scope_function_redefinition) { ++use_counts_[v8::Isolate::kSloppyModeBlockScopedFunctionRedefinition]; } - return variable; } -Block* Parser::BuildInitializationBlock( - DeclarationParsingResult* parsing_result, - ZonePtrList<const AstRawString>* names, bool* ok) { - Block* result = factory()->NewBlock(1, true); +Statement* Parser::BuildInitializationBlock( + DeclarationParsingResult* parsing_result) { + ScopedPtrList<Statement> statements(pointer_buffer()); for (const auto& declaration : parsing_result->declarations) { - DeclareAndInitializeVariables(result, &(parsing_result->descriptor), - &declaration, names, CHECK_OK); + InitializeVariables(&statements, parsing_result->descriptor.kind, + &declaration); } - return result; + return factory()->NewBlock(true, statements); } Statement* Parser::DeclareFunction(const AstRawString* variable_name, FunctionLiteral* function, VariableMode mode, - int pos, bool is_sloppy_block_function, - ZonePtrList<const AstRawString>* names, - bool* ok) { + int beg_pos, int end_pos, + bool is_sloppy_block_function, + ZonePtrList<const AstRawString>* names) { VariableProxy* proxy = - factory()->NewVariableProxy(variable_name, NORMAL_VARIABLE, pos); - Declaration* declaration = - factory()->NewFunctionDeclaration(proxy, function, pos); - Declare(declaration, DeclarationDescriptor::NORMAL, mode, kCreatedInitialized, - CHECK_OK); + factory()->NewVariableProxy(variable_name, NORMAL_VARIABLE, beg_pos); + Declaration* declaration = factory()->NewFunctionDeclaration( + function, is_sloppy_block_function, beg_pos); + bool was_added; + Declare(declaration, proxy, NORMAL_VARIABLE, mode, kCreatedInitialized, + scope(), &was_added); if (names) names->Add(variable_name, zone()); if (is_sloppy_block_function) { SloppyBlockFunctionStatement* statement = - factory()->NewSloppyBlockFunctionStatement(); + factory()->NewSloppyBlockFunctionStatement(end_pos); GetDeclarationScope()->DeclareSloppyBlockFunction(variable_name, scope(), statement); return statement; } - return factory()->NewEmptyStatement(kNoSourcePosition); + return factory()->EmptyStatement(); } Statement* Parser::DeclareClass(const AstRawString* variable_name, Expression* value, ZonePtrList<const AstRawString>* names, - int class_token_pos, int end_pos, bool* ok) { - Declaration* decl = DeclareVariable(variable_name, VariableMode::kLet, - class_token_pos, CHECK_OK); - decl->proxy()->var()->set_initializer_position(end_pos); + int class_token_pos, int end_pos) { + VariableProxy* proxy = + DeclareVariable(variable_name, VariableMode::kLet, class_token_pos); + proxy->var()->set_initializer_position(end_pos); if (names) names->Add(variable_name, zone()); - Assignment* assignment = factory()->NewAssignment(Token::INIT, decl->proxy(), - value, class_token_pos); + Assignment* assignment = + factory()->NewAssignment(Token::INIT, proxy, value, class_token_pos); return IgnoreCompletion( factory()->NewExpressionStatement(assignment, kNoSourcePosition)); } -Statement* Parser::DeclareNative(const AstRawString* name, int pos, bool* ok) { +Statement* Parser::DeclareNative(const AstRawString* name, int pos) { // Make sure that the function containing the native declaration // isn't lazily compiled. The extension structures are only // accessible while parsing the first time not when reparsing @@ -1422,18 +1467,17 @@ Statement* Parser::DeclareNative(const AstRawString* name, int pos, bool* ok) { // TODO(1240846): It's weird that native function declarations are // introduced dynamically when we meet their declarations, whereas // other functions are set up when entering the surrounding scope. - Declaration* decl = DeclareVariable(name, VariableMode::kVar, pos, CHECK_OK); + VariableProxy* proxy = DeclareVariable(name, VariableMode::kVar, pos); NativeFunctionLiteral* lit = factory()->NewNativeFunctionLiteral(name, extension_, kNoSourcePosition); return factory()->NewExpressionStatement( - factory()->NewAssignment(Token::INIT, decl->proxy(), lit, - kNoSourcePosition), + factory()->NewAssignment(Token::INIT, proxy, lit, kNoSourcePosition), pos); } void Parser::DeclareLabel(ZonePtrList<const AstRawString>** labels, ZonePtrList<const AstRawString>** own_labels, - VariableProxy* var, bool* ok) { + VariableProxy* var) { DCHECK(IsIdentifier(var)); const AstRawString* label = var->raw_name(); @@ -1444,7 +1488,6 @@ void Parser::DeclareLabel(ZonePtrList<const AstRawString>** labels, // make later anyway so we should go back and fix this then. if (ContainsLabel(*labels, label) || TargetStackContainsLabel(label)) { ReportMessage(MessageTemplate::kLabelRedeclaration, label); - *ok = false; return; } @@ -1464,7 +1507,7 @@ void Parser::DeclareLabel(ZonePtrList<const AstRawString>** labels, // Remove the "ghost" variable that turned out to be a label // from the top scope. This way, we don't try to resolve it // during the scope processing. - scope()->RemoveUnresolved(var); + scope()->DeleteUnresolved(var); } bool Parser::ContainsLabel(ZonePtrList<const AstRawString>* labels, @@ -1513,16 +1556,6 @@ Expression* Parser::RewriteReturn(Expression* return_value, int pos) { return return_value; } -Expression* Parser::RewriteDoExpression(Block* body, int pos, bool* ok) { - Variable* result = NewTemporary(ast_value_factory()->dot_result_string()); - DoExpression* expr = factory()->NewDoExpression(body, result, pos); - if (!Rewriter::Rewrite(this, GetClosureScope(), expr, ast_value_factory())) { - *ok = false; - return nullptr; - } - return expr; -} - Statement* Parser::RewriteSwitchStatement(SwitchStatement* switch_statement, Scope* scope) { // In order to get the CaseClauses to execute in their own lexical scope, @@ -1561,53 +1594,34 @@ Statement* Parser::RewriteSwitchStatement(SwitchStatement* switch_statement, return switch_block; } -void Parser::RewriteCatchPattern(CatchInfo* catch_info, bool* ok) { - if (catch_info->name == nullptr) { - DCHECK_NOT_NULL(catch_info->pattern); - catch_info->name = ast_value_factory()->dot_catch_string(); - } - Variable* catch_variable = - catch_info->scope->DeclareLocal(catch_info->name, VariableMode::kVar); - if (catch_info->pattern != nullptr) { - DeclarationDescriptor descriptor; - descriptor.declaration_kind = DeclarationDescriptor::NORMAL; - descriptor.scope = scope(); - descriptor.mode = VariableMode::kLet; - descriptor.declaration_pos = catch_info->pattern->position(); - descriptor.initialization_pos = catch_info->pattern->position(); - - // Initializer position for variables declared by the pattern. - const int initializer_position = position(); +Block* Parser::RewriteCatchPattern(CatchInfo* catch_info) { + DCHECK_NOT_NULL(catch_info->pattern); - DeclarationParsingResult::Declaration decl( - catch_info->pattern, initializer_position, - factory()->NewVariableProxy(catch_variable)); + // Initializer position for variables declared by the pattern. + const int initializer_position = position(); - catch_info->init_block = factory()->NewBlock(8, true); - DeclareAndInitializeVariables(catch_info->init_block, &descriptor, &decl, - &catch_info->bound_names, ok); - } else { - catch_info->bound_names.Add(catch_info->name, zone()); - } + DeclarationParsingResult::Declaration decl( + catch_info->pattern, initializer_position, + factory()->NewVariableProxy(catch_info->variable)); + + ScopedPtrList<Statement> init_statements(pointer_buffer()); + InitializeVariables(&init_statements, NORMAL_VARIABLE, &decl); + return factory()->NewBlock(true, init_statements); } -void Parser::ValidateCatchBlock(const CatchInfo& catch_info, bool* ok) { - // Check for `catch(e) { let e; }` and similar errors. - Scope* inner_block_scope = catch_info.inner_block->scope(); - if (inner_block_scope != nullptr) { - Declaration* decl = inner_block_scope->CheckLexDeclarationsConflictingWith( - catch_info.bound_names); - if (decl != nullptr) { - const AstRawString* name = decl->proxy()->raw_name(); - int position = decl->proxy()->position(); +void Parser::ReportVarRedeclarationIn(const AstRawString* name, Scope* scope) { + for (Declaration* decl : *scope->declarations()) { + if (decl->var()->raw_name() == name) { + int position = decl->position(); Scanner::Location location = position == kNoSourcePosition ? Scanner::Location::invalid() - : Scanner::Location(position, position + 1); + : Scanner::Location(position, position + name->length()); ReportMessageAt(location, MessageTemplate::kVarRedeclaration, name); - *ok = false; + return; } } + UNREACHABLE(); } Statement* Parser::RewriteTryStatement(Block* try_block, Block* catch_block, @@ -1647,18 +1661,17 @@ Statement* Parser::RewriteTryStatement(Block* try_block, Block* catch_block, } } -void Parser::ParseAndRewriteGeneratorFunctionBody(int pos, FunctionKind kind, - ZonePtrList<Statement>* body, - bool* ok) { +void Parser::ParseAndRewriteGeneratorFunctionBody( + int pos, FunctionKind kind, ScopedPtrList<Statement>* body) { // For ES6 Generators, we just prepend the initial yield. Expression* initial_yield = BuildInitialYield(pos, kind); - body->Add(factory()->NewExpressionStatement(initial_yield, kNoSourcePosition), - zone()); - ParseStatementList(body, Token::RBRACE, ok); + body->Add( + factory()->NewExpressionStatement(initial_yield, kNoSourcePosition)); + ParseStatementList(body, Token::RBRACE); } void Parser::ParseAndRewriteAsyncGeneratorFunctionBody( - int pos, FunctionKind kind, ZonePtrList<Statement>* body, bool* ok) { + int pos, FunctionKind kind, ScopedPtrList<Statement>* body) { // For ES2017 Async Generators, we produce: // // try { @@ -1681,58 +1694,69 @@ void Parser::ParseAndRewriteAsyncGeneratorFunctionBody( // "done" iterator result object containing a Promise-unwrapped value. DCHECK(IsAsyncGeneratorFunction(kind)); - Block* try_block = factory()->NewBlock(3, false); - Expression* initial_yield = BuildInitialYield(pos, kind); - try_block->statements()->Add( - factory()->NewExpressionStatement(initial_yield, kNoSourcePosition), - zone()); - ParseStatementList(try_block->statements(), Token::RBRACE, ok); - if (!*ok) return; + Block* try_block; + { + ScopedPtrList<Statement> statements(pointer_buffer()); + Expression* initial_yield = BuildInitialYield(pos, kind); + statements.Add( + factory()->NewExpressionStatement(initial_yield, kNoSourcePosition)); + ParseStatementList(&statements, Token::RBRACE); + + // Don't create iterator result for async generators, as the resume methods + // will create it. + // TODO(leszeks): This will create another suspend point, which is + // unnecessary if there is already an unconditional return in the body. + Statement* final_return = BuildReturnStatement( + factory()->NewUndefinedLiteral(kNoSourcePosition), kNoSourcePosition); + statements.Add(final_return); - // Don't create iterator result for async generators, as the resume methods - // will create it. - // TODO(leszeks): This will create another suspend point, which is unnecessary - // if there is already an unconditional return in the body. - Statement* final_return = BuildReturnStatement( - factory()->NewUndefinedLiteral(kNoSourcePosition), kNoSourcePosition); - try_block->statements()->Add(final_return, zone()); + try_block = factory()->NewBlock(false, statements); + } // For AsyncGenerators, a top-level catch block will reject the Promise. Scope* catch_scope = NewHiddenCatchScope(); - ZonePtrList<Expression>* reject_args = - new (zone()) ZonePtrList<Expression>(2, zone()); - reject_args->Add(factory()->NewVariableProxy( - function_state_->scope()->generator_object_var()), - zone()); - reject_args->Add(factory()->NewVariableProxy(catch_scope->catch_variable()), - zone()); + Block* catch_block; + { + ScopedPtrList<Expression> reject_args(pointer_buffer()); + reject_args.Add(factory()->NewVariableProxy( + function_state_->scope()->generator_object_var())); + reject_args.Add(factory()->NewVariableProxy(catch_scope->catch_variable())); - Expression* reject_call = factory()->NewCallRuntime( - Runtime::kInlineAsyncGeneratorReject, reject_args, kNoSourcePosition); - Block* catch_block = IgnoreCompletion( - factory()->NewReturnStatement(reject_call, kNoSourcePosition)); + Expression* reject_call = factory()->NewCallRuntime( + Runtime::kInlineAsyncGeneratorReject, reject_args, kNoSourcePosition); + catch_block = IgnoreCompletion( + factory()->NewReturnStatement(reject_call, kNoSourcePosition)); + } - TryStatement* try_catch = factory()->NewTryCatchStatementForAsyncAwait( - try_block, catch_scope, catch_block, kNoSourcePosition); + { + ScopedPtrList<Statement> statements(pointer_buffer()); + TryStatement* try_catch = factory()->NewTryCatchStatementForAsyncAwait( + try_block, catch_scope, catch_block, kNoSourcePosition); + statements.Add(try_catch); + try_block = factory()->NewBlock(false, statements); + } - try_block = factory()->NewBlock(1, false); - try_block->statements()->Add(try_catch, zone()); + Expression* close_call; + { + ScopedPtrList<Expression> close_args(pointer_buffer()); + VariableProxy* call_proxy = factory()->NewVariableProxy( + function_state_->scope()->generator_object_var()); + close_args.Add(call_proxy); + close_call = factory()->NewCallRuntime(Runtime::kInlineGeneratorClose, + close_args, kNoSourcePosition); + } - Block* finally_block = factory()->NewBlock(1, false); - ZonePtrList<Expression>* close_args = - new (zone()) ZonePtrList<Expression>(1, zone()); - VariableProxy* call_proxy = factory()->NewVariableProxy( - function_state_->scope()->generator_object_var()); - close_args->Add(call_proxy, zone()); - Expression* close_call = factory()->NewCallRuntime( - Runtime::kInlineGeneratorClose, close_args, kNoSourcePosition); - finally_block->statements()->Add( - factory()->NewExpressionStatement(close_call, kNoSourcePosition), zone()); + Block* finally_block; + { + ScopedPtrList<Statement> statements(pointer_buffer()); + statements.Add( + factory()->NewExpressionStatement(close_call, kNoSourcePosition)); + finally_block = factory()->NewBlock(false, statements); + } body->Add(factory()->NewTryFinallyStatement(try_block, finally_block, - kNoSourcePosition), - zone()); + kNoSourcePosition)); } void Parser::DeclareFunctionNameVar(const AstRawString* function_name, @@ -1745,81 +1769,6 @@ void Parser::DeclareFunctionNameVar(const AstRawString* function_name, } } -// [if (IteratorType == kNormal)] -// !%_IsJSReceiver(result = iterator.next()) && -// %ThrowIteratorResultNotAnObject(result) -// [else if (IteratorType == kAsync)] -// !%_IsJSReceiver(result = Await(iterator.next())) && -// %ThrowIteratorResultNotAnObject(result) -// [endif] -Expression* Parser::BuildIteratorNextResult(VariableProxy* iterator, - VariableProxy* next, - Variable* result, IteratorType type, - int pos) { - Expression* next_property = factory()->NewResolvedProperty(iterator, next); - ZonePtrList<Expression>* next_arguments = - new (zone()) ZonePtrList<Expression>(0, zone()); - Expression* next_call = - factory()->NewCall(next_property, next_arguments, kNoSourcePosition); - if (type == IteratorType::kAsync) { - function_state_->AddSuspend(); - next_call = factory()->NewAwait(next_call, pos); - } - Expression* result_proxy = factory()->NewVariableProxy(result); - Expression* left = - factory()->NewAssignment(Token::ASSIGN, result_proxy, next_call, pos); - - // %_IsJSReceiver(...) - ZonePtrList<Expression>* is_spec_object_args = - new (zone()) ZonePtrList<Expression>(1, zone()); - is_spec_object_args->Add(left, zone()); - Expression* is_spec_object_call = factory()->NewCallRuntime( - Runtime::kInlineIsJSReceiver, is_spec_object_args, pos); - - // %ThrowIteratorResultNotAnObject(result) - Expression* result_proxy_again = factory()->NewVariableProxy(result); - ZonePtrList<Expression>* throw_arguments = - new (zone()) ZonePtrList<Expression>(1, zone()); - throw_arguments->Add(result_proxy_again, zone()); - Expression* throw_call = factory()->NewCallRuntime( - Runtime::kThrowIteratorResultNotAnObject, throw_arguments, pos); - - return factory()->NewBinaryOperation( - Token::AND, - factory()->NewUnaryOperation(Token::NOT, is_spec_object_call, pos), - throw_call, pos); -} - -Statement* Parser::InitializeForEachStatement(ForEachStatement* stmt, - Expression* each, - Expression* subject, - Statement* body) { - ForOfStatement* for_of = stmt->AsForOfStatement(); - if (for_of != nullptr) { - const bool finalize = true; - return InitializeForOfStatement(for_of, each, subject, body, finalize, - IteratorType::kNormal, each->position()); - } else { - if (each->IsArrayLiteral() || each->IsObjectLiteral()) { - Variable* temp = NewTemporary(ast_value_factory()->empty_string()); - VariableProxy* temp_proxy = factory()->NewVariableProxy(temp); - Expression* assign_each = - RewriteDestructuringAssignment(factory()->NewAssignment( - Token::ASSIGN, each, temp_proxy, kNoSourcePosition)); - auto block = factory()->NewBlock(2, false); - block->statements()->Add( - factory()->NewExpressionStatement(assign_each, kNoSourcePosition), - zone()); - block->statements()->Add(body, zone()); - body = block; - each = factory()->NewVariableProxy(temp); - } - MarkExpressionAsAssigned(each); - stmt->AsForInStatement()->Initialize(each, subject, body); - } - return stmt; -} - // Special case for legacy for // // for (var x = initializer in enumerable) body @@ -1872,65 +1821,25 @@ Block* Parser::RewriteForVarInLegacy(const ForInfo& for_info) { // } void Parser::DesugarBindingInForEachStatement(ForInfo* for_info, Block** body_block, - Expression** each_variable, - bool* ok) { + Expression** each_variable) { DCHECK_EQ(1, for_info->parsing_result.declarations.size()); DeclarationParsingResult::Declaration& decl = for_info->parsing_result.declarations[0]; Variable* temp = NewTemporary(ast_value_factory()->dot_for_string()); - auto each_initialization_block = factory()->NewBlock(1, true); - { - auto descriptor = for_info->parsing_result.descriptor; - descriptor.declaration_pos = kNoSourcePosition; - descriptor.initialization_pos = kNoSourcePosition; - descriptor.scope = scope(); - decl.initializer = factory()->NewVariableProxy(temp); - - bool is_for_var_of = - for_info->mode == ForEachStatement::ITERATE && - for_info->parsing_result.descriptor.mode == VariableMode::kVar; - bool collect_names = - IsLexicalVariableMode(for_info->parsing_result.descriptor.mode) || - is_for_var_of; - - DeclareAndInitializeVariables( - each_initialization_block, &descriptor, &decl, - collect_names ? &for_info->bound_names : nullptr, CHECK_OK_VOID); - - // Annex B.3.5 prohibits the form - // `try {} catch(e) { for (var e of {}); }` - // So if we are parsing a statement like `for (var ... of ...)` - // we need to walk up the scope chain and look for catch scopes - // which have a simple binding, then compare their binding against - // all of the names declared in the init of the for-of we're - // parsing. - if (is_for_var_of) { - Scope* catch_scope = scope(); - while (catch_scope != nullptr && !catch_scope->is_declaration_scope()) { - if (catch_scope->is_catch_scope()) { - auto name = catch_scope->catch_variable()->raw_name(); - // If it's a simple binding and the name is declared in the for loop. - if (name != ast_value_factory()->dot_catch_string() && - for_info->bound_names.Contains(name)) { - ReportMessageAt(for_info->parsing_result.bindings_loc, - MessageTemplate::kVarRedeclaration, name); - *ok = false; - return; - } - } - catch_scope = catch_scope->outer_scope(); - } - } - } + ScopedPtrList<Statement> each_initialization_statements(pointer_buffer()); + decl.initializer = factory()->NewVariableProxy(temp); + InitializeVariables(&each_initialization_statements, NORMAL_VARIABLE, &decl); *body_block = factory()->NewBlock(3, false); - (*body_block)->statements()->Add(each_initialization_block, zone()); + (*body_block) + ->statements() + ->Add(factory()->NewBlock(true, each_initialization_statements), zone()); *each_variable = factory()->NewVariableProxy(temp, for_info->position); } // Create a TDZ for any lexically-bound names in for in/of statements. Block* Parser::CreateForEachStatementTDZ(Block* init_block, - const ForInfo& for_info, bool* ok) { + const ForInfo& for_info) { if (IsLexicalVariableMode(for_info.parsing_result.descriptor.mode)) { DCHECK_NULL(init_block); @@ -1940,142 +1849,17 @@ Block* Parser::CreateForEachStatementTDZ(Block* init_block, // TODO(adamk): This needs to be some sort of special // INTERNAL variable that's invisible to the debugger // but visible to everything else. - Declaration* tdz_decl = - DeclareVariable(for_info.bound_names[i], VariableMode::kLet, - kNoSourcePosition, CHECK_OK); - tdz_decl->proxy()->var()->set_initializer_position(position()); + VariableProxy* tdz_proxy = DeclareVariable( + for_info.bound_names[i], VariableMode::kLet, kNoSourcePosition); + tdz_proxy->var()->set_initializer_position(position()); } } return init_block; } -Statement* Parser::InitializeForOfStatement( - ForOfStatement* for_of, Expression* each, Expression* iterable, - Statement* body, bool finalize, IteratorType type, int next_result_pos) { - // Create the auxiliary expressions needed for iterating over the iterable, - // and initialize the given ForOfStatement with them. - // If finalize is true, also instrument the loop with code that performs the - // proper ES6 iterator finalization. In that case, the result is not - // immediately a ForOfStatement. - const int nopos = kNoSourcePosition; - auto avfactory = ast_value_factory(); - - Variable* iterator = NewTemporary(avfactory->dot_iterator_string()); - Variable* next = NewTemporary(avfactory->empty_string()); - Variable* result = NewTemporary(avfactory->dot_result_string()); - Variable* completion = NewTemporary(avfactory->empty_string()); - - // iterator = GetIterator(iterable, type) - Expression* assign_iterator; - { - assign_iterator = factory()->NewAssignment( - Token::ASSIGN, factory()->NewVariableProxy(iterator), - factory()->NewGetIterator(iterable, type, iterable->position()), - iterable->position()); - } - - Expression* assign_next; - { - assign_next = factory()->NewAssignment( - Token::ASSIGN, factory()->NewVariableProxy(next), - factory()->NewProperty(factory()->NewVariableProxy(iterator), - factory()->NewStringLiteral( - avfactory->next_string(), kNoSourcePosition), - kNoSourcePosition), - kNoSourcePosition); - } - - // [if (IteratorType == kNormal)] - // !%_IsJSReceiver(result = iterator.next()) && - // %ThrowIteratorResultNotAnObject(result) - // [else if (IteratorType == kAsync)] - // !%_IsJSReceiver(result = Await(iterator.next())) && - // %ThrowIteratorResultNotAnObject(result) - // [endif] - Expression* next_result; - { - VariableProxy* iterator_proxy = factory()->NewVariableProxy(iterator); - VariableProxy* next_proxy = factory()->NewVariableProxy(next); - next_result = BuildIteratorNextResult(iterator_proxy, next_proxy, result, - type, next_result_pos); - } - - // result.done - Expression* result_done; - { - Expression* done_literal = factory()->NewStringLiteral( - ast_value_factory()->done_string(), kNoSourcePosition); - Expression* result_proxy = factory()->NewVariableProxy(result); - result_done = - factory()->NewProperty(result_proxy, done_literal, kNoSourcePosition); - } - - // result.value - Expression* result_value; - { - Expression* value_literal = - factory()->NewStringLiteral(avfactory->value_string(), nopos); - Expression* result_proxy = factory()->NewVariableProxy(result); - result_value = factory()->NewProperty(result_proxy, value_literal, nopos); - } - - // {{tmp = #result_value, completion = kAbruptCompletion, tmp}} - // Expression* result_value (gets overwritten) - if (finalize) { - Variable* tmp = NewTemporary(avfactory->empty_string()); - Expression* save_result = factory()->NewAssignment( - Token::ASSIGN, factory()->NewVariableProxy(tmp), result_value, nopos); - - Expression* set_completion_abrupt = factory()->NewAssignment( - Token::ASSIGN, factory()->NewVariableProxy(completion), - factory()->NewSmiLiteral(Parser::kAbruptCompletion, nopos), nopos); - - result_value = factory()->NewBinaryOperation(Token::COMMA, save_result, - set_completion_abrupt, nopos); - result_value = factory()->NewBinaryOperation( - Token::COMMA, result_value, factory()->NewVariableProxy(tmp), nopos); - } - - // each = #result_value; - Expression* assign_each; - { - assign_each = - factory()->NewAssignment(Token::ASSIGN, each, result_value, nopos); - if (each->IsArrayLiteral() || each->IsObjectLiteral()) { - assign_each = RewriteDestructuringAssignment(assign_each->AsAssignment()); - } - } - - // {{completion = kNormalCompletion;}} - Statement* set_completion_normal; - if (finalize) { - Expression* proxy = factory()->NewVariableProxy(completion); - Expression* assignment = factory()->NewAssignment( - Token::ASSIGN, proxy, - factory()->NewSmiLiteral(Parser::kNormalCompletion, nopos), nopos); - - set_completion_normal = - IgnoreCompletion(factory()->NewExpressionStatement(assignment, nopos)); - } - - // { #loop-body; #set_completion_normal } - // Statement* body (gets overwritten) - if (finalize) { - Block* block = factory()->NewBlock(2, false); - block->statements()->Add(body, zone()); - block->statements()->Add(set_completion_normal, zone()); - body = block; - } - - for_of->Initialize(body, iterator, assign_iterator, assign_next, next_result, - result_done, assign_each); - return finalize ? FinalizeForOfStatement(for_of, completion, type, nopos) - : for_of; -} - Statement* Parser::DesugarLexicalBindingsInForStatement( ForStatement* loop, Statement* init, Expression* cond, Statement* next, - Statement* body, Scope* inner_scope, const ForInfo& for_info, bool* ok) { + Statement* body, Scope* inner_scope, const ForInfo& for_info) { // ES6 13.7.4.8 specifies that on each loop iteration the let variables are // copied into a new environment. Moreover, the "next" statement must be // evaluated not in the environment of the just completed iteration but in @@ -2177,18 +1961,18 @@ Statement* Parser::DesugarLexicalBindingsInForStatement( // For each let variable x: // make statement: let/const x = temp_x. for (int i = 0; i < for_info.bound_names.length(); i++) { - Declaration* decl = DeclareVariable( + VariableProxy* proxy = DeclareVariable( for_info.bound_names[i], for_info.parsing_result.descriptor.mode, - kNoSourcePosition, CHECK_OK); - inner_vars.Add(decl->proxy()->var(), zone()); + kNoSourcePosition); + inner_vars.Add(proxy->var(), zone()); VariableProxy* temp_proxy = factory()->NewVariableProxy(temps.at(i)); Assignment* assignment = factory()->NewAssignment( - Token::INIT, decl->proxy(), temp_proxy, kNoSourcePosition); + Token::INIT, proxy, temp_proxy, kNoSourcePosition); Statement* assignment_statement = factory()->NewExpressionStatement(assignment, kNoSourcePosition); int declaration_pos = for_info.parsing_result.descriptor.declaration_pos; DCHECK_NE(declaration_pos, kNoSourcePosition); - decl->proxy()->var()->set_initializer_position(declaration_pos); + proxy->var()->set_initializer_position(declaration_pos); ignore_completion_block->statements()->Add(assignment_statement, zone()); } @@ -2234,7 +2018,7 @@ Statement* Parser::DesugarLexicalBindingsInForStatement( if (cond) { Statement* stop = factory()->NewBreakStatement(outer_loop, kNoSourcePosition); - Statement* noop = factory()->NewEmptyStatement(kNoSourcePosition); + Statement* noop = factory()->EmptyStatement(); ignore_completion_block->statements()->Add( factory()->NewIfStatement(cond, noop, stop, cond->position()), zone()); @@ -2297,7 +2081,7 @@ Statement* Parser::DesugarLexicalBindingsInForStatement( } Statement* stop = factory()->NewBreakStatement(outer_loop, kNoSourcePosition); - Statement* empty = factory()->NewEmptyStatement(kNoSourcePosition); + Statement* empty = factory()->EmptyStatement(); Statement* if_flag_break = factory()->NewIfStatement(compare, stop, empty, kNoSourcePosition); inner_block->statements()->Add(IgnoreCompletion(if_flag_break), zone()); @@ -2311,9 +2095,19 @@ Statement* Parser::DesugarLexicalBindingsInForStatement( return outer_block; } +void ParserFormalParameters::ValidateDuplicate(Parser* parser) const { + if (has_duplicate()) { + parser->ReportMessageAt(duplicate_loc, MessageTemplate::kParamDupe); + } +} +void ParserFormalParameters::ValidateStrictMode(Parser* parser) const { + if (strict_error_loc.IsValid()) { + parser->ReportMessageAt(strict_error_loc, strict_error_message); + } +} + void Parser::AddArrowFunctionFormalParameters( - ParserFormalParameters* parameters, Expression* expr, int end_pos, - bool* ok) { + ParserFormalParameters* parameters, Expression* expr, int end_pos) { // ArrowFunctionFormals :: // Nary(Token::COMMA, VariableProxy*, Tail) // Binary(Token::COMMA, NonTailArrowFunctionFormals, Tail) @@ -2339,11 +2133,11 @@ void Parser::AddArrowFunctionFormalParameters( // the first child expression. Expression* next = nary->first(); for (size_t i = 0; i < nary->subsequent_length(); ++i) { - AddArrowFunctionFormalParameters( - parameters, next, nary->subsequent_op_position(i), CHECK_OK_VOID); + AddArrowFunctionFormalParameters(parameters, next, + nary->subsequent_op_position(i)); next = nary->subsequent(i); } - AddArrowFunctionFormalParameters(parameters, next, end_pos, CHECK_OK_VOID); + AddArrowFunctionFormalParameters(parameters, next, end_pos); return; } @@ -2357,8 +2151,7 @@ void Parser::AddArrowFunctionFormalParameters( Expression* left = binop->left(); Expression* right = binop->right(); int comma_pos = binop->position(); - AddArrowFunctionFormalParameters(parameters, left, comma_pos, - CHECK_OK_VOID); + AddArrowFunctionFormalParameters(parameters, left, comma_pos); // LHS of comma expression should be unparenthesized. expr = right; } @@ -2376,11 +2169,6 @@ void Parser::AddArrowFunctionFormalParameters( Expression* initializer = nullptr; if (expr->IsAssignment()) { - if (expr->IsRewritableExpression()) { - // This expression was parsed as a possible destructuring assignment. - // Mark it as already-rewritten to avoid an unnecessary visit later. - expr->AsRewritableExpression()->set_rewritten(); - } Assignment* assignment = expr->AsAssignment(); DCHECK(!assignment->IsCompoundAssignment()); initializer = assignment->value(); @@ -2393,26 +2181,19 @@ void Parser::AddArrowFunctionFormalParameters( void Parser::DeclareArrowFunctionFormalParameters( ParserFormalParameters* parameters, Expression* expr, - const Scanner::Location& params_loc, Scanner::Location* duplicate_loc, - bool* ok) { - if (expr->IsEmptyParentheses()) return; + const Scanner::Location& params_loc) { + if (expr->IsEmptyParentheses() || has_error()) return; - AddArrowFunctionFormalParameters(parameters, expr, params_loc.end_pos, - CHECK_OK_VOID); + AddArrowFunctionFormalParameters(parameters, expr, params_loc.end_pos); if (parameters->arity > Code::kMaxArguments) { ReportMessageAt(params_loc, MessageTemplate::kMalformedArrowFunParamList); - *ok = false; return; } - bool has_duplicate = false; - DeclareFormalParameters(parameters->scope, parameters->params, - parameters->is_simple, &has_duplicate); - if (has_duplicate) { - *duplicate_loc = scanner()->location(); - } - DCHECK_EQ(parameters->is_simple, parameters->scope->has_simple_parameters()); + DeclareFormalParameters(parameters); + DCHECK_IMPLIES(parameters->is_simple, + parameters->scope->has_simple_parameters()); } void Parser::PrepareGeneratorVariables() { @@ -2428,7 +2209,7 @@ FunctionLiteral* Parser::ParseFunctionLiteral( FunctionNameValidity function_name_validity, FunctionKind kind, int function_token_pos, FunctionLiteral::FunctionType function_type, LanguageMode language_mode, - ZonePtrList<const AstRawString>* arguments_for_wrapped_function, bool* ok) { + ZonePtrList<const AstRawString>* arguments_for_wrapped_function) { // Function :: // '(' FormalParameterList? ')' '{' FunctionBody '}' // @@ -2493,18 +2274,16 @@ FunctionLiteral* Parser::ParseFunctionLiteral( // parenthesis before the function means that it will be called // immediately). bar can be parsed lazily, but we need to parse it in a mode // that tracks unresolved variables. - DCHECK_IMPLIES(parse_lazily(), FLAG_lazy); - DCHECK_IMPLIES(parse_lazily(), allow_lazy_); + DCHECK_IMPLIES(parse_lazily(), info()->allow_lazy_compile()); + DCHECK_IMPLIES(parse_lazily(), has_error() || allow_lazy_); DCHECK_IMPLIES(parse_lazily(), extension_ == nullptr); const bool is_lazy = eager_compile_hint == FunctionLiteral::kShouldLazyCompile; const bool is_top_level = AllowsLazyParsingWithoutUnresolvedVariables(); + const bool is_eager_top_level_function = !is_lazy && is_top_level; const bool is_lazy_top_level_function = is_lazy && is_top_level; const bool is_lazy_inner_function = is_lazy && !is_top_level; - const bool is_expression = - function_type == FunctionLiteral::kAnonymousExpression || - function_type == FunctionLiteral::kNamedExpression; RuntimeCallTimerScope runtime_timer( runtime_call_stats_, @@ -2531,22 +2310,28 @@ FunctionLiteral* Parser::ParseFunctionLiteral( // Inner functions will be parsed using a temporary Zone. After parsing, we // will migrate unresolved variable into a Scope in the main Zone. - const bool should_preparse_inner = - parse_lazily() && FLAG_lazy_inner_functions && is_lazy_inner_function && - (!is_expression || FLAG_aggressive_lazy_inner_functions); + const bool should_preparse_inner = parse_lazily() && is_lazy_inner_function; + + // If parallel compile tasks are enabled, and the function is an eager + // top level function, then we can pre-parse the function and parse / compile + // in a parallel task on a worker thread. + bool should_post_parallel_task = + parse_lazily() && is_eager_top_level_function && + FLAG_parallel_compile_tasks && info()->parallel_tasks() && + scanner()->stream()->can_be_cloned_for_parallel_access(); // This may be modified later to reflect preparsing decision taken - bool should_preparse = - (parse_lazily() && is_lazy_top_level_function) || should_preparse_inner; + bool should_preparse = (parse_lazily() && is_lazy_top_level_function) || + should_preparse_inner || should_post_parallel_task; - ZonePtrList<Statement>* body = nullptr; + ScopedPtrList<Statement> body(pointer_buffer()); int expected_property_count = -1; int suspend_count = -1; int num_parameters = -1; int function_length = -1; bool has_duplicate_parameters = false; int function_literal_id = GetNextFunctionLiteralId(); - ProducedPreParsedScopeData* produced_preparsed_scope_data = nullptr; + ProducedPreparseData* produced_preparse_data = nullptr; // This Scope lives in the main zone. We'll migrate data into that zone later. Zone* parse_zone = should_preparse ? &preparser_zone_ : zone(); @@ -2556,7 +2341,10 @@ FunctionLiteral* Parser::ParseFunctionLiteral( scope->SetScopeName(function_name); #endif - if (!is_wrapped) Expect(Token::LPAREN, CHECK_OK); + if (!is_wrapped && V8_UNLIKELY(!Check(Token::LPAREN))) { + ReportUnexpectedToken(Next()); + return nullptr; + } scope->set_start_position(position()); // Eager or lazy parse? If is_lazy_top_level_function, we'll parse @@ -2565,15 +2353,18 @@ FunctionLiteral* Parser::ParseFunctionLiteral( // 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); + should_preparse && SkipFunction(function_name, kind, function_type, scope, + &num_parameters, &produced_preparse_data); + 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 skipping aborted, it rewound the scanner until before the LPAREN. + // Consume it in that case. + if (should_preparse) Consume(Token::LPAREN); + should_post_parallel_task = false; + ParseFunction(&body, function_name, pos, kind, function_type, scope, + &num_parameters, &function_length, &has_duplicate_parameters, + &expected_property_count, &suspend_count, + arguments_for_wrapped_function); } if (V8_UNLIKELY(FLAG_log_function_events)) { @@ -2589,16 +2380,12 @@ FunctionLiteral* Parser::ParseFunctionLiteral( 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}}; + const RuntimeCallCounterId counters[2] = { + 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_]); + counters[parsing_on_main_thread_]); } } @@ -2606,13 +2393,12 @@ FunctionLiteral* Parser::ParseFunctionLiteral( // 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); + function_name_location); if (is_strict(language_mode)) { - CheckStrictOctalLiteral(scope->start_position(), scope->end_position(), - CHECK_OK); + CheckStrictOctalLiteral(scope->start_position(), scope->end_position()); } - CheckConflictingVarDeclarations(scope, CHECK_OK); + CheckConflictingVarDeclarations(scope); FunctionLiteral::ParameterFlag duplicate_parameters = has_duplicate_parameters ? FunctionLiteral::kHasDuplicateParameters @@ -2622,23 +2408,25 @@ FunctionLiteral* Parser::ParseFunctionLiteral( FunctionLiteral* function_literal = factory()->NewFunctionLiteral( function_name, scope, body, expected_property_count, num_parameters, function_length, duplicate_parameters, function_type, eager_compile_hint, - pos, true, function_literal_id, produced_preparsed_scope_data); + pos, true, function_literal_id, produced_preparse_data); function_literal->set_function_token_position(function_token_pos); function_literal->set_suspend_count(suspend_count); + if (should_post_parallel_task) { + // Start a parallel parse / compile task on the compiler dispatcher. + info()->parallel_tasks()->Enqueue(info(), function_name, function_literal); + } + if (should_infer_name) { fni_.AddFunction(function_literal); } return function_literal; } -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, - FunctionLiteral::EagerCompileHint* hint, bool* ok) { +bool Parser::SkipFunction(const AstRawString* function_name, FunctionKind kind, + FunctionLiteral::FunctionType function_type, + DeclarationScope* function_scope, int* num_parameters, + ProducedPreparseData** produced_preparse_data) { FunctionState function_state(&function_state_, &scope_, function_scope); function_scope->set_zone(&preparser_zone_); @@ -2649,23 +2437,23 @@ bool Parser::SkipFunction( scanner()->current_token() == Token::ARROW); // FIXME(marja): There are 2 ways to skip functions now. Unify them. - if (consumed_preparsed_scope_data_) { - DCHECK(FLAG_preparser_scope_analysis); + if (consumed_preparse_data_) { int end_position; LanguageMode language_mode; int num_inner_functions; bool uses_super_property; - *produced_preparsed_scope_data = - consumed_preparsed_scope_data_->GetDataForSkippableFunction( + if (stack_overflow()) return true; + *produced_preparse_data = + consumed_preparse_data_->GetDataForSkippableFunction( main_zone(), function_scope->start_position(), &end_position, num_parameters, &num_inner_functions, &uses_super_property, &language_mode); - function_scope->outer_scope()->SetMustUsePreParsedScopeData(); + function_scope->outer_scope()->SetMustUsePreparseData(); function_scope->set_is_skipped_function(true); function_scope->set_end_position(end_position); scanner()->SeekForward(end_position - 1); - Expect(Token::RBRACE, CHECK_OK_VALUE(kLazyParsingComplete)); + Expect(Token::RBRACE); SetLanguageMode(function_scope, language_mode); if (uses_super_property) { function_scope->RecordSuperPropertyUsage(); @@ -2676,53 +2464,47 @@ bool Parser::SkipFunction( } Scanner::BookmarkScope bookmark(scanner()); - bookmark.Set(); + bookmark.Set(function_scope->start_position()); // 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"); - // Aborting inner function preparsing would leave scopes in an inconsistent - // state; we don't parse inner functions in the abortable mode anyway. - DCHECK(!is_inner_function || !may_abort); - PreParser::PreParseResult result = reusable_preparser()->PreParseFunction( - function_name, kind, function_type, function_scope, is_inner_function, - may_abort, use_counts_, produced_preparsed_scope_data, this->script_id()); - - // Return immediately if pre-parser decided to abort parsing. - if (result == PreParser::kPreParseAbort) { - bookmark.Apply(); - function_scope->ResetAfterPreparsing(ast_value_factory(), true); - *hint = FunctionLiteral::kShouldEagerCompile; - return false; - } + function_name, kind, function_type, function_scope, use_counts_, + produced_preparse_data, this->script_id()); if (result == PreParser::kPreParseStackOverflow) { // Propagate stack overflow. set_stack_overflow(); - *ok = false; - } else if (pending_error_handler()->ErrorUnidentifiableByPreParser()) { + } else if (pending_error_handler()->has_error_unidentifiable_by_preparser()) { + // Make sure we don't re-preparse inner functions of the aborted function. + // The error might be in an inner function. + allow_lazy_ = false; + mode_ = PARSE_EAGERLY; + DCHECK(!pending_error_handler()->stack_overflow()); // 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(); + pending_error_handler()->clear_unidentifiable_error(); return false; } else if (pending_error_handler()->has_pending_error()) { - *ok = false; + DCHECK(!pending_error_handler()->stack_overflow()); + DCHECK(has_error()); } else { + DCHECK(!pending_error_handler()->stack_overflow()); 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)); + Expect(Token::RBRACE); 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()); + function_scope->AnalyzePartially(this, factory()); } return true; @@ -2734,7 +2516,7 @@ Statement* Parser::BuildAssertIsCoercible(Variable* var, // throw /* type error kNonCoercible) */; auto source_position = pattern->position(); const AstRawString* property = ast_value_factory()->empty_string(); - MessageTemplate::Template msg = MessageTemplate::kNonCoercible; + MessageTemplate msg = MessageTemplate::kNonCoercible; for (ObjectLiteralProperty* literal_property : *pattern->properties()) { Expression* key = literal_property->key(); if (key->IsPropertyName()) { @@ -2759,7 +2541,7 @@ Statement* Parser::BuildAssertIsCoercible(Variable* var, IfStatement* if_statement = factory()->NewIfStatement( condition, factory()->NewExpressionStatement(throw_type_error, kNoSourcePosition), - factory()->NewEmptyStatement(kNoSourcePosition), kNoSourcePosition); + factory()->EmptyStatement(), kNoSourcePosition); return if_statement; } @@ -2774,13 +2556,6 @@ class InitializerRewriter final // called by the base class (template). friend class AstTraversalVisitor<InitializerRewriter>; - // Just rewrite destructuring assignments wrapped in RewritableExpressions. - void VisitRewritableExpression(RewritableExpression* to_rewrite) { - if (to_rewrite->is_rewritten()) return; - parser_->RewriteDestructuringAssignment(to_rewrite); - AstTraversalVisitor::VisitRewritableExpression(to_rewrite); - } - // Code in function literals does not need to be eagerly rewritten, it will be // rewritten when scheduled. void VisitFunctionLiteral(FunctionLiteral* expr) {} @@ -2789,60 +2564,58 @@ class InitializerRewriter final }; void Parser::RewriteParameterInitializer(Expression* expr) { + if (has_error()) return; InitializerRewriter rewriter(stack_limit_, expr, this); rewriter.Run(); } - Block* Parser::BuildParameterInitializationBlock( - const ParserFormalParameters& parameters, bool* ok) { + const ParserFormalParameters& parameters) { DCHECK(!parameters.is_simple); DCHECK(scope()->is_function_scope()); DCHECK_EQ(scope(), parameters.scope); - Block* init_block = factory()->NewBlock(parameters.num_parameters(), true); + ScopedPtrList<Statement> init_statements(pointer_buffer()); int index = 0; for (auto parameter : parameters.params) { - DeclarationDescriptor descriptor; - descriptor.declaration_kind = DeclarationDescriptor::PARAMETER; - descriptor.scope = scope(); - descriptor.mode = VariableMode::kLet; - descriptor.declaration_pos = parameter->pattern->position(); - // The position that will be used by the AssignmentExpression - // which copies from the temp parameter to the pattern. - // - // TODO(adamk): Should this be kNoSourcePosition, since - // it's just copying from a temp var to the real param var? - descriptor.initialization_pos = parameter->pattern->position(); Expression* initial_value = factory()->NewVariableProxy(parameters.scope->parameter(index)); - if (parameter->initializer != nullptr) { + if (parameter->initializer() != nullptr) { // IS_UNDEFINED($param) ? initializer : $param - // Ensure initializer is rewritten - RewriteParameterInitializer(parameter->initializer); + if (parameter->initializer()->IsClassLiteral()) { + // Initializers could have their own scopes. So set the scope + // here if necessary. + BlockState block_state( + &scope_, parameter->initializer()->AsClassLiteral()->scope()); + + // Ensure initializer is rewritten + RewriteParameterInitializer(parameter->initializer()); + } else { + // Ensure initializer is rewritten + RewriteParameterInitializer(parameter->initializer()); + } auto condition = factory()->NewCompareOperation( Token::EQ_STRICT, factory()->NewVariableProxy(parameters.scope->parameter(index)), factory()->NewUndefinedLiteral(kNoSourcePosition), kNoSourcePosition); - initial_value = factory()->NewConditional( - condition, parameter->initializer, initial_value, kNoSourcePosition); - descriptor.initialization_pos = parameter->initializer->position(); + initial_value = + factory()->NewConditional(condition, parameter->initializer(), + initial_value, kNoSourcePosition); } Scope* param_scope = scope(); - Block* param_block = init_block; + ScopedPtrList<Statement>* param_init_statements = &init_statements; + + base::Optional<ScopedPtrList<Statement>> non_simple_param_init_statements; if (!parameter->is_simple() && scope()->AsDeclarationScope()->calls_sloppy_eval()) { param_scope = NewVarblockScope(); - param_scope->set_start_position(descriptor.initialization_pos); + param_scope->set_start_position(parameter->pattern->position()); param_scope->set_end_position(parameter->initializer_end_position); param_scope->RecordEvalCall(); - param_block = factory()->NewBlock(8, true); - param_block->set_scope(param_scope); - // Pass the appropriate scope in so that PatternRewriter can appropriately - // rewrite inner initializers of the pattern to param_scope - descriptor.scope = param_scope; + non_simple_param_init_statements.emplace(pointer_buffer()); + param_init_statements = &non_simple_param_init_statements.value(); // Rewrite the outer initializer to point to param_scope ReparentExpressionScope(stack_limit(), initial_value, param_scope); } @@ -2850,122 +2623,68 @@ Block* Parser::BuildParameterInitializationBlock( BlockState block_state(&scope_, param_scope); DeclarationParsingResult::Declaration decl( parameter->pattern, parameter->initializer_end_position, initial_value); - DeclareAndInitializeVariables(param_block, &descriptor, &decl, nullptr, - CHECK_OK); - if (param_block != init_block) { + InitializeVariables(param_init_statements, PARAMETER_VARIABLE, &decl); + + if (param_init_statements != &init_statements) { + DCHECK_EQ(param_init_statements, + &non_simple_param_init_statements.value()); + Block* param_block = + factory()->NewBlock(true, *non_simple_param_init_statements); + non_simple_param_init_statements.reset(); + param_block->set_scope(param_scope); param_scope = param_scope->FinalizeBlockScope(); - if (param_scope != nullptr) { - CheckConflictingVarDeclarations(param_scope, CHECK_OK); - } - init_block->statements()->Add(param_block, zone()); + init_statements.Add(param_block); } ++index; } - return init_block; + return factory()->NewBlock(true, init_statements); } Scope* Parser::NewHiddenCatchScope() { Scope* catch_scope = NewScopeWithParent(scope(), CATCH_SCOPE); + bool was_added; catch_scope->DeclareLocal(ast_value_factory()->dot_catch_string(), - VariableMode::kVar); + VariableMode::kVar, NORMAL_VARIABLE, &was_added); + DCHECK(was_added); catch_scope->set_is_hidden(); return catch_scope; } Block* Parser::BuildRejectPromiseOnException(Block* inner_block) { - // .promise = %AsyncFunctionPromiseCreate(); // try { // <inner_block> // } catch (.catch) { - // %RejectPromise(.promise, .catch); - // return .promise; - // } finally { - // %AsyncFunctionPromiseRelease(.promise); + // return %_AsyncFunctionReject(.generator_object, .catch, can_suspend); // } - Block* result = factory()->NewBlock(2, true); - - // .promise = %AsyncFunctionPromiseCreate(); - Statement* set_promise; - { - Expression* create_promise = factory()->NewCallRuntime( - Context::ASYNC_FUNCTION_PROMISE_CREATE_INDEX, - new (zone()) ZonePtrList<Expression>(0, zone()), kNoSourcePosition); - Assignment* assign_promise = factory()->NewAssignment( - Token::ASSIGN, factory()->NewVariableProxy(PromiseVariable()), - create_promise, kNoSourcePosition); - set_promise = - factory()->NewExpressionStatement(assign_promise, kNoSourcePosition); - } - result->statements()->Add(set_promise, zone()); + Block* result = factory()->NewBlock(1, true); - // catch (.catch) { return %RejectPromise(.promise, .catch), .promise } + // catch (.catch) { + // return %_AsyncFunctionReject(.generator_object, .catch, can_suspend) + // } Scope* catch_scope = NewHiddenCatchScope(); - Expression* promise_reject = BuildRejectPromise( - factory()->NewVariableProxy(catch_scope->catch_variable()), - kNoSourcePosition); + Expression* reject_promise; + { + ScopedPtrList<Expression> args(pointer_buffer()); + args.Add(factory()->NewVariableProxy( + function_state_->scope()->generator_object_var())); + args.Add(factory()->NewVariableProxy(catch_scope->catch_variable())); + args.Add(factory()->NewBooleanLiteral(function_state_->CanSuspend(), + kNoSourcePosition)); + reject_promise = factory()->NewCallRuntime( + Runtime::kInlineAsyncFunctionReject, args, kNoSourcePosition); + } Block* catch_block = IgnoreCompletion( - factory()->NewReturnStatement(promise_reject, kNoSourcePosition)); + factory()->NewReturnStatement(reject_promise, kNoSourcePosition)); TryStatement* try_catch_statement = factory()->NewTryCatchStatementForAsyncAwait( inner_block, catch_scope, catch_block, kNoSourcePosition); - - // There is no TryCatchFinally node, so wrap it in an outer try/finally - Block* outer_try_block = IgnoreCompletion(try_catch_statement); - - // finally { %AsyncFunctionPromiseRelease(.promise, can_suspend) } - Block* finally_block; - { - ZonePtrList<Expression>* args = - new (zone()) ZonePtrList<Expression>(1, zone()); - args->Add(factory()->NewVariableProxy(PromiseVariable()), zone()); - args->Add(factory()->NewBooleanLiteral(function_state_->CanSuspend(), - kNoSourcePosition), - zone()); - Expression* call_promise_release = factory()->NewCallRuntime( - Context::ASYNC_FUNCTION_PROMISE_RELEASE_INDEX, args, kNoSourcePosition); - Statement* promise_release = factory()->NewExpressionStatement( - call_promise_release, kNoSourcePosition); - finally_block = IgnoreCompletion(promise_release); - } - - Statement* try_finally_statement = factory()->NewTryFinallyStatement( - outer_try_block, finally_block, kNoSourcePosition); - - result->statements()->Add(try_finally_statement, zone()); + result->statements()->Add(try_catch_statement, zone()); return result; } -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 - // already happened for the exception that got us here. - ZonePtrList<Expression>* args = - new (zone()) ZonePtrList<Expression>(3, zone()); - args->Add(factory()->NewVariableProxy(PromiseVariable()), zone()); - args->Add(value, zone()); - args->Add(factory()->NewBooleanLiteral(false, pos), zone()); - Expression* call_runtime = - factory()->NewCallRuntime(Runtime::kInlineRejectPromise, args, pos); - return factory()->NewBinaryOperation( - Token::COMMA, call_runtime, - factory()->NewVariableProxy(PromiseVariable()), pos); -} - -Variable* Parser::PromiseVariable() { - // Based on the various compilation paths, there are many different code - // paths which may be the first to access the Promise temporary. Whichever - // comes first should create it and stash it in the FunctionState. - Variable* promise = function_state_->scope()->promise_var(); - if (promise == nullptr) { - promise = function_state_->scope()->DeclarePromiseVar( - ast_value_factory()->dot_promise_string()); - } - return promise; -} - Expression* Parser::BuildInitialYield(int pos, FunctionKind kind) { Expression* yield_result = factory()->NewVariableProxy( function_state_->scope()->generator_object_var()); @@ -2977,22 +2696,19 @@ Expression* Parser::BuildInitialYield(int pos, FunctionKind kind) { Suspend::kOnExceptionThrow); } -ZonePtrList<Statement>* Parser::ParseFunction( - const AstRawString* function_name, int pos, FunctionKind kind, - FunctionLiteral::FunctionType function_type, +void Parser::ParseFunction( + ScopedPtrList<Statement>* body, const AstRawString* function_name, int pos, + FunctionKind kind, FunctionLiteral::FunctionType function_type, DeclarationScope* function_scope, int* num_parameters, int* function_length, bool* has_duplicate_parameters, int* expected_property_count, int* suspend_count, - ZonePtrList<const AstRawString>* arguments_for_wrapped_function, bool* ok) { + ZonePtrList<const AstRawString>* arguments_for_wrapped_function) { ParsingModeScope mode(this, allow_lazy_ ? PARSE_LAZILY : PARSE_EAGERLY); FunctionState function_state(&function_state_, &scope_, function_scope); bool is_wrapped = function_type == FunctionLiteral::kWrapped; - DuplicateFinder duplicate_finder; - ExpressionClassifier formals_classifier(this, &duplicate_finder); - int expected_parameters_end_pos = parameters_end_pos_; if (expected_parameters_end_pos != kNoSourcePosition) { // This is the first function encountered in a CreateDynamicFunction eval. @@ -3004,173 +2720,147 @@ ZonePtrList<Statement>* Parser::ParseFunction( ParserFormalParameters formals(function_scope); - if (is_wrapped) { - // For a function implicitly wrapped in function header and footer, the - // function arguments are provided separately to the source, and are - // declared directly here. - int arguments_length = arguments_for_wrapped_function->length(); - for (int i = 0; i < arguments_length; i++) { - const bool is_rest = false; - Expression* argument = ExpressionFromIdentifier( - arguments_for_wrapped_function->at(i), kNoSourcePosition); - AddFormalParameter(&formals, argument, NullExpression(), - kNoSourcePosition, is_rest); - } - DCHECK_EQ(arguments_length, formals.num_parameters()); - DeclareFormalParameters(formals.scope, formals.params, formals.is_simple); - } else { - // For a regular function, the function arguments are parsed from source. - DCHECK_NULL(arguments_for_wrapped_function); - ParseFormalParameterList(&formals, CHECK_OK); - if (expected_parameters_end_pos != kNoSourcePosition) { - // Check for '(' or ')' shenanigans in the parameter string for dynamic - // functions. - int position = peek_position(); - if (position < expected_parameters_end_pos) { - ReportMessageAt(Scanner::Location(position, position + 1), - MessageTemplate::kArgStringTerminatesParametersEarly); - *ok = false; - return nullptr; - } else if (position > expected_parameters_end_pos) { - ReportMessageAt(Scanner::Location(expected_parameters_end_pos - 2, - expected_parameters_end_pos), - MessageTemplate::kUnexpectedEndOfArgString); - *ok = false; - return nullptr; + { + ParameterDeclarationParsingScope formals_scope(this); + if (is_wrapped) { + // For a function implicitly wrapped in function header and footer, the + // function arguments are provided separately to the source, and are + // declared directly here. + int arguments_length = arguments_for_wrapped_function->length(); + for (int i = 0; i < arguments_length; i++) { + const bool is_rest = false; + Expression* argument = ExpressionFromIdentifier( + arguments_for_wrapped_function->at(i), kNoSourcePosition); + AddFormalParameter(&formals, argument, NullExpression(), + kNoSourcePosition, is_rest); } - } - Expect(Token::RPAREN, CHECK_OK); - int formals_end_position = scanner()->location().end_pos; + DCHECK_EQ(arguments_length, formals.num_parameters()); + DeclareFormalParameters(&formals); + } else { + // For a regular function, the function arguments are parsed from source. + DCHECK_NULL(arguments_for_wrapped_function); + ParseFormalParameterList(&formals); + if (expected_parameters_end_pos != kNoSourcePosition) { + // Check for '(' or ')' shenanigans in the parameter string for dynamic + // functions. + int position = peek_position(); + if (position < expected_parameters_end_pos) { + ReportMessageAt(Scanner::Location(position, position + 1), + MessageTemplate::kArgStringTerminatesParametersEarly); + return; + } else if (position > expected_parameters_end_pos) { + ReportMessageAt(Scanner::Location(expected_parameters_end_pos - 2, + expected_parameters_end_pos), + MessageTemplate::kUnexpectedEndOfArgString); + return; + } + } + Expect(Token::RPAREN); + int formals_end_position = scanner()->location().end_pos; - CheckArityRestrictions(formals.arity, kind, formals.has_rest, - function_scope->start_position(), - formals_end_position, CHECK_OK); - Expect(Token::LBRACE, CHECK_OK); + CheckArityRestrictions(formals.arity, kind, formals.has_rest, + function_scope->start_position(), + formals_end_position); + Expect(Token::LBRACE); + } + formals.duplicate_loc = formals_scope.duplicate_location(); } + *num_parameters = formals.num_parameters(); *function_length = formals.function_length; - ZonePtrList<Statement>* body = new (zone()) ZonePtrList<Statement>(8, zone()); + AcceptINScope scope(this, true); ParseFunctionBody(body, function_name, pos, formals, kind, function_type, - FunctionBodyType::kBlock, true, ok); + FunctionBodyType::kBlock); - // Validate parameter names. We can do this only after parsing the function, - // since the function can declare itself strict. - const bool allow_duplicate_parameters = - is_sloppy(function_scope->language_mode()) && formals.is_simple && - !IsConciseMethod(kind); - ValidateFormalParameters(function_scope->language_mode(), - allow_duplicate_parameters, CHECK_OK); - - RewriteDestructuringAssignments(); - - *has_duplicate_parameters = - !classifier()->is_valid_formal_parameter_list_without_duplicates(); + *has_duplicate_parameters = formals.has_duplicate(); *expected_property_count = function_state.expected_property_count(); *suspend_count = function_state.suspend_count(); - return body; } void Parser::DeclareClassVariable(const AstRawString* name, - ClassInfo* class_info, int class_token_pos, - bool* ok) { + ClassInfo* class_info, int class_token_pos) { #ifdef DEBUG scope()->SetScopeName(name); #endif if (name != nullptr) { - VariableProxy* proxy = factory()->NewVariableProxy(name, NORMAL_VARIABLE); - Declaration* declaration = - factory()->NewVariableDeclaration(proxy, class_token_pos); - class_info->variable = Declare( - declaration, DeclarationDescriptor::NORMAL, VariableMode::kConst, - Variable::DefaultInitializationFlag(VariableMode::kConst), ok); + VariableProxy* proxy = + DeclareVariable(name, VariableMode::kConst, class_token_pos); + class_info->variable = proxy->var(); } } // TODO(gsathya): Ideally, this should just bypass scope analysis and // allocate a slot directly on the context. We should just store this // index in the AST, instead of storing the variable. -Variable* Parser::CreateSyntheticContextVariable(const AstRawString* name, - bool* ok) { - VariableProxy* proxy = factory()->NewVariableProxy(name, NORMAL_VARIABLE); - Declaration* declaration = - factory()->NewVariableDeclaration(proxy, kNoSourcePosition); - Variable* var = Declare( - declaration, DeclarationDescriptor::NORMAL, VariableMode::kConst, - Variable::DefaultInitializationFlag(VariableMode::kConst), CHECK_OK); - var->ForceContextAllocation(); - return var; +Variable* Parser::CreateSyntheticContextVariable(const AstRawString* name) { + VariableProxy* proxy = + DeclareVariable(name, VariableMode::kConst, kNoSourcePosition); + proxy->var()->ForceContextAllocation(); + return proxy->var(); } -// This method declares a property of the given class. It updates the -// following fields of class_info, as appropriate: -// - constructor -// - properties -void Parser::DeclareClassProperty(const AstRawString* class_name, - ClassLiteralProperty* property, - const AstRawString* property_name, - ClassLiteralProperty::Kind kind, - bool is_static, bool is_constructor, - bool is_computed_name, ClassInfo* class_info, - bool* ok) { - if (is_constructor) { - DCHECK(!class_info->constructor); - class_info->constructor = property->value()->AsFunctionLiteral(); - DCHECK_NOT_NULL(class_info->constructor); - class_info->constructor->set_raw_name( - class_name != nullptr ? ast_value_factory()->NewConsString(class_name) - : nullptr); - return; - } - - if (kind != ClassLiteralProperty::PUBLIC_FIELD && - kind != ClassLiteralProperty::PRIVATE_FIELD) { - class_info->properties->Add(property, zone()); - return; - } - +void Parser::DeclareClassField(ClassLiteralProperty* property, + const AstRawString* property_name, + bool is_static, bool is_computed_name, + bool is_private, ClassInfo* class_info) { DCHECK(allow_harmony_public_fields() || allow_harmony_private_fields()); if (is_static) { - DCHECK(allow_harmony_static_fields()); - DCHECK_EQ(kind, ClassLiteralProperty::PUBLIC_FIELD); class_info->static_fields->Add(property, zone()); } else { class_info->instance_fields->Add(property, zone()); } + DCHECK_IMPLIES(is_computed_name, !is_private); if (is_computed_name) { - DCHECK_EQ(kind, ClassLiteralProperty::PUBLIC_FIELD); // We create a synthetic variable name here so that scope // analysis doesn't dedupe the vars. - Variable* computed_name_var = CreateSyntheticContextVariable( - ClassFieldVariableName(ast_value_factory(), - class_info->computed_field_count), - CHECK_OK_VOID); + Variable* computed_name_var = + CreateSyntheticContextVariable(ClassFieldVariableName( + ast_value_factory(), class_info->computed_field_count)); property->set_computed_name_var(computed_name_var); class_info->properties->Add(property, zone()); + } else if (is_private) { + Variable* private_name_var = CreateSyntheticContextVariable(property_name); + private_name_var->set_initializer_position(property->value()->position()); + property->set_private_name_var(private_name_var); + class_info->properties->Add(property, zone()); } +} - if (kind == ClassLiteralProperty::PRIVATE_FIELD) { - Variable* private_field_name_var = - CreateSyntheticContextVariable(property_name, CHECK_OK_VOID); - property->set_private_field_name_var(private_field_name_var); - class_info->properties->Add(property, zone()); +// This method declares a property of the given class. It updates the +// following fields of class_info, as appropriate: +// - constructor +// - properties +void Parser::DeclareClassProperty(const AstRawString* class_name, + ClassLiteralProperty* property, + bool is_constructor, ClassInfo* class_info) { + if (is_constructor) { + DCHECK(!class_info->constructor); + class_info->constructor = property->value()->AsFunctionLiteral(); + DCHECK_NOT_NULL(class_info->constructor); + class_info->constructor->set_raw_name( + class_name != nullptr ? ast_value_factory()->NewConsString(class_name) + : nullptr); + return; } + + class_info->properties->Add(property, zone()); } FunctionLiteral* Parser::CreateInitializerFunction( const char* name, DeclarationScope* scope, ZonePtrList<ClassLiteral::Property>* fields) { DCHECK_EQ(scope->function_kind(), - FunctionKind::kClassFieldsInitializerFunction); + FunctionKind::kClassMembersInitializerFunction); // function() { .. class fields initializer .. } - ZonePtrList<Statement>* statements = NewStatementList(1); - InitializeClassFieldsStatement* static_fields = - factory()->NewInitializeClassFieldsStatement(fields, kNoSourcePosition); - statements->Add(static_fields, zone()); + ScopedPtrList<Statement> statements(pointer_buffer()); + InitializeClassMembersStatement* static_fields = + factory()->NewInitializeClassMembersStatement(fields, kNoSourcePosition); + statements.Add(static_fields); return factory()->NewFunctionLiteral( ast_value_factory()->GetOneByteString(name), scope, statements, 0, 0, 0, FunctionLiteral::kNoDuplicateParameters, @@ -3190,7 +2880,7 @@ FunctionLiteral* Parser::CreateInitializerFunction( Expression* Parser::RewriteClassLiteral(Scope* block_scope, const AstRawString* name, ClassInfo* class_info, int pos, - int end_pos, bool* ok) { + int end_pos) { DCHECK_NOT_NULL(block_scope); DCHECK_EQ(block_scope->scope_type(), BLOCK_SCOPE); DCHECK_EQ(block_scope->language_mode(), LanguageMode::kStrict); @@ -3214,18 +2904,18 @@ Expression* Parser::RewriteClassLiteral(Scope* block_scope, class_info->static_fields); } - FunctionLiteral* instance_fields_initializer_function = nullptr; - if (class_info->has_instance_class_fields) { - instance_fields_initializer_function = CreateInitializerFunction( - "<instance_fields_initializer>", class_info->instance_fields_scope, + FunctionLiteral* instance_members_initializer_function = nullptr; + if (class_info->has_instance_members) { + instance_members_initializer_function = CreateInitializerFunction( + "<instance_members_initializer>", class_info->instance_members_scope, class_info->instance_fields); - class_info->constructor->set_requires_instance_fields_initializer(true); + class_info->constructor->set_requires_instance_members_initializer(true); } ClassLiteral* class_literal = factory()->NewClassLiteral( block_scope, class_info->variable, class_info->extends, class_info->constructor, class_info->properties, - static_fields_initializer, instance_fields_initializer_function, pos, + static_fields_initializer, instance_members_initializer_function, pos, end_pos, class_info->has_name_static_property, class_info->has_static_computed_names, class_info->is_anonymous); @@ -3233,18 +2923,18 @@ Expression* Parser::RewriteClassLiteral(Scope* block_scope, return class_literal; } -void Parser::CheckConflictingVarDeclarations(Scope* scope, bool* ok) { +void Parser::CheckConflictingVarDeclarations(Scope* scope) { + if (has_error()) return; Declaration* decl = scope->CheckConflictingVarDeclarations(); if (decl != nullptr) { // In ES6, conflicting variable bindings are early errors. - const AstRawString* name = decl->proxy()->raw_name(); - int position = decl->proxy()->position(); + const AstRawString* name = decl->var()->raw_name(); + int position = decl->position(); Scanner::Location location = position == kNoSourcePosition ? Scanner::Location::invalid() : Scanner::Location(position, position + 1); ReportMessageAt(location, MessageTemplate::kVarRedeclaration, name); - *ok = false; } } @@ -3255,7 +2945,7 @@ bool Parser::IsPropertyWithPrivateFieldKey(Expression* expression) { if (!property->key()->IsVariableProxy()) return false; VariableProxy* key = property->key()->AsVariableProxy(); - return key->is_private_field(); + return key->IsPrivateName(); } void Parser::InsertShadowingVarBindingInitializers(Block* inner_block) { @@ -3267,11 +2957,11 @@ void Parser::InsertShadowingVarBindingInitializers(Block* inner_block) { DCHECK(function_scope->is_function_scope()); BlockState block_state(&scope_, inner_scope); for (Declaration* decl : *inner_scope->declarations()) { - if (decl->proxy()->var()->mode() != VariableMode::kVar || + if (decl->var()->mode() != VariableMode::kVar || !decl->IsVariableDeclaration()) { continue; } - const AstRawString* name = decl->proxy()->raw_name(); + const AstRawString* name = decl->var()->raw_name(); Variable* parameter = function_scope->LookupLocal(name); if (parameter == nullptr) continue; VariableProxy* to = NewUnresolved(name); @@ -3305,9 +2995,7 @@ bool Parser::TargetStackContainsLabel(const AstRawString* label) { return false; } - -BreakableStatement* Parser::LookupBreakTarget(const AstRawString* label, - bool* ok) { +BreakableStatement* Parser::LookupBreakTarget(const AstRawString* label) { bool anonymous = label == nullptr; for (ParserTarget* t = target_stack_; t != nullptr; t = t->previous()) { BreakableStatement* stat = t->statement(); @@ -3319,9 +3007,7 @@ BreakableStatement* Parser::LookupBreakTarget(const AstRawString* label, return nullptr; } - -IterationStatement* Parser::LookupContinueTarget(const AstRawString* label, - bool* ok) { +IterationStatement* Parser::LookupContinueTarget(const AstRawString* label) { bool anonymous = label == nullptr; for (ParserTarget* t = target_stack_; t != nullptr; t = t->previous()) { IterationStatement* stat = t->statement()->AsIterationStatement(); @@ -3336,7 +3022,6 @@ IterationStatement* Parser::LookupContinueTarget(const AstRawString* label, return nullptr; } - void Parser::HandleSourceURLComments(Isolate* isolate, Handle<Script> script) { Handle<String> source_url = scanner_.SourceUrl(isolate); if (!source_url.is_null()) { @@ -3444,59 +3129,57 @@ Expression* Parser::CloseTemplateLiteral(TemplateLiteralState* state, int start, factory()->NewGetTemplateObject(cooked_strings, raw_strings, pos); // Call TagFn - ZonePtrList<Expression>* call_args = - new (zone()) ZonePtrList<Expression>(expressions->length() + 1, zone()); - call_args->Add(template_object, zone()); - call_args->AddAll(*expressions, zone()); + ScopedPtrList<Expression> call_args(pointer_buffer()); + call_args.Add(template_object); + call_args.AddAll(*expressions); return factory()->NewTaggedTemplate(tag, call_args, pos); } } namespace { -bool OnlyLastArgIsSpread(ZonePtrList<Expression>* args) { - for (int i = 0; i < args->length() - 1; i++) { - if (args->at(i)->IsSpread()) { +bool OnlyLastArgIsSpread(const ScopedPtrList<Expression>& args) { + for (int i = 0; i < args.length() - 1; i++) { + if (args.at(i)->IsSpread()) { return false; } } - return args->at(args->length() - 1)->IsSpread(); + return args.at(args.length() - 1)->IsSpread(); } } // namespace ArrayLiteral* Parser::ArrayLiteralFromListWithSpread( - ZonePtrList<Expression>* list) { + const ScopedPtrList<Expression>& list) { // If there's only a single spread argument, a fast path using CallWithSpread // is taken. - DCHECK_LT(1, list->length()); + DCHECK_LT(1, list.length()); // The arguments of the spread call become a single ArrayLiteral. int first_spread = 0; - for (; first_spread < list->length() && !list->at(first_spread)->IsSpread(); + for (; first_spread < list.length() && !list.at(first_spread)->IsSpread(); ++first_spread) { } - DCHECK_LT(first_spread, list->length()); + DCHECK_LT(first_spread, list.length()); return factory()->NewArrayLiteral(list, first_spread, kNoSourcePosition); } Expression* Parser::SpreadCall(Expression* function, - ZonePtrList<Expression>* args_list, int pos, - Call::PossiblyEval is_possibly_eval) { + const ScopedPtrList<Expression>& args_list, + int pos, Call::PossiblyEval is_possibly_eval) { // Handle this case in BytecodeGenerator. if (OnlyLastArgIsSpread(args_list) || function->IsSuperCallReference()) { return factory()->NewCall(function, args_list, pos); } - ZonePtrList<Expression>* args = - new (zone()) ZonePtrList<Expression>(3, zone()); + ScopedPtrList<Expression> args(pointer_buffer()); if (function->IsProperty()) { // Method calls if (function->AsProperty()->IsSuperAccess()) { Expression* home = ThisExpression(kNoSourcePosition); - args->Add(function, zone()); - args->Add(home, zone()); + args.Add(function); + args.Add(home); } else { Variable* temp = NewTemporary(ast_value_factory()->empty_string()); VariableProxy* obj = factory()->NewVariableProxy(temp); @@ -3504,29 +3187,29 @@ Expression* Parser::SpreadCall(Expression* function, Token::ASSIGN, obj, function->AsProperty()->obj(), kNoSourcePosition); function = factory()->NewProperty( assign_obj, function->AsProperty()->key(), kNoSourcePosition); - args->Add(function, zone()); + args.Add(function); obj = factory()->NewVariableProxy(temp); - args->Add(obj, zone()); + args.Add(obj); } } else { // Non-method calls - args->Add(function, zone()); - args->Add(factory()->NewUndefinedLiteral(kNoSourcePosition), zone()); + args.Add(function); + args.Add(factory()->NewUndefinedLiteral(kNoSourcePosition)); } - args->Add(ArrayLiteralFromListWithSpread(args_list), zone()); + args.Add(ArrayLiteralFromListWithSpread(args_list)); return factory()->NewCallRuntime(Context::REFLECT_APPLY_INDEX, args, pos); } Expression* Parser::SpreadCallNew(Expression* function, - ZonePtrList<Expression>* args_list, int pos) { + const ScopedPtrList<Expression>& args_list, + int pos) { if (OnlyLastArgIsSpread(args_list)) { // Handle in BytecodeGenerator. return factory()->NewCallNew(function, args_list, pos); } - ZonePtrList<Expression>* args = - new (zone()) ZonePtrList<Expression>(2, zone()); - args->Add(function, zone()); - args->Add(ArrayLiteralFromListWithSpread(args_list), zone()); + ScopedPtrList<Expression> args(pointer_buffer()); + args.Add(function); + args.Add(ArrayLiteralFromListWithSpread(args_list)); return factory()->NewCallRuntime(Context::REFLECT_CONSTRUCT_INDEX, args, pos); } @@ -3549,27 +3232,34 @@ void Parser::SetAsmModule() { // incremented after parsing is done. ++use_counts_[v8::Isolate::kUseAsm]; DCHECK(scope()->is_declaration_scope()); - scope()->AsDeclarationScope()->set_asm_module(); + scope()->AsDeclarationScope()->set_is_asm_module(); + info_->set_contains_asm_module(true); } -Expression* Parser::ExpressionListToExpression(ZonePtrList<Expression>* args) { - Expression* expr = args->at(0); - for (int i = 1; i < args->length(); ++i) { - expr = factory()->NewBinaryOperation(Token::COMMA, expr, args->at(i), - expr->position()); +Expression* Parser::ExpressionListToExpression( + const ScopedPtrList<Expression>& args) { + Expression* expr = args.at(0); + if (args.length() == 1) return expr; + if (args.length() == 2) { + return factory()->NewBinaryOperation(Token::COMMA, expr, args.at(1), + args.at(1)->position()); + } + NaryOperation* result = + factory()->NewNaryOperation(Token::COMMA, expr, args.length() - 1); + for (int i = 1; i < args.length(); i++) { + result->AddSubsequent(args.at(i), args.at(i)->position()); } - return expr; + return result; } // This method completes the desugaring of the body of async_function. -void Parser::RewriteAsyncFunctionBody(ZonePtrList<Statement>* body, - Block* block, Expression* return_value, - bool* ok) { +void Parser::RewriteAsyncFunctionBody(ScopedPtrList<Statement>* body, + Block* block, Expression* return_value) { // function async_function() { - // .generator_object = %CreateJSGeneratorObject(); + // .generator_object = %_AsyncFunctionEnter(); // BuildRejectPromiseOnException({ // ... block ... - // return %ResolvePromise(.promise, expr), .promise; + // return %_AsyncFunctionResolve(.generator_object, expr); // }) // } @@ -3577,40 +3267,13 @@ void Parser::RewriteAsyncFunctionBody(ZonePtrList<Statement>* body, return_value, return_value->position()), zone()); block = BuildRejectPromiseOnException(block); - body->Add(block, zone()); -} - -void Parser::RewriteDestructuringAssignments() { - const auto& assignments = - function_state_->destructuring_assignments_to_rewrite(); - auto it = assignments.rbegin(); - for (; it != assignments.rend(); ++it) { - // Rewrite list in reverse, so that nested assignment patterns are rewritten - // correctly. - RewritableExpression* to_rewrite = *it; - DCHECK_NOT_NULL(to_rewrite); - if (!to_rewrite->is_rewritten()) { - // Since this function is called at the end of parsing the program, - // pair.scope may already have been removed by FinalizeBlockScope in the - // meantime. - Scope* scope = to_rewrite->scope()->GetUnremovedScope(); - // Scope at the time of the rewriting and the original parsing - // should be in the same function. - DCHECK(scope->GetClosureScope() == scope_->GetClosureScope()); - BlockState block_state(&scope_, scope); - RewriteDestructuringAssignment(to_rewrite); - } - } -} - -void Parser::QueueDestructuringAssignmentForRewriting( - RewritableExpression* expr) { - function_state_->AddDestructuringAssignment(expr); + body->Add(block); } void Parser::SetFunctionNameFromPropertyName(LiteralProperty* property, const AstRawString* name, const AstRawString* prefix) { + if (has_error()) return; // Ensure that the function we are going to create has shared name iff // we are not going to set it later. if (property->NeedsSetFunctionName()) { @@ -3636,7 +3299,7 @@ void Parser::SetFunctionNameFromPropertyName(ObjectLiteralProperty* property, // Ignore "__proto__" as a name when it's being used to set the [[Prototype]] // of an object literal. // See ES #sec-__proto__-property-names-in-object-initializers. - if (property->IsPrototype()) return; + if (property->IsPrototype() || has_error()) return; DCHECK(!property->value()->IsAnonymousFunctionDefinition() || property->kind() == ObjectLiteralProperty::COMPUTED); @@ -3691,440 +3354,10 @@ Statement* Parser::CheckCallable(Variable* var, Expression* error, int pos) { Statement* throw_call = factory()->NewExpressionStatement(error, pos); validate_var = factory()->NewIfStatement( - condition, factory()->NewEmptyStatement(nopos), throw_call, nopos); + condition, factory()->EmptyStatement(), throw_call, nopos); } return validate_var; } -void Parser::BuildIteratorClose(ZonePtrList<Statement>* statements, - Variable* iterator, Variable* input, - Variable* var_output, IteratorType type) { - // - // This function adds four statements to [statements], corresponding to the - // following code: - // - // let iteratorReturn = iterator.return; - // if (IS_NULL_OR_UNDEFINED(iteratorReturn) { - // return {value: input, done: true}; - // } - // output = %_Call(iteratorReturn, iterator, input); - // if (!IS_RECEIVER(output)) %ThrowIterResultNotAnObject(output); - // - - const int nopos = kNoSourcePosition; - - // let iteratorReturn = iterator.return; - Variable* var_return = var_output; // Reusing the output variable. - Statement* get_return; - { - Expression* iterator_proxy = factory()->NewVariableProxy(iterator); - Expression* literal = factory()->NewStringLiteral( - ast_value_factory()->return_string(), nopos); - Expression* property = - factory()->NewProperty(iterator_proxy, literal, nopos); - Expression* return_proxy = factory()->NewVariableProxy(var_return); - Expression* assignment = - factory()->NewAssignment(Token::ASSIGN, return_proxy, property, nopos); - get_return = factory()->NewExpressionStatement(assignment, nopos); - } - - // if (IS_NULL_OR_UNDEFINED(iteratorReturn) { - // return {value: input, done: true}; - // } - Statement* check_return; - { - Expression* condition = factory()->NewCompareOperation( - Token::EQ, factory()->NewVariableProxy(var_return), - factory()->NewNullLiteral(nopos), nopos); - - Expression* value = factory()->NewVariableProxy(input); - - Statement* return_input = BuildReturnStatement(value, nopos); - - check_return = factory()->NewIfStatement( - condition, return_input, factory()->NewEmptyStatement(nopos), nopos); - } - - // output = %_Call(iteratorReturn, iterator, input); - Statement* call_return; - { - auto args = new (zone()) ZonePtrList<Expression>(3, zone()); - args->Add(factory()->NewVariableProxy(var_return), zone()); - args->Add(factory()->NewVariableProxy(iterator), zone()); - args->Add(factory()->NewVariableProxy(input), zone()); - - Expression* call = - factory()->NewCallRuntime(Runtime::kInlineCall, args, nopos); - if (type == IteratorType::kAsync) { - function_state_->AddSuspend(); - call = factory()->NewAwait(call, nopos); - } - Expression* output_proxy = factory()->NewVariableProxy(var_output); - Expression* assignment = - factory()->NewAssignment(Token::ASSIGN, output_proxy, call, nopos); - call_return = factory()->NewExpressionStatement(assignment, nopos); - } - - // if (!IS_RECEIVER(output)) %ThrowIteratorResultNotAnObject(output); - Statement* validate_output; - { - Expression* is_receiver_call; - { - auto args = new (zone()) ZonePtrList<Expression>(1, zone()); - args->Add(factory()->NewVariableProxy(var_output), zone()); - is_receiver_call = - factory()->NewCallRuntime(Runtime::kInlineIsJSReceiver, args, nopos); - } - - Statement* throw_call; - { - auto args = new (zone()) ZonePtrList<Expression>(1, zone()); - args->Add(factory()->NewVariableProxy(var_output), zone()); - Expression* call = factory()->NewCallRuntime( - Runtime::kThrowIteratorResultNotAnObject, args, nopos); - throw_call = factory()->NewExpressionStatement(call, nopos); - } - - validate_output = factory()->NewIfStatement( - is_receiver_call, factory()->NewEmptyStatement(nopos), throw_call, - nopos); - } - - statements->Add(get_return, zone()); - statements->Add(check_return, zone()); - statements->Add(call_return, zone()); - statements->Add(validate_output, zone()); -} - -void Parser::FinalizeIteratorUse(Variable* completion, Expression* condition, - Variable* iter, Block* iterator_use, - Block* target, IteratorType type) { - // - // This function adds two statements to [target], corresponding to the - // following code: - // - // completion = kNormalCompletion; - // try { - // try { - // iterator_use - // } catch(e) { - // if (completion === kAbruptCompletion) completion = kThrowCompletion; - // %ReThrow(e); - // } - // } finally { - // if (condition) { - // #BuildIteratorCloseForCompletion(iter, completion) - // } - // } - // - - const int nopos = kNoSourcePosition; - - // completion = kNormalCompletion; - Statement* initialize_completion; - { - Expression* proxy = factory()->NewVariableProxy(completion); - Expression* assignment = factory()->NewAssignment( - Token::ASSIGN, proxy, - factory()->NewSmiLiteral(Parser::kNormalCompletion, nopos), nopos); - initialize_completion = - factory()->NewExpressionStatement(assignment, nopos); - } - - // if (completion === kAbruptCompletion) completion = kThrowCompletion; - Statement* set_completion_throw; - { - Expression* condition = factory()->NewCompareOperation( - Token::EQ_STRICT, factory()->NewVariableProxy(completion), - factory()->NewSmiLiteral(Parser::kAbruptCompletion, nopos), nopos); - - Expression* proxy = factory()->NewVariableProxy(completion); - Expression* assignment = factory()->NewAssignment( - Token::ASSIGN, proxy, - factory()->NewSmiLiteral(Parser::kThrowCompletion, nopos), nopos); - Statement* statement = factory()->NewExpressionStatement(assignment, nopos); - set_completion_throw = factory()->NewIfStatement( - condition, statement, factory()->NewEmptyStatement(nopos), nopos); - } - - // if (condition) { - // #BuildIteratorCloseForCompletion(iter, completion) - // } - Block* maybe_close; - { - Block* block = factory()->NewBlock(2, true); - Expression* proxy = factory()->NewVariableProxy(completion); - BuildIteratorCloseForCompletion(block->statements(), iter, proxy, type); - DCHECK_EQ(block->statements()->length(), 2); - - maybe_close = IgnoreCompletion(factory()->NewIfStatement( - condition, block, factory()->NewEmptyStatement(nopos), nopos)); - } - - // try { #try_block } - // catch(e) { - // #set_completion_throw; - // %ReThrow(e); - // } - Statement* try_catch; - { - Scope* catch_scope = NewHiddenCatchScope(); - - Statement* rethrow; - // We use %ReThrow rather than the ordinary throw because we want to - // preserve the original exception message. This is also why we create a - // TryCatchStatementForReThrow below (which does not clear the pending - // message), rather than a TryCatchStatement. - { - auto args = new (zone()) ZonePtrList<Expression>(1, zone()); - args->Add(factory()->NewVariableProxy(catch_scope->catch_variable()), - zone()); - rethrow = factory()->NewExpressionStatement( - factory()->NewCallRuntime(Runtime::kReThrow, args, nopos), nopos); - } - - Block* catch_block = factory()->NewBlock(2, false); - catch_block->statements()->Add(set_completion_throw, zone()); - catch_block->statements()->Add(rethrow, zone()); - - try_catch = factory()->NewTryCatchStatementForReThrow( - iterator_use, catch_scope, catch_block, nopos); - } - - // try { #try_catch } finally { #maybe_close } - Statement* try_finally; - { - Block* try_block = factory()->NewBlock(1, false); - try_block->statements()->Add(try_catch, zone()); - - try_finally = - factory()->NewTryFinallyStatement(try_block, maybe_close, nopos); - } - - target->statements()->Add(initialize_completion, zone()); - target->statements()->Add(try_finally, zone()); -} - -void Parser::BuildIteratorCloseForCompletion(ZonePtrList<Statement>* statements, - Variable* iterator, - Expression* completion, - IteratorType type) { - // - // This function adds two statements to [statements], corresponding to the - // following code: - // - // let iteratorReturn = iterator.return; - // if (!IS_NULL_OR_UNDEFINED(iteratorReturn)) { - // if (completion === kThrowCompletion) { - // if (!IS_CALLABLE(iteratorReturn)) { - // throw MakeTypeError(kReturnMethodNotCallable); - // } - // [if (IteratorType == kAsync)] - // try { Await(%_Call(iteratorReturn, iterator) } catch (_) { } - // [else] - // try { %_Call(iteratorReturn, iterator) } catch (_) { } - // [endif] - // } else { - // [if (IteratorType == kAsync)] - // let output = Await(%_Call(iteratorReturn, iterator)); - // [else] - // let output = %_Call(iteratorReturn, iterator); - // [endif] - // if (!IS_RECEIVER(output)) { - // %ThrowIterResultNotAnObject(output); - // } - // } - // } - // - - const int nopos = kNoSourcePosition; - // let iteratorReturn = iterator.return; - Variable* var_return = NewTemporary(ast_value_factory()->empty_string()); - Statement* get_return; - { - Expression* iterator_proxy = factory()->NewVariableProxy(iterator); - Expression* literal = factory()->NewStringLiteral( - ast_value_factory()->return_string(), nopos); - Expression* property = - factory()->NewProperty(iterator_proxy, literal, nopos); - Expression* return_proxy = factory()->NewVariableProxy(var_return); - Expression* assignment = - factory()->NewAssignment(Token::ASSIGN, return_proxy, property, nopos); - get_return = factory()->NewExpressionStatement(assignment, nopos); - } - - // if (!IS_CALLABLE(iteratorReturn)) { - // throw MakeTypeError(kReturnMethodNotCallable); - // } - Statement* check_return_callable; - { - Expression* throw_expr = - NewThrowTypeError(MessageTemplate::kReturnMethodNotCallable, - ast_value_factory()->empty_string(), nopos); - check_return_callable = CheckCallable(var_return, throw_expr, nopos); - } - - // try { %_Call(iteratorReturn, iterator) } catch (_) { } - Statement* try_call_return; - { - auto args = new (zone()) ZonePtrList<Expression>(2, zone()); - args->Add(factory()->NewVariableProxy(var_return), zone()); - args->Add(factory()->NewVariableProxy(iterator), zone()); - - Expression* call = - factory()->NewCallRuntime(Runtime::kInlineCall, args, nopos); - - if (type == IteratorType::kAsync) { - function_state_->AddSuspend(); - call = factory()->NewAwait(call, nopos); - } - - Block* try_block = factory()->NewBlock(1, false); - try_block->statements()->Add(factory()->NewExpressionStatement(call, nopos), - zone()); - - Block* catch_block = factory()->NewBlock(0, false); - try_call_return = - factory()->NewTryCatchStatement(try_block, nullptr, catch_block, nopos); - } - - // let output = %_Call(iteratorReturn, iterator); - // if (!IS_RECEIVER(output)) { - // %ThrowIteratorResultNotAnObject(output); - // } - Block* validate_return; - { - Variable* var_output = NewTemporary(ast_value_factory()->empty_string()); - Statement* call_return; - { - auto args = new (zone()) ZonePtrList<Expression>(2, zone()); - args->Add(factory()->NewVariableProxy(var_return), zone()); - args->Add(factory()->NewVariableProxy(iterator), zone()); - Expression* call = - factory()->NewCallRuntime(Runtime::kInlineCall, args, nopos); - if (type == IteratorType::kAsync) { - function_state_->AddSuspend(); - call = factory()->NewAwait(call, nopos); - } - - Expression* output_proxy = factory()->NewVariableProxy(var_output); - Expression* assignment = - factory()->NewAssignment(Token::ASSIGN, output_proxy, call, nopos); - call_return = factory()->NewExpressionStatement(assignment, nopos); - } - - Expression* is_receiver_call; - { - auto args = new (zone()) ZonePtrList<Expression>(1, zone()); - args->Add(factory()->NewVariableProxy(var_output), zone()); - is_receiver_call = - factory()->NewCallRuntime(Runtime::kInlineIsJSReceiver, args, nopos); - } - - Statement* throw_call; - { - auto args = new (zone()) ZonePtrList<Expression>(1, zone()); - args->Add(factory()->NewVariableProxy(var_output), zone()); - Expression* call = factory()->NewCallRuntime( - Runtime::kThrowIteratorResultNotAnObject, args, nopos); - throw_call = factory()->NewExpressionStatement(call, nopos); - } - - Statement* check_return = factory()->NewIfStatement( - is_receiver_call, factory()->NewEmptyStatement(nopos), throw_call, - nopos); - - validate_return = factory()->NewBlock(2, false); - validate_return->statements()->Add(call_return, zone()); - validate_return->statements()->Add(check_return, zone()); - } - - // if (completion === kThrowCompletion) { - // #check_return_callable; - // #try_call_return; - // } else { - // #validate_return; - // } - Statement* call_return_carefully; - { - Expression* condition = factory()->NewCompareOperation( - Token::EQ_STRICT, completion, - factory()->NewSmiLiteral(Parser::kThrowCompletion, nopos), nopos); - - Block* then_block = factory()->NewBlock(2, false); - then_block->statements()->Add(check_return_callable, zone()); - then_block->statements()->Add(try_call_return, zone()); - - call_return_carefully = factory()->NewIfStatement(condition, then_block, - validate_return, nopos); - } - - // if (!IS_NULL_OR_UNDEFINED(iteratorReturn)) { ... } - Statement* maybe_call_return; - { - Expression* condition = factory()->NewCompareOperation( - Token::EQ, factory()->NewVariableProxy(var_return), - factory()->NewNullLiteral(nopos), nopos); - - maybe_call_return = factory()->NewIfStatement( - condition, factory()->NewEmptyStatement(nopos), call_return_carefully, - nopos); - } - - statements->Add(get_return, zone()); - statements->Add(maybe_call_return, zone()); -} - -Statement* Parser::FinalizeForOfStatement(ForOfStatement* loop, - Variable* var_completion, - IteratorType type, int pos) { - // - // This function replaces the loop with the following wrapping: - // - // completion = kNormalCompletion; - // try { - // try { - // #loop; - // } catch(e) { - // if (completion === kAbruptCompletion) completion = kThrowCompletion; - // %ReThrow(e); - // } - // } finally { - // if (!(completion === kNormalCompletion)) { - // #BuildIteratorCloseForCompletion(#iterator, completion) - // } - // } - // - // Note that the loop's body and its assign_each already contain appropriate - // assignments to completion (see InitializeForOfStatement). - // - - const int nopos = kNoSourcePosition; - - // !(completion === kNormalCompletion) - Expression* closing_condition; - { - Expression* cmp = factory()->NewCompareOperation( - Token::EQ_STRICT, factory()->NewVariableProxy(var_completion), - factory()->NewSmiLiteral(Parser::kNormalCompletion, nopos), nopos); - closing_condition = factory()->NewUnaryOperation(Token::NOT, cmp, nopos); - } - - Block* final_loop = factory()->NewBlock(2, false); - { - Block* try_block = factory()->NewBlock(1, false); - try_block->statements()->Add(loop, zone()); - - FinalizeIteratorUse(var_completion, closing_condition, loop->iterator(), - try_block, final_loop, type); - } - - return final_loop; -} - -#undef CHECK_OK -#undef CHECK_OK_VOID -#undef CHECK_FAILED - } // namespace internal } // namespace v8 diff --git a/deps/v8/src/parsing/parser.h b/deps/v8/src/parsing/parser.h index 35de0656d3..cc0ceb2607 100644 --- a/deps/v8/src/parsing/parser.h +++ b/deps/v8/src/parsing/parser.h @@ -16,6 +16,7 @@ #include "src/parsing/parser-base.h" #include "src/parsing/parsing.h" #include "src/parsing/preparser.h" +#include "src/pointer-with-payload.h" #include "src/zone/zone-chunk-list.h" namespace v8 { @@ -24,12 +25,12 @@ class ScriptCompiler; namespace internal { -class ConsumedPreParsedScopeData; +class ConsumedPreparseData; class ParseInfo; class ParserTarget; class ParserTargetScope; class PendingCompilationErrorHandler; -class PreParsedScopeData; +class PreparseData; class FunctionEntry { public: @@ -83,33 +84,55 @@ class Parser; struct ParserFormalParameters : FormalParametersBase { struct Parameter : public ZoneObject { - Parameter(const AstRawString* name, Expression* pattern, - Expression* initializer, int position, + Parameter(Expression* pattern, Expression* initializer, int position, int initializer_end_position, bool is_rest) - : name(name), + : initializer_and_is_rest(initializer, is_rest), pattern(pattern), - initializer(initializer), position(position), - initializer_end_position(initializer_end_position), - is_rest(is_rest) {} - const AstRawString* name; + initializer_end_position(initializer_end_position) {} + + PointerWithPayload<Expression, bool, 1> initializer_and_is_rest; + Expression* pattern; - Expression* initializer; + Expression* initializer() const { + return initializer_and_is_rest.GetPointer(); + } int position; int initializer_end_position; - bool is_rest; + inline bool is_rest() const { return initializer_and_is_rest.GetPayload(); } + Parameter* next_parameter = nullptr; bool is_simple() const { - return pattern->IsVariableProxy() && initializer == nullptr && !is_rest; + return pattern->IsVariableProxy() && initializer() == nullptr && + !is_rest(); + } + + const AstRawString* name() const { + DCHECK(is_simple()); + return pattern->AsVariableProxy()->raw_name(); } Parameter** next() { return &next_parameter; } Parameter* const* next() const { return &next_parameter; } }; + void set_strict_parameter_error(const Scanner::Location& loc, + MessageTemplate message) { + strict_error_loc = loc; + strict_error_message = message; + } + + bool has_duplicate() const { return duplicate_loc.IsValid(); } + void ValidateDuplicate(Parser* parser) const; + void ValidateStrictMode(Parser* parser) const; + explicit ParserFormalParameters(DeclarationScope* scope) : FormalParametersBase(scope) {} + base::ThreadedList<Parameter> params; + Scanner::Location duplicate_loc = Scanner::Location::invalid(); + Scanner::Location strict_error_loc = Scanner::Location::invalid(); + MessageTemplate strict_error_message = MessageTemplate::kNone; }; template <> @@ -118,34 +141,32 @@ struct ParserTypes<Parser> { typedef Parser Impl; // Return types for traversing functions. - typedef const AstRawString* Identifier; - typedef v8::internal::Expression* Expression; - typedef v8::internal::FunctionLiteral* FunctionLiteral; - typedef ObjectLiteral::Property* ObjectLiteralProperty; + typedef v8::internal::Block* Block; + typedef v8::internal::BreakableStatement* BreakableStatement; typedef ClassLiteral::Property* ClassLiteralProperty; - typedef v8::internal::Suspend* Suspend; - typedef v8::internal::RewritableExpression* RewritableExpression; - typedef ZonePtrList<v8::internal::Expression>* ExpressionList; - typedef ZonePtrList<ObjectLiteral::Property>* ObjectPropertyList; typedef ZonePtrList<ClassLiteral::Property>* ClassPropertyList; + typedef v8::internal::Expression* Expression; + typedef ScopedPtrList<v8::internal::Expression> ExpressionList; typedef ParserFormalParameters FormalParameters; - typedef v8::internal::Statement* Statement; - typedef ZonePtrList<v8::internal::Statement>* StatementList; - typedef v8::internal::Block* Block; - typedef v8::internal::BreakableStatement* BreakableStatement; typedef v8::internal::ForStatement* ForStatement; + typedef v8::internal::FunctionLiteral* FunctionLiteral; + typedef const AstRawString* Identifier; typedef v8::internal::IterationStatement* IterationStatement; - typedef v8::internal::FuncNameInferrer FuncNameInferrer; - typedef v8::internal::SourceRange SourceRange; - typedef v8::internal::SourceRangeScope SourceRangeScope; + typedef ObjectLiteral::Property* ObjectLiteralProperty; + typedef ScopedPtrList<v8::internal::ObjectLiteralProperty> ObjectPropertyList; + typedef v8::internal::Statement* Statement; + typedef ScopedPtrList<v8::internal::Statement> StatementList; + typedef v8::internal::Suspend* Suspend; // For constructing objects returned by the traversing functions. typedef AstNodeFactory Factory; + // Other implementation-specific functions. + typedef v8::internal::FuncNameInferrer FuncNameInferrer; + typedef v8::internal::SourceRange SourceRange; + typedef v8::internal::SourceRangeScope SourceRangeScope; typedef ParserTarget Target; typedef ParserTargetScope TargetScope; - - static constexpr bool ExpressionClassifierReportErrors = true; }; class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { @@ -173,7 +194,9 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { // their corresponding scope infos. Therefore, looking up variables in the // deserialized scopes is not possible. void DeserializeScopeChain(Isolate* isolate, ParseInfo* info, - MaybeHandle<ScopeInfo> maybe_outer_scope_info); + MaybeHandle<ScopeInfo> maybe_outer_scope_info, + Scope::DeserializationMode mode = + Scope::DeserializationMode::kScopesOnly); // Move statistics to Isolate void UpdateStatistics(Isolate* isolate, Handle<Script> script); @@ -181,8 +204,11 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { private: friend class ParserBase<Parser>; - friend class v8::internal::ExpressionClassifierErrorTracker< - ParserTypes<Parser>>; + friend struct ParserFormalParameters; + friend class i::ExpressionScope<ParserTypes<Parser>>; + friend class i::VariableDeclarationParsingScope<ParserTypes<Parser>>; + friend class i::ParameterDeclarationParsingScope<ParserTypes<Parser>>; + friend class i::ArrowHeadParsingScope<ParserTypes<Parser>>; friend bool v8::internal::parsing::ParseProgram(ParseInfo*, Isolate*); friend bool v8::internal::parsing::ParseFunction( ParseInfo*, Handle<SharedFunctionInfo> shared_info, Isolate*); @@ -236,8 +262,8 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { // We manually construct the AST and scopes for a top-level function and the // function wrapper. void ParseWrapped(Isolate* isolate, ParseInfo* info, - ZonePtrList<Statement>* body, DeclarationScope* scope, - Zone* zone, bool* ok); + ScopedPtrList<Statement>* body, DeclarationScope* scope, + Zone* zone); ZonePtrList<const AstRawString>* PrepareWrappedArguments(Isolate* isolate, ParseInfo* info, @@ -251,31 +277,33 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { 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); SET_ALLOW(harmony_public_fields); SET_ALLOW(harmony_static_fields); SET_ALLOW(harmony_dynamic_import); SET_ALLOW(harmony_import_meta); SET_ALLOW(harmony_private_fields); + SET_ALLOW(harmony_private_methods); SET_ALLOW(eval_cache); #undef SET_ALLOW + preparse_data_buffer_.reserve(128); } return reusable_preparser_; } - void ParseModuleItemList(ZonePtrList<Statement>* body, bool* ok); - Statement* ParseModuleItem(bool* ok); - const AstRawString* ParseModuleSpecifier(bool* ok); - void ParseImportDeclaration(bool* ok); - Statement* ParseExportDeclaration(bool* ok); - Statement* ParseExportDefault(bool* ok); + void ParseModuleItemList(ScopedPtrList<Statement>* body); + Statement* ParseModuleItem(); + const AstRawString* ParseModuleSpecifier(); + void ParseImportDeclaration(); + Statement* ParseExportDeclaration(); + Statement* ParseExportDefault(); + void ParseExportStar(); struct ExportClauseData { const AstRawString* export_name; const AstRawString* local_name; Scanner::Location location; }; ZoneChunkList<ExportClauseData>* ParseExportClause( - Scanner::Location* reserved_loc, bool* ok); + Scanner::Location* reserved_loc); struct NamedImport : public ZoneObject { const AstRawString* import_name; const AstRawString* local_name; @@ -286,117 +314,87 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { local_name(local_name), location(location) {} }; - ZonePtrList<const NamedImport>* ParseNamedImports(int pos, bool* ok); - Block* BuildInitializationBlock(DeclarationParsingResult* parsing_result, - ZonePtrList<const AstRawString>* names, - bool* ok); + ZonePtrList<const NamedImport>* ParseNamedImports(int pos); + Statement* BuildInitializationBlock(DeclarationParsingResult* parsing_result); void DeclareLabel(ZonePtrList<const AstRawString>** labels, ZonePtrList<const AstRawString>** own_labels, - VariableProxy* expr, bool* ok); + VariableProxy* expr); bool ContainsLabel(ZonePtrList<const AstRawString>* labels, const AstRawString* label); Expression* RewriteReturn(Expression* return_value, int pos); Statement* RewriteSwitchStatement(SwitchStatement* switch_statement, Scope* scope); - void RewriteCatchPattern(CatchInfo* catch_info, bool* ok); - void ValidateCatchBlock(const CatchInfo& catch_info, bool* ok); + Block* RewriteCatchPattern(CatchInfo* catch_info); + void ReportVarRedeclarationIn(const AstRawString* name, Scope* scope); Statement* RewriteTryStatement(Block* try_block, Block* catch_block, const SourceRange& catch_range, Block* finally_block, const SourceRange& finally_range, const CatchInfo& catch_info, int pos); void ParseAndRewriteGeneratorFunctionBody(int pos, FunctionKind kind, - ZonePtrList<Statement>* body, - bool* ok); - void ParseAndRewriteAsyncGeneratorFunctionBody(int pos, FunctionKind kind, - ZonePtrList<Statement>* body, - bool* ok); + ScopedPtrList<Statement>* body); + void ParseAndRewriteAsyncGeneratorFunctionBody( + int pos, FunctionKind kind, ScopedPtrList<Statement>* body); void DeclareFunctionNameVar(const AstRawString* function_name, FunctionLiteral::FunctionType function_type, DeclarationScope* function_scope); Statement* DeclareFunction(const AstRawString* variable_name, FunctionLiteral* function, VariableMode mode, - int pos, bool is_sloppy_block_function, - ZonePtrList<const AstRawString>* names, bool* ok); - Variable* CreateSyntheticContextVariable(const AstRawString* synthetic_name, - bool* ok); + int beg_pos, int end_pos, + bool is_sloppy_block_function, + ZonePtrList<const AstRawString>* names); + Variable* CreateSyntheticContextVariable(const AstRawString* synthetic_name); FunctionLiteral* CreateInitializerFunction( const char* name, DeclarationScope* scope, ZonePtrList<ClassLiteral::Property>* fields); - V8_INLINE Statement* DeclareClass(const AstRawString* variable_name, - Expression* value, - ZonePtrList<const AstRawString>* names, - int class_token_pos, int end_pos, bool* ok); - V8_INLINE void DeclareClassVariable(const AstRawString* name, - ClassInfo* class_info, - int class_token_pos, bool* ok); - V8_INLINE void DeclareClassProperty(const AstRawString* class_name, - ClassLiteralProperty* property, - const AstRawString* property_name, - ClassLiteralProperty::Kind kind, - bool is_static, bool is_constructor, - bool is_computed_name, - ClassInfo* class_info, bool* ok); - V8_INLINE Expression* RewriteClassLiteral(Scope* block_scope, - const AstRawString* name, - ClassInfo* class_info, int pos, - int end_pos, bool* ok); - V8_INLINE Statement* DeclareNative(const AstRawString* name, int pos, - bool* ok); - - V8_INLINE Block* IgnoreCompletion(Statement* statement); - - V8_INLINE Scope* NewHiddenCatchScope(); + + bool IdentifierEquals(const AstRawString* identifier, + const AstRawString* other) { + return identifier == other; + } + + Statement* DeclareClass(const AstRawString* variable_name, Expression* value, + ZonePtrList<const AstRawString>* names, + int class_token_pos, int end_pos); + void DeclareClassVariable(const AstRawString* name, ClassInfo* class_info, + int class_token_pos); + void DeclareClassProperty(const AstRawString* class_name, + ClassLiteralProperty* property, bool is_constructor, + ClassInfo* class_info); + void DeclareClassField(ClassLiteralProperty* property, + const AstRawString* property_name, bool is_static, + bool is_computed_name, bool is_private, + ClassInfo* class_info); + Expression* RewriteClassLiteral(Scope* block_scope, const AstRawString* name, + ClassInfo* class_info, int pos, int end_pos); + Statement* DeclareNative(const AstRawString* name, int pos); + + Block* IgnoreCompletion(Statement* statement); + + Scope* NewHiddenCatchScope(); // PatternRewriter and associated methods defined in pattern-rewriter.cc. friend class PatternRewriter; - void DeclareAndInitializeVariables( - Block* block, const DeclarationDescriptor* declaration_descriptor, - const DeclarationParsingResult::Declaration* declaration, - ZonePtrList<const AstRawString>* names, bool* ok); - void RewriteDestructuringAssignment(RewritableExpression* expr); - Expression* RewriteDestructuringAssignment(Assignment* assignment); - - // [if (IteratorType == kAsync)] - // !%_IsJSReceiver(result = Await(next.[[Call]](iterator, « »)) && - // %ThrowIteratorResultNotAnObject(result) - // [else] - // !%_IsJSReceiver(result = next.[[Call]](iterator, « »)) && - // %ThrowIteratorResultNotAnObject(result) - // [endif] - Expression* BuildIteratorNextResult(VariableProxy* iterator, - VariableProxy* next, Variable* result, - IteratorType type, int pos); - - // Initialize the components of a for-in / for-of statement. - Statement* InitializeForEachStatement(ForEachStatement* stmt, - Expression* each, Expression* subject, - Statement* body); - Statement* InitializeForOfStatement(ForOfStatement* stmt, Expression* each, - Expression* iterable, Statement* body, - bool finalize, IteratorType type, - int next_result_pos = kNoSourcePosition); + void InitializeVariables( + ScopedPtrList<Statement>* statements, VariableKind kind, + const DeclarationParsingResult::Declaration* declaration); Block* RewriteForVarInLegacy(const ForInfo& for_info); void DesugarBindingInForEachStatement(ForInfo* for_info, Block** body_block, - Expression** each_variable, bool* ok); - Block* CreateForEachStatementTDZ(Block* init_block, const ForInfo& for_info, - bool* ok); + Expression** each_variable); + Block* CreateForEachStatementTDZ(Block* init_block, const ForInfo& for_info); Statement* DesugarLexicalBindingsInForStatement( ForStatement* loop, Statement* init, Expression* cond, Statement* next, - Statement* body, Scope* inner_scope, const ForInfo& for_info, bool* ok); - - Expression* RewriteDoExpression(Block* body, int pos, bool* ok); + Statement* body, Scope* inner_scope, const ForInfo& for_info); FunctionLiteral* ParseFunctionLiteral( const AstRawString* name, Scanner::Location function_name_location, FunctionNameValidity function_name_validity, FunctionKind kind, int function_token_position, FunctionLiteral::FunctionType type, LanguageMode language_mode, - ZonePtrList<const AstRawString>* arguments_for_wrapped_function, - bool* ok); + ZonePtrList<const AstRawString>* arguments_for_wrapped_function); ObjectLiteral* InitializeObjectLiteral(ObjectLiteral* object_literal) { object_literal->CalculateEmitStore(main_zone()); @@ -412,7 +410,7 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { // The var declarations are hoisted to the function scope, but originate from // a scope where the name has also been let bound or the var declaration is // hoisted over such a scope. - void CheckConflictingVarDeclarations(Scope* scope, bool* ok); + void CheckConflictingVarDeclarations(Scope* scope); bool IsPropertyWithPrivateFieldKey(Expression* property); @@ -423,22 +421,22 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { // Implement sloppy block-scoped functions, ES2015 Annex B 3.3 void InsertSloppyBlockFunctionVarBindings(DeclarationScope* scope); - VariableProxy* NewUnresolved(const AstRawString* name, int begin_pos, - VariableKind kind = NORMAL_VARIABLE); - VariableProxy* NewUnresolved(const AstRawString* name); - Variable* Declare(Declaration* declaration, - DeclarationDescriptor::Kind declaration_kind, - VariableMode mode, InitializationFlag init, bool* ok, - Scope* declaration_scope = nullptr, - int var_end_pos = kNoSourcePosition); - Declaration* DeclareVariable(const AstRawString* name, VariableMode mode, - int pos, bool* ok); - Declaration* DeclareVariable(const AstRawString* name, VariableMode mode, - InitializationFlag init, int pos, bool* ok); + VariableProxy* DeclareVariable(const AstRawString* name, VariableMode mode, + int pos); + VariableProxy* DeclareVariable(const AstRawString* name, VariableMode mode, + InitializationFlag init, int pos); + void DeclareVariable(VariableProxy* proxy, VariableKind kind, + VariableMode mode, InitializationFlag init, + Scope* declaration_scope, bool* added, int begin, + int end = kNoSourcePosition); + void Declare(Declaration* declaration, VariableProxy* proxy, + VariableKind kind, VariableMode mode, InitializationFlag init, + Scope* declaration_scope, bool* added, + int var_end_pos = kNoSourcePosition); bool TargetStackContainsLabel(const AstRawString* label); - BreakableStatement* LookupBreakTarget(const AstRawString* label, bool* ok); - IterationStatement* LookupContinueTarget(const AstRawString* label, bool* ok); + BreakableStatement* LookupBreakTarget(const AstRawString* label); + IterationStatement* LookupContinueTarget(const AstRawString* label); Statement* BuildAssertIsCoercible(Variable* var, ObjectLiteral* pattern); @@ -448,8 +446,6 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { // Skip over a lazy function, either using cached data if we have it, or // 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. // 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. @@ -460,22 +456,19 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { 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); + ProducedPreparseData** produced_preparsed_scope_data); Block* BuildParameterInitializationBlock( - const ParserFormalParameters& parameters, bool* ok); + const ParserFormalParameters& parameters); Block* BuildRejectPromiseOnException(Block* block); - ZonePtrList<Statement>* ParseFunction( - const AstRawString* function_name, int pos, FunctionKind kind, - FunctionLiteral::FunctionType function_type, + void ParseFunction( + ScopedPtrList<Statement>* body, const AstRawString* function_name, + int pos, FunctionKind kind, FunctionLiteral::FunctionType function_type, DeclarationScope* function_scope, int* num_parameters, int* function_length, bool* has_duplicate_parameters, int* expected_property_count, int* suspend_count, - ZonePtrList<const AstRawString>* arguments_for_wrapped_function, - bool* ok); + ZonePtrList<const AstRawString>* arguments_for_wrapped_function); void ThrowPendingError(Isolate* isolate, Handle<Script> script); @@ -497,7 +490,6 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { } void AddExpression(Expression* expression, Zone* zone) { - DCHECK_NOT_NULL(expression); expressions_.Add(expression, zone); } @@ -524,60 +516,40 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { Expression* CloseTemplateLiteral(TemplateLiteralState* state, int start, Expression* tag); - ArrayLiteral* ArrayLiteralFromListWithSpread(ZonePtrList<Expression>* list); - Expression* SpreadCall(Expression* function, ZonePtrList<Expression>* args, - int pos, Call::PossiblyEval is_possibly_eval); - Expression* SpreadCallNew(Expression* function, ZonePtrList<Expression>* args, - int pos); + ArrayLiteral* ArrayLiteralFromListWithSpread( + const ScopedPtrList<Expression>& list); + Expression* SpreadCall(Expression* function, + const ScopedPtrList<Expression>& args, int pos, + Call::PossiblyEval is_possibly_eval); + Expression* SpreadCallNew(Expression* function, + const ScopedPtrList<Expression>& args, int pos); Expression* RewriteSuperCall(Expression* call_expression); void SetLanguageMode(Scope* scope, LanguageMode mode); void SetAsmModule(); - // Rewrite all DestructuringAssignments in the current FunctionState. - V8_INLINE void RewriteDestructuringAssignments(); - Expression* RewriteSpreads(ArrayLiteral* lit); - V8_INLINE void QueueDestructuringAssignmentForRewriting( - RewritableExpression* assignment); - friend class InitializerRewriter; void RewriteParameterInitializer(Expression* expr); Expression* BuildInitialYield(int pos, FunctionKind kind); Assignment* BuildCreateJSGeneratorObject(int pos, FunctionKind kind); - Expression* BuildRejectPromise(Expression* value, int pos); - Variable* PromiseVariable(); - Variable* AsyncGeneratorAwaitVariable(); // Generic AST generator for throwing errors from compiled code. Expression* NewThrowError(Runtime::FunctionId function_id, - MessageTemplate::Template message, - const AstRawString* arg, int pos); - - void FinalizeIteratorUse(Variable* completion, Expression* condition, - Variable* iter, Block* iterator_use, Block* result, - IteratorType type); + MessageTemplate message, const AstRawString* arg, + int pos); Statement* FinalizeForOfStatement(ForOfStatement* loop, Variable* completion, IteratorType type, int pos); - void BuildIteratorClose(ZonePtrList<Statement>* statements, - Variable* iterator, Variable* input, Variable* output, - IteratorType type); - void BuildIteratorCloseForCompletion(ZonePtrList<Statement>* statements, - Variable* iterator, - Expression* completion, - IteratorType type); Statement* CheckCallable(Variable* var, Expression* error, int pos); - V8_INLINE void RewriteAsyncFunctionBody(ZonePtrList<Statement>* body, - Block* block, - Expression* return_value, bool* ok); + void RewriteAsyncFunctionBody(ScopedPtrList<Statement>* body, Block* block, + Expression* return_value); void AddArrowFunctionFormalParameters(ParserFormalParameters* parameters, - Expression* params, int end_pos, - bool* ok); + Expression* params, int end_pos); void SetFunctionName(Expression* value, const AstRawString* name, const AstRawString* prefix = nullptr); @@ -586,6 +558,10 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { return identifier == ast_value_factory()->eval_string(); } + V8_INLINE bool IsAsync(const AstRawString* identifier) const { + return identifier == ast_value_factory()->async_string(); + } + V8_INLINE bool IsArguments(const AstRawString* identifier) const { return identifier == ast_value_factory()->arguments_string(); } @@ -606,7 +582,6 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { // inside a variable proxy). We exclude the case of 'this', which // has been converted to a variable proxy. V8_INLINE static bool IsIdentifier(Expression* expression) { - DCHECK_NOT_NULL(expression); VariableProxy* operand = expression->AsVariableProxy(); return operand != nullptr && !operand->is_this() && !operand->is_new_target(); @@ -646,14 +621,6 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { return string->AsArrayIndex(index); } - V8_INLINE bool IsUseStrictDirective(Statement* statement) const { - return IsStringLiteral(statement, ast_value_factory()->use_strict_string()); - } - - V8_INLINE bool IsUseAsmDirective(Statement* statement) const { - return IsStringLiteral(statement, ast_value_factory()->use_asm_string()); - } - // Returns true if the statement is an expression statement containing // a single string literal. If a second argument is given, the literal // is also compared with it and the result is true only if they are equal. @@ -711,21 +678,11 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { } } - // Determine if the expression is a variable proxy and mark it as being used - // in an assignment or with a increment/decrement operator. - V8_INLINE static void MarkExpressionAsAssigned(Expression* expression) { - DCHECK_NOT_NULL(expression); - if (expression->IsVariableProxy()) { - expression->AsVariableProxy()->set_is_assigned(); - } - } - // A shortcut for performing a ToString operation V8_INLINE Expression* ToString(Expression* expr) { if (expr->IsStringLiteral()) return expr; - ZonePtrList<Expression>* args = - new (zone()) ZonePtrList<Expression>(1, zone()); - args->Add(expr, zone()); + ScopedPtrList<Expression> args(pointer_buffer()); + args.Add(expr); return factory()->NewCallRuntime(Runtime::kInlineToString, args, expr->position()); } @@ -752,8 +709,8 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { int pos); // Generate AST node that throws a ReferenceError with the given type. - V8_INLINE Expression* NewThrowReferenceError( - MessageTemplate::Template message, int pos) { + V8_INLINE Expression* NewThrowReferenceError(MessageTemplate message, + int pos) { return NewThrowError(Runtime::kNewReferenceError, message, ast_value_factory()->empty_string(), pos); } @@ -761,33 +718,26 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { // Generate AST node that throws a SyntaxError with the given // type. The first argument may be null (in the handle sense) in // which case no arguments are passed to the constructor. - V8_INLINE Expression* NewThrowSyntaxError(MessageTemplate::Template message, + V8_INLINE Expression* NewThrowSyntaxError(MessageTemplate message, const AstRawString* arg, int pos) { return NewThrowError(Runtime::kNewSyntaxError, message, arg, pos); } // Generate AST node that throws a TypeError with the given // type. Both arguments must be non-null (in the handle sense). - V8_INLINE Expression* NewThrowTypeError(MessageTemplate::Template message, + V8_INLINE Expression* NewThrowTypeError(MessageTemplate message, const AstRawString* arg, int pos) { return NewThrowError(Runtime::kNewTypeError, message, arg, pos); } // Reporting errors. void ReportMessageAt(Scanner::Location source_location, - MessageTemplate::Template message, - const char* arg = nullptr, + MessageTemplate message, const char* arg = nullptr, ParseErrorType error_type = kSyntaxError) { - if (stack_overflow()) { - // Suppress the error message (syntax error or such) in the presence of a - // stack overflow. The isolate allows only one pending exception at at - // time - // and we want to report the stack overflow later. - return; - } pending_error_handler()->ReportMessageAt(source_location.beg_pos, source_location.end_pos, message, arg, error_type); + scanner_.set_parser_error(); } // Dummy implementation. The parser should never have a unidentifiable @@ -795,21 +745,18 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { V8_INLINE void ReportUnidentifiableError() { UNREACHABLE(); } void ReportMessageAt(Scanner::Location source_location, - MessageTemplate::Template message, - const AstRawString* arg, + MessageTemplate message, const AstRawString* arg, ParseErrorType error_type = kSyntaxError) { - if (stack_overflow()) { - // Suppress the error message (syntax error or such) in the presence of a - // stack overflow. The isolate allows only one pending exception at at - // time - // and we want to report the stack overflow later. - return; - } pending_error_handler()->ReportMessageAt(source_location.beg_pos, source_location.end_pos, message, arg, error_type); + scanner_.set_parser_error(); } + void ReportUnexpectedTokenAt( + Scanner::Location location, Token::Value token, + MessageTemplate message = MessageTemplate::kUnexpectedToken); + // "null" return type creators. V8_INLINE static std::nullptr_t NullIdentifier() { return nullptr; } V8_INLINE static std::nullptr_t NullExpression() { return nullptr; } @@ -821,6 +768,8 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { return nullptr; } V8_INLINE static std::nullptr_t NullStatement() { return nullptr; } + V8_INLINE static std::nullptr_t NullBlock() { return nullptr; } + Expression* FailureExpression() { return factory()->FailureExpression(); } template <typename T> V8_INLINE static bool IsNull(T subject) { @@ -860,7 +809,7 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { Expression* NewTargetExpression(int pos); Expression* ImportMetaExpression(int pos); - Literal* ExpressionFromLiteral(Token::Value token, int pos); + Expression* ExpressionFromLiteral(Token::Value token, int pos); V8_INLINE VariableProxy* ExpressionFromIdentifier( const AstRawString* name, int start_position, @@ -868,13 +817,12 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { if (infer == InferName::kYes) { fni_.PushVariableName(name); } - return NewUnresolved(name, start_position); + return expression_scope()->NewVariable(name, start_position); } - V8_INLINE Expression* ExpressionFromString(int pos) { - const AstRawString* symbol = GetSymbol(); - fni_.PushLiteralName(symbol); - return factory()->NewStringLiteral(symbol, pos); + V8_INLINE Variable* DeclareCatchVariableName(Scope* scope, + const AstRawString* name) { + return scope->DeclareCatchVariableName(name); } V8_INLINE ZonePtrList<Expression>* NewExpressionList(int size) const { @@ -892,9 +840,8 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { return new (zone()) ZonePtrList<Statement>(size, zone()); } - V8_INLINE Expression* NewV8Intrinsic(const AstRawString* name, - ZonePtrList<Expression>* args, int pos, - bool* ok); + Expression* NewV8Intrinsic(const AstRawString* name, + const ScopedPtrList<Expression>& args, int pos); V8_INLINE Statement* NewThrowStatement(Expression* exception, int pos) { return factory()->NewExpressionStatement( @@ -907,44 +854,37 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { int initializer_end_position, bool is_rest) { parameters->UpdateArityAndFunctionLength(initializer != nullptr, is_rest); - bool has_simple_name = pattern->IsVariableProxy() && initializer == nullptr; - const AstRawString* name = has_simple_name - ? pattern->AsVariableProxy()->raw_name() - : ast_value_factory()->empty_string(); auto parameter = new (parameters->scope->zone()) - ParserFormalParameters::Parameter(name, pattern, initializer, + ParserFormalParameters::Parameter(pattern, initializer, scanner()->location().beg_pos, initializer_end_position, is_rest); parameters->params.Add(parameter); } - V8_INLINE void DeclareFormalParameters( - DeclarationScope* scope, - const base::ThreadedList<ParserFormalParameters::Parameter>& parameters, - bool is_simple, bool* has_duplicate = nullptr) { - if (!is_simple) scope->SetHasNonSimpleParameters(); - for (auto parameter : parameters) { - bool is_optional = parameter->initializer != nullptr; + V8_INLINE void DeclareFormalParameters(ParserFormalParameters* parameters) { + bool is_simple = parameters->is_simple; + DeclarationScope* scope = parameters->scope; + if (!is_simple) scope->MakeParametersNonSimple(); + for (auto parameter : parameters->params) { + bool is_optional = parameter->initializer() != nullptr; // If the parameter list is simple, declare the parameters normally with // their names. If the parameter list is not simple, declare a temporary // for each parameter - the corresponding named variable is declared by // BuildParamerterInitializationBlock. scope->DeclareParameter( - is_simple ? parameter->name : ast_value_factory()->empty_string(), + is_simple ? parameter->name() : ast_value_factory()->empty_string(), is_simple ? VariableMode::kVar : VariableMode::kTemporary, - is_optional, parameter->is_rest, has_duplicate, ast_value_factory(), + is_optional, parameter->is_rest(), ast_value_factory(), parameter->position); } } - void DeclareArrowFunctionFormalParameters(ParserFormalParameters* parameters, - Expression* params, - const Scanner::Location& params_loc, - Scanner::Location* duplicate_loc, - bool* ok); + void DeclareArrowFunctionFormalParameters( + ParserFormalParameters* parameters, Expression* params, + const Scanner::Location& params_loc); - Expression* ExpressionListToExpression(ZonePtrList<Expression>* args); + Expression* ExpressionListToExpression(const ScopedPtrList<Expression>& args); void SetFunctionNameFromPropertyName(LiteralProperty* property, const AstRawString* name, @@ -956,11 +896,6 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { void SetFunctionNameFromIdentifierRef(Expression* value, Expression* identifier); - V8_INLINE ZoneList<typename ExpressionClassifier::Error>* - GetReportedErrorList() const { - return function_state_->GetReportedErrorList(); - } - V8_INLINE void CountUsage(v8::Isolate::UseCounterFeature feature) { ++use_counts_[feature]; } @@ -1091,9 +1026,21 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { node, new (zone()) TryFinallyStatementSourceRanges(body_range)); } + // Generate the next internal variable name for binding an exported namespace + // object (used to implement the "export * as" syntax). + const AstRawString* NextInternalNamespaceExportName(); + + ParseInfo* info() const { return info_; } + + std::vector<uint8_t>* preparse_data_buffer() { + return &preparse_data_buffer_; + } + // Parser's private field members. friend class PreParserZoneScope; // Uses reusable_preparser(). + friend class PreparseDataBuilder; // Uses preparse_data_buffer() + ParseInfo* info_; Scanner scanner_; Zone preparser_zone_; PreParser* reusable_preparser_; @@ -1107,13 +1054,17 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { ScriptCompiler::CompileOptions compile_options_; + // For NextInternalNamespaceExportName(). + int number_of_named_namespace_exports_ = 0; + // Other information which will be stored in Parser and moved to Isolate after // parsing. int use_counts_[v8::Isolate::kUseCounterFeatureCount]; int total_preparse_skipped_; bool allow_lazy_; bool temp_zoned_; - ConsumedPreParsedScopeData* consumed_preparsed_scope_data_; + ConsumedPreparseData* consumed_preparse_data_; + std::vector<uint8_t> preparse_data_buffer_; // If not kNoSourcePosition, indicates that the first function literal // encountered is a dynamic function, see CreateDynamicFunction(). This field diff --git a/deps/v8/src/parsing/parsing.cc b/deps/v8/src/parsing/parsing.cc index 378023cbeb..7ff080b2f9 100644 --- a/deps/v8/src/parsing/parsing.cc +++ b/deps/v8/src/parsing/parsing.cc @@ -42,7 +42,6 @@ bool ParseProgram(ParseInfo* info, Isolate* isolate) { info->pending_error_handler()->ReportErrors(isolate, info->script(), info->ast_value_factory()); } else { - result->scope()->AttachOuterScopeInfo(info, isolate); info->set_language_mode(info->literal()->language_mode()); if (info->is_eval()) { info->set_allow_eval_cache(parser.allow_eval_cache()); @@ -80,7 +79,7 @@ bool ParseFunction(ParseInfo* info, Handle<SharedFunctionInfo> shared_info, info->pending_error_handler()->ReportErrors(isolate, info->script(), info->ast_value_factory()); } else { - result->scope()->AttachOuterScopeInfo(info, isolate); + info->ast_value_factory()->Internalize(isolate); if (info->is_eval()) { info->set_allow_eval_cache(parser.allow_eval_cache()); } diff --git a/deps/v8/src/parsing/pattern-rewriter.cc b/deps/v8/src/parsing/pattern-rewriter.cc index 4465670a8f..0ef570ee52 100644 --- a/deps/v8/src/parsing/pattern-rewriter.cc +++ b/deps/v8/src/parsing/pattern-rewriter.cc @@ -3,7 +3,7 @@ // found in the LICENSE file. #include "src/ast/ast.h" -#include "src/messages.h" +#include "src/message-template.h" #include "src/objects-inl.h" #include "src/parsing/expression-scope-reparenter.h" #include "src/parsing/parser.h" @@ -12,314 +12,108 @@ namespace v8 { namespace internal { +// An AST visitor which performs declaration and assignment related tasks, +// particularly for destructuring patterns: +// +// 1. Declares variables from variable proxies (particularly for destructuring +// declarations), +// 2. Marks destructuring-assigned variable proxies as assigned, and +// 3. Rewrites scopes for parameters containing a sloppy eval. +// +// Historically this also rewrote destructuring assignments/declarations as a +// block of multiple assignments, hence the named, however this is now done +// during bytecode generation. +// +// TODO(leszeks): Rename or remove this class class PatternRewriter final : public AstVisitor<PatternRewriter> { public: - // Limit the allowed number of local variables in a function. The hard limit - // is that offsets computed by FullCodeGenerator::StackOperand and similar - // functions are ints, and they should not overflow. In addition, accessing - // local variables creates user-controlled constants in the generated code, - // and we don't want too much user-controlled memory inside the code (this was - // the reason why this limit was introduced in the first place; see - // https://codereview.chromium.org/7003030/ ). - static const int kMaxNumFunctionLocals = 4194303; // 2^22-1 - typedef Parser::DeclarationDescriptor DeclarationDescriptor; - static void DeclareAndInitializeVariables( - Parser* parser, Block* block, - const DeclarationDescriptor* declaration_descriptor, - const Parser::DeclarationParsingResult::Declaration* declaration, - ZonePtrList<const AstRawString>* names, bool* ok); - - static Expression* RewriteDestructuringAssignment(Parser* parser, - Assignment* to_rewrite, - Scope* scope); + static void InitializeVariables( + Parser* parser, VariableKind kind, + const Parser::DeclarationParsingResult::Declaration* declaration); private: - 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), - block_(nullptr), - descriptor_(descriptor), - names_(names), - current_value_(nullptr), - ok_(nullptr), + PatternRewriter(Parser* parser, VariableKind kind, int initializer_position, + bool declares_parameter_containing_sloppy_eval) + : parser_(parser), 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) {} + declares_parameter_containing_sloppy_eval) {} #define DECLARE_VISIT(type) void Visit##type(v8::internal::type* node); // Visiting functions for AST nodes make this an AstVisitor. AST_NODE_LIST(DECLARE_VISIT) #undef DECLARE_VISIT - PatternContext context() const { return context_; } - - void RecurseIntoSubpattern(AstNode* pattern, Expression* value) { - Expression* old_value = current_value_; - current_value_ = value; - recursion_level_++; - Visit(pattern); - recursion_level_--; - current_value_ = old_value; - } - - Expression* Rewrite(Assignment* assign) { + Expression* Visit(Assignment* assign) { + if (parser_->has_error()) return parser_->FailureExpression(); 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); + VisitObjectLiteral(pattern->AsObjectLiteral()); } else { DCHECK(pattern->IsArrayLiteral()); - VisitArrayLiteral(pattern->AsArrayLiteral(), &temp); + VisitArrayLiteral(pattern->AsArrayLiteral()); } - DCHECK_NOT_NULL(temp); - current_value_ = old_value; - return factory()->NewDoExpression(block_, temp, pos); + return assign; } - 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; } - bool IsSubPattern() const { return recursion_level_ > 1; } - void RewriteParameterScopes(Expression* expr); - Variable* CreateTempVar(Expression* value = nullptr); + Scope* scope() const { return parser_->scope(); } - AstNodeFactory* factory() const { return parser_->factory(); } - AstValueFactory* ast_value_factory() const { - return parser_->ast_value_factory(); - } - Zone* zone() const { return parser_->zone(); } - Scope* scope() const { return scope_; } - - Scope* const scope_; Parser* const parser_; - Block* block_; - const DeclarationDescriptor* descriptor_; - ZonePtrList<const AstRawString>* names_; - Expression* current_value_; - bool* ok_; const int initializer_position_; - const int value_beg_position_; - PatternContext context_; - const bool declares_parameter_containing_sloppy_eval_ : 1; - int recursion_level_; + const bool declares_parameter_containing_sloppy_eval_; DEFINE_AST_VISITOR_MEMBERS_WITHOUT_STACKOVERFLOW() }; -void Parser::DeclareAndInitializeVariables( - Block* block, const DeclarationDescriptor* declaration_descriptor, - const DeclarationParsingResult::Declaration* declaration, - ZonePtrList<const AstRawString>* names, bool* ok) { - PatternRewriter::DeclareAndInitializeVariables( - this, block, declaration_descriptor, declaration, names, ok); -} - -void Parser::RewriteDestructuringAssignment(RewritableExpression* to_rewrite) { - 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()); - return PatternRewriter::RewriteDestructuringAssignment(this, assignment, - scope()); -} - -void PatternRewriter::DeclareAndInitializeVariables( - Parser* parser, Block* block, - const DeclarationDescriptor* declaration_descriptor, - const Parser::DeclarationParsingResult::Declaration* declaration, - ZonePtrList<const AstRawString>* names, bool* ok) { - DCHECK(block->ignore_completion_value()); - - 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.ok_ = ok; - - rewriter.RecurseIntoSubpattern(declaration->pattern, - declaration->initializer); -} - -Expression* PatternRewriter::RewriteDestructuringAssignment( - Parser* parser, Assignment* to_rewrite, Scope* scope) { - DCHECK(!scope->HasBeenRemoved()); - - PatternRewriter rewriter(scope, parser, ASSIGNMENT); - return rewriter.Rewrite(to_rewrite); -} - -void PatternRewriter::VisitVariableProxy(VariableProxy* pattern) { - Expression* value = current_value_; +void Parser::InitializeVariables( + ScopedPtrList<Statement>* statements, VariableKind kind, + const DeclarationParsingResult::Declaration* declaration) { + if (has_error()) return; - if (IsAssignmentContext()) { - // In an assignment context, simply perform the assignment - Assignment* assignment = factory()->NewAssignment( - Token::ASSIGN, pattern, value, pattern->position()); - block_->statements()->Add( - factory()->NewExpressionStatement(assignment, pattern->position()), - zone()); + if (!declaration->initializer) { + // The parameter scope is only a block scope if the initializer calls sloppy + // eval. Since there is no initializer, we can't be calling sloppy eval. + DCHECK_IMPLIES(kind == PARAMETER_VARIABLE, scope()->is_function_scope()); return; } - DCHECK_NOT_NULL(block_); - DCHECK_NOT_NULL(descriptor_); - DCHECK_NOT_NULL(ok_); - - 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 - // assignment for variables and constants because the value must be assigned - // when the variable is encountered in the source. But the variable/constant - // is declared (and set to 'undefined') upon entering the function within - // which the variable or constant is declared. Only function variables have - // an initial value in the declaration (because they are initialized upon - // entering the function). - const AstRawString* name = pattern->raw_name(); - VariableProxy* proxy = pattern; - Declaration* declaration; - if (descriptor_->mode == VariableMode::kVar && - !scope()->is_declaration_scope()) { - DCHECK(scope()->is_block_scope() || scope()->is_with_scope()); - declaration = factory()->NewNestedVariableDeclaration( - proxy, scope(), descriptor_->declaration_pos); - } else { - declaration = - factory()->NewVariableDeclaration(proxy, descriptor_->declaration_pos); - } - - // When an extra declaration scope needs to be inserted to account for - // 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. - Variable* var = parser_->Declare( - declaration, descriptor_->declaration_kind, descriptor_->mode, - Variable::DefaultInitializationFlag(descriptor_->mode), ok_, - outer_function_scope); - if (!*ok_) return; - DCHECK_NOT_NULL(var); - DCHECK(proxy->is_resolved()); - DCHECK_NE(initializer_position_, kNoSourcePosition); - var->set_initializer_position(initializer_position_); - - 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; - return; - } - if (names_) { - names_->Add(name, zone()); - } - - // If there's no initializer, we're done. - if (value == nullptr) return; - - Scope* var_init_scope = scope(); - Parser::MarkLoopVariableAsAssigned(var_init_scope, proxy->var(), - descriptor_->declaration_kind); - - // A declaration of the form: - // - // var v = x; - // - // is syntactic sugar for: - // - // var v; v = x; - // - // 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); - DCHECK_NOT_NULL(proxy->var()); - } - // Add break location for destructured sub-pattern. - int pos = value_beg_position_; + PatternRewriter::InitializeVariables(this, kind, declaration); + int pos = declaration->value_beg_position; if (pos == kNoSourcePosition) { - pos = IsSubPattern() ? pattern->position() : value->position(); + pos = declaration->initializer_position; } - Assignment* assignment = - factory()->NewAssignment(Token::INIT, proxy, value, pos); - block_->statements()->Add(factory()->NewExpressionStatement(assignment, pos), - zone()); + Assignment* assignment = factory()->NewAssignment( + Token::INIT, declaration->pattern, declaration->initializer, pos); + statements->Add(factory()->NewExpressionStatement(assignment, pos)); } -Variable* PatternRewriter::CreateTempVar(Expression* value) { - auto temp = scope()->NewTemporary(ast_value_factory()->empty_string()); - if (value != nullptr) { - auto assignment = factory()->NewAssignment( - Token::ASSIGN, factory()->NewVariableProxy(temp), value, - kNoSourcePosition); +void PatternRewriter::InitializeVariables( + Parser* parser, VariableKind kind, + const Parser::DeclarationParsingResult::Declaration* declaration) { + PatternRewriter rewriter( + parser, kind, declaration->initializer_position, + kind == PARAMETER_VARIABLE && parser->scope()->is_block_scope()); - block_->statements()->Add( - factory()->NewExpressionStatement(assignment, kNoSourcePosition), - zone()); - } - return temp; + rewriter.Visit(declaration->pattern); } -void PatternRewriter::VisitRewritableExpression(RewritableExpression* node) { - 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()); - node->set_rewritten(); - return Visit(node->expression()); +void PatternRewriter::VisitVariableProxy(VariableProxy* proxy) { + DCHECK(!parser_->has_error()); + Variable* var = + proxy->is_resolved() + ? proxy->var() + : scope()->GetDeclarationScope()->LookupLocal(proxy->raw_name()); + + DCHECK_NOT_NULL(var); + + DCHECK_NE(initializer_position_, kNoSourcePosition); + var->set_initializer_position(initializer_position_); } // When an extra declaration scope needs to be inserted to account for @@ -332,378 +126,35 @@ void PatternRewriter::RewriteParameterScopes(Expression* expr) { } } -void PatternRewriter::VisitObjectLiteral(ObjectLiteral* pattern, - Variable** temp_var) { - auto temp = *temp_var = CreateTempVar(current_value_); - - ZonePtrList<Expression>* rest_runtime_callargs = nullptr; - if (pattern->has_rest_property()) { - // non_rest_properties_count = pattern->properties()->length - 1; - // args_length = 1 + non_rest_properties_count because we need to - // pass temp as well to the runtime function. - int args_length = pattern->properties()->length(); - rest_runtime_callargs = - new (zone()) ZonePtrList<Expression>(args_length, zone()); - rest_runtime_callargs->Add(factory()->NewVariableProxy(temp), zone()); - } - - block_->statements()->Add(parser_->BuildAssertIsCoercible(temp, pattern), - zone()); - +void PatternRewriter::VisitObjectLiteral(ObjectLiteral* pattern) { for (ObjectLiteralProperty* property : *pattern->properties()) { - Expression* value; - - if (property->kind() == ObjectLiteralProperty::Kind::SPREAD) { - // var { y, [x++]: a, ...c } = temp - // becomes - // var y = temp.y; - // var temp1 = %ToName(x++); - // var a = temp[temp1]; - // var c; - // c = %CopyDataPropertiesWithExcludedProperties(temp, "y", temp1); - value = factory()->NewCallRuntime( - Runtime::kCopyDataPropertiesWithExcludedProperties, - rest_runtime_callargs, kNoSourcePosition); - } else { - Expression* key = property->key(); - - if (!key->IsLiteral()) { - // Computed property names contain expressions which might require - // scope rewriting. - RewriteParameterScopes(key); - } - - if (pattern->has_rest_property()) { - Expression* excluded_property = key; - - if (property->is_computed_name()) { - DCHECK(!key->IsPropertyName() || !key->IsNumberLiteral()); - auto args = new (zone()) ZonePtrList<Expression>(1, zone()); - args->Add(key, zone()); - auto to_name_key = CreateTempVar(factory()->NewCallRuntime( - Runtime::kToName, args, kNoSourcePosition)); - key = factory()->NewVariableProxy(to_name_key); - excluded_property = factory()->NewVariableProxy(to_name_key); - } else { - DCHECK(key->IsPropertyName() || key->IsNumberLiteral()); - } - - DCHECK_NOT_NULL(rest_runtime_callargs); - rest_runtime_callargs->Add(excluded_property, zone()); - } - - value = factory()->NewProperty(factory()->NewVariableProxy(temp), key, - kNoSourcePosition); + Expression* key = property->key(); + if (!key->IsLiteral()) { + // Computed property names contain expressions which might require + // scope rewriting. + RewriteParameterScopes(key); } - - RecurseIntoSubpattern(property->value(), value); + Visit(property->value()); } } -void PatternRewriter::VisitObjectLiteral(ObjectLiteral* node) { - Variable* temp_var = nullptr; - VisitObjectLiteral(node, &temp_var); -} - -void PatternRewriter::VisitArrayLiteral(ArrayLiteral* node, - Variable** temp_var) { - DCHECK(block_->ignore_completion_value()); - - auto temp = *temp_var = CreateTempVar(current_value_); - auto iterator = CreateTempVar(factory()->NewGetIterator( - factory()->NewVariableProxy(temp), current_value_, IteratorType::kNormal, - current_value_->position())); - auto next = CreateTempVar(factory()->NewProperty( - factory()->NewVariableProxy(iterator), - factory()->NewStringLiteral(ast_value_factory()->next_string(), - kNoSourcePosition), - kNoSourcePosition)); - auto done = - CreateTempVar(factory()->NewBooleanLiteral(false, kNoSourcePosition)); - auto result = CreateTempVar(); - auto v = CreateTempVar(); - auto completion = CreateTempVar(); - auto nopos = kNoSourcePosition; - - // For the purpose of iterator finalization, we temporarily set block_ to a - // new block. In the main body of this function, we write to block_ (both - // explicitly and implicitly via recursion). At the end of the function, we - // wrap this new block in a try-finally statement, restore block_ to its - // original value, and add the try-finally statement to block_. - auto target = block_; - block_ = factory()->NewBlock(8, true); - - Spread* spread = nullptr; +void PatternRewriter::VisitArrayLiteral(ArrayLiteral* node) { for (Expression* value : *node->values()) { - if (value->IsSpread()) { - spread = value->AsSpread(); - break; - } - - // if (!done) { - // done = true; // If .next, .done or .value throws, don't close. - // result = IteratorNext(iterator); - // if (result.done) { - // v = undefined; - // } else { - // v = result.value; - // done = false; - // } - // } - Statement* if_not_done; - { - auto result_done = factory()->NewProperty( - factory()->NewVariableProxy(result), - factory()->NewStringLiteral(ast_value_factory()->done_string(), - kNoSourcePosition), - kNoSourcePosition); - - auto assign_undefined = factory()->NewAssignment( - Token::ASSIGN, factory()->NewVariableProxy(v), - factory()->NewUndefinedLiteral(kNoSourcePosition), kNoSourcePosition); - - auto assign_value = factory()->NewAssignment( - Token::ASSIGN, factory()->NewVariableProxy(v), - factory()->NewProperty( - factory()->NewVariableProxy(result), - factory()->NewStringLiteral(ast_value_factory()->value_string(), - kNoSourcePosition), - kNoSourcePosition), - kNoSourcePosition); - - auto unset_done = factory()->NewAssignment( - Token::ASSIGN, factory()->NewVariableProxy(done), - factory()->NewBooleanLiteral(false, kNoSourcePosition), - kNoSourcePosition); - - auto inner_else = factory()->NewBlock(2, true); - inner_else->statements()->Add( - factory()->NewExpressionStatement(assign_value, nopos), zone()); - inner_else->statements()->Add( - factory()->NewExpressionStatement(unset_done, nopos), zone()); - - auto inner_if = factory()->NewIfStatement( - result_done, - factory()->NewExpressionStatement(assign_undefined, nopos), - inner_else, nopos); - - auto next_block = factory()->NewBlock(3, true); - next_block->statements()->Add( - factory()->NewExpressionStatement( - factory()->NewAssignment( - Token::ASSIGN, factory()->NewVariableProxy(done), - factory()->NewBooleanLiteral(true, nopos), nopos), - nopos), - zone()); - next_block->statements()->Add( - factory()->NewExpressionStatement( - parser_->BuildIteratorNextResult( - factory()->NewVariableProxy(iterator), - factory()->NewVariableProxy(next), result, - IteratorType::kNormal, kNoSourcePosition), - kNoSourcePosition), - zone()); - next_block->statements()->Add(inner_if, zone()); - - if_not_done = factory()->NewIfStatement( - factory()->NewUnaryOperation( - Token::NOT, factory()->NewVariableProxy(done), kNoSourcePosition), - next_block, factory()->NewEmptyStatement(kNoSourcePosition), - kNoSourcePosition); - } - block_->statements()->Add(if_not_done, zone()); - - if (!value->IsTheHoleLiteral()) { - { - // completion = kAbruptCompletion; - Expression* proxy = factory()->NewVariableProxy(completion); - Expression* assignment = factory()->NewAssignment( - Token::ASSIGN, proxy, - factory()->NewSmiLiteral(Parser::kAbruptCompletion, nopos), nopos); - block_->statements()->Add( - factory()->NewExpressionStatement(assignment, nopos), zone()); - } - - RecurseIntoSubpattern(value, factory()->NewVariableProxy(v)); - - { - // completion = kNormalCompletion; - Expression* proxy = factory()->NewVariableProxy(completion); - Expression* assignment = factory()->NewAssignment( - Token::ASSIGN, proxy, - factory()->NewSmiLiteral(Parser::kNormalCompletion, nopos), nopos); - block_->statements()->Add( - factory()->NewExpressionStatement(assignment, nopos), zone()); - } - } - } - - if (spread != nullptr) { - // A spread can only occur as the last component. It is not handled by - // RecurseIntoSubpattern above. - - // let array = []; - // let index = 0; - // while (!done) { - // done = true; // If .next, .done or .value throws, don't close. - // result = IteratorNext(iterator); - // if (!result.done) { - // StoreInArrayLiteral(array, index, result.value); - // done = false; - // } - // index++; - // } - - // let array = []; - Variable* array; - { - auto empty_exprs = new (zone()) ZonePtrList<Expression>(0, zone()); - array = CreateTempVar( - factory()->NewArrayLiteral(empty_exprs, kNoSourcePosition)); - } - - // let index = 0; - Variable* index = - CreateTempVar(factory()->NewSmiLiteral(0, kNoSourcePosition)); - - // done = true; - Statement* set_done = factory()->NewExpressionStatement( - factory()->NewAssignment( - Token::ASSIGN, factory()->NewVariableProxy(done), - factory()->NewBooleanLiteral(true, nopos), nopos), - nopos); - - // result = IteratorNext(iterator); - Statement* get_next = factory()->NewExpressionStatement( - parser_->BuildIteratorNextResult(factory()->NewVariableProxy(iterator), - factory()->NewVariableProxy(next), - result, IteratorType::kNormal, nopos), - nopos); - - // StoreInArrayLiteral(array, index, result.value); - Statement* store; - { - auto value = factory()->NewProperty( - factory()->NewVariableProxy(result), - factory()->NewStringLiteral(ast_value_factory()->value_string(), - nopos), - nopos); - store = factory()->NewExpressionStatement( - factory()->NewStoreInArrayLiteral(factory()->NewVariableProxy(array), - factory()->NewVariableProxy(index), - value, nopos), - nopos); - } - - // done = false; - Statement* unset_done = factory()->NewExpressionStatement( - factory()->NewAssignment( - Token::ASSIGN, factory()->NewVariableProxy(done), - factory()->NewBooleanLiteral(false, nopos), nopos), - nopos); - - // if (!result.done) { #store; #unset_done } - Statement* maybe_store_and_unset_done; - { - Expression* result_done = - factory()->NewProperty(factory()->NewVariableProxy(result), - factory()->NewStringLiteral( - ast_value_factory()->done_string(), nopos), - nopos); - - Block* then = factory()->NewBlock(2, true); - then->statements()->Add(store, zone()); - then->statements()->Add(unset_done, zone()); - - maybe_store_and_unset_done = factory()->NewIfStatement( - factory()->NewUnaryOperation(Token::NOT, result_done, nopos), then, - factory()->NewEmptyStatement(nopos), nopos); - } - - // index++; - Statement* increment_index; - { - increment_index = factory()->NewExpressionStatement( - factory()->NewCountOperation( - Token::INC, false, factory()->NewVariableProxy(index), nopos), - nopos); - } - - // while (!done) { - // #set_done; - // #get_next; - // #maybe_store_and_unset_done; - // #increment_index; - // } - WhileStatement* loop = - factory()->NewWhileStatement(nullptr, nullptr, nopos); - { - Expression* condition = factory()->NewUnaryOperation( - Token::NOT, factory()->NewVariableProxy(done), nopos); - Block* body = factory()->NewBlock(4, true); - body->statements()->Add(set_done, zone()); - body->statements()->Add(get_next, zone()); - body->statements()->Add(maybe_store_and_unset_done, zone()); - body->statements()->Add(increment_index, zone()); - loop->Initialize(condition, body); - } - - block_->statements()->Add(loop, zone()); - RecurseIntoSubpattern(spread->expression(), - factory()->NewVariableProxy(array)); + if (value->IsTheHoleLiteral()) continue; + Visit(value); } - - Expression* closing_condition = factory()->NewUnaryOperation( - Token::NOT, factory()->NewVariableProxy(done), nopos); - - parser_->FinalizeIteratorUse(completion, closing_condition, iterator, block_, - target, IteratorType::kNormal); - block_ = target; -} - -void PatternRewriter::VisitArrayLiteral(ArrayLiteral* node) { - Variable* temp_var = nullptr; - VisitArrayLiteral(node, &temp_var); } void PatternRewriter::VisitAssignment(Assignment* node) { - // let {<pattern> = <init>} = <value> - // becomes - // temp = <value>; - // <pattern> = temp === undefined ? <init> : temp; DCHECK_EQ(Token::ASSIGN, node->op()); - auto initializer = node->value(); - auto value = initializer; - auto temp = CreateTempVar(current_value_); - - Expression* is_undefined = factory()->NewCompareOperation( - Token::EQ_STRICT, factory()->NewVariableProxy(temp), - factory()->NewUndefinedLiteral(kNoSourcePosition), kNoSourcePosition); - value = factory()->NewConditional(is_undefined, initializer, - factory()->NewVariableProxy(temp), - kNoSourcePosition); - // Initializer may have been parsed in the wrong scope. - RewriteParameterScopes(initializer); - - RecurseIntoSubpattern(node->target(), value); -} - - -// =============== AssignmentPattern only ================== - -void PatternRewriter::VisitProperty(v8::internal::Property* node) { - DCHECK(IsAssignmentContext()); - auto value = current_value_; - - Assignment* assignment = - factory()->NewAssignment(Token::ASSIGN, node, value, node->position()); + RewriteParameterScopes(node->value()); - block_->statements()->Add( - factory()->NewExpressionStatement(assignment, kNoSourcePosition), zone()); + Visit(node->target()); } +void PatternRewriter::VisitSpread(Spread* node) { Visit(node->expression()); } // =============== UNREACHABLE ============================= @@ -734,17 +185,16 @@ NOT_A_PATTERN(ForOfStatement) NOT_A_PATTERN(ForStatement) NOT_A_PATTERN(FunctionDeclaration) NOT_A_PATTERN(FunctionLiteral) -NOT_A_PATTERN(GetIterator) NOT_A_PATTERN(GetTemplateObject) NOT_A_PATTERN(IfStatement) NOT_A_PATTERN(ImportCallExpression) NOT_A_PATTERN(Literal) NOT_A_PATTERN(NativeFunctionLiteral) +NOT_A_PATTERN(Property) NOT_A_PATTERN(RegExpLiteral) NOT_A_PATTERN(ResolvedProperty) NOT_A_PATTERN(ReturnStatement) NOT_A_PATTERN(SloppyBlockFunctionStatement) -NOT_A_PATTERN(Spread) NOT_A_PATTERN(StoreInArrayLiteral) NOT_A_PATTERN(SuperPropertyReference) NOT_A_PATTERN(SuperCallReference) @@ -761,7 +211,7 @@ NOT_A_PATTERN(WithStatement) NOT_A_PATTERN(Yield) NOT_A_PATTERN(YieldStar) NOT_A_PATTERN(Await) -NOT_A_PATTERN(InitializeClassFieldsStatement) +NOT_A_PATTERN(InitializeClassMembersStatement) #undef NOT_A_PATTERN } // namespace internal diff --git a/deps/v8/src/parsing/preparse-data-impl.h b/deps/v8/src/parsing/preparse-data-impl.h new file mode 100644 index 0000000000..7d1f0feed8 --- /dev/null +++ b/deps/v8/src/parsing/preparse-data-impl.h @@ -0,0 +1,234 @@ +// 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_PREPARSE_DATA_IMPL_H_ +#define V8_PARSING_PREPARSE_DATA_IMPL_H_ + +#include "src/parsing/preparse-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. + +// Wraps a ZoneVector<uint8_t> to have with functions named the same as +// PodArray<uint8_t>. +class ZoneVectorWrapper { + public: + ZoneVectorWrapper() = default; + explicit ZoneVectorWrapper(ZoneVector<uint8_t>* data) : data_(data) {} + + int data_length() const { return static_cast<int>(data_->size()); } + + uint8_t get(int index) const { return data_->at(index); } + + private: + ZoneVector<uint8_t>* data_ = nullptr; +}; + +template <class Data> +class BaseConsumedPreparseData : public ConsumedPreparseData { + public: + class ByteData : public PreparseByteDataConstants { + public: + ByteData() {} + + // 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; +#ifdef DEBUG + consumed_data->has_data_ = true; +#endif + } + explicit ReadingScope(BaseConsumedPreparseData<Data>* parent) + : ReadingScope(parent->scope_data_.get(), parent->GetScopeData()) {} + ~ReadingScope() { +#ifdef DEBUG + consumed_data_->has_data_ = false; +#endif + } + + private: + ByteData* consumed_data_; + DISALLOW_HEAP_ALLOCATION(no_gc); + }; + + void SetPosition(int position) { + DCHECK_LE(position, data_.data_length()); + index_ = position; + } + + size_t RemainingBytes() const { + DCHECK(has_data_); + DCHECK_LE(index_, data_.data_length()); + return data_.data_length() - index_; + } + + bool HasRemainingBytes(size_t bytes) const { + DCHECK(has_data_); + return index_ <= data_.data_length() && bytes <= RemainingBytes(); + } + + int32_t ReadUint32() { + DCHECK(has_data_); + DCHECK(HasRemainingBytes(kUint32Size)); + // Check that there indeed is an integer following. + DCHECK_EQ(data_.get(index_++), kUint32Size); + int32_t result = data_.get(index_) + (data_.get(index_ + 1) << 8) + + (data_.get(index_ + 2) << 16) + + (data_.get(index_ + 3) << 24); + index_ += 4; + stored_quarters_ = 0; + return result; + } + + int32_t ReadVarint32() { + DCHECK(HasRemainingBytes(kVarintMinSize)); + DCHECK_EQ(data_.get(index_++), kVarintMinSize); + int32_t value = 0; + bool has_another_byte; + unsigned shift = 0; + do { + uint8_t byte = data_.get(index_++); + value |= static_cast<int32_t>(byte & 0x7F) << shift; + shift += 7; + has_another_byte = byte & 0x80; + } while (has_another_byte); + DCHECK_EQ(data_.get(index_++), kVarintEndMarker); + stored_quarters_ = 0; + return value; + } + + uint8_t ReadUint8() { + DCHECK(has_data_); + DCHECK(HasRemainingBytes(kUint8Size)); + // Check that there indeed is a byte following. + DCHECK_EQ(data_.get(index_++), kUint8Size); + stored_quarters_ = 0; + return data_.get(index_++); + } + + uint8_t ReadQuarter() { + DCHECK(has_data_); + if (stored_quarters_ == 0) { + DCHECK(HasRemainingBytes(kUint8Size)); + // Check that there indeed are quarters following. + DCHECK_EQ(data_.get(index_++), kQuarterMarker); + 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_ = 0; + uint8_t stored_quarters_ = 0; + uint8_t stored_byte_ = 0; +#ifdef DEBUG + bool has_data_ = false; +#endif + }; + + BaseConsumedPreparseData() : scope_data_(new ByteData()), child_index_(0) {} + + virtual Data GetScopeData() = 0; + + virtual ProducedPreparseData* GetChildData(Zone* zone, int child_index) = 0; + + ProducedPreparseData* 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 + bool VerifyDataStart(); +#endif + + private: + void RestoreDataForScope(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(BaseConsumedPreparseData); +}; + +// Implementation of ConsumedPreparseData for on-heap data. +class OnHeapConsumedPreparseData final + : public BaseConsumedPreparseData<PreparseData> { + public: + OnHeapConsumedPreparseData(Isolate* isolate, Handle<PreparseData> data); + + PreparseData GetScopeData() final; + ProducedPreparseData* GetChildData(Zone* zone, int child_index) final; + + private: + Isolate* isolate_; + Handle<PreparseData> data_; +}; + +// A serialized PreparseData in zone memory (as apposed to being on-heap). +class ZonePreparseData : public ZoneObject { + public: + ZonePreparseData(Zone* zone, Vector<uint8_t>* byte_data, int child_length); + + Handle<PreparseData> Serialize(Isolate* isolate); + + int children_length() const { return static_cast<int>(children_.size()); } + + ZonePreparseData* get_child(int index) { return children_[index]; } + + void set_child(int index, ZonePreparseData* child) { + DCHECK_NOT_NULL(child); + children_[index] = child; + } + + ZoneVector<uint8_t>* byte_data() { return &byte_data_; } + + private: + ZoneVector<uint8_t> byte_data_; + ZoneVector<ZonePreparseData*> children_; + + DISALLOW_COPY_AND_ASSIGN(ZonePreparseData); +}; + +// Implementation of ConsumedPreparseData for PreparseData +// serialized into zone memory. +class ZoneConsumedPreparseData final + : public BaseConsumedPreparseData<ZoneVectorWrapper> { + public: + ZoneConsumedPreparseData(Zone* zone, ZonePreparseData* data); + + ZoneVectorWrapper GetScopeData() final; + ProducedPreparseData* GetChildData(Zone* zone, int child_index) final; + + private: + ZonePreparseData* data_; + ZoneVectorWrapper scope_data_wrapper_; +}; + +} // namespace internal +} // namespace v8 + +#endif // V8_PARSING_PREPARSE_DATA_IMPL_H_ diff --git a/deps/v8/src/parsing/preparse-data.cc b/deps/v8/src/parsing/preparse-data.cc new file mode 100644 index 0000000000..68986e451a --- /dev/null +++ b/deps/v8/src/parsing/preparse-data.cc @@ -0,0 +1,716 @@ +// Copyright 2017 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. + +#include "src/parsing/preparse-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/parser.h" +#include "src/parsing/preparse-data-impl.h" +#include "src/parsing/preparser.h" + +namespace v8 { +namespace internal { + +namespace { + +class ScopeCallsSloppyEvalField : public BitField8<bool, 0, 1> {}; +class InnerScopeCallsEvalField + : public BitField8<bool, ScopeCallsSloppyEvalField::kNext, 1> {}; + +class VariableMaybeAssignedField : public BitField8<bool, 0, 1> {}; +class VariableContextAllocatedField + : public BitField8<bool, VariableMaybeAssignedField::kNext, 1> {}; + +class HasDataField : public BitField<bool, 0, 1> {}; +class NumberOfParametersField + : public BitField<uint16_t, HasDataField::kNext, 16> {}; + +class LanguageField : public BitField8<LanguageMode, 0, 1> {}; +class UsesSuperField : public BitField8<bool, LanguageField::kNext, 1> {}; +STATIC_ASSERT(LanguageModeSize <= LanguageField::kNumValues); + +} // namespace + +/* + + Internal data format for the backing store of PreparseDataBuilder and + PreparseData::scope_data (on the heap): + + (Skippable function data:) + ------------------------------------ + | scope_data_start (debug only) | + ------------------------------------ + | data for inner function n | + | ... | + ------------------------------------ + | data for inner function 1 | + | ... | + ------------------------------------ + (Scope allocation data:) << scope_data_start points here in debug + ------------------------------------ + magic value (debug only) + ------------------------------------ + scope positions (debug only) + ------------------------------------ + | scope type << only in debug | + | eval | + | ---------------------- | + | | data for variables | | + | | ... | | + | ---------------------- | + ------------------------------------ + ------------------------------------ + | data for inner scope m | << but not for function scopes + | ... | + ------------------------------------ + ... + ------------------------------------ + | data for inner scope 1 | + | ... | + ------------------------------------ + + PreparseData::child_data is an array of PreparseData objects, one + for each skippable inner function. + + ConsumedPreparseData wraps a PreparseData and reads data from it. + + */ + +PreparseDataBuilder::PreparseDataBuilder(Zone* zone, + PreparseDataBuilder* parent_builder) + : parent_(parent_builder), + byte_data_(), + children_(zone), + function_scope_(nullptr), + num_inner_functions_(0), + num_inner_with_data_(0), + bailed_out_(false), + has_data_(false) {} + +void PreparseDataBuilder::DataGatheringScope::Start( + DeclarationScope* function_scope) { + Zone* main_zone = preparser_->main_zone(); + builder_ = new (main_zone) + PreparseDataBuilder(main_zone, preparser_->preparse_data_builder()); + preparser_->set_preparse_data_builder(builder_); + function_scope->set_preparse_data_builder(builder_); +} + +PreparseDataBuilder::DataGatheringScope::~DataGatheringScope() { + if (builder_ == nullptr) return; + // Copy over the data from the buffer into the zone-allocated byte_data_ + PreparseDataBuilder* parent = builder_->parent_; + if (parent != nullptr && builder_->HasDataForParent()) { + parent->children_.push_back(builder_); + } + preparser_->set_preparse_data_builder(parent); +} + +#ifdef DEBUG +void PreparseDataBuilder::ByteData::WriteUint32(uint32_t data) { + DCHECK(!is_finalized_); + byte_data_->push_back(kUint32Size); + byte_data_->push_back(data & 0xFF); + byte_data_->push_back((data >> 8) & 0xFF); + byte_data_->push_back((data >> 16) & 0xFF); + byte_data_->push_back((data >> 24) & 0xFF); + free_quarters_in_last_byte_ = 0; +} + +void PreparseDataBuilder::ByteData::SaveCurrentSizeAtFirstUint32() { + CHECK(!is_finalized_); + uint32_t data = static_cast<uint32_t>(byte_data_->size()); + uint8_t* start = &byte_data_->front(); + int i = 0; + // Check that that position already holds an item of the expected size. + CHECK_GE(byte_data_->size(), kUint32Size); + CHECK_EQ(start[i++], kUint32Size); + start[i++] = data & 0xFF; + start[i++] = (data >> 8) & 0xFF; + start[i++] = (data >> 16) & 0xFF; + start[i++] = (data >> 24) & 0xFF; +} + +int PreparseDataBuilder::ByteData::length() const { + CHECK(!is_finalized_); + return static_cast<int>(byte_data_->size()); +} +#endif + +void PreparseDataBuilder::ByteData::WriteVarint32(uint32_t data) { +#ifdef DEBUG + // Save expected item size in debug mode. + byte_data_->push_back(kVarintMinSize); +#endif + // See ValueSerializer::WriteVarint. + do { + uint8_t next_byte = (data & 0x7F); + data >>= 7; + // Add continue bit. + if (data) next_byte |= 0x80; + byte_data_->push_back(next_byte & 0xFF); + } while (data); +#ifdef DEBUG + // Save a varint marker in debug mode. + byte_data_->push_back(kVarintEndMarker); +#endif + free_quarters_in_last_byte_ = 0; +} + +void PreparseDataBuilder::ByteData::WriteUint8(uint8_t data) { + DCHECK(!is_finalized_); +#ifdef DEBUG + // Save expected item size in debug mode. + byte_data_->push_back(kUint8Size); +#endif + byte_data_->push_back(data); + free_quarters_in_last_byte_ = 0; +} + +void PreparseDataBuilder::ByteData::WriteQuarter(uint8_t data) { + DCHECK(!is_finalized_); + DCHECK_LE(data, 3); + if (free_quarters_in_last_byte_ == 0) { +#ifdef DEBUG + // Save a marker in debug mode. + byte_data_->push_back(kQuarterMarker); +#endif + byte_data_->push_back(0); + free_quarters_in_last_byte_ = 3; + } else { + --free_quarters_in_last_byte_; + } + + uint8_t shift_amount = free_quarters_in_last_byte_ * 2; + DCHECK_EQ(byte_data_->back() & (3 << shift_amount), 0); + byte_data_->back() |= (data << shift_amount); +} + +void PreparseDataBuilder::ByteData::Start(std::vector<uint8_t>* buffer) { + DCHECK(!is_finalized_); + byte_data_ = buffer; + DCHECK_EQ(byte_data_->size(), 0); +} + +void PreparseDataBuilder::ByteData::Finalize(Zone* zone) { + int size = static_cast<int>(byte_data_->size()); + uint8_t* raw_zone_data = + static_cast<uint8_t*>(ZoneAllocationPolicy(zone).New(size)); + memcpy(raw_zone_data, &byte_data_->front(), size); + + byte_data_->resize(0); + + zone_byte_data_ = Vector<uint8_t>(raw_zone_data, size); +#ifdef DEBUG + is_finalized_ = true; +#endif +} + +void PreparseDataBuilder::DataGatheringScope::SetSkippableFunction( + DeclarationScope* function_scope, int num_inner_functions) { + DCHECK_NULL(builder_->function_scope_); + builder_->function_scope_ = function_scope; + DCHECK_EQ(builder_->num_inner_functions_, 0); + builder_->num_inner_functions_ = num_inner_functions; + builder_->parent_->has_data_ = true; +} + +bool PreparseDataBuilder::HasInnerFunctions() const { + return !children_.is_empty(); +} + +bool PreparseDataBuilder::HasData() const { return !bailed_out_ && has_data_; } + +bool PreparseDataBuilder::HasDataForParent() const { + return HasData() || function_scope_ != nullptr; +} + +bool PreparseDataBuilder::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. + return !IsDefaultConstructor(scope->AsDeclarationScope()->function_kind()); + } + if (!scope->is_hidden()) { + for (Variable* var : *scope->locals()) { + if (IsDeclaredVariableMode(var->mode())) return true; + } + } + for (Scope* inner = scope->inner_scope(); inner != nullptr; + inner = inner->sibling()) { + if (ScopeNeedsData(inner)) return true; + } + return false; +} + +bool PreparseDataBuilder::SaveDataForSkippableFunction( + PreparseDataBuilder* builder) { + DeclarationScope* function_scope = builder->function_scope_; + // Start position is used for a sanity check when consuming the data, we could + // remove it in the future if we're very pressed for space but it's been good + // at catching bugs in the wild so far. + byte_data_.WriteVarint32(function_scope->start_position()); + byte_data_.WriteVarint32(function_scope->end_position()); + + bool has_data = builder->HasData(); + uint32_t has_data_and_num_parameters = + HasDataField::encode(has_data) | + NumberOfParametersField::encode(function_scope->num_parameters()); + byte_data_.WriteVarint32(has_data_and_num_parameters); + byte_data_.WriteVarint32(builder->num_inner_functions_); + + uint8_t language_and_super = + LanguageField::encode(function_scope->language_mode()) | + UsesSuperField::encode(function_scope->NeedsHomeObject()); + byte_data_.WriteQuarter(language_and_super); + return has_data; +} + +void PreparseDataBuilder::SaveScopeAllocationData(DeclarationScope* scope, + Parser* parser) { + if (!has_data_) return; + DCHECK(HasInnerFunctions()); + + byte_data_.Start(parser->preparse_data_buffer()); + +#ifdef DEBUG + // Reserve Uint32 for scope_data_start debug info. + byte_data_.WriteUint32(0); +#endif + + for (const auto& builder : children_) { + // Keep track of functions with inner data. {children_} contains also the + // builders that have no inner functions at all. + if (SaveDataForSkippableFunction(builder)) num_inner_with_data_++; + } + + // Don't save imcoplete scope information when bailed out. + if (!bailed_out_) { +#ifdef DEBUG + // function data items, kSkippableMinFunctionDataSize each. + CHECK_GE(byte_data_.length(), kPlaceholderSize); + CHECK_LE(byte_data_.length(), std::numeric_limits<uint32_t>::max()); + + byte_data_.SaveCurrentSizeAtFirstUint32(); + // 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(scope->start_position()); + byte_data_.WriteUint32(scope->end_position()); +#endif + + if (ScopeNeedsData(scope)) SaveDataForScope(scope); + } + byte_data_.Finalize(parser->factory()->zone()); +} + +void PreparseDataBuilder::SaveDataForScope(Scope* scope) { + DCHECK_NE(scope->end_position(), kNoSourcePosition); + DCHECK(ScopeNeedsData(scope)); + +#ifdef DEBUG + byte_data_.WriteUint8(scope->scope_type()); +#endif + + uint8_t eval = + ScopeCallsSloppyEvalField::encode( + scope->is_declaration_scope() && + scope->AsDeclarationScope()->calls_sloppy_eval()) | + InnerScopeCallsEvalField::encode(scope->inner_scope_calls_eval()); + byte_data_.WriteUint8(eval); + + if (scope->scope_type() == ScopeType::FUNCTION_SCOPE) { + Variable* function = scope->AsDeclarationScope()->function_var(); + if (function != nullptr) SaveDataForVariable(function); + } + + for (Variable* var : *scope->locals()) { + if (IsDeclaredVariableMode(var->mode())) SaveDataForVariable(var); + } + + SaveDataForInnerScopes(scope); +} + +void PreparseDataBuilder::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. + const AstRawString* name = var->raw_name(); + byte_data_.WriteUint8(name->is_one_byte()); + byte_data_.WriteUint32(name->length()); + for (int i = 0; i < name->length(); ++i) { + byte_data_.WriteUint8(name->raw_data()[i]); + } +#endif + + byte variable_data = VariableMaybeAssignedField::encode( + var->maybe_assigned() == kMaybeAssigned) | + VariableContextAllocatedField::encode( + var->has_forced_context_allocation()); + byte_data_.WriteQuarter(variable_data); +} + +void PreparseDataBuilder::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. + for (Scope* inner = scope->inner_scope(); inner != nullptr; + inner = inner->sibling()) { + if (ScopeIsSkippableFunctionScope(inner)) { + // Don't save data about function scopes, since they'll have their own + // PreparseDataBuilder where their data is saved. + DCHECK_NOT_NULL(inner->AsDeclarationScope()->preparse_data_builder()); + continue; + } + if (!ScopeNeedsData(inner)) continue; + SaveDataForScope(inner); + } +} + +bool PreparseDataBuilder::ScopeIsSkippableFunctionScope(Scope* scope) { + // Lazy non-arrow function scopes are skippable. Lazy functions are exactly + // those Scopes which have their own PreparseDataBuilder 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). + if (scope->scope_type() != ScopeType::FUNCTION_SCOPE) return false; + DeclarationScope* declaration_scope = scope->AsDeclarationScope(); + return !declaration_scope->is_arrow_scope() && + declaration_scope->preparse_data_builder() != nullptr; +} + +Handle<PreparseData> PreparseDataBuilder::ByteData::CopyToHeap( + Isolate* isolate, int children_length) { + DCHECK(is_finalized_); + int data_length = zone_byte_data_.length(); + Handle<PreparseData> data = + isolate->factory()->NewPreparseData(data_length, children_length); + data->copy_in(0, zone_byte_data_.start(), data_length); + return data; +} + +ZonePreparseData* PreparseDataBuilder::ByteData::CopyToZone( + Zone* zone, int children_length) { + DCHECK(is_finalized_); + return new (zone) ZonePreparseData(zone, &zone_byte_data_, children_length); +} + +Handle<PreparseData> PreparseDataBuilder::Serialize(Isolate* isolate) { + DCHECK(HasData()); + DCHECK(!ThisOrParentBailedOut()); + Handle<PreparseData> data = + byte_data_.CopyToHeap(isolate, num_inner_with_data_); + int i = 0; + for (const auto& builder : children_) { + if (!builder->HasData()) continue; + Handle<PreparseData> child_data = builder->Serialize(isolate); + data->set_child(i++, *child_data); + } + DCHECK_EQ(i, data->children_length()); + return data; +} + +ZonePreparseData* PreparseDataBuilder::Serialize(Zone* zone) { + DCHECK(HasData()); + DCHECK(!ThisOrParentBailedOut()); + ZonePreparseData* data = byte_data_.CopyToZone(zone, num_inner_with_data_); + int i = 0; + for (const auto& builder : children_) { + if (!builder->HasData()) continue; + ZonePreparseData* child = builder->Serialize(zone); + data->set_child(i++, child); + } + DCHECK_EQ(i, data->children_length()); + return data; +} + +class BuilderProducedPreparseData final : public ProducedPreparseData { + public: + explicit BuilderProducedPreparseData(PreparseDataBuilder* builder) + : builder_(builder) { + DCHECK(builder->HasData()); + } + + Handle<PreparseData> Serialize(Isolate* isolate) final { + return builder_->Serialize(isolate); + } + + ZonePreparseData* Serialize(Zone* zone) final { + return builder_->Serialize(zone); + }; + + private: + PreparseDataBuilder* builder_; +}; + +class OnHeapProducedPreparseData final : public ProducedPreparseData { + public: + explicit OnHeapProducedPreparseData(Handle<PreparseData> data) + : data_(data) {} + + Handle<PreparseData> Serialize(Isolate* isolate) final { + DCHECK(!data_->is_null()); + return data_; + } + + ZonePreparseData* Serialize(Zone* zone) final { + // Not required. + UNREACHABLE(); + }; + + private: + Handle<PreparseData> data_; +}; + +class ZoneProducedPreparseData final : public ProducedPreparseData { + public: + explicit ZoneProducedPreparseData(ZonePreparseData* data) : data_(data) {} + + Handle<PreparseData> Serialize(Isolate* isolate) final { + return data_->Serialize(isolate); + } + + ZonePreparseData* Serialize(Zone* zone) final { return data_; }; + + private: + ZonePreparseData* data_; +}; + +ProducedPreparseData* ProducedPreparseData::For(PreparseDataBuilder* builder, + Zone* zone) { + return new (zone) BuilderProducedPreparseData(builder); +} + +ProducedPreparseData* ProducedPreparseData::For(Handle<PreparseData> data, + Zone* zone) { + return new (zone) OnHeapProducedPreparseData(data); +} + +ProducedPreparseData* ProducedPreparseData::For(ZonePreparseData* data, + Zone* zone) { + return new (zone) ZoneProducedPreparseData(data); +} + +template <class Data> +ProducedPreparseData* +BaseConsumedPreparseData<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. + typename ByteData::ReadingScope reading_scope(this); + CHECK(scope_data_->HasRemainingBytes( + PreparseByteDataConstants::kSkippableFunctionMinDataSize)); + int start_position_from_data = scope_data_->ReadVarint32(); + CHECK_EQ(start_position, start_position_from_data); + *end_position = scope_data_->ReadVarint32(); + DCHECK_GT(*end_position, start_position); + + uint32_t has_data_and_num_parameters = scope_data_->ReadVarint32(); + bool has_data = HasDataField::decode(has_data_and_num_parameters); + *num_parameters = + NumberOfParametersField::decode(has_data_and_num_parameters); + *num_inner_functions = scope_data_->ReadVarint32(); + + uint8_t language_and_super = scope_data_->ReadQuarter(); + *language_mode = LanguageMode(LanguageField::decode(language_and_super)); + *uses_super_property = UsesSuperField::decode(language_and_super); + + if (!has_data) return nullptr; + + // Retrieve the corresponding PreparseData 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. + return GetChildData(zone, child_index_++); +} + +template <class Data> +void BaseConsumedPreparseData<Data>::RestoreScopeAllocationData( + DeclarationScope* scope) { + DCHECK_EQ(scope->scope_type(), ScopeType::FUNCTION_SCOPE); + 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, ByteData::kMagicValue); + + int start_position_from_data = scope_data_->ReadUint32(); + int end_position_from_data = scope_data_->ReadUint32(); + DCHECK_EQ(start_position_from_data, scope->start_position()); + DCHECK_EQ(end_position_from_data, scope->end_position()); +#endif + + RestoreDataForScope(scope); + + // Check that we consumed all scope data. + DCHECK_EQ(scope_data_->RemainingBytes(), 0); +} + +template <typename Data> +void BaseConsumedPreparseData<Data>::RestoreDataForScope(Scope* scope) { + if (scope->is_declaration_scope() && + scope->AsDeclarationScope()->is_skipped_function()) { + return; + } + + // 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 (!PreparseDataBuilder::ScopeNeedsData(scope)) return; + + // scope_type is stored only in debug mode. + DCHECK_EQ(scope_data_->ReadUint8(), scope->scope_type()); + + CHECK(scope_data_->HasRemainingBytes(ByteData::kUint8Size)); + uint32_t eval = scope_data_->ReadUint8(); + if (ScopeCallsSloppyEvalField::decode(eval)) scope->RecordEvalCall(); + if (InnerScopeCallsEvalField::decode(eval)) scope->RecordInnerScopeEvalCall(); + + if (scope->scope_type() == ScopeType::FUNCTION_SCOPE) { + Variable* function = scope->AsDeclarationScope()->function_var(); + if (function != nullptr) RestoreDataForVariable(function); + } + + for (Variable* var : *scope->locals()) { + if (IsDeclaredVariableMode(var->mode())) RestoreDataForVariable(var); + } + + RestoreDataForInnerScopes(scope); +} + +template <typename Data> +void BaseConsumedPreparseData<Data>::RestoreDataForVariable(Variable* var) { +#ifdef DEBUG + const AstRawString* name = var->raw_name(); + bool data_one_byte = scope_data_->ReadUint8(); + DCHECK_IMPLIES(name->is_one_byte(), data_one_byte); + DCHECK_EQ(scope_data_->ReadUint32(), static_cast<uint32_t>(name->length())); + if (!name->is_one_byte() && data_one_byte) { + // It's possible that "name" is a two-byte representation of the string + // stored in the data. + for (int i = 0; i < 2 * name->length(); i += 2) { +#if defined(V8_TARGET_LITTLE_ENDIAN) + DCHECK_EQ(scope_data_->ReadUint8(), name->raw_data()[i]); + DCHECK_EQ(0, name->raw_data()[i + 1]); +#else + DCHECK_EQ(scope_data_->ReadUint8(), name->raw_data()[i + 1]); + DCHECK_EQ(0, name->raw_data()[i]); +#endif // V8_TARGET_LITTLE_ENDIAN + } + } else { + for (int i = 0; i < name->length(); ++i) { + DCHECK_EQ(scope_data_->ReadUint8(), name->raw_data()[i]); + } + } +#endif + uint8_t variable_data = scope_data_->ReadQuarter(); + if (VariableMaybeAssignedField::decode(variable_data)) { + var->set_maybe_assigned(); + } + if (VariableContextAllocatedField::decode(variable_data)) { + var->set_is_used(); + var->ForceContextAllocation(); + } +} + +template <typename Data> +void BaseConsumedPreparseData<Data>::RestoreDataForInnerScopes(Scope* scope) { + for (Scope* inner = scope->inner_scope(); inner != nullptr; + inner = inner->sibling()) { + RestoreDataForScope(inner); + } +} + +#ifdef DEBUG +template <class Data> +bool BaseConsumedPreparseData<Data>::VerifyDataStart() { + typename ByteData::ReadingScope reading_scope(this); + // The first uint32 contains the size of the skippable function data. + int scope_data_start = scope_data_->ReadUint32(); + scope_data_->SetPosition(scope_data_start); + CHECK_EQ(scope_data_->ReadUint32(), ByteData::kMagicValue); + // The first data item is scope_data_start. Skip over it. + scope_data_->SetPosition(ByteData::kPlaceholderSize); + return true; +} +#endif + +PreparseData OnHeapConsumedPreparseData::GetScopeData() { return *data_; } + +ProducedPreparseData* OnHeapConsumedPreparseData::GetChildData(Zone* zone, + int index) { + DisallowHeapAllocation no_gc; + Handle<PreparseData> child_data_handle(data_->get_child(index), isolate_); + return ProducedPreparseData::For(child_data_handle, zone); +} + +OnHeapConsumedPreparseData::OnHeapConsumedPreparseData( + Isolate* isolate, Handle<PreparseData> data) + : BaseConsumedPreparseData<PreparseData>(), isolate_(isolate), data_(data) { + DCHECK_NOT_NULL(isolate); + DCHECK(data->IsPreparseData()); + DCHECK(VerifyDataStart()); +} + +ZonePreparseData::ZonePreparseData(Zone* zone, Vector<uint8_t>* byte_data, + int children_length) + : byte_data_(byte_data->begin(), byte_data->end(), zone), + children_(children_length, zone) {} + +Handle<PreparseData> ZonePreparseData::Serialize(Isolate* isolate) { + int data_size = static_cast<int>(byte_data()->size()); + int child_data_length = children_length(); + Handle<PreparseData> result = + isolate->factory()->NewPreparseData(data_size, child_data_length); + result->copy_in(0, byte_data()->data(), data_size); + + for (int i = 0; i < child_data_length; i++) { + ZonePreparseData* child = get_child(i); + DCHECK_NOT_NULL(child); + Handle<PreparseData> child_data = child->Serialize(isolate); + result->set_child(i, *child_data); + } + return result; +} + +ZoneConsumedPreparseData::ZoneConsumedPreparseData(Zone* zone, + ZonePreparseData* data) + : data_(data), scope_data_wrapper_(data_->byte_data()) { + DCHECK(VerifyDataStart()); +} + +ZoneVectorWrapper ZoneConsumedPreparseData::GetScopeData() { + return scope_data_wrapper_; +} + +ProducedPreparseData* ZoneConsumedPreparseData::GetChildData(Zone* zone, + int child_index) { + CHECK_GT(data_->children_length(), child_index); + ZonePreparseData* child_data = data_->get_child(child_index); + if (child_data == nullptr) return nullptr; + return ProducedPreparseData::For(child_data, zone); +} + +std::unique_ptr<ConsumedPreparseData> ConsumedPreparseData::For( + Isolate* isolate, Handle<PreparseData> data) { + DCHECK(!data.is_null()); + return base::make_unique<OnHeapConsumedPreparseData>(isolate, data); +} + +std::unique_ptr<ConsumedPreparseData> ConsumedPreparseData::For( + Zone* zone, ZonePreparseData* data) { + if (data == nullptr) return {}; + return base::make_unique<ZoneConsumedPreparseData>(zone, data); +} + +} // namespace internal +} // namespace v8 diff --git a/deps/v8/src/parsing/preparse-data.h b/deps/v8/src/parsing/preparse-data.h new file mode 100644 index 0000000000..0e08297c36 --- /dev/null +++ b/deps/v8/src/parsing/preparse-data.h @@ -0,0 +1,275 @@ +// Copyright 2017 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_PREPARSE_DATA_H_ +#define V8_PARSING_PREPARSE_DATA_H_ + +#include "src/globals.h" +#include "src/handles.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 PodArray; + +class Parser; +class PreParser; +class PreparseData; +class ZonePreparseData; + +/* + + Skipping inner functions. + + Consider the following code: + (function eager_outer() { + function lazy_inner() { + let a; + function skip_me() { a; } + } + + return lazy_inner; + })(); + + ... lazy_inner(); ... + + When parsing the code the first time, eager_outer is parsed and lazy_inner + (and everything inside it) is preparsed. When lazy_inner is called, we don't + want to parse or preparse skip_me again. Instead, we want to skip over it, + since it has already been preparsed once. + + In order to be able to do this, we need to store the information needed for + allocating the variables in lazy_inner when we preparse it, and then later do + scope allocation based on that data. + + We need the following data for each scope in lazy_inner's scope tree: + For each Variable: + - is_used + - maybe_assigned + - has_forced_context_allocation + + For each Scope: + - inner_scope_calls_eval_. + + ProducedPreparseData implements storing the above mentioned data and + ConsumedPreparseData implements restoring it (= setting the context + allocation status of the variables in a Scope (and its subscopes) based on the + data). + + */ + +struct PreparseByteDataConstants { +#ifdef DEBUG + static constexpr int kMagicValue = 0xC0DE0DE; + + static constexpr size_t kUint32Size = 5; + static constexpr size_t kVarintMinSize = 3; + static constexpr size_t kVarintEndMarker = 0xF1; + static constexpr size_t kUint8Size = 2; + static constexpr size_t kQuarterMarker = 0xF2; + static constexpr size_t kPlaceholderSize = kUint32Size; +#else + static constexpr size_t kUint32Size = 4; + static constexpr size_t kVarintMinSize = 1; + static constexpr size_t kUint8Size = 1; + static constexpr size_t kPlaceholderSize = 0; +#endif + + static const size_t kSkippableFunctionMinDataSize = + 4 * kVarintMinSize + 1 * kUint8Size; +}; + +class PreparseDataBuilder : public ZoneObject, + public PreparseByteDataConstants { + public: + // Create a PreparseDataBuilder object which will collect data as we + // parse. + explicit PreparseDataBuilder(Zone* zone, PreparseDataBuilder* parent_builder); + + PreparseDataBuilder* 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 + // ProducedPreparseData, and so do all lazy functions inside it. + class DataGatheringScope { + public: + explicit DataGatheringScope(PreParser* preparser) + : preparser_(preparser), builder_(nullptr) {} + + void Start(DeclarationScope* function_scope); + void SetSkippableFunction(DeclarationScope* function_scope, + int num_inner_functions); + ~DataGatheringScope(); + + private: + PreParser* preparser_; + PreparseDataBuilder* builder_; + + DISALLOW_COPY_AND_ASSIGN(DataGatheringScope); + }; + + class ByteData : public ZoneObject, public PreparseByteDataConstants { + public: + ByteData() : byte_data_(nullptr), free_quarters_in_last_byte_(0) {} + + ~ByteData() {} + + void Start(std::vector<uint8_t>* buffer); + void Finalize(Zone* zone); + + Handle<PreparseData> CopyToHeap(Isolate* isolate, int children_length); + ZonePreparseData* CopyToZone(Zone* zone, int children_length); + + void WriteVarint32(uint32_t data); + void WriteUint8(uint8_t data); + void WriteQuarter(uint8_t data); + +#ifdef DEBUG + void WriteUint32(uint32_t data); + // For overwriting previously written data at position 0. + void SaveCurrentSizeAtFirstUint32(); + int length() const; +#endif + + private: + union { + // Only used during construction (is_finalized_ == false). + std::vector<uint8_t>* byte_data_; + // Once the data is finalized, it lives in a Zone, this implies + // is_finalized_ == true. + Vector<uint8_t> zone_byte_data_; + }; + uint8_t free_quarters_in_last_byte_; + +#ifdef DEBUG + bool is_finalized_ = false; +#endif + }; + + // Saves the information needed for allocating the Scope's (and its + // subscopes') variables. + void SaveScopeAllocationData(DeclarationScope* scope, Parser* parser); + + // In some cases, PreParser cannot produce the same Scope structure as + // Parser. If it happens, we're unable to produce the data that would enable + // skipping the inner functions of that function. + void Bailout() { + bailed_out_ = true; + // We don't need to call Bailout on existing / future children: the only way + // to try to retrieve their data is through calling Serialize on the parent, + // and if the parent is bailed out, it won't call Serialize on its children. + } + + bool bailed_out() const { return bailed_out_; } + +#ifdef DEBUG + bool ThisOrParentBailedOut() const { + if (bailed_out_) return true; + if (parent_ == nullptr) return false; + return parent_->ThisOrParentBailedOut(); + } +#endif // DEBUG + + bool HasInnerFunctions() const; + bool HasData() const; + bool HasDataForParent() const; + + static bool ScopeNeedsData(Scope* scope); + static bool ScopeIsSkippableFunctionScope(Scope* scope); + void AddSkippableFunction(int start_position, int end_position, + int num_parameters, int num_inner_functions, + LanguageMode language_mode, bool has_data, + bool uses_super_property); + + private: + friend class BuilderProducedPreparseData; + + Handle<PreparseData> Serialize(Isolate* isolate); + ZonePreparseData* Serialize(Zone* zone); + + void SaveDataForScope(Scope* scope); + void SaveDataForVariable(Variable* var); + void SaveDataForInnerScopes(Scope* scope); + bool SaveDataForSkippableFunction(PreparseDataBuilder* builder); + + void CopyByteData(Zone* zone); + + PreparseDataBuilder* parent_; + ByteData byte_data_; + ZoneChunkList<PreparseDataBuilder*> children_; + + DeclarationScope* function_scope_; + int num_inner_functions_; + int num_inner_with_data_; + + // Whether we've given up producing the data for this function. + bool bailed_out_ : 1; + bool has_data_ : 1; + + DISALLOW_COPY_AND_ASSIGN(PreparseDataBuilder); +}; + +class ProducedPreparseData : 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 Handle<PreparseData> Serialize(Isolate* isolate) = 0; + + // If there is data (if the Scope contains skippable inner functions), return + // an off-heap ZonePreparseData representing the data; otherwise + // return nullptr. + virtual ZonePreparseData* Serialize(Zone* zone) = 0; + + // Create a ProducedPreparseData which is a proxy for a previous + // produced PreparseData in zone. + static ProducedPreparseData* For(PreparseDataBuilder* builder, Zone* zone); + + // Create a ProducedPreparseData which is a proxy for a previous + // produced PreparseData on the heap. + static ProducedPreparseData* For(Handle<PreparseData> data, Zone* zone); + + // Create a ProducedPreparseData which is a proxy for a previous + // produced PreparseData in zone. + static ProducedPreparseData* For(ZonePreparseData* data, Zone* zone); +}; + +class ConsumedPreparseData { + public: + // Creates a ConsumedPreparseData representing the data of an on-heap + // PreparseData |data|. + static std::unique_ptr<ConsumedPreparseData> For(Isolate* isolate, + Handle<PreparseData> data); + + // Creates a ConsumedPreparseData representing the data of an off-heap + // ZonePreparseData |data|. + static std::unique_ptr<ConsumedPreparseData> For(Zone* zone, + ZonePreparseData* data); + + virtual ~ConsumedPreparseData() = default; + + virtual ProducedPreparseData* GetDataForSkippableFunction( + Zone* zone, int start_position, int* end_position, int* num_parameters, + int* num_inner_functions, bool* uses_super_property, + LanguageMode* language_mode) = 0; + + // Restores the information needed for allocating the Scope's (and its + // subscopes') variables. + virtual void RestoreScopeAllocationData(DeclarationScope* scope) = 0; + + protected: + ConsumedPreparseData() = default; + + private: + DISALLOW_COPY_AND_ASSIGN(ConsumedPreparseData); +}; + +} // namespace internal +} // namespace v8 + +#endif // V8_PARSING_PREPARSE_DATA_H_ diff --git a/deps/v8/src/parsing/preparsed-scope-data-impl.h b/deps/v8/src/parsing/preparsed-scope-data-impl.h deleted file mode 100644 index e2d31c07d5..0000000000 --- a/deps/v8/src/parsing/preparsed-scope-data-impl.h +++ /dev/null @@ -1,259 +0,0 @@ -// 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 deleted file mode 100644 index 9d61740753..0000000000 --- a/deps/v8/src/parsing/preparsed-scope-data.cc +++ /dev/null @@ -1,737 +0,0 @@ -// Copyright 2017 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. - -#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 { -namespace internal { - -namespace { - -class ScopeCallsSloppyEvalField : public BitField<bool, 0, 1> {}; -class InnerScopeCallsEvalField - : public BitField<bool, ScopeCallsSloppyEvalField::kNext, 1> {}; - -class VariableMaybeAssignedField : public BitField8<bool, 0, 1> {}; -class VariableContextAllocatedField - : public BitField8<bool, VariableMaybeAssignedField::kNext, 1> {}; - -class LanguageField : public BitField8<LanguageMode, 0, 1> {}; -class UsesSuperField : public BitField8<bool, LanguageField::kNext, 1> {}; -STATIC_ASSERT(LanguageModeSize <= LanguageField::kNumValues); - -} // namespace - -/* - - Internal data format for the backing store of PreParsedScopeDataBuilder and - PreParsedScopeData::scope_data (on the heap): - - (Skippable function data:) - ------------------------------------ - | scope_data_start (debug only) | - ------------------------------------ - | data for inner function 1 | - | ... | - ------------------------------------ - | data for inner function n | - | ... | - ------------------------------------ - (Scope allocation data:) << scope_data_start points here in debug - ------------------------------------ - magic value (debug only) - ------------------------------------ - scope positions (debug only) - ------------------------------------ - | scope type << only in debug | - | eval | - | ---------------------- | - | | data for variables | | - | | ... | | - | ---------------------- | - ------------------------------------ - ------------------------------------ - | data for inner scope 1 | << but not for function scopes - | ... | - ------------------------------------ - ... - ------------------------------------ - | data for inner scope m | - | ... | - ------------------------------------ - - PreParsedScopeData::child_data is an array of PreParsedScopeData objects, one - for each skippable inner function. - - ConsumedPreParsedScopeData wraps a PreParsedScopeData and reads data from it. - - */ - -void PreParsedScopeDataBuilder::ByteData::WriteUint32(uint32_t data) { -#ifdef DEBUG - // Save expected item size in debug mode. - backing_store_.push_back(kUint32Size); -#endif - const uint8_t* d = reinterpret_cast<uint8_t*>(&data); - for (int i = 0; i < 4; ++i) { - backing_store_.push_back(*d++); - } - free_quarters_in_last_byte_ = 0; -} - -#ifdef DEBUG -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); - DCHECK_EQ(*it, kUint32Size); - ++it; - const uint8_t* d = reinterpret_cast<uint8_t*>(&data); - for (size_t i = 0; i < 4; ++i) { - *it++ = *d++; - } -} -#endif - -void PreParsedScopeDataBuilder::ByteData::WriteUint8(uint8_t data) { -#ifdef DEBUG - // Save expected item size in debug mode. - backing_store_.push_back(kUint8Size); -#endif - backing_store_.push_back(data); - free_quarters_in_last_byte_ = 0; -} - -void PreParsedScopeDataBuilder::ByteData::WriteQuarter(uint8_t data) { - DCHECK_LE(data, 3); - if (free_quarters_in_last_byte_ == 0) { -#ifdef DEBUG - // Save a marker in debug mode. - backing_store_.push_back(kQuarterMarker); -#endif - backing_store_.push_back(0); - free_quarters_in_last_byte_ = 3; - } else { - --free_quarters_in_last_byte_; - } - - uint8_t shift_amount = free_quarters_in_last_byte_ * 2; - DCHECK_EQ(backing_store_.back() & (3 << shift_amount), 0); - backing_store_.back() |= (data << shift_amount); -} - -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); - - DisallowHeapAllocation no_gc; - PodArray<uint8_t>* raw_array = *array; - - int i = 0; - for (uint8_t item : backing_store_) { - raw_array->set(i++, item); - } - return array; -} - -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); - } -#ifdef DEBUG - // Reserve space for scope_data_start, written later: - byte_data_->WriteUint32(0); -#endif -} - -PreParsedScopeDataBuilder::DataGatheringScope::DataGatheringScope( - DeclarationScope* function_scope, PreParser* preparser) - : function_scope_(function_scope), - preparser_(preparser), - builder_(nullptr) { - if (FLAG_preparser_scope_analysis) { - PreParsedScopeDataBuilder* parent = - preparser->preparsed_scope_data_builder(); - Zone* main_zone = preparser->main_zone(); - builder_ = new (main_zone) PreParsedScopeDataBuilder(main_zone, parent); - preparser->set_preparsed_scope_data_builder(builder_); - function_scope->set_preparsed_scope_data_builder(builder_); - } -} - -PreParsedScopeDataBuilder::DataGatheringScope::~DataGatheringScope() { - if (builder_) { - preparser_->set_preparsed_scope_data_builder(builder_->parent_); - } -} - -void PreParsedScopeDataBuilder::DataGatheringScope::MarkFunctionAsSkippable( - int end_position, int num_inner_functions) { - 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 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; - } - - // Start position is used for a sanity check when consuming the data, we could - // remove it in the future if we're very pressed for space but it's been good - // at catching bugs in the wild so far. - byte_data_->WriteUint32(start_position); - byte_data_->WriteUint32(end_position); - byte_data_->WriteUint32(num_parameters); - byte_data_->WriteUint32(num_inner_functions); - - uint8_t language_and_super = LanguageField::encode(language_mode) | - UsesSuperField::encode(uses_super_property); - - byte_data_->WriteQuarter(language_and_super); -} - -void PreParsedScopeDataBuilder::SaveScopeAllocationData( - DeclarationScope* scope) { - // The data contains a uint32 (reserved space for scope_data_start) and - // function data items, kSkippableFunctionDataSize each. - DCHECK_GE(byte_data_->size(), ByteData::kPlaceholderSize); - DCHECK_LE(byte_data_->size(), std::numeric_limits<uint32_t>::max()); - DCHECK_EQ(byte_data_->size() % ByteData::kSkippableFunctionDataSize, - ByteData::kPlaceholderSize); - - if (bailed_out_) { - return; - } - - 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 == ByteData::kPlaceholderSize) { - return; - } - -#ifdef DEBUG - byte_data_->OverwriteFirstUint32(scope_data_start); - - // For a data integrity check, write a value between data about skipped inner - // funcs and data about variables. - byte_data_->WriteUint32(ByteData::kMagicValue); - byte_data_->WriteUint32(scope->start_position()); - byte_data_->WriteUint32(scope->end_position()); -#endif - - SaveDataForScope(scope); -} - -bool PreParsedScopeDataBuilder::ContainsInnerFunctions() const { - return byte_data_->size() > ByteData::kPlaceholderSize; -} - -MaybeHandle<PreParsedScopeData> PreParsedScopeDataBuilder::Serialize( - Isolate* isolate) { - if (bailed_out_) { - return MaybeHandle<PreParsedScopeData>(); - } - - DCHECK(!ThisOrParentBailedOut()); - - if (byte_data_->size() <= ByteData::kPlaceholderSize) { - // The data contains only the placeholder. - return MaybeHandle<PreParsedScopeData>(); - } - - int child_data_length = static_cast<int>(data_for_inner_functions_.size()); - Handle<PreParsedScopeData> data = - isolate->factory()->NewPreParsedScopeData(child_data_length); - - Handle<PodArray<uint8_t>> scope_data_array = byte_data_->Serialize(isolate); - data->set_scope_data(*scope_data_array); - - int i = 0; - for (const auto& item : data_for_inner_functions_) { - Handle<PreParsedScopeData> child_data; - if (item->Serialize(isolate).ToHandle(&child_data)) { - data->set_child_data(i, *child_data); - } else { - DCHECK(data->child_data(i)->IsNull()); - } - i++; - } - - return data; -} - -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. - return !IsDefaultConstructor(scope->AsDeclarationScope()->function_kind()); - } - if (!scope->is_hidden()) { - for (Variable* var : *scope->locals()) { - if (IsDeclaredVariableMode(var->mode())) { - return true; - } - } - } - for (Scope* inner = scope->inner_scope(); inner != nullptr; - inner = inner->sibling()) { - if (ScopeNeedsData(inner)) { - return true; - } - } - return false; -} - -bool PreParsedScopeDataBuilder::ScopeIsSkippableFunctionScope(Scope* scope) { - // Lazy non-arrow function scopes are skippable. Lazy functions are exactly - // 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). - if (scope->scope_type() != ScopeType::FUNCTION_SCOPE) { - return false; - } - DeclarationScope* declaration_scope = scope->AsDeclarationScope(); - return !declaration_scope->is_arrow_scope() && - declaration_scope->preparsed_scope_data_builder() != nullptr; -} - -void PreParsedScopeDataBuilder::SaveDataForScope(Scope* scope) { - DCHECK_NE(scope->end_position(), kNoSourcePosition); - - if (!ScopeNeedsData(scope)) { - return; - } - -#ifdef DEBUG - byte_data_->WriteUint8(scope->scope_type()); -#endif - - uint8_t eval = - ScopeCallsSloppyEvalField::encode( - scope->is_declaration_scope() && - scope->AsDeclarationScope()->calls_sloppy_eval()) | - InnerScopeCallsEvalField::encode(scope->inner_scope_calls_eval()); - byte_data_->WriteUint8(eval); - - if (scope->scope_type() == ScopeType::FUNCTION_SCOPE) { - Variable* function = scope->AsDeclarationScope()->function_var(); - if (function != nullptr) { - SaveDataForVariable(function); - } - } - - for (Variable* var : *scope->locals()) { - if (IsDeclaredVariableMode(var->mode())) { - SaveDataForVariable(var); - } - } - - SaveDataForInnerScopes(scope); -} - -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. - const AstRawString* name = var->raw_name(); - byte_data_->WriteUint8(name->is_one_byte()); - byte_data_->WriteUint32(name->length()); - for (int i = 0; i < name->length(); ++i) { - byte_data_->WriteUint8(name->raw_data()[i]); - } -#endif - byte variable_data = VariableMaybeAssignedField::encode( - var->maybe_assigned() == kMaybeAssigned) | - VariableContextAllocatedField::encode( - var->has_forced_context_allocation()); - byte_data_->WriteQuarter(variable_data); -} - -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. - std::vector<Scope*> scopes; - for (Scope* inner = scope->inner_scope(); inner != nullptr; - inner = inner->sibling()) { - if (ScopeIsSkippableFunctionScope(inner)) { - // Don't save data about function scopes, since they'll have their own - // PreParsedScopeDataBuilder where their data is saved. - DCHECK_NOT_NULL( - inner->AsDeclarationScope()->preparsed_scope_data_builder()); - continue; - } - scopes.push_back(inner); - } - for (auto it = scopes.rbegin(); it != scopes.rend(); ++it) { - SaveDataForScope(*it); - } -} - -class BuilderProducedPreParsedScopeData final - : public ProducedPreParsedScopeData { - public: - explicit BuilderProducedPreParsedScopeData(PreParsedScopeDataBuilder* builder) - : builder_(builder) {} - - MaybeHandle<PreParsedScopeData> Serialize(Isolate* isolate) final { - return builder_->Serialize(isolate); - } - - ZonePreParsedScopeData* Serialize(Zone* zone) final { - return builder_->Serialize(zone); - }; - - private: - PreParsedScopeDataBuilder* builder_; -}; - -class OnHeapProducedPreParsedScopeData final - : public ProducedPreParsedScopeData { - public: - explicit OnHeapProducedPreParsedScopeData(Handle<PreParsedScopeData> data) - : data_(data) {} - - MaybeHandle<PreParsedScopeData> Serialize(Isolate* isolate) final { - return data_; - } - - ZonePreParsedScopeData* Serialize(Zone* zone) final { - // Not required. - UNREACHABLE(); - }; - - 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* -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. - 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); - - *end_position = scope_data_->ReadUint32(); - DCHECK_GT(*end_position, start_position); - *num_parameters = scope_data_->ReadUint32(); - *num_inner_functions = scope_data_->ReadUint32(); - - uint8_t language_and_super = scope_data_->ReadQuarter(); - *language_mode = LanguageMode(LanguageField::decode(language_and_super)); - *uses_super_property = UsesSuperField::decode(language_and_super); - - // 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. - return GetChildData(zone, child_index_++); -} - -template <class Data> -void BaseConsumedPreParsedScopeData<Data>::RestoreScopeAllocationData( - DeclarationScope* scope) { - DCHECK_EQ(scope->scope_type(), ScopeType::FUNCTION_SCOPE); - 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, ByteData::kMagicValue); - - int start_position_from_data = scope_data_->ReadUint32(); - int end_position_from_data = scope_data_->ReadUint32(); - DCHECK_EQ(start_position_from_data, scope->start_position()); - DCHECK_EQ(end_position_from_data, scope->end_position()); -#endif - - RestoreData(scope); - - // Check that we consumed all scope data. - DCHECK_EQ(scope_data_->RemainingBytes(), 0); -} - -template <typename Data> -void BaseConsumedPreParsedScopeData<Data>::RestoreData(Scope* scope) { - if (scope->is_declaration_scope() && - scope->AsDeclarationScope()->is_skipped_function()) { - return; - } - - // 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 (!PreParsedScopeDataBuilder::ScopeNeedsData(scope)) { - return; - } - - // scope_type is stored only in debug mode. - CHECK_GE(scope_data_->RemainingBytes(), ByteData::kUint8Size); - DCHECK_EQ(scope_data_->ReadUint8(), scope->scope_type()); - - uint32_t eval = scope_data_->ReadUint8(); - if (ScopeCallsSloppyEvalField::decode(eval)) { - scope->RecordEvalCall(); - } - if (InnerScopeCallsEvalField::decode(eval)) { - scope->RecordInnerScopeEvalCall(); - } - - if (scope->scope_type() == ScopeType::FUNCTION_SCOPE) { - Variable* function = scope->AsDeclarationScope()->function_var(); - if (function != nullptr) { - RestoreDataForVariable(function); - } - } - - for (Variable* var : *scope->locals()) { - if (IsDeclaredVariableMode(var->mode())) { - RestoreDataForVariable(var); - } - } - - RestoreDataForInnerScopes(scope); -} - -template <typename Data> -void BaseConsumedPreParsedScopeData<Data>::RestoreDataForVariable( - Variable* var) { -#ifdef DEBUG - const AstRawString* name = var->raw_name(); - bool data_one_byte = scope_data_->ReadUint8(); - DCHECK_IMPLIES(name->is_one_byte(), data_one_byte); - DCHECK_EQ(scope_data_->ReadUint32(), static_cast<uint32_t>(name->length())); - if (!name->is_one_byte() && data_one_byte) { - // It's possible that "name" is a two-byte representation of the string - // stored in the data. - for (int i = 0; i < 2 * name->length(); i += 2) { -#if defined(V8_TARGET_LITTLE_ENDIAN) - DCHECK_EQ(scope_data_->ReadUint8(), name->raw_data()[i]); - DCHECK_EQ(0, name->raw_data()[i + 1]); -#else - DCHECK_EQ(scope_data_->ReadUint8(), name->raw_data()[i + 1]); - DCHECK_EQ(0, name->raw_data()[i]); -#endif // V8_TARGET_LITTLE_ENDIAN - } - } else { - for (int i = 0; i < name->length(); ++i) { - DCHECK_EQ(scope_data_->ReadUint8(), name->raw_data()[i]); - } - } -#endif - uint8_t variable_data = scope_data_->ReadQuarter(); - if (VariableMaybeAssignedField::decode(variable_data)) { - var->set_maybe_assigned(); - } - if (VariableContextAllocatedField::decode(variable_data)) { - var->set_is_used(); - var->ForceContextAllocation(); - } -} - -template <typename Data> -void BaseConsumedPreParsedScopeData<Data>::RestoreDataForInnerScopes( - Scope* scope) { - std::vector<Scope*> scopes; - for (Scope* inner = scope->inner_scope(); inner != nullptr; - inner = inner->sibling()) { - scopes.push_back(inner); - } - for (auto it = scopes.rbegin(); it != scopes.rend(); ++it) { - RestoreData(*it); - } -} - -#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 deleted file mode 100644 index 25298c4331..0000000000 --- a/deps/v8/src/parsing/preparsed-scope-data.h +++ /dev/null @@ -1,214 +0,0 @@ -// Copyright 2017 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_H_ -#define V8_PARSING_PREPARSED_SCOPE_DATA_H_ - -#include "src/globals.h" -#include "src/handles.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 PodArray; - -class PreParser; -class PreParsedScopeData; -class ZonePreParsedScopeData; - -/* - - Skipping inner functions. - - Consider the following code: - (function eager_outer() { - function lazy_inner() { - let a; - function skip_me() { a; } - } - - return lazy_inner; - })(); - - ... lazy_inner(); ... - - When parsing the code the first time, eager_outer is parsed and lazy_inner - (and everything inside it) is preparsed. When lazy_inner is called, we don't - want to parse or preparse skip_me again. Instead, we want to skip over it, - since it has already been preparsed once. - - In order to be able to do this, we need to store the information needed for - allocating the variables in lazy_inner when we preparse it, and then later do - scope allocation based on that data. - - We need the following data for each scope in lazy_inner's scope tree: - For each Variable: - - is_used - - maybe_assigned - - has_forced_context_allocation - - For each Scope: - - inner_scope_calls_eval_. - - ProducedPreParsedScopeData implements storing the above mentioned data and - ConsumedPreParsedScopeData implements restoring it (= setting the context - allocation status of the variables in a Scope (and its subscopes) based on the - data). - - */ - -class PreParsedScopeDataBuilder : public ZoneObject { - public: - class ByteData; - - // Create a PreParsedScopeDataBuilder object which will collect data as we - // parse. - PreParsedScopeDataBuilder(Zone* zone, PreParsedScopeDataBuilder* 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 - // ProducedPreParsedScopeData, and so do all lazy functions inside it. - class DataGatheringScope { - public: - DataGatheringScope(DeclarationScope* function_scope, PreParser* preparser); - ~DataGatheringScope(); - - void MarkFunctionAsSkippable(int end_position, int num_inner_functions); - - private: - DeclarationScope* function_scope_; - PreParser* preparser_; - PreParsedScopeDataBuilder* builder_; - - DISALLOW_COPY_AND_ASSIGN(DataGatheringScope); - }; - - // Saves the information needed for allocating the Scope's (and its - // subscopes') variables. - void SaveScopeAllocationData(DeclarationScope* scope); - - // In some cases, PreParser cannot produce the same Scope structure as - // Parser. If it happens, we're unable to produce the data that would enable - // skipping the inner functions of that function. - void Bailout() { - bailed_out_ = true; - - // We don't need to call Bailout on existing / future children: the only way - // to try to retrieve their data is through calling Serialize on the parent, - // and if the parent is bailed out, it won't call Serialize on its children. - } - - bool bailed_out() const { return bailed_out_; } - -#ifdef DEBUG - bool ThisOrParentBailedOut() const { - if (bailed_out_) { - return true; - } - if (parent_ == nullptr) { - return false; - } - return parent_->ThisOrParentBailedOut(); - } -#endif // DEBUG - - bool ContainsInnerFunctions() const; - - 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, - bool uses_super_property); - - void SaveDataForScope(Scope* scope); - void SaveDataForVariable(Variable* var); - void SaveDataForInnerScopes(Scope* scope); - - PreParsedScopeDataBuilder* parent_; - - ByteData* byte_data_; - ZoneChunkList<PreParsedScopeDataBuilder*> data_for_inner_functions_; - - // Whether we've given up producing the data for this function. - bool bailed_out_; - - DISALLOW_COPY_AND_ASSIGN(PreParsedScopeDataBuilder); -}; - -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: - // Creates a ConsumedPreParsedScopeData representing the data of an on-heap - // PreParsedScopeData |data|. - static std::unique_ptr<ConsumedPreParsedScopeData> For( - 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); - - virtual ~ConsumedPreParsedScopeData() = default; - - 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) = 0; - - // Restores the information needed for allocating the Scope's (and its - // subscopes') variables. - virtual void RestoreScopeAllocationData(DeclarationScope* scope) = 0; - - protected: - ConsumedPreParsedScopeData() = default; - - private: - DISALLOW_COPY_AND_ASSIGN(ConsumedPreParsedScopeData); -}; - -} // namespace internal -} // namespace v8 - -#endif // V8_PARSING_PREPARSED_SCOPE_DATA_H_ diff --git a/deps/v8/src/parsing/preparser.cc b/deps/v8/src/parsing/preparser.cc index 0e74014542..ee496aad10 100644 --- a/deps/v8/src/parsing/preparser.cc +++ b/deps/v8/src/parsing/preparser.cc @@ -9,9 +9,8 @@ #include "src/conversions-inl.h" #include "src/conversions.h" #include "src/globals.h" -#include "src/parsing/duplicate-finder.h" #include "src/parsing/parser-base.h" -#include "src/parsing/preparsed-scope-data.h" +#include "src/parsing/preparse-data.h" #include "src/parsing/preparser.h" #include "src/unicode.h" #include "src/utils.h" @@ -19,26 +18,11 @@ namespace v8 { namespace internal { -// ---------------------------------------------------------------------------- -// The CHECK_OK macro is a convenient macro to enforce error -// handling for functions that may fail (by returning !*ok). -// -// CAUTION: This macro appends extra statements after a call, -// thus it must never be used where only a single statement -// is correct (e.g. an if statement branch w/o braces)! - -#define CHECK_OK_VALUE(x) ok); \ - if (!*ok) return x; \ - ((void)0 -#define DUMMY ) // to make indentation work -#undef DUMMY - -#define CHECK_OK CHECK_OK_VALUE(Expression::Default()) -#define CHECK_OK_VOID CHECK_OK_VALUE(this->Void()) - namespace { -PreParserIdentifier GetSymbolHelper(Scanner* scanner) { +PreParserIdentifier GetSymbolHelper(Scanner* scanner, + const AstRawString* string, + AstValueFactory* avf) { // These symbols require slightly different treatement: // - regular keywords (async, await, etc.; treated in 1st switch.) // - 'contextual' keywords (and may contain escaped; treated in 2nd switch.) @@ -53,24 +37,20 @@ PreParserIdentifier GetSymbolHelper(Scanner* scanner) { default: break; } - switch (scanner->current_contextual_token()) { - case Token::CONSTRUCTOR: - return PreParserIdentifier::Constructor(); - case Token::NAME: - return PreParserIdentifier::Name(); - default: - break; + if (string == avf->constructor_string()) { + return PreParserIdentifier::Constructor(); + } + if (string == avf->name_string()) { + return PreParserIdentifier::Name(); } if (scanner->literal_contains_escapes()) { return PreParserIdentifier::Default(); } - switch (scanner->current_contextual_token()) { - case Token::EVAL: - return PreParserIdentifier::Eval(); - case Token::ARGUMENTS: - return PreParserIdentifier::Arguments(); - default: - break; + if (string == avf->eval_string()) { + return PreParserIdentifier::Eval(); + } + if (string == avf->arguments_string()) { + return PreParserIdentifier::Arguments(); } return PreParserIdentifier::Default(); } @@ -78,12 +58,11 @@ PreParserIdentifier GetSymbolHelper(Scanner* scanner) { } // unnamed namespace PreParserIdentifier PreParser::GetSymbol() const { - PreParserIdentifier symbol = GetSymbolHelper(scanner()); - if (track_unresolved_variables_) { - const AstRawString* result = scanner()->CurrentSymbol(ast_value_factory()); - DCHECK_NOT_NULL(result); - symbol.string_ = result; - } + const AstRawString* result = scanner()->CurrentSymbol(ast_value_factory()); + PreParserIdentifier symbol = + GetSymbolHelper(scanner(), result, ast_value_factory()); + DCHECK_NOT_NULL(result); + symbol.string_ = result; return symbol; } @@ -94,6 +73,12 @@ PreParser::PreParseResult PreParser::PreParseProgram() { scope->set_is_being_lazily_parsed(true); #endif + if (FLAG_harmony_hashbang) { + // Note: We should only skip the hashbang in non-Eval scripts + // (currently, Eval is not handled by the PreParser). + scanner()->SkipHashBang(); + } + // ModuleDeclarationInstantiation for Source Text Module Records creates a // new Module Environment Record whose outer lexical environment record is // the global scope. @@ -101,26 +86,30 @@ PreParser::PreParseResult PreParser::PreParseProgram() { FunctionState top_scope(&function_state_, &scope_, scope); original_scope_ = scope_; - bool ok = true; - int start_position = scanner()->peek_location().beg_pos; - PreParserStatementList body; - ParseStatementList(body, Token::EOS, &ok); + int start_position = peek_position(); + PreParserScopedStatementList body(pointer_buffer()); + ParseStatementList(&body, Token::EOS); original_scope_ = nullptr; if (stack_overflow()) return kPreParseStackOverflow; - if (!ok) { - ReportUnexpectedToken(scanner()->current_token()); - } else if (is_strict(language_mode())) { - CheckStrictOctalLiteral(start_position, scanner()->location().end_pos, &ok); + if (is_strict(language_mode())) { + CheckStrictOctalLiteral(start_position, scanner()->location().end_pos); } return kPreParseSuccess; } +void PreParserFormalParameters::ValidateDuplicate(PreParser* preparser) const { + if (has_duplicate_) preparser->ReportUnidentifiableError(); +} + +void PreParserFormalParameters::ValidateStrictMode(PreParser* preparser) const { + if (strict_parameter_error_) preparser->ReportUnidentifiableError(); +} + PreParser::PreParseResult PreParser::PreParseFunction( const AstRawString* function_name, FunctionKind kind, FunctionLiteral::FunctionType function_type, - DeclarationScope* function_scope, bool is_inner_function, bool may_abort, - int* use_counts, ProducedPreParsedScopeData** produced_preparsed_scope_data, - int script_id) { + DeclarationScope* function_scope, int* use_counts, + ProducedPreparseData** produced_preparse_data, int script_id) { DCHECK_EQ(FUNCTION_SCOPE, function_scope->scope_type()); use_counts_ = use_counts; set_script_id(script_id); @@ -128,19 +117,7 @@ PreParser::PreParseResult PreParser::PreParseFunction( 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<PreParsedScopeDataBuilder::DataGatheringScope> - preparsed_scope_data_builder_scope; - if (FLAG_preparser_scope_analysis && !IsArrowFunction(kind)) { - DCHECK(track_unresolved_variables_); - preparsed_scope_data_builder_scope.reset( - new PreParsedScopeDataBuilder::DataGatheringScope(function_scope, - this)); - } + PreParserFormalParameters formals(function_scope); // In the preparser, we use the function literal ids to count how many // FunctionLiterals were encountered. The PreParser doesn't actually persist @@ -153,122 +130,123 @@ PreParser::PreParseResult PreParser::PreParseFunction( DCHECK_NULL(function_state_); DCHECK_NULL(scope_); FunctionState function_state(&function_state_, &scope_, function_scope); - // This indirection is needed so that we can use the CHECK_OK macros. - bool ok_holder = true; - bool* ok = &ok_holder; - PreParserFormalParameters formals(function_scope); - DuplicateFinder duplicate_finder; - std::unique_ptr<ExpressionClassifier> formals_classifier; + // Start collecting data for a new function which might contain skippable + // functions. + PreparseDataBuilder::DataGatheringScope preparse_data_builder_scope(this); + + if (IsArrowFunction(kind)) { + formals.is_simple = function_scope->has_simple_parameters(); + } else { + preparse_data_builder_scope.Start(function_scope); - // Parse non-arrow function parameters. For arrow functions, the parameters - // have already been parsed. - if (!IsArrowFunction(kind)) { - formals_classifier.reset(new ExpressionClassifier(this, &duplicate_finder)); + // Parse non-arrow function parameters. For arrow functions, the parameters + // have already been parsed. + ParameterDeclarationParsingScope formals_scope(this); // We return kPreParseSuccess in failure cases too - errors are retrieved // separately by Parser::SkipLazyFunctionBody. - ParseFormalParameterList( - &formals, - CHECK_OK_VALUE(pending_error_handler()->ErrorUnidentifiableByPreParser() - ? kPreParseNotIdentifiableError - : kPreParseSuccess)); - Expect(Token::RPAREN, CHECK_OK_VALUE(kPreParseSuccess)); + ParseFormalParameterList(&formals); + if (formals_scope.has_duplicate()) formals.set_has_duplicate(); + if (!formals.is_simple) { + BuildParameterInitializationBlock(formals); + } + + Expect(Token::RPAREN); int formals_end_position = scanner()->location().end_pos; - CheckArityRestrictions( - formals.arity, kind, formals.has_rest, function_scope->start_position(), - formals_end_position, CHECK_OK_VALUE(kPreParseSuccess)); + CheckArityRestrictions(formals.arity, kind, formals.has_rest, + function_scope->start_position(), + formals_end_position); } - Expect(Token::LBRACE, CHECK_OK_VALUE(kPreParseSuccess)); + Expect(Token::LBRACE); DeclarationScope* inner_scope = function_scope; - LazyParsingResult result; if (!formals.is_simple) { inner_scope = NewVarblockScope(); - inner_scope->set_start_position(scanner()->location().beg_pos); + inner_scope->set_start_position(position()); } { BlockState block_state(&scope_, inner_scope); - result = ParseStatementListAndLogFunction(&formals, may_abort, ok); + ParseStatementListAndLogFunction(&formals); } - if (!formals.is_simple) { - BuildParameterInitializationBlock(formals, ok); + bool allow_duplicate_parameters = false; + + if (formals.is_simple) { + if (is_sloppy(function_scope->language_mode())) { + function_scope->HoistSloppyBlockFunctions(nullptr); + } + allow_duplicate_parameters = + is_sloppy(function_scope->language_mode()) && !IsConciseMethod(kind); + } else { if (is_sloppy(inner_scope->language_mode())) { inner_scope->HoistSloppyBlockFunctions(nullptr); } SetLanguageMode(function_scope, inner_scope->language_mode()); inner_scope->set_end_position(scanner()->peek_location().end_pos); - inner_scope->FinalizeBlockScope(); - } else { - if (is_sloppy(function_scope->language_mode())) { - function_scope->HoistSloppyBlockFunctions(nullptr); + if (inner_scope->FinalizeBlockScope() != nullptr) { + const AstRawString* conflict = inner_scope->FindVariableDeclaredIn( + function_scope, VariableMode::kLastLexicalVariableMode); + if (conflict != nullptr) ReportVarRedeclarationIn(conflict, inner_scope); } } use_counts_ = nullptr; - if (result == kLazyParsingAborted) { - DCHECK(!pending_error_handler()->ErrorUnidentifiableByPreParser()); - return kPreParseAbort; - } else if (stack_overflow()) { - DCHECK(!pending_error_handler()->ErrorUnidentifiableByPreParser()); + if (stack_overflow()) { return kPreParseStackOverflow; - } else if (pending_error_handler()->ErrorUnidentifiableByPreParser()) { - DCHECK(!*ok); + } else if (pending_error_handler()->has_error_unidentifiable_by_preparser()) { return kPreParseNotIdentifiableError; - } else if (!*ok) { + } else if (has_error()) { 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 // function, since the function can declare itself strict. - const bool allow_duplicate_parameters = - is_sloppy(function_scope->language_mode()) && formals.is_simple && - !IsConciseMethod(kind); - ValidateFormalParameters(function_scope->language_mode(), - allow_duplicate_parameters, ok); - if (!*ok) { - if (pending_error_handler()->ErrorUnidentifiableByPreParser()) { + ValidateFormalParameters(language_mode(), formals, + allow_duplicate_parameters); + if (has_error()) { + if (pending_error_handler()->has_error_unidentifiable_by_preparser()) { 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()); + // 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); + DeclareFunctionNameVar(function_name, function_type, function_scope); + + if (preparse_data_builder_->HasData()) { + *produced_preparse_data = + ProducedPreparseData::For(preparse_data_builder_, main_zone()); } + } - *produced_preparsed_scope_data = ProducedPreParsedScopeData::For( - preparsed_scope_data_builder_, main_zone()); + if (pending_error_handler()->has_error_unidentifiable_by_preparser()) { + return kPreParseNotIdentifiableError; } - 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); + CheckStrictOctalLiteral(function_scope->start_position(), end_pos); } } - DCHECK(!pending_error_handler()->ErrorUnidentifiableByPreParser()); + DCHECK(!pending_error_handler()->has_error_unidentifiable_by_preparser()); return kPreParseSuccess; } - // Preparsing checks a JavaScript program and emits preparse-data that helps // a later parsing to be faster. // See preparser-data.h for the data. @@ -287,94 +265,87 @@ PreParser::Expression PreParser::ParseFunctionLiteral( FunctionNameValidity function_name_validity, FunctionKind kind, int function_token_pos, FunctionLiteral::FunctionType function_type, LanguageMode language_mode, - ZonePtrList<const AstRawString>* arguments_for_wrapped_function, bool* ok) { + ZonePtrList<const AstRawString>* arguments_for_wrapped_function) { // Wrapped functions are not parsed in the preparser. DCHECK_NULL(arguments_for_wrapped_function); DCHECK_NE(FunctionLiteral::kWrapped, function_type); // Function :: // '(' FormalParameterList? ')' '{' FunctionBody '}' - const RuntimeCallCounterId counters[2][2] = { - {RuntimeCallCounterId::kPreParseBackgroundNoVariableResolution, - RuntimeCallCounterId::kPreParseNoVariableResolution}, - {RuntimeCallCounterId::kPreParseBackgroundWithVariableResolution, - RuntimeCallCounterId::kPreParseWithVariableResolution}}; - RuntimeCallTimerScope runtime_timer( - runtime_call_stats_, - counters[track_unresolved_variables_][parsing_on_main_thread_]); + const RuntimeCallCounterId counters[2] = { + RuntimeCallCounterId::kPreParseBackgroundWithVariableResolution, + RuntimeCallCounterId::kPreParseWithVariableResolution}; + RuntimeCallTimerScope runtime_timer(runtime_call_stats_, + counters[parsing_on_main_thread_]); base::ElapsedTimer timer; if (V8_UNLIKELY(FLAG_log_function_events)) timer.Start(); DeclarationScope* function_scope = NewFunctionScope(kind); function_scope->SetLanguageMode(language_mode); + int func_id = GetNextFunctionLiteralId(); + bool skippable_function = false; // Start collecting data for a new function which might contain skippable // functions. - std::unique_ptr<PreParsedScopeDataBuilder::DataGatheringScope> - preparsed_scope_data_builder_scope; - if (!function_state_->next_function_is_likely_called() && - preparsed_scope_data_builder_ != nullptr) { - DCHECK(FLAG_preparser_scope_analysis); - DCHECK(track_unresolved_variables_); - preparsed_scope_data_builder_scope.reset( - new PreParsedScopeDataBuilder::DataGatheringScope(function_scope, - this)); - } + { + PreparseDataBuilder::DataGatheringScope preparse_data_builder_scope(this); + skippable_function = !function_state_->next_function_is_likely_called() && + preparse_data_builder_ != nullptr; + if (skippable_function) { + preparse_data_builder_scope.Start(function_scope); + } - FunctionState function_state(&function_state_, &scope_, function_scope); - DuplicateFinder duplicate_finder; - ExpressionClassifier formals_classifier(this, &duplicate_finder); - int func_id = GetNextFunctionLiteralId(); + FunctionState function_state(&function_state_, &scope_, function_scope); - Expect(Token::LPAREN, CHECK_OK); - int start_position = scanner()->location().beg_pos; - function_scope->set_start_position(start_position); - PreParserFormalParameters formals(function_scope); - ParseFormalParameterList(&formals, CHECK_OK); - Expect(Token::RPAREN, CHECK_OK); - int formals_end_position = scanner()->location().end_pos; + Expect(Token::LPAREN); + int start_position = position(); + function_scope->set_start_position(start_position); + PreParserFormalParameters formals(function_scope); + { + ParameterDeclarationParsingScope formals_scope(this); + ParseFormalParameterList(&formals); + if (formals_scope.has_duplicate()) formals.set_has_duplicate(); + } + Expect(Token::RPAREN); + int formals_end_position = scanner()->location().end_pos; - CheckArityRestrictions(formals.arity, kind, formals.has_rest, start_position, - formals_end_position, CHECK_OK); + CheckArityRestrictions(formals.arity, kind, formals.has_rest, + start_position, formals_end_position); - Expect(Token::LBRACE, CHECK_OK); + Expect(Token::LBRACE); - // Parse function body. - PreParserStatementList body; - int pos = function_token_pos == kNoSourcePosition ? peek_position() - : function_token_pos; - ParseFunctionBody(body, function_name, pos, formals, kind, function_type, - FunctionBodyType::kBlock, true, CHECK_OK); + // Parse function body. + PreParserScopedStatementList body(pointer_buffer()); + int pos = function_token_pos == kNoSourcePosition ? peek_position() + : function_token_pos; + AcceptINScope scope(this, true); + ParseFunctionBody(&body, function_name, pos, formals, kind, function_type, + FunctionBodyType::kBlock); - // Parsing the body may change the language mode in our scope. - language_mode = function_scope->language_mode(); + // Parsing the body may change the language mode in our scope. + language_mode = function_scope->language_mode(); - if (is_sloppy(language_mode)) { - function_scope->HoistSloppyBlockFunctions(nullptr); - } + if (is_sloppy(language_mode)) { + function_scope->HoistSloppyBlockFunctions(nullptr); + } - // Validate name and parameter names. We can do this only after parsing the - // function, since the function can declare itself strict. - CheckFunctionName(language_mode, function_name, function_name_validity, - function_name_location, CHECK_OK); - const bool allow_duplicate_parameters = - is_sloppy(language_mode) && formals.is_simple && !IsConciseMethod(kind); - ValidateFormalParameters(language_mode, allow_duplicate_parameters, CHECK_OK); - - int end_position = scanner()->location().end_pos; - if (is_strict(language_mode)) { - CheckStrictOctalLiteral(start_position, end_position, CHECK_OK); - } + // Validate name and parameter names. We can do this only after parsing the + // function, since the function can declare itself strict. + CheckFunctionName(language_mode, function_name, function_name_validity, + function_name_location); - if (preparsed_scope_data_builder_scope) { - preparsed_scope_data_builder_scope->MarkFunctionAsSkippable( - end_position, GetLastFunctionLiteralId() - func_id); + if (is_strict(language_mode)) { + CheckStrictOctalLiteral(start_position, end_position()); + } + if (skippable_function) { + preparse_data_builder_scope.SetSkippableFunction( + function_scope, GetLastFunctionLiteralId() - func_id); + } } + if (V8_UNLIKELY(FLAG_log_function_events)) { double ms = timer.Elapsed().InMillisecondsF(); - const char* event_name = track_unresolved_variables_ - ? "preparse-resolution" - : "preparse-no-resolution"; + const char* event_name = "preparse-resolution"; // We might not always get a function name here. However, it can be easily // reconstructed from the script id and the byte range in the log processor. const char* name = ""; @@ -392,87 +363,47 @@ PreParser::Expression PreParser::ParseFunctionLiteral( return Expression::Default(); } -PreParser::LazyParsingResult PreParser::ParseStatementListAndLogFunction( - PreParserFormalParameters* formals, bool may_abort, bool* ok) { - PreParserStatementList body; - LazyParsingResult result = ParseStatementList( - body, Token::RBRACE, may_abort, CHECK_OK_VALUE(kLazyParsingComplete)); - if (result == kLazyParsingAborted) return result; +void PreParser::ParseStatementListAndLogFunction( + PreParserFormalParameters* formals) { + PreParserScopedStatementList body(pointer_buffer()); + ParseStatementList(&body, Token::RBRACE); // Position right after terminal '}'. - DCHECK_EQ(Token::RBRACE, scanner()->peek()); + DCHECK_IMPLIES(!has_error(), scanner()->peek() == Token::RBRACE); int body_end = scanner()->peek_location().end_pos; DCHECK_EQ(this->scope()->is_function_scope(), formals->is_simple); log_.LogFunction(body_end, formals->num_parameters(), GetLastFunctionLiteralId()); - return kLazyParsingComplete; } -PreParserStatement PreParser::BuildParameterInitializationBlock( - const PreParserFormalParameters& parameters, bool* ok) { +PreParserBlock PreParser::BuildParameterInitializationBlock( + const PreParserFormalParameters& parameters) { DCHECK(!parameters.is_simple); DCHECK(scope()->is_function_scope()); - if (FLAG_preparser_scope_analysis && - scope()->AsDeclarationScope()->calls_sloppy_eval() && - preparsed_scope_data_builder_ != nullptr) { + if (scope()->AsDeclarationScope()->calls_sloppy_eval() && + preparse_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 (preparsed_scope_data_builder_->parent() != nullptr) { + if (preparse_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.) - preparsed_scope_data_builder_->parent()->Bailout(); + preparse_data_builder_->parent()->Bailout(); } else { // Lazy parsing started at the current function; it cannot contain // skippable functions. - preparsed_scope_data_builder_->Bailout(); + preparse_data_builder_->Bailout(); } } - return PreParserStatement::Default(); -} - -PreParserExpression PreParser::ExpressionFromIdentifier( - const PreParserIdentifier& name, int start_position, InferName infer) { - VariableProxy* proxy = nullptr; - if (track_unresolved_variables_) { - DCHECK_NOT_NULL(name.string_); - proxy = scope()->NewUnresolved(factory()->ast_node_factory(), name.string_, - start_position, NORMAL_VARIABLE); - } - return PreParserExpression::FromIdentifier(name, proxy, zone()); + return PreParserBlock::Default(); } -void PreParser::DeclareAndInitializeVariables( - PreParserStatement block, - const DeclarationDescriptor* declaration_descriptor, - const DeclarationParsingResult::Declaration* declaration, - ZonePtrList<const AstRawString>* names, bool* ok) { - if (declaration->pattern.variables_ != nullptr) { - DCHECK(FLAG_lazy_inner_functions); - DCHECK(track_unresolved_variables_); - for (auto variable : *(declaration->pattern.variables_)) { - declaration_descriptor->scope->RemoveUnresolved(variable); - Variable* var = scope()->DeclareVariableName( - variable->raw_name(), declaration_descriptor->mode); - if (FLAG_preparser_scope_analysis) { - MarkLoopVariableAsAssigned(declaration_descriptor->scope, var, - declaration_descriptor->declaration_kind); - // This is only necessary if there is an initializer, but we don't have - // that information here. Consequently, the preparser sometimes says - // maybe-assigned where the parser (correctly) says never-assigned. - } - if (names) { - names->Add(variable->raw_name(), zone()); - } - } - } +bool PreParser::IdentifierEquals(const PreParserIdentifier& identifier, + const AstRawString* other) { + return identifier.string_ == other; } -#undef CHECK_OK -#undef CHECK_OK_CUSTOM - - } // namespace internal } // namespace v8 diff --git a/deps/v8/src/parsing/preparser.h b/deps/v8/src/parsing/preparser.h index 65509a2029..d403854743 100644 --- a/deps/v8/src/parsing/preparser.h +++ b/deps/v8/src/parsing/preparser.h @@ -22,7 +22,7 @@ namespace internal { // interface as AstNodeFactory, so ParserBase doesn't need to care which one is // used. -class PreParsedScopeDataBuilder; +class PreparseDataBuilder; class PreParserIdentifier { public: @@ -56,8 +56,12 @@ class PreParserIdentifier { } bool IsNull() const { return type_ == kNullIdentifier; } bool IsEval() const { return type_ == kEvalIdentifier; } + bool IsAsync() const { return type_ == kAsyncIdentifier; } bool IsArguments() const { return type_ == kArgumentsIdentifier; } - bool IsEvalOrArguments() const { return IsEval() || IsArguments(); } + bool IsEvalOrArguments() const { + STATIC_ASSERT(kEvalIdentifier + 1 == kArgumentsIdentifier); + return IsInRange(type_, kEvalIdentifier, kArgumentsIdentifier); + } bool IsConstructor() const { return type_ == kConstructorIdentifier; } bool IsAwait() const { return type_ == kAwaitIdentifier; } bool IsName() const { return type_ == kNameIdentifier; } @@ -77,7 +81,6 @@ class PreParserIdentifier { }; explicit PreParserIdentifier(Type type) : string_(nullptr), type_(type) {} - // Only non-nullptr when PreParser.track_unresolved_variables_ is true. const AstRawString* string_; Type type_; @@ -88,89 +91,54 @@ class PreParserIdentifier { class PreParserExpression { public: - using VariableZoneThreadedListType = - ZoneThreadedList<VariableProxy, VariableProxy::PreParserNext>; - - PreParserExpression() - : code_(TypeField::encode(kNull)), variables_(nullptr) {} + PreParserExpression() : code_(TypeField::encode(kNull)) {} static PreParserExpression Null() { return PreParserExpression(); } + static PreParserExpression Failure() { + return PreParserExpression(TypeField::encode(kFailure)); + } - static PreParserExpression Default( - VariableZoneThreadedListType* variables = nullptr) { - return PreParserExpression(TypeField::encode(kExpression), variables); + static PreParserExpression Default() { + return PreParserExpression(TypeField::encode(kExpression)); } static PreParserExpression Spread(const PreParserExpression& expression) { - return PreParserExpression(TypeField::encode(kSpreadExpression), - expression.variables_); + return PreParserExpression(TypeField::encode(kSpreadExpression)); } - static PreParserExpression FromIdentifier(const PreParserIdentifier& id, - VariableProxy* variable, - Zone* zone) { - PreParserExpression expression(TypeField::encode(kIdentifierExpression) | - IdentifierTypeField::encode(id.type_)); - expression.AddVariable(variable, zone); - return expression; + static PreParserExpression FromIdentifier(const PreParserIdentifier& id) { + return PreParserExpression(TypeField::encode(kIdentifierExpression) | + IdentifierTypeField::encode(id.type_)); } static PreParserExpression BinaryOperation(const PreParserExpression& left, Token::Value op, const PreParserExpression& right, Zone* zone) { - if (op == Token::COMMA) { - // Possibly an arrow function parameter list. - if (left.variables_ == nullptr) { - return PreParserExpression(TypeField::encode(kExpression), - right.variables_); - } - if (right.variables_ != nullptr) { - left.variables_->Append(std::move(*right.variables_)); - } - return PreParserExpression(TypeField::encode(kExpression), - left.variables_); - } return PreParserExpression(TypeField::encode(kExpression)); } - static PreParserExpression Assignment( - VariableZoneThreadedListType* variables) { + static PreParserExpression Assignment() { return PreParserExpression(TypeField::encode(kExpression) | - ExpressionTypeField::encode(kAssignment), - variables); + ExpressionTypeField::encode(kAssignment)); } static PreParserExpression NewTargetExpression() { return PreParserExpression::Default(); } - static PreParserExpression ObjectLiteral( - VariableZoneThreadedListType* variables) { - return PreParserExpression(TypeField::encode(kObjectLiteralExpression), - variables); + static PreParserExpression ObjectLiteral() { + return PreParserExpression(TypeField::encode(kObjectLiteralExpression)); } - static PreParserExpression ArrayLiteral( - VariableZoneThreadedListType* variables) { - return PreParserExpression(TypeField::encode(kArrayLiteralExpression), - variables); + static PreParserExpression ArrayLiteral() { + return PreParserExpression(TypeField::encode(kArrayLiteralExpression)); } static PreParserExpression StringLiteral() { return PreParserExpression(TypeField::encode(kStringLiteralExpression)); } - static PreParserExpression UseStrictStringLiteral() { - return PreParserExpression(TypeField::encode(kStringLiteralExpression) | - IsUseStrictField::encode(true)); - } - - static PreParserExpression UseAsmStringLiteral() { - return PreParserExpression(TypeField::encode(kStringLiteralExpression) | - IsUseAsmField::encode(true)); - } - static PreParserExpression This() { return PreParserExpression(TypeField::encode(kExpression) | ExpressionTypeField::encode(kThisExpression)); @@ -229,6 +197,9 @@ class PreParserExpression { } bool IsNull() const { return TypeField::decode(code_) == kNull; } + bool IsFailureExpression() const { + return TypeField::decode(code_) == kFailure; + } bool IsIdentifier() const { return TypeField::decode(code_) == kIdentifierExpression; @@ -252,18 +223,14 @@ class PreParserExpression { return TypeField::decode(code_) == kArrayLiteralExpression; } - bool IsStringLiteral() const { - return TypeField::decode(code_) == kStringLiteralExpression; - } - - bool IsUseStrictLiteral() const { - return TypeField::decode(code_) == kStringLiteralExpression && - IsUseStrictField::decode(code_); + bool IsPattern() const { + STATIC_ASSERT(kObjectLiteralExpression + 1 == kArrayLiteralExpression); + return IsInRange(TypeField::decode(code_), kObjectLiteralExpression, + kArrayLiteralExpression); } - bool IsUseAsmLiteral() const { - return TypeField::decode(code_) == kStringLiteralExpression && - IsUseAsmField::decode(code_); + bool IsStringLiteral() const { + return TypeField::decode(code_) == kStringLiteralExpression; } bool IsThis() const { @@ -325,24 +292,25 @@ class PreParserExpression { return TypeField::decode(code_) == kSpreadExpression; } + bool is_parenthesized() const { return IsParenthesizedField::decode(code_); } + + void mark_parenthesized() { + code_ = IsParenthesizedField::update(code_, true); + } + + void clear_parenthesized() { + code_ = IsParenthesizedField::update(code_, false); + } + PreParserExpression AsFunctionLiteral() { return *this; } // Dummy implementation for making expression->somefunc() work in both Parser // and PreParser. PreParserExpression* operator->() { return this; } - void set_is_private_field() { - if (variables_ != nullptr) { - DCHECK(IsIdentifier()); - DCHECK(AsIdentifier().IsPrivateName()); - DCHECK_EQ(1, variables_->LengthForTest()); - variables_->first()->set_is_private_field(); - } - } - // More dummy implementations of things PreParser doesn't need to track: void SetShouldEagerCompile() {} - void mark_as_iife() {} + void mark_as_oneshot_iife() {} int position() const { return kNoSourcePosition; } void set_function_token_position(int position) {} @@ -352,6 +320,7 @@ class PreParserExpression { private: enum Type { kNull, + kFailure, kExpression, kIdentifierExpression, kStringLiteralExpression, @@ -373,20 +342,8 @@ class PreParserExpression { kAssignment }; - explicit PreParserExpression( - uint32_t expression_code, - VariableZoneThreadedListType* variables = nullptr) - : code_(expression_code), variables_(variables) {} - - void AddVariable(VariableProxy* variable, Zone* zone) { - if (variable == nullptr) { - return; - } - if (variables_ == nullptr) { - variables_ = new (zone) VariableZoneThreadedListType(); - } - variables_->Add(variable); - } + explicit PreParserExpression(uint32_t expression_code) + : code_(expression_code) {} // The first three bits are for the Type. typedef BitField<Type, 0, 3> TypeField; @@ -396,80 +353,66 @@ class PreParserExpression { // Expression nodes may be represented as multiple Types, not exclusively // through kExpression. // TODO(caitp, adamk): clean up PreParserExpression bitfields. - typedef BitField<bool, 31, 1> ParenthesizedField; + typedef BitField<bool, TypeField::kNext, 1> IsParenthesizedField; // The rest of the bits are interpreted depending on the value // of the Type field, so they can share the storage. - typedef BitField<ExpressionType, TypeField::kNext, 4> ExpressionTypeField; - typedef BitField<bool, TypeField::kNext, 1> IsUseStrictField; - typedef BitField<bool, IsUseStrictField::kNext, 1> IsUseAsmField; - typedef BitField<PreParserIdentifier::Type, TypeField::kNext, 8> + typedef BitField<ExpressionType, IsParenthesizedField::kNext, 4> + ExpressionTypeField; + typedef BitField<PreParserIdentifier::Type, IsParenthesizedField::kNext, 8> IdentifierTypeField; - typedef BitField<bool, TypeField::kNext, 1> HasCoverInitializedNameField; + typedef BitField<bool, IsParenthesizedField::kNext, 1> + HasCoverInitializedNameField; uint32_t code_; - // If the PreParser is used in the variable tracking mode, PreParserExpression - // accumulates variables in that expression. - VariableZoneThreadedListType* variables_; - friend class PreParser; friend class PreParserFactory; friend class PreParserExpressionList; }; +class PreParserStatement; +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_; } + + private: + explicit PreParserStatementList(bool is_null) : is_null_(is_null) {} + bool is_null_; +}; + +class PreParserScopedStatementList { + public: + explicit PreParserScopedStatementList(std::vector<void*>* buffer) {} + void Rewind() {} + void MergeInto(const PreParserScopedStatementList* other) {} + void Add(const PreParserStatement& element) {} + int length() { return 0; } +}; // 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. class PreParserExpressionList { - using VariableZoneThreadedListType = - ZoneThreadedList<VariableProxy, VariableProxy::PreParserNext>; - public: - // These functions make list->Add(some_expression) work (and do nothing). - 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_)); - } + explicit PreParserExpressionList(std::vector<void*>* buffer) : length_(0) {} + + int length() const { return length_; } + + void Add(const PreParserExpression& expression) { ++length_; } - int length() const { return length_; } - static PreParserExpressionList Null() { return PreParserExpressionList(-1); } - bool IsNull() const { return length_ == -1; } - void Set(int index, const PreParserExpression& element) {} private: - explicit PreParserExpressionList(int n) : length_(n), variables_(nullptr) {} int length_; - VariableZoneThreadedListType* variables_; - friend class PreParser; friend class PreParserFactory; }; -class PreParserStatement; - -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_; } - - private: - explicit PreParserStatementList(bool is_null) : is_null_(is_null) {} - bool is_null_; -}; - class PreParserStatement { public: static PreParserStatement Default() { @@ -488,33 +431,21 @@ class PreParserStatement { return PreParserStatement(kJumpStatement); } + void InitializeStatements(const PreParserScopedStatementList& statements, + Zone* zone) {} + // Creates expression statement from expression. // Preserves being an unparenthesized string literal, possibly // "use strict". static PreParserStatement ExpressionStatement( const PreParserExpression& expression) { - if (expression.IsUseStrictLiteral()) { - return PreParserStatement(kUseStrictExpressionStatement); - } - if (expression.IsUseAsmLiteral()) { - return PreParserStatement(kUseAsmExpressionStatement); - } if (expression.IsStringLiteral()) { return PreParserStatement(kStringLiteralExpressionStatement); } return Default(); } - bool IsStringLiteral() { - return code_ == kStringLiteralExpressionStatement || IsUseStrictLiteral() || - IsUseAsmLiteral(); - } - - bool IsUseStrictLiteral() { - return code_ == kUseStrictExpressionStatement; - } - - bool IsUseAsmLiteral() { return code_ == kUseAsmExpressionStatement; } + bool IsStringLiteral() { return code_ == kStringLiteralExpressionStatement; } bool IsJumpStatement() { return code_ == kJumpStatement; @@ -540,22 +471,46 @@ class PreParserStatement { void Initialize(PreParserStatement init, const PreParserExpression& cond, PreParserStatement next, PreParserStatement body, const SourceRange& body_range = {}) {} + void Initialize(PreParserExpression each, const PreParserExpression& subject, + PreParserStatement body, const SourceRange& body_range = {}) { + } - private: + protected: enum Type { kNullStatement, kEmptyStatement, kUnknownStatement, kJumpStatement, kStringLiteralExpressionStatement, - kUseStrictExpressionStatement, - kUseAsmExpressionStatement, }; explicit PreParserStatement(Type code) : code_(code) {} + + private: Type code_; }; +// A PreParserBlock extends statement with a place to store the scope. +// The scope is dropped as the block is returned as a statement. +class PreParserBlock : public PreParserStatement { + public: + void set_scope(Scope* scope) { scope_ = scope; } + Scope* scope() const { return scope_; } + static PreParserBlock Default() { + return PreParserBlock(PreParserStatement::kUnknownStatement); + } + static PreParserBlock Null() { + return PreParserBlock(PreParserStatement::kNullStatement); + } + // Dummy implementation for making block->somefunc() work in both Parser and + // PreParser. + PreParserBlock* operator->() { return this; } + + private: + explicit PreParserBlock(PreParserStatement::Type type) + : PreParserStatement(type), scope_(nullptr) {} + Scope* scope_; +}; class PreParserFactory { public: @@ -566,16 +521,7 @@ class PreParserFactory { PreParserExpression NewStringLiteral(const PreParserIdentifier& identifier, int pos) { - // This is needed for object literal property names. Property names are - // normalized to string literals during object literal parsing. - PreParserExpression expression = PreParserExpression::Default(); - if (identifier.string_ != nullptr) { - DCHECK(FLAG_lazy_inner_functions); - VariableProxy* variable = ast_node_factory_.NewVariableProxy( - identifier.string_, NORMAL_VARIABLE); - expression.AddVariable(variable, zone_); - } - return expression; + return PreParserExpression::Default(); } PreParserExpression NewNumberLiteral(double number, int pos) { @@ -593,30 +539,31 @@ class PreParserFactory { } PreParserExpression NewArrayLiteral(const PreParserExpressionList& values, int first_spread_index, int pos) { - return PreParserExpression::ArrayLiteral(values.variables_); + return PreParserExpression::ArrayLiteral(); } PreParserExpression NewClassLiteralProperty(const PreParserExpression& key, const PreParserExpression& value, ClassLiteralProperty::Kind kind, bool is_static, - bool is_computed_name) { + bool is_computed_name, + bool is_private) { return PreParserExpression::Default(); } PreParserExpression NewObjectLiteralProperty(const PreParserExpression& key, const PreParserExpression& value, ObjectLiteralProperty::Kind kind, bool is_computed_name) { - return PreParserExpression::Default(value.variables_); + return PreParserExpression::Default(); } PreParserExpression NewObjectLiteralProperty(const PreParserExpression& key, const PreParserExpression& value, bool is_computed_name) { - return PreParserExpression::Default(value.variables_); + return PreParserExpression::Default(); } PreParserExpression NewObjectLiteral( const PreParserExpressionList& properties, int boilerplate_properties, int pos, bool has_rest_property) { - return PreParserExpression::ObjectLiteral(properties.variables_); + return PreParserExpression::ObjectLiteral(); } PreParserExpression NewVariableProxy(void* variable) { return PreParserExpression::Default(); @@ -653,16 +600,12 @@ class PreParserFactory { int pos) { return PreParserExpression::Default(); } - PreParserExpression NewRewritableExpression( - const PreParserExpression& expression, Scope* scope) { - return expression; - } PreParserExpression NewAssignment(Token::Value op, const PreParserExpression& left, const PreParserExpression& right, int pos) { // Identifiers need to be tracked since this might be a parameter with a // default value inside an arrow function parameter list. - return PreParserExpression::Assignment(left.variables_); + return PreParserExpression::Assignment(); } PreParserExpression NewYield(const PreParserExpression& expression, int pos, Suspend::OnAbruptResume on_abrupt_resume) { @@ -717,14 +660,14 @@ class PreParserFactory { } PreParserExpression NewFunctionLiteral( const PreParserIdentifier& name, Scope* scope, - PreParserStatementList body, int expected_property_count, + const PreParserScopedStatementList& body, int expected_property_count, int parameter_count, int function_length, FunctionLiteral::ParameterFlag has_duplicate_parameters, FunctionLiteral::FunctionType function_type, FunctionLiteral::EagerCompileHint eager_compile_hint, int position, bool has_braces, int function_literal_id, - ProducedPreParsedScopeData* produced_preparsed_scope_data = nullptr) { - DCHECK_NULL(produced_preparsed_scope_data); + ProducedPreparseData* produced_preparse_data = nullptr) { + DCHECK_NULL(produced_preparse_data); return PreParserExpression::Default(); } @@ -734,17 +677,25 @@ class PreParserFactory { } PreParserExpression NewEmptyParentheses(int pos) { - return PreParserExpression::Default(); + PreParserExpression result = PreParserExpression::Default(); + result.mark_parenthesized(); + return result; } - PreParserStatement NewEmptyStatement(int pos) { - return PreParserStatement::Default(); + PreParserStatement EmptyStatement() { return PreParserStatement::Default(); } + + PreParserBlock NewBlock(int capacity, bool ignore_completion_value) { + return PreParserBlock::Default(); } - PreParserStatement NewBlock( - int capacity, bool ignore_completion_value, - ZonePtrList<const AstRawString>* labels = nullptr) { - return PreParserStatement::Default(); + PreParserBlock NewBlock(bool ignore_completion_value, + ZonePtrList<const AstRawString>* labels) { + return PreParserBlock::Default(); + } + + PreParserBlock NewBlock(bool ignore_completion_value, + const PreParserScopedStatementList& list) { + return PreParserBlock::Default(); } PreParserStatement NewDebuggerStatement(int pos) { @@ -801,8 +752,9 @@ class PreParserFactory { return PreParserStatement::Default(); } - PreParserStatement NewCaseClause(const PreParserExpression& label, - PreParserStatementList statements) { + PreParserStatement NewCaseClause( + const PreParserExpression& label, + const PreParserScopedStatementList& statements) { return PreParserStatement::Default(); } @@ -821,7 +773,7 @@ class PreParserFactory { PreParserStatement NewForOfStatement( ZonePtrList<const AstRawString>* labels, - ZonePtrList<const AstRawString>* own_labels, int pos) { + ZonePtrList<const AstRawString>* own_labels, int pos, IteratorType type) { return PreParserStatement::Default(); } @@ -837,35 +789,32 @@ class PreParserFactory { } private: - // For creating VariableProxy objects (if - // PreParser::track_unresolved_variables_ is used). + // For creating VariableProxy objects to track unresolved variables. AstNodeFactory ast_node_factory_; Zone* zone_; }; +class PreParser; -struct PreParserFormalParameters : FormalParametersBase { - struct Parameter : public ZoneObject { - 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; } - - VariableZoneThreadedListType* variables_; - Parameter* next_parameter = nullptr; - bool is_rest : 1; - }; +class PreParserFormalParameters : public FormalParametersBase { + public: explicit PreParserFormalParameters(DeclarationScope* scope) : FormalParametersBase(scope) {} - base::ThreadedList<Parameter> params; -}; + void set_has_duplicate() { has_duplicate_ = true; } + bool has_duplicate() { return has_duplicate_; } + void ValidateDuplicate(PreParser* preparser) const; + void set_strict_parameter_error(const Scanner::Location& loc, + MessageTemplate message) { + strict_parameter_error_ = loc.IsValid(); + } + void ValidateStrictMode(PreParser* preparser) const; -class PreParser; + private: + bool has_duplicate_ = false; + bool strict_parameter_error_ = false; +}; class PreParserTarget { public: @@ -880,7 +829,7 @@ class PreParserTargetScope { class PreParserFuncNameInferrer { public: - PreParserFuncNameInferrer(AstValueFactory* avf, Zone* zone) {} + explicit PreParserFuncNameInferrer(AstValueFactory* avf) {} void RemoveAsyncKeywordFromEnd() const {} void Infer() const {} void RemoveLastFunction() const {} @@ -920,39 +869,40 @@ class PreParserSourceRangeScope { DISALLOW_IMPLICIT_CONSTRUCTORS(PreParserSourceRangeScope); }; +class PreParserPropertyList {}; + template <> struct ParserTypes<PreParser> { typedef ParserBase<PreParser> Base; typedef PreParser Impl; // Return types for traversing functions. - typedef PreParserIdentifier Identifier; + typedef PreParserExpression ClassLiteralProperty; typedef PreParserExpression Expression; typedef PreParserExpression FunctionLiteral; typedef PreParserExpression ObjectLiteralProperty; - typedef PreParserExpression ClassLiteralProperty; typedef PreParserExpression Suspend; - typedef PreParserExpression RewritableExpression; typedef PreParserExpressionList ExpressionList; typedef PreParserExpressionList ObjectPropertyList; - typedef PreParserExpressionList ClassPropertyList; typedef PreParserFormalParameters FormalParameters; - typedef PreParserStatement Statement; - typedef PreParserStatementList StatementList; - typedef PreParserStatement Block; + typedef PreParserIdentifier Identifier; + typedef PreParserPropertyList ClassPropertyList; + typedef PreParserScopedStatementList StatementList; + typedef PreParserBlock Block; typedef PreParserStatement BreakableStatement; - typedef PreParserStatement IterationStatement; typedef PreParserStatement ForStatement; + typedef PreParserStatement IterationStatement; + typedef PreParserStatement Statement; // For constructing objects returned by the traversing functions. typedef PreParserFactory Factory; - typedef PreParserTarget Target; - typedef PreParserTargetScope TargetScope; + // Other implementation-specific tasks. typedef PreParserFuncNameInferrer FuncNameInferrer; typedef PreParserSourceRange SourceRange; typedef PreParserSourceRangeScope SourceRangeScope; - static constexpr bool ExpressionClassifierReportErrors = false; + typedef PreParserTarget Target; + typedef PreParserTargetScope TargetScope; }; @@ -970,7 +920,6 @@ struct ParserTypes<PreParser> { // it is used) are generally omitted. class PreParser : public ParserBase<PreParser> { friend class ParserBase<PreParser>; - friend class v8::internal::ExpressionClassifier<ParserTypes<PreParser>>; public: typedef PreParserIdentifier Identifier; @@ -979,7 +928,6 @@ class PreParser : public ParserBase<PreParser> { enum PreParseResult { kPreParseStackOverflow, - kPreParseAbort, kPreParseNotIdentifiableError, kPreParseSuccess }; @@ -995,8 +943,7 @@ class PreParser : public ParserBase<PreParser> { runtime_call_stats, logger, script_id, parsing_module, parsing_on_main_thread), use_counts_(nullptr), - track_unresolved_variables_(false), - preparsed_scope_data_builder_(nullptr) {} + preparse_data_builder_(nullptr) {} static bool IsPreParser() { return true; } @@ -1019,25 +966,23 @@ class PreParser : public ParserBase<PreParser> { PreParseResult PreParseFunction( const AstRawString* function_name, FunctionKind kind, FunctionLiteral::FunctionType function_type, - DeclarationScope* function_scope, bool track_unresolved_variables, - bool may_abort, int* use_counts, - ProducedPreParsedScopeData** produced_preparser_scope_data, - int script_id); - - V8_INLINE static bool ShouldTrackUnresolvedVariables(bool is_inner_function) { - return FLAG_preparser_scope_analysis || is_inner_function; - } + DeclarationScope* function_scope, int* use_counts, + ProducedPreparseData** produced_preparser_scope_data, int script_id); - PreParsedScopeDataBuilder* preparsed_scope_data_builder() const { - return preparsed_scope_data_builder_; + PreparseDataBuilder* preparse_data_builder() const { + return preparse_data_builder_; } - void set_preparsed_scope_data_builder( - PreParsedScopeDataBuilder* preparsed_scope_data_builder) { - preparsed_scope_data_builder_ = preparsed_scope_data_builder; + void set_preparse_data_builder(PreparseDataBuilder* preparse_data_builder) { + preparse_data_builder_ = preparse_data_builder; } private: + friend class i::ExpressionScope<ParserTypes<PreParser>>; + friend class i::VariableDeclarationParsingScope<ParserTypes<PreParser>>; + friend class i::ParameterDeclarationParsingScope<ParserTypes<PreParser>>; + friend class i::ArrowHeadParsingScope<ParserTypes<PreParser>>; + friend class PreParserFormalParameters; // These types form an algebra over syntactic categories that is just // rich enough to let us recognize and propagate the constructs that // are either being counted in the preparser data, or is important @@ -1057,13 +1002,11 @@ class PreParser : public ParserBase<PreParser> { return pending_error_handler_; } - 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) { + V8_INLINE bool SkipFunction(const AstRawString* name, FunctionKind kind, + FunctionLiteral::FunctionType function_type, + DeclarationScope* function_scope, + int* num_parameters, + ProducedPreparseData** produced_preparse_data) { UNREACHABLE(); } @@ -1072,15 +1015,13 @@ class PreParser : public ParserBase<PreParser> { FunctionNameValidity function_name_validity, FunctionKind kind, int function_token_pos, FunctionLiteral::FunctionType function_type, LanguageMode language_mode, - ZonePtrList<const AstRawString>* arguments_for_wrapped_function, - bool* ok); + ZonePtrList<const AstRawString>* arguments_for_wrapped_function); PreParserExpression InitializeObjectLiteral(PreParserExpression literal) { return literal; } - LazyParsingResult ParseStatementListAndLogFunction( - PreParserFormalParameters* formals, bool maybe_abort, bool* ok); + void ParseStatementListAndLogFunction(PreParserFormalParameters* formals); struct TemplateLiteralState {}; @@ -1099,7 +1040,7 @@ class PreParser : public ParserBase<PreParser> { const PreParserExpression& expression) { return expression.IsPropertyWithPrivateFieldKey(); } - V8_INLINE void CheckConflictingVarDeclarations(Scope* scope, bool* ok) {} + V8_INLINE void CheckConflictingVarDeclarations(Scope* scope) {} V8_INLINE void SetLanguageMode(Scope* scope, LanguageMode mode) { scope->SetLanguageMode(mode); @@ -1114,22 +1055,14 @@ class PreParser : public ParserBase<PreParser> { SpreadCallNew(const PreParserExpression& function, const PreParserExpressionList& args, int pos); - V8_INLINE void RewriteDestructuringAssignments() {} - V8_INLINE void PrepareGeneratorVariables() {} V8_INLINE void RewriteAsyncFunctionBody( - PreParserStatementList body, PreParserStatement block, - const PreParserExpression& return_value, bool* ok) {} - - void DeclareAndInitializeVariables( - PreParserStatement block, - const DeclarationDescriptor* declaration_descriptor, - const DeclarationParsingResult::Declaration* declaration, - ZonePtrList<const AstRawString>* names, bool* ok); + const PreParserScopedStatementList* body, PreParserStatement block, + const PreParserExpression& return_value) {} V8_INLINE void DeclareLabel(ZonePtrList<const AstRawString>** labels, ZonePtrList<const AstRawString>** own_labels, - const PreParserExpression& expr, bool* ok) { + const PreParserExpression& expr) { DCHECK(!parsing_module_ || !expr.AsIdentifier().IsAwait()); DCHECK(IsIdentifier(expr)); } @@ -1149,24 +1082,29 @@ class PreParser : public ParserBase<PreParser> { return PreParserStatement::Default(); } - V8_INLINE void RewriteCatchPattern(CatchInfo* catch_info, bool* ok) { - if (track_unresolved_variables_) { - const AstRawString* catch_name = catch_info->name.string_; - if (catch_name == nullptr) { - catch_name = ast_value_factory()->dot_catch_string(); - } - catch_info->scope->DeclareCatchVariableName(catch_name); + void DeclareVariable(VariableProxy* proxy, VariableKind kind, + VariableMode mode, InitializationFlag init, Scope* scope, + bool* was_added, int position) { + DeclareVariableName(proxy->raw_name(), mode, scope, was_added, kind); + } - if (catch_info->pattern.variables_ != nullptr) { - for (auto variable : *catch_info->pattern.variables_) { - scope()->DeclareVariableName(variable->raw_name(), - VariableMode::kLet); - } - } + void DeclareVariableName(const AstRawString* name, VariableMode mode, + Scope* scope, bool* was_added, + VariableKind kind = NORMAL_VARIABLE) { + if (scope->DeclareVariableName(name, mode, was_added, kind) == nullptr) { + ReportUnidentifiableError(); } } - V8_INLINE void ValidateCatchBlock(const CatchInfo& catch_info, bool* ok) {} + V8_INLINE PreParserBlock RewriteCatchPattern(CatchInfo* catch_info) { + return PreParserBlock::Default(); + } + + V8_INLINE void ReportVarRedeclarationIn(const AstRawString* name, + Scope* scope) { + ReportUnidentifiableError(); + } + V8_INLINE PreParserStatement RewriteTryStatement( PreParserStatement try_block, PreParserStatement catch_block, const SourceRange& catch_range, PreParserStatement finally_block, @@ -1174,20 +1112,24 @@ class PreParser : public ParserBase<PreParser> { return PreParserStatement::Default(); } + V8_INLINE void ReportUnexpectedTokenAt( + Scanner::Location location, Token::Value token, + MessageTemplate message = MessageTemplate::kUnexpectedToken) { + ReportUnidentifiableError(); + } V8_INLINE void ParseAndRewriteGeneratorFunctionBody( - int pos, FunctionKind kind, PreParserStatementList body, bool* ok) { - ParseStatementList(body, Token::RBRACE, ok); + int pos, FunctionKind kind, PreParserScopedStatementList* body) { + ParseStatementList(body, Token::RBRACE); } V8_INLINE void ParseAndRewriteAsyncGeneratorFunctionBody( - int pos, FunctionKind kind, PreParserStatementList body, bool* ok) { - ParseStatementList(body, Token::RBRACE, ok); + int pos, FunctionKind kind, PreParserScopedStatementList* body) { + ParseStatementList(body, Token::RBRACE); } V8_INLINE void DeclareFunctionNameVar( const AstRawString* function_name, FunctionLiteral::FunctionType function_type, DeclarationScope* function_scope) { - if (track_unresolved_variables_ && - function_type == FunctionLiteral::kNamedExpression && + if (function_type == FunctionLiteral::kNamedExpression && function_scope->LookupLocal(function_name) == nullptr) { DCHECK_EQ(function_scope, scope()); function_scope->DeclareFunctionVar(function_name); @@ -1202,31 +1144,33 @@ class PreParser : public ParserBase<PreParser> { function_scope); } - V8_INLINE PreParserExpression RewriteDoExpression(PreParserStatement body, - int pos, bool* ok) { - return PreParserExpression::Default(); - } + bool IdentifierEquals(const PreParserIdentifier& identifier, + const AstRawString* other); // TODO(nikolaos): The preparser currently does not keep track of labels // and targets. V8_INLINE PreParserStatement - LookupBreakTarget(const PreParserIdentifier& label, bool* ok) { + LookupBreakTarget(const PreParserIdentifier& label) { return PreParserStatement::Default(); } V8_INLINE PreParserStatement - LookupContinueTarget(const PreParserIdentifier& label, bool* ok) { + LookupContinueTarget(const PreParserIdentifier& label) { return PreParserStatement::Default(); } V8_INLINE PreParserStatement DeclareFunction(const PreParserIdentifier& variable_name, const PreParserExpression& function, VariableMode mode, - int pos, bool is_sloppy_block_function, - ZonePtrList<const AstRawString>* names, bool* ok) { + int beg_pos, int end_pos, bool is_sloppy_block_function, + ZonePtrList<const AstRawString>* names) { DCHECK_NULL(names); if (variable_name.string_ != nullptr) { - DCHECK(track_unresolved_variables_); - scope()->DeclareVariableName(variable_name.string_, mode); + bool was_added; + if (is_strict(language_mode())) { + DeclareVariableName(variable_name.string_, mode, scope(), &was_added); + } else { + scope()->DeclareVariableName(variable_name.string_, mode, &was_added); + } if (is_sloppy_block_function) { GetDeclarationScope()->DeclareSloppyBlockFunction(variable_name.string_, scope()); @@ -1238,47 +1182,51 @@ class PreParser : public ParserBase<PreParser> { V8_INLINE PreParserStatement DeclareClass( const PreParserIdentifier& variable_name, const PreParserExpression& value, ZonePtrList<const AstRawString>* names, - int class_token_pos, int end_pos, bool* ok) { + int class_token_pos, int end_pos) { // Preparser shouldn't be used in contexts where we need to track the names. DCHECK_NULL(names); if (variable_name.string_ != nullptr) { - DCHECK(track_unresolved_variables_); - scope()->DeclareVariableName(variable_name.string_, VariableMode::kLet); + bool was_added; + DeclareVariableName(variable_name.string_, VariableMode::kLet, scope(), + &was_added); } return PreParserStatement::Default(); } V8_INLINE void DeclareClassVariable(const PreParserIdentifier& name, ClassInfo* class_info, - int class_token_pos, bool* ok) { + int class_token_pos) { if (name.string_ != nullptr) { - DCHECK(track_unresolved_variables_); - scope()->DeclareVariableName(name.string_, VariableMode::kConst); + bool was_added; + DeclareVariableName(name.string_, VariableMode::kConst, scope(), + &was_added); } } V8_INLINE void DeclareClassProperty(const PreParserIdentifier& class_name, const PreParserExpression& property, - const PreParserIdentifier& property_name, - ClassLiteralProperty::Kind kind, - bool is_static, bool is_constructor, - bool is_computed_name, - ClassInfo* class_info, bool* ok) { - if (kind == ClassLiteralProperty::PUBLIC_FIELD && is_computed_name) { - scope()->DeclareVariableName( + bool is_constructor, + ClassInfo* class_info) {} + + V8_INLINE void DeclareClassField(const PreParserExpression& property, + const PreParserIdentifier& property_name, + bool is_static, bool is_computed_name, + bool is_private, ClassInfo* class_info) { + DCHECK_IMPLIES(is_computed_name, !is_private); + if (is_computed_name) { + bool was_added; + DeclareVariableName( ClassFieldVariableName(ast_value_factory(), class_info->computed_field_count), - VariableMode::kConst); - } - - if (kind == ClassLiteralProperty::PRIVATE_FIELD && - property_name.string_ != nullptr) { - DCHECK(track_unresolved_variables_); - scope()->DeclareVariableName(property_name.string_, VariableMode::kConst); + VariableMode::kConst, scope(), &was_added); + } else if (is_private && property_name.string_ != nullptr) { + bool was_added; + DeclareVariableName(property_name.string_, VariableMode::kConst, scope(), + &was_added); } } V8_INLINE PreParserExpression RewriteClassLiteral(Scope* scope, const PreParserIdentifier& name, - ClassInfo* class_info, int pos, int end_pos, bool* ok) { + ClassInfo* class_info, int pos, int end_pos) { bool has_default_constructor = !class_info->has_seen_constructor; // Account for the default constructor. if (has_default_constructor) { @@ -1300,14 +1248,14 @@ class PreParser : public ParserBase<PreParser> { if (class_info->has_static_class_fields) { GetNextFunctionLiteralId(); } - if (class_info->has_instance_class_fields) { + if (class_info->has_instance_members) { GetNextFunctionLiteralId(); } return PreParserExpression::Default(); } V8_INLINE PreParserStatement DeclareNative(const PreParserIdentifier& name, - int pos, bool* ok) { + int pos) { return PreParserStatement::Default(); } @@ -1319,6 +1267,10 @@ class PreParser : public ParserBase<PreParser> { return identifier.IsEval(); } + V8_INLINE bool IsAsync(const PreParserIdentifier& identifier) const { + return identifier.IsAsync(); + } + V8_INLINE bool IsArguments(const PreParserIdentifier& identifier) const { return identifier.IsArguments(); } @@ -1378,14 +1330,6 @@ class PreParser : public ParserBase<PreParser> { return false; } - V8_INLINE bool IsUseStrictDirective(PreParserStatement statement) const { - return statement.IsUseStrictLiteral(); - } - - V8_INLINE bool IsUseAsmDirective(PreParserStatement statement) const { - return statement.IsUseAsmLiteral(); - } - V8_INLINE bool IsStringLiteral(PreParserStatement statement) const { return statement.IsStringLiteral(); } @@ -1407,19 +1351,6 @@ class PreParser : public ParserBase<PreParser> { V8_INLINE static void CheckAssigningFunctionLiteralToProperty( const PreParserExpression& left, const PreParserExpression& right) {} - V8_INLINE void MarkExpressionAsAssigned( - const PreParserExpression& expression) { - // TODO(marja): To be able to produce the same errors, the preparser needs - // to start tracking which expressions are variables and which are assigned. - if (expression.variables_ != nullptr) { - DCHECK(FLAG_lazy_inner_functions); - DCHECK(track_unresolved_variables_); - for (auto variable : *expression.variables_) { - variable->set_is_assigned(); - } - } - } - V8_INLINE bool ShortcutNumericLiteralBinaryExpression( PreParserExpression* x, const PreParserExpression& y, Token::Value op, int pos) { @@ -1430,6 +1361,7 @@ class PreParser : public ParserBase<PreParser> { PreParserExpression y, Token::Value op, int pos, const SourceRange& range) { + x->clear_parenthesized(); return nullptr; } @@ -1439,64 +1371,27 @@ class PreParser : public ParserBase<PreParser> { } V8_INLINE PreParserStatement - BuildInitializationBlock(DeclarationParsingResult* parsing_result, - ZonePtrList<const AstRawString>* names, bool* ok) { - for (auto declaration : parsing_result->declarations) { - DeclareAndInitializeVariables(PreParserStatement::Default(), - &(parsing_result->descriptor), &declaration, - names, ok); - } + BuildInitializationBlock(DeclarationParsingResult* parsing_result) { return PreParserStatement::Default(); } - V8_INLINE PreParserStatement InitializeForEachStatement( - PreParserStatement stmt, const PreParserExpression& each, - const PreParserExpression& subject, PreParserStatement body) { - MarkExpressionAsAssigned(each); - return stmt; - } - - V8_INLINE PreParserStatement InitializeForOfStatement( - PreParserStatement stmt, const PreParserExpression& each, - const PreParserExpression& iterable, PreParserStatement body, - bool finalize, IteratorType type, - int next_result_pos = kNoSourcePosition) { - MarkExpressionAsAssigned(each); - return stmt; - } - - V8_INLINE PreParserStatement RewriteForVarInLegacy(const ForInfo& for_info) { - return PreParserStatement::Null(); + V8_INLINE PreParserBlock RewriteForVarInLegacy(const ForInfo& for_info) { + return PreParserBlock::Null(); } V8_INLINE void DesugarBindingInForEachStatement( ForInfo* for_info, PreParserStatement* body_block, - PreParserExpression* each_variable, bool* ok) { - if (track_unresolved_variables_) { - DCHECK_EQ(1, for_info->parsing_result.declarations.size()); - bool is_for_var_of = - for_info->mode == ForEachStatement::ITERATE && - for_info->parsing_result.descriptor.mode == VariableMode::kVar; - bool collect_names = - IsLexicalVariableMode(for_info->parsing_result.descriptor.mode) || - is_for_var_of; - - DeclareAndInitializeVariables( - PreParserStatement::Default(), &for_info->parsing_result.descriptor, - &for_info->parsing_result.declarations[0], - collect_names ? &for_info->bound_names : nullptr, ok); - } + PreParserExpression* each_variable) { } - V8_INLINE PreParserStatement CreateForEachStatementTDZ( - PreParserStatement init_block, const ForInfo& for_info, bool* ok) { - if (track_unresolved_variables_) { - if (IsLexicalVariableMode(for_info.parsing_result.descriptor.mode)) { - for (auto name : for_info.bound_names) { - scope()->DeclareVariableName(name, VariableMode::kLet); - } - return PreParserStatement::Default(); + V8_INLINE PreParserBlock CreateForEachStatementTDZ(PreParserBlock init_block, + const ForInfo& for_info) { + if (IsLexicalVariableMode(for_info.parsing_result.descriptor.mode)) { + for (auto name : for_info.bound_names) { + bool was_added; + DeclareVariableName(name, VariableMode::kLet, scope(), &was_added); } + return PreParserBlock::Default(); } return init_block; } @@ -1504,24 +1399,22 @@ class PreParser : public ParserBase<PreParser> { V8_INLINE StatementT DesugarLexicalBindingsInForStatement( PreParserStatement loop, PreParserStatement init, const PreParserExpression& cond, PreParserStatement next, - PreParserStatement body, Scope* inner_scope, const ForInfo& for_info, - bool* ok) { + PreParserStatement body, Scope* inner_scope, const ForInfo& for_info) { // See Parser::DesugarLexicalBindingsInForStatement. - if (track_unresolved_variables_) { - for (auto name : for_info.bound_names) { - inner_scope->DeclareVariableName( - name, for_info.parsing_result.descriptor.mode); - } + for (auto name : for_info.bound_names) { + bool was_added; + DeclareVariableName(name, for_info.parsing_result.descriptor.mode, + inner_scope, &was_added); } return loop; } - PreParserStatement BuildParameterInitializationBlock( - const PreParserFormalParameters& parameters, bool* ok); + PreParserBlock BuildParameterInitializationBlock( + const PreParserFormalParameters& parameters); - V8_INLINE PreParserStatement + V8_INLINE PreParserBlock BuildRejectPromiseOnException(PreParserStatement init_block) { - return PreParserStatement::Default(); + return PreParserBlock::Default(); } V8_INLINE void InsertSloppyBlockFunctionVarBindings(DeclarationScope* scope) { @@ -1531,44 +1424,52 @@ class PreParser : public ParserBase<PreParser> { V8_INLINE void InsertShadowingVarBindingInitializers( PreParserStatement block) {} - V8_INLINE PreParserExpression - NewThrowReferenceError(MessageTemplate::Template message, int pos) { + V8_INLINE PreParserExpression NewThrowReferenceError(MessageTemplate message, + int pos) { return PreParserExpression::Default(); } - V8_INLINE PreParserExpression - NewThrowSyntaxError(MessageTemplate::Template message, - const PreParserIdentifier& arg, int pos) { + V8_INLINE PreParserExpression NewThrowSyntaxError( + MessageTemplate message, const PreParserIdentifier& arg, int pos) { return PreParserExpression::Default(); } - V8_INLINE PreParserExpression - NewThrowTypeError(MessageTemplate::Template message, - const PreParserIdentifier& arg, int pos) { + V8_INLINE PreParserExpression NewThrowTypeError( + MessageTemplate message, const PreParserIdentifier& arg, int pos) { return PreParserExpression::Default(); } // Reporting errors. void ReportMessageAt(Scanner::Location source_location, - MessageTemplate::Template message, - const char* arg = nullptr, + MessageTemplate message, const char* arg = nullptr, ParseErrorType error_type = kSyntaxError) { pending_error_handler()->ReportMessageAt(source_location.beg_pos, source_location.end_pos, message, arg, error_type); + scanner()->set_parser_error(); } V8_INLINE void ReportUnidentifiableError() { - pending_error_handler()->SetUnidentifiableError(); + pending_error_handler()->set_unidentifiable_error(); + scanner()->set_parser_error(); } V8_INLINE void ReportMessageAt(Scanner::Location source_location, - MessageTemplate::Template message, + MessageTemplate message, const PreParserIdentifier& arg, ParseErrorType error_type = kSyntaxError) { UNREACHABLE(); } + void ReportMessageAt(Scanner::Location source_location, + MessageTemplate message, const AstRawString* arg, + ParseErrorType error_type = kSyntaxError) { + pending_error_handler()->ReportMessageAt(source_location.beg_pos, + source_location.end_pos, message, + arg, error_type); + scanner()->set_parser_error(); + } + // "null" return type creators. V8_INLINE static PreParserIdentifier NullIdentifier() { return PreParserIdentifier::Null(); @@ -1576,19 +1477,19 @@ class PreParser : public ParserBase<PreParser> { V8_INLINE static PreParserExpression NullExpression() { return PreParserExpression::Null(); } + V8_INLINE static PreParserExpression FailureExpression() { + return PreParserExpression::Failure(); + } V8_INLINE static PreParserExpression NullLiteralProperty() { return PreParserExpression::Null(); } - V8_INLINE static PreParserExpressionList NullExpressionList() { - return PreParserExpressionList::Null(); - } - V8_INLINE static PreParserStatementList NullStatementList() { return PreParserStatementList::Null(); } V8_INLINE static PreParserStatement NullStatement() { return PreParserStatement::Null(); } + V8_INLINE static PreParserBlock NullBlock() { return PreParserBlock::Null(); } template <typename T> V8_INLINE static bool IsNull(T subject) { @@ -1611,38 +1512,32 @@ class PreParser : public ParserBase<PreParser> { } V8_INLINE PreParserExpression ThisExpression(int pos = kNoSourcePosition) { - if (track_unresolved_variables_) { - scope()->NewUnresolved(factory()->ast_node_factory(), - ast_value_factory()->this_string(), pos, - THIS_VARIABLE); - } + scope()->NewUnresolved(factory()->ast_node_factory(), + ast_value_factory()->this_string(), pos, + THIS_VARIABLE); return PreParserExpression::This(); } V8_INLINE PreParserExpression NewSuperPropertyReference(int pos) { - if (track_unresolved_variables_) { - scope()->NewUnresolved(factory()->ast_node_factory(), - ast_value_factory()->this_function_string(), pos, - NORMAL_VARIABLE); - scope()->NewUnresolved(factory()->ast_node_factory(), - ast_value_factory()->this_string(), pos, - THIS_VARIABLE); - } + scope()->NewUnresolved(factory()->ast_node_factory(), + ast_value_factory()->this_function_string(), pos, + NORMAL_VARIABLE); + scope()->NewUnresolved(factory()->ast_node_factory(), + ast_value_factory()->this_string(), pos, + THIS_VARIABLE); return PreParserExpression::Default(); } V8_INLINE PreParserExpression NewSuperCallReference(int pos) { - if (track_unresolved_variables_) { - scope()->NewUnresolved(factory()->ast_node_factory(), - ast_value_factory()->this_function_string(), pos, - NORMAL_VARIABLE); - scope()->NewUnresolved(factory()->ast_node_factory(), - ast_value_factory()->new_target_string(), pos, - NORMAL_VARIABLE); - scope()->NewUnresolved(factory()->ast_node_factory(), - ast_value_factory()->this_string(), pos, - THIS_VARIABLE); - } + scope()->NewUnresolved(factory()->ast_node_factory(), + ast_value_factory()->this_function_string(), pos, + NORMAL_VARIABLE); + scope()->NewUnresolved(factory()->ast_node_factory(), + ast_value_factory()->new_target_string(), pos, + NORMAL_VARIABLE); + scope()->NewUnresolved(factory()->ast_node_factory(), + ast_value_factory()->this_string(), pos, + THIS_VARIABLE); return PreParserExpression::SuperCallReference(); } @@ -1656,30 +1551,26 @@ class PreParser : public ParserBase<PreParser> { V8_INLINE PreParserExpression ExpressionFromLiteral(Token::Value token, int pos) { - return PreParserExpression::Default(); + if (token != Token::STRING) return PreParserExpression::Default(); + return PreParserExpression::StringLiteral(); } PreParserExpression ExpressionFromIdentifier( const PreParserIdentifier& name, int start_position, - InferName infer = InferName::kYes); - - V8_INLINE PreParserExpression ExpressionFromString(int pos) { - if (scanner()->IsUseStrict()) { - return PreParserExpression::UseStrictStringLiteral(); + InferName infer = InferName::kYes) { + if (name.string_ != nullptr) { + expression_scope()->NewVariable(name.string_, start_position); } - return PreParserExpression::StringLiteral(); - } - - V8_INLINE PreParserExpressionList NewExpressionList(int size) const { - return PreParserExpressionList(); + return PreParserExpression::FromIdentifier(name); } - V8_INLINE PreParserExpressionList NewObjectPropertyList(int size) const { - return PreParserExpressionList(); + V8_INLINE Variable* DeclareCatchVariableName( + Scope* scope, const PreParserIdentifier& identifier) { + return scope->DeclareCatchVariableName(identifier.string_); } - V8_INLINE PreParserExpressionList NewClassPropertyList(int size) const { - return PreParserExpressionList(); + V8_INLINE PreParserPropertyList NewClassPropertyList(int size) const { + return PreParserPropertyList(); } V8_INLINE PreParserStatementList NewStatementList(int size) const { @@ -1688,7 +1579,7 @@ class PreParser : public ParserBase<PreParser> { V8_INLINE PreParserExpression NewV8Intrinsic(const PreParserIdentifier& name, - const PreParserExpressionList& arguments, int pos, bool* ok) { + const PreParserExpressionList& arguments, int pos) { return PreParserExpression::Default(); } @@ -1698,74 +1589,28 @@ class PreParser : public ParserBase<PreParser> { } V8_INLINE void AddFormalParameter(PreParserFormalParameters* parameters, - const PreParserExpression& pattern, + PreParserExpression& pattern, const PreParserExpression& initializer, int initializer_end_position, bool is_rest) { - if (track_unresolved_variables_) { - DCHECK(FLAG_lazy_inner_functions); - parameters->params.Add(new (zone()) PreParserFormalParameters::Parameter( - pattern.variables_, is_rest)); - } + DeclarationScope* scope = parameters->scope; + scope->RecordParameter(is_rest); parameters->UpdateArityAndFunctionLength(!initializer.IsNull(), is_rest); } V8_INLINE void DeclareFormalParameters( - DeclarationScope* scope, - 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_->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; - if (parameter->variables_ != nullptr) { - for (auto variable : (*parameter->variables_)) { - // We declare the parameter name for all names, but only create a - // parameter entry for the first one. - scope->DeclareParameterName(variable->raw_name(), - parameter->is_rest, ast_value_factory(), - true, add_parameter); - add_parameter = false; - } - } - if (add_parameter) { - // No names were declared; declare a dummy one here to up the - // parameter count. - DCHECK(!is_simple); - scope->DeclareParameterName(ast_value_factory()->empty_string(), - parameter->is_rest, ast_value_factory(), - false, add_parameter); - } - } - } + const PreParserFormalParameters* parameters) { + if (!parameters->is_simple) parameters->scope->SetHasNonSimpleParameters(); } V8_INLINE void DeclareArrowFunctionFormalParameters( PreParserFormalParameters* parameters, const PreParserExpression& params, - const Scanner::Location& params_loc, Scanner::Location* duplicate_loc, - bool* ok) { - // TODO(wingo): Detect duplicated identifiers in paramlists. Detect - // parameter lists that are too long. - if (track_unresolved_variables_) { - DCHECK(FLAG_lazy_inner_functions); - if (params.variables_ != nullptr) { - for (auto variable : *params.variables_) { - parameters->scope->DeclareVariableName(variable->raw_name(), - VariableMode::kVar); - } - } - } + const Scanner::Location& params_loc) { } V8_INLINE PreParserExpression ExpressionListToExpression(const PreParserExpressionList& args) { - return PreParserExpression::Default(args.variables_); + return PreParserExpression::Default(); } V8_INLINE void SetFunctionNameFromPropertyName( @@ -1775,11 +1620,6 @@ class PreParser : public ParserBase<PreParser> { const PreParserExpression& value, const PreParserExpression& identifier) { } - V8_INLINE ZoneList<typename ExpressionClassifier::Error>* - GetReportedErrorList() const { - return function_state_->GetReportedErrorList(); - } - V8_INLINE void CountUsage(v8::Isolate::UseCounterFeature feature) { if (use_counts_ != nullptr) ++use_counts_[feature]; } @@ -1797,10 +1637,9 @@ class PreParser : public ParserBase<PreParser> { // Preparser's private field members. int* use_counts_; - bool track_unresolved_variables_; PreParserLogger log_; - PreParsedScopeDataBuilder* preparsed_scope_data_builder_; + PreparseDataBuilder* preparse_data_builder_; }; PreParserExpression PreParser::SpreadCall(const PreParserExpression& function, diff --git a/deps/v8/src/parsing/rewriter.cc b/deps/v8/src/parsing/rewriter.cc index 151244f692..5ba7b3ba51 100644 --- a/deps/v8/src/parsing/rewriter.cc +++ b/deps/v8/src/parsing/rewriter.cc @@ -18,13 +18,13 @@ class Processor final : public AstVisitor<Processor> { Processor(uintptr_t stack_limit, DeclarationScope* closure_scope, Variable* result, AstValueFactory* ast_value_factory) : result_(result), - result_assigned_(false), replacement_(nullptr), - is_set_(false), - breakable_(false), zone_(ast_value_factory->zone()), closure_scope_(closure_scope), - factory_(ast_value_factory, ast_value_factory->zone()) { + factory_(ast_value_factory, ast_value_factory->zone()), + result_assigned_(false), + is_set_(false), + breakable_(false) { DCHECK_EQ(closure_scope, closure_scope->GetClosureScope()); InitializeAstVisitor(stack_limit); } @@ -32,13 +32,13 @@ class Processor final : public AstVisitor<Processor> { Processor(Parser* parser, DeclarationScope* closure_scope, Variable* result, AstValueFactory* ast_value_factory) : result_(result), - result_assigned_(false), replacement_(nullptr), - is_set_(false), - breakable_(false), zone_(ast_value_factory->zone()), closure_scope_(closure_scope), - factory_(ast_value_factory, zone_) { + factory_(ast_value_factory, zone_), + result_assigned_(false), + is_set_(false), + breakable_(false) { DCHECK_EQ(closure_scope, closure_scope->GetClosureScope()); InitializeAstVisitor(parser->stack_limit()); } @@ -64,24 +64,10 @@ class Processor final : public AstVisitor<Processor> { private: Variable* result_; - // We are not tracking result usage via the result_'s use - // counts (we leave the accurate computation to the - // usage analyzer). Instead we simple remember if - // there was ever an assignment to result_. - bool result_assigned_; - // When visiting a node, we "return" a replacement for that node in // [replacement_]. In many cases this will just be the original node. Statement* replacement_; - // To avoid storing to .result all the time, we eliminate some of - // the stores by keeping track of whether or not we're sure .result - // will be overwritten anyway. This is a bit more tricky than what I - // was hoping for. - bool is_set_; - - bool breakable_; - class BreakableScope final { public: explicit BreakableScope(Processor* processor, bool breakable = true) @@ -108,6 +94,20 @@ class Processor final : public AstVisitor<Processor> { void VisitIterationStatement(IterationStatement* stmt); DEFINE_AST_VISITOR_SUBCLASS_MEMBERS(); + + // We are not tracking result usage via the result_'s use + // counts (we leave the accurate computation to the + // usage analyzer). Instead we simple remember if + // there was ever an assignment to result_. + bool result_assigned_; + + // To avoid storing to .result all the time, we eliminate some of + // the stores by keeping track of whether or not we're sure .result + // will be overwritten anyway. This is a bit more tricky than what I + // was hoping for. + bool is_set_; + + bool breakable_; }; @@ -337,8 +337,8 @@ void Processor::VisitDebuggerStatement(DebuggerStatement* node) { replacement_ = node; } -void Processor::VisitInitializeClassFieldsStatement( - InitializeClassFieldsStatement* node) { +void Processor::VisitInitializeClassMembersStatement( + InitializeClassMembersStatement* node) { replacement_ = node; } @@ -405,36 +405,5 @@ bool Rewriter::Rewrite(ParseInfo* info) { return true; } -bool Rewriter::Rewrite(Parser* parser, DeclarationScope* closure_scope, - DoExpression* expr, AstValueFactory* factory) { - DisallowHeapAllocation no_allocation; - DisallowHandleAllocation no_handles; - DisallowHandleDereference no_deref; - - Block* block = expr->block(); - DCHECK_EQ(closure_scope, closure_scope->GetClosureScope()); - DCHECK(block->scope() == nullptr || - block->scope()->GetClosureScope() == closure_scope); - ZonePtrList<Statement>* body = block->statements(); - VariableProxy* result = expr->result(); - Variable* result_var = result->var(); - - if (!body->is_empty()) { - Processor processor(parser, closure_scope, result_var, factory); - processor.Process(body); - if (processor.HasStackOverflow()) return false; - - if (!processor.result_assigned()) { - AstNodeFactory* node_factory = processor.factory(); - Expression* undef = node_factory->NewUndefinedLiteral(kNoSourcePosition); - Statement* completion = node_factory->NewExpressionStatement( - processor.SetResult(undef), expr->position()); - body->Add(completion, factory->zone()); - } - } - return true; -} - - } // namespace internal } // namespace v8 diff --git a/deps/v8/src/parsing/rewriter.h b/deps/v8/src/parsing/rewriter.h index d0b1191a9f..62873b2980 100644 --- a/deps/v8/src/parsing/rewriter.h +++ b/deps/v8/src/parsing/rewriter.h @@ -9,7 +9,6 @@ namespace v8 { namespace internal { class AstValueFactory; -class DoExpression; class Isolate; class ParseInfo; class Parser; @@ -25,15 +24,6 @@ class Rewriter { // Assumes code has been parsed and scopes have been analyzed. Mutates the // AST, so the AST should not continue to be used in the case of failure. static bool Rewrite(ParseInfo* info); - - // Rewrite a list of statements, using the same rules as a top-level program, - // to ensure identical behaviour of completion result. The temporary is added - // to the closure scope of the do-expression, which matches the closure scope - // of the outer scope (the do-expression itself runs in a block scope, not a - // closure scope). This closure scope needs to be passed in since the - // do-expression could have dropped its own block scope. - static bool Rewrite(Parser* parser, DeclarationScope* closure_scope, - DoExpression* expr, AstValueFactory* factory); }; diff --git a/deps/v8/src/parsing/scanner-character-streams.cc b/deps/v8/src/parsing/scanner-character-streams.cc index 8472e9f4fc..32dcaacbf5 100644 --- a/deps/v8/src/parsing/scanner-character-streams.cc +++ b/deps/v8/src/parsing/scanner-character-streams.cc @@ -20,8 +20,8 @@ namespace internal { class ScopedExternalStringLock { public: - explicit ScopedExternalStringLock(ExternalString* string) { - DCHECK(string); + explicit ScopedExternalStringLock(ExternalString string) { + DCHECK(!string.is_null()); if (string->IsExternalOneByteString()) { resource_ = ExternalOneByteString::cast(string)->resource(); } else { @@ -33,7 +33,7 @@ class ScopedExternalStringLock { } // Copying a lock increases the locking depth. - ScopedExternalStringLock(const ScopedExternalStringLock& other) + ScopedExternalStringLock(const ScopedExternalStringLock& other) V8_NOEXCEPT : resource_(other.resource_) { resource_->Lock(); } @@ -84,13 +84,16 @@ class OnHeapStream { OnHeapStream(Handle<String> string, size_t start_offset, size_t end) : string_(string), start_offset_(start_offset), length_(end) {} - OnHeapStream(const OnHeapStream& other) : start_offset_(0), length_(0) { + OnHeapStream(const OnHeapStream&) V8_NOEXCEPT : 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_]}; + // The no_gc argument is only here because of the templated way this class + // is used along with other implementations that require V8 heap access. + Range<Char> GetDataAt(size_t pos, RuntimeCallStats* stats, + DisallowHeapAllocation* no_gc) { + return {&string_->GetChars(*no_gc)[start_offset_ + Min(length_, pos)], + &string_->GetChars(*no_gc)[start_offset_ + length_]}; } static const bool kCanBeCloned = false; @@ -109,16 +112,21 @@ class ExternalStringStream { typedef typename CharTraits<Char>::ExternalString ExternalString; public: - ExternalStringStream(ExternalString* string, size_t start_offset, + 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_) {} + ExternalStringStream(const ExternalStringStream& other) V8_NOEXCEPT + : lock_(other.lock_), + data_(other.data_), + length_(other.length_) {} - Range<Char> GetDataAt(size_t pos, RuntimeCallStats* stats) { + // The no_gc argument is only here because of the templated way this class + // is used along with other implementations that require V8 heap access. + Range<Char> GetDataAt(size_t pos, RuntimeCallStats* stats, + DisallowHeapAllocation* no_gc = nullptr) { return {&data_[Min(length_, pos)], &data_[length_]}; } @@ -137,7 +145,10 @@ class TestingStream { public: TestingStream(const Char* data, size_t length) : data_(data), length_(length) {} - Range<Char> GetDataAt(size_t pos, RuntimeCallStats* stats) { + // The no_gc argument is only here because of the templated way this class + // is used along with other implementations that require V8 heap access. + Range<Char> GetDataAt(size_t pos, RuntimeCallStats* stats, + DisallowHeapAllocation* no_gc = nullptr) { return {&data_[Min(length_, pos)], &data_[length_]}; } @@ -156,12 +167,15 @@ class ChunkedStream { explicit ChunkedStream(ScriptCompiler::ExternalSourceStream* source) : source_(source) {} - ChunkedStream(const ChunkedStream& other) { + ChunkedStream(const ChunkedStream&) V8_NOEXCEPT { // TODO(rmcilroy): Implement cloning for chunked streams. UNREACHABLE(); } - Range<Char> GetDataAt(size_t pos, RuntimeCallStats* stats) { + // The no_gc argument is only here because of the templated way this class + // is used along with other implementations that require V8 heap access. + Range<Char> GetDataAt(size_t pos, RuntimeCallStats* stats, + DisallowHeapAllocation* no_gc = nullptr) { Chunk chunk = FindChunk(pos, stats); size_t buffer_end = chunk.length; size_t buffer_pos = Min(buffer_end, pos - chunk.position); @@ -257,8 +271,9 @@ class BufferedCharacterStream : public Utf16CharacterStream { buffer_start_ = &buffer_[0]; buffer_cursor_ = buffer_start_; + DisallowHeapAllocation no_gc; Range<uint8_t> range = - byte_stream_.GetDataAt(position, runtime_call_stats()); + byte_stream_.GetDataAt(position, runtime_call_stats(), &no_gc); if (range.length() == 0) { buffer_end_ = buffer_start_; return false; @@ -310,8 +325,9 @@ class UnbufferedCharacterStream : public Utf16CharacterStream { bool ReadBlock() final { size_t position = pos(); buffer_pos_ = position; + DisallowHeapAllocation no_gc; Range<uint16_t> range = - byte_stream_.GetDataAt(position, runtime_call_stats()); + byte_stream_.GetDataAt(position, runtime_call_stats(), &no_gc); buffer_start_ = range.start; buffer_end_ = range.end; buffer_cursor_ = buffer_start_; @@ -356,7 +372,9 @@ class RelocatingCharacterStream } void UpdateBufferPointers() { - Range<uint16_t> range = byte_stream_.GetDataAt(0, runtime_call_stats()); + DisallowHeapAllocation no_gc; + Range<uint16_t> range = + byte_stream_.GetDataAt(0, runtime_call_stats(), &no_gc); if (range.start != buffer_start_) { buffer_cursor_ = (buffer_cursor_ - buffer_start_) + range.start; buffer_start_ = range.start; @@ -707,9 +725,9 @@ Utf16CharacterStream* ScannerStream::For(Isolate* isolate, Handle<String> data, DCHECK_LE(end_pos, data->length()); size_t start_offset = 0; if (data->IsSlicedString()) { - SlicedString* string = SlicedString::cast(*data); + SlicedString string = SlicedString::cast(*data); start_offset = string->offset(); - String* parent = string->parent(); + String parent = string->parent(); if (parent->IsThinString()) parent = ThinString::cast(parent)->actual(); data = handle(parent, isolate); } else { diff --git a/deps/v8/src/parsing/scanner-inl.h b/deps/v8/src/parsing/scanner-inl.h index 9647957062..1e2cf9e447 100644 --- a/deps/v8/src/parsing/scanner-inl.h +++ b/deps/v8/src/parsing/scanner-inl.h @@ -6,159 +6,19 @@ #define V8_PARSING_SCANNER_INL_H_ #include "src/char-predicates-inl.h" +#include "src/parsing/keywords-gen.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') \ @@ -166,7 +26,6 @@ static const Token::Value one_char_tokens[] = { 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) \ @@ -176,17 +35,13 @@ static const Token::Value one_char_tokens[] = { 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) \ @@ -196,36 +51,26 @@ static const Token::Value one_char_tokens[] = { 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) \ @@ -233,124 +78,235 @@ static const Token::Value one_char_tokens[] = { 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) + KEYWORD("yield", Token::YIELD) + +constexpr bool IsKeywordStart(char c) { +#define KEYWORD_GROUP_CHECK(ch) c == ch || +#define KEYWORD_CHECK(keyword, token) + return KEYWORDS(KEYWORD_GROUP_CHECK, KEYWORD_CHECK) /* || */ false; +#undef KEYWORD_GROUP_CHECK +#undef KEYWORD_CHECK +} 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; \ - } \ - } + return PerfectKeywordHash::GetToken(reinterpret_cast<const char*>(input), + input_length); +} + +// Recursive constexpr template magic to check if a character is in a given +// string. +template <int N> +constexpr bool IsInString(const char (&s)[N], char c, size_t i = 0) { + return i >= N ? false : s[i] == c ? true : IsInString(s, c, i + 1); +} + +inline constexpr bool CanBeKeywordCharacter(char c) { + return IsInString( +#define KEYWORD_GROUP_CASE(ch) // Nothing +#define KEYWORD(keyword, token) keyword + // Use C string literal concatenation ("a" "b" becomes "ab") to build one + // giant string containing all the keywords. KEYWORDS(KEYWORD_GROUP_CASE, KEYWORD) - } - return Token::IDENTIFIER; -#undef KEYWORDS #undef KEYWORD #undef KEYWORD_GROUP_CASE + , + c); +} + +// Make sure tokens are stored as a single byte. +STATIC_ASSERT(sizeof(Token::Value) == 1); + +// Get the shortest token that this character starts, the token may change +// depending on subsequent characters. +constexpr Token::Value GetOneCharToken(char c) { + // clang-format off + return + c == '(' ? Token::LPAREN : + c == ')' ? Token::RPAREN : + c == '{' ? Token::LBRACE : + c == '}' ? Token::RBRACE : + c == '[' ? Token::LBRACK : + c == ']' ? Token::RBRACK : + c == '?' ? Token::CONDITIONAL : + c == ':' ? Token::COLON : + c == ';' ? Token::SEMICOLON : + c == ',' ? Token::COMMA : + c == '.' ? Token::PERIOD : + c == '|' ? Token::BIT_OR : + c == '&' ? Token::BIT_AND : + c == '^' ? Token::BIT_XOR : + c == '~' ? Token::BIT_NOT : + c == '!' ? Token::NOT : + c == '<' ? Token::LT : + c == '>' ? Token::GT : + c == '%' ? Token::MOD : + c == '=' ? Token::ASSIGN : + c == '+' ? Token::ADD : + c == '-' ? Token::SUB : + c == '*' ? Token::MUL : + c == '/' ? Token::DIV : + c == '#' ? Token::PRIVATE_NAME : + c == '"' ? Token::STRING : + c == '\'' ? Token::STRING : + c == '`' ? Token::TEMPLATE_SPAN : + c == '\\' ? Token::IDENTIFIER : + // Whitespace or line terminator + c == ' ' ? Token::WHITESPACE : + c == '\t' ? Token::WHITESPACE : + c == '\v' ? Token::WHITESPACE : + c == '\f' ? Token::WHITESPACE : + c == '\r' ? Token::WHITESPACE : + c == '\n' ? Token::WHITESPACE : + // IsDecimalDigit must be tested before IsAsciiIdentifier + IsDecimalDigit(c) ? Token::NUMBER : + IsAsciiIdentifier(c) ? Token::IDENTIFIER : + Token::ILLEGAL; + // clang-format on } +// Table of one-character tokens, by character (0x00..0x7F only). +static const constexpr Token::Value one_char_tokens[128] = { +#define CALL_GET_SCAN_FLAGS(N) GetOneCharToken(N), + INT_0_TO_127_LIST(CALL_GET_SCAN_FLAGS) +#undef CALL_GET_SCAN_FLAGS +}; + +#undef KEYWORDS + V8_INLINE Token::Value Scanner::ScanIdentifierOrKeyword() { - LiteralScope literal(this); - return ScanIdentifierOrKeywordInner(&literal); + next().literal_chars.Start(); + return ScanIdentifierOrKeywordInner(); +} + +// Character flags for the fast path of scanning a keyword or identifier token. +enum class ScanFlags : uint8_t { + kTerminatesLiteral = 1 << 0, + // "Cannot" rather than "can" so that this flag can be ORed together across + // multiple characters. + kCannotBeKeyword = 1 << 1, + kCannotBeKeywordStart = 1 << 2, + kStringTerminator = 1 << 3, + kNeedsSlowPath = 1 << 4, +}; +constexpr uint8_t GetScanFlags(char c) { + return + // Keywords are all lowercase and only contain letters. + // Note that non-identifier characters do not set this flag, so + // that it plays well with kTerminatesLiteral. + (IsAsciiIdentifier(c) && !CanBeKeywordCharacter(c) + ? static_cast<uint8_t>(ScanFlags::kCannotBeKeyword) + : 0) | + (IsKeywordStart(c) + ? 0 + : static_cast<uint8_t>(ScanFlags::kCannotBeKeywordStart)) | + // Anything that isn't an identifier character will terminate the + // literal, or at least terminates the literal fast path processing + // (like an escape). + (!IsAsciiIdentifier(c) + ? static_cast<uint8_t>(ScanFlags::kTerminatesLiteral) + : 0) | + // Possible string termination characters. + ((c == '\'' || c == '"' || c == '\n' || c == '\r' || c == '\\') + ? static_cast<uint8_t>(ScanFlags::kStringTerminator) + : 0) | + // Escapes are processed on the slow path. + (c == '\\' ? static_cast<uint8_t>(ScanFlags::kNeedsSlowPath) : 0); +} +inline bool TerminatesLiteral(uint8_t scan_flags) { + return (scan_flags & static_cast<uint8_t>(ScanFlags::kTerminatesLiteral)); +} +inline bool CanBeKeyword(uint8_t scan_flags) { + return !(scan_flags & static_cast<uint8_t>(ScanFlags::kCannotBeKeyword)); } +inline bool NeedsSlowPath(uint8_t scan_flags) { + return (scan_flags & static_cast<uint8_t>(ScanFlags::kNeedsSlowPath)); +} +inline bool MayTerminateString(uint8_t scan_flags) { + return (scan_flags & static_cast<uint8_t>(ScanFlags::kStringTerminator)); +} +// Table of precomputed scan flags for the 128 ASCII characters, for branchless +// flag calculation during the scan. +static constexpr const uint8_t character_scan_flags[128] = { +#define CALL_GET_SCAN_FLAGS(N) GetScanFlags(N), + INT_0_TO_127_LIST(CALL_GET_SCAN_FLAGS) +#undef CALL_GET_SCAN_FLAGS +}; -V8_INLINE Token::Value Scanner::ScanIdentifierOrKeywordInner( - LiteralScope* literal) { - DCHECK(unicode_cache_->IsIdentifierStart(c0_)); +inline bool CharCanBeKeyword(uc32 c) { + return static_cast<uint32_t>(c) < arraysize(character_scan_flags) && + CanBeKeyword(character_scan_flags[c]); +} + +V8_INLINE Token::Value Scanner::ScanIdentifierOrKeywordInner() { + DCHECK(IsIdentifierStart(c0_)); bool escaped = false; - if (IsInRange(c0_, 'a', 'z') || c0_ == '_') { - do { + bool can_be_keyword = true; + + STATIC_ASSERT(arraysize(character_scan_flags) == kMaxAscii + 1); + if (V8_LIKELY(static_cast<uint32_t>(c0_) <= kMaxAscii)) { + if (V8_LIKELY(c0_ != '\\')) { + uint8_t scan_flags = character_scan_flags[c0_]; + DCHECK(!TerminatesLiteral(scan_flags)); + STATIC_ASSERT(static_cast<uint8_t>(ScanFlags::kCannotBeKeywordStart) == + static_cast<uint8_t>(ScanFlags::kCannotBeKeyword) << 1); + scan_flags >>= 1; + // Make sure the shifting above doesn't set NeedsSlowPath. Otherwise we'll + // fall into the slow path after scanning the identifier. + DCHECK(!NeedsSlowPath(scan_flags)); 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; + AdvanceUntil([this, &scan_flags](uc32 c0) { + if (V8_UNLIKELY(static_cast<uint32_t>(c0) > kMaxAscii)) { + // A non-ascii character means we need to drop through to the slow + // path. + // TODO(leszeks): This would be most efficient as a goto to the slow + // path, check codegen and maybe use a bool instead. + scan_flags |= static_cast<uint8_t>(ScanFlags::kNeedsSlowPath); + return true; + } + uint8_t char_flags = character_scan_flags[c0]; + scan_flags |= char_flags; + if (TerminatesLiteral(char_flags)) { + return true; + } else { + AddLiteralChar(static_cast<char>(c0)); + return false; + } + }); + + if (V8_LIKELY(!NeedsSlowPath(scan_flags))) { + if (!CanBeKeyword(scan_flags)) return Token::IDENTIFIER; + // Could be a keyword or identifier. + Vector<const uint8_t> chars = next().literal_chars.one_byte_literal(); + return KeywordOrIdentifierToken(chars.start(), chars.length()); } - } 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; + can_be_keyword = CanBeKeyword(scan_flags); + } else { + // Special case for escapes at the start of an identifier. + escaped = true; + uc32 c = ScanIdentifierUnicodeEscape(); + DCHECK(!IsIdentifierStart(-1)); + if (c == '\\' || !IsIdentifierStart(c)) { + return Token::ILLEGAL; + } + AddLiteralChar(c); + can_be_keyword = CharCanBeKeyword(c); } - AddLiteralChar(c); } - return ScanIdentifierOrKeywordInnerSlow(literal, escaped); + return ScanIdentifierOrKeywordInnerSlow(escaped, can_be_keyword); } V8_INLINE Token::Value Scanner::SkipWhiteSpace() { int start_position = source_pos(); // We won't skip behind the end of input. - DCHECK(!unicode_cache_->IsWhiteSpaceOrLineTerminator(kEndOfInput)); + DCHECK(!IsWhiteSpaceOrLineTerminator(kEndOfInput)); // Advance as long as character is a WhiteSpace or LineTerminator. - while (unicode_cache_->IsWhiteSpaceOrLineTerminator(c0_)) { + while (IsWhiteSpaceOrLineTerminator(c0_)) { if (!next().after_line_terminator && unibrow::IsLineTerminator(c0_)) { next().after_line_terminator = true; } @@ -371,178 +327,194 @@ V8_INLINE Token::Value Scanner::ScanSingleToken() { 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_ == '>') { - // >> >>= >>> >>>= + if (V8_LIKELY(static_cast<unsigned>(c0_) <= kMaxAscii)) { + token = one_char_tokens[c0_]; + + switch (token) { + case Token::LPAREN: + case Token::RPAREN: + case Token::LBRACE: + case Token::RBRACE: + case Token::LBRACK: + case Token::RBRACK: + case Token::CONDITIONAL: + case Token::COLON: + case Token::SEMICOLON: + case Token::COMMA: + case Token::BIT_NOT: + case Token::ILLEGAL: + // One character tokens. + return Select(token); + + case Token::STRING: + return ScanString(); + + case Token::LT: + // < <= << <<= <!-- 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(); + if (c0_ == '=') return Select(Token::LTE); + if (c0_ == '<') return Select('=', Token::ASSIGN_SHL, Token::SHL); + if (c0_ == '!') { + token = ScanHtmlComment(); 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 == '@') { + return Token::LT; + + case Token::GT: + // > >= >> >>= >>> >>>= + 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 Token::ASSIGN: + // = == === => + Advance(); + if (c0_ == '=') return Select('=', Token::EQ_STRICT, Token::EQ); + if (c0_ == '>') return Select(Token::ARROW); + return Token::ASSIGN; + + case Token::NOT: + // ! != !== + Advance(); + if (c0_ == '=') return Select('=', Token::NE_STRICT, Token::NE); + return Token::NOT; + + case Token::ADD: + // + ++ += + Advance(); + if (c0_ == '+') return Select(Token::INC); + if (c0_ == '=') return Select(Token::ASSIGN_ADD); + return Token::ADD; + + case Token::SUB: + // - -- --> -= + Advance(); + if (c0_ == '-') { Advance(); - token = SkipSourceURLComment(); + 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 Token::MUL: + // * *= + Advance(); + if (c0_ == '*') return Select('=', Token::ASSIGN_EXP, Token::EXP); + if (c0_ == '=') return Select(Token::ASSIGN_MUL); + return Token::MUL; + + case Token::MOD: + // % %= + return Select('=', Token::ASSIGN_MOD, Token::MOD); + + case Token::DIV: + // / // /* /= + Advance(); + if (c0_ == '/') { + uc32 c = Peek(); + if (c == '#' || c == '@') { + Advance(); + Advance(); + token = SkipSourceURLComment(); + continue; + } + token = SkipSingleLineComment(); 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; + if (c0_ == '*') { + token = SkipMultiLineComment(); + continue; } - } - return Token::PERIOD; + if (c0_ == '=') return Select(Token::ASSIGN_DIV); + return Token::DIV; + + case Token::BIT_AND: + // & && &= + Advance(); + if (c0_ == '&') return Select(Token::AND); + if (c0_ == '=') return Select(Token::ASSIGN_BIT_AND); + return Token::BIT_AND; - case '`': - Advance(); - return ScanTemplateSpan(); + case Token::BIT_OR: + // | || |= + Advance(); + if (c0_ == '|') return Select(Token::OR); + if (c0_ == '=') return Select(Token::ASSIGN_BIT_OR); + return Token::BIT_OR; + + case Token::BIT_XOR: + // ^ ^= + return Select('=', Token::ASSIGN_BIT_XOR, Token::BIT_XOR); - case '#': - return ScanPrivateName(); + case Token::PERIOD: + // . Number + Advance(); + if (IsDecimalDigit(c0_)) return ScanNumber(true); + if (c0_ == '.') { + if (Peek() == '.') { + Advance(); + Advance(); + return Token::ELLIPSIS; + } + } + return Token::PERIOD; - default: - if (unicode_cache_->IsIdentifierStart(c0_) || - (CombineSurrogatePair() && - unicode_cache_->IsIdentifierStart(c0_))) { - Token::Value token = ScanIdentifierOrKeyword(); - if (!Token::IsContextualKeyword(token)) return token; + case Token::TEMPLATE_SPAN: + Advance(); + return ScanTemplateSpan(); - next().contextual_token = token; - return Token::IDENTIFIER; - } - if (IsDecimalDigit(c0_)) return ScanNumber(false); - if (c0_ == kEndOfInput) return Token::EOS; - token = SkipWhiteSpace(); - continue; + case Token::PRIVATE_NAME: + return ScanPrivateName(); + + case Token::WHITESPACE: + token = SkipWhiteSpace(); + continue; + + case Token::NUMBER: + return ScanNumber(false); + + case Token::IDENTIFIER: + return ScanIdentifierOrKeyword(); + + default: + UNREACHABLE(); + } } + + if (IsIdentifierStart(c0_) || + (CombineSurrogatePair() && IsIdentifierStart(c0_))) { + return ScanIdentifierOrKeyword(); + } + if (c0_ == kEndOfInput) { + return source_->has_parser_error() ? Token::ILLEGAL : Token::EOS; + } + token = SkipWhiteSpace(); + // 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; +void Scanner::Scan(TokenDesc* next_desc) { + DCHECK_EQ(next_desc, &next()); - next().token = ScanSingleToken(); - next().location.end_pos = source_pos(); + next_desc->token = ScanSingleToken(); + DCHECK_IMPLIES(has_parser_error(), next_desc->token == Token::ILLEGAL); + next_desc->location.end_pos = source_pos(); #ifdef DEBUG SanityCheckTokenDesc(current()); @@ -551,6 +523,8 @@ void Scanner::Scan() { #endif } +void Scanner::Scan() { Scan(next_); } + } // namespace internal } // namespace v8 diff --git a/deps/v8/src/parsing/scanner.cc b/deps/v8/src/parsing/scanner.cc index 525b1bc681..43fc589e88 100644 --- a/deps/v8/src/parsing/scanner.cc +++ b/deps/v8/src/parsing/scanner.cc @@ -13,16 +13,15 @@ #include "src/ast/ast-value-factory.h" #include "src/conversions-inl.h" #include "src/objects/bigint.h" -#include "src/parsing/duplicate-finder.h" // For Scanner::FindSymbol #include "src/parsing/scanner-inl.h" +#include "src/zone/zone.h" namespace v8 { namespace internal { class Scanner::ErrorState { public: - ErrorState(MessageTemplate::Template* message_stack, - Scanner::Location* location_stack) + ErrorState(MessageTemplate* message_stack, Scanner::Location* location_stack) : message_stack_(message_stack), old_message_(*message_stack), location_stack_(location_stack), @@ -49,8 +48,8 @@ class Scanner::ErrorState { } private: - MessageTemplate::Template* const message_stack_; - MessageTemplate::Template const old_message_; + MessageTemplate* const message_stack_; + MessageTemplate const old_message_; Scanner::Location* const location_stack_; Scanner::Location const old_location_; }; @@ -59,7 +58,6 @@ class Scanner::ErrorState { // Scanner::LiteralBuffer Handle<String> Scanner::LiteralBuffer::Internalize(Isolate* isolate) const { - DCHECK(is_used_); if (is_one_byte()) { return isolate->factory()->InternalizeOneByteString(one_byte_literal()); } @@ -67,20 +65,21 @@ Handle<String> Scanner::LiteralBuffer::Internalize(Isolate* isolate) const { } int Scanner::LiteralBuffer::NewCapacity(int min_capacity) { - int capacity = Max(min_capacity, backing_store_.length()); - int new_capacity = Min(capacity * kGrowthFactory, capacity + kMaxGrowth); - return new_capacity; + return min_capacity < (kMaxGrowth / (kGrowthFactor - 1)) + ? min_capacity * kGrowthFactor + : min_capacity + kMaxGrowth; } void Scanner::LiteralBuffer::ExpandBuffer() { - Vector<byte> new_store = Vector<byte>::New(NewCapacity(kInitialCapacity)); + int min_capacity = Max(kInitialCapacity, backing_store_.length()); + Vector<byte> new_store = Vector<byte>::New(NewCapacity(min_capacity)); MemCopy(new_store.start(), backing_store_.start(), position_); backing_store_.Dispose(); backing_store_ = new_store; } void Scanner::LiteralBuffer::ConvertToTwoByte() { - DCHECK(is_one_byte_); + DCHECK(is_one_byte()); Vector<byte> new_store; int new_content_size = position_ * kUC16Size; if (new_content_size >= backing_store_.length()) { @@ -104,7 +103,7 @@ void Scanner::LiteralBuffer::ConvertToTwoByte() { } void Scanner::LiteralBuffer::AddTwoByteChar(uc32 code_unit) { - DCHECK(!is_one_byte_); + DCHECK(!is_one_byte()); if (position_ >= backing_store_.length()) ExpandBuffer(); if (code_unit <= static_cast<uc32>(unibrow::Utf16::kMaxNonSurrogateCharCode)) { @@ -124,36 +123,23 @@ void Scanner::LiteralBuffer::AddTwoByteChar(uc32 code_unit) { // ---------------------------------------------------------------------------- // Scanner::BookmarkScope -const size_t Scanner::BookmarkScope::kBookmarkAtFirstPos = - std::numeric_limits<size_t>::max() - 2; const size_t Scanner::BookmarkScope::kNoBookmark = std::numeric_limits<size_t>::max() - 1; const size_t Scanner::BookmarkScope::kBookmarkWasApplied = std::numeric_limits<size_t>::max(); -void Scanner::BookmarkScope::Set() { +void Scanner::BookmarkScope::Set(size_t position) { DCHECK_EQ(bookmark_, kNoBookmark); - - // The first token is a bit special, since current_ will still be - // uninitialized. In this case, store kBookmarkAtFirstPos and special-case it - // when - // applying the bookmark. - DCHECK_IMPLIES(scanner_->current().token == Token::UNINITIALIZED, - scanner_->current().location.beg_pos == - scanner_->next().location.beg_pos); - bookmark_ = (scanner_->current().token == Token::UNINITIALIZED) - ? kBookmarkAtFirstPos - : scanner_->location().beg_pos; + bookmark_ = position; } void Scanner::BookmarkScope::Apply() { DCHECK(HasBeenSet()); // Caller hasn't called SetBookmark. - if (bookmark_ == kBookmarkAtFirstPos) { - scanner_->SeekNext(0); + if (had_parser_error_) { + scanner_->set_parser_error(); } else { + scanner_->reset_parser_error_flag(); scanner_->SeekNext(bookmark_); - scanner_->Next(); - DCHECK_EQ(scanner_->location().beg_pos, static_cast<int>(bookmark_)); } bookmark_ = kBookmarkWasApplied; } @@ -169,10 +155,8 @@ bool Scanner::BookmarkScope::HasBeenApplied() const { // ---------------------------------------------------------------------------- // Scanner -Scanner::Scanner(UnicodeCache* unicode_cache, Utf16CharacterStream* source, - bool is_module) - : unicode_cache_(unicode_cache), - source_(source), +Scanner::Scanner(Utf16CharacterStream* source, bool is_module) + : source_(source), found_html_comment_(false), allow_harmony_numeric_separator_(false), is_module_(is_module), @@ -241,13 +225,14 @@ Token::Value Scanner::Next() { // current_ as next_ and scan into it, leaving next_next_ uninitialized. if (V8_LIKELY(next_next().token == Token::UNINITIALIZED)) { next_ = previous; - next().after_line_terminator = false; - Scan(); + // User 'previous' instead of 'next_' because for some reason the compiler + // thinks 'next_' could be modified before the entry into Scan. + previous->after_line_terminator = false; + Scan(previous); } else { next_ = next_next_; next_next_ = previous; previous->token = Token::UNINITIALIZED; - previous->contextual_token = Token::UNINITIALIZED; DCHECK_NE(Token::UNINITIALIZED, current().token); } return current().token; @@ -300,42 +285,41 @@ Token::Value Scanner::SkipSourceURLComment() { void Scanner::TryToParseSourceURLComment() { // Magic comments are of the form: //[#@]\s<name>=\s*<value>\s*.* and this // function will just return if it cannot parse a magic comment. - DCHECK(!unicode_cache_->IsWhiteSpaceOrLineTerminator(kEndOfInput)); - if (!unicode_cache_->IsWhiteSpace(c0_)) return; + DCHECK(!IsWhiteSpaceOrLineTerminator(kEndOfInput)); + if (!IsWhiteSpace(c0_)) return; Advance(); LiteralBuffer name; name.Start(); - while (c0_ != kEndOfInput && - !unicode_cache_->IsWhiteSpaceOrLineTerminator(c0_) && c0_ != '=') { + while (c0_ != kEndOfInput && !IsWhiteSpaceOrLineTerminator(c0_) && + c0_ != '=') { name.AddChar(c0_); Advance(); } if (!name.is_one_byte()) return; Vector<const uint8_t> name_literal = name.one_byte_literal(); LiteralBuffer* value; - if (name_literal == STATIC_CHAR_VECTOR("sourceURL")) { + if (name_literal == StaticCharVector("sourceURL")) { value = &source_url_; - } else if (name_literal == STATIC_CHAR_VECTOR("sourceMappingURL")) { + } else if (name_literal == StaticCharVector("sourceMappingURL")) { value = &source_mapping_url_; } else { return; } if (c0_ != '=') return; - value->Drop(); value->Start(); Advance(); - while (unicode_cache_->IsWhiteSpace(c0_)) { + while (IsWhiteSpace(c0_)) { Advance(); } while (c0_ != kEndOfInput && !unibrow::IsLineTerminator(c0_)) { // Disallowed characters. if (c0_ == '"' || c0_ == '\'') { - value->Drop(); + value->Start(); return; } - if (unicode_cache_->IsWhiteSpace(c0_)) { + if (IsWhiteSpace(c0_)) { break; } value->AddChar(c0_); @@ -343,8 +327,8 @@ void Scanner::TryToParseSourceURLComment() { } // Allow whitespace at the end. while (c0_ != kEndOfInput && !unibrow::IsLineTerminator(c0_)) { - if (!unicode_cache_->IsWhiteSpace(c0_)) { - value->Drop(); + if (!IsWhiteSpace(c0_)) { + value->Start(); break; } Advance(); @@ -377,6 +361,13 @@ Token::Value Scanner::SkipMultiLineComment() { return Token::ILLEGAL; } +void Scanner::SkipHashBang() { + if (c0_ == '#' && Peek() == '!' && source_pos() == 0) { + SkipSingleLineComment(); + Scan(); + } +} + Token::Value Scanner::ScanHtmlComment() { // Check for <!-- comments. DCHECK_EQ(c0_, '!'); @@ -393,51 +384,20 @@ Token::Value Scanner::ScanHtmlComment() { #ifdef DEBUG void Scanner::SanityCheckTokenDesc(const TokenDesc& token) const { - // Most tokens should not have literal_chars or even raw_literal chars. - // The rules are: - // - UNINITIALIZED: we don't care. - // - TEMPLATE_*: need both literal + raw literal chars. - // - IDENTIFIERS, STRINGS, etc.: need a literal, but no raw literal. - // - all others: should have neither. - // Furthermore, only TEMPLATE_* tokens can have a - // invalid_template_escape_message. + // Only TEMPLATE_* tokens can have a invalid_template_escape_message. + // ILLEGAL and UNINITIALIZED can have garbage for the field. switch (token.token) { case Token::UNINITIALIZED: + case Token::ILLEGAL: // token.literal_chars & other members might be garbage. That's ok. - break; case Token::TEMPLATE_SPAN: case Token::TEMPLATE_TAIL: - DCHECK(token.raw_literal_chars.is_used()); - DCHECK(token.literal_chars.is_used()); - break; - case Token::ESCAPED_KEYWORD: - case Token::ESCAPED_STRICT_RESERVED_WORD: - case Token::FUTURE_STRICT_RESERVED_WORD: - case Token::IDENTIFIER: - case Token::NUMBER: - case Token::BIGINT: - case Token::REGEXP_LITERAL: - case Token::SMI: - case Token::STRING: - case Token::PRIVATE_NAME: - DCHECK(token.literal_chars.is_used()); - DCHECK(!token.raw_literal_chars.is_used()); - DCHECK_EQ(token.invalid_template_escape_message, MessageTemplate::kNone); break; default: - DCHECK(!token.literal_chars.is_used()); - DCHECK(!token.raw_literal_chars.is_used()); DCHECK_EQ(token.invalid_template_escape_message, MessageTemplate::kNone); break; } - - DCHECK_IMPLIES(token.token != Token::IDENTIFIER, - token.contextual_token == Token::UNINITIALIZED); - DCHECK_IMPLIES(token.contextual_token != Token::UNINITIALIZED, - token.token == Token::IDENTIFIER && - Token::IsContextualKeyword(token.contextual_token)); - DCHECK(!Token::IsContextualKeyword(token.token)); } #endif // DEBUG @@ -541,24 +501,45 @@ Token::Value Scanner::ScanString() { uc32 quote = c0_; Advance(); // consume quote - LiteralScope literal(this); + next().literal_chars.Start(); while (true) { + if (V8_UNLIKELY(c0_ == kEndOfInput)) return Token::ILLEGAL; + if ((V8_UNLIKELY(static_cast<uint32_t>(c0_) >= kMaxAscii) && + !unibrow::IsStringLiteralLineTerminator(c0_)) || + !MayTerminateString(character_scan_flags[c0_])) { + AddLiteralChar(c0_); + AdvanceUntil([this](uc32 c0) { + if (V8_UNLIKELY(static_cast<uint32_t>(c0) > kMaxAscii)) { + if (V8_UNLIKELY(unibrow::IsStringLiteralLineTerminator(c0))) { + return true; + } + AddLiteralChar(c0); + return false; + } + uint8_t char_flags = character_scan_flags[c0]; + if (MayTerminateString(char_flags)) return true; + AddLiteralChar(c0); + return false; + }); + } if (c0_ == quote) { - literal.Complete(); Advance(); return Token::STRING; } - if (c0_ == kEndOfInput || unibrow::IsStringLiteralLineTerminator(c0_)) { - return Token::ILLEGAL; - } if (c0_ == '\\') { Advance(); // TODO(verwaest): Check whether we can remove the additional check. - if (c0_ == kEndOfInput || !ScanEscape<false>()) { + if (V8_UNLIKELY(c0_ == kEndOfInput || !ScanEscape<false>())) { return Token::ILLEGAL; } continue; } + if (V8_UNLIKELY(c0_ == kEndOfInput || + unibrow::IsStringLiteralLineTerminator(c0_))) { + return Token::ILLEGAL; + } + DCHECK_NE(quote, c0_); + DCHECK((c0_ == '\'' || c0_ == '"')); AddLiteralCharAdvance(); } } @@ -570,17 +551,17 @@ Token::Value Scanner::ScanPrivateName() { return Token::ILLEGAL; } - LiteralScope literal(this); + next().literal_chars.Start(); DCHECK_EQ(c0_, '#'); - DCHECK(!unicode_cache_->IsIdentifierStart(kEndOfInput)); - if (!unicode_cache_->IsIdentifierStart(Peek())) { + DCHECK(!IsIdentifierStart(kEndOfInput)); + if (!IsIdentifierStart(Peek())) { ReportScannerError(source_pos(), MessageTemplate::kInvalidOrUnexpectedToken); return Token::ILLEGAL; } AddLiteralCharAdvance(); - Token::Value token = ScanIdentifierOrKeywordInner(&literal); + Token::Value token = ScanIdentifierOrKeywordInner(); return token == Token::ILLEGAL ? Token::ILLEGAL : Token::PRIVATE_NAME; } @@ -605,7 +586,7 @@ Token::Value Scanner::ScanTemplateSpan() { ErrorState octal_error_state(&octal_message_, &octal_pos_); Token::Value result = Token::TEMPLATE_SPAN; - LiteralScope literal(this); + next().literal_chars.Start(); next().raw_literal_chars.Start(); const bool capture_raw = true; while (true) { @@ -658,10 +639,8 @@ Token::Value Scanner::ScanTemplateSpan() { AddLiteralChar(c); } } - literal.Complete(); next().location.end_pos = source_pos(); next().token = result; - next().contextual_token = Token::UNINITIALIZED; return result; } @@ -669,7 +648,6 @@ Token::Value Scanner::ScanTemplateSpan() { Handle<String> Scanner::SourceUrl(Isolate* isolate) const { Handle<String> tmp; if (source_url_.length() > 0) { - DCHECK(source_url_.is_used()); tmp = source_url_.Internalize(isolate); } return tmp; @@ -678,7 +656,6 @@ Handle<String> Scanner::SourceUrl(Isolate* isolate) const { Handle<String> Scanner::SourceMappingUrl(Isolate* isolate) const { Handle<String> tmp; if (source_mapping_url_.length() > 0) { - DCHECK(source_mapping_url_.is_used()); tmp = source_mapping_url_.Internalize(isolate); } return tmp; @@ -847,7 +824,7 @@ Token::Value Scanner::ScanNumber(bool seen_period) { NumberKind kind = DECIMAL; - LiteralScope literal(this); + next().literal_chars.Start(); bool at_start = !seen_period; int start_pos = source_pos(); // For reporting octal positions. if (seen_period) { @@ -905,10 +882,8 @@ Token::Value Scanner::ScanNumber(bool seen_period) { } if (next().literal_chars.one_byte_literal().length() <= 10 && - value <= Smi::kMaxValue && c0_ != '.' && - !unicode_cache_->IsIdentifierStart(c0_)) { + value <= Smi::kMaxValue && c0_ != '.' && !IsIdentifierStart(c0_)) { next().smi_value_ = static_cast<uint32_t>(value); - literal.Complete(); if (kind == DECIMAL_WITH_LEADING_ZERO) { octal_pos_ = Location(start_pos, source_pos()); @@ -963,12 +938,10 @@ Token::Value Scanner::ScanNumber(bool seen_period) { // not be an identifier start or a decimal digit; see ECMA-262 // section 7.8.3, page 17 (note that we read only one decimal digit // if the value is 0). - if (IsDecimalDigit(c0_) || unicode_cache_->IsIdentifierStart(c0_)) { + if (IsDecimalDigit(c0_) || IsIdentifierStart(c0_)) { return Token::ILLEGAL; } - literal.Complete(); - if (kind == DECIMAL_WITH_LEADING_ZERO) { octal_pos_ = Location(start_pos, source_pos()); octal_message_ = MessageTemplate::kStrictDecimalWithLeadingZero; @@ -1004,54 +977,49 @@ uc32 Scanner::ScanUnicodeEscape() { return ScanHexNumber<capture_raw, unicode>(4); } -Token::Value Scanner::ScanIdentifierOrKeywordInnerSlow(LiteralScope* literal, - bool escaped) { +Token::Value Scanner::ScanIdentifierOrKeywordInnerSlow(bool escaped, + bool can_be_keyword) { while (true) { if (c0_ == '\\') { escaped = true; uc32 c = ScanIdentifierUnicodeEscape(); // Only allow legal identifier part characters. // TODO(verwaest): Make this true. - // DCHECK(!unicode_cache_->IsIdentifierPart('\\')); - DCHECK(!unicode_cache_->IsIdentifierPart(-1)); - if (c == '\\' || !unicode_cache_->IsIdentifierPart(c)) { + // DCHECK(!IsIdentifierPart('\')); + DCHECK(!IsIdentifierPart(-1)); + if (c == '\\' || !IsIdentifierPart(c)) { return Token::ILLEGAL; } + can_be_keyword = can_be_keyword && CharCanBeKeyword(c); AddLiteralChar(c); - } else if (unicode_cache_->IsIdentifierPart(c0_) || - (CombineSurrogatePair() && - unicode_cache_->IsIdentifierPart(c0_))) { + } else if (IsIdentifierPart(c0_) || + (CombineSurrogatePair() && IsIdentifierPart(c0_))) { + can_be_keyword = can_be_keyword && CharCanBeKeyword(c0_); AddLiteralCharAdvance(); } else { break; } } - if (next().literal_chars.is_one_byte()) { + if (can_be_keyword && next().literal_chars.is_one_byte()) { Vector<const uint8_t> chars = next().literal_chars.one_byte_literal(); Token::Value token = KeywordOrIdentifierToken(chars.start(), chars.length()); /* TODO(adamk): YIELD should be handled specially. */ if (token == Token::FUTURE_STRICT_RESERVED_WORD) { - literal->Complete(); if (escaped) return Token::ESCAPED_STRICT_RESERVED_WORD; return token; } - if (token == Token::IDENTIFIER || Token::IsContextualKeyword(token)) { - literal->Complete(); - return token; - } + if (token == Token::IDENTIFIER) return token; if (!escaped) return token; - literal->Complete(); if (token == Token::LET || token == Token::STATIC) { return Token::ESCAPED_STRICT_RESERVED_WORD; } return Token::ESCAPED_KEYWORD; } - literal->Complete(); return Token::IDENTIFIER; } @@ -1065,7 +1033,7 @@ bool Scanner::ScanRegExpPattern() { // 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); + next().literal_chars.Start(); if (next().token == Token::ASSIGN_DIV) { AddLiteralChar('='); } @@ -1098,9 +1066,7 @@ bool Scanner::ScanRegExpPattern() { } Advance(); // consume '/' - literal.Complete(); next().token = Token::REGEXP_LITERAL; - next().contextual_token = Token::UNINITIALIZED; return true; } @@ -1110,7 +1076,7 @@ Maybe<RegExp::Flags> Scanner::ScanRegExpFlags() { // Scan regular expression flags. int flags = 0; - while (unicode_cache_->IsIdentifierPart(c0_)) { + while (IsIdentifierPart(c0_)) { RegExp::Flags flag = RegExp::kNone; switch (c0_) { case 'g': @@ -1173,7 +1139,6 @@ const AstRawString* Scanner::CurrentRawSymbol( double Scanner::DoubleValue() { DCHECK(is_literal_one_byte()); return StringToDouble( - unicode_cache_, literal_one_byte_string(), ALLOW_HEX | ALLOW_OCTAL | ALLOW_IMPLICIT_OCTAL | ALLOW_BINARY); } @@ -1188,14 +1153,6 @@ const char* Scanner::CurrentLiteralAsCString(Zone* zone) const { return buffer; } -bool Scanner::IsDuplicateSymbol(DuplicateFinder* duplicate_finder, - AstValueFactory* ast_value_factory) const { - DCHECK_NOT_NULL(duplicate_finder); - DCHECK_NOT_NULL(ast_value_factory); - const AstRawString* string = CurrentSymbol(ast_value_factory); - return !duplicate_finder->known_symbols_.insert(string).second; -} - void Scanner::SeekNext(size_t position) { // Use with care: This cleanly resets most, but not all scanner state. // TODO(vogelheim): Fix this, or at least DCHECK the relevant conditions. @@ -1206,7 +1163,7 @@ void Scanner::SeekNext(size_t position) { // current_ will remain unchanged, so overwrite it fully.) for (TokenDesc& token : token_storage_) { token.token = Token::UNINITIALIZED; - token.contextual_token = Token::UNINITIALIZED; + token.invalid_template_escape_message = MessageTemplate::kNone; } // 2, reset the source to the desired position, source_->Seek(position); diff --git a/deps/v8/src/parsing/scanner.h b/deps/v8/src/parsing/scanner.h index 83002b53c8..383159557b 100644 --- a/deps/v8/src/parsing/scanner.h +++ b/deps/v8/src/parsing/scanner.h @@ -13,8 +13,9 @@ #include "src/base/logging.h" #include "src/char-predicates.h" #include "src/globals.h" -#include "src/messages.h" +#include "src/message-template.h" #include "src/parsing/token.h" +#include "src/pointer-with-payload.h" #include "src/unicode-decoder.h" #include "src/unicode.h" @@ -23,12 +24,11 @@ namespace internal { class AstRawString; class AstValueFactory; -class DuplicateFinder; class ExternalOneByteString; class ExternalTwoByteString; class ParserRecorder; class RuntimeCallStats; -class UnicodeCache; +class Zone; // --------------------------------------------------------------------- // Buffered stream of UTF-16 code units, using an internal UTF-16 buffer. @@ -40,6 +40,13 @@ class Utf16CharacterStream { virtual ~Utf16CharacterStream() = default; + V8_INLINE void set_parser_error() { + buffer_cursor_ = buffer_end_; + has_parser_error_ = true; + } + V8_INLINE void reset_parser_error_flag() { has_parser_error_ = false; } + V8_INLINE bool has_parser_error() const { return has_parser_error_; } + inline uc32 Peek() { if (V8_LIKELY(buffer_cursor_ < buffer_end_)) { return static_cast<uc32>(*buffer_cursor_); @@ -109,6 +116,11 @@ class Utf16CharacterStream { } } + // Returns true if the stream could access the V8 heap after construction. + bool can_be_cloned_for_parallel_access() const { + return can_be_cloned() && !can_access_heap(); + } + // 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; @@ -138,7 +150,7 @@ class Utf16CharacterStream { bool ReadBlockChecked() { size_t position = pos(); USE(position); - bool success = ReadBlock(); + bool success = !has_parser_error() && ReadBlock(); // Post-conditions: 1, We should always be at the right position. // 2, Cursor should be inside the buffer. @@ -186,6 +198,7 @@ class Utf16CharacterStream { const uint16_t* buffer_end_; size_t buffer_pos_; RuntimeCallStats* runtime_call_stats_; + bool has_parser_error_ = false; }; // ---------------------------------------------------------------------------- @@ -197,12 +210,14 @@ class Scanner { class BookmarkScope { public: explicit BookmarkScope(Scanner* scanner) - : scanner_(scanner), bookmark_(kNoBookmark) { + : scanner_(scanner), + bookmark_(kNoBookmark), + had_parser_error_(scanner->has_parser_error()) { DCHECK_NOT_NULL(scanner_); } ~BookmarkScope() = default; - void Set(); + void Set(size_t bookmark); void Apply(); bool HasBeenSet() const; bool HasBeenApplied() const; @@ -210,24 +225,39 @@ class Scanner { private: static const size_t kNoBookmark; static const size_t kBookmarkWasApplied; - static const size_t kBookmarkAtFirstPos; Scanner* scanner_; size_t bookmark_; + bool had_parser_error_; DISALLOW_COPY_AND_ASSIGN(BookmarkScope); }; + // Sets the Scanner into an error state to stop further scanning and terminate + // the parsing by only returning ILLEGAL tokens after that. + V8_INLINE void set_parser_error() { + if (!has_parser_error()) { + c0_ = kEndOfInput; + source_->set_parser_error(); + for (TokenDesc& desc : token_storage_) desc.token = Token::ILLEGAL; + } + } + V8_INLINE void reset_parser_error_flag() { + source_->reset_parser_error_flag(); + } + V8_INLINE bool has_parser_error() const { + return source_->has_parser_error(); + } + // Representation of an interval of source positions. struct Location { Location(int b, int e) : beg_pos(b), end_pos(e) { } Location() : beg_pos(0), end_pos(0) { } - bool IsValid() const { - return beg_pos >= 0 && end_pos >= beg_pos; - } + int length() const { return end_pos - beg_pos; } + bool IsValid() const { return IsInRange(beg_pos, 0, end_pos); } - static Location invalid() { return Location(-1, -1); } + static Location invalid() { return Location(-1, 0); } int beg_pos; int end_pos; @@ -237,8 +267,7 @@ class Scanner { static const int kNoOctalLocation = -1; static const uc32 kEndOfInput = Utf16CharacterStream::kEndOfInput; - explicit Scanner(UnicodeCache* scanner_contants, Utf16CharacterStream* source, - bool is_module); + explicit Scanner(Utf16CharacterStream* source, bool is_module); void Initialize(); @@ -249,27 +278,28 @@ class Scanner { // Returns the current token again. Token::Value current_token() const { return current().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()). 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_; } + MessageTemplate error() const { return scanner_error_; } const Location& error_location() const { return scanner_error_location_; } bool has_invalid_template_escape() const { return current().invalid_template_escape_message != MessageTemplate::kNone; } - MessageTemplate::Template invalid_template_escape_message() const { + MessageTemplate invalid_template_escape_message() const { DCHECK(has_invalid_template_escape()); return current().invalid_template_escape_message; } + + void clear_invalid_template_escape_message() { + DCHECK(has_invalid_template_escape()); + current_->invalid_template_escape_message = MessageTemplate::kNone; + } + Location invalid_template_escape_location() const { DCHECK(has_invalid_template_escape()); return current().invalid_template_escape_location; @@ -301,55 +331,27 @@ class Scanner { return current().token == token; } - inline bool CurrentMatchesContextual(Token::Value token) const { - DCHECK(Token::IsContextualKeyword(token)); - return current_contextual_token() == token; - } - - // Match the token against the contextual keyword or literal buffer. - inline bool CurrentMatchesContextualEscaped(Token::Value token) const { - DCHECK(Token::IsContextualKeyword(token) || token == Token::LET); - // Escaped keywords are not matched as tokens. So if we require escape - // and/or string processing we need to look at the literal content - // (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) || - (current().literal_chars.is_used() && - current().literal_chars.Equals(Vector<const char>( - Token::String(token), Token::StringLength(token)))); - } + template <size_t N> + bool NextLiteralEquals(const char (&s)[N]) { + DCHECK_EQ(Token::STRING, peek()); + // The length of the token is used to make sure the literal equals without + // taking escape sequences (e.g., "use \x73trict") or line continuations + // (e.g., "use \(newline) strict") into account. + if (!is_next_literal_one_byte()) return false; + if (peek_location().length() != N + 1) return false; - bool IsUseStrict() const { - return current().token == Token::STRING && - current().literal_chars.Equals( - Vector<const char>("use strict", strlen("use strict"))); + Vector<const uint8_t> next = next_literal_one_byte_string(); + const char* chars = reinterpret_cast<const char*>(next.start()); + return next.length() == N - 1 && strncmp(s, chars, N - 1) == 0; } - bool IsGet() { return CurrentMatchesContextual(Token::GET); } - - bool IsSet() { return CurrentMatchesContextual(Token::SET); } - - bool IsLet() const { - return CurrentMatches(Token::LET) || - CurrentMatchesContextualEscaped(Token::LET); - } - - // Check whether the CurrentSymbol() has already been seen. - // The DuplicateFinder holds the data, so different instances can be used - // for different sets of duplicates to check for. - bool IsDuplicateSymbol(DuplicateFinder* duplicate_finder, - AstValueFactory* ast_value_factory) const; - - UnicodeCache* unicode_cache() const { return unicode_cache_; } - // Returns the location of the last seen octal literal. Location octal_position() const { return octal_pos_; } void clear_octal_position() { octal_pos_ = Location::invalid(); octal_message_ = MessageTemplate::kNone; } - MessageTemplate::Template octal_message() const { return octal_message_; } + MessageTemplate octal_message() const { return octal_message_; } // Returns the value of the last smi that was scanned. uint32_t smi_value() const { return current().smi_value_; } @@ -405,6 +407,9 @@ class Scanner { const Utf16CharacterStream* stream() const { return source_; } + // If the next characters in the stream are "#!", the line is skipped. + void SkipHashBang(); + private: // Scoped helper for saving & restoring scanner error state. // This is used for tagged template literals, in which normally forbidden @@ -414,20 +419,17 @@ class Scanner { // LiteralBuffer - Collector of chars of literals. class LiteralBuffer { public: - LiteralBuffer() - : backing_store_(), position_(0), is_one_byte_(true), is_used_(false) {} + LiteralBuffer() : backing_store_(), position_(0), is_one_byte_(true) {} ~LiteralBuffer() { backing_store_.Dispose(); } V8_INLINE void AddChar(char code_unit) { - DCHECK(is_used_); DCHECK(IsValidAscii(code_unit)); AddOneByteChar(static_cast<byte>(code_unit)); } V8_INLINE void AddChar(uc32 code_unit) { - DCHECK(is_used_); - if (is_one_byte_) { + if (is_one_byte()) { if (code_unit <= static_cast<uc32>(unibrow::Latin1::kMaxChar)) { AddOneByteChar(static_cast<byte>(code_unit)); return; @@ -440,14 +442,12 @@ class Scanner { bool is_one_byte() const { return is_one_byte_; } bool Equals(Vector<const char> keyword) const { - DCHECK(is_used_); return is_one_byte() && keyword.length() == position_ && (memcmp(keyword.start(), backing_store_.start(), position_) == 0); } Vector<const uint16_t> two_byte_literal() const { - DCHECK(!is_one_byte_); - DCHECK(is_used_); + DCHECK(!is_one_byte()); DCHECK_EQ(position_ & 0x1, 0); return Vector<const uint16_t>( reinterpret_cast<const uint16_t*>(backing_store_.start()), @@ -455,24 +455,14 @@ class Scanner { } Vector<const uint8_t> one_byte_literal() const { - DCHECK(is_one_byte_); - DCHECK(is_used_); + DCHECK(is_one_byte()); return Vector<const uint8_t>( reinterpret_cast<const uint8_t*>(backing_store_.start()), position_); } - int length() const { return is_one_byte_ ? position_ : (position_ >> 1); } + int length() const { return is_one_byte() ? position_ : (position_ >> 1); } void Start() { - DCHECK(!is_used_); - DCHECK_EQ(0, position_); - is_used_ = true; - } - - bool is_used() const { return is_used_; } - - void Drop() { - is_used_ = false; position_ = 0; is_one_byte_ = true; } @@ -481,8 +471,7 @@ class Scanner { private: static const int kInitialCapacity = 16; - static const int kGrowthFactory = 4; - static const int kMinConversionSlack = 256; + static const int kGrowthFactor = 4; static const int kMaxGrowth = 1 * MB; inline bool IsValidAscii(char code_unit) { @@ -494,7 +483,7 @@ class Scanner { } V8_INLINE void AddOneByteChar(byte one_byte_char) { - DCHECK(is_one_byte_); + DCHECK(is_one_byte()); if (position_ >= backing_store_.length()) ExpandBuffer(); backing_store_[position_] = one_byte_char; position_ += kOneByteSize; @@ -507,42 +496,37 @@ class Scanner { Vector<byte> backing_store_; int position_; + bool is_one_byte_; - bool is_used_; 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}; LiteralBuffer literal_chars; LiteralBuffer raw_literal_chars; Token::Value token = Token::UNINITIALIZED; - MessageTemplate::Template invalid_template_escape_message = - MessageTemplate::kNone; + MessageTemplate invalid_template_escape_message = MessageTemplate::kNone; Location invalid_template_escape_location; - Token::Value contextual_token = Token::UNINITIALIZED; uint32_t smi_value_ = 0; bool after_line_terminator = false; + +#ifdef DEBUG + bool CanAccessLiteral() const { + return token == Token::PRIVATE_NAME || token == Token::ILLEGAL || + token == Token::UNINITIALIZED || token == Token::REGEXP_LITERAL || + token == Token::ESCAPED_KEYWORD || + IsInRange(token, Token::NUMBER, Token::STRING) || + (Token::IsAnyIdentifier(token) && !Token::IsKeyword(token)) || + IsInRange(token, Token::TEMPLATE_SPAN, Token::TEMPLATE_TAIL); + } + bool CanAccessRawLiteral() const { + return token == Token::ILLEGAL || token == Token::UNINITIALIZED || + IsInRange(token, Token::TEMPLATE_SPAN, Token::TEMPLATE_TAIL); + } +#endif // DEBUG }; enum NumberKind { @@ -575,14 +559,13 @@ class Scanner { scanner_error_ = MessageTemplate::kNone; } - void ReportScannerError(const Location& location, - MessageTemplate::Template error) { + void ReportScannerError(const Location& location, MessageTemplate error) { if (has_error()) return; scanner_error_ = error; scanner_error_location_ = location; } - void ReportScannerError(int pos, MessageTemplate::Template error) { + void ReportScannerError(int pos, MessageTemplate error) { if (has_error()) return; scanner_error_ = error; scanner_error_location_ = Location(pos, pos + 1); @@ -668,45 +651,41 @@ class Scanner { // token as a one-byte literal. E.g. Token::FUNCTION pretends to have a // literal "function". Vector<const uint8_t> literal_one_byte_string() const { - if (current().literal_chars.is_used()) - return current().literal_chars.one_byte_literal(); - const char* str = Token::String(current().token); - const uint8_t* str_as_uint8 = reinterpret_cast<const uint8_t*>(str); - return Vector<const uint8_t>(str_as_uint8, - Token::StringLength(current().token)); + DCHECK(current().CanAccessLiteral() || Token::IsKeyword(current().token)); + return current().literal_chars.one_byte_literal(); } Vector<const uint16_t> literal_two_byte_string() const { - DCHECK(current().literal_chars.is_used()); + DCHECK(current().CanAccessLiteral() || Token::IsKeyword(current().token)); return current().literal_chars.two_byte_literal(); } bool is_literal_one_byte() const { - return !current().literal_chars.is_used() || - current().literal_chars.is_one_byte(); + DCHECK(current().CanAccessLiteral() || Token::IsKeyword(current().token)); + return current().literal_chars.is_one_byte(); } // Returns the literal string for the next token (the token that // would be returned if Next() were called). Vector<const uint8_t> next_literal_one_byte_string() const { - DCHECK(next().literal_chars.is_used()); + DCHECK(next().CanAccessLiteral()); return next().literal_chars.one_byte_literal(); } Vector<const uint16_t> next_literal_two_byte_string() const { - DCHECK(next().literal_chars.is_used()); + DCHECK(next().CanAccessLiteral()); return next().literal_chars.two_byte_literal(); } bool is_next_literal_one_byte() const { - DCHECK(next().literal_chars.is_used()); + DCHECK(next().CanAccessLiteral()); return next().literal_chars.is_one_byte(); } Vector<const uint8_t> raw_literal_one_byte_string() const { - DCHECK(current().raw_literal_chars.is_used()); + DCHECK(current().CanAccessRawLiteral()); return current().raw_literal_chars.one_byte_literal(); } Vector<const uint16_t> raw_literal_two_byte_string() const { - DCHECK(current().raw_literal_chars.is_used()); + DCHECK(current().CanAccessRawLiteral()); return current().raw_literal_chars.two_byte_literal(); } bool is_raw_literal_one_byte() const { - DCHECK(current().raw_literal_chars.is_used()); + DCHECK(current().CanAccessRawLiteral()); return current().raw_literal_chars.is_one_byte(); } @@ -721,6 +700,11 @@ class Scanner { // Scans a single JavaScript token. V8_INLINE Token::Value ScanSingleToken(); V8_INLINE void Scan(); + // Performance hack: pass through a pre-calculated "next()" value to avoid + // having to re-calculate it in Scan. You'd think the compiler would be able + // to hoist the next() calculation out of the inlined Scan method, but seems + // that pointer aliasing analysis fails show that this is safe. + V8_INLINE void Scan(TokenDesc* next_desc); V8_INLINE Token::Value SkipWhiteSpace(); Token::Value SkipSingleHTMLComment(); @@ -745,9 +729,9 @@ class Scanner { Token::Value ScanNumber(bool seen_period); V8_INLINE Token::Value ScanIdentifierOrKeyword(); - V8_INLINE Token::Value ScanIdentifierOrKeywordInner(LiteralScope* literal); - Token::Value ScanIdentifierOrKeywordInnerSlow(LiteralScope* literal, - bool escaped); + V8_INLINE Token::Value ScanIdentifierOrKeywordInner(); + Token::Value ScanIdentifierOrKeywordInnerSlow(bool escaped, + bool can_be_keyword); Token::Value ScanString(); Token::Value ScanPrivateName(); @@ -779,16 +763,13 @@ class Scanner { // Subtract delimiters. source_length -= 2; } - return token.literal_chars.is_used() && - (token.literal_chars.length() != source_length); + return token.literal_chars.length() != source_length; } #ifdef DEBUG void SanityCheckTokenDesc(const TokenDesc&) const; #endif - UnicodeCache* const unicode_cache_; - TokenDesc& next() { return *next_; } const TokenDesc& current() const { return *current_; } @@ -822,9 +803,9 @@ class Scanner { // Last-seen positions of potentially problematic tokens. Location octal_pos_; - MessageTemplate::Template octal_message_; + MessageTemplate octal_message_; - MessageTemplate::Template scanner_error_; + MessageTemplate scanner_error_; Location scanner_error_location_; }; diff --git a/deps/v8/src/parsing/token.cc b/deps/v8/src/parsing/token.cc index 4cbf244a2b..ec4b623775 100644 --- a/deps/v8/src/parsing/token.cc +++ b/deps/v8/src/parsing/token.cc @@ -10,32 +10,37 @@ namespace v8 { namespace internal { #define T(name, string, precedence) #name, -const char* const Token::name_[NUM_TOKENS] = {TOKEN_LIST(T, T, T)}; +const char* const Token::name_[NUM_TOKENS] = {TOKEN_LIST(T, T)}; #undef T #define T(name, string, precedence) string, -const char* const Token::string_[NUM_TOKENS] = {TOKEN_LIST(T, T, T)}; +const char* const Token::string_[NUM_TOKENS] = {TOKEN_LIST(T, T)}; #undef T constexpr uint8_t length(const char* str) { return str ? static_cast<uint8_t>(strlen(str)) : 0; } #define T(name, string, precedence) length(string), -const uint8_t Token::string_length_[NUM_TOKENS] = {TOKEN_LIST(T, T, T)}; +const uint8_t Token::string_length_[NUM_TOKENS] = {TOKEN_LIST(T, T)}; #undef T -#define T(name, string, precedence) precedence, -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', -const char Token::token_type[] = {TOKEN_LIST(KT, KK, KC)}; +#define T1(name, string, precedence) \ + ((Token::name == Token::IN) ? 0 : precedence), +#define T2(name, string, precedence) precedence, +// precedence_[0] for accept_IN == false, precedence_[1] for accept_IN = true. +const int8_t Token::precedence_[2][NUM_TOKENS] = {{TOKEN_LIST(T1, T1)}, + {TOKEN_LIST(T2, T2)}}; +#undef T2 +#undef T1 + +#define KT(a, b, c) \ + IsPropertyNameBits::encode(Token::IsAnyIdentifier(a) || a == ESCAPED_KEYWORD), +#define KK(a, b, c) \ + IsKeywordBits::encode(true) | IsPropertyNameBits::encode(true), +const uint8_t Token::token_flags[] = {TOKEN_LIST(KT, KK)}; #undef KT #undef KK -#undef KC } // namespace internal } // namespace v8 diff --git a/deps/v8/src/parsing/token.h b/deps/v8/src/parsing/token.h index e1c6239e36..c457d39e92 100644 --- a/deps/v8/src/parsing/token.h +++ b/deps/v8/src/parsing/token.h @@ -20,12 +20,6 @@ namespace internal { // // T: Non-keyword tokens // K: Keyword tokens -// C: Contextual keyword token -// -// Contextual keyword tokens are tokens that are scanned as Token::IDENTIFIER, -// but that in some contexts are treated as keywords. This mostly happens -// when ECMAScript introduces new keywords, but for backwards compatibility -// allows them to still be used as indentifiers in most contexts. // IGNORE_TOKEN is a convenience macro that can be supplied as // an argument (at any position) for a TOKEN_LIST call. It does @@ -33,7 +27,8 @@ namespace internal { #define IGNORE_TOKEN(name, string, precedence) -/* Binary operators sorted by precedence */ +/* Binary operators */ +/* ADD and SUB are at the end since they are UnaryOp */ #define BINARY_OP_TOKEN_LIST(T, E) \ E(T, BIT_OR, "|", 6) \ E(T, BIT_XOR, "^", 7) \ @@ -41,12 +36,12 @@ namespace internal { 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) + E(T, EXP, "**", 14) \ + E(T, ADD, "+", 12) \ + E(T, SUB, "-", 12) #define EXPAND_BINOP_ASSIGN_TOKEN(T, name, string, precedence) \ T(ASSIGN_##name, string "=", 2) @@ -54,32 +49,47 @@ namespace internal { #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) \ +#define TOKEN_LIST(T, K) \ + \ + /* BEGIN PropertyOrCall */ \ + /* BEGIN Member */ \ + /* BEGIN Template */ \ + /* ES6 Template Literals */ \ + T(TEMPLATE_SPAN, nullptr, 0) \ + T(TEMPLATE_TAIL, nullptr, 0) \ + /* END Template */ \ \ /* Punctuators (ECMA-262, section 7.7, page 15). */ \ + /* BEGIN Property */ \ + T(PERIOD, ".", 0) \ + T(LBRACK, "[", 0) \ + /* END Property */ \ + /* END Member */ \ T(LPAREN, "(", 0) \ + /* END PropertyOrCall */ \ T(RPAREN, ")", 0) \ - T(LBRACK, "[", 0) \ T(RBRACK, "]", 0) \ T(LBRACE, "{", 0) \ - T(RBRACE, "}", 0) \ T(COLON, ":", 0) \ - T(SEMICOLON, ";", 0) \ - T(PERIOD, ".", 0) \ T(ELLIPSIS, "...", 0) \ T(CONDITIONAL, "?", 3) \ - T(INC, "++", 0) \ - T(DEC, "--", 0) \ - T(ARROW, "=>", 0) \ + /* BEGIN AutoSemicolon */ \ + T(SEMICOLON, ";", 0) \ + T(RBRACE, "}", 0) \ + /* End of source indicator. */ \ + T(EOS, "EOS", 0) \ + /* END AutoSemicolon */ \ \ - /* Assignment operators. */ \ + /* BEGIN ArrowOrAssignmentOp */ \ + T(ARROW, "=>", 0) \ + /* BEGIN AssignmentOp */ \ /* IsAssignmentOp() relies on this block of enum values being */ \ /* contiguous and sorted in the same order! */ \ T(INIT, "=init", 2) /* AST-use only. */ \ T(ASSIGN, "=", 2) \ BINARY_OP_TOKEN_LIST(T, EXPAND_BINOP_ASSIGN_TOKEN) \ + /* END AssignmentOp */ \ + /* END ArrowOrAssignmentOp */ \ \ /* Binary operators sorted by precedence. */ \ /* IsBinaryOp() relies on this block of enum values */ \ @@ -87,8 +97,24 @@ namespace internal { T(COMMA, ",", 1) \ T(OR, "||", 4) \ T(AND, "&&", 5) \ + \ + /* Unary operators, starting at ADD in BINARY_OP_TOKEN_LIST */ \ + /* IsUnaryOp() relies on this block of enum values */ \ + /* being contiguous and sorted in the same order! */ \ BINARY_OP_TOKEN_LIST(T, EXPAND_BINOP_TOKEN) \ \ + T(NOT, "!", 0) \ + T(BIT_NOT, "~", 0) \ + K(DELETE, "delete", 0) \ + K(TYPEOF, "typeof", 0) \ + K(VOID, "void", 0) \ + \ + /* BEGIN IsCountOp */ \ + T(INC, "++", 0) \ + T(DEC, "--", 0) \ + /* END IsCountOp */ \ + /* END IsUnaryOrCountOp */ \ + \ /* Compare operators sorted by precedence. */ \ /* IsCompareOp() relies on this block of enum values */ \ /* being contiguous and sorted in the same order! */ \ @@ -103,15 +129,6 @@ namespace internal { K(INSTANCEOF, "instanceof", 10) \ K(IN, "in", 10) \ \ - /* Unary operators. */ \ - /* IsUnaryOp() relies on this block of enum values */ \ - /* being contiguous and sorted in the same order! */ \ - T(NOT, "!", 0) \ - T(BIT_NOT, "~", 0) \ - K(DELETE, "delete", 0) \ - K(TYPEOF, "typeof", 0) \ - K(VOID, "void", 0) \ - \ /* Keywords (ECMA-262, section 7.5.2, page 13). */ \ K(BREAK, "break", 0) \ K(CASE, "case", 0) \ @@ -149,6 +166,8 @@ namespace internal { T(BIGINT, nullptr, 0) \ T(STRING, nullptr, 0) \ \ + /* BEGIN Callable */ \ + K(SUPER, "super", 0) \ /* BEGIN AnyIdentifier */ \ /* Identifiers (not keywords or future reserved words). */ \ T(IDENTIFIER, nullptr, 0) \ @@ -161,14 +180,14 @@ namespace internal { /* 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 */ \ + /* END Callable */ \ + K(ENUM, "enum", 0) \ K(CLASS, "class", 0) \ K(CONST, "const", 0) \ K(EXPORT, "export", 0) \ K(EXTENDS, "extends", 0) \ K(IMPORT, "import", 0) \ - K(SUPER, "super", 0) \ T(PRIVATE_NAME, nullptr, 0) \ \ /* Illegal token - not able to scan. */ \ @@ -178,35 +197,13 @@ namespace internal { /* Scanner-internal use only. */ \ T(WHITESPACE, nullptr, 0) \ T(UNINITIALIZED, nullptr, 0) \ - T(REGEXP_LITERAL, nullptr, 0) \ - \ - /* ES6 Template Literals */ \ - T(TEMPLATE_SPAN, nullptr, 0) \ - T(TEMPLATE_TAIL, nullptr, 0) \ - \ - /* Contextual keyword tokens */ \ - C(GET, "get", 0) \ - C(SET, "set", 0) \ - C(OF, "of", 0) \ - C(TARGET, "target", 0) \ - C(META, "meta", 0) \ - C(AS, "as", 0) \ - C(FROM, "from", 0) \ - C(NAME, "name", 0) \ - C(PROTO_UNDERSCORED, "__proto__", 0) \ - C(CONSTRUCTOR, "constructor", 0) \ - C(PRIVATE_CONSTRUCTOR, "#constructor", 0) \ - C(PROTOTYPE, "prototype", 0) \ - C(EVAL, "eval", 0) \ - C(ARGUMENTS, "arguments", 0) \ - C(UNDEFINED, "undefined", 0) \ - C(ANONYMOUS, "anonymous", 0) + T(REGEXP_LITERAL, nullptr, 0) class Token { public: // All token values. #define T(name, string, precedence) name, - enum Value : uint8_t { TOKEN_LIST(T, T, T) NUM_TOKENS }; + enum Value : uint8_t { TOKEN_LIST(T, T) NUM_TOKENS }; #undef T // Returns a string corresponding to the C++ token name @@ -216,43 +213,73 @@ class Token { return name_[token]; } - static char TypeForTesting(Value token) { return token_type[token]; } + class IsKeywordBits : public BitField8<bool, 0, 1> {}; + class IsPropertyNameBits : public BitField8<bool, IsKeywordBits::kNext, 1> {}; // Predicates - static bool IsKeyword(Value token) { return token_type[token] == 'K'; } - static bool IsContextualKeyword(Value token) { - return IsInRange(token, GET, ANONYMOUS); + static bool IsKeyword(Value token) { + return IsKeywordBits::decode(token_flags[token]); + } + + static bool IsPropertyName(Value token) { + return IsPropertyNameBits::decode(token_flags[token]); } - static bool IsIdentifier(Value token, LanguageMode language_mode, - bool is_generator, bool disallow_await) { - if (IsInRange(token, IDENTIFIER, ASYNC)) return true; - if (IsInRange(token, LET, ESCAPED_STRICT_RESERVED_WORD)) { - return is_sloppy(language_mode); - } + V8_INLINE static bool IsValidIdentifier(Value token, + LanguageMode language_mode, + bool is_generator, + bool disallow_await) { + if (V8_LIKELY(IsInRange(token, IDENTIFIER, ASYNC))) return true; if (token == AWAIT) return !disallow_await; if (token == YIELD) return !is_generator && is_sloppy(language_mode); - return false; + return IsStrictReservedWord(token) && is_sloppy(language_mode); + } + + static bool IsCallable(Value token) { + return IsInRange(token, SUPER, ESCAPED_STRICT_RESERVED_WORD); + } + + static bool IsAutoSemicolon(Value token) { + return IsInRange(token, SEMICOLON, EOS); } static bool IsAnyIdentifier(Value token) { - return IsInRange(token, IDENTIFIER, ENUM); + return IsInRange(token, IDENTIFIER, ESCAPED_STRICT_RESERVED_WORD); } static bool IsStrictReservedWord(Value token) { - return IsInRange(token, LET, ESCAPED_STRICT_RESERVED_WORD); + return IsInRange(token, YIELD, ESCAPED_STRICT_RESERVED_WORD); } static bool IsLiteral(Value token) { return IsInRange(token, NULL_LITERAL, STRING); } + static bool IsTemplate(Value token) { + return IsInRange(token, TEMPLATE_SPAN, TEMPLATE_TAIL); + } + + static bool IsMember(Value token) { + return IsInRange(token, TEMPLATE_SPAN, LBRACK); + } + + static bool IsProperty(Value token) { + return IsInRange(token, PERIOD, LBRACK); + } + + static bool IsPropertyOrCall(Value token) { + return IsInRange(token, TEMPLATE_SPAN, LPAREN); + } + + static bool IsArrowOrAssignmentOp(Value token) { + return IsInRange(token, ARROW, ASSIGN_SUB); + } + static bool IsAssignmentOp(Value token) { - return IsInRange(token, INIT, ASSIGN_EXP); + return IsInRange(token, INIT, ASSIGN_SUB); } - static bool IsGetOrSet(Value op) { return IsInRange(op, GET, SET); } - static bool IsBinaryOp(Value op) { return IsInRange(op, COMMA, EXP); } + static bool IsBinaryOp(Value op) { return IsInRange(op, COMMA, SUB); } static bool IsCompareOp(Value op) { return IsInRange(op, EQ, IN); } @@ -263,7 +290,7 @@ class Token { static bool IsEqualityOp(Value op) { return IsInRange(op, EQ, EQ_STRICT); } static Value BinaryOpForAssignment(Value op) { - DCHECK(IsInRange(op, ASSIGN_BIT_OR, ASSIGN_EXP)); + DCHECK(IsInRange(op, ASSIGN_BIT_OR, ASSIGN_SUB)); Value result = static_cast<Value>(op - ASSIGN_BIT_OR + BIT_OR); DCHECK(IsBinaryOp(result)); return result; @@ -273,18 +300,11 @@ class Token { return IsInRange(op, BIT_OR, SHR) || op == BIT_NOT; } - static bool IsUnaryOp(Value op) { - return IsInRange(op, NOT, VOID) || IsInRange(op, ADD, SUB); - } - + static bool IsUnaryOp(Value op) { return IsInRange(op, ADD, VOID); } static bool IsCountOp(Value op) { return IsInRange(op, INC, DEC); } - + static bool IsUnaryOrCountOp(Value op) { return IsInRange(op, ADD, DEC); } static bool IsShiftOp(Value op) { return IsInRange(op, SHL, 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). @@ -300,17 +320,17 @@ class Token { // Returns the precedence > 0 for binary and compare // operators; returns 0 otherwise. - static int Precedence(Value token) { + static int Precedence(Value token, bool accept_IN) { DCHECK_GT(NUM_TOKENS, token); // token is unsigned - return precedence_[token]; + return precedence_[accept_IN][token]; } private: static const char* const name_[NUM_TOKENS]; static const char* const string_[NUM_TOKENS]; static const uint8_t string_length_[NUM_TOKENS]; - static const int8_t precedence_[NUM_TOKENS]; - static const char token_type[NUM_TOKENS]; + static const int8_t precedence_[2][NUM_TOKENS]; + static const uint8_t token_flags[NUM_TOKENS]; }; } // namespace internal |