e443b89f23
Includes support for modifier spans and duplicate named capture groups. Drops the flow graph implementation to ease maintenance. TEST=corelib/regexp Bug: https://github.com/dart-lang/sdk/issues/56573 Bug: https://github.com/dart-lang/sdk/issues/61337 Bug: https://github.com/dart-lang/sdk/issues/62349 Bug: https://github.com/dart-lang/sdk/issues/62708 Change-Id: I05640ba945a4fa5476e7ad463738f4f39d842c14 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/480121 Reviewed-by: Lasse Nielsen <lrn@google.com> Commit-Queue: Ryan Macnak <rmacnak@google.com>
586 lines
23 KiB
C++
586 lines
23 KiB
C++
// Copyright 2012 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 "vm/regexp/regexp-macro-assembler.h"
|
|
|
|
#include <limits>
|
|
|
|
#include "vm/regexp/base.h"
|
|
#include "vm/regexp/label.h"
|
|
#include "vm/regexp/special-case.h"
|
|
|
|
#ifdef V8_INTL_SUPPORT
|
|
#include "unicode/uchar.h"
|
|
#include "unicode/unistr.h"
|
|
#endif // V8_INTL_SUPPORT
|
|
|
|
namespace dart {
|
|
|
|
RegExpMacroAssembler::RegExpMacroAssembler(Isolate* isolate,
|
|
Zone* zone,
|
|
Mode mode)
|
|
: slow_safe_compiler_(false),
|
|
backtrack_limit_(JSRegExp::kNoBacktrackLimit),
|
|
global_mode_(NOT_GLOBAL),
|
|
isolate_(isolate),
|
|
zone_(zone),
|
|
mode_(mode) {}
|
|
|
|
bool RegExpMacroAssembler::has_backtrack_limit() const {
|
|
return backtrack_limit_ != JSRegExp::kNoBacktrackLimit;
|
|
}
|
|
|
|
int RegExpMacroAssembler::stack_limit_slack_slot_count() const {
|
|
return 32;
|
|
}
|
|
|
|
bool RegExpMacroAssembler::CanReadUnaligned() const {
|
|
return true;
|
|
}
|
|
|
|
// static
|
|
int RegExpMacroAssembler::CaseInsensitiveCompareNonUnicode(Address byte_offset1,
|
|
Address byte_offset2,
|
|
size_t byte_length,
|
|
Isolate* isolate) {
|
|
#ifdef V8_INTL_SUPPORT
|
|
DCHECK_EQ(0, byte_length % 2);
|
|
size_t length = byte_length / 2;
|
|
uint16_t* substring1 = reinterpret_cast<uint16_t*>(byte_offset1);
|
|
uint16_t* substring2 = reinterpret_cast<uint16_t*>(byte_offset2);
|
|
|
|
for (size_t i = 0; i < length; i++) {
|
|
UChar32 c1 = RegExpCaseFolding::Canonicalize(substring1[i]);
|
|
UChar32 c2 = RegExpCaseFolding::Canonicalize(substring2[i]);
|
|
if (c1 != c2) {
|
|
return 0;
|
|
}
|
|
}
|
|
return 1;
|
|
#else
|
|
return CaseInsensitiveCompareUnicode(byte_offset1, byte_offset2, byte_length,
|
|
isolate);
|
|
#endif
|
|
}
|
|
|
|
// static
|
|
int RegExpMacroAssembler::CaseInsensitiveCompareUnicode(Address byte_offset1,
|
|
Address byte_offset2,
|
|
size_t byte_length,
|
|
Isolate* isolate) {
|
|
// This function is not allowed to cause a garbage collection.
|
|
// A GC might move the calling generated code and invalidate the
|
|
// return address on the stack.
|
|
DCHECK_EQ(0, byte_length % 2);
|
|
|
|
#ifdef V8_INTL_SUPPORT
|
|
int32_t length = static_cast<int32_t>(byte_length >> 1);
|
|
icu::UnicodeString uni_str_1(reinterpret_cast<const char16_t*>(byte_offset1),
|
|
length);
|
|
return uni_str_1.caseCompare(reinterpret_cast<const char16_t*>(byte_offset2),
|
|
length, U_FOLD_CASE_DEFAULT) == 0;
|
|
#else
|
|
uint16_t* substring1 = reinterpret_cast<uint16_t*>(byte_offset1);
|
|
uint16_t* substring2 = reinterpret_cast<uint16_t*>(byte_offset2);
|
|
size_t length = byte_length >> 1;
|
|
DCHECK_NOT_NULL(isolate);
|
|
unibrow::Mapping<unibrow::Ecma262Canonicalize>* canonicalize =
|
|
isolate->regexp_macro_assembler_canonicalize();
|
|
for (size_t i = 0; i < length; i++) {
|
|
unibrow::uchar c1 = substring1[i];
|
|
unibrow::uchar c2 = substring2[i];
|
|
if (c1 != c2) {
|
|
unibrow::uchar s1[1] = {c1};
|
|
canonicalize->get(c1, '\0', s1);
|
|
if (s1[0] != c2) {
|
|
unibrow::uchar s2[1] = {c2};
|
|
canonicalize->get(c2, '\0', s2);
|
|
if (s1[0] != s2[0]) {
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 1;
|
|
#endif // V8_INTL_SUPPORT
|
|
}
|
|
|
|
namespace {
|
|
|
|
uint32_t Hash(const ZoneList<CharacterRange>* ranges) {
|
|
size_t seed = 0;
|
|
for (int i = 0; i < ranges->length(); i++) {
|
|
const CharacterRange& r = ranges->at(i);
|
|
seed ^= r.from();
|
|
seed ^= r.to();
|
|
}
|
|
return static_cast<uint32_t>(seed);
|
|
}
|
|
|
|
constexpr uint32_t MaskEndOfRangeMarker(uint32_t c) {
|
|
// CharacterRanges may use 0x10ffff as the end-of-range marker irrespective
|
|
// of whether the regexp IsUnicode or not; translate the marker value here.
|
|
DCHECK_IMPLIES(c > kMaxUint16, c == String::kMaxCodePoint);
|
|
return c & 0xffff;
|
|
}
|
|
|
|
int RangeArrayLengthFor(const ZoneList<CharacterRange>* ranges) {
|
|
const int ranges_length = ranges->length();
|
|
return MaskEndOfRangeMarker(ranges->at(ranges_length - 1).to()) == kMaxUint16
|
|
? ranges_length * 2 - 1
|
|
: ranges_length * 2;
|
|
}
|
|
|
|
bool Equals(const ZoneList<CharacterRange>* lhs, const TypedData& rhs) {
|
|
ASSERT(rhs.ElementSizeInBytes() == 2); // uint16
|
|
const int rhs_length = rhs.Length();
|
|
if (rhs_length != RangeArrayLengthFor(lhs)) return false;
|
|
for (int i = 0; i < lhs->length(); i++) {
|
|
const CharacterRange& r = lhs->at(i);
|
|
if (rhs.GetUint16(i * 2 + 0) != r.from()) return false;
|
|
if (i * 2 + 1 == rhs_length) break;
|
|
if (rhs.GetUint16(i * 2 + 1) != r.to() + 1) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
TypedDataPtr MakeRangeArray(Isolate* isolate,
|
|
const ZoneList<CharacterRange>* ranges) {
|
|
const int ranges_length = ranges->length();
|
|
const int range_array_length = RangeArrayLengthFor(ranges);
|
|
TypedData& range_array = TypedData::Handle(
|
|
TypedData::New(kTypedDataUint16ArrayCid, range_array_length));
|
|
for (int i = 0; i < ranges_length; i++) {
|
|
const CharacterRange& r = ranges->at(i);
|
|
DCHECK_LE(r.from(), kMaxUint16);
|
|
range_array.SetUint16(i * 2 + 0, r.from());
|
|
const uint32_t to = MaskEndOfRangeMarker(r.to());
|
|
if (i == ranges_length - 1 && to == kMaxUint16) {
|
|
DCHECK_EQ(range_array_length, ranges_length * 2 - 1);
|
|
break; // Avoid overflow by leaving the last range open-ended.
|
|
}
|
|
DCHECK_LT(to, kMaxUint16);
|
|
range_array.SetUint16(i * 2 + 1, to + 1); // Exclusive.
|
|
}
|
|
return range_array.ptr();
|
|
}
|
|
|
|
} // namespace
|
|
|
|
TypedDataPtr NativeRegExpMacroAssembler::GetOrAddRangeArray(
|
|
const ZoneList<CharacterRange>* ranges) {
|
|
const uint32_t hash = Hash(ranges);
|
|
|
|
if (range_array_cache_.count(hash) != 0) {
|
|
TypedData* range_array = range_array_cache_[hash];
|
|
if (Equals(ranges, *range_array)) return range_array->ptr();
|
|
}
|
|
|
|
TypedDataPtr range_array = MakeRangeArray(isolate(), ranges);
|
|
range_array_cache_[hash] = &TypedData::Handle(range_array);
|
|
return range_array;
|
|
}
|
|
|
|
// static
|
|
uint32_t RegExpMacroAssembler::IsCharacterInRangeArray(uint32_t current_char,
|
|
Address raw_byte_array) {
|
|
// Use uint32_t to avoid complexity around bool return types (which may be
|
|
// optimized to use only the least significant byte).
|
|
static constexpr uint32_t kTrue = 1;
|
|
static constexpr uint32_t kFalse = 0;
|
|
|
|
// Uint16
|
|
const TypedData& ranges = TypedData::CheckedHandle(
|
|
Thread::Current()->zone(), UntaggedObject::FromAddr(raw_byte_array));
|
|
DCHECK_GE(ranges.Length(), 1);
|
|
|
|
// Shortcut for fully out of range chars.
|
|
if (current_char < ranges.GetUint16(0)) return kFalse;
|
|
if (current_char >= ranges.GetUint16(ranges.Length() - 1)) {
|
|
// The last range may be open-ended.
|
|
return (ranges.Length() % 2) == 0 ? kFalse : kTrue;
|
|
}
|
|
|
|
// Binary search for the matching range. `ranges` is encoded as
|
|
// [from0, to0, from1, to1, ..., fromN, toN], or
|
|
// [from0, to0, from1, to1, ..., fromN] (open-ended last interval).
|
|
|
|
int mid, lower = 0, upper = ranges.Length();
|
|
do {
|
|
mid = lower + (upper - lower) / 2;
|
|
const uint16_t elem = ranges.GetUint16(mid);
|
|
if (current_char < elem) {
|
|
upper = mid;
|
|
} else if (current_char > elem) {
|
|
lower = mid + 1;
|
|
} else {
|
|
DCHECK_EQ(current_char, elem);
|
|
break;
|
|
}
|
|
} while (lower < upper);
|
|
|
|
const bool current_char_ge_last_elem = current_char >= ranges.GetUint16(mid);
|
|
const int current_range_start_index =
|
|
current_char_ge_last_elem ? mid : mid - 1;
|
|
|
|
// Ranges start at even indices and end at odd indices.
|
|
return (current_range_start_index % 2) == 0 ? kTrue : kFalse;
|
|
}
|
|
|
|
void RegExpMacroAssembler::CheckNotInSurrogatePair(int cp_offset,
|
|
V8Label* on_failure) {
|
|
V8Label ok;
|
|
// Check that current character is not a trail surrogate.
|
|
LoadCurrentCharacter(cp_offset, &ok);
|
|
CheckCharacterNotInRange(kTrailSurrogateStart, kTrailSurrogateEnd, &ok);
|
|
// Check that previous character is not a lead surrogate.
|
|
LoadCurrentCharacter(cp_offset - 1, &ok);
|
|
CheckCharacterInRange(kLeadSurrogateStart, kLeadSurrogateEnd, on_failure);
|
|
Bind(&ok);
|
|
}
|
|
|
|
void RegExpMacroAssembler::LoadCurrentCharacter(int cp_offset,
|
|
V8Label* on_end_of_input,
|
|
bool check_bounds,
|
|
int characters,
|
|
int eats_at_least) {
|
|
// By default, eats_at_least = characters.
|
|
if (eats_at_least == kUseCharactersValue) {
|
|
eats_at_least = characters;
|
|
}
|
|
|
|
LoadCurrentCharacterImpl(cp_offset, on_end_of_input, check_bounds, characters,
|
|
eats_at_least);
|
|
}
|
|
|
|
void NativeRegExpMacroAssembler::LoadCurrentCharacterImpl(
|
|
int cp_offset,
|
|
V8Label* on_end_of_input,
|
|
bool check_bounds,
|
|
int characters,
|
|
int eats_at_least) {
|
|
// It's possible to preload a small number of characters when each success
|
|
// path requires a large number of characters, but not the reverse.
|
|
DCHECK_GE(eats_at_least, characters);
|
|
|
|
CHECK(base::IsInRange(cp_offset, kMinCPOffset, kMaxCPOffset));
|
|
if (check_bounds) {
|
|
if (cp_offset >= 0) {
|
|
CheckPosition(cp_offset + eats_at_least - 1, on_end_of_input);
|
|
} else {
|
|
CheckPosition(cp_offset, on_end_of_input);
|
|
}
|
|
}
|
|
LoadCurrentCharacterUnchecked(cp_offset, characters);
|
|
}
|
|
|
|
void RegExpMacroAssembler::SkipUntilCharAnd(int cp_offset,
|
|
int advance_by,
|
|
unsigned character,
|
|
unsigned mask,
|
|
int eats_at_least,
|
|
V8Label* on_match,
|
|
V8Label* on_no_match) {
|
|
V8Label loop;
|
|
Bind(&loop);
|
|
LoadCurrentCharacter(cp_offset, on_no_match, true, 1, eats_at_least);
|
|
CheckCharacterAfterAnd(character, mask, on_match);
|
|
AdvanceCurrentPosition(advance_by);
|
|
GoTo(&loop);
|
|
}
|
|
|
|
void RegExpMacroAssembler::SkipUntilChar(int cp_offset,
|
|
int advance_by,
|
|
unsigned character,
|
|
V8Label* on_match,
|
|
V8Label* on_no_match) {
|
|
V8Label loop;
|
|
Bind(&loop);
|
|
LoadCurrentCharacter(cp_offset, on_no_match, true);
|
|
CheckCharacter(character, on_match);
|
|
AdvanceCurrentPosition(advance_by);
|
|
GoTo(&loop);
|
|
}
|
|
|
|
void RegExpMacroAssembler::SkipUntilCharPosChecked(int cp_offset,
|
|
int advance_by,
|
|
unsigned character,
|
|
int eats_at_least,
|
|
V8Label* on_match,
|
|
V8Label* on_no_match) {
|
|
V8Label loop;
|
|
Bind(&loop);
|
|
LoadCurrentCharacter(cp_offset, on_no_match, true, 1, eats_at_least);
|
|
CheckCharacter(character, on_match);
|
|
AdvanceCurrentPosition(advance_by);
|
|
GoTo(&loop);
|
|
}
|
|
|
|
void RegExpMacroAssembler::SkipUntilCharOrChar(int cp_offset,
|
|
int advance_by,
|
|
unsigned char1,
|
|
unsigned char2,
|
|
V8Label* on_match,
|
|
V8Label* on_no_match) {
|
|
V8Label loop;
|
|
Bind(&loop);
|
|
LoadCurrentCharacter(cp_offset, on_no_match, true);
|
|
CheckCharacter(char1, on_match);
|
|
CheckCharacter(char2, on_match);
|
|
AdvanceCurrentPosition(advance_by);
|
|
GoTo(&loop);
|
|
}
|
|
|
|
void RegExpMacroAssembler::SkipUntilGtOrNotBitInTable(int cp_offset,
|
|
int advance_by,
|
|
unsigned character,
|
|
const TypedData& table,
|
|
V8Label* on_match,
|
|
V8Label* on_no_match) {
|
|
ASSERT(base::IsInRange(character, std::numeric_limits<uint16_t>::min(),
|
|
std::numeric_limits<uint16_t>::max()));
|
|
V8Label loop, advance_and_continue;
|
|
Bind(&loop);
|
|
LoadCurrentCharacter(cp_offset, on_no_match, true);
|
|
CheckCharacterGT(character, on_match);
|
|
CheckBitInTable(table, &advance_and_continue);
|
|
GoTo(on_match);
|
|
Bind(&advance_and_continue);
|
|
AdvanceCurrentPosition(advance_by);
|
|
GoTo(&loop);
|
|
}
|
|
|
|
void RegExpMacroAssembler::SkipUntilOneOfMasked(int cp_offset,
|
|
int advance_by,
|
|
unsigned both_chars,
|
|
unsigned both_mask,
|
|
int max_offset,
|
|
unsigned chars1,
|
|
unsigned mask1,
|
|
unsigned chars2,
|
|
unsigned mask2,
|
|
V8Label* on_match1,
|
|
V8Label* on_match2,
|
|
V8Label* on_failure) {
|
|
V8Label loop, found;
|
|
Bind(&loop);
|
|
CheckPosition(max_offset, on_failure);
|
|
LoadCurrentCharacter(cp_offset, on_failure, false, 4);
|
|
CheckCharacterAfterAnd(both_chars, both_mask, &found);
|
|
AdvanceCurrentPosition(advance_by);
|
|
GoTo(&loop);
|
|
Bind(&found);
|
|
CheckCharacterAfterAnd(chars1, mask1, on_match1);
|
|
CheckCharacterAfterAnd(chars2, mask2, on_match2);
|
|
AdvanceCurrentPosition(advance_by);
|
|
GoTo(&loop);
|
|
}
|
|
|
|
bool RegExpMacroAssembler::CanOptimizeSpecialClassRanges(
|
|
StandardCharacterSet character_set) const {
|
|
if (character_set == StandardCharacterSet::kNotWhitespace) {
|
|
// The emitted code for generic character classes is good enough.
|
|
return false;
|
|
}
|
|
if (character_set == StandardCharacterSet::kWhitespace &&
|
|
mode() != Mode::LATIN1) {
|
|
// TODO(pthier): Support \s for 2-byte inputs.
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void RegExpMacroAssembler::SkipUntilOneOfMasked3(
|
|
const SkipUntilOneOfMasked3Args& args) {
|
|
// The base implementation for architectures that don't implement simd
|
|
// optimizations.
|
|
//
|
|
// See the definition of the kSkipUntilOneOfMasked3 peephole bytecode
|
|
// for more context. The initial bytecode sequence is:
|
|
//
|
|
// sequence offset name
|
|
// bc0 0 SkipUntilBitInTable
|
|
// bc1 20 CheckPosition
|
|
// bc2 28 Load4CurrentCharsUnchecked
|
|
// bc3 2c AndCheck4Chars
|
|
// bc4 3c AdvanceCpAndGoto
|
|
// bc5 48 Load4CurrentChars
|
|
// bc6 4c AndCheck4Chars
|
|
// bc7 5c AndCheck4Chars
|
|
// bc8 6c AndCheckNot4Chars
|
|
|
|
V8Label bc0_skip_until_bit_in_table, bc1_check_current_position,
|
|
bc4_advance_cp_and_goto, bc5_load_4_current_chars;
|
|
Bind(&bc0_skip_until_bit_in_table);
|
|
SkipUntilBitInTable(args.bc0_cp_offset, *args.bc0_table,
|
|
*args.bc0_nibble_table, args.bc0_advance_by,
|
|
&bc1_check_current_position, &bc1_check_current_position);
|
|
Bind(&bc1_check_current_position);
|
|
CheckPosition(args.bc1_cp_offset, args.bc1_on_failure);
|
|
LoadCurrentCharacter(args.bc2_cp_offset, nullptr, false, 4);
|
|
CheckCharacterAfterAnd(args.bc3_characters, args.bc3_mask,
|
|
&bc5_load_4_current_chars);
|
|
Bind(&bc4_advance_cp_and_goto);
|
|
AdvanceCurrentPosition(args.bc4_by);
|
|
GoTo(&bc0_skip_until_bit_in_table);
|
|
Bind(&bc5_load_4_current_chars);
|
|
LoadCurrentCharacter(args.bc5_cp_offset, &bc4_advance_cp_and_goto, true, 4);
|
|
CheckCharacterAfterAnd(args.bc6_characters, args.bc6_mask, args.bc6_on_equal);
|
|
CheckCharacterAfterAnd(args.bc7_characters, args.bc7_mask, args.bc7_on_equal);
|
|
CheckNotCharacterAfterAnd(args.bc8_characters, args.bc8_mask,
|
|
&bc4_advance_cp_and_goto);
|
|
GoTo(args.fallthrough_jump_target);
|
|
}
|
|
|
|
#ifndef COMPILING_IRREGEXP_FOR_EXTERNAL_EMBEDDER
|
|
|
|
// Returns a {Result} sentinel, or the number of successful matches.
|
|
int NativeRegExpMacroAssembler::Match(DirectHandle<IrRegExpData> regexp_data,
|
|
const String\DirectHandle<String> subject,
|
|
int* offsets_vector,
|
|
int offsets_vector_length,
|
|
int previous_index,
|
|
Isolate* isolate) {
|
|
ASSERT(subject->IsFlat());
|
|
DCHECK_LE(0, previous_index);
|
|
DCHECK_LE(previous_index, subject->length());
|
|
|
|
// No allocations before calling the regexp, but we can't use
|
|
// DisallowGarbageCollection, since regexps might be preempted, and another
|
|
// thread might do allocation anyway.
|
|
|
|
Tagged<String> subject_ptr = *subject;
|
|
// Character offsets into string.
|
|
int start_offset = previous_index;
|
|
int char_length = subject_ptr->length() - start_offset;
|
|
int slice_offset = 0;
|
|
|
|
// The string has been flattened, so if it is a cons string it contains the
|
|
// full string in the first part.
|
|
if (StringShape(subject_ptr).IsCons()) {
|
|
DCHECK_EQ(0, Cast<ConsString>(subject_ptr)->second()->length());
|
|
subject_ptr = Cast<ConsString>(subject_ptr)->first();
|
|
} else if (StringShape(subject_ptr).IsSliced()) {
|
|
Tagged<SlicedString> slice = Cast<SlicedString>(subject_ptr);
|
|
subject_ptr = slice->parent();
|
|
slice_offset = slice->offset();
|
|
}
|
|
if (StringShape(subject_ptr).IsThin()) {
|
|
subject_ptr = Cast<ThinString>(subject_ptr)->actual();
|
|
}
|
|
// Ensure that an underlying string has the same representation.
|
|
bool is_one_byte = subject_ptr->IsOneByteRepresentation();
|
|
ASSERT(IsExternalString(subject_ptr) || IsSeqString(subject_ptr));
|
|
// String is now either Sequential or External
|
|
int char_size_shift = is_one_byte ? 0 : 1;
|
|
|
|
DisallowGarbageCollection no_gc;
|
|
const uint8_t* input_start =
|
|
subject_ptr->AddressOfCharacterAt(start_offset + slice_offset, no_gc);
|
|
int byte_length = char_length << char_size_shift;
|
|
const uint8_t* input_end = input_start + byte_length;
|
|
return Execute(*subject, start_offset, input_start, input_end, offsets_vector,
|
|
offsets_vector_length, isolate, *regexp_data);
|
|
}
|
|
|
|
// static
|
|
int NativeRegExpMacroAssembler::ExecuteForTesting(Tagged<String> input,
|
|
int start_offset,
|
|
const uint8_t* input_start,
|
|
const uint8_t* input_end,
|
|
int* output,
|
|
int output_size,
|
|
Isolate* isolate,
|
|
Tagged<JSRegExp> regexp) {
|
|
Tagged<RegExpData> data = regexp->data(isolate);
|
|
return Execute(input, start_offset, input_start, input_end, output,
|
|
output_size, isolate, SbxCast<IrRegExpData>(data));
|
|
}
|
|
|
|
// Returns a {Result} sentinel, or the number of successful matches.
|
|
int NativeRegExpMacroAssembler::Execute(
|
|
Tagged<String>
|
|
input, // This needs to be the unpacked (sliced, cons) string.
|
|
int start_offset,
|
|
const uint8_t* input_start,
|
|
const uint8_t* input_end,
|
|
int* output,
|
|
int output_size,
|
|
Isolate* isolate,
|
|
Tagged<IrRegExpData> regexp_data) {
|
|
bool is_one_byte = String::IsOneByteRepresentationUnderneath(input);
|
|
Tagged<Code> code = regexp_data->code(isolate, is_one_byte);
|
|
RegExp::CallOrigin call_origin = RegExp::CallOrigin::kFromRuntime;
|
|
|
|
using RegexpMatcherSig =
|
|
// NOLINTNEXTLINE(readability/casting)
|
|
int(Address input_string, int start_offset, const uint8_t* input_start,
|
|
const uint8_t* input_end, int* output, int output_size,
|
|
int call_origin, Isolate* isolate, Address regexp_data);
|
|
|
|
auto fn = GeneratedCode<RegexpMatcherSig>::FromCode(isolate, code);
|
|
int result = fn.CallSandboxed(input.ptr(), start_offset, input_start,
|
|
input_end, output, output_size, call_origin,
|
|
isolate, regexp_data.ptr());
|
|
DCHECK_GE(result, SMALLEST_REGEXP_RESULT);
|
|
|
|
if (result == EXCEPTION && !isolate->has_exception()) {
|
|
// We detected a stack overflow (on the backtrack stack) in RegExp code,
|
|
// but haven't created the exception yet. Additionally, we allow heap
|
|
// allocation because even though it invalidates {input_start} and
|
|
// {input_end}, we are about to return anyway.
|
|
AllowGarbageCollection allow_allocation;
|
|
isolate->StackOverflow();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
#endif // !COMPILING_IRREGEXP_FOR_EXTERNAL_EMBEDDER
|
|
|
|
// clang-format off
|
|
const uint8_t RegExpMacroAssembler::word_character_map_[] = {
|
|
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
|
|
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
|
|
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
|
|
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
|
|
|
|
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
|
|
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
|
|
0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, // '0' - '7'
|
|
0xFFu, 0xFFu, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, // '8' - '9'
|
|
|
|
0x00u, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, // 'A' - 'G'
|
|
0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, // 'H' - 'O'
|
|
0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, // 'P' - 'W'
|
|
0xFFu, 0xFFu, 0xFFu, 0x00u, 0x00u, 0x00u, 0x00u, 0xFFu, // 'X' - 'Z', '_'
|
|
|
|
0x00u, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, // 'a' - 'g'
|
|
0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, // 'h' - 'o'
|
|
0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, // 'p' - 'w'
|
|
0xFFu, 0xFFu, 0xFFu, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, // 'x' - 'z'
|
|
// Latin-1 range
|
|
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
|
|
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
|
|
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
|
|
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
|
|
|
|
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
|
|
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
|
|
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
|
|
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
|
|
|
|
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
|
|
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
|
|
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
|
|
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
|
|
|
|
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
|
|
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
|
|
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
|
|
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
|
|
};
|
|
// clang-format on
|
|
|
|
} // namespace dart
|