Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(959)

Unified Diff: src/runtime/runtime-regexp.cc

Issue 2433923003: [regexp] Add fast-path for global, callable replace (Closed)
Patch Set: Rebase Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/runtime/runtime.h ('k') | src/string-builder.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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) {
« no previous file with comments | « src/runtime/runtime.h ('k') | src/string-builder.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698