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)); |
} |