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

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

Issue 2764343004: [regexp] Named capture support for callable replacements (Closed)
Patch Set: Final tweaks Created 3 years, 9 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/objects-inl.h ('k') | test/mjsunit/harmony/regexp-named-captures.js » ('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 b10ee3230b1671d11422f3569963c79105663181..824835401dd4fafe776c7539a185d5375a32b855 100644
--- a/src/runtime/runtime-regexp.cc
+++ b/src/runtime/runtime-regexp.cc
@@ -4,6 +4,8 @@
#include "src/runtime/runtime-utils.h"
+#include <functional>
+
#include "src/arguments.h"
#include "src/conversions-inl.h"
#include "src/isolate-inl.h"
@@ -885,7 +887,7 @@ class VectorBackedMatch : public String::Match {
public:
VectorBackedMatch(Isolate* isolate, Handle<String> subject,
Handle<String> match, int match_position,
- ZoneVector<Handle<Object>>* captures)
+ std::vector<Handle<Object>>* captures)
: isolate_(isolate),
match_(match),
match_position_(match_position),
@@ -924,9 +926,34 @@ class VectorBackedMatch : public String::Match {
Handle<String> subject_;
Handle<String> match_;
const int match_position_;
- ZoneVector<Handle<Object>>* captures_;
+ std::vector<Handle<Object>>* captures_;
};
+// Create the groups object (see also the RegExp result creation in
+// RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo).
+Handle<JSObject> ConstructNamedCaptureGroupsObject(
+ Isolate* isolate, Handle<FixedArray> capture_map,
+ std::function<Object*(int)> f_get_capture) {
+ Handle<JSObject> groups = isolate->factory()->NewJSObjectWithNullProto();
+
+ const int capture_count = capture_map->length() >> 1;
+ for (int i = 0; i < capture_count; i++) {
+ const int name_ix = i * 2;
+ const int index_ix = i * 2 + 1;
+
+ Handle<String> capture_name(String::cast(capture_map->get(name_ix)));
+ const int capture_ix = Smi::cast(capture_map->get(index_ix))->value();
+ DCHECK(1 <= capture_ix && capture_ix <= capture_count);
+
+ Handle<Object> capture_value(f_get_capture(capture_ix), isolate);
+ DCHECK(capture_value->IsString());
+
+ JSObject::AddProperty(groups, capture_name, capture_value, NONE);
+ }
+
+ return groups;
+}
+
// 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>
@@ -934,8 +961,9 @@ static Object* SearchRegExpMultiple(Isolate* isolate, Handle<String> subject,
Handle<JSRegExp> regexp,
Handle<RegExpMatchInfo> last_match_array,
Handle<JSArray> result_array) {
- DCHECK(subject->IsFlat());
+ DCHECK(RegExpUtils::IsUnmodifiedRegExp(isolate, regexp));
DCHECK_NE(has_capture, regexp->CaptureCount() == 0);
+ DCHECK(subject->IsFlat());
int capture_count = regexp->CaptureCount();
int subject_length = subject->length();
@@ -1013,11 +1041,19 @@ static Object* SearchRegExpMultiple(Isolate* isolate, Handle<String> subject,
if (has_capture) {
// Arguments array to replace function is match, captures, index and
- // subject, i.e., 3 + capture count in total.
- Handle<FixedArray> elements =
- isolate->factory()->NewFixedArray(3 + capture_count);
+ // subject, i.e., 3 + capture count in total. If the RegExp contains
+ // named captures, they are also passed as the last argument.
+
+ Handle<Object> maybe_capture_map(regexp->CaptureNameMap(), isolate);
+ const bool has_named_captures = maybe_capture_map->IsFixedArray();
+
+ const int argc =
+ has_named_captures ? 4 + capture_count : 3 + capture_count;
+
+ Handle<FixedArray> elements = isolate->factory()->NewFixedArray(argc);
+ int cursor = 0;
- elements->set(0, *match);
+ elements->set(cursor++, *match);
for (int i = 1; i <= capture_count; i++) {
int start = current_match[i * 2];
if (start >= 0) {
@@ -1025,14 +1061,25 @@ static Object* SearchRegExpMultiple(Isolate* isolate, Handle<String> subject,
DCHECK(start <= end);
Handle<String> substring =
isolate->factory()->NewSubString(subject, start, end);
- elements->set(i, *substring);
+ elements->set(cursor++, *substring);
} else {
DCHECK(current_match[i * 2 + 1] < 0);
- elements->set(i, isolate->heap()->undefined_value());
+ elements->set(cursor++, isolate->heap()->undefined_value());
}
}
- elements->set(capture_count + 1, Smi::FromInt(match_start));
- elements->set(capture_count + 2, *subject);
+
+ elements->set(cursor++, Smi::FromInt(match_start));
+ elements->set(cursor++, *subject);
+
+ if (has_named_captures) {
+ Handle<FixedArray> capture_map =
+ Handle<FixedArray>::cast(maybe_capture_map);
+ Handle<JSObject> groups = ConstructNamedCaptureGroupsObject(
+ isolate, capture_map, [=](int ix) { return elements->get(ix); });
+ elements->set(cursor++, *groups);
+ }
+
+ DCHECK_EQ(cursor, argc);
builder.Add(*isolate->factory()->NewJSArrayWithElements(elements));
} else {
builder.Add(*match);
@@ -1084,15 +1131,16 @@ MUST_USE_RESULT MaybeHandle<String> RegExpReplace(Isolate* isolate,
Handle<JSRegExp> regexp,
Handle<String> string,
Handle<Object> replace_obj) {
+ // Functional fast-paths are dispatched directly by replace builtin.
+ DCHECK(RegExpUtils::IsUnmodifiedRegExp(isolate, regexp));
+ DCHECK(!replace_obj->IsCallable());
+
Factory* factory = isolate->factory();
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());
-
Handle<String> replace;
ASSIGN_RETURN_ON_EXCEPTION(isolate, replace,
Object::ToString(isolate, replace_obj), String);
@@ -1254,26 +1302,49 @@ RUNTIME_FUNCTION(Runtime_StringReplaceNonGlobalRegExpWithFunction) {
builder.AppendString(factory->NewSubString(subject, 0, index));
// Compute the parameter list consisting of the match, captures, index,
- // and subject for the replace function invocation.
+ // and subject for the replace function invocation. If the RegExp contains
+ // named captures, they are also passed as the last argument.
+
// The number of captures plus one for the match.
const int m = match_indices->NumberOfCaptureRegisters() / 2;
- const int argc = m + 2;
+ bool has_named_captures = false;
+ Handle<FixedArray> capture_map;
+ if (m > 1) {
+ // The existence of capture groups implies IRREGEXP kind.
+ DCHECK_EQ(regexp->TypeTag(), JSRegExp::IRREGEXP);
+
+ Object* maybe_capture_map = regexp->CaptureNameMap();
+ if (maybe_capture_map->IsFixedArray()) {
+ has_named_captures = true;
+ capture_map = handle(FixedArray::cast(maybe_capture_map));
+ }
+ }
+
+ const int argc = has_named_captures ? m + 3 : m + 2;
ScopedVector<Handle<Object>> argv(argc);
+ int cursor = 0;
for (int j = 0; j < m; j++) {
bool ok;
Handle<String> capture =
RegExpUtils::GenericCaptureGetter(isolate, match_indices, j, &ok);
if (ok) {
- argv[j] = capture;
+ argv[cursor++] = capture;
} else {
- argv[j] = factory->undefined_value();
+ argv[cursor++] = factory->undefined_value();
}
}
- argv[argc - 2] = handle(Smi::FromInt(index), isolate);
- argv[argc - 1] = subject;
+ argv[cursor++] = handle(Smi::FromInt(index), isolate);
+ argv[cursor++] = subject;
+
+ if (has_named_captures) {
+ argv[cursor++] = ConstructNamedCaptureGroupsObject(
+ isolate, capture_map, [&argv](int ix) { return *argv[ix]; });
+ }
+
+ DCHECK_EQ(cursor, argc);
Handle<Object> replacement_obj;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
@@ -1578,14 +1649,14 @@ RUNTIME_FUNCTION(Runtime_RegExpReplace) {
isolate, position_obj,
Object::GetProperty(result, factory->index_string()));
- // TODO(jgruber): Extract and correct error handling. Since we can go up to
- // 2^53 - 1 (at least for ToLength), we might actually need uint64_t here?
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, position_obj, Object::ToInteger(isolate, position_obj));
const uint32_t position =
std::min(PositiveNumberToUint32(*position_obj), length);
- ZoneVector<Handle<Object>> captures(&zone);
+ std::vector<Handle<Object>> captures;
+ captures.reserve(captures_length);
+
for (int n = 0; n < captures_length; n++) {
Handle<Object> capture;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
@@ -1598,17 +1669,29 @@ RUNTIME_FUNCTION(Runtime_RegExpReplace) {
captures.push_back(capture);
}
+ Handle<Object> groups_obj;
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+ isolate, groups_obj,
+ Object::GetProperty(result, factory->groups_string()));
+
+ const bool has_named_captures = !groups_obj->IsUndefined(isolate);
+
Handle<String> replacement;
if (functional_replace) {
- const int argc = captures_length + 2;
+ const int argc =
+ has_named_captures ? captures_length + 3 : captures_length + 2;
ScopedVector<Handle<Object>> argv(argc);
+ int cursor = 0;
for (int j = 0; j < captures_length; j++) {
- argv[j] = captures[j];
+ argv[cursor++] = captures[j];
}
- argv[captures_length] = handle(Smi::FromInt(position), isolate);
- argv[captures_length + 1] = string;
+ argv[cursor++] = handle(Smi::FromInt(position), isolate);
+ argv[cursor++] = string;
+ if (has_named_captures) argv[cursor++] = groups_obj;
+
+ DCHECK_EQ(cursor, argc);
Handle<Object> replacement_obj;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
@@ -1619,6 +1702,7 @@ RUNTIME_FUNCTION(Runtime_RegExpReplace) {
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, replacement, Object::ToString(isolate, replacement_obj));
} else {
+ DCHECK(!functional_replace);
VectorBackedMatch m(isolate, string, match, position, &captures);
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, replacement, String::GetSubstitution(isolate, &m, replace));
« no previous file with comments | « src/objects-inl.h ('k') | test/mjsunit/harmony/regexp-named-captures.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698