// 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"
|
#include "src/zone/zone-containers.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),
|
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 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),
|
is_non_simple_parameter_list_(0) {
|
base->classifier_ = this;
|
reported_errors_begin_ = reported_errors_end_ = reported_errors_->size();
|
}
|
|
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 !is_non_simple_parameter_list_;
|
}
|
|
V8_INLINE void RecordNonSimpleParameter() {
|
is_non_simple_parameter_list_ = 1;
|
}
|
|
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 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_->size());
|
// Propagate errors from inner, but don't overwrite already recorded
|
// errors.
|
unsigned non_arrow_inner_invalid_productions =
|
inner->invalid_productions_ & ~ArrowFormalParametersProduction;
|
if (non_arrow_inner_invalid_productions) {
|
unsigned errors = non_arrow_inner_invalid_productions & productions &
|
~invalid_productions_;
|
// The result will continue to be a valid arrow formal parameters if the
|
// inner expression is a valid binding pattern.
|
bool copy_BP_to_AFP = false;
|
if (productions & ArrowFormalParametersProduction &&
|
is_valid_arrow_formal_parameters()) {
|
// Also whether we've seen any non-simple parameters
|
// if expecting an arrow function parameter.
|
is_non_simple_parameter_list_ |= inner->is_non_simple_parameter_list_;
|
if (!inner->is_valid_binding_pattern()) {
|
copy_BP_to_AFP = true;
|
invalid_productions_ |= ArrowFormalParametersProduction;
|
}
|
}
|
// Traverse the list of errors reported by the inner classifier
|
// to copy what's necessary.
|
if (errors != 0 || copy_BP_to_AFP) {
|
invalid_productions_ |= errors;
|
int binding_pattern_index = inner->reported_errors_end_;
|
for (int i = inner->reported_errors_begin_;
|
i < inner->reported_errors_end_; i++) {
|
int k = reported_errors_->at(i).kind;
|
if (errors & (1 << k)) Copy(i);
|
// Check if it's a BP error that has to be copied to an AFP error.
|
if (k == kBindingPatternProduction && copy_BP_to_AFP) {
|
if (reported_errors_end_ <= i) {
|
// If the BP error itself has not already been copied,
|
// copy it now and change it to an AFP error.
|
Copy(i);
|
reported_errors_->at(reported_errors_end_-1).kind =
|
kArrowFormalParametersProduction;
|
} else {
|
// Otherwise, if the BP error was already copied, keep its
|
// position and wait until the end of the traversal.
|
DCHECK_EQ(reported_errors_end_, i+1);
|
binding_pattern_index = i;
|
}
|
}
|
}
|
// Do we still have to copy the BP error to an AFP error?
|
if (binding_pattern_index < inner->reported_errors_end_) {
|
// If there's still unused space in the list of the inner
|
// classifier, copy it there, otherwise add it to the end
|
// of the list.
|
if (reported_errors_end_ < inner->reported_errors_end_)
|
Copy(binding_pattern_index);
|
else
|
Add(reported_errors_->at(binding_pattern_index));
|
reported_errors_->at(reported_errors_end_-1).kind =
|
kArrowFormalParametersProduction;
|
}
|
}
|
}
|
reported_errors_->resize(reported_errors_end_);
|
inner->reported_errors_begin_ = inner->reported_errors_end_ =
|
reported_errors_end_;
|
}
|
|
V8_INLINE void Discard() {
|
if (reported_errors_end_ == reported_errors_->size()) {
|
reported_errors_->resize(reported_errors_begin_);
|
reported_errors_end_ = reported_errors_begin_;
|
}
|
DCHECK_EQ(reported_errors_begin_, reported_errors_end_);
|
}
|
|
ExpressionClassifier* previous() const { return previous_; }
|
|
private:
|
V8_INLINE const Error& reported_error(ErrorKind kind) const {
|
if (invalid_productions_ & (1 << kind)) {
|
for (int i = reported_errors_begin_; i < reported_errors_end_; i++) {
|
if (reported_errors_->at(i).kind == kind)
|
return reported_errors_->at(i);
|
}
|
UNREACHABLE();
|
}
|
// We should only be looking for an error when we know that one has
|
// been reported. But we're not... So this is to make sure we have
|
// the same behaviour.
|
UNREACHABLE();
|
|
// Make MSVC happy by returning an error from this inaccessible path.
|
static Error none;
|
return none;
|
}
|
|
// Adds e to the end of the list of reported errors for this classifier.
|
// It is expected that this classifier is the last one in the stack.
|
V8_INLINE void Add(const Error& e) {
|
DCHECK_EQ(reported_errors_end_, reported_errors_->size());
|
reported_errors_->push_back(e);
|
reported_errors_end_++;
|
}
|
|
// Copies the error at position i of the list of reported errors, so that
|
// it becomes the last error reported for this classifier. Position i
|
// could be either after the existing errors of this classifier (i.e.,
|
// in an inner classifier) or it could be an existing error (in case a
|
// copy is needed).
|
V8_INLINE void Copy(int i) {
|
DCHECK_LT(i, reported_errors_->size());
|
if (reported_errors_end_ != i)
|
reported_errors_->at(reported_errors_end_) = reported_errors_->at(i);
|
reported_errors_end_++;
|
}
|
|
typename Types::Base* base_;
|
ExpressionClassifier* previous_;
|
Zone* zone_;
|
ZoneVector<Error>* reported_errors_;
|
DuplicateFinder* duplicate_finder_;
|
unsigned invalid_productions_ : 15;
|
unsigned is_non_simple_parameter_list_ : 1;
|
// The uint16_t for reported_errors_begin_ and reported_errors_end_ will
|
// not be enough in the case of a long series of expressions using nested
|
// classifiers, e.g., a long sequence of assignments, as in:
|
// literals with spreads, as in:
|
// var N=65536; eval("var x;" + "x=".repeat(N) + "42");
|
// This should not be a problem, as such things currently fail with a
|
// stack overflow while parsing.
|
uint16_t reported_errors_begin_;
|
uint16_t reported_errors_end_;
|
|
DISALLOW_COPY_AND_ASSIGN(ExpressionClassifier);
|
};
|
|
|
#undef ERROR_CODES
|
|
|
} // namespace internal
|
} // namespace v8
|
|
#endif // V8_PARSING_EXPRESSION_CLASSIFIER_H_
|