Commit 560f2d8b authored by Iain Ireland's avatar Iain Ireland Committed by Commit Bot

Reland "[regexp] Rewrite error handling"

This is a reland of e80ca24c

Original change's description:
> [regexp] Rewrite error handling
>
> This patch modifies irregexp's error handling. Instead of representing
> errors as C strings, they are represented as an enumeration value
> (RegExpError), and only converted to strings when throwing the error
> object in regexp.cc. This makes it significantly easier to integrate
> into SpiderMonkey. A few notes:
>
> 1. Depending on whether the stack overflows during parsing or
>    analysis, the stack overflow message can vary ("Stack overflow" or
>    "Maximum call stack size exceeded"). I kept that behaviour in this
>    patch, under the assumption that stack overflow messages are
>    (sadly) the sorts of things that real world code ends up depending
>    on.
>
> 2. Depending on the point in code where the error was identified,
>    invalid unicode escapes could be reported as "Invalid Unicode
>    escape", "Invalid unicode escape", or "Invalid Unicode escape
>    sequence". I fervently hope that nobody depends on the specific
>    wording of a syntax error, so I standardized on the first one. (It
>    was both the most common, and the most consistent with other
>    "Invalid X escape" messages.)
>
> 3. In addition to changing the representation, this patch also adds an
>    error_pos field to RegExpParser and RegExpCompileData, which stores
>    the position at which an error occurred. This is used by
>    SpiderMonkey to provide more helpful messages about where a syntax
>    error occurred in large regular expressions.
>
> 4. This model is closer to V8's existing MessageTemplate
>    infrastructure. I considered trying to integrate it more closely
>    with MessageTemplate, but since one of our stated goals for this
>    project was to make it easier to use irregexp outside of V8, I
>    decided to hold off.
>
> R=jgruber@chromium.org
>
> Bug: v8:10303
> Change-Id: I62605fd2def2fc539f38a7e0eefa04d36e14bbde
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2091863
> Commit-Queue: Jakob Gruber <jgruber@chromium.org>
> Reviewed-by: Jakob Gruber <jgruber@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#66784}

R=jgruber@chromium.org

