Index: src/runtime/runtime-regexp.cc |
diff --git a/src/runtime/runtime-regexp.cc b/src/runtime/runtime-regexp.cc |
index 97f46736f3b9b60ade191968f2e1f33c6134c61e..7a89448e11db71ec4ffb57a61b79aeeee48ca419 100644 |
--- a/src/runtime/runtime-regexp.cc |
+++ b/src/runtime/runtime-regexp.cc |
@@ -973,13 +973,13 @@ class VectorBackedMatch : public String::Match { |
ZoneVector<Handle<Object>>* captures_; |
}; |
-// Only called from RegExpExecMultiple so it doesn't need to maintain |
+// Only called from Runtime_RegExpExecMultiple so it doesn't need to maintain |
// separate last match info. See comment on that function. |
template <bool has_capture> |
-MaybeHandle<Object> SearchRegExpMultiple( |
- Isolate* isolate, Handle<String> subject, Handle<JSRegExp> regexp, |
- Handle<RegExpMatchInfo> last_match_array, |
- Handle<FixedArray> result_elements) { |
+static Object* SearchRegExpMultiple(Isolate* isolate, Handle<String> subject, |
+ Handle<JSRegExp> regexp, |
+ Handle<RegExpMatchInfo> last_match_array, |
+ Handle<JSArray> result_array) { |
DCHECK(subject->IsFlat()); |
DCHECK_NE(has_capture, regexp->CaptureCount() == 0); |
@@ -999,20 +999,27 @@ MaybeHandle<Object> SearchRegExpMultiple( |
for (int i = 0; i < capture_registers; i++) { |
last_match[i] = Smi::cast(last_match_cache->get(i))->value(); |
} |
- Handle<FixedArray> cached_fixed_array(FixedArray::cast(cached_answer)); |
+ Handle<FixedArray> cached_fixed_array = |
+ Handle<FixedArray>(FixedArray::cast(cached_answer)); |
+ // The cache FixedArray is a COW-array and we need to return a copy. |
+ Handle<FixedArray> copied_fixed_array = |
+ isolate->factory()->CopyFixedArrayWithMap( |
+ cached_fixed_array, isolate->factory()->fixed_array_map()); |
+ JSArray::SetContent(result_array, copied_fixed_array); |
RegExpImpl::SetLastMatchInfo(last_match_array, subject, capture_count, |
last_match); |
DeleteArray(last_match); |
- // The cache FixedArray is a COW-array and we need to return a copy. |
- return isolate->factory()->CopyFixedArrayWithMap( |
- cached_fixed_array, isolate->factory()->fixed_array_map()); |
+ return *result_array; |
} |
} |
RegExpImpl::GlobalCache global_cache(regexp, subject, isolate); |
- if (global_cache.HasException()) return MaybeHandle<Object>(); |
+ if (global_cache.HasException()) return isolate->heap()->exception(); |
// Ensured in Runtime_RegExpExecMultiple. |
+ DCHECK(result_array->HasFastObjectElements()); |
+ Handle<FixedArray> result_elements( |
+ FixedArray::cast(result_array->elements())); |
if (result_elements->length() < 16) { |
result_elements = isolate->factory()->NewFixedArrayWithHoles(16); |
} |
@@ -1079,7 +1086,7 @@ MaybeHandle<Object> SearchRegExpMultiple( |
} |
} |
- if (global_cache.HasException()) return MaybeHandle<Object>(); |
+ if (global_cache.HasException()) return isolate->heap()->exception(); |
if (match_start >= 0) { |
// Finished matching, with at least one match. |
@@ -1091,9 +1098,6 @@ MaybeHandle<Object> SearchRegExpMultiple( |
RegExpImpl::SetLastMatchInfo(last_match_array, subject, capture_count, |
global_cache.LastSuccessfulMatch()); |
- Handle<FixedArray> result_fixed_array = builder.array(); |
- result_fixed_array->Shrink(builder.length()); |
- |
if (subject_length > kMinLengthToCache) { |
// Store the last successful match into the array for caching. |
// TODO(yangguo): do not expose last match to JS and simplify caching. |
@@ -1104,194 +1108,22 @@ MaybeHandle<Object> SearchRegExpMultiple( |
for (int i = 0; i < capture_registers; i++) { |
last_match_cache->set(i, Smi::FromInt(last_match[i])); |
} |
- // Cache the result and turn the FixedArray into a COW array. |
+ Handle<FixedArray> result_fixed_array = builder.array(); |
+ result_fixed_array->Shrink(builder.length()); |
+ // Cache the result and copy the FixedArray into a COW array. |
+ Handle<FixedArray> copied_fixed_array = |
+ isolate->factory()->CopyFixedArrayWithMap( |
+ result_fixed_array, isolate->factory()->fixed_array_map()); |
RegExpResultsCache::Enter( |
- isolate, subject, handle(regexp->data(), isolate), result_fixed_array, |
+ isolate, subject, handle(regexp->data(), isolate), copied_fixed_array, |
last_match_cache, RegExpResultsCache::REGEXP_MULTIPLE_INDICES); |
} |
- // The cache FixedArray is a COW-array and we need to return a copy. |
- return isolate->factory()->CopyFixedArrayWithMap( |
- result_fixed_array, isolate->factory()->fixed_array_map()); |
- } else { |
- return isolate->factory()->null_value(); // No matches at all. |
- } |
-} |
- |
-// This is only called for StringReplaceGlobalRegExpWithFunction. This sets |
-// lastMatchInfoOverride to maintain the last match info, so we don't need to |
-// set any other last match array info. |
-MaybeHandle<Object> RegExpExecMultiple(Isolate* isolate, |
- Handle<JSRegExp> regexp, |
- Handle<String> subject, |
- Handle<RegExpMatchInfo> last_match_info, |
- Handle<FixedArray> result_array) { |
- subject = String::Flatten(subject); |
- CHECK(regexp->GetFlags() & JSRegExp::kGlobal); |
- |
- if (regexp->CaptureCount() == 0) { |
- return SearchRegExpMultiple<false>(isolate, subject, regexp, |
- last_match_info, result_array); |
+ return *builder.ToJSArray(result_array); |
} else { |
- return SearchRegExpMultiple<true>(isolate, subject, regexp, last_match_info, |
- result_array); |
+ return isolate->heap()->null_value(); // No matches at all. |
} |
} |
-// Helper function for replacing regular expressions with the result of a |
-// function application in String.prototype.replace. |
-MaybeHandle<String> StringReplaceGlobalRegExpWithFunction( |
- Isolate* isolate, Handle<String> subject, Handle<JSRegExp> regexp, |
- Handle<Object> replace_obj) { |
- Factory* factory = isolate->factory(); |
- |
- // TODO(jgruber): Convert result_array into a List<Handle<Object>> (or |
- // similar) and adapt / remove FixedArrayBuilder. |
- Handle<RegExpMatchInfo> last_match_info = isolate->regexp_last_match_info(); |
- Handle<FixedArray> result_array = factory->NewFixedArrayWithHoles(16); |
- |
- Handle<Object> res; |
- ASSIGN_RETURN_ON_EXCEPTION(isolate, res, |
- RegExpExecMultiple(isolate, regexp, subject, |
- last_match_info, result_array), |
- String); |
- |
- // Reload the last match info since it might have changed in the meantime. |
- last_match_info = isolate->regexp_last_match_info(); |
- |
- if (res->IsNull(isolate)) return subject; // No matches at all. |
- |
- result_array = Handle<FixedArray>::cast(res); |
- const int result_length = result_array->length(); |
- |
- const int num_captures = last_match_info->NumberOfCaptureRegisters() / 2; |
- if (num_captures == 1) { |
- // If the number of captures is one then there are no explicit captures in |
- // the regexp, just the implicit capture that captures the whole match. In |
- // this case we can simplify quite a bit and end up with something faster. |
- // The builder will consist of some integers that indicate slices of the |
- // input string and some replacements that were returned from the replace |
- // function. |
- int match_start = 0; |
- for (int i = 0; i < result_length; i++) { |
- Handle<Object> elem = FixedArray::get(*result_array, i, isolate); |
- if (elem->IsSmi()) { |
- // Integers represent slices of the original string. |
- // TODO(jgruber): Maybe we don't need this weird encoding anymore (in |
- // preparation to invoking StringBuilderConcat), but can just copy into |
- // the result string with the IncrementalStringBuilder as we go? |
- const int elem_value = Handle<Smi>::cast(elem)->value(); |
- if (elem_value > 0) { |
- match_start = (elem_value >> 11) + (elem_value & 0x7ff); |
- } else { |
- Handle<Object> next_elem = |
- FixedArray::get(*result_array, ++i, isolate); |
- const int next_elem_value = Handle<Smi>::cast(next_elem)->value(); |
- match_start = next_elem_value - elem_value; |
- } |
- } else { |
- DCHECK(elem->IsString()); |
- Handle<String> elem_string = Handle<String>::cast(elem); |
- |
- // Overwrite the i'th element in the results with the string we got |
- // back from the callback function. |
- const int argc = 3; |
- ScopedVector<Handle<Object>> argv(argc); |
- |
- argv[0] = elem_string; |
- argv[1] = handle(Smi::FromInt(match_start), isolate); |
- argv[2] = subject; |
- |
- Handle<Object> replacement_obj; |
- ASSIGN_RETURN_ON_EXCEPTION( |
- isolate, replacement_obj, |
- Execution::Call(isolate, replace_obj, factory->undefined_value(), |
- argc, argv.start()), |
- String); |
- |
- Handle<String> replacement; |
- ASSIGN_RETURN_ON_EXCEPTION(isolate, replacement, |
- Object::ToString(isolate, replacement_obj), |
- String); |
- |
- result_array->set(i, *replacement); |
- match_start += elem_string->length(); |
- } |
- } |
- } else { |
- DCHECK(num_captures > 1); |
- for (int i = 0; i < result_length; i++) { |
- Handle<Object> elem = FixedArray::get(*result_array, i, isolate); |
- if (elem->IsSmi()) continue; |
- |
- // TODO(jgruber): We can skip this whole round-trip through a JS array |
- // for result_array. |
- Handle<JSArray> elem_array = Handle<JSArray>::cast(elem); |
- Handle<FixedArray> elem_array_elems( |
- FixedArray::cast(elem_array->elements()), isolate); |
- |
- const int argc = elem_array_elems->length(); |
- ScopedVector<Handle<Object>> argv(argc); |
- |
- for (int j = 0; j < argc; j++) { |
- argv[j] = FixedArray::get(*elem_array_elems, j, isolate); |
- } |
- |
- // TODO(jgruber): This call is another pattern we could refactor. |
- Handle<Object> replacement_obj; |
- ASSIGN_RETURN_ON_EXCEPTION( |
- isolate, replacement_obj, |
- Execution::Call(isolate, replace_obj, factory->undefined_value(), |
- argc, argv.start()), |
- String); |
- |
- Handle<String> replacement; |
- ASSIGN_RETURN_ON_EXCEPTION(isolate, replacement, |
- Object::ToString(isolate, replacement_obj), |
- String); |
- |
- result_array->set(i, *replacement); |
- } |
- } |
- |
- if (result_length == 0) { |
- return factory->empty_string(); |
- } else if (result_length == 1) { |
- Handle<Object> first = FixedArray::get(*result_array, 0, isolate); |
- if (first->IsString()) return Handle<String>::cast(first); |
- } |
- |
- bool one_byte = subject->HasOnlyOneByteChars(); |
- const int length = StringBuilderConcatLength(subject->length(), *result_array, |
- result_length, &one_byte); |
- |
- if (length == -1) { |
- isolate->Throw(isolate->heap()->illegal_argument_string()); |
- return MaybeHandle<String>(); |
- } |
- |
- if (one_byte) { |
- Handle<SeqOneByteString> answer; |
- ASSIGN_RETURN_ON_EXCEPTION(isolate, answer, |
- isolate->factory()->NewRawOneByteString(length), |
- String); |
- StringBuilderConcatHelper(*subject, answer->GetChars(), *result_array, |
- result_length); |
- return answer; |
- } else { |
- DCHECK(!one_byte); |
- Handle<SeqTwoByteString> answer; |
- ASSIGN_RETURN_ON_EXCEPTION(isolate, answer, |
- isolate->factory()->NewRawTwoByteString(length), |
- String); |
- StringBuilderConcatHelper(*subject, answer->GetChars(), *result_array, |
- result_length); |
- return answer; |
- } |
- |
- UNREACHABLE(); |
- return MaybeHandle<String>(); |
-} |
- |
MaybeHandle<String> StringReplaceNonGlobalRegExpWithFunction( |
Isolate* isolate, Handle<String> subject, Handle<JSRegExp> regexp, |
Handle<Object> replace_obj) { |
@@ -1371,87 +1203,76 @@ MaybeHandle<String> RegExpReplace(Isolate* isolate, Handle<JSRegExp> regexp, |
const int flags = regexp->GetFlags(); |
const bool global = (flags & JSRegExp::kGlobal) != 0; |
- const bool functional_replace = replace_obj->IsCallable(); |
- if (!functional_replace) { |
- Handle<String> replace; |
- ASSIGN_RETURN_ON_EXCEPTION(isolate, replace, |
- Object::ToString(isolate, replace_obj), String); |
- replace = String::Flatten(replace); |
+ // Functional fast-paths are dispatched directly by replace builtin. |
+ DCHECK(!replace_obj->IsCallable()); |
- Handle<RegExpMatchInfo> last_match_info = isolate->regexp_last_match_info(); |
+ Handle<String> replace; |
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, replace, |
+ Object::ToString(isolate, replace_obj), String); |
+ replace = String::Flatten(replace); |
- if (!global) { |
- // Non-global regexp search, string replace. |
+ Handle<RegExpMatchInfo> last_match_info = isolate->regexp_last_match_info(); |
- Handle<Object> match_indices_obj; |
- ASSIGN_RETURN_ON_EXCEPTION( |
- isolate, match_indices_obj, |
- RegExpImpl::Exec(regexp, string, 0, last_match_info), String); |
+ if (!global) { |
+ // Non-global regexp search, string replace. |
- if (match_indices_obj->IsNull(isolate)) { |
- RETURN_ON_EXCEPTION( |
- isolate, RegExpUtils::SetLastIndex(isolate, regexp, 0), String); |
- return string; |
- } |
+ Handle<Object> match_indices_obj; |
+ ASSIGN_RETURN_ON_EXCEPTION( |
+ isolate, match_indices_obj, |
+ RegExpImpl::Exec(regexp, string, 0, last_match_info), String); |
- auto match_indices = Handle<RegExpMatchInfo>::cast(match_indices_obj); |
+ if (match_indices_obj->IsNull(isolate)) { |
+ RETURN_ON_EXCEPTION( |
+ isolate, RegExpUtils::SetLastIndex(isolate, regexp, 0), String); |
+ return string; |
+ } |
- const int start_index = match_indices->Capture(0); |
- const int end_index = match_indices->Capture(1); |
+ auto match_indices = Handle<RegExpMatchInfo>::cast(match_indices_obj); |
- IncrementalStringBuilder builder(isolate); |
- builder.AppendString(factory->NewSubString(string, 0, start_index)); |
+ const int start_index = match_indices->Capture(0); |
+ const int end_index = match_indices->Capture(1); |
- if (replace->length() > 0) { |
- MatchInfoBackedMatch m(isolate, string, match_indices); |
- Handle<String> replacement; |
- ASSIGN_RETURN_ON_EXCEPTION( |
- isolate, replacement, String::GetSubstitution(isolate, &m, replace), |
- String); |
- builder.AppendString(replacement); |
- } |
+ IncrementalStringBuilder builder(isolate); |
+ builder.AppendString(factory->NewSubString(string, 0, start_index)); |
- builder.AppendString( |
- factory->NewSubString(string, end_index, string->length())); |
- return builder.Finish(); |
- } else { |
- // Global regexp search, string replace. |
- DCHECK(global); |
- RETURN_ON_EXCEPTION( |
- isolate, RegExpUtils::SetLastIndex(isolate, regexp, 0), String); |
+ if (replace->length() > 0) { |
+ MatchInfoBackedMatch m(isolate, string, match_indices); |
+ Handle<String> replacement; |
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, replacement, |
+ String::GetSubstitution(isolate, &m, replace), |
+ String); |
+ builder.AppendString(replacement); |
+ } |
- if (replace->length() == 0) { |
- if (string->HasOnlyOneByteChars()) { |
- Object* result = |
- StringReplaceGlobalRegExpWithEmptyString<SeqOneByteString>( |
- isolate, string, regexp, last_match_info); |
- return handle(String::cast(result), isolate); |
- } else { |
- Object* result = |
- StringReplaceGlobalRegExpWithEmptyString<SeqTwoByteString>( |
- isolate, string, regexp, last_match_info); |
- return handle(String::cast(result), isolate); |
- } |
- } |
+ builder.AppendString( |
+ factory->NewSubString(string, end_index, string->length())); |
+ return builder.Finish(); |
+ } else { |
+ // Global regexp search, string replace. |
+ DCHECK(global); |
+ RETURN_ON_EXCEPTION(isolate, RegExpUtils::SetLastIndex(isolate, regexp, 0), |
+ String); |
- Object* result = StringReplaceGlobalRegExpWithString( |
- isolate, string, regexp, replace, last_match_info); |
- if (result->IsString()) { |
+ if (replace->length() == 0) { |
+ if (string->HasOnlyOneByteChars()) { |
+ Object* result = |
+ StringReplaceGlobalRegExpWithEmptyString<SeqOneByteString>( |
+ isolate, string, regexp, last_match_info); |
return handle(String::cast(result), isolate); |
} else { |
- return MaybeHandle<String>(); |
+ Object* result = |
+ StringReplaceGlobalRegExpWithEmptyString<SeqTwoByteString>( |
+ isolate, string, regexp, last_match_info); |
+ return handle(String::cast(result), isolate); |
} |
} |
- } else { |
- DCHECK(functional_replace); |
- if (global) { |
- // Global regexp search, function replace. |
- return StringReplaceGlobalRegExpWithFunction(isolate, string, regexp, |
- replace_obj); |
+ |
+ Object* result = StringReplaceGlobalRegExpWithString( |
+ isolate, string, regexp, replace, last_match_info); |
+ if (result->IsString()) { |
+ return handle(String::cast(result), isolate); |
} else { |
- // Non-global regexp search, function replace. |
- return StringReplaceNonGlobalRegExpWithFunction(isolate, string, regexp, |
- replace_obj); |
+ return MaybeHandle<String>(); |
} |
} |
@@ -1461,16 +1282,27 @@ MaybeHandle<String> RegExpReplace(Isolate* isolate, Handle<JSRegExp> regexp, |
} // namespace |
-RUNTIME_FUNCTION(Runtime_StringReplaceGlobalRegExpWithFunction) { |
- HandleScope scope(isolate); |
- DCHECK(args.length() == 3); |
+// This is only called for StringReplaceGlobalRegExpWithFunction. |
+RUNTIME_FUNCTION(Runtime_RegExpExecMultiple) { |
+ HandleScope handles(isolate); |
+ DCHECK(args.length() == 4); |
- CONVERT_ARG_HANDLE_CHECKED(String, subject, 0); |
- CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 1); |
- CONVERT_ARG_HANDLE_CHECKED(JSObject, replace, 2); |
+ CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 0); |
+ CONVERT_ARG_HANDLE_CHECKED(String, subject, 1); |
+ CONVERT_ARG_HANDLE_CHECKED(RegExpMatchInfo, last_match_info, 2); |
+ CONVERT_ARG_HANDLE_CHECKED(JSArray, result_array, 3); |
+ CHECK(result_array->HasFastObjectElements()); |
- RETURN_RESULT_OR_FAILURE(isolate, StringReplaceGlobalRegExpWithFunction( |
- isolate, subject, regexp, replace)); |
+ subject = String::Flatten(subject); |
+ CHECK(regexp->GetFlags() & JSRegExp::kGlobal); |
+ |
+ if (regexp->CaptureCount() == 0) { |
+ return SearchRegExpMultiple<false>(isolate, subject, regexp, |
+ last_match_info, result_array); |
+ } else { |
+ return SearchRegExpMultiple<true>(isolate, subject, regexp, last_match_info, |
+ result_array); |
+ } |
} |
RUNTIME_FUNCTION(Runtime_StringReplaceNonGlobalRegExpWithFunction) { |