Commit 7b11480f authored by Florian Sattler's avatar Florian Sattler Committed by Commit Bot

[preparser] Remove ExpressionClassifier error tracking in the PreParser.

PreParser now does not longer track which kind of error occurred.
If we see an error we reparse with the parser and report the error.
Furthermore, this fixes tests in test-parsing.

Change-Id: I1860949fab4d65ff4a5a1b63796c7574494f9d50
Reviewed-on: https://chromium-review.googlesource.com/1231173
Commit-Queue: Florian Sattler <sattlerf@google.com>
Reviewed-by: 's avatarAdam Klein <adamk@chromium.org>
Reviewed-by: 's avatarToon Verwaest <verwaest@chromium.org>
Reviewed-by: 's avatarMarja Hölttä <marja@chromium.org>
Cr-Commit-Position: refs/heads/master@{#56281}
parent e4c650ad
......@@ -5,6 +5,8 @@
#ifndef V8_PARSING_EXPRESSION_CLASSIFIER_H_
#define V8_PARSING_EXPRESSION_CLASSIFIER_H_
#include <type_traits>
#include "src/messages.h"
#include "src/parsing/scanner.h"
......@@ -46,14 +48,38 @@ class DuplicateFinder;
// by calling the method Discard. Both actions result in removing the
// classifier from the parser's stack.
// Expression classifier is split into four parts. The base implementing the
// general expression classifier logic. Two parts that implement the error
// tracking interface, where one is the actual implementation and the other is
// an empty class providing only the interface without logic. The expression
// classifier class then combines the other parts and provides the full
// expression classifier interface by inheriting conditionally, controlled by
// Types::ExpressionClassifierReportErrors, either from the ErrorTracker or the
// EmptyErrorTracker.
//
// Base
// / \
// / \
// / \
// / \
// ErrorTracker EmptyErrorTracker
// \ /
// \ /
// \ /
// \ /
// ExpressionClassifier
template <typename Types>
class ExpressionClassifier {
class ExpressionClassifier;
template <typename Types, typename ErrorTracker>
class ExpressionClassifierBase {
public:
enum ErrorKind : unsigned {
#define DEFINE_ERROR_KIND(NAME, CODE) k##NAME = CODE,
ERROR_CODES(DEFINE_ERROR_KIND)
#undef DEFINE_ERROR_KIND
kUnusedError = 15 // Larger than error codes; should fit in 4 bits
kUnusedError = 15 // Larger than error codes; should fit in 4 bits
};
struct Error {
......@@ -85,23 +111,14 @@ class ExpressionClassifier {
};
// clang-format on
explicit ExpressionClassifier(typename Types::Base* base,
DuplicateFinder* duplicate_finder = nullptr)
explicit ExpressionClassifierBase(typename Types::Base* base,
DuplicateFinder* duplicate_finder = nullptr)
: base_(base),
previous_(base->classifier_),
zone_(base->impl()->zone()),
reported_errors_(base->impl()->GetReportedErrorList()),
duplicate_finder_(duplicate_finder),
invalid_productions_(0),
is_non_simple_parameter_list_(0) {
base->classifier_ = this;
reported_errors_begin_ = reported_errors_end_ = reported_errors_->length();
}
is_non_simple_parameter_list_(0) {}
V8_INLINE ~ExpressionClassifier() {
Discard();
if (base_->classifier_ == this) base_->classifier_ = previous_;
}
virtual ~ExpressionClassifierBase() = default;
V8_INLINE bool is_valid(unsigned productions) const {
return (invalid_productions_ & productions) == 0;
......@@ -149,80 +166,338 @@ class ExpressionClassifier {
return is_valid(AsyncArrowFormalParametersProduction);
}
V8_INLINE bool is_simple_parameter_list() const {
return !is_non_simple_parameter_list_;
}
V8_INLINE void RecordNonSimpleParameter() {
is_non_simple_parameter_list_ = 1;
}
V8_INLINE void Accumulate(ExpressionClassifier<Types>* const inner,
unsigned productions) {
#ifdef DEBUG
static_cast<ErrorTracker*>(this)->CheckErrorPositions(inner);
#endif
// Propagate errors from inner, but don't overwrite already recorded
// errors.
unsigned non_arrow_inner_invalid_productions =
inner->invalid_productions_ & ~ArrowFormalParametersProduction;
if (non_arrow_inner_invalid_productions) {
unsigned errors = non_arrow_inner_invalid_productions & productions &
~this->invalid_productions_;
// The result will continue to be a valid arrow formal parameters if the
// inner expression is a valid binding pattern.
bool copy_BP_to_AFP = false;
if (productions & ArrowFormalParametersProduction &&
this->is_valid_arrow_formal_parameters()) {
// Also whether we've seen any non-simple parameters
// if expecting an arrow function parameter.
this->is_non_simple_parameter_list_ |=
inner->is_non_simple_parameter_list_;
if (!inner->is_valid_binding_pattern()) {
copy_BP_to_AFP = true;
this->invalid_productions_ |= ArrowFormalParametersProduction;
}
}
if (errors != 0 || copy_BP_to_AFP) {
this->invalid_productions_ |= errors;
static_cast<ErrorTracker*>(this)->AccumulateErrorImpl(
inner, productions, errors, copy_BP_to_AFP);
}
}
static_cast<ErrorTracker*>(this)->RewindErrors(inner);
}
protected:
typename Types::Base* base_;
DuplicateFinder* duplicate_finder_;
unsigned invalid_productions_ : kUnusedError;
STATIC_ASSERT(kUnusedError <= 15);
unsigned is_non_simple_parameter_list_ : 1;
};
template <typename Types>
class ExpressionClassifierErrorTracker
: public ExpressionClassifierBase<Types,
ExpressionClassifierErrorTracker<Types>> {
public:
using BaseClassType =
ExpressionClassifierBase<Types, ExpressionClassifierErrorTracker<Types>>;
using typename BaseClassType::Error;
using typename BaseClassType::ErrorKind;
using TP = typename BaseClassType::TargetProduction;
ExpressionClassifierErrorTracker(typename Types::Base* base,
DuplicateFinder* duplicate_finder)
: BaseClassType(base, duplicate_finder),
reported_errors_(base->impl()->GetReportedErrorList()) {
reported_errors_begin_ = reported_errors_end_ = reported_errors_->length();
}
~ExpressionClassifierErrorTracker() override { Discard(); }
V8_INLINE void Discard() {
if (reported_errors_end_ == reported_errors_->length()) {
reported_errors_->Rewind(reported_errors_begin_);
reported_errors_end_ = reported_errors_begin_;
}
DCHECK_EQ(reported_errors_begin_, reported_errors_end_);
}
protected:
V8_INLINE const Error& reported_error(ErrorKind kind) const {
if (this->invalid_productions_ & (1 << kind)) {
for (int i = reported_errors_begin_; i < reported_errors_end_; i++) {
if (reported_errors_->at(i).kind == kind)
return reported_errors_->at(i);
}
UNREACHABLE();
}
// We should only be looking for an error when we know that one has
// been reported. But we're not... So this is to make sure we have
// the same behaviour.
UNREACHABLE();
// Make MSVC happy by returning an error from this inaccessible path.
static Error none;
return none;
}
// Adds e to the end of the list of reported errors for this classifier.
// It is expected that this classifier is the last one in the stack.
V8_INLINE void Add(const Error& e) {
DCHECK_EQ(reported_errors_end_, reported_errors_->length());
reported_errors_->Add(e, this->base_->impl()->zone());
reported_errors_end_++;
}
// Copies the error at position i of the list of reported errors, so that
// it becomes the last error reported for this classifier. Position i
// could be either after the existing errors of this classifier (i.e.,
// in an inner classifier) or it could be an existing error (in case a
// copy is needed).
V8_INLINE void Copy(int i) {
DCHECK_LT(i, reported_errors_->length());
if (reported_errors_end_ != i)
reported_errors_->at(reported_errors_end_) = reported_errors_->at(i);
reported_errors_end_++;
}
private:
#ifdef DEBUG
V8_INLINE void CheckErrorPositions(ExpressionClassifier<Types>* const inner) {
DCHECK_EQ(inner->reported_errors_, this->reported_errors_);
DCHECK_EQ(inner->reported_errors_begin_, this->reported_errors_end_);
DCHECK_EQ(inner->reported_errors_end_, this->reported_errors_->length());
}
#endif
V8_INLINE void RewindErrors(ExpressionClassifier<Types>* const inner) {
this->reported_errors_->Rewind(this->reported_errors_end_);
inner->reported_errors_begin_ = inner->reported_errors_end_ =
this->reported_errors_end_;
}
void AccumulateErrorImpl(ExpressionClassifier<Types>* const inner,
unsigned productions, unsigned errors,
bool copy_BP_to_AFP) {
// Traverse the list of errors reported by the inner classifier
// to copy what's necessary.
int binding_pattern_index = inner->reported_errors_end_;
for (int i = inner->reported_errors_begin_; i < inner->reported_errors_end_;
i++) {
int k = this->reported_errors_->at(i).kind;
if (errors & (1 << k)) this->Copy(i);
// Check if it's a BP error that has to be copied to an AFP error.
if (k == ErrorKind::kBindingPatternProduction && copy_BP_to_AFP) {
if (this->reported_errors_end_ <= i) {
// If the BP error itself has not already been copied,
// copy it now and change it to an AFP error.
this->Copy(i);
this->reported_errors_->at(this->reported_errors_end_ - 1).kind =
ErrorKind::kArrowFormalParametersProduction;
} else {
// Otherwise, if the BP error was already copied, keep its
// position and wait until the end of the traversal.
DCHECK_EQ(this->reported_errors_end_, i + 1);
binding_pattern_index = i;
}
}
}
// Do we still have to copy the BP error to an AFP error?
if (binding_pattern_index < inner->reported_errors_end_) {
// If there's still unused space in the list of the inner
// classifier, copy it there, otherwise add it to the end
// of the list.
if (this->reported_errors_end_ < inner->reported_errors_end_)
this->Copy(binding_pattern_index);
else
Add(this->reported_errors_->at(binding_pattern_index));
this->reported_errors_->at(this->reported_errors_end_ - 1).kind =
ErrorKind::kArrowFormalParametersProduction;
}
}
private:
ZoneList<Error>* reported_errors_;
// The uint16_t for reported_errors_begin_ and reported_errors_end_ will
// not be enough in the case of a long series of expressions using nested
// classifiers, e.g., a long sequence of assignments, as in:
// literals with spreads, as in:
// var N=65536; eval("var x;" + "x=".repeat(N) + "42");
// This should not be a problem, as such things currently fail with a
// stack overflow while parsing.
uint16_t reported_errors_begin_;
uint16_t reported_errors_end_;
friend BaseClassType;
};
template <typename Types>
class ExpressionClassifierEmptyErrorTracker
: public ExpressionClassifierBase<
Types, ExpressionClassifierEmptyErrorTracker<Types>> {
public:
using BaseClassType =
ExpressionClassifierBase<Types,
ExpressionClassifierEmptyErrorTracker<Types>>;
using typename BaseClassType::Error;
using typename BaseClassType::ErrorKind;
using TP = typename BaseClassType::TargetProduction;
ExpressionClassifierEmptyErrorTracker(typename Types::Base* base,
DuplicateFinder* duplicate_finder)
: BaseClassType(base, duplicate_finder) {}
V8_INLINE void Discard() {}
protected:
V8_INLINE const Error& reported_error(ErrorKind kind) const {
static Error none;
return none;
}
V8_INLINE void Add(const Error& e) {}
private:
#ifdef DEBUG
V8_INLINE void CheckErrorPositions(ExpressionClassifier<Types>* const inner) {
}
#endif
V8_INLINE void RewindErrors(ExpressionClassifier<Types>* const inner) {}
V8_INLINE void AccumulateErrorImpl(ExpressionClassifier<Types>* const inner,
unsigned productions, unsigned errors,
bool copy_BP_to_AFP) {}
friend BaseClassType;
};
template <typename Types>
class ExpressionClassifier
: public std::conditional<
Types::ExpressionClassifierReportErrors,
ExpressionClassifierErrorTracker<Types>,
ExpressionClassifierEmptyErrorTracker<Types>>::type {
static constexpr bool ReportErrors = Types::ExpressionClassifierReportErrors;
public:
using BaseClassType = typename std::conditional<
Types::ExpressionClassifierReportErrors,
typename ExpressionClassifierErrorTracker<Types>::BaseClassType,
typename ExpressionClassifierEmptyErrorTracker<Types>::BaseClassType>::
type;
using typename BaseClassType::Error;
using typename BaseClassType::ErrorKind;
using TP = typename BaseClassType::TargetProduction;
explicit ExpressionClassifier(typename Types::Base* base,
DuplicateFinder* duplicate_finder = nullptr)
: std::conditional<Types::ExpressionClassifierReportErrors,
ExpressionClassifierErrorTracker<Types>,
ExpressionClassifierEmptyErrorTracker<Types>>::
type(base, duplicate_finder),
previous_(base->classifier_) {
base->classifier_ = this;
}
V8_INLINE ~ExpressionClassifier() override {
if (this->base_->classifier_ == this) this->base_->classifier_ = previous_;
}
V8_INLINE const Error& expression_error() const {
return reported_error(kExpressionProduction);
return this->reported_error(ErrorKind::kExpressionProduction);
}
V8_INLINE const Error& formal_parameter_initializer_error() const {
return reported_error(kFormalParameterInitializerProduction);
return this->reported_error(
ErrorKind::kFormalParameterInitializerProduction);
}
V8_INLINE const Error& binding_pattern_error() const {
return reported_error(kBindingPatternProduction);
return this->reported_error(ErrorKind::kBindingPatternProduction);
}
V8_INLINE const Error& assignment_pattern_error() const {
return reported_error(kAssignmentPatternProduction);
return this->reported_error(ErrorKind::kAssignmentPatternProduction);
}
V8_INLINE const Error& arrow_formal_parameters_error() const {
return reported_error(kArrowFormalParametersProduction);
return this->reported_error(ErrorKind::kArrowFormalParametersProduction);
}
V8_INLINE const Error& duplicate_formal_parameter_error() const {
return reported_error(kDistinctFormalParametersProduction);
return this->reported_error(ErrorKind::kDistinctFormalParametersProduction);
}
V8_INLINE const Error& strict_mode_formal_parameter_error() const {
return reported_error(kStrictModeFormalParametersProduction);
return this->reported_error(
ErrorKind::kStrictModeFormalParametersProduction);
}
V8_INLINE const Error& let_pattern_error() const {
return reported_error(kLetPatternProduction);
return this->reported_error(ErrorKind::kLetPatternProduction);
}
V8_INLINE const Error& async_arrow_formal_parameters_error() const {
return reported_error(kAsyncArrowFormalParametersProduction);
return this->reported_error(
ErrorKind::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;
}
V8_INLINE bool does_error_reporting() { return ReportErrors; }
void RecordExpressionError(const Scanner::Location& loc,
MessageTemplate::Template message,
const char* arg = nullptr) {
if (!is_valid_expression()) return;
invalid_productions_ |= ExpressionProduction;
Add(Error(loc, message, kExpressionProduction, arg));
if (!this->is_valid_expression()) return;
this->invalid_productions_ |= TP::ExpressionProduction;
this->Add(Error(loc, message, ErrorKind::kExpressionProduction, arg));
}
void RecordFormalParameterInitializerError(const Scanner::Location& loc,
MessageTemplate::Template message,
const char* arg = nullptr) {
if (!is_valid_formal_parameter_initializer()) return;
invalid_productions_ |= FormalParameterInitializerProduction;
Add(Error(loc, message, kFormalParameterInitializerProduction, arg));
if (!this->is_valid_formal_parameter_initializer()) return;
this->invalid_productions_ |= TP::FormalParameterInitializerProduction;
this->Add(Error(loc, message,
ErrorKind::kFormalParameterInitializerProduction, arg));
}
void RecordBindingPatternError(const Scanner::Location& loc,
MessageTemplate::Template message,
const char* arg = nullptr) {
if (!is_valid_binding_pattern()) return;
invalid_productions_ |= BindingPatternProduction;
Add(Error(loc, message, kBindingPatternProduction, arg));
if (!this->is_valid_binding_pattern()) return;
this->invalid_productions_ |= TP::BindingPatternProduction;
this->Add(Error(loc, message, ErrorKind::kBindingPatternProduction, arg));
}
void RecordAssignmentPatternError(const Scanner::Location& loc,
MessageTemplate::Template message,
const char* arg = nullptr) {
if (!is_valid_assignment_pattern()) return;
invalid_productions_ |= AssignmentPatternProduction;
Add(Error(loc, message, kAssignmentPatternProduction, arg));
if (!this->is_valid_assignment_pattern()) return;
this->invalid_productions_ |= TP::AssignmentPatternProduction;
this->Add(
Error(loc, message, ErrorKind::kAssignmentPatternProduction, arg));
}
void RecordPatternError(const Scanner::Location& loc,
......@@ -235,24 +510,26 @@ class ExpressionClassifier {
void RecordArrowFormalParametersError(const Scanner::Location& loc,
MessageTemplate::Template message,
const char* arg = nullptr) {
if (!is_valid_arrow_formal_parameters()) return;
invalid_productions_ |= ArrowFormalParametersProduction;
Add(Error(loc, message, kArrowFormalParametersProduction, arg));
if (!this->is_valid_arrow_formal_parameters()) return;
this->invalid_productions_ |= TP::ArrowFormalParametersProduction;
this->Add(
Error(loc, message, ErrorKind::kArrowFormalParametersProduction, arg));
}
void RecordAsyncArrowFormalParametersError(const Scanner::Location& loc,
MessageTemplate::Template message,
const char* arg = nullptr) {
if (!is_valid_async_arrow_formal_parameters()) return;
invalid_productions_ |= AsyncArrowFormalParametersProduction;
Add(Error(loc, message, kAsyncArrowFormalParametersProduction, arg));
if (!this->is_valid_async_arrow_formal_parameters()) return;
this->invalid_productions_ |= TP::AsyncArrowFormalParametersProduction;
this->Add(Error(loc, message,
ErrorKind::kAsyncArrowFormalParametersProduction, arg));
}
void RecordDuplicateFormalParameterError(const Scanner::Location& loc) {
if (!is_valid_formal_parameter_list_without_duplicates()) return;
invalid_productions_ |= DistinctFormalParametersProduction;
Add(Error(loc, MessageTemplate::kParamDupe,
kDistinctFormalParametersProduction));
if (!this->is_valid_formal_parameter_list_without_duplicates()) return;
this->invalid_productions_ |= TP::DistinctFormalParametersProduction;
this->Add(Error(loc, MessageTemplate::kParamDupe,
ErrorKind::kDistinctFormalParametersProduction));
}
// Record a binding that would be invalid in strict mode. Confusingly this
......@@ -261,160 +538,30 @@ class ExpressionClassifier {
void RecordStrictModeFormalParameterError(const Scanner::Location& loc,
MessageTemplate::Template message,
const char* arg = nullptr) {
if (!is_valid_strict_mode_formal_parameters()) return;
invalid_productions_ |= StrictModeFormalParametersProduction;
Add(Error(loc, message, kStrictModeFormalParametersProduction, arg));
if (!this->is_valid_strict_mode_formal_parameters()) return;
this->invalid_productions_ |= TP::StrictModeFormalParametersProduction;
this->Add(Error(loc, message,
ErrorKind::kStrictModeFormalParametersProduction, arg));
}
void RecordLetPatternError(const Scanner::Location& loc,
MessageTemplate::Template message,
const char* arg = nullptr) {
if (!is_valid_let_pattern()) return;
invalid_productions_ |= LetPatternProduction;
Add(Error(loc, message, kLetPatternProduction, arg));
}
void Accumulate(ExpressionClassifier* inner, unsigned productions) {
DCHECK_EQ(inner->reported_errors_, reported_errors_);
DCHECK_EQ(inner->reported_errors_begin_, reported_errors_end_);
DCHECK_EQ(inner->reported_errors_end_, reported_errors_->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 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_->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_);
if (!this->is_valid_let_pattern()) return;
this->invalid_productions_ |= TP::LetPatternProduction;
this->Add(Error(loc, message, ErrorKind::kLetPatternProduction, arg));
}
ExpressionClassifier* previous() const { return previous_; }
private:
V8_INLINE const Error& reported_error(ErrorKind kind) const {
if (invalid_productions_ & (1 << kind)) {
for (int i = reported_errors_begin_; i < reported_errors_end_; i++) {
if (reported_errors_->at(i).kind == kind)
return reported_errors_->at(i);
}
UNREACHABLE();
}
// We should only be looking for an error when we know that one has
// been reported. But we're not... So this is to make sure we have
// the same behaviour.
UNREACHABLE();
// Make MSVC happy by returning an error from this inaccessible path.
static Error none;
return none;
}
// Adds e to the end of the list of reported errors for this classifier.
// It is expected that this classifier is the last one in the stack.
V8_INLINE void Add(const Error& e) {
DCHECK_EQ(reported_errors_end_, reported_errors_->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_ : 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
......
......@@ -945,7 +945,11 @@ class ParserBase {
void ReportClassifierError(
const typename ExpressionClassifier::Error& error) {
impl()->ReportMessageAt(error.location, error.message, error.arg);
if (classifier()->does_error_reporting()) {
impl()->ReportMessageAt(error.location, error.message, error.arg);
} else {
impl()->ReportUnidentifiableError();
}
}
void ValidateExpression(bool* ok) {
......@@ -4393,17 +4397,29 @@ ParserBase<Impl>::ParseArrowFunctionLiteral(
int dummy_num_parameters = -1;
DCHECK_NE(kind & FunctionKind::kArrowFunction, 0);
FunctionLiteral::EagerCompileHint hint;
bool parse_result = impl()->SkipFunction(
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);
DCHECK(parse_result);
USE(parse_result);
DCHECK_NULL(produced_preparsed_scope_data);
// Discard any queued destructuring assignments which appeared
// in this function's parameter list, and which were adopted
// into this function state, above.
function_state.RewindDestructuringAssignments(0);
if (did_preparse_successfully) {
// Discard any queued destructuring assignments which appeared
// in this function's parameter list, and which were adopted
// into this function state, above.
function_state.RewindDestructuringAssignments(0);
} else {
// In case we did not sucessfully preparse the function because of an
// unidentified error we do a full reparse to return the error.
Consume(Token::LBRACE);
body = impl()->NewStatementList(8);
ParseFunctionBody(body, impl()->NullIdentifier(), kNoSourcePosition,
formal_parameters, kind,
FunctionLiteral::kAnonymousExpression, ok);
CHECK(!*ok);
return impl()->NullExpression();
}
} else {
Consume(Token::LBRACE);
body = impl()->NewStatementList(8);
......
......@@ -2553,12 +2553,12 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
// abort lazy parsing if it suspects that wasn't a good idea. If so (in
// which case the parser is expected to have backtracked), or if we didn't
// try to lazy parse in the first place, we'll have to parse eagerly.
bool did_preparse =
bool did_preparse_successfully =
should_preparse &&
SkipFunction(function_name, kind, function_type, scope, &num_parameters,
&produced_preparsed_scope_data, is_lazy_inner_function,
is_lazy_top_level_function, &eager_compile_hint, CHECK_OK);
if (!did_preparse) {
if (!did_preparse_successfully) {
body = ParseFunction(
function_name, pos, kind, function_type, scope, &num_parameters,
&function_length, &has_duplicate_parameters, &expected_property_count,
......@@ -2577,7 +2577,7 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
reinterpret_cast<const char*>(function_name->raw_data()),
function_name->byte_length());
}
if (V8_UNLIKELY(FLAG_runtime_stats) && did_preparse) {
if (V8_UNLIKELY(FLAG_runtime_stats) && did_preparse_successfully) {
const RuntimeCallCounterId counters[2][2] = {
{RuntimeCallCounterId::kPreParseBackgroundNoVariableResolution,
RuntimeCallCounterId::kPreParseNoVariableResolution},
......@@ -2691,6 +2691,14 @@ bool Parser::SkipFunction(
// Propagate stack overflow.
set_stack_overflow();
*ok = false;
} else if (pending_error_handler()->ErrorUnidentifiableByPreParser()) {
// If we encounter an error that the preparser can not identify we reset to
// the state before preparsing. The caller may then fully parse the function
// to identify the actual error.
bookmark.Apply();
function_scope->ResetAfterPreparsing(ast_value_factory(), true);
pending_error_handler()->ResetUnidentifiableError();
return false;
} else if (pending_error_handler()->has_pending_error()) {
*ok = false;
} else {
......
......@@ -144,6 +144,8 @@ struct ParserTypes<Parser> {
typedef ParserTarget Target;
typedef ParserTargetScope TargetScope;
static constexpr bool ExpressionClassifierReportErrors = true;
};
class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
......@@ -179,7 +181,8 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
private:
friend class ParserBase<Parser>;
friend class v8::internal::ExpressionClassifier<ParserTypes<Parser>>;
friend class v8::internal::ExpressionClassifierErrorTracker<
ParserTypes<Parser>>;
friend bool v8::internal::parsing::ParseProgram(ParseInfo*, Isolate*);
friend bool v8::internal::parsing::ParseFunction(
ParseInfo*, Handle<SharedFunctionInfo> shared_info, Isolate*);
......@@ -448,6 +451,13 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
// by parsing the function with PreParser. Consumes the ending }.
// If may_abort == true, the (pre-)parser may decide to abort skipping
// in order to force the function to be eagerly parsed, after all.
// In case the preparser detects an error it cannot identify, it resets the
// scanner- and preparser state to the initial one, before PreParsing the
// function.
// SkipFunction returns true if it correctly parsed the function, including
// cases where we detect an error. It returns false, if we needed to stop
// parsing or could not identify an error correctly, meaning the caller needs
// to fully reparse. In this case it resets the scanner and preparser state.
bool SkipFunction(const AstRawString* function_name, FunctionKind kind,
FunctionLiteral::FunctionType function_type,
DeclarationScope* function_scope, int* num_parameters,
......@@ -782,6 +792,10 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
arg, error_type);
}
// Dummy implementation. The parser should never have a unidentifiable
// error.
V8_INLINE void ReportUnidentifiableError() { UNREACHABLE(); }
void ReportMessageAt(Scanner::Location source_location,
MessageTemplate::Template message,
const AstRawString* arg,
......
......@@ -128,7 +128,6 @@ PreParser::PreParseResult PreParser::PreParseFunction(
function_scope->set_is_being_lazily_parsed(true);
#endif
DCHECK(!track_unresolved_variables_);
track_unresolved_variables_ =
ShouldTrackUnresolvedVariables(is_inner_function);
......@@ -168,7 +167,11 @@ PreParser::PreParseResult PreParser::PreParseFunction(
formals_classifier.reset(new ExpressionClassifier(this, &duplicate_finder));
// We return kPreParseSuccess in failure cases too - errors are retrieved
// separately by Parser::SkipLazyFunctionBody.
ParseFormalParameterList(&formals, CHECK_OK_VALUE(kPreParseSuccess));
ParseFormalParameterList(
&formals,
CHECK_OK_VALUE(pending_error_handler()->ErrorUnidentifiableByPreParser()
? kPreParseNotIdentifiableError
: kPreParseSuccess));
Expect(Token::RPAREN, CHECK_OK_VALUE(kPreParseSuccess));
int formals_end_position = scanner()->location().end_pos;
......@@ -221,10 +224,15 @@ PreParser::PreParseResult PreParser::PreParseFunction(
track_unresolved_variables_ = false;
if (result == kLazyParsingAborted) {
DCHECK(!pending_error_handler()->ErrorUnidentifiableByPreParser());
return kPreParseAbort;
} else if (stack_overflow()) {
DCHECK(!pending_error_handler()->ErrorUnidentifiableByPreParser());
return kPreParseStackOverflow;
} else if (!*ok) {
if (pending_error_handler()->ErrorUnidentifiableByPreParser()) {
return kPreParseNotIdentifiableError;
}
DCHECK(pending_error_handler()->has_pending_error());
} else {
DCHECK_EQ(Token::RBRACE, scanner()->peek());
......@@ -235,19 +243,25 @@ PreParser::PreParseResult PreParser::PreParseFunction(
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_VALUE(kPreParseSuccess));
ValidateFormalParameters(
function_scope->language_mode(), allow_duplicate_parameters,
CHECK_OK_VALUE(
pending_error_handler()->ErrorUnidentifiableByPreParser()
? kPreParseNotIdentifiableError
: kPreParseSuccess));
*produced_preparsed_scope_data = ProducedPreParsedScopeData::For(
preparsed_scope_data_builder_, main_zone());
}
DCHECK(!pending_error_handler()->ErrorUnidentifiableByPreParser());
if (is_strict(function_scope->language_mode())) {
int end_pos = scanner()->location().end_pos;
CheckStrictOctalLiteral(function_scope->start_position(), end_pos, ok);
}
}
DCHECK(!pending_error_handler()->ErrorUnidentifiableByPreParser());
return kPreParseSuccess;
}
......
......@@ -953,6 +953,7 @@ struct ParserTypes<PreParser> {
typedef PreParserFuncNameInferrer FuncNameInferrer;
typedef PreParserSourceRange SourceRange;
typedef PreParserSourceRangeScope SourceRangeScope;
static constexpr bool ExpressionClassifierReportErrors = false;
};
......@@ -980,6 +981,7 @@ class PreParser : public ParserBase<PreParser> {
enum PreParseResult {
kPreParseStackOverflow,
kPreParseAbort,
kPreParseNotIdentifiableError,
kPreParseSuccess
};
......@@ -1557,6 +1559,10 @@ class PreParser : public ParserBase<PreParser> {
arg, error_type);
}
V8_INLINE void ReportUnidentifiableError() {
pending_error_handler()->SetUnidentifiableError();
}
V8_INLINE void ReportMessageAt(Scanner::Location source_location,
MessageTemplate::Template message,
const PreParserIdentifier& arg,
......
......@@ -62,6 +62,12 @@ class PendingCompilationErrorHandler {
Handle<String> FormatErrorMessageForTest(Isolate* isolate) const;
bool SetUnidentifiableError() { return unidentifiable_error_ = true; }
bool ResetUnidentifiableError() { return unidentifiable_error_ = false; }
bool ErrorUnidentifiableByPreParser() { return unidentifiable_error_; }
private:
class MessageDetails {
public:
......@@ -97,6 +103,7 @@ class PendingCompilationErrorHandler {
bool has_pending_error_;
bool stack_overflow_;
bool unidentifiable_error_ = false;
MessageDetails error_details_;
ParseErrorType error_type_;
......
......@@ -1550,8 +1550,10 @@ void TestParserSyncWithFlags(i::Handle<i::String> source,
"However, the preparser succeeded",
source->ToCString().get(), message_string->ToCString().get());
}
// Check that preparser and parser produce the same error.
if (test_preparser && !ignore_error_msg) {
// Check that preparser and parser produce the same error, except for cases
// where we do not track errors in the preparser.
if (test_preparser && !ignore_error_msg &&
!pending_error_handler.ErrorUnidentifiableByPreParser()) {
i::Handle<i::String> preparser_message =
pending_error_handler.FormatErrorMessageForTest(CcTest::i_isolate());
if (!i::String::Equals(isolate, message_string, preparser_message)) {
......@@ -2041,7 +2043,7 @@ TEST(ErrorsFutureStrictReservedWords) {
{"() => {", "}"},
{nullptr, nullptr}};
const char* invalid_statements[] = {
FUTURE_STRICT_RESERVED_LEX_BINDINGS("let") nullptr};
FUTURE_STRICT_RESERVED_LEX_BINDINGS(let) nullptr};
RunParserSyncTest(non_strict_contexts, invalid_statements, kError);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment