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

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

Issue 2433923003: [regexp] Add fast-path for global, callable replace (Closed)
Patch Set: Re-add asserts and COW array usage 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 94a4936b9f1925de03d33c53c58f9b688ac5e6c7..48d946f095f55e74554bb770c23327e9c2c8a2b5 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.
jgruber 2016/10/19 16:39:13 It might not make sense anymore for the result cac
Yang 2016/10/20 09:53:14 I'd simply introduce a version of SBC that only ta
jgruber 2016/10/20 10:58:30 That would work, I'm just not sure what we would g
+ 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());
+ return *builder.ToJSArray(result_array);
} else {
- return isolate->factory()->null_value(); // No matches at all.
+ return isolate->heap()->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);
- } else {
- return SearchRegExpMultiple<true>(isolate, subject, regexp, last_match_info,
- result_array);
- }
-}
-
-// 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) {
@@ -1444,15 +1276,10 @@ MaybeHandle<String> RegExpReplace(Isolate* isolate, Handle<JSRegExp> regexp,
}
} else {
DCHECK(functional_replace);
- if (global) {
- // Global regexp search, function replace.
- return StringReplaceGlobalRegExpWithFunction(isolate, string, regexp,
- replace_obj);
- } else {
- // Non-global regexp search, function replace.
- return StringReplaceNonGlobalRegExpWithFunction(isolate, string, regexp,
- replace_obj);
- }
+ DCHECK(!global); // Handled as fast path.
jgruber 2016/10/19 16:39:13 The entire functional_replace branch should be rem
+ // Non-global regexp search, function replace.
+ return StringReplaceNonGlobalRegExpWithFunction(isolate, string, regexp,
+ replace_obj);
}
UNREACHABLE();
@@ -1461,16 +1288,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);
jgruber 2016/10/19 16:39:13 Reusing the result array does not seem to make any
Yang 2016/10/20 09:53:14 I wonder whether we need a JSArray for this at all
jgruber 2016/10/20 10:58:30 Sure. I kept the JSArray return type for now becau
+ 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