Bug: v8:10303
Change-Id: Iad1f11a0e0b9e525d7499aacb56c27eff9e7c7b5
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2109952Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#66798}
parent 1e1d4d82
...@@ -2833,6 +2833,8 @@ v8_source_set("v8_base_without_compiler") { ...@@ -2833,6 +2833,8 @@ v8_source_set("v8_base_without_compiler") {
"src/regexp/regexp-compiler.h", "src/regexp/regexp-compiler.h",
"src/regexp/regexp-dotprinter.cc", "src/regexp/regexp-dotprinter.cc",
"src/regexp/regexp-dotprinter.h", "src/regexp/regexp-dotprinter.h",
"src/regexp/regexp-error.cc",
"src/regexp/regexp-error.h",
"src/regexp/regexp-interpreter.cc", "src/regexp/regexp-interpreter.cc",
"src/regexp/regexp-interpreter.h", "src/regexp/regexp-interpreter.h",
"src/regexp/regexp-macro-assembler-arch.h", "src/regexp/regexp-macro-assembler-arch.h",
......
...@@ -3594,7 +3594,9 @@ template <typename... Propagators> ...@@ -3594,7 +3594,9 @@ template <typename... Propagators>
class Analysis : public NodeVisitor { class Analysis : public NodeVisitor {
public: public:
Analysis(Isolate* isolate, bool is_one_byte) Analysis(Isolate* isolate, bool is_one_byte)
: isolate_(isolate), is_one_byte_(is_one_byte), error_message_(nullptr) {} : isolate_(isolate),
is_one_byte_(is_one_byte),
error_(RegExpError::kNone) {}
void EnsureAnalyzed(RegExpNode* that) { void EnsureAnalyzed(RegExpNode* that) {
StackLimitCheck check(isolate()); StackLimitCheck check(isolate());
...@@ -3602,7 +3604,7 @@ class Analysis : public NodeVisitor { ...@@ -3602,7 +3604,7 @@ class Analysis : public NodeVisitor {
if (FLAG_correctness_fuzzer_suppressions) { if (FLAG_correctness_fuzzer_suppressions) {
FATAL("Analysis: Aborting on stack overflow"); FATAL("Analysis: Aborting on stack overflow");
} }
fail("Stack overflow"); fail(RegExpError::kAnalysisStackOverflow);
return; return;
} }
if (that->info()->been_analyzed || that->info()->being_analyzed) return; if (that->info()->been_analyzed || that->info()->being_analyzed) return;
...@@ -3612,12 +3614,12 @@ class Analysis : public NodeVisitor { ...@@ -3612,12 +3614,12 @@ class Analysis : public NodeVisitor {
that->info()->been_analyzed = true; that->info()->been_analyzed = true;
} }
bool has_failed() { return error_message_ != nullptr; } bool has_failed() { return error_ != RegExpError::kNone; }
const char* error_message() { RegExpError error() {
DCHECK(error_message_ != nullptr); DCHECK(error_ != RegExpError::kNone);
return error_message_; return error_;
} }
void fail(const char* error_message) { error_message_ = error_message; } void fail(RegExpError error) { error_ = error; }
Isolate* isolate() const { return isolate_; } Isolate* isolate() const { return isolate_; }
...@@ -3702,19 +3704,19 @@ class Analysis : public NodeVisitor { ...@@ -3702,19 +3704,19 @@ class Analysis : public NodeVisitor {
private: private:
Isolate* isolate_; Isolate* isolate_;
bool is_one_byte_; bool is_one_byte_;
const char* error_message_; RegExpError error_;
DISALLOW_IMPLICIT_CONSTRUCTORS(Analysis); DISALLOW_IMPLICIT_CONSTRUCTORS(Analysis);
}; };
const char* AnalyzeRegExp(Isolate* isolate, bool is_one_byte, RegExpError AnalyzeRegExp(Isolate* isolate, bool is_one_byte,
RegExpNode* node) { RegExpNode* node) {
Analysis<AssertionPropagator, EatsAtLeastPropagator> analysis(isolate, Analysis<AssertionPropagator, EatsAtLeastPropagator> analysis(isolate,
is_one_byte); is_one_byte);
DCHECK_EQ(node->info()->been_analyzed, false); DCHECK_EQ(node->info()->been_analyzed, false);
analysis.EnsureAnalyzed(node); analysis.EnsureAnalyzed(node);
DCHECK_IMPLIES(analysis.has_failed(), analysis.error_message() != nullptr); DCHECK_IMPLIES(analysis.has_failed(), analysis.error() != RegExpError::kNone);
return analysis.has_failed() ? analysis.error_message() : nullptr; return analysis.has_failed() ? analysis.error() : RegExpError::kNone;
} }
void BackReferenceNode::FillInBMInfo(Isolate* isolate, int offset, int budget, void BackReferenceNode::FillInBMInfo(Isolate* isolate, int offset, int budget,
......
...@@ -423,10 +423,7 @@ struct PreloadState { ...@@ -423,10 +423,7 @@ struct PreloadState {
// Analysis performs assertion propagation and computes eats_at_least_ values. // Analysis performs assertion propagation and computes eats_at_least_ values.
// See the comments on AssertionPropagator and EatsAtLeastPropagator for more // See the comments on AssertionPropagator and EatsAtLeastPropagator for more
// details. // details.
// RegExpError AnalyzeRegExp(Isolate* isolate, bool is_one_byte, RegExpNode* node);
// This method returns nullptr on success or a null-terminated failure message
// on failure.
const char* AnalyzeRegExp(Isolate* isolate, bool is_one_byte, RegExpNode* node);
class FrequencyCollator { class FrequencyCollator {
public: public:
...@@ -503,18 +500,17 @@ class RegExpCompiler { ...@@ -503,18 +500,17 @@ class RegExpCompiler {
} }
struct CompilationResult final { struct CompilationResult final {
explicit CompilationResult(const char* error_message) explicit CompilationResult(RegExpError err) : error(err) {}
: error_message(error_message) {}
CompilationResult(Object code, int registers) CompilationResult(Object code, int registers)
: code(code), num_registers(registers) {} : code(code), num_registers(registers) {}
static CompilationResult RegExpTooBig() { static CompilationResult RegExpTooBig() {
return CompilationResult("RegExp too big"); return CompilationResult(RegExpError::kTooLarge);
} }
bool Succeeded() const { return error_message == nullptr; } bool Succeeded() const { return error == RegExpError::kNone; }
const char* const error_message = nullptr; const RegExpError error = RegExpError::kNone;
Object code; Object code;
int num_registers = 0; int num_registers = 0;
}; };
......
// Copyright 2020 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/regexp/regexp-error.h"
namespace v8 {
namespace internal {
const char* kRegExpErrorStrings[] = {
#define TEMPLATE(NAME, STRING) STRING,
REGEXP_ERROR_MESSAGES(TEMPLATE)
#undef TEMPLATE
};
const char* RegExpErrorString(RegExpError error) {
DCHECK_LT(error, RegExpError::NumErrors);
return kRegExpErrorStrings[static_cast<int>(error)];
}
} // namespace internal
} // namespace v8
// Copyright 2020 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_REGEXP_REGEXP_ERROR_H_
#define V8_REGEXP_REGEXP_ERROR_H_
#include "src/base/logging.h"
#include "src/base/macros.h"
namespace v8 {
namespace internal {
#define REGEXP_ERROR_MESSAGES(T) \
T(None, "") \
T(StackOverflow, "Maximum call stack size exceeded") \
T(AnalysisStackOverflow, "Stack overflow") \
T(TooLarge, "Regular expression too large") \
T(UnterminatedGroup, "Unterminated group") \
T(UnmatchedParen, "Unmatched ')'") \
T(EscapeAtEndOfPattern, "\\ at end of pattern") \
T(InvalidPropertyName, "Invalid property name") \
T(InvalidEscape, "Invalid escape") \
T(InvalidDecimalEscape, "Invalid decimal escape") \
T(InvalidUnicodeEscape, "Invalid Unicode escape") \
T(NothingToRepeat, "Nothing to repeat") \
T(LoneQuantifierBrackets, "Lone quantifier brackets") \
T(RangeOutOfOrder, "numbers out of order in {} quantifier") \
T(IncompleteQuantifier, "Incomplete quantifier") \
T(InvalidQuantifier, "Invalid quantifier") \
T(InvalidGroup, "Invalid group") \
T(MultipleFlagDashes, "Multiple dashes in flag group") \
T(RepeatedFlag, "Repeated flag in flag group") \
T(InvalidFlagGroup, "Invalid flag group") \
T(TooManyCaptures, "Too many captures") \
T(InvalidCaptureGroupName, "Invalid capture group name") \
T(DuplicateCaptureGroupName, "Duplicate capture group name") \
T(InvalidNamedReference, "Invalid named reference") \
T(InvalidNamedCaptureReference, "Invalid named capture referenced") \
T(InvalidClassEscape, "Invalid class escape") \
T(InvalidClassPropertyName, "Invalid property name in character class") \
T(InvalidCharacterClass, "Invalid character class") \
T(UnterminatedCharacterClass, "Unterminated character class") \
T(OutOfOrderCharacterClass, "Range out of order in character class")
enum class RegExpError : uint32_t {
#define TEMPLATE(NAME, STRING) k##NAME,
REGEXP_ERROR_MESSAGES(TEMPLATE)
#undef TEMPLATE
NumErrors
};
V8_EXPORT_PRIVATE const char* RegExpErrorString(RegExpError error);
} // namespace internal
} // namespace v8
#endif // V8_REGEXP_REGEXP_ERROR_H_
...@@ -24,11 +24,10 @@ ...@@ -24,11 +24,10 @@
namespace v8 { namespace v8 {
namespace internal { namespace internal {
RegExpParser::RegExpParser(FlatStringReader* in, Handle<String>* error, RegExpParser::RegExpParser(FlatStringReader* in, JSRegExp::Flags flags,
JSRegExp::Flags flags, Isolate* isolate, Zone* zone) Isolate* isolate, Zone* zone)
: isolate_(isolate), : isolate_(isolate),
zone_(zone), zone_(zone),
error_(error),
captures_(nullptr), captures_(nullptr),
named_captures_(nullptr), named_captures_(nullptr),
named_back_references_(nullptr), named_back_references_(nullptr),
...@@ -81,13 +80,12 @@ void RegExpParser::Advance() { ...@@ -81,13 +80,12 @@ void RegExpParser::Advance() {
if (FLAG_correctness_fuzzer_suppressions) { if (FLAG_correctness_fuzzer_suppressions) {
FATAL("Aborting on stack overflow"); FATAL("Aborting on stack overflow");
} }
ReportError(CStrVector( ReportError(RegExpError::kStackOverflow);
MessageFormatter::TemplateString(MessageTemplate::kStackOverflow)));
} else if (zone()->excess_allocation()) { } else if (zone()->excess_allocation()) {
if (FLAG_correctness_fuzzer_suppressions) { if (FLAG_correctness_fuzzer_suppressions) {
FATAL("Aborting on excess zone allocation"); FATAL("Aborting on excess zone allocation");
} }
ReportError(CStrVector("Regular expression too large")); ReportError(RegExpError::kTooLarge);
} else { } else {
current_ = ReadNext<true>(); current_ = ReadNext<true>();
} }
...@@ -139,15 +137,12 @@ bool RegExpParser::IsSyntaxCharacterOrSlash(uc32 c) { ...@@ -139,15 +137,12 @@ bool RegExpParser::IsSyntaxCharacterOrSlash(uc32 c) {
return false; return false;
} }
RegExpTree* RegExpParser::ReportError(RegExpError error) {
RegExpTree* RegExpParser::ReportError(Vector<const char> message) {
if (failed_) return nullptr; // Do not overwrite any existing error. if (failed_) return nullptr; // Do not overwrite any existing error.
failed_ = true; failed_ = true;
*error_ = isolate() error_ = error;
->factory() error_pos_ = position();
->NewStringFromOneByte(Vector<const uint8_t>::cast(message)) // Zip to the end to make sure no more input is read.
.ToHandleChecked();
// Zip to the end to make sure the no more input is read.
current_ = kEndMarker; current_ = kEndMarker;
next_pos_ = in()->length(); next_pos_ = in()->length();
return nullptr; return nullptr;
...@@ -194,14 +189,14 @@ RegExpTree* RegExpParser::ParseDisjunction() { ...@@ -194,14 +189,14 @@ RegExpTree* RegExpParser::ParseDisjunction() {
case kEndMarker: case kEndMarker:
if (state->IsSubexpression()) { if (state->IsSubexpression()) {
// Inside a parenthesized group when hitting end of input. // Inside a parenthesized group when hitting end of input.
return ReportError(CStrVector("Unterminated group")); return ReportError(RegExpError::kUnterminatedGroup);
} }
DCHECK_EQ(INITIAL, state->group_type()); DCHECK_EQ(INITIAL, state->group_type());
// Parsing completed successfully. // Parsing completed successfully.
return builder->ToRegExp(); return builder->ToRegExp();
case ')': { case ')': {
if (!state->IsSubexpression()) { if (!state->IsSubexpression()) {
return ReportError(CStrVector("Unmatched ')'")); return ReportError(RegExpError::kUnmatchedParen);
} }
DCHECK_NE(INITIAL, state->group_type()); DCHECK_NE(INITIAL, state->group_type());
...@@ -252,7 +247,7 @@ RegExpTree* RegExpParser::ParseDisjunction() { ...@@ -252,7 +247,7 @@ RegExpTree* RegExpParser::ParseDisjunction() {
case '*': case '*':
case '+': case '+':
case '?': case '?':
return ReportError(CStrVector("Nothing to repeat")); return ReportError(RegExpError::kNothingToRepeat);
case '^': { case '^': {
Advance(); Advance();
if (builder->multiline()) { if (builder->multiline()) {
...@@ -307,7 +302,7 @@ RegExpTree* RegExpParser::ParseDisjunction() { ...@@ -307,7 +302,7 @@ RegExpTree* RegExpParser::ParseDisjunction() {
case '\\': case '\\':
switch (Next()) { switch (Next()) {
case kEndMarker: case kEndMarker:
return ReportError(CStrVector("\\ at end of pattern")); return ReportError(RegExpError::kEscapeAtEndOfPattern);
case 'b': case 'b':
Advance(2); Advance(2);
builder->AddAssertion(new (zone()) RegExpAssertion( builder->AddAssertion(new (zone()) RegExpAssertion(
...@@ -364,7 +359,7 @@ RegExpTree* RegExpParser::ParseDisjunction() { ...@@ -364,7 +359,7 @@ RegExpTree* RegExpParser::ParseDisjunction() {
} }
} }
} }
return ReportError(CStrVector("Invalid property name")); return ReportError(RegExpError::kInvalidPropertyName);
} else { } else {
builder->AddCharacter(p); builder->AddCharacter(p);
} }
...@@ -400,7 +395,7 @@ RegExpTree* RegExpParser::ParseDisjunction() { ...@@ -400,7 +395,7 @@ RegExpTree* RegExpParser::ParseDisjunction() {
// With /u, no identity escapes except for syntax characters // With /u, no identity escapes except for syntax characters
// are allowed. Otherwise, all identity escapes are allowed. // are allowed. Otherwise, all identity escapes are allowed.
if (unicode()) { if (unicode()) {
return ReportError(CStrVector("Invalid escape")); return ReportError(RegExpError::kInvalidEscape);
} }
uc32 first_digit = Next(); uc32 first_digit = Next();
if (first_digit == '8' || first_digit == '9') { if (first_digit == '8' || first_digit == '9') {
...@@ -414,7 +409,7 @@ RegExpTree* RegExpParser::ParseDisjunction() { ...@@ -414,7 +409,7 @@ RegExpTree* RegExpParser::ParseDisjunction() {
Advance(); Advance();
if (unicode() && Next() >= '0' && Next() <= '9') { if (unicode() && Next() >= '0' && Next() <= '9') {
// With /u, decimal escape with leading 0 are not parsed as octal. // With /u, decimal escape with leading 0 are not parsed as octal.
return ReportError(CStrVector("Invalid decimal escape")); return ReportError(RegExpError::kInvalidDecimalEscape);
} }
uc32 octal = ParseOctalLiteral(); uc32 octal = ParseOctalLiteral();
builder->AddCharacter(octal); builder->AddCharacter(octal);
...@@ -455,7 +450,7 @@ RegExpTree* RegExpParser::ParseDisjunction() { ...@@ -455,7 +450,7 @@ RegExpTree* RegExpParser::ParseDisjunction() {
// ES#prod-annexB-ExtendedPatternCharacter // ES#prod-annexB-ExtendedPatternCharacter
if (unicode()) { if (unicode()) {
// With /u, invalid escapes are not treated as identity escapes. // With /u, invalid escapes are not treated as identity escapes.
return ReportError(CStrVector("Invalid unicode escape")); return ReportError(RegExpError::kInvalidUnicodeEscape);
} }
builder->AddCharacter('\\'); builder->AddCharacter('\\');
} else { } else {
...@@ -473,7 +468,7 @@ RegExpTree* RegExpParser::ParseDisjunction() { ...@@ -473,7 +468,7 @@ RegExpTree* RegExpParser::ParseDisjunction() {
builder->AddCharacter('x'); builder->AddCharacter('x');
} else { } else {
// With /u, invalid escapes are not treated as identity escapes. // With /u, invalid escapes are not treated as identity escapes.
return ReportError(CStrVector("Invalid escape")); return ReportError(RegExpError::kInvalidEscape);
} }
break; break;
} }
...@@ -486,7 +481,7 @@ RegExpTree* RegExpParser::ParseDisjunction() { ...@@ -486,7 +481,7 @@ RegExpTree* RegExpParser::ParseDisjunction() {
builder->AddCharacter('u'); builder->AddCharacter('u');
} else { } else {
// With /u, invalid escapes are not treated as identity escapes. // With /u, invalid escapes are not treated as identity escapes.
return ReportError(CStrVector("Invalid Unicode escape")); return ReportError(RegExpError::kInvalidUnicodeEscape);
} }
break; break;
} }
...@@ -510,7 +505,7 @@ RegExpTree* RegExpParser::ParseDisjunction() { ...@@ -510,7 +505,7 @@ RegExpTree* RegExpParser::ParseDisjunction() {
builder->AddCharacter(current()); builder->AddCharacter(current());
Advance(); Advance();
} else { } else {
return ReportError(CStrVector("Invalid escape")); return ReportError(RegExpError::kInvalidEscape);
} }
break; break;
} }
...@@ -518,13 +513,13 @@ RegExpTree* RegExpParser::ParseDisjunction() { ...@@ -518,13 +513,13 @@ RegExpTree* RegExpParser::ParseDisjunction() {
case '{': { case '{': {
int dummy; int dummy;
bool parsed = ParseIntervalQuantifier(&dummy, &dummy CHECK_FAILED); bool parsed = ParseIntervalQuantifier(&dummy, &dummy CHECK_FAILED);
if (parsed) return ReportError(CStrVector("Nothing to repeat")); if (parsed) return ReportError(RegExpError::kNothingToRepeat);
V8_FALLTHROUGH; V8_FALLTHROUGH;
} }
case '}': case '}':
case ']': case ']':
if (unicode()) { if (unicode()) {
return ReportError(CStrVector("Lone quantifier brackets")); return ReportError(RegExpError::kLoneQuantifierBrackets);
} }
V8_FALLTHROUGH; V8_FALLTHROUGH;
default: default:
...@@ -559,13 +554,12 @@ RegExpTree* RegExpParser::ParseDisjunction() { ...@@ -559,13 +554,12 @@ RegExpTree* RegExpParser::ParseDisjunction() {
case '{': case '{':
if (ParseIntervalQuantifier(&min, &max)) { if (ParseIntervalQuantifier(&min, &max)) {
if (max < min) { if (max < min) {
return ReportError( return ReportError(RegExpError::kRangeOutOfOrder);
CStrVector("numbers out of order in {} quantifier"));
} }
break; break;
} else if (unicode()) { } else if (unicode()) {
// With /u, incomplete quantifiers are not allowed. // With /u, incomplete quantifiers are not allowed.
return ReportError(CStrVector("Incomplete quantifier")); return ReportError(RegExpError::kIncompleteQuantifier);
} }
continue; continue;
default: default:
...@@ -581,7 +575,7 @@ RegExpTree* RegExpParser::ParseDisjunction() { ...@@ -581,7 +575,7 @@ RegExpTree* RegExpParser::ParseDisjunction() {
Advance(); Advance();
} }
if (!builder->AddQuantifierToAtom(min, max, quantifier_type)) { if (!builder->AddQuantifierToAtom(min, max, quantifier_type)) {
return ReportError(CStrVector("Invalid quantifier")); return ReportError(RegExpError::kInvalidQuantifier);
} }
} }
} }
...@@ -616,7 +610,7 @@ RegExpParser::RegExpParserState* RegExpParser::ParseOpenParenthesis( ...@@ -616,7 +610,7 @@ RegExpParser::RegExpParserState* RegExpParser::ParseOpenParenthesis(
case 's': case 's':
case 'm': { case 'm': {
if (!FLAG_regexp_mode_modifiers) { if (!FLAG_regexp_mode_modifiers) {
ReportError(CStrVector("Invalid group")); ReportError(RegExpError::kInvalidGroup);
return nullptr; return nullptr;
} }
Advance(); Advance();
...@@ -625,7 +619,7 @@ RegExpParser::RegExpParserState* RegExpParser::ParseOpenParenthesis( ...@@ -625,7 +619,7 @@ RegExpParser::RegExpParserState* RegExpParser::ParseOpenParenthesis(
switch (current()) { switch (current()) {
case '-': case '-':
if (!flags_sense) { if (!flags_sense) {
ReportError(CStrVector("Multiple dashes in flag group")); ReportError(RegExpError::kMultipleFlagDashes);
return nullptr; return nullptr;
} }
flags_sense = false; flags_sense = false;
...@@ -639,7 +633,7 @@ RegExpParser::RegExpParserState* RegExpParser::ParseOpenParenthesis( ...@@ -639,7 +633,7 @@ RegExpParser::RegExpParserState* RegExpParser::ParseOpenParenthesis(
if (current() == 'm') bit = JSRegExp::kMultiline; if (current() == 'm') bit = JSRegExp::kMultiline;
if (current() == 's') bit = JSRegExp::kDotAll; if (current() == 's') bit = JSRegExp::kDotAll;
if (((switch_on | switch_off) & bit) != 0) { if (((switch_on | switch_off) & bit) != 0) {
ReportError(CStrVector("Repeated flag in flag group")); ReportError(RegExpError::kRepeatedFlag);
return nullptr; return nullptr;
} }
if (flags_sense) { if (flags_sense) {
...@@ -667,7 +661,7 @@ RegExpParser::RegExpParserState* RegExpParser::ParseOpenParenthesis( ...@@ -667,7 +661,7 @@ RegExpParser::RegExpParserState* RegExpParser::ParseOpenParenthesis(
subexpr_type = GROUPING; // Will break us out of the outer loop. subexpr_type = GROUPING; // Will break us out of the outer loop.
continue; continue;
default: default:
ReportError(CStrVector("Invalid flag group")); ReportError(RegExpError::kInvalidFlagGroup);
return nullptr; return nullptr;
} }
} }
...@@ -691,13 +685,13 @@ RegExpParser::RegExpParserState* RegExpParser::ParseOpenParenthesis( ...@@ -691,13 +685,13 @@ RegExpParser::RegExpParserState* RegExpParser::ParseOpenParenthesis(
Advance(); Advance();
break; break;
default: default:
ReportError(CStrVector("Invalid group")); ReportError(RegExpError::kInvalidGroup);
return nullptr; return nullptr;
} }
} }
if (subexpr_type == CAPTURE) { if (subexpr_type == CAPTURE) {
if (captures_started_ >= JSRegExp::kMaxCaptures) { if (captures_started_ >= JSRegExp::kMaxCaptures) {
ReportError(CStrVector("Too many captures")); ReportError(RegExpError::kTooManyCaptures);
return nullptr; return nullptr;
} }
captures_started_++; captures_started_++;
...@@ -846,20 +840,20 @@ const ZoneVector<uc16>* RegExpParser::ParseCaptureGroupName() { ...@@ -846,20 +840,20 @@ const ZoneVector<uc16>* RegExpParser::ParseCaptureGroupName() {
if (c == '\\' && current() == 'u') { if (c == '\\' && current() == 'u') {
Advance(); Advance();
if (!ParseUnicodeEscape(&c)) { if (!ParseUnicodeEscape(&c)) {
ReportError(CStrVector("Invalid Unicode escape sequence")); ReportError(RegExpError::kInvalidUnicodeEscape);
return nullptr; return nullptr;
} }
} }
// The backslash char is misclassified as both ID_Start and ID_Continue. // The backslash char is misclassified as both ID_Start and ID_Continue.
if (c == '\\') { if (c == '\\') {
ReportError(CStrVector("Invalid capture group name")); ReportError(RegExpError::kInvalidCaptureGroupName);
return nullptr; return nullptr;
} }
if (at_start) { if (at_start) {
if (!IsIdentifierStart(c)) { if (!IsIdentifierStart(c)) {
ReportError(CStrVector("Invalid capture group name")); ReportError(RegExpError::kInvalidCaptureGroupName);
return nullptr; return nullptr;
} }
push_code_unit(name, c); push_code_unit(name, c);
...@@ -870,7 +864,7 @@ const ZoneVector<uc16>* RegExpParser::ParseCaptureGroupName() { ...@@ -870,7 +864,7 @@ const ZoneVector<uc16>* RegExpParser::ParseCaptureGroupName() {
} else if (IsIdentifierPart(c)) { } else if (IsIdentifierPart(c)) {
push_code_unit(name, c); push_code_unit(name, c);
} else { } else {
ReportError(CStrVector("Invalid capture group name")); ReportError(RegExpError::kInvalidCaptureGroupName);
return nullptr; return nullptr;
} }
} }
...@@ -897,7 +891,7 @@ bool RegExpParser::CreateNamedCaptureAtIndex(const ZoneVector<uc16>* name, ...@@ -897,7 +891,7 @@ bool RegExpParser::CreateNamedCaptureAtIndex(const ZoneVector<uc16>* name,
const auto& named_capture_it = named_captures_->find(capture); const auto& named_capture_it = named_captures_->find(capture);
if (named_capture_it != named_captures_->end()) { if (named_capture_it != named_captures_->end()) {
ReportError(CStrVector("Duplicate capture group name")); ReportError(RegExpError::kDuplicateCaptureGroupName);
return false; return false;
} }
} }
...@@ -911,7 +905,7 @@ bool RegExpParser::ParseNamedBackReference(RegExpBuilder* builder, ...@@ -911,7 +905,7 @@ bool RegExpParser::ParseNamedBackReference(RegExpBuilder* builder,
RegExpParserState* state) { RegExpParserState* state) {
// The parser is assumed to be on the '<' in \k<name>. // The parser is assumed to be on the '<' in \k<name>.
if (current() != '<') { if (current() != '<') {
ReportError(CStrVector("Invalid named reference")); ReportError(RegExpError::kInvalidNamedReference);
return false; return false;
} }
...@@ -944,7 +938,7 @@ void RegExpParser::PatchNamedBackReferences() { ...@@ -944,7 +938,7 @@ void RegExpParser::PatchNamedBackReferences() {
if (named_back_references_ == nullptr) return; if (named_back_references_ == nullptr) return;
if (named_captures_ == nullptr) { if (named_captures_ == nullptr) {
ReportError(CStrVector("Invalid named capture referenced")); ReportError(RegExpError::kInvalidNamedCaptureReference);
return; return;
} }
...@@ -965,7 +959,7 @@ void RegExpParser::PatchNamedBackReferences() { ...@@ -965,7 +959,7 @@ void RegExpParser::PatchNamedBackReferences() {
if (capture_it != named_captures_->end()) { if (capture_it != named_captures_->end()) {
index = (*capture_it)->index(); index = (*capture_it)->index();
} else { } else {
ReportError(CStrVector("Invalid named capture referenced")); ReportError(RegExpError::kInvalidNamedCaptureReference);
return; return;
} }
...@@ -1606,7 +1600,7 @@ uc32 RegExpParser::ParseClassCharacterEscape() { ...@@ -1606,7 +1600,7 @@ uc32 RegExpParser::ParseClassCharacterEscape() {
} }
if (unicode()) { if (unicode()) {
// With /u, invalid escapes are not treated as identity escapes. // With /u, invalid escapes are not treated as identity escapes.
ReportError(CStrVector("Invalid class escape")); ReportError(RegExpError::kInvalidClassEscape);
return 0; return 0;
} }
if ((controlLetter >= '0' && controlLetter <= '9') || if ((controlLetter >= '0' && controlLetter <= '9') ||
...@@ -1639,7 +1633,7 @@ uc32 RegExpParser::ParseClassCharacterEscape() { ...@@ -1639,7 +1633,7 @@ uc32 RegExpParser::ParseClassCharacterEscape() {
// ES#prod-annexB-LegacyOctalEscapeSequence // ES#prod-annexB-LegacyOctalEscapeSequence
if (unicode()) { if (unicode()) {
// With /u, decimal escape is not interpreted as octal character code. // With /u, decimal escape is not interpreted as octal character code.
ReportError(CStrVector("Invalid class escape")); ReportError(RegExpError::kInvalidClassEscape);
return 0; return 0;
} }
return ParseOctalLiteral(); return ParseOctalLiteral();
...@@ -1649,7 +1643,7 @@ uc32 RegExpParser::ParseClassCharacterEscape() { ...@@ -1649,7 +1643,7 @@ uc32 RegExpParser::ParseClassCharacterEscape() {
if (ParseHexEscape(2, &value)) return value; if (ParseHexEscape(2, &value)) return value;
if (unicode()) { if (unicode()) {
// With /u, invalid escapes are not treated as identity escapes. // With /u, invalid escapes are not treated as identity escapes.
ReportError(CStrVector("Invalid escape")); ReportError(RegExpError::kInvalidEscape);
return 0; return 0;
} }
// If \x is not followed by a two-digit hexadecimal, treat it // If \x is not followed by a two-digit hexadecimal, treat it
...@@ -1662,7 +1656,7 @@ uc32 RegExpParser::ParseClassCharacterEscape() { ...@@ -1662,7 +1656,7 @@ uc32 RegExpParser::ParseClassCharacterEscape() {
if (ParseUnicodeEscape(&value)) return value; if (ParseUnicodeEscape(&value)) return value;
if (unicode()) { if (unicode()) {
// With /u, invalid escapes are not treated as identity escapes. // With /u, invalid escapes are not treated as identity escapes.
ReportError(CStrVector("Invalid unicode escape")); ReportError(RegExpError::kInvalidUnicodeEscape);
return 0; return 0;
} }
// If \u is not followed by a two-digit hexadecimal, treat it // If \u is not followed by a two-digit hexadecimal, treat it
...@@ -1677,7 +1671,7 @@ uc32 RegExpParser::ParseClassCharacterEscape() { ...@@ -1677,7 +1671,7 @@ uc32 RegExpParser::ParseClassCharacterEscape() {
Advance(); Advance();
return result; return result;
} }
ReportError(CStrVector("Invalid escape")); ReportError(RegExpError::kInvalidEscape);
return 0; return 0;
} }
} }
...@@ -1704,7 +1698,7 @@ void RegExpParser::ParseClassEscape(ZoneList<CharacterRange>* ranges, ...@@ -1704,7 +1698,7 @@ void RegExpParser::ParseClassEscape(ZoneList<CharacterRange>* ranges,
return; return;
} }
case kEndMarker: case kEndMarker:
ReportError(CStrVector("\\ at end of pattern")); ReportError(RegExpError::kEscapeAtEndOfPattern);
return; return;
case 'p': case 'p':
case 'P': case 'P':
...@@ -1715,7 +1709,7 @@ void RegExpParser::ParseClassEscape(ZoneList<CharacterRange>* ranges, ...@@ -1715,7 +1709,7 @@ void RegExpParser::ParseClassEscape(ZoneList<CharacterRange>* ranges,
ZoneVector<char> name_2(zone); ZoneVector<char> name_2(zone);
if (!ParsePropertyClassName(&name_1, &name_2) || if (!ParsePropertyClassName(&name_1, &name_2) ||
!AddPropertyClassRange(ranges, negate, name_1, name_2)) { !AddPropertyClassRange(ranges, negate, name_1, name_2)) {
ReportError(CStrVector("Invalid property name in character class")); ReportError(RegExpError::kInvalidClassPropertyName);
} }
*is_class_escape = true; *is_class_escape = true;
return; return;
...@@ -1734,10 +1728,6 @@ void RegExpParser::ParseClassEscape(ZoneList<CharacterRange>* ranges, ...@@ -1734,10 +1728,6 @@ void RegExpParser::ParseClassEscape(ZoneList<CharacterRange>* ranges,
} }
RegExpTree* RegExpParser::ParseCharacterClass(const RegExpBuilder* builder) { RegExpTree* RegExpParser::ParseCharacterClass(const RegExpBuilder* builder) {
static const char* kUnterminated = "Unterminated character class";
static const char* kRangeInvalid = "Invalid character class";
static const char* kRangeOutOfOrder = "Range out of order in character class";
DCHECK_EQ(current(), '['); DCHECK_EQ(current(), '[');
Advance(); Advance();
bool is_negated = false; bool is_negated = false;
...@@ -1770,7 +1760,7 @@ RegExpTree* RegExpParser::ParseCharacterClass(const RegExpBuilder* builder) { ...@@ -1770,7 +1760,7 @@ RegExpTree* RegExpParser::ParseCharacterClass(const RegExpBuilder* builder) {
// Either end is an escaped character class. Treat the '-' verbatim. // Either end is an escaped character class. Treat the '-' verbatim.
if (unicode()) { if (unicode()) {
// ES2015 21.2.2.15.1 step 1. // ES2015 21.2.2.15.1 step 1.
return ReportError(CStrVector(kRangeInvalid)); return ReportError(RegExpError::kInvalidCharacterClass);
} }
if (!is_class_1) ranges->Add(CharacterRange::Singleton(char_1), zone()); if (!is_class_1) ranges->Add(CharacterRange::Singleton(char_1), zone());
ranges->Add(CharacterRange::Singleton('-'), zone()); ranges->Add(CharacterRange::Singleton('-'), zone());
...@@ -1779,7 +1769,7 @@ RegExpTree* RegExpParser::ParseCharacterClass(const RegExpBuilder* builder) { ...@@ -1779,7 +1769,7 @@ RegExpTree* RegExpParser::ParseCharacterClass(const RegExpBuilder* builder) {
} }
// ES2015 21.2.2.15.1 step 6. // ES2015 21.2.2.15.1 step 6.
if (char_1 > char_2) { if (char_1 > char_2) {
return ReportError(CStrVector(kRangeOutOfOrder)); return ReportError(RegExpError::kOutOfOrderCharacterClass);
} }
ranges->Add(CharacterRange::Range(char_1, char_2), zone()); ranges->Add(CharacterRange::Range(char_1, char_2), zone());
} else { } else {
...@@ -1787,7 +1777,7 @@ RegExpTree* RegExpParser::ParseCharacterClass(const RegExpBuilder* builder) { ...@@ -1787,7 +1777,7 @@ RegExpTree* RegExpParser::ParseCharacterClass(const RegExpBuilder* builder) {
} }
} }
if (!has_more()) { if (!has_more()) {
return ReportError(CStrVector(kUnterminated)); return ReportError(RegExpError::kUnterminatedCharacterClass);
} }
Advance(); Advance();
RegExpCharacterClass::CharacterClassFlags character_class_flags; RegExpCharacterClass::CharacterClassFlags character_class_flags;
...@@ -1804,14 +1794,16 @@ bool RegExpParser::ParseRegExp(Isolate* isolate, Zone* zone, ...@@ -1804,14 +1794,16 @@ bool RegExpParser::ParseRegExp(Isolate* isolate, Zone* zone,
FlatStringReader* input, JSRegExp::Flags flags, FlatStringReader* input, JSRegExp::Flags flags,
RegExpCompileData* result) { RegExpCompileData* result) {
DCHECK(result != nullptr); DCHECK(result != nullptr);
RegExpParser parser(input, &result->error, flags, isolate, zone); RegExpParser parser(input, flags, isolate, zone);
RegExpTree* tree = parser.ParsePattern(); RegExpTree* tree = parser.ParsePattern();
if (parser.failed()) { if (parser.failed()) {
DCHECK(tree == nullptr); DCHECK(tree == nullptr);
DCHECK(!result->error.is_null()); DCHECK(parser.error_ != RegExpError::kNone);
result->error = parser.error_;
result->error_pos = parser.error_pos_;
} else { } else {
DCHECK(tree != nullptr); DCHECK(tree != nullptr);
DCHECK(result->error.is_null()); DCHECK(parser.error_ == RegExpError::kNone);
if (FLAG_trace_regexp_parser) { if (FLAG_trace_regexp_parser) {
StdoutStream os; StdoutStream os;
tree->Print(os, zone); tree->Print(os, zone);
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "src/objects/js-regexp.h" #include "src/objects/js-regexp.h"
#include "src/objects/objects.h" #include "src/objects/objects.h"
#include "src/regexp/regexp-ast.h" #include "src/regexp/regexp-ast.h"
#include "src/regexp/regexp-error.h"
#include "src/zone/zone.h" #include "src/zone/zone.h"
namespace v8 { namespace v8 {
...@@ -153,8 +154,8 @@ class RegExpBuilder : public ZoneObject { ...@@ -153,8 +154,8 @@ class RegExpBuilder : public ZoneObject {
class V8_EXPORT_PRIVATE RegExpParser { class V8_EXPORT_PRIVATE RegExpParser {
public: public:
RegExpParser(FlatStringReader* in, Handle<String>* error, RegExpParser(FlatStringReader* in, JSRegExp::Flags flags, Isolate* isolate,
JSRegExp::Flags flags, Isolate* isolate, Zone* zone); Zone* zone);
static bool ParseRegExp(Isolate* isolate, Zone* zone, FlatStringReader* input, static bool ParseRegExp(Isolate* isolate, Zone* zone, FlatStringReader* input,
JSRegExp::Flags flags, RegExpCompileData* result); JSRegExp::Flags flags, RegExpCompileData* result);
...@@ -202,7 +203,7 @@ class V8_EXPORT_PRIVATE RegExpParser { ...@@ -202,7 +203,7 @@ class V8_EXPORT_PRIVATE RegExpParser {
char ParseClassEscape(); char ParseClassEscape();
RegExpTree* ReportError(Vector<const char> message); RegExpTree* ReportError(RegExpError error);
void Advance(); void Advance();
void Advance(int dist); void Advance(int dist);
void Reset(int pos); void Reset(int pos);
...@@ -335,7 +336,8 @@ class V8_EXPORT_PRIVATE RegExpParser { ...@@ -335,7 +336,8 @@ class V8_EXPORT_PRIVATE RegExpParser {
Isolate* isolate_; Isolate* isolate_;
Zone* zone_; Zone* zone_;
Handle<String>* error_; RegExpError error_ = RegExpError::kNone;
int error_pos_ = 0;
ZoneList<RegExpCapture*>* captures_; ZoneList<RegExpCapture*>* captures_;
ZoneSet<RegExpCapture*, RegExpCaptureNameLess>* named_captures_; ZoneSet<RegExpCapture*, RegExpCaptureNameLess>* named_captures_;
ZoneList<RegExpBackReference*>* named_back_references_; ZoneList<RegExpBackReference*>* named_back_references_;
......
...@@ -92,9 +92,15 @@ class RegExpImpl final : public AllStatic { ...@@ -92,9 +92,15 @@ class RegExpImpl final : public AllStatic {
}; };
V8_WARN_UNUSED_RESULT V8_WARN_UNUSED_RESULT
static inline MaybeHandle<Object> ThrowRegExpException( static inline MaybeHandle<Object> ThrowRegExpException(Isolate* isolate,
Isolate* isolate, Handle<JSRegExp> re, Handle<String> pattern, Handle<JSRegExp> re,
Handle<String> error_text) { Handle<String> pattern,
RegExpError error) {
Vector<const char> error_data = CStrVector(RegExpErrorString(error));
Handle<String> error_text =
isolate->factory()
->NewStringFromOneByte(Vector<const uint8_t>::cast(error_data))
.ToHandleChecked();
THROW_NEW_ERROR( THROW_NEW_ERROR(
isolate, isolate,
NewSyntaxError(MessageTemplate::kMalformedRegExp, pattern, error_text), NewSyntaxError(MessageTemplate::kMalformedRegExp, pattern, error_text),
...@@ -102,7 +108,7 @@ static inline MaybeHandle<Object> ThrowRegExpException( ...@@ -102,7 +108,7 @@ static inline MaybeHandle<Object> ThrowRegExpException(
} }
inline void ThrowRegExpException(Isolate* isolate, Handle<JSRegExp> re, inline void ThrowRegExpException(Isolate* isolate, Handle<JSRegExp> re,
Handle<String> error_text) { RegExpError error_text) {
USE(ThrowRegExpException(isolate, re, Handle<String>(re->Pattern(), isolate), USE(ThrowRegExpException(isolate, re, Handle<String>(re->Pattern(), isolate),
error_text)); error_text));
} }
...@@ -408,7 +414,7 @@ bool RegExpImpl::CompileIrregexp(Isolate* isolate, Handle<JSRegExp> re, ...@@ -408,7 +414,7 @@ bool RegExpImpl::CompileIrregexp(Isolate* isolate, Handle<JSRegExp> re,
Compile(isolate, &zone, &compile_data, flags, pattern, sample_subject, Compile(isolate, &zone, &compile_data, flags, pattern, sample_subject,
is_one_byte, re->BacktrackLimit()); is_one_byte, re->BacktrackLimit());
if (!compilation_succeeded) { if (!compilation_succeeded) {
DCHECK(!compile_data.error.is_null()); DCHECK(compile_data.error != RegExpError::kNone);
ThrowRegExpException(isolate, re, compile_data.error); ThrowRegExpException(isolate, re, compile_data.error);
return false; return false;
} }
...@@ -741,8 +747,7 @@ bool RegExpImpl::Compile(Isolate* isolate, Zone* zone, RegExpCompileData* data, ...@@ -741,8 +747,7 @@ bool RegExpImpl::Compile(Isolate* isolate, Zone* zone, RegExpCompileData* data,
Handle<String> sample_subject, bool is_one_byte, Handle<String> sample_subject, bool is_one_byte,
uint32_t backtrack_limit) { uint32_t backtrack_limit) {
if ((data->capture_count + 1) * 2 - 1 > RegExpMacroAssembler::kMaxRegister) { if ((data->capture_count + 1) * 2 - 1 > RegExpMacroAssembler::kMaxRegister) {
data->error = data->error = RegExpError::kTooLarge;
isolate->factory()->NewStringFromAsciiChecked("RegExp too big");
return false; return false;
} }
...@@ -810,8 +815,8 @@ bool RegExpImpl::Compile(Isolate* isolate, Zone* zone, RegExpCompileData* data, ...@@ -810,8 +815,8 @@ bool RegExpImpl::Compile(Isolate* isolate, Zone* zone, RegExpCompileData* data,
if (node == nullptr) node = new (zone) EndNode(EndNode::BACKTRACK, zone); if (node == nullptr) node = new (zone) EndNode(EndNode::BACKTRACK, zone);
data->node = node; data->node = node;
if (const char* error_message = AnalyzeRegExp(isolate, is_one_byte, node)) { data->error = AnalyzeRegExp(isolate, is_one_byte, node);
data->error = isolate->factory()->NewStringFromAsciiChecked(error_message); if (data->error != RegExpError::kNone) {
return false; return false;
} }
...@@ -913,13 +918,12 @@ bool RegExpImpl::Compile(Isolate* isolate, Zone* zone, RegExpCompileData* data, ...@@ -913,13 +918,12 @@ bool RegExpImpl::Compile(Isolate* isolate, Zone* zone, RegExpCompileData* data,
} }
} }
if (result.error_message != nullptr) { if (result.error != RegExpError::kNone) {
if (FLAG_correctness_fuzzer_suppressions && if (FLAG_correctness_fuzzer_suppressions &&
strncmp(result.error_message, "Stack overflow", 15) == 0) { result.error == RegExpError::kStackOverflow) {
FATAL("Aborting on stack overflow"); FATAL("Aborting on stack overflow");
} }
data->error = data->error = result.error;
isolate->factory()->NewStringFromAsciiChecked(result.error_message);
} }
data->code = result.code; data->code = result.code;
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#define V8_REGEXP_REGEXP_H_ #define V8_REGEXP_REGEXP_H_
#include "src/objects/js-regexp.h" #include "src/objects/js-regexp.h"
#include "src/regexp/regexp-error.h"
namespace v8 { namespace v8 {
namespace internal { namespace internal {
...@@ -42,7 +43,11 @@ struct RegExpCompileData { ...@@ -42,7 +43,11 @@ struct RegExpCompileData {
// The error message. Only used if an error occurred during parsing or // The error message. Only used if an error occurred during parsing or
// compilation. // compilation.
Handle<String> error; RegExpError error = RegExpError::kNone;
// The position at which the error was detected. Only used if an
// error occurred.
int error_pos = 0;
// The number of capture groups, without the global capture \0. // The number of capture groups, without the global capture \0.
int capture_count = 0; int capture_count = 0;
......
...@@ -76,7 +76,7 @@ static void CheckParseEq(const char* input, const char* expected, ...@@ -76,7 +76,7 @@ static void CheckParseEq(const char* input, const char* expected,
CHECK(v8::internal::RegExpParser::ParseRegExp(CcTest::i_isolate(), &zone, CHECK(v8::internal::RegExpParser::ParseRegExp(CcTest::i_isolate(), &zone,
&reader, flags, &result)); &reader, flags, &result));
CHECK_NOT_NULL(result.tree); CHECK_NOT_NULL(result.tree);
CHECK(result.error.is_null()); CHECK(result.error == RegExpError::kNone);
std::ostringstream os; std::ostringstream os;
result.tree->Print(os, &zone); result.tree->Print(os, &zone);
if (strcmp(expected, os.str().c_str()) != 0) { if (strcmp(expected, os.str().c_str()) != 0) {
...@@ -94,7 +94,7 @@ static bool CheckSimple(const char* input) { ...@@ -94,7 +94,7 @@ static bool CheckSimple(const char* input) {
CHECK(v8::internal::RegExpParser::ParseRegExp( CHECK(v8::internal::RegExpParser::ParseRegExp(
CcTest::i_isolate(), &zone, &reader, JSRegExp::kNone, &result)); CcTest::i_isolate(), &zone, &reader, JSRegExp::kNone, &result));
CHECK_NOT_NULL(result.tree); CHECK_NOT_NULL(result.tree);
CHECK(result.error.is_null()); CHECK(result.error == RegExpError::kNone);
return result.simple; return result.simple;
} }
...@@ -112,7 +112,7 @@ static MinMaxPair CheckMinMaxMatch(const char* input) { ...@@ -112,7 +112,7 @@ static MinMaxPair CheckMinMaxMatch(const char* input) {
CHECK(v8::internal::RegExpParser::ParseRegExp( CHECK(v8::internal::RegExpParser::ParseRegExp(
CcTest::i_isolate(), &zone, &reader, JSRegExp::kNone, &result)); CcTest::i_isolate(), &zone, &reader, JSRegExp::kNone, &result));
CHECK_NOT_NULL(result.tree); CHECK_NOT_NULL(result.tree);
CHECK(result.error.is_null()); CHECK(result.error == RegExpError::kNone);
int min_match = result.tree->min_match(); int min_match = result.tree->min_match();
int max_match = result.tree->max_match(); int max_match = result.tree->max_match();
MinMaxPair pair = { min_match, max_match }; MinMaxPair pair = { min_match, max_match };
...@@ -428,9 +428,8 @@ static void ExpectError(const char* input, const char* expected, ...@@ -428,9 +428,8 @@ static void ExpectError(const char* input, const char* expected,
CHECK(!v8::internal::RegExpParser::ParseRegExp(isolate, &zone, &reader, flags, CHECK(!v8::internal::RegExpParser::ParseRegExp(isolate, &zone, &reader, flags,
&result)); &result));
CHECK_NULL(result.tree); CHECK_NULL(result.tree);
CHECK(!result.error.is_null()); CHECK(result.error != RegExpError::kNone);
std::unique_ptr<char[]> str = result.error->ToCString(ALLOW_NULLS); CHECK_EQ(0, strcmp(expected, RegExpErrorString(result.error)));
CHECK_EQ(0, strcmp(expected, str.get()));
} }
...@@ -468,7 +467,7 @@ TEST(Errors) { ...@@ -468,7 +467,7 @@ TEST(Errors) {
ExpectError("\\k<a", kInvalidCaptureName, true); ExpectError("\\k<a", kInvalidCaptureName, true);
const char* kDuplicateCaptureName = "Duplicate capture group name"; const char* kDuplicateCaptureName = "Duplicate capture group name";
ExpectError("(?<a>.)(?<a>.)", kDuplicateCaptureName, true); ExpectError("(?<a>.)(?<a>.)", kDuplicateCaptureName, true);
const char* kInvalidUnicodeEscape = "Invalid Unicode escape sequence"; const char* kInvalidUnicodeEscape = "Invalid Unicode escape";
ExpectError("(?<\\u{FISK}", kInvalidUnicodeEscape, true); ExpectError("(?<\\u{FISK}", kInvalidUnicodeEscape, true);
const char* kInvalidCaptureReferenced = "Invalid named capture referenced"; const char* kInvalidCaptureReferenced = "Invalid named capture referenced";
ExpectError("\\k<a>", kInvalidCaptureReferenced, true); ExpectError("\\k<a>", kInvalidCaptureReferenced, true);
......
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