Index: src/runtime.cc |
diff --git a/src/runtime.cc b/src/runtime.cc |
index a3eb09ffa8c564c13bb5e6dcf1eba24a6b8805ca..e3a150d907419a10273d1cd8923d6566e1973811 100644 |
--- a/src/runtime.cc |
+++ b/src/runtime.cc |
@@ -2284,6 +2284,134 @@ static Object* StringReplaceRegExpWithString(String* subject, |
return *(builder.ToString()); |
} |
+template <typename ResultSeqString> |
+static Object* StringReplaceRegExpWithEmptyString(ResultSeqString* subject, |
+ JSRegExp* regexp, |
+ JSArray* last_match_info) { |
+ ASSERT(subject->IsFlat()); |
+ |
+ HandleScope handles; |
+ |
+ Handle<String> subject_handle(subject); |
+ Handle<JSRegExp> regexp_handle(regexp); |
+ Handle<JSArray> last_match_info_handle(last_match_info); |
+ Handle<Object> match = RegExpImpl::Exec(regexp_handle, |
+ subject_handle, |
+ 0, |
+ last_match_info_handle); |
+ if (match.is_null()) return Failure::Exception(); |
+ if (match->IsNull()) return *subject_handle; |
+ |
+ ASSERT(last_match_info_handle->HasFastElements()); |
+ |
+ HandleScope loop_scope; |
+ int start, end; |
+ { |
+ AssertNoAllocation match_info_array_is_not_in_a_handle; |
+ FixedArray* match_info_array = |
+ FixedArray::cast(last_match_info_handle->elements()); |
+ |
+ start = RegExpImpl::GetCapture(match_info_array, 0); |
+ end = RegExpImpl::GetCapture(match_info_array, 1); |
+ } |
+ |
+ int length = subject->length(); |
+ int new_length = length - (end - start); |
+ if (new_length == 0) { |
+ return Heap::empty_string(); |
+ } |
+ // TODO(sandholm) try to use types statically to determine this. |
+ Handle<ResultSeqString> answer; |
+ if (subject_handle->IsAsciiRepresentation()) { |
+ answer = |
+ Handle<ResultSeqString>::cast(Factory::NewRawAsciiString(new_length)); |
+ } else { |
+ answer = |
+ Handle<ResultSeqString>::cast(Factory::NewRawTwoByteString(new_length)); |
+ } |
+ |
+ // If the regexp isn't global, only match once. |
+ if (!regexp_handle->GetFlags().is_global()) { |
+ if (start > 0) { |
+ String::WriteToFlat(*subject_handle, |
+ answer->GetChars(), |
+ 0, |
+ start); |
+ } |
+ if (end < length) { |
+ String::WriteToFlat(*subject_handle, |
+ answer->GetChars() + start, |
+ end, |
+ length); |
+ } |
+ return *answer; |
+ } |
+ |
+ int prev = 0; // Index of end of last match. |
+ int next = 0; // Start of next search (prev unless last match was empty). |
+ int position = 0; |
+ |
+ do { |
+ if (prev < start) { |
+ // Add substring subject[prev;start] to answer string. |
+ String::WriteToFlat(*subject_handle, |
+ answer->GetChars() + position, |
+ prev, |
+ start); |
+ position += start - prev; |
+ } |
+ prev = end; |
+ next = end; |
+ // Continue from where the match ended, unless it was an empty match. |
+ if (start == end) { |
+ next++; |
+ if (next > length) break; |
+ } |
+ match = RegExpImpl::Exec(regexp_handle, |
+ subject_handle, |
+ next, |
+ last_match_info_handle); |
+ if (match.is_null()) return Failure::Exception(); |
+ if (match->IsNull()) break; |
+ |
+ ASSERT(last_match_info_handle->HasFastElements()); |
+ HandleScope loop_scope; |
+ { |
+ AssertNoAllocation match_info_array_is_not_in_a_handle; |
+ FixedArray* match_info_array = |
+ FixedArray::cast(last_match_info_handle->elements()); |
+ start = RegExpImpl::GetCapture(match_info_array, 0); |
+ end = RegExpImpl::GetCapture(match_info_array, 1); |
+ } |
+ } while (true); |
+ |
+ if (prev < length) { |
+ // Add substring subject[prev;length] to answer string. |
+ String::WriteToFlat(*subject_handle, |
+ answer->GetChars() + position, |
+ prev, |
+ length); |
+ position += length - prev; |
+ } |
+ |
+ if (position == 0) { |
+ return Heap::empty_string(); |
+ } |
+ |
+ // Shorten string and fill |
+ int string_size = ResultSeqString::SizeFor(position); |
+ int allocated_string_size = ResultSeqString::SizeFor(new_length); |
+ int delta = allocated_string_size - string_size; |
+ |
+ answer->set_length(position); |
+ if (delta == 0) return *answer; |
+ |
+ Address end_of_string = answer->address() + string_size; |
+ Heap::CreateFillerObjectAt(end_of_string, delta); |
+ |
+ return *answer; |
+} |
+ |
static Object* Runtime_StringReplaceRegExpWithString(Arguments args) { |
ASSERT(args.length() == 4); |
@@ -2311,6 +2439,18 @@ static Object* Runtime_StringReplaceRegExpWithString(Arguments args) { |
ASSERT(last_match_info->HasFastElements()); |
+ if (replacement->length() == 0) { |
+ if (subject->IsAsciiRepresentation()) { |
+ return StringReplaceRegExpWithEmptyString(SeqAsciiString::cast(subject), |
+ regexp, |
+ last_match_info); |
+ } else { |
+ return StringReplaceRegExpWithEmptyString(SeqTwoByteString::cast(subject), |
+ regexp, |
+ last_match_info); |
+ } |
+ } |
+ |
return StringReplaceRegExpWithString(subject, |
regexp, |
replacement, |