| Index: src/runtime/runtime-regexp.cc
|
| diff --git a/src/runtime/runtime-regexp.cc b/src/runtime/runtime-regexp.cc
|
| index 4fd2e37aa71958dd6e9612db40fe960ff00a9bf8..aec95565109a11c50430b23f481cbee3f7868c27 100644
|
| --- a/src/runtime/runtime-regexp.cc
|
| +++ b/src/runtime/runtime-regexp.cc
|
| @@ -1084,15 +1084,32 @@ MUST_USE_RESULT MaybeHandle<String> StringReplaceNonGlobalRegExpWithFunction(
|
| Factory* factory = isolate->factory();
|
| Handle<RegExpMatchInfo> last_match_info = isolate->regexp_last_match_info();
|
|
|
| - // TODO(jgruber): This is a pattern we could refactor.
|
| + const int flags = regexp->GetFlags();
|
| +
|
| + DCHECK(RegExpUtils::IsUnmodifiedRegExp(isolate, regexp));
|
| + DCHECK_EQ(flags & JSRegExp::kGlobal, 0);
|
| +
|
| + // TODO(jgruber): This should be an easy port to CSA with massive payback.
|
| +
|
| + const bool sticky = (flags & JSRegExp::kSticky) != 0;
|
| + uint32_t last_index = 0;
|
| + if (sticky) {
|
| + Handle<Object> last_index_obj(regexp->LastIndex(), isolate);
|
| + ASSIGN_RETURN_ON_EXCEPTION(isolate, last_index_obj,
|
| + Object::ToLength(isolate, last_index_obj),
|
| + String);
|
| + last_index = PositiveNumberToUint32(*last_index_obj);
|
| +
|
| + if (static_cast<int>(last_index) > subject->length()) last_index = 0;
|
| + }
|
| +
|
| Handle<Object> match_indices_obj;
|
| ASSIGN_RETURN_ON_EXCEPTION(
|
| isolate, match_indices_obj,
|
| - RegExpImpl::Exec(regexp, subject, 0, last_match_info), String);
|
| + RegExpImpl::Exec(regexp, subject, last_index, last_match_info), String);
|
|
|
| if (match_indices_obj->IsNull(isolate)) {
|
| - RETURN_ON_EXCEPTION(isolate, RegExpUtils::SetLastIndex(isolate, regexp, 0),
|
| - String);
|
| + if (sticky) regexp->SetLastIndex(0);
|
| return subject;
|
| }
|
|
|
| @@ -1102,6 +1119,8 @@ MUST_USE_RESULT MaybeHandle<String> StringReplaceNonGlobalRegExpWithFunction(
|
| const int index = match_indices->Capture(0);
|
| const int end_of_match = match_indices->Capture(1);
|
|
|
| + if (sticky) regexp->SetLastIndex(end_of_match);
|
| +
|
| IncrementalStringBuilder builder(isolate);
|
| builder.AppendString(factory->NewSubString(subject, 0, index));
|
|
|
| @@ -1153,10 +1172,9 @@ MUST_USE_RESULT MaybeHandle<String> RegExpReplace(Isolate* isolate,
|
| Handle<Object> replace_obj) {
|
| Factory* factory = isolate->factory();
|
|
|
| - // TODO(jgruber): We need the even stricter guarantee of an unmodified
|
| - // JSRegExp map here for access to GetFlags to be legal.
|
| const int flags = regexp->GetFlags();
|
| const bool global = (flags & JSRegExp::kGlobal) != 0;
|
| + const bool sticky = (flags & JSRegExp::kSticky) != 0;
|
|
|
| // Functional fast-paths are dispatched directly by replace builtin.
|
| DCHECK(!replace_obj->IsCallable());
|
| @@ -1171,14 +1189,24 @@ MUST_USE_RESULT MaybeHandle<String> RegExpReplace(Isolate* isolate,
|
| if (!global) {
|
| // Non-global regexp search, string replace.
|
|
|
| + uint32_t last_index = 0;
|
| + if (sticky) {
|
| + Handle<Object> last_index_obj(regexp->LastIndex(), isolate);
|
| + ASSIGN_RETURN_ON_EXCEPTION(isolate, last_index_obj,
|
| + Object::ToLength(isolate, last_index_obj),
|
| + String);
|
| + last_index = PositiveNumberToUint32(*last_index_obj);
|
| +
|
| + if (static_cast<int>(last_index) > string->length()) last_index = 0;
|
| + }
|
| +
|
| Handle<Object> match_indices_obj;
|
| ASSIGN_RETURN_ON_EXCEPTION(
|
| isolate, match_indices_obj,
|
| - RegExpImpl::Exec(regexp, string, 0, last_match_info), String);
|
| + RegExpImpl::Exec(regexp, string, last_index, last_match_info), String);
|
|
|
| if (match_indices_obj->IsNull(isolate)) {
|
| - RETURN_ON_EXCEPTION(
|
| - isolate, RegExpUtils::SetLastIndex(isolate, regexp, 0), String);
|
| + if (sticky) regexp->SetLastIndex(0);
|
| return string;
|
| }
|
|
|
| @@ -1187,6 +1215,8 @@ MUST_USE_RESULT MaybeHandle<String> RegExpReplace(Isolate* isolate,
|
| const int start_index = match_indices->Capture(0);
|
| const int end_index = match_indices->Capture(1);
|
|
|
| + if (sticky) regexp->SetLastIndex(end_index);
|
| +
|
| IncrementalStringBuilder builder(isolate);
|
| builder.AppendString(factory->NewSubString(string, 0, start_index));
|
|
|
| @@ -1268,6 +1298,8 @@ RUNTIME_FUNCTION(Runtime_StringReplaceNonGlobalRegExpWithFunction) {
|
| CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 1);
|
| CONVERT_ARG_HANDLE_CHECKED(JSObject, replace, 2);
|
|
|
| + DCHECK(RegExpUtils::IsUnmodifiedRegExp(isolate, regexp));
|
| +
|
| RETURN_RESULT_OR_FAILURE(isolate, StringReplaceNonGlobalRegExpWithFunction(
|
| isolate, subject, regexp, replace));
|
| }
|
|
|