|  | // 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 "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. | 
|  |  | 
|  | template <typename Types> | 
|  | class ExpressionClassifier { | 
|  | 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), | 
|  | type(kSyntaxError), | 
|  | arg(nullptr) {} | 
|  | V8_INLINE explicit Error(Scanner::Location loc, | 
|  | MessageTemplate::Template msg, ErrorKind k, | 
|  | const char* a = nullptr, | 
|  | ParseErrorType t = kSyntaxError) | 
|  | : location(loc), message(msg), kind(k), type(t), arg(a) {} | 
|  |  | 
|  | Scanner::Location location; | 
|  | MessageTemplate::Template message : 26; | 
|  | unsigned kind : 4; | 
|  | ParseErrorType type : 2; | 
|  | 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 | 
|  |  | 
|  | enum FunctionProperties : unsigned { | 
|  | NonSimpleParameter = 1 << 0 | 
|  | }; | 
|  |  | 
|  | explicit ExpressionClassifier(typename Types::Base* base, | 
|  | DuplicateFinder* duplicate_finder = nullptr) | 
|  | : base_(base), | 
|  | previous_(base->classifier_), | 
|  | zone_(base->impl()->zone()), | 
|  | reported_errors_(base->impl()->GetReportedErrorList()), | 
|  | duplicate_finder_(duplicate_finder), | 
|  | invalid_productions_(0), | 
|  | function_properties_(0) { | 
|  | base->classifier_ = this; | 
|  | reported_errors_begin_ = reported_errors_end_ = reported_errors_->length(); | 
|  | } | 
|  |  | 
|  | V8_INLINE ~ExpressionClassifier() { | 
|  | Discard(); | 
|  | if (base_->classifier_ == this) base_->classifier_ = previous_; | 
|  | } | 
|  |  | 
|  | 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 const Error& expression_error() const { | 
|  | return reported_error(kExpressionProduction); | 
|  | } | 
|  |  | 
|  | V8_INLINE const Error& formal_parameter_initializer_error() const { | 
|  | return reported_error(kFormalParameterInitializerProduction); | 
|  | } | 
|  |  | 
|  | V8_INLINE const Error& binding_pattern_error() const { | 
|  | return reported_error(kBindingPatternProduction); | 
|  | } | 
|  |  | 
|  | V8_INLINE const Error& assignment_pattern_error() const { | 
|  | return reported_error(kAssignmentPatternProduction); | 
|  | } | 
|  |  | 
|  | V8_INLINE const Error& arrow_formal_parameters_error() const { | 
|  | return reported_error(kArrowFormalParametersProduction); | 
|  | } | 
|  |  | 
|  | V8_INLINE const Error& duplicate_formal_parameter_error() const { | 
|  | return reported_error(kDistinctFormalParametersProduction); | 
|  | } | 
|  |  | 
|  | V8_INLINE const Error& strict_mode_formal_parameter_error() const { | 
|  | return reported_error(kStrictModeFormalParametersProduction); | 
|  | } | 
|  |  | 
|  | V8_INLINE const Error& let_pattern_error() const { | 
|  | return reported_error(kLetPatternProduction); | 
|  | } | 
|  |  | 
|  | V8_INLINE const Error& async_arrow_formal_parameters_error() const { | 
|  | return reported_error(kAsyncArrowFormalParametersProduction); | 
|  | } | 
|  |  | 
|  | V8_INLINE bool is_simple_parameter_list() const { | 
|  | return !(function_properties_ & NonSimpleParameter); | 
|  | } | 
|  |  | 
|  | V8_INLINE void RecordNonSimpleParameter() { | 
|  | function_properties_ |= NonSimpleParameter; | 
|  | } | 
|  |  | 
|  | void RecordExpressionError(const Scanner::Location& loc, | 
|  | MessageTemplate::Template message, | 
|  | const char* arg = nullptr) { | 
|  | if (!is_valid_expression()) return; | 
|  | invalid_productions_ |= ExpressionProduction; | 
|  | Add(Error(loc, message, kExpressionProduction, arg)); | 
|  | } | 
|  |  | 
|  | void RecordExpressionError(const Scanner::Location& loc, | 
|  | MessageTemplate::Template message, | 
|  | ParseErrorType type, const char* arg = nullptr) { | 
|  | if (!is_valid_expression()) return; | 
|  | invalid_productions_ |= ExpressionProduction; | 
|  | Add(Error(loc, message, kExpressionProduction, arg, type)); | 
|  | } | 
|  |  | 
|  | void RecordFormalParameterInitializerError(const Scanner::Location& loc, | 
|  | MessageTemplate::Template message, | 
|  | const char* arg = nullptr) { | 
|  | if (!is_valid_formal_parameter_initializer()) return; | 
|  | invalid_productions_ |= FormalParameterInitializerProduction; | 
|  | Add(Error(loc, message, kFormalParameterInitializerProduction, arg)); | 
|  | } | 
|  |  | 
|  | void RecordBindingPatternError(const Scanner::Location& loc, | 
|  | MessageTemplate::Template message, | 
|  | const char* arg = nullptr) { | 
|  | if (!is_valid_binding_pattern()) return; | 
|  | invalid_productions_ |= BindingPatternProduction; | 
|  | Add(Error(loc, message, kBindingPatternProduction, arg)); | 
|  | } | 
|  |  | 
|  | void RecordAssignmentPatternError(const Scanner::Location& loc, | 
|  | MessageTemplate::Template message, | 
|  | const char* arg = nullptr) { | 
|  | if (!is_valid_assignment_pattern()) return; | 
|  | invalid_productions_ |= AssignmentPatternProduction; | 
|  | Add(Error(loc, message, kAssignmentPatternProduction, arg)); | 
|  | } | 
|  |  | 
|  | 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 (!is_valid_arrow_formal_parameters()) return; | 
|  | invalid_productions_ |= ArrowFormalParametersProduction; | 
|  | Add(Error(loc, message, kArrowFormalParametersProduction, arg)); | 
|  | } | 
|  |  | 
|  | void RecordAsyncArrowFormalParametersError(const Scanner::Location& loc, | 
|  | MessageTemplate::Template message, | 
|  | const char* arg = nullptr) { | 
|  | if (!is_valid_async_arrow_formal_parameters()) return; | 
|  | invalid_productions_ |= AsyncArrowFormalParametersProduction; | 
|  | Add(Error(loc, message, kAsyncArrowFormalParametersProduction, arg)); | 
|  | } | 
|  |  | 
|  | void RecordDuplicateFormalParameterError(const Scanner::Location& loc) { | 
|  | if (!is_valid_formal_parameter_list_without_duplicates()) return; | 
|  | invalid_productions_ |= DistinctFormalParametersProduction; | 
|  | Add(Error(loc, MessageTemplate::kParamDupe, | 
|  | 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 (!is_valid_strict_mode_formal_parameters()) return; | 
|  | invalid_productions_ |= StrictModeFormalParametersProduction; | 
|  | Add(Error(loc, message, kStrictModeFormalParametersProduction, arg)); | 
|  | } | 
|  |  | 
|  | void RecordLetPatternError(const Scanner::Location& loc, | 
|  | MessageTemplate::Template message, | 
|  | const char* arg = nullptr) { | 
|  | if (!is_valid_let_pattern()) return; | 
|  | invalid_productions_ |= LetPatternProduction; | 
|  | Add(Error(loc, message, kLetPatternProduction, arg)); | 
|  | } | 
|  |  | 
|  | void Accumulate(ExpressionClassifier* inner, unsigned productions) { | 
|  | DCHECK_EQ(inner->reported_errors_, reported_errors_); | 
|  | DCHECK_EQ(inner->reported_errors_begin_, reported_errors_end_); | 
|  | DCHECK_EQ(inner->reported_errors_end_, reported_errors_->length()); | 
|  | // Propagate errors from inner, but don't overwrite already recorded | 
|  | // errors. | 
|  | unsigned non_arrow_inner_invalid_productions = | 
|  | inner->invalid_productions_ & ~ArrowFormalParametersProduction; | 
|  | if (non_arrow_inner_invalid_productions) { | 
|  | unsigned errors = non_arrow_inner_invalid_productions & productions & | 
|  | ~invalid_productions_; | 
|  | // The result will continue to be a valid arrow formal parameters if the | 
|  | // inner expression is a valid binding pattern. | 
|  | bool copy_BP_to_AFP = false; | 
|  | if (productions & ArrowFormalParametersProduction && | 
|  | is_valid_arrow_formal_parameters()) { | 
|  | // Also copy function properties if expecting an arrow function | 
|  | // parameter. | 
|  | function_properties_ |= inner->function_properties_; | 
|  | if (!inner->is_valid_binding_pattern()) { | 
|  | copy_BP_to_AFP = true; | 
|  | invalid_productions_ |= ArrowFormalParametersProduction; | 
|  | } | 
|  | } | 
|  | // Traverse the list of errors reported by the inner classifier | 
|  | // to copy what's necessary. | 
|  | if (errors != 0 || copy_BP_to_AFP) { | 
|  | invalid_productions_ |= errors; | 
|  | int binding_pattern_index = inner->reported_errors_end_; | 
|  | for (int i = inner->reported_errors_begin_; | 
|  | i < inner->reported_errors_end_; i++) { | 
|  | int k = reported_errors_->at(i).kind; | 
|  | if (errors & (1 << k)) Copy(i); | 
|  | // Check if it's a BP error that has to be copied to an AFP error. | 
|  | if (k == kBindingPatternProduction && copy_BP_to_AFP) { | 
|  | if (reported_errors_end_ <= i) { | 
|  | // If the BP error itself has not already been copied, | 
|  | // copy it now and change it to an AFP error. | 
|  | Copy(i); | 
|  | reported_errors_->at(reported_errors_end_-1).kind = | 
|  | kArrowFormalParametersProduction; | 
|  | } else { | 
|  | // Otherwise, if the BP error was already copied, keep its | 
|  | // position and wait until the end of the traversal. | 
|  | DCHECK_EQ(reported_errors_end_, i+1); | 
|  | binding_pattern_index = i; | 
|  | } | 
|  | } | 
|  | } | 
|  | // Do we still have to copy the BP error to an AFP error? | 
|  | if (binding_pattern_index < inner->reported_errors_end_) { | 
|  | // If there's still unused space in the list of the inner | 
|  | // classifier, copy it there, otherwise add it to the end | 
|  | // of the list. | 
|  | if (reported_errors_end_ < inner->reported_errors_end_) | 
|  | Copy(binding_pattern_index); | 
|  | else | 
|  | Add(reported_errors_->at(binding_pattern_index)); | 
|  | reported_errors_->at(reported_errors_end_-1).kind = | 
|  | kArrowFormalParametersProduction; | 
|  | } | 
|  | } | 
|  | } | 
|  | reported_errors_->Rewind(reported_errors_end_); | 
|  | inner->reported_errors_begin_ = inner->reported_errors_end_ = | 
|  | reported_errors_end_; | 
|  | } | 
|  |  | 
|  | 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_); | 
|  | } | 
|  |  | 
|  | ExpressionClassifier* previous() const { return previous_; } | 
|  |  | 
|  | private: | 
|  | V8_INLINE const Error& reported_error(ErrorKind kind) const { | 
|  | if (invalid_productions_ & (1 << kind)) { | 
|  | for (int i = reported_errors_begin_; i < reported_errors_end_; i++) { | 
|  | if (reported_errors_->at(i).kind == kind) | 
|  | return reported_errors_->at(i); | 
|  | } | 
|  | UNREACHABLE(); | 
|  | } | 
|  | // We should only be looking for an error when we know that one has | 
|  | // been reported.  But we're not...  So this is to make sure we have | 
|  | // the same behaviour. | 
|  | UNREACHABLE(); | 
|  |  | 
|  | // Make MSVC happy by returning an error from this inaccessible path. | 
|  | static Error none; | 
|  | return none; | 
|  | } | 
|  |  | 
|  | // Adds e to the end of the list of reported errors for this classifier. | 
|  | // It is expected that this classifier is the last one in the stack. | 
|  | V8_INLINE void Add(const Error& e) { | 
|  | DCHECK_EQ(reported_errors_end_, reported_errors_->length()); | 
|  | reported_errors_->Add(e, 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_++; | 
|  | } | 
|  |  | 
|  | typename Types::Base* base_; | 
|  | ExpressionClassifier* previous_; | 
|  | Zone* zone_; | 
|  | ZoneList<Error>* reported_errors_; | 
|  | DuplicateFinder* duplicate_finder_; | 
|  | unsigned invalid_productions_ : 14; | 
|  | unsigned function_properties_ : 2; | 
|  | // The uint16_t for reported_errors_begin_ and reported_errors_end_ will | 
|  | // not be enough in the case of a long series of expressions using nested | 
|  | // classifiers, e.g., a long sequence of assignments, as in: | 
|  | // literals with spreads, as in: | 
|  | // var N=65536; eval("var x;" + "x=".repeat(N) + "42"); | 
|  | // This should not be a problem, as such things currently fail with a | 
|  | // stack overflow while parsing. | 
|  | uint16_t reported_errors_begin_; | 
|  | uint16_t reported_errors_end_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(ExpressionClassifier); | 
|  | }; | 
|  |  | 
|  |  | 
|  | #undef ERROR_CODES | 
|  |  | 
|  |  | 
|  | }  // namespace internal | 
|  | }  // namespace v8 | 
|  |  | 
|  | #endif  // V8_PARSING_EXPRESSION_CLASSIFIER_H_ |