7 #include "src/arguments-inl.h" 8 #include "src/conversions-inl.h" 9 #include "src/counters.h" 10 #include "src/isolate-inl.h" 11 #include "src/message-template.h" 12 #include "src/objects/js-array-inl.h" 13 #include "src/regexp/jsregexp-inl.h" 14 #include "src/regexp/jsregexp.h" 15 #include "src/regexp/regexp-utils.h" 16 #include "src/runtime/runtime-utils.h" 17 #include "src/string-builder-inl.h" 18 #include "src/string-search.h" 19 #include "src/zone/zone-chunk-list.h" 28 bool has_named_captures) {
29 const uint32_t kAdditionalArgsWithoutNamedCaptures = 2;
30 const uint32_t kAdditionalArgsWithNamedCaptures = 3;
31 if (num_captures > Code::kMaxArguments)
return -1;
33 ? num_captures + kAdditionalArgsWithNamedCaptures
34 : num_captures + kAdditionalArgsWithoutNamedCaptures;
35 STATIC_ASSERT(Code::kMaxArguments < std::numeric_limits<uint32_t>::max() -
36 kAdditionalArgsWithNamedCaptures);
37 return (argc > Code::kMaxArguments) ? -1 : argc;
42 int LookupNamedCapture(
const std::function<
bool(String)>& name_matches,
43 FixedArray capture_name_map) {
47 int maybe_capture_index = -1;
48 const int named_capture_count = capture_name_map->length() >> 1;
49 for (
int j = 0; j < named_capture_count; j++) {
52 const int name_ix = j * 2;
53 const int index_ix = j * 2 + 1;
55 String capture_name = String::cast(capture_name_map->get(name_ix));
56 if (!name_matches(capture_name))
continue;
58 maybe_capture_index = Smi::ToInt(capture_name_map->get(index_ix));
62 return maybe_capture_index;
70 : parts_(zone), replacement_substrings_(zone) {}
82 int parts() {
return static_cast<int>(parts_.size()); }
89 REPLACEMENT_SUBSTRING,
95 struct ReplacementPart {
96 static inline ReplacementPart SubjectMatch() {
97 return ReplacementPart(SUBJECT_CAPTURE, 0);
99 static inline ReplacementPart SubjectCapture(
int capture_index) {
100 return ReplacementPart(SUBJECT_CAPTURE, capture_index);
102 static inline ReplacementPart SubjectPrefix() {
103 return ReplacementPart(SUBJECT_PREFIX, 0);
105 static inline ReplacementPart SubjectSuffix(
int subject_length) {
106 return ReplacementPart(SUBJECT_SUFFIX, subject_length);
108 static inline ReplacementPart ReplacementString() {
109 return ReplacementPart(REPLACEMENT_STRING, 0);
111 static inline ReplacementPart EmptyReplacement() {
112 return ReplacementPart(EMPTY_REPLACEMENT, 0);
114 static inline ReplacementPart ReplacementSubString(
int from,
int to) {
117 return ReplacementPart(-from, to);
122 ReplacementPart(
int tag,
int data) : tag(tag), data(data) {
124 DCHECK(tag < NUMBER_OF_PART_TYPES);
144 template <
typename Char>
147 FixedArray capture_name_map,
int capture_count,
148 int subject_length) {
152 int length = characters.length();
154 for (
int i = 0;
i < length;
i++) {
155 Char c = characters[
i];
157 int next_index =
i + 1;
158 if (next_index == length) {
161 Char c2 = characters[next_index];
167 ReplacementPart::ReplacementSubString(last, next_index));
168 last = next_index + 1;
177 parts->push_back(ReplacementPart::ReplacementSubString(last,
i));
179 parts->push_back(ReplacementPart::SubjectPrefix());
185 parts->push_back(ReplacementPart::ReplacementSubString(last,
i));
187 parts->push_back(ReplacementPart::SubjectSuffix(subject_length));
193 parts->push_back(ReplacementPart::ReplacementSubString(last,
i));
195 parts->push_back(ReplacementPart::SubjectMatch());
209 int capture_ref = c2 -
'0';
210 if (capture_ref > capture_count) {
214 int second_digit_index = next_index + 1;
215 if (second_digit_index < length) {
217 Char c3 = characters[second_digit_index];
218 if (
'0' <= c3 && c3 <=
'9') {
219 int double_digit_ref = capture_ref * 10 + c3 -
'0';
220 if (double_digit_ref <= capture_count) {
221 next_index = second_digit_index;
222 capture_ref = double_digit_ref;
226 if (capture_ref > 0) {
229 ReplacementPart::ReplacementSubString(last,
i));
231 DCHECK(capture_ref <= capture_count);
232 parts->push_back(ReplacementPart::SubjectCapture(capture_ref));
233 last = next_index + 1;
239 if (capture_name_map.is_null()) {
247 const int name_start_index = next_index + 1;
248 int closing_bracket_index = -1;
249 for (
int j = name_start_index; j < length; j++) {
250 if (characters[j] ==
'>') {
251 closing_bracket_index = j;
258 if (closing_bracket_index == -1) {
264 characters.SubVector(name_start_index, closing_bracket_index);
268 const int capture_index = LookupNamedCapture(
269 [=](
String capture_name) {
270 return capture_name->IsEqualTo(requested_name);
279 DCHECK(capture_index == -1 ||
280 (1 <= capture_index && capture_index <= capture_count));
283 parts->push_back(ReplacementPart::ReplacementSubString(last,
i));
286 (capture_index == -1)
287 ? ReplacementPart::EmptyReplacement()
288 : ReplacementPart::SubjectCapture(capture_index));
289 last = closing_bracket_index + 1;
290 i = closing_bracket_index;
304 parts->push_back(ReplacementPart::ReplacementSubString(last, length));
316 int subject_length) {
320 DCHECK(content.IsFlat());
323 if (capture_count > 0) {
324 DCHECK_EQ(regexp->TypeTag(), JSRegExp::IRREGEXP);
325 Object* maybe_capture_name_map = regexp->CaptureNameMap();
326 if (maybe_capture_name_map->IsFixedArray()) {
327 capture_name_map = FixedArray::cast(maybe_capture_name_map);
332 if (content.IsOneByte()) {
333 simple = ParseReplacementPattern(&parts_, content.ToOneByteVector(),
334 capture_name_map, capture_count,
337 DCHECK(content.IsTwoByte());
338 simple = ParseReplacementPattern(&parts_, content.ToUC16Vector(),
339 capture_name_map, capture_count,
342 if (simple)
return true;
346 int substring_index = 0;
347 for (ReplacementPart& part : parts_) {
352 replacement_substrings_.push_back(
353 isolate->factory()->NewSubString(replacement, from, to));
354 part.tag = REPLACEMENT_SUBSTRING;
355 part.data = substring_index;
357 }
else if (tag == REPLACEMENT_STRING) {
358 replacement_substrings_.push_back(replacement);
359 part.data = substring_index;
367 void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
368 int match_from,
int match_to, int32_t* match) {
369 DCHECK_LT(0, parts_.size());
370 for (ReplacementPart& part : parts_) {
373 if (match_from > 0) builder->AddSubjectSlice(0, match_from);
375 case SUBJECT_SUFFIX: {
376 int subject_length = part.data;
377 if (match_to < subject_length) {
378 builder->AddSubjectSlice(match_to, subject_length);
382 case SUBJECT_CAPTURE: {
383 int capture = part.data;
384 int from = match[capture * 2];
385 int to = match[capture * 2 + 1];
386 if (from >= 0 && to > from) {
387 builder->AddSubjectSlice(from, to);
391 case REPLACEMENT_SUBSTRING:
392 case REPLACEMENT_STRING:
393 builder->AddString(replacement_substrings_[part.data]);
395 case EMPTY_REPLACEMENT:
403 void FindOneByteStringIndices(Vector<const uint8_t> subject, uint8_t pattern,
404 std::vector<int>* indices,
unsigned int limit) {
408 const uint8_t* subject_start = subject.start();
409 const uint8_t* subject_end = subject_start + subject.length();
410 const uint8_t* pos = subject_start;
412 pos =
reinterpret_cast<const uint8_t*
>(
413 memchr(pos, pattern, subject_end - pos));
414 if (pos ==
nullptr)
return;
415 indices->push_back(static_cast<int>(pos - subject_start));
421 void FindTwoByteStringIndices(
const Vector<const uc16> subject, uc16 pattern,
422 std::vector<int>* indices,
unsigned int limit) {
424 const uc16* subject_start = subject.start();
425 const uc16* subject_end = subject_start + subject.length();
426 for (
const uc16* pos = subject_start; pos < subject_end && limit > 0; pos++) {
427 if (*pos == pattern) {
428 indices->push_back(static_cast<int>(pos - subject_start));
434 template <
typename SubjectChar,
typename PatternChar>
435 void FindStringIndices(Isolate* isolate, Vector<const SubjectChar> subject,
436 Vector<const PatternChar> pattern,
437 std::vector<int>* indices,
unsigned int limit) {
441 int pattern_length = pattern.length();
443 StringSearch<PatternChar, SubjectChar> search(isolate, pattern);
445 index = search.Search(subject, index);
446 if (index < 0)
return;
447 indices->push_back(index);
448 index += pattern_length;
453 void FindStringIndicesDispatch(Isolate* isolate, String subject, String pattern,
454 std::vector<int>* indices,
unsigned int limit) {
456 DisallowHeapAllocation no_gc;
457 String::FlatContent subject_content = subject->GetFlatContent();
458 String::FlatContent pattern_content = pattern->GetFlatContent();
459 DCHECK(subject_content.IsFlat());
460 DCHECK(pattern_content.IsFlat());
461 if (subject_content.IsOneByte()) {
462 Vector<const uint8_t> subject_vector = subject_content.ToOneByteVector();
463 if (pattern_content.IsOneByte()) {
464 Vector<const uint8_t> pattern_vector =
465 pattern_content.ToOneByteVector();
466 if (pattern_vector.length() == 1) {
467 FindOneByteStringIndices(subject_vector, pattern_vector[0], indices,
470 FindStringIndices(isolate, subject_vector, pattern_vector, indices,
474 FindStringIndices(isolate, subject_vector,
475 pattern_content.ToUC16Vector(), indices, limit);
478 Vector<const uc16> subject_vector = subject_content.ToUC16Vector();
479 if (pattern_content.IsOneByte()) {
480 Vector<const uint8_t> pattern_vector =
481 pattern_content.ToOneByteVector();
482 if (pattern_vector.length() == 1) {
483 FindTwoByteStringIndices(subject_vector, pattern_vector[0], indices,
486 FindStringIndices(isolate, subject_vector, pattern_vector, indices,
490 Vector<const uc16> pattern_vector = pattern_content.ToUC16Vector();
491 if (pattern_vector.length() == 1) {
492 FindTwoByteStringIndices(subject_vector, pattern_vector[0], indices,
495 FindStringIndices(isolate, subject_vector, pattern_vector, indices,
504 std::vector<int>* GetRewoundRegexpIndicesList(Isolate* isolate) {
505 std::vector<int>* list = isolate->regexp_indices();
510 void TruncateRegexpIndicesList(Isolate* isolate) {
513 static const int kMaxRegexpIndicesListCapacity = 8 * KB;
514 std::vector<int>* indicies = isolate->regexp_indices();
515 if (indicies->capacity() > kMaxRegexpIndicesListCapacity) {
518 indicies->shrink_to_fit();
523 template <
typename ResultSeqString>
524 V8_WARN_UNUSED_RESULT
static Object* StringReplaceGlobalAtomRegExpWithString(
525 Isolate* isolate, Handle<String> subject, Handle<JSRegExp> pattern_regexp,
526 Handle<String> replacement, Handle<RegExpMatchInfo> last_match_info) {
527 DCHECK(subject->IsFlat());
528 DCHECK(replacement->IsFlat());
530 std::vector<int>* indices = GetRewoundRegexpIndicesList(isolate);
532 DCHECK_EQ(JSRegExp::ATOM, pattern_regexp->TypeTag());
534 String::cast(pattern_regexp->DataAt(JSRegExp::kAtomPatternIndex));
535 int subject_len = subject->length();
536 int pattern_len = pattern->length();
537 int replacement_len = replacement->length();
539 FindStringIndicesDispatch(isolate, *subject, pattern, indices, 0xFFFFFFFF);
541 if (indices->empty())
return *subject;
545 static_cast<int64_t>(pattern_len)) *
546 static_cast<int64_t>(indices->size()) +
547 static_cast<int64_t>(subject_len);
549 if (result_len_64 > static_cast<int64_t>(String::kMaxLength)) {
550 STATIC_ASSERT(String::kMaxLength < kMaxInt);
551 result_len = kMaxInt;
553 result_len =
static_cast<int>(result_len_64);
555 if (result_len == 0) {
556 return ReadOnlyRoots(isolate).empty_string();
562 MaybeHandle<SeqString> maybe_res;
563 if (ResultSeqString::kHasOneByteEncoding) {
564 maybe_res = isolate->factory()->NewRawOneByteString(result_len);
566 maybe_res = isolate->factory()->NewRawTwoByteString(result_len);
568 Handle<SeqString> untyped_res;
569 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, untyped_res, maybe_res);
570 Handle<ResultSeqString> result = Handle<ResultSeqString>::cast(untyped_res);
572 DisallowHeapAllocation no_gc;
573 for (
int index : *indices) {
575 if (subject_pos < index) {
576 String::WriteToFlat(*subject, result->GetChars() + result_pos,
578 result_pos += index - subject_pos;
582 if (replacement_len > 0) {
583 String::WriteToFlat(*replacement, result->GetChars() + result_pos, 0,
585 result_pos += replacement_len;
588 subject_pos = index + pattern_len;
591 if (subject_pos < subject_len) {
592 String::WriteToFlat(*subject, result->GetChars() + result_pos, subject_pos,
596 int32_t match_indices[] = {indices->back(), indices->back() + pattern_len};
597 RegExpImpl::SetLastMatchInfo(isolate, last_match_info, subject, 0,
600 TruncateRegexpIndicesList(isolate);
605 V8_WARN_UNUSED_RESULT
static Object* StringReplaceGlobalRegExpWithString(
606 Isolate* isolate, Handle<String> subject, Handle<JSRegExp> regexp,
607 Handle<String> replacement, Handle<RegExpMatchInfo> last_match_info) {
608 DCHECK(subject->IsFlat());
609 DCHECK(replacement->IsFlat());
611 int capture_count = regexp->CaptureCount();
612 int subject_length = subject->length();
614 JSRegExp::Type typeTag = regexp->TypeTag();
615 if (typeTag == JSRegExp::IRREGEXP) {
617 if (RegExpImpl::IrregexpPrepare(isolate, regexp, subject) == -1) {
618 DCHECK(isolate->has_pending_exception());
619 return ReadOnlyRoots(isolate).exception();
624 Zone zone(isolate->allocator(), ZONE_NAME);
625 CompiledReplacement compiled_replacement(&zone);
626 const bool simple_replace = compiled_replacement.Compile(
627 isolate, regexp, replacement, capture_count, subject_length);
630 if (typeTag == JSRegExp::ATOM && simple_replace) {
631 if (subject->HasOnlyOneByteChars() && replacement->HasOnlyOneByteChars()) {
632 return StringReplaceGlobalAtomRegExpWithString<SeqOneByteString>(
633 isolate, subject, regexp, replacement, last_match_info);
635 return StringReplaceGlobalAtomRegExpWithString<SeqTwoByteString>(
636 isolate, subject, regexp, replacement, last_match_info);
640 RegExpImpl::GlobalCache global_cache(regexp, subject, isolate);
641 if (global_cache.HasException())
return ReadOnlyRoots(isolate).exception();
643 int32_t* current_match = global_cache.FetchNext();
644 if (current_match ==
nullptr) {
645 if (global_cache.HasException())
return ReadOnlyRoots(isolate).exception();
652 int expected_parts = (compiled_replacement.parts() + 1) * 4 + 1;
653 ReplacementStringBuilder builder(isolate->heap(), subject, expected_parts);
658 const int parts_added_per_loop = 2 * (compiled_replacement.parts() + 2);
663 builder.EnsureCapacity(parts_added_per_loop);
665 int start = current_match[0];
666 int end = current_match[1];
669 builder.AddSubjectSlice(prev, start);
672 if (simple_replace) {
673 builder.AddString(replacement);
675 compiled_replacement.Apply(&builder, start, end, current_match);
679 current_match = global_cache.FetchNext();
680 }
while (current_match !=
nullptr);
682 if (global_cache.HasException())
return ReadOnlyRoots(isolate).exception();
684 if (prev < subject_length) {
685 builder.EnsureCapacity(2);
686 builder.AddSubjectSlice(prev, subject_length);
689 RegExpImpl::SetLastMatchInfo(isolate, last_match_info, subject, capture_count,
690 global_cache.LastSuccessfulMatch());
692 RETURN_RESULT_OR_FAILURE(isolate, builder.ToString());
695 template <
typename ResultSeqString>
696 V8_WARN_UNUSED_RESULT
static Object* StringReplaceGlobalRegExpWithEmptyString(
697 Isolate* isolate, Handle<String> subject, Handle<JSRegExp> regexp,
698 Handle<RegExpMatchInfo> last_match_info) {
699 DCHECK(subject->IsFlat());
702 if (regexp->TypeTag() == JSRegExp::ATOM) {
703 Handle<String> empty_string = isolate->factory()->empty_string();
704 if (subject->IsOneByteRepresentation()) {
705 return StringReplaceGlobalAtomRegExpWithString<SeqOneByteString>(
706 isolate, subject, regexp, empty_string, last_match_info);
708 return StringReplaceGlobalAtomRegExpWithString<SeqTwoByteString>(
709 isolate, subject, regexp, empty_string, last_match_info);
713 RegExpImpl::GlobalCache global_cache(regexp, subject, isolate);
714 if (global_cache.HasException())
return ReadOnlyRoots(isolate).exception();
716 int32_t* current_match = global_cache.FetchNext();
717 if (current_match ==
nullptr) {
718 if (global_cache.HasException())
return ReadOnlyRoots(isolate).exception();
722 int start = current_match[0];
723 int end = current_match[1];
724 int capture_count = regexp->CaptureCount();
725 int subject_length = subject->length();
727 int new_length = subject_length - (end - start);
728 if (new_length == 0)
return ReadOnlyRoots(isolate).empty_string();
730 Handle<ResultSeqString> answer;
731 if (ResultSeqString::kHasOneByteEncoding) {
732 answer = Handle<ResultSeqString>::cast(
733 isolate->factory()->NewRawOneByteString(new_length).ToHandleChecked());
735 answer = Handle<ResultSeqString>::cast(
736 isolate->factory()->NewRawTwoByteString(new_length).ToHandleChecked());
742 DisallowHeapAllocation no_gc;
744 start = current_match[0];
745 end = current_match[1];
748 String::WriteToFlat(*subject, answer->GetChars() + position, prev, start);
749 position += start - prev;
753 current_match = global_cache.FetchNext();
754 }
while (current_match !=
nullptr);
756 if (global_cache.HasException())
return ReadOnlyRoots(isolate).exception();
758 RegExpImpl::SetLastMatchInfo(isolate, last_match_info, subject, capture_count,
759 global_cache.LastSuccessfulMatch());
761 if (prev < subject_length) {
763 String::WriteToFlat(*subject, answer->GetChars() + position, prev,
765 position += subject_length - prev;
768 if (position == 0)
return ReadOnlyRoots(isolate).empty_string();
771 int string_size = ResultSeqString::SizeFor(position);
772 int allocated_string_size = ResultSeqString::SizeFor(new_length);
773 int delta = allocated_string_size - string_size;
775 answer->set_length(position);
776 if (delta == 0)
return *answer;
778 Address end_of_string = answer->address() + string_size;
779 Heap* heap = isolate->heap();
787 if (!heap->IsLargeObject(*answer)) {
788 heap->CreateFillerObjectAt(end_of_string, delta, ClearRecordedSlots::kNo);
795 Object* StringReplaceGlobalRegExpWithStringHelper(
796 Isolate* isolate, Handle<JSRegExp> regexp, Handle<String> subject,
797 Handle<String> replacement, Handle<RegExpMatchInfo> last_match_info) {
798 CHECK(regexp->GetFlags() & JSRegExp::kGlobal);
800 subject = String::Flatten(isolate, subject);
802 if (replacement->length() == 0) {
803 if (subject->HasOnlyOneByteChars()) {
804 return StringReplaceGlobalRegExpWithEmptyString<SeqOneByteString>(
805 isolate, subject, regexp, last_match_info);
807 return StringReplaceGlobalRegExpWithEmptyString<SeqTwoByteString>(
808 isolate, subject, regexp, last_match_info);
812 replacement = String::Flatten(isolate, replacement);
814 return StringReplaceGlobalRegExpWithString(isolate, subject, regexp,
815 replacement, last_match_info);
820 RUNTIME_FUNCTION(Runtime_StringSplit) {
821 HandleScope handle_scope(isolate);
822 DCHECK_EQ(3, args.length());
823 CONVERT_ARG_HANDLE_CHECKED(String, subject, 0);
824 CONVERT_ARG_HANDLE_CHECKED(String, pattern, 1);
825 CONVERT_NUMBER_CHECKED(
uint32_t, limit, Uint32, args[2]);
828 int subject_length = subject->length();
829 int pattern_length = pattern->length();
830 CHECK_LT(0, pattern_length);
832 if (limit == 0xFFFFFFFFu) {
833 FixedArray last_match_cache_unused;
834 Handle<Object> cached_answer(
835 RegExpResultsCache::Lookup(isolate->heap(), *subject, *pattern,
836 &last_match_cache_unused,
837 RegExpResultsCache::STRING_SPLIT_SUBSTRINGS),
839 if (*cached_answer != Smi::kZero) {
841 Handle<JSArray> result = isolate->factory()->NewJSArrayWithElements(
842 Handle<FixedArray>::cast(cached_answer));
851 subject = String::Flatten(isolate, subject);
852 pattern = String::Flatten(isolate, pattern);
854 std::vector<int>* indices = GetRewoundRegexpIndicesList(isolate);
856 FindStringIndicesDispatch(isolate, *subject, *pattern, indices, limit);
858 if (static_cast<uint32_t>(indices->size()) < limit) {
859 indices->push_back(subject_length);
865 int part_count =
static_cast<int>(indices->size());
867 Handle<JSArray> result =
868 isolate->factory()->NewJSArray(PACKED_ELEMENTS, part_count, part_count,
869 INITIALIZE_ARRAY_ELEMENTS_WITH_HOLE);
871 DCHECK(result->HasObjectElements());
873 Handle<FixedArray> elements(FixedArray::cast(result->elements()), isolate);
875 if (part_count == 1 && indices->at(0) == subject_length) {
876 elements->set(0, *subject);
879 FOR_WITH_HANDLE_SCOPE(isolate,
int,
i = 0,
i,
i < part_count,
i++, {
880 int part_end = indices->at(
i);
881 Handle<String> substring =
882 isolate->factory()->NewProperSubString(subject, part_start, part_end);
883 elements->set(
i, *substring);
884 part_start = part_end + pattern_length;
888 if (limit == 0xFFFFFFFFu) {
889 if (result->HasObjectElements()) {
890 RegExpResultsCache::Enter(isolate, subject, pattern, elements,
891 isolate->factory()->empty_fixed_array(),
892 RegExpResultsCache::STRING_SPLIT_SUBSTRINGS);
896 TruncateRegexpIndicesList(isolate);
901 RUNTIME_FUNCTION(Runtime_RegExpExec) {
902 HandleScope scope(isolate);
903 DCHECK_EQ(4, args.length());
904 CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 0);
905 CONVERT_ARG_HANDLE_CHECKED(String, subject, 1);
906 CONVERT_INT32_ARG_CHECKED(index, 2);
907 CONVERT_ARG_HANDLE_CHECKED(RegExpMatchInfo, last_match_info, 3);
911 CHECK_GE(subject->length(), index);
912 isolate->counters()->regexp_entry_runtime()->Increment();
913 RETURN_RESULT_OR_FAILURE(isolate, RegExpImpl::Exec(isolate, regexp, subject,
914 index, last_match_info));
917 RUNTIME_FUNCTION(Runtime_RegExpInternalReplace) {
918 HandleScope scope(isolate);
919 DCHECK_EQ(3, args.length());
920 CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 0);
921 CONVERT_ARG_HANDLE_CHECKED(String, subject, 1);
922 CONVERT_ARG_HANDLE_CHECKED(String, replacement, 2);
924 Handle<RegExpMatchInfo> internal_match_info =
925 isolate->regexp_internal_match_info();
927 return StringReplaceGlobalRegExpWithStringHelper(
928 isolate, regexp, subject, replacement, internal_match_info);
933 class MatchInfoBackedMatch :
public String::Match {
935 MatchInfoBackedMatch(Isolate* isolate, Handle<JSRegExp> regexp,
936 Handle<String> subject,
937 Handle<RegExpMatchInfo> match_info)
938 : isolate_(isolate), match_info_(match_info) {
939 subject_ = String::Flatten(isolate, subject);
941 if (regexp->TypeTag() == JSRegExp::IRREGEXP) {
942 Object* o = regexp->CaptureNameMap();
943 has_named_captures_ = o->IsFixedArray();
944 if (has_named_captures_) {
945 capture_name_map_ = handle(FixedArray::cast(o), isolate);
948 has_named_captures_ =
false;
952 Handle<String> GetMatch()
override {
953 return RegExpUtils::GenericCaptureGetter(isolate_, match_info_, 0,
nullptr);
956 Handle<String> GetPrefix()
override {
957 const int match_start = match_info_->Capture(0);
958 return isolate_->factory()->NewSubString(subject_, 0, match_start);
961 Handle<String> GetSuffix()
override {
962 const int match_end = match_info_->Capture(1);
963 return isolate_->factory()->NewSubString(subject_, match_end,
967 bool HasNamedCaptures()
override {
return has_named_captures_; }
969 int CaptureCount()
override {
970 return match_info_->NumberOfCaptureRegisters() / 2;
973 MaybeHandle<String> GetCapture(
int i,
bool* capture_exists)
override {
974 Handle<Object> capture_obj = RegExpUtils::GenericCaptureGetter(
975 isolate_, match_info_,
i, capture_exists);
976 return (*capture_exists) ? Object::ToString(isolate_, capture_obj)
977 : isolate_->factory()->empty_string();
980 MaybeHandle<String> GetNamedCapture(Handle<String> name,
981 CaptureState* state)
override {
982 DCHECK(has_named_captures_);
983 const int capture_index = LookupNamedCapture(
984 [=](String capture_name) {
return capture_name->Equals(*name); },
987 if (capture_index == -1) {
992 DCHECK(1 <= capture_index && capture_index <= CaptureCount());
995 Handle<String> capture_value;
996 ASSIGN_RETURN_ON_EXCEPTION(isolate_, capture_value,
997 GetCapture(capture_index, &capture_exists),
1000 if (!capture_exists) {
1002 return isolate_->factory()->empty_string();
1005 return capture_value;
1011 Handle<String> subject_;
1012 Handle<RegExpMatchInfo> match_info_;
1014 bool has_named_captures_;
1015 Handle<FixedArray> capture_name_map_;
1018 class VectorBackedMatch :
public String::Match {
1020 VectorBackedMatch(Isolate* isolate, Handle<String> subject,
1021 Handle<String> match,
int match_position,
1022 ZoneVector<Handle<Object>>* captures,
1023 Handle<Object> groups_obj)
1024 : isolate_(isolate),
1026 match_position_(match_position),
1027 captures_(captures) {
1028 subject_ = String::Flatten(isolate, subject);
1030 DCHECK(groups_obj->IsUndefined(isolate) || groups_obj->IsJSReceiver());
1031 has_named_captures_ = !groups_obj->IsUndefined(isolate);
1032 if (has_named_captures_) groups_obj_ = Handle<JSReceiver>::cast(groups_obj);
1035 Handle<String> GetMatch()
override {
return match_; }
1037 Handle<String> GetPrefix()
override {
1038 return isolate_->factory()->NewSubString(subject_, 0, match_position_);
1041 Handle<String> GetSuffix()
override {
1042 const int match_end_position = match_position_ + match_->length();
1043 return isolate_->factory()->NewSubString(subject_, match_end_position,
1044 subject_->length());
1047 bool HasNamedCaptures()
override {
return has_named_captures_; }
1049 int CaptureCount()
override {
return static_cast<int>(captures_->size()); }
1051 MaybeHandle<String> GetCapture(
int i,
bool* capture_exists)
override {
1052 Handle<Object> capture_obj = captures_->at(
i);
1053 if (capture_obj->IsUndefined(isolate_)) {
1054 *capture_exists =
false;
1055 return isolate_->factory()->empty_string();
1057 *capture_exists =
true;
1058 return Object::ToString(isolate_, capture_obj);
1061 MaybeHandle<String> GetNamedCapture(Handle<String> name,
1062 CaptureState* state)
override {
1063 DCHECK(has_named_captures_);
1065 Maybe<bool> maybe_capture_exists =
1066 JSReceiver::HasProperty(groups_obj_, name);
1067 if (maybe_capture_exists.IsNothing())
return MaybeHandle<String>();
1069 if (!maybe_capture_exists.FromJust()) {
1074 Handle<Object> capture_obj;
1075 ASSIGN_RETURN_ON_EXCEPTION(isolate_, capture_obj,
1076 Object::GetProperty(isolate_, groups_obj_, name),
1078 if (capture_obj->IsUndefined(isolate_)) {
1080 return isolate_->factory()->empty_string();
1083 return Object::ToString(isolate_, capture_obj);
1089 Handle<String> subject_;
1090 Handle<String> match_;
1091 const int match_position_;
1092 ZoneVector<Handle<Object>>* captures_;
1094 bool has_named_captures_;
1095 Handle<JSReceiver> groups_obj_;
1100 Handle<JSObject> ConstructNamedCaptureGroupsObject(
1101 Isolate* isolate, Handle<FixedArray> capture_map,
1102 const std::function<Object*(
int)>& f_get_capture) {
1103 Handle<JSObject> groups = isolate->factory()->NewJSObjectWithNullProto();
1105 const int capture_count = capture_map->length() >> 1;
1106 for (
int i = 0;
i < capture_count;
i++) {
1107 const int name_ix =
i * 2;
1108 const int index_ix =
i * 2 + 1;
1110 Handle<String> capture_name(String::cast(capture_map->get(name_ix)),
1112 const int capture_ix = Smi::ToInt(capture_map->get(index_ix));
1113 DCHECK(1 <= capture_ix && capture_ix <= capture_count);
1115 Handle<Object> capture_value(f_get_capture(capture_ix), isolate);
1116 DCHECK(capture_value->IsUndefined(isolate) || capture_value->IsString());
1118 JSObject::AddProperty(isolate, groups, capture_name, capture_value, NONE);
1126 template <
bool has_capture>
1127 static Object* SearchRegExpMultiple(Isolate* isolate, Handle<String> subject,
1128 Handle<JSRegExp> regexp,
1129 Handle<RegExpMatchInfo> last_match_array,
1130 Handle<JSArray> result_array) {
1131 DCHECK(RegExpUtils::IsUnmodifiedRegExp(isolate, regexp));
1132 DCHECK_NE(has_capture, regexp->CaptureCount() == 0);
1133 DCHECK(subject->IsFlat());
1135 int capture_count = regexp->CaptureCount();
1136 int subject_length = subject->length();
1138 static const int kMinLengthToCache = 0x1000;
1140 if (subject_length > kMinLengthToCache) {
1141 FixedArray last_match_cache;
1142 Object* cached_answer = RegExpResultsCache::Lookup(
1143 isolate->heap(), *subject, regexp->data(), &last_match_cache,
1144 RegExpResultsCache::REGEXP_MULTIPLE_INDICES);
1145 if (cached_answer->IsFixedArray()) {
1146 int capture_registers = (capture_count + 1) * 2;
1147 int32_t* last_match = NewArray<int32_t>(capture_registers);
1148 for (
int i = 0;
i < capture_registers;
i++) {
1149 last_match[
i] = Smi::ToInt(last_match_cache->get(
i));
1151 Handle<FixedArray> cached_fixed_array =
1152 Handle<FixedArray>(FixedArray::cast(cached_answer), isolate);
1154 Handle<FixedArray> copied_fixed_array =
1155 isolate->factory()->CopyFixedArrayWithMap(
1156 cached_fixed_array, isolate->factory()->fixed_array_map());
1157 JSArray::SetContent(result_array, copied_fixed_array);
1158 RegExpImpl::SetLastMatchInfo(isolate, last_match_array, subject,
1159 capture_count, last_match);
1160 DeleteArray(last_match);
1161 return *result_array;
1165 RegExpImpl::GlobalCache global_cache(regexp, subject, isolate);
1166 if (global_cache.HasException())
return ReadOnlyRoots(isolate).exception();
1169 DCHECK(result_array->HasObjectElements());
1170 Handle<FixedArray> result_elements(FixedArray::cast(result_array->elements()),
1172 if (result_elements->length() < 16) {
1173 result_elements = isolate->factory()->NewFixedArrayWithHoles(16);
1176 FixedArrayBuilder builder(result_elements);
1179 int match_start = -1;
1184 static const int kMaxBuilderEntriesPerRegExpMatch = 5;
1187 int32_t* current_match = global_cache.FetchNext();
1188 if (current_match ==
nullptr)
break;
1189 match_start = current_match[0];
1190 builder.EnsureCapacity(isolate, kMaxBuilderEntriesPerRegExpMatch);
1191 if (match_end < match_start) {
1192 ReplacementStringBuilder::AddSubjectSlice(&builder, match_end,
1195 match_end = current_match[1];
1198 HandleScope temp_scope(isolate);
1199 Handle<String> match;
1201 match = isolate->factory()->NewProperSubString(subject, match_start,
1205 isolate->factory()->NewSubString(subject, match_start, match_end);
1214 Handle<Object> maybe_capture_map(regexp->CaptureNameMap(), isolate);
1215 const bool has_named_captures = maybe_capture_map->IsFixedArray();
1218 has_named_captures ? 4 + capture_count : 3 + capture_count;
1220 Handle<FixedArray> elements = isolate->factory()->NewFixedArray(argc);
1223 elements->set(cursor++, *match);
1224 for (
int i = 1;
i <= capture_count;
i++) {
1225 int start = current_match[
i * 2];
1227 int end = current_match[
i * 2 + 1];
1228 DCHECK(start <= end);
1229 Handle<String> substring =
1230 isolate->factory()->NewSubString(subject, start, end);
1231 elements->set(cursor++, *substring);
1233 DCHECK_GT(0, current_match[
i * 2 + 1]);
1234 elements->set(cursor++, ReadOnlyRoots(isolate).undefined_value());
1238 elements->set(cursor++, Smi::FromInt(match_start));
1239 elements->set(cursor++, *subject);
1241 if (has_named_captures) {
1242 Handle<FixedArray> capture_map =
1243 Handle<FixedArray>::cast(maybe_capture_map);
1244 Handle<JSObject> groups = ConstructNamedCaptureGroupsObject(
1245 isolate, capture_map, [=](
int ix) {
return elements->get(ix); });
1246 elements->set(cursor++, *groups);
1249 DCHECK_EQ(cursor, argc);
1250 builder.Add(*isolate->factory()->NewJSArrayWithElements(elements));
1252 builder.Add(*match);
1257 if (global_cache.HasException())
return ReadOnlyRoots(isolate).exception();
1259 if (match_start >= 0) {
1261 if (match_end < subject_length) {
1262 ReplacementStringBuilder::AddSubjectSlice(&builder, match_end,
1266 RegExpImpl::SetLastMatchInfo(isolate, last_match_array, subject,
1268 global_cache.LastSuccessfulMatch());
1270 if (subject_length > kMinLengthToCache) {
1273 int capture_registers = (capture_count + 1) * 2;
1274 Handle<FixedArray> last_match_cache =
1275 isolate->factory()->NewFixedArray(capture_registers);
1276 int32_t* last_match = global_cache.LastSuccessfulMatch();
1277 for (
int i = 0;
i < capture_registers;
i++) {
1278 last_match_cache->set(
i, Smi::FromInt(last_match[
i]));
1280 Handle<FixedArray> result_fixed_array =
1281 FixedArray::ShrinkOrEmpty(isolate, builder.array(), builder.length());
1283 Handle<FixedArray> copied_fixed_array =
1284 isolate->factory()->CopyFixedArrayWithMap(
1285 result_fixed_array, isolate->factory()->fixed_array_map());
1286 RegExpResultsCache::Enter(
1287 isolate, subject, handle(regexp->data(), isolate), copied_fixed_array,
1288 last_match_cache, RegExpResultsCache::REGEXP_MULTIPLE_INDICES);
1290 return *builder.ToJSArray(result_array);
1292 return ReadOnlyRoots(isolate).null_value();
1298 V8_WARN_UNUSED_RESULT MaybeHandle<String> RegExpReplace(
1299 Isolate* isolate, Handle<JSRegExp> regexp, Handle<String>
string,
1300 Handle<Object> replace_obj) {
1302 DCHECK(RegExpUtils::IsUnmodifiedRegExp(isolate, regexp));
1303 DCHECK(!replace_obj->IsCallable());
1305 Factory* factory = isolate->factory();
1307 const int flags = regexp->GetFlags();
1308 const bool global = (flags & JSRegExp::kGlobal) != 0;
1309 const bool sticky = (flags & JSRegExp::kSticky) != 0;
1311 Handle<String> replace;
1312 ASSIGN_RETURN_ON_EXCEPTION(isolate, replace,
1313 Object::ToString(isolate, replace_obj), String);
1314 replace = String::Flatten(isolate, replace);
1316 Handle<RegExpMatchInfo> last_match_info = isolate->regexp_last_match_info();
1323 Handle<Object> last_index_obj(regexp->last_index(), isolate);
1324 ASSIGN_RETURN_ON_EXCEPTION(isolate, last_index_obj,
1325 Object::ToLength(isolate, last_index_obj),
1327 last_index = PositiveNumberToUint32(*last_index_obj);
1330 Handle<Object> match_indices_obj(ReadOnlyRoots(isolate).null_value(),
1335 if (last_index <= static_cast<uint32_t>(string->length())) {
1336 ASSIGN_RETURN_ON_EXCEPTION(isolate, match_indices_obj,
1337 RegExpImpl::Exec(isolate, regexp,
string,
1338 last_index, last_match_info),
1342 if (match_indices_obj->IsNull(isolate)) {
1343 if (sticky) regexp->set_last_index(Smi::kZero, SKIP_WRITE_BARRIER);
1347 auto match_indices = Handle<RegExpMatchInfo>::cast(match_indices_obj);
1349 const int start_index = match_indices->Capture(0);
1350 const int end_index = match_indices->Capture(1);
1353 regexp->set_last_index(Smi::FromInt(end_index), SKIP_WRITE_BARRIER);
1355 IncrementalStringBuilder builder(isolate);
1356 builder.AppendString(factory->NewSubString(
string, 0, start_index));
1358 if (replace->length() > 0) {
1359 MatchInfoBackedMatch m(isolate, regexp,
string, match_indices);
1360 Handle<String> replacement;
1361 ASSIGN_RETURN_ON_EXCEPTION(isolate, replacement,
1362 String::GetSubstitution(isolate, &m, replace),
1364 builder.AppendString(replacement);
1367 builder.AppendString(
1368 factory->NewSubString(
string, end_index, string->length()));
1369 return builder.Finish();
1373 RETURN_ON_EXCEPTION(isolate, RegExpUtils::SetLastIndex(isolate, regexp, 0),
1376 if (replace->length() == 0) {
1377 if (string->HasOnlyOneByteChars()) {
1379 StringReplaceGlobalRegExpWithEmptyString<SeqOneByteString>(
1380 isolate, string, regexp, last_match_info);
1381 return handle(String::cast(result), isolate);
1384 StringReplaceGlobalRegExpWithEmptyString<SeqTwoByteString>(
1385 isolate, string, regexp, last_match_info);
1386 return handle(String::cast(result), isolate);
1390 Object* result = StringReplaceGlobalRegExpWithString(
1391 isolate,
string, regexp, replace, last_match_info);
1392 if (result->IsString()) {
1393 return handle(String::cast(result), isolate);
1395 return MaybeHandle<String>();
1405 RUNTIME_FUNCTION(Runtime_RegExpExecMultiple) {
1406 HandleScope handles(isolate);
1407 DCHECK_EQ(4, args.length());
1409 CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 0);
1410 CONVERT_ARG_HANDLE_CHECKED(String, subject, 1);
1411 CONVERT_ARG_HANDLE_CHECKED(RegExpMatchInfo, last_match_info, 2);
1412 CONVERT_ARG_HANDLE_CHECKED(JSArray, result_array, 3);
1413 CHECK(result_array->HasObjectElements());
1415 subject = String::Flatten(isolate, subject);
1416 CHECK(regexp->GetFlags() & JSRegExp::kGlobal);
1418 if (regexp->CaptureCount() == 0) {
1419 return SearchRegExpMultiple<false>(isolate, subject, regexp,
1420 last_match_info, result_array);
1422 return SearchRegExpMultiple<true>(isolate, subject, regexp, last_match_info,
1427 RUNTIME_FUNCTION(Runtime_StringReplaceNonGlobalRegExpWithFunction) {
1428 HandleScope scope(isolate);
1429 DCHECK_EQ(3, args.length());
1430 CONVERT_ARG_HANDLE_CHECKED(String, subject, 0);
1431 CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 1);
1432 CONVERT_ARG_HANDLE_CHECKED(JSReceiver, replace_obj, 2);
1434 DCHECK(RegExpUtils::IsUnmodifiedRegExp(isolate, regexp));
1435 DCHECK(replace_obj->map()->is_callable());
1437 Factory* factory = isolate->factory();
1438 Handle<RegExpMatchInfo> last_match_info = isolate->regexp_last_match_info();
1440 const int flags = regexp->GetFlags();
1441 DCHECK_EQ(flags & JSRegExp::kGlobal, 0);
1445 const bool sticky = (flags & JSRegExp::kSticky) != 0;
1448 Handle<Object> last_index_obj(regexp->last_index(), isolate);
1449 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1450 isolate, last_index_obj, Object::ToLength(isolate, last_index_obj));
1451 last_index = PositiveNumberToUint32(*last_index_obj);
1453 if (last_index > static_cast<uint32_t>(subject->length())) last_index = 0;
1456 Handle<Object> match_indices_obj;
1457 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1458 isolate, match_indices_obj,
1459 RegExpImpl::Exec(isolate, regexp, subject, last_index, last_match_info));
1461 if (match_indices_obj->IsNull(isolate)) {
1462 if (sticky) regexp->set_last_index(Smi::kZero, SKIP_WRITE_BARRIER);
1466 Handle<RegExpMatchInfo> match_indices =
1467 Handle<RegExpMatchInfo>::cast(match_indices_obj);
1469 const int index = match_indices->Capture(0);
1470 const int end_of_match = match_indices->Capture(1);
1473 regexp->set_last_index(Smi::FromInt(end_of_match), SKIP_WRITE_BARRIER);
1475 IncrementalStringBuilder builder(isolate);
1476 builder.AppendString(factory->NewSubString(subject, 0, index));
1483 const int m = match_indices->NumberOfCaptureRegisters() / 2;
1485 bool has_named_captures =
false;
1486 Handle<FixedArray> capture_map;
1489 DCHECK_EQ(regexp->TypeTag(), JSRegExp::IRREGEXP);
1491 Object* maybe_capture_map = regexp->CaptureNameMap();
1492 if (maybe_capture_map->IsFixedArray()) {
1493 has_named_captures =
true;
1494 capture_map = handle(FixedArray::cast(maybe_capture_map), isolate);
1498 const uint32_t argc = GetArgcForReplaceCallable(m, has_named_captures);
1499 if (argc == static_cast<uint32_t>(-1)) {
1500 THROW_NEW_ERROR_RETURN_FAILURE(
1501 isolate, NewRangeError(MessageTemplate::kTooManyArguments));
1503 ScopedVector<Handle<Object>> argv(argc);
1506 for (
int j = 0; j < m; j++) {
1508 Handle<String> capture =
1509 RegExpUtils::GenericCaptureGetter(isolate, match_indices, j, &ok);
1511 argv[cursor++] = capture;
1513 argv[cursor++] = factory->undefined_value();
1517 argv[cursor++] = handle(Smi::FromInt(index), isolate);
1518 argv[cursor++] = subject;
1520 if (has_named_captures) {
1521 argv[cursor++] = ConstructNamedCaptureGroupsObject(
1522 isolate, capture_map, [&argv](
int ix) {
return *argv[ix]; });
1525 DCHECK_EQ(cursor, argc);
1527 Handle<Object> replacement_obj;
1528 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1529 isolate, replacement_obj,
1530 Execution::Call(isolate, replace_obj, factory->undefined_value(), argc,
1533 Handle<String> replacement;
1534 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1535 isolate, replacement, Object::ToString(isolate, replacement_obj));
1537 builder.AppendString(replacement);
1538 builder.AppendString(
1539 factory->NewSubString(subject, end_of_match, subject->length()));
1541 RETURN_RESULT_OR_FAILURE(isolate, builder.Finish());
1546 V8_WARN_UNUSED_RESULT MaybeHandle<Object> ToUint32(Isolate* isolate,
1547 Handle<Object>
object,
1549 if (object->IsUndefined(isolate)) {
1554 Handle<Object> number;
1555 ASSIGN_RETURN_ON_EXCEPTION(isolate, number, Object::ToNumber(isolate,
object),
1557 *out = NumberToUint32(*number);
1561 Handle<JSArray> NewJSArrayWithElements(Isolate* isolate,
1562 Handle<FixedArray> elems,
1564 return isolate->factory()->NewJSArrayWithElements(
1565 FixedArray::ShrinkOrEmpty(isolate, elems, num_elems));
1573 RUNTIME_FUNCTION(Runtime_RegExpSplit) {
1574 HandleScope scope(isolate);
1575 DCHECK_EQ(3, args.length());
1577 CONVERT_ARG_HANDLE_CHECKED(JSReceiver, recv, 0);
1578 CONVERT_ARG_HANDLE_CHECKED(String,
string, 1);
1579 CONVERT_ARG_HANDLE_CHECKED(Object, limit_obj, 2);
1581 Factory* factory = isolate->factory();
1583 Handle<JSFunction> regexp_fun = isolate->regexp_function();
1584 Handle<Object> ctor;
1585 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1586 isolate, ctor, Object::SpeciesConstructor(isolate, recv, regexp_fun));
1588 Handle<Object> flags_obj;
1589 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1591 JSObject::GetProperty(isolate, recv, factory->flags_string()));
1593 Handle<String> flags;
1594 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, flags,
1595 Object::ToString(isolate, flags_obj));
1597 Handle<String> u_str = factory->LookupSingleCharacterStringFromCode(
'u');
1598 const bool unicode = (String::IndexOf(isolate, flags, u_str, 0) >= 0);
1600 Handle<String> y_str = factory->LookupSingleCharacterStringFromCode(
'y');
1601 const bool sticky = (String::IndexOf(isolate, flags, y_str, 0) >= 0);
1603 Handle<String> new_flags = flags;
1605 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, new_flags,
1606 factory->NewConsString(flags, y_str));
1609 Handle<JSReceiver> splitter;
1613 ScopedVector<Handle<Object>> argv(argc);
1615 argv[1] = new_flags;
1617 Handle<Object> splitter_obj;
1618 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1619 isolate, splitter_obj,
1620 Execution::New(isolate, ctor, argc, argv.start()));
1622 splitter = Handle<JSReceiver>::cast(splitter_obj);
1626 RETURN_FAILURE_ON_EXCEPTION(isolate, ToUint32(isolate, limit_obj, &limit));
1628 const uint32_t length =
string->length();
1630 if (limit == 0)
return *factory->NewJSArray(0);
1633 Handle<Object> result;
1634 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1635 isolate, result, RegExpUtils::RegExpExec(isolate, splitter,
string,
1636 factory->undefined_value()));
1638 if (!result->IsNull(isolate))
return *factory->NewJSArray(0);
1640 Handle<FixedArray> elems = factory->NewUninitializedFixedArray(1);
1641 elems->set(0, *
string);
1642 return *factory->NewJSArrayWithElements(elems);
1645 static const int kInitialArraySize = 8;
1646 Handle<FixedArray> elems = factory->NewFixedArrayWithHoles(kInitialArraySize);
1651 while (string_index < length) {
1652 RETURN_FAILURE_ON_EXCEPTION(
1653 isolate, RegExpUtils::SetLastIndex(isolate, splitter, string_index));
1655 Handle<Object> result;
1656 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1657 isolate, result, RegExpUtils::RegExpExec(isolate, splitter,
string,
1658 factory->undefined_value()));
1660 if (result->IsNull(isolate)) {
1661 string_index =
static_cast<uint32_t>(
1662 RegExpUtils::AdvanceStringIndex(
string, string_index, unicode));
1666 Handle<Object> last_index_obj;
1667 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1668 isolate, last_index_obj, RegExpUtils::GetLastIndex(isolate, splitter));
1670 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1671 isolate, last_index_obj, Object::ToLength(isolate, last_index_obj));
1674 std::min(PositiveNumberToUint32(*last_index_obj), length);
1675 if (end == prev_string_index) {
1676 string_index =
static_cast<uint32_t>(
1677 RegExpUtils::AdvanceStringIndex(
string, string_index, unicode));
1682 Handle<String> substr =
1683 factory->NewSubString(
string, prev_string_index, string_index);
1684 elems = FixedArray::SetAndGrow(isolate, elems, num_elems++, substr);
1685 if (num_elems == limit) {
1686 return *NewJSArrayWithElements(isolate, elems, num_elems);
1690 prev_string_index = end;
1692 Handle<Object> num_captures_obj;
1693 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1694 isolate, num_captures_obj,
1695 Object::GetProperty(isolate, result,
1696 isolate->factory()->length_string()));
1698 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1699 isolate, num_captures_obj, Object::ToLength(isolate, num_captures_obj));
1700 const uint32_t num_captures = PositiveNumberToUint32(*num_captures_obj);
1703 Handle<Object> capture;
1704 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1705 isolate, capture, Object::GetElement(isolate, result,
i));
1706 elems = FixedArray::SetAndGrow(isolate, elems, num_elems++, capture);
1707 if (num_elems == limit) {
1708 return *NewJSArrayWithElements(isolate, elems, num_elems);
1712 string_index = prev_string_index;
1716 Handle<String> substr =
1717 factory->NewSubString(
string, prev_string_index, length);
1718 elems = FixedArray::SetAndGrow(isolate, elems, num_elems++, substr);
1721 return *NewJSArrayWithElements(isolate, elems, num_elems);
1727 RUNTIME_FUNCTION(Runtime_RegExpReplace) {
1728 HandleScope scope(isolate);
1729 DCHECK_EQ(3, args.length());
1731 CONVERT_ARG_HANDLE_CHECKED(JSReceiver, recv, 0);
1732 CONVERT_ARG_HANDLE_CHECKED(String,
string, 1);
1733 Handle<Object> replace_obj = args.at(2);
1735 Factory* factory = isolate->factory();
1737 string = String::Flatten(isolate,
string);
1739 const bool functional_replace = replace_obj->IsCallable();
1742 if (RegExpUtils::IsUnmodifiedRegExp(isolate, recv)) {
1745 CHECK(!functional_replace);
1746 RETURN_RESULT_OR_FAILURE(
1747 isolate, RegExpReplace(isolate, Handle<JSRegExp>::cast(recv),
string,
1751 const uint32_t length =
string->length();
1753 Handle<String> replace;
1754 if (!functional_replace) {
1755 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, replace,
1756 Object::ToString(isolate, replace_obj));
1759 Handle<Object> global_obj;
1760 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1761 isolate, global_obj,
1762 JSReceiver::GetProperty(isolate, recv, factory->global_string()));
1763 const bool global = global_obj->BooleanValue(isolate);
1765 bool unicode =
false;
1767 Handle<Object> unicode_obj;
1768 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1769 isolate, unicode_obj,
1770 JSReceiver::GetProperty(isolate, recv, factory->unicode_string()));
1771 unicode = unicode_obj->BooleanValue(isolate);
1773 RETURN_FAILURE_ON_EXCEPTION(isolate,
1774 RegExpUtils::SetLastIndex(isolate, recv, 0));
1777 Zone zone(isolate->allocator(), ZONE_NAME);
1778 ZoneVector<Handle<Object>> results(&zone);
1781 Handle<Object> result;
1782 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1783 isolate, result, RegExpUtils::RegExpExec(isolate, recv,
string,
1784 factory->undefined_value()));
1786 if (result->IsNull(isolate))
break;
1788 results.push_back(result);
1791 Handle<Object> match_obj;
1792 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, match_obj,
1793 Object::GetElement(isolate, result, 0));
1795 Handle<String> match;
1796 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, match,
1797 Object::ToString(isolate, match_obj));
1799 if (match->length() == 0) {
1800 RETURN_FAILURE_ON_EXCEPTION(isolate, RegExpUtils::SetAdvancedStringIndex(
1801 isolate, recv,
string, unicode));
1806 IncrementalStringBuilder builder(isolate);
1809 for (
const auto& result : results) {
1810 HandleScope handle_scope(isolate);
1811 Handle<Object> captures_length_obj;
1812 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1813 isolate, captures_length_obj,
1814 Object::GetProperty(isolate, result, factory->length_string()));
1816 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1817 isolate, captures_length_obj,
1818 Object::ToLength(isolate, captures_length_obj));
1820 PositiveNumberToUint32(*captures_length_obj);
1822 Handle<Object> match_obj;
1823 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, match_obj,
1824 Object::GetElement(isolate, result, 0));
1826 Handle<String> match;
1827 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, match,
1828 Object::ToString(isolate, match_obj));
1830 const int match_length = match->length();
1832 Handle<Object> position_obj;
1833 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1834 isolate, position_obj,
1835 Object::GetProperty(isolate, result, factory->index_string()));
1837 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1838 isolate, position_obj, Object::ToInteger(isolate, position_obj));
1840 std::min(PositiveNumberToUint32(*position_obj), length);
1843 ZoneVector<Handle<Object>> captures(&zone);
1845 for (
uint32_t n = 0; n < captures_length; n++) {
1846 Handle<Object> capture;
1847 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1848 isolate, capture, Object::GetElement(isolate, result, n));
1850 if (!capture->IsUndefined(isolate)) {
1851 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, capture,
1852 Object::ToString(isolate, capture));
1854 captures.push_back(capture);
1857 Handle<Object> groups_obj = isolate->factory()->undefined_value();
1858 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1859 isolate, groups_obj,
1860 Object::GetProperty(isolate, result, factory->groups_string()));
1862 const bool has_named_captures = !groups_obj->IsUndefined(isolate);
1864 Handle<String> replacement;
1865 if (functional_replace) {
1867 GetArgcForReplaceCallable(captures_length, has_named_captures);
1868 if (argc == static_cast<uint32_t>(-1)) {
1869 THROW_NEW_ERROR_RETURN_FAILURE(
1870 isolate, NewRangeError(MessageTemplate::kTooManyArguments));
1873 ScopedVector<Handle<Object>> argv(argc);
1876 for (
uint32_t j = 0; j < captures_length; j++) {
1877 argv[cursor++] = captures[j];
1880 argv[cursor++] = handle(Smi::FromInt(position), isolate);
1881 argv[cursor++] = string;
1882 if (has_named_captures) argv[cursor++] = groups_obj;
1884 DCHECK_EQ(cursor, argc);
1886 Handle<Object> replacement_obj;
1887 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1888 isolate, replacement_obj,
1889 Execution::Call(isolate, replace_obj, factory->undefined_value(),
1890 argc, argv.start()));
1892 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1893 isolate, replacement, Object::ToString(isolate, replacement_obj));
1895 DCHECK(!functional_replace);
1896 if (!groups_obj->IsUndefined(isolate)) {
1897 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1898 isolate, groups_obj, JSReceiver::ToObject(isolate, groups_obj));
1900 VectorBackedMatch m(isolate,
string, match, position, &captures,
1902 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1903 isolate, replacement, String::GetSubstitution(isolate, &m, replace));
1906 if (position >= next_source_position) {
1907 builder.AppendString(
1908 factory->NewSubString(
string, next_source_position, position));
1909 builder.AppendString(replacement);
1911 next_source_position = position + match_length;
1915 if (next_source_position < length) {
1916 builder.AppendString(
1917 factory->NewSubString(
string, next_source_position, length));
1920 RETURN_RESULT_OR_FAILURE(isolate, builder.Finish());
1923 RUNTIME_FUNCTION(Runtime_RegExpInitializeAndCompile) {
1924 HandleScope scope(isolate);
1925 DCHECK_EQ(3, args.length());
1929 CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 0);
1930 CONVERT_ARG_HANDLE_CHECKED(String, source, 1);
1931 CONVERT_ARG_HANDLE_CHECKED(String, flags, 2);
1933 RETURN_FAILURE_ON_EXCEPTION(isolate,
1934 JSRegExp::Initialize(regexp, source, flags));
1939 RUNTIME_FUNCTION(Runtime_IsRegExp) {
1940 SealHandleScope shs(isolate);
1941 DCHECK_EQ(1, args.length());
1942 CONVERT_ARG_CHECKED(Object, obj, 0);
1943 return isolate->heap()->ToBoolean(obj->IsJSRegExp());