Commit 9460101c authored by Joshua Litt's avatar Joshua Litt Committed by Commit Bot

[regexp] Implement the match indices proposal

Implements match indices for regexp, as specified by
https://github.com/tc39/proposal-regexp-match-indices,
a stage 3 TC39 proposal. This implementation is hidden
behind the '--harmony-regexp-match-indices' flag.

Regexp match indices extends the JSRegExpResult object
with an array of indices of matches, as well as a
dictionary of capture names to match indices.

Bug: v8:9548
Change-Id: I9866a2d1f5af6a507de710357cb5e74c694e7558
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1734937
Commit-Queue: Joshua Litt <joshualitt@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarAdam Klein <adamk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#63272}
parent aba185a7
......@@ -913,6 +913,7 @@ action("postmortem-metadata") {
"src/objects/js-promise-inl.h",
"src/objects/js-promise.h",
"src/objects/js-regexp-inl.h",
"src/objects/js-regexp.cc",
"src/objects/js-regexp.h",
"src/objects/js-regexp-string-iterator-inl.h",
"src/objects/js-regexp-string-iterator.h",
......@@ -2551,6 +2552,7 @@ v8_source_set("v8_base_without_compiler") {
"src/objects/js-regexp-inl.h",
"src/objects/js-regexp-string-iterator-inl.h",
"src/objects/js-regexp-string-iterator.h",
"src/objects/js-regexp.cc",
"src/objects/js-regexp.h",
"src/objects/js-relative-time-format-inl.h",
"src/objects/js-relative-time-format.cc",
......
......@@ -16,6 +16,7 @@
#include "src/objects/contexts.h"
#include "src/objects/field-index-inl.h"
#include "src/objects/js-array-inl.h"
#include "src/objects/js-regexp-inl.h"
#include "src/objects/module-inl.h"
#include "src/objects/property-details.h"
#include "src/objects/prototype.h"
......@@ -840,5 +841,25 @@ Handle<AccessorInfo> Accessors::MakeErrorStackInfo(Isolate* isolate) {
&ErrorStackGetter, &ErrorStackSetter);
}
//
// Accessors::RegExpResultIndices
//
void Accessors::RegExpResultIndicesGetter(
v8::Local<v8::Name> key, const v8::PropertyCallbackInfo<v8::Value>& info) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate());
HandleScope scope(isolate);
Handle<JSRegExpResult> regexp_result(
Handle<JSRegExpResult>::cast(Utils::OpenHandle(*info.Holder())));
Handle<Object> indices(
JSRegExpResult::GetAndCacheIndices(isolate, regexp_result));
info.GetReturnValue().Set(Utils::ToLocal(indices));
}
Handle<AccessorInfo> Accessors::MakeRegExpResultIndicesInfo(Isolate* isolate) {
return MakeAccessor(isolate, isolate->factory()->indices_string(),
&RegExpResultIndicesGetter, nullptr);
}
} // namespace internal
} // namespace v8
......@@ -43,6 +43,8 @@ class JavaScriptFrame;
kHasSideEffectToReceiver) \
V(_, function_prototype, FunctionPrototype, kHasNoSideEffect, \
kHasSideEffectToReceiver) \
V(_, regexp_result_indices, RegExpResultIndices, kHasSideEffectToReceiver, \
kHasSideEffectToReceiver) \
V(_, string_length, StringLength, kHasNoSideEffect, kHasSideEffectToReceiver)
#define ACCESSOR_SETTER_LIST(V) \
......
......@@ -1278,11 +1278,22 @@ RegExpBuiltinsAssembler::FastStoreLastIndex(FastJSRegExp, Smi): void;
@hasSameInstanceTypeAsParent
extern class JSRegExpResult extends JSArray {
// The below fields are for internal use only.
cached_indices_or_match_info: JSRegExpResultIndices | RegExpMatchInfo;
names: FixedArray | Undefined;
// The below fields are externally exposed.
index: Object;
input: Object;
groups: Object;
}
@hasSameInstanceTypeAsParent
extern class JSRegExpResultIndices extends JSArray {
// The groups field is externally exposed.
groups: Object;
}
extern class JSRegExpStringIterator extends JSObject {
iterating_reg_exp: Object;
iterated_string: String;
......
......@@ -35,7 +35,8 @@ TNode<IntPtrT> RegExpBuiltinsAssembler::IntPtrZero() {
TNode<JSRegExpResult> RegExpBuiltinsAssembler::AllocateRegExpResult(
TNode<Context> context, TNode<Smi> length, TNode<Smi> index,
TNode<String> input, TNode<FixedArray>* elements_out) {
TNode<String> input, TNode<RegExpMatchInfo> match_info,
TNode<FixedArray>* elements_out) {
CSA_ASSERT(this, SmiLessThanOrEqual(
length, SmiConstant(JSArray::kMaxFastArrayLength)));
CSA_ASSERT(this, SmiGreaterThan(length, SmiConstant(0)));
......@@ -62,11 +63,22 @@ TNode<JSRegExpResult> RegExpBuiltinsAssembler::AllocateRegExpResult(
TNode<JSRegExpResult> result = CAST(array);
// Load undefined value once here to avoid multiple LoadRoots.
TNode<Oddball> undefined_value =
UncheckedCast<Oddball>(LoadRoot(RootIndex::kUndefinedValue));
StoreObjectFieldNoWriteBarrier(result, JSRegExpResult::kIndexOffset, index);
// TODO(jgruber,tebbi): Could skip barrier but the MemoryOptimizer complains.
StoreObjectField(result, JSRegExpResult::kInputOffset, input);
StoreObjectFieldNoWriteBarrier(result, JSRegExpResult::kGroupsOffset,
UndefinedConstant());
undefined_value);
StoreObjectFieldNoWriteBarrier(result, JSRegExpResult::kNamesOffset,
undefined_value);
// Stash match_info in order to build JSRegExpResultIndices lazily when the
// 'indices' property is accessed.
StoreObjectField(result, JSRegExpResult::kCachedIndicesOrMatchInfoOffset,
match_info);
// Finish elements initialization.
......@@ -168,7 +180,7 @@ TNode<JSRegExpResult> RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo(
TNode<FixedArray> result_elements;
TNode<JSRegExpResult> result = AllocateRegExpResult(
context, num_results, start, string, &result_elements);
context, num_results, start, string, match_info, &result_elements);
UnsafeStoreFixedArrayElement(result_elements, 0, first);
......@@ -244,6 +256,9 @@ TNode<JSRegExpResult> RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo(
TNode<IntPtrT> names_length = LoadAndUntagFixedArrayBaseLength(names);
CSA_ASSERT(this, IntPtrGreaterThan(names_length, IntPtrZero()));
// Stash names in case we need them to build the indices array later.
StoreObjectField(result, JSRegExpResult::kNamesOffset, names);
// Allocate a new object to store the named capture properties.
// TODO(jgruber): Could be optimized by adding the object map to the heap
// root list.
......
......@@ -35,7 +35,8 @@ class RegExpBuiltinsAssembler : public CodeStubAssembler {
// and input string.
TNode<JSRegExpResult> AllocateRegExpResult(
TNode<Context> context, TNode<Smi> length, TNode<Smi> index,
TNode<String> input, TNode<FixedArray>* elements_out = nullptr);
TNode<String> input, TNode<RegExpMatchInfo> match_info,
TNode<FixedArray>* elements_out = nullptr);
TNode<Object> FastLoadLastIndexBeforeSmiCheck(TNode<JSRegExp> regexp);
TNode<Smi> FastLoadLastIndex(TNode<JSRegExp> regexp) {
......
......@@ -207,7 +207,8 @@ DEFINE_IMPLICATION(harmony_import_meta, harmony_dynamic_import)
V(harmony_private_methods, "harmony private methods in class literals") \
V(harmony_regexp_sequence, "RegExp Unicode sequence properties") \
V(harmony_weak_refs, "harmony weak references") \
V(harmony_optional_chaining, "harmony optional chaining syntax")
V(harmony_optional_chaining, "harmony optional chaining syntax") \
V(harmony_regexp_match_indices, "harmony regexp match indices")
#ifdef V8_INTL_SUPPORT
#define HARMONY_INPROGRESS(V) \
......
......@@ -284,6 +284,10 @@ class Genesis {
void TransferNamedProperties(Handle<JSObject> from, Handle<JSObject> to);
void TransferIndexedProperties(Handle<JSObject> from, Handle<JSObject> to);
Handle<Map> CreateInitialMapForArraySubclass(int size,
int inobject_properties,
int additional_properties = 0);
static bool CompileExtension(Isolate* isolate, v8::Extension* extension);
Isolate* isolate_;
......@@ -4414,6 +4418,17 @@ void Genesis::InitializeGlobal_harmony_promise_all_settled() {
}
}
void Genesis::InitializeGlobal_harmony_regexp_match_indices() {
if (!FLAG_harmony_regexp_match_indices) return;
// Add indices accessor to JSRegExpResult's initial map.
Handle<Map> initial_map(native_context()->regexp_result_map(), isolate());
Descriptor d = Descriptor::AccessorConstant(
factory()->indices_string(), factory()->regexp_result_indices_accessor(),
NONE);
initial_map->AppendDescriptor(isolate(), &d);
}
#ifdef V8_INTL_SUPPORT
void Genesis::InitializeGlobal_harmony_intl_date_format_range() {
......@@ -4895,42 +4910,12 @@ bool Genesis::InstallNatives() {
// predefines the properties index, input, and groups).
{
// JSRegExpResult initial map.
// Find global.Array.prototype to inherit from.
Handle<JSFunction> array_constructor(native_context()->array_function(),
isolate());
Handle<JSObject> array_prototype(
JSObject::cast(array_constructor->instance_prototype()), isolate());
// Add initial map.
Handle<Map> initial_map = factory()->NewMap(
JS_ARRAY_TYPE, JSRegExpResult::kSize, TERMINAL_FAST_ELEMENTS_KIND,
JSRegExpResult::kInObjectPropertyCount);
initial_map->SetConstructor(*array_constructor);
// Set prototype on map.
initial_map->set_has_non_instance_prototype(false);
Map::SetPrototype(isolate(), initial_map, array_prototype);
// Update map with length accessor from Array and add "index", "input" and
// "groups".
Map::EnsureDescriptorSlack(isolate(), initial_map,
JSRegExpResult::kInObjectPropertyCount + 1);
// length descriptor.
{
JSFunction array_function = native_context()->array_function();
Handle<DescriptorArray> array_descriptors(
array_function.initial_map().instance_descriptors(), isolate());
Handle<String> length = factory()->length_string();
int old = array_descriptors->SearchWithCache(
isolate(), *length, array_function.initial_map());
DCHECK_NE(old, DescriptorArray::kNotFound);
Descriptor d = Descriptor::AccessorConstant(
length, handle(array_descriptors->GetStrongValue(old), isolate()),
array_descriptors->GetDetails(old).attributes());
initial_map->AppendDescriptor(isolate(), &d);
}
// Add additional slack to the initial map in case regexp_match_indices
// are enabled to account for the additional descriptor.
int additional_slack = 1;
Handle<Map> initial_map = CreateInitialMapForArraySubclass(
JSRegExpResult::kSize, JSRegExpResult::kInObjectPropertyCount,
additional_slack);
// index descriptor.
{
......@@ -4959,6 +4944,27 @@ bool Genesis::InstallNatives() {
native_context()->set_regexp_result_map(*initial_map);
}
// Create a constructor for JSRegExpResultIndices (a variant of Array that
// predefines the groups property).
{
// JSRegExpResultIndices initial map.
Handle<Map> initial_map = CreateInitialMapForArraySubclass(
JSRegExpResultIndices::kSize,
JSRegExpResultIndices::kInObjectPropertyCount);
// groups descriptor.
{
Descriptor d = Descriptor::DataField(
isolate(), factory()->groups_string(),
JSRegExpResultIndices::kGroupsIndex, NONE, Representation::Tagged());
initial_map->AppendDescriptor(isolate(), &d);
DCHECK_EQ(initial_map->LastAdded(),
JSRegExpResultIndices::kGroupsDescriptorIndex);
}
native_context()->set_regexp_result_indices_map(*initial_map);
}
// Add @@iterator method to the arguments object maps.
{
PropertyAttributes attribs = DONT_ENUM;
......@@ -5360,6 +5366,47 @@ void Genesis::TransferObject(Handle<JSObject> from, Handle<JSObject> to) {
JSObject::ForceSetPrototype(to, proto);
}
Handle<Map> Genesis::CreateInitialMapForArraySubclass(int size,
int inobject_properties,
int additional_slack) {
// Find global.Array.prototype to inherit from.
Handle<JSFunction> array_constructor(native_context()->array_function(),
isolate());
Handle<JSObject> array_prototype(native_context()->initial_array_prototype(),
isolate());
// Add initial map.
Handle<Map> initial_map = factory()->NewMap(
JS_ARRAY_TYPE, size, TERMINAL_FAST_ELEMENTS_KIND, inobject_properties);
initial_map->SetConstructor(*array_constructor);
// Set prototype on map.
initial_map->set_has_non_instance_prototype(false);
Map::SetPrototype(isolate(), initial_map, array_prototype);
// Update map with length accessor from Array.
static constexpr int kTheLengthAccessor = 1;
Map::EnsureDescriptorSlack(
isolate(), initial_map,
inobject_properties + kTheLengthAccessor + additional_slack);
// length descriptor.
{
JSFunction array_function = native_context()->array_function();
Handle<DescriptorArray> array_descriptors(
array_function.initial_map().instance_descriptors(), isolate());
Handle<String> length = factory()->length_string();
int old = array_descriptors->SearchWithCache(isolate(), *length,
array_function.initial_map());
DCHECK_NE(old, DescriptorArray::kNotFound);
Descriptor d = Descriptor::AccessorConstant(
length, handle(array_descriptors->GetStrongValue(old), isolate()),
array_descriptors->GetDetails(old).attributes());
initial_map->AppendDescriptor(isolate(), &d);
}
return initial_map;
}
Genesis::Genesis(
Isolate* isolate, MaybeHandle<JSGlobalProxy> maybe_global_proxy,
v8::Local<v8::ObjectTemplate> global_proxy_template,
......
......@@ -202,6 +202,7 @@
V(_, illegal_access_string, "illegal access") \
V(_, illegal_argument_string, "illegal argument") \
V(_, index_string, "index") \
V(_, indices_string, "indices") \
V(_, Infinity_string, "Infinity") \
V(_, infinity_string, "infinity") \
V(_, input_string, "input") \
......
......@@ -233,6 +233,7 @@ enum ContextLookupFlags {
V(REGEXP_PROTOTYPE_MAP_INDEX, Map, regexp_prototype_map) \
V(REGEXP_REPLACE_FUNCTION_INDEX, JSFunction, regexp_replace_function) \
V(REGEXP_RESULT_MAP_INDEX, Map, regexp_result_map) \
V(REGEXP_RESULT_INDICES_MAP_INDEX, Map, regexp_result_indices_map) \
V(REGEXP_SEARCH_FUNCTION_INDEX, JSFunction, regexp_search_function) \
V(REGEXP_SPECIES_PROTECTOR_INDEX, PropertyCell, regexp_species_protector) \
V(REGEXP_SPLIT_FUNCTION_INDEX, JSFunction, regexp_split_function) \
......
......@@ -452,6 +452,7 @@ V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
V(JSProxy, JS_PROXY_TYPE) \
V(JSRegExp, JS_REGEXP_TYPE) \
V(JSRegExpResult, JS_ARRAY_TYPE) \
V(JSRegExpResultIndices, JS_ARRAY_TYPE) \
V(JSRegExpStringIterator, JS_REGEXP_STRING_ITERATOR_TYPE) \
V(JSSet, JS_SET_TYPE) \
V(JSStringIterator, JS_STRING_ITERATOR_TYPE) \
......
......@@ -7,6 +7,7 @@
#include "src/objects/js-regexp.h"
#include "src/objects/js-array-inl.h"
#include "src/objects/objects-inl.h" // Needed for write barriers
#include "src/objects/smi.h"
#include "src/objects/string.h"
......@@ -18,14 +19,22 @@ namespace v8 {
namespace internal {
OBJECT_CONSTRUCTORS_IMPL(JSRegExp, JSObject)
OBJECT_CONSTRUCTORS_IMPL(JSRegExpResult, JSArray)
OBJECT_CONSTRUCTORS_IMPL(JSRegExpResultIndices, JSArray)
CAST_ACCESSOR(JSRegExp)
CAST_ACCESSOR(JSRegExpResult)
CAST_ACCESSOR(JSRegExpResultIndices)
ACCESSORS(JSRegExp, data, Object, kDataOffset)
ACCESSORS(JSRegExp, flags, Object, kFlagsOffset)
ACCESSORS(JSRegExp, source, Object, kSourceOffset)
ACCESSORS(JSRegExp, last_index, Object, kLastIndexOffset)
ACCESSORS(JSRegExpResult, cached_indices_or_match_info, Object,
kCachedIndicesOrMatchInfoOffset)
ACCESSORS(JSRegExpResult, names, Object, kNamesOffset)
JSRegExp::Type JSRegExp::TypeTag() const {
Object data = this->data();
if (data.IsUndefined()) return JSRegExp::NOT_COMPILED;
......
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/objects/js-regexp.h"
#include "src/objects/js-array-inl.h"
#include "src/objects/js-regexp-inl.h"
namespace v8 {
namespace internal {
Handle<JSArray> JSRegExpResult::GetAndCacheIndices(
Isolate* isolate, Handle<JSRegExpResult> regexp_result) {
// Check for cached indices.
Handle<Object> indices_or_match_info(
regexp_result->cached_indices_or_match_info(), isolate);
if (indices_or_match_info->IsRegExpMatchInfo()) {
// Build and cache indices for next lookup.
// TODO(joshualitt): Instead of caching the indices, we could call
// ReconfigureToDataProperty on 'indices' setting its value to this
// newly created array. However, care would have to be taken to ensure
// a new map is not created each time.
Handle<RegExpMatchInfo> match_info(
RegExpMatchInfo::cast(regexp_result->cached_indices_or_match_info()),
isolate);
Handle<Object> maybe_names(regexp_result->names(), isolate);
indices_or_match_info =
JSRegExpResultIndices::BuildIndices(isolate, match_info, maybe_names);
// Cache the result and clear the names array.
regexp_result->set_cached_indices_or_match_info(*indices_or_match_info);
regexp_result->set_names(ReadOnlyRoots(isolate).undefined_value());
}
return Handle<JSArray>::cast(indices_or_match_info);
}
Handle<JSRegExpResultIndices> JSRegExpResultIndices::BuildIndices(
Isolate* isolate, Handle<RegExpMatchInfo> match_info,
Handle<Object> maybe_names) {
Handle<JSRegExpResultIndices> indices(Handle<JSRegExpResultIndices>::cast(
isolate->factory()->NewJSObjectFromMap(
isolate->regexp_result_indices_map())));
// Initialize indices length to avoid having a partially initialized object
// should GC be triggered by creating a NewFixedArray.
indices->set_length(Smi::kZero);
// Build indices array from RegExpMatchInfo.
int num_indices = match_info->NumberOfCaptureRegisters();
int num_results = num_indices >> 1;
Handle<FixedArray> indices_array =
isolate->factory()->NewFixedArray(num_results);
JSArray::SetContent(indices, indices_array);
for (int i = 0; i < num_results; i++) {
int base_offset = i * 2;
int start_offset = match_info->Capture(base_offset);
int end_offset = match_info->Capture(base_offset + 1);
// Any unmatched captures are set to undefined, otherwise we set them to a
// subarray of the indices.
if (start_offset == -1) {
indices_array->set(i, ReadOnlyRoots(isolate).undefined_value());
} else {
Handle<FixedArray> indices_sub_array(
isolate->factory()->NewFixedArray(2));
indices_sub_array->set(0, Smi::FromInt(start_offset));
indices_sub_array->set(1, Smi::FromInt(end_offset));
Handle<JSArray> indices_sub_jsarray =
isolate->factory()->NewJSArrayWithElements(indices_sub_array,
PACKED_SMI_ELEMENTS, 2);
indices_array->set(i, *indices_sub_jsarray);
}
}
// If there are no capture groups, set the groups property to undefined.
FieldIndex groups_index =
FieldIndex::ForDescriptor(indices->map(), kGroupsDescriptorIndex);
if (maybe_names->IsUndefined(isolate)) {
indices->RawFastPropertyAtPut(groups_index,
ReadOnlyRoots(isolate).undefined_value());
return indices;
}
// Create a groups property which returns a dictionary of named captures to
// their corresponding capture indices.
Handle<FixedArray> names(Handle<FixedArray>::cast(maybe_names));
int num_names = names->length() >> 1;
Handle<NameDictionary> group_names = NameDictionary::New(isolate, num_names);
for (int i = 0; i < num_names; i++) {
int base_offset = i * 2;
int name_offset = base_offset;
int index_offset = base_offset + 1;
Handle<String> name(String::cast(names->get(name_offset)), isolate);
Handle<Smi> smi_index(Smi::cast(names->get(index_offset)), isolate);
Handle<Object> capture_indices(indices_array->get(smi_index->value()),
isolate);
if (!capture_indices->IsUndefined(isolate)) {
capture_indices = Handle<JSArray>::cast(capture_indices);
}
group_names = NameDictionary::Add(
isolate, group_names, name, capture_indices, PropertyDetails::Empty());
}
// Convert group_names to a JSObject and store at the groups property of the
// result indices.
Handle<FixedArrayBase> elements = isolate->factory()->empty_fixed_array();
Handle<HeapObject> null =
Handle<HeapObject>::cast(isolate->factory()->null_value());
Handle<JSObject> js_group_names =
isolate->factory()->NewSlowJSObjectWithPropertiesAndElements(
null, group_names, elements);
indices->RawFastPropertyAtPut(groups_index, *js_group_names);
return indices;
}
} // namespace internal
} // namespace v8
......@@ -199,18 +199,59 @@ DEFINE_OPERATORS_FOR_FLAGS(JSRegExp::Flags)
// After creation the result must be treated as a JSArray in all regards.
class JSRegExpResult : public JSArray {
public:
DECL_CAST(JSRegExpResult)
// TODO(joshualitt): We would like to add printers and verifiers to
// JSRegExpResult, and maybe JSRegExpResultIndices, but both have the same
// instance type as JSArray.
// cached_indices_or_match_info and names, are used to construct the
// JSRegExpResultIndices returned from the indices property lazily.
DECL_ACCESSORS(cached_indices_or_match_info, Object)
DECL_ACCESSORS(names, Object)
// Layout description.
DEFINE_FIELD_OFFSET_CONSTANTS(JSArray::kSize,
TORQUE_GENERATED_JSREG_EXP_RESULT_FIELDS)
static Handle<JSArray> GetAndCacheIndices(
Isolate* isolate, Handle<JSRegExpResult> regexp_result);
// Indices of in-object properties.
static const int kIndexIndex = 0;
static const int kInputIndex = 1;
static const int kGroupsIndex = 2;
static const int kInObjectPropertyCount = 3;
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(JSRegExpResult);
OBJECT_CONSTRUCTORS(JSRegExpResult, JSArray);
};
// JSRegExpResultIndices is just a JSArray with a specific initial map.
// This initial map adds in-object properties for "group"
// properties, as assigned by RegExp.prototype.exec, which allows
// faster creation of RegExp exec results.
// This class just holds constants used when creating the result.
// After creation the result must be treated as a JSArray in all regards.
class JSRegExpResultIndices : public JSArray {
public:
DECL_CAST(JSRegExpResultIndices)
// Layout description.
DEFINE_FIELD_OFFSET_CONSTANTS(
JSArray::kSize, TORQUE_GENERATED_JSREG_EXP_RESULT_INDICES_FIELDS)
static Handle<JSRegExpResultIndices> BuildIndices(
Isolate* isolate, Handle<RegExpMatchInfo> match_info,
Handle<Object> maybe_names);
// Indices of in-object properties.
static const int kGroupsIndex = 0;
static const int kInObjectPropertyCount = 1;
// Descriptor index of groups.
static const int kGroupsDescriptorIndex = 1;
OBJECT_CONSTRUCTORS(JSRegExpResultIndices, JSArray);
};
} // namespace internal
......
......@@ -158,6 +158,7 @@ class ZoneForwardList;
V(JSReceiver) \
V(JSRegExp) \
V(JSRegExpResult) \
V(JSRegExpResultIndices) \
V(JSRegExpStringIterator) \
V(JSSet) \
V(JSSetIterator) \
......
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-regexp-match-indices
// Sanity test.
{
const re = /a+(?<Z>z)?/;
const m = re.exec("xaaaz");
assertEquals(m.indices, [[1, 5], [4, 5]]);
assertEquals(m.indices.groups, {'Z': [4, 5]})
}
// Capture groups that are not matched return `undefined`.
{
const re = /a+(?<Z>z)?/;
const m = re.exec("xaaay");
assertEquals(m.indices, [[1, 4], undefined]);
assertEquals(m.indices.groups, {'Z': undefined});
}
// Two capture groups.
{
const re = /a+(?<A>zz)?(?<B>ii)?/;
const m = re.exec("xaaazzii");
assertEquals(m.indices, [[1, 8], [4, 6], [6, 8]]);
assertEquals(m.indices.groups, {'A': [4, 6], 'B': [6, 8]});
}
// No capture groups.
{
const re = /a+/;
const m = re.exec("xaaazzii");
assertEquals(m.indices [[1, 4]]);
assertEquals(m.indices.groups, undefined);
}
// No match.
{
const re = /a+/;
const m = re.exec("xzzii");
assertEquals(null, m);
}
// Unnamed capture groups.
{
const re = /a+(z)?/;
const m = re.exec("xaaaz")
assertEquals(m.indices, [[1, 5], [4, 5]]);
assertEquals(m.indices.groups, undefined)
}
// Named and unnamed capture groups.
{
const re = /a+(z)?(?<Y>y)?/;
const m = re.exec("xaaazyy")
assertEquals(m.indices, [[1, 6], [4, 5], [5, 6]]);
assertEquals(m.indices.groups, {'Y': [5, 6]})
}
// Verify property overwrite.
{
const re = /a+(?<Z>z)?/;
const m = re.exec("xaaaz");
m.indices = null;
assertEquals(null, m.indices);
}
// Mess with array prototype, we should still do the right thing.
{
Object.defineProperty(Array.prototype, "groups", {
get: () => {
assertUnreachable();
return null;
},
set: (x) => {
assertUnreachable();
}
});
Object.defineProperty(Array.prototype, "0", {
get: () => {
assertUnreachable();
return null;
},
set: (x) => {
assertUnreachable();
}
});
const re = /a+(?<Z>z)?/;
const m = re.exec("xaaaz");
assertEquals(m.indices.groups, {'Z': [4, 5]})
}
......@@ -293,54 +293,54 @@ KNOWN_MAPS = {
("read_only_space", 0x023e9): (88, "EnumCacheMap"),
("read_only_space", 0x02489): (83, "ArrayBoilerplateDescriptionMap"),
("read_only_space", 0x02679): (91, "InterceptorInfoMap"),
("read_only_space", 0x04ef9): (78, "AccessCheckInfoMap"),
("read_only_space", 0x04f49): (79, "AccessorInfoMap"),
("read_only_space", 0x04f99): (80, "AccessorPairMap"),
("read_only_space", 0x04fe9): (81, "AliasedArgumentsEntryMap"),
("read_only_space", 0x05039): (82, "AllocationMementoMap"),
("read_only_space", 0x05089): (84, "AsmWasmDataMap"),
("read_only_space", 0x050d9): (85, "AsyncGeneratorRequestMap"),
("read_only_space", 0x05129): (86, "ClassPositionsMap"),
("read_only_space", 0x05179): (87, "DebugInfoMap"),
("read_only_space", 0x051c9): (89, "FunctionTemplateInfoMap"),
("read_only_space", 0x05219): (90, "FunctionTemplateRareDataMap"),
("read_only_space", 0x05269): (92, "InterpreterDataMap"),
("read_only_space", 0x052b9): (93, "ObjectTemplateInfoMap"),
("read_only_space", 0x05309): (94, "PromiseCapabilityMap"),
("read_only_space", 0x05359): (95, "PromiseReactionMap"),
("read_only_space", 0x053a9): (96, "PrototypeInfoMap"),
("read_only_space", 0x053f9): (97, "ScriptMap"),
("read_only_space", 0x05449): (98, "SourcePositionTableWithFrameCacheMap"),
("read_only_space", 0x05499): (99, "SourceTextModuleInfoEntryMap"),
("read_only_space", 0x054e9): (100, "StackFrameInfoMap"),
("read_only_space", 0x05539): (101, "StackTraceFrameMap"),
("read_only_space", 0x05589): (102, "TemplateObjectDescriptionMap"),
("read_only_space", 0x055d9): (103, "Tuple2Map"),
("read_only_space", 0x05629): (104, "Tuple3Map"),
("read_only_space", 0x05679): (105, "WasmCapiFunctionDataMap"),
("read_only_space", 0x056c9): (106, "WasmDebugInfoMap"),
("read_only_space", 0x05719): (107, "WasmExceptionTagMap"),
("read_only_space", 0x05769): (108, "WasmExportedFunctionDataMap"),
("read_only_space", 0x057b9): (109, "WasmIndirectFunctionTableMap"),
("read_only_space", 0x05809): (110, "WasmJSFunctionDataMap"),
("read_only_space", 0x05859): (111, "CallableTaskMap"),
("read_only_space", 0x058a9): (112, "CallbackTaskMap"),
("read_only_space", 0x058f9): (113, "PromiseFulfillReactionJobTaskMap"),
("read_only_space", 0x05949): (114, "PromiseRejectReactionJobTaskMap"),
("read_only_space", 0x05999): (115, "PromiseResolveThenableJobTaskMap"),
("read_only_space", 0x059e9): (116, "InternalClassMap"),
("read_only_space", 0x05a39): (117, "SmiPairMap"),
("read_only_space", 0x05a89): (118, "SmiBoxMap"),
("read_only_space", 0x05ad9): (119, "SortStateMap"),
("read_only_space", 0x05b29): (122, "AllocationSiteWithWeakNextMap"),
("read_only_space", 0x05b79): (122, "AllocationSiteWithoutWeakNextMap"),
("read_only_space", 0x05bc9): (157, "LoadHandler1Map"),
("read_only_space", 0x05c19): (157, "LoadHandler2Map"),
("read_only_space", 0x05c69): (157, "LoadHandler3Map"),
("read_only_space", 0x05cb9): (165, "StoreHandler0Map"),
("read_only_space", 0x05d09): (165, "StoreHandler1Map"),
("read_only_space", 0x05d59): (165, "StoreHandler2Map"),
("read_only_space", 0x05da9): (165, "StoreHandler3Map"),
("read_only_space", 0x04f11): (78, "AccessCheckInfoMap"),
("read_only_space", 0x04f61): (79, "AccessorInfoMap"),
("read_only_space", 0x04fb1): (80, "AccessorPairMap"),
("read_only_space", 0x05001): (81, "AliasedArgumentsEntryMap"),
("read_only_space", 0x05051): (82, "AllocationMementoMap"),
("read_only_space", 0x050a1): (84, "AsmWasmDataMap"),
("read_only_space", 0x050f1): (85, "AsyncGeneratorRequestMap"),
("read_only_space", 0x05141): (86, "ClassPositionsMap"),
("read_only_space", 0x05191): (87, "DebugInfoMap"),
("read_only_space", 0x051e1): (89, "FunctionTemplateInfoMap"),
("read_only_space", 0x05231): (90, "FunctionTemplateRareDataMap"),
("read_only_space", 0x05281): (92, "InterpreterDataMap"),
("read_only_space", 0x052d1): (93, "ObjectTemplateInfoMap"),
("read_only_space", 0x05321): (94, "PromiseCapabilityMap"),
("read_only_space", 0x05371): (95, "PromiseReactionMap"),
("read_only_space", 0x053c1): (96, "PrototypeInfoMap"),
("read_only_space", 0x05411): (97, "ScriptMap"),
("read_only_space", 0x05461): (98, "SourcePositionTableWithFrameCacheMap"),
("read_only_space", 0x054b1): (99, "SourceTextModuleInfoEntryMap"),
("read_only_space", 0x05501): (100, "StackFrameInfoMap"),
("read_only_space", 0x05551): (101, "StackTraceFrameMap"),
("read_only_space", 0x055a1): (102, "TemplateObjectDescriptionMap"),
("read_only_space", 0x055f1): (103, "Tuple2Map"),
("read_only_space", 0x05641): (104, "Tuple3Map"),
("read_only_space", 0x05691): (105, "WasmCapiFunctionDataMap"),
("read_only_space", 0x056e1): (106, "WasmDebugInfoMap"),
("read_only_space", 0x05731): (107, "WasmExceptionTagMap"),
("read_only_space", 0x05781): (108, "WasmExportedFunctionDataMap"),
("read_only_space", 0x057d1): (109, "WasmIndirectFunctionTableMap"),
("read_only_space", 0x05821): (110, "WasmJSFunctionDataMap"),
("read_only_space", 0x05871): (111, "CallableTaskMap"),
("read_only_space", 0x058c1): (112, "CallbackTaskMap"),
("read_only_space", 0x05911): (113, "PromiseFulfillReactionJobTaskMap"),
("read_only_space", 0x05961): (114, "PromiseRejectReactionJobTaskMap"),
("read_only_space", 0x059b1): (115, "PromiseResolveThenableJobTaskMap"),
("read_only_space", 0x05a01): (116, "InternalClassMap"),
("read_only_space", 0x05a51): (117, "SmiPairMap"),
("read_only_space", 0x05aa1): (118, "SmiBoxMap"),
("read_only_space", 0x05af1): (119, "SortStateMap"),
("read_only_space", 0x05b41): (122, "AllocationSiteWithWeakNextMap"),
("read_only_space", 0x05b91): (122, "AllocationSiteWithoutWeakNextMap"),
("read_only_space", 0x05be1): (157, "LoadHandler1Map"),
("read_only_space", 0x05c31): (157, "LoadHandler2Map"),
("read_only_space", 0x05c81): (157, "LoadHandler3Map"),
("read_only_space", 0x05cd1): (165, "StoreHandler0Map"),
("read_only_space", 0x05d21): (165, "StoreHandler1Map"),
("read_only_space", 0x05d71): (165, "StoreHandler2Map"),
("read_only_space", 0x05dc1): (165, "StoreHandler3Map"),
("map_space", 0x00111): (1057, "ExternalMap"),
("map_space", 0x00161): (1073, "JSMessageObjectMap"),
}
......@@ -400,29 +400,30 @@ KNOWN_OBJECTS = {
("old_space", 0x00421): "FunctionNameAccessor",
("old_space", 0x00491): "FunctionLengthAccessor",
("old_space", 0x00501): "FunctionPrototypeAccessor",
("old_space", 0x00571): "StringLengthAccessor",
("old_space", 0x005e1): "InvalidPrototypeValidityCell",
("old_space", 0x005f1): "EmptyScript",
("old_space", 0x00671): "ManyClosuresCell",
("old_space", 0x00689): "ArrayConstructorProtector",
("old_space", 0x00699): "NoElementsProtector",
("old_space", 0x006c1): "IsConcatSpreadableProtector",
("old_space", 0x006d1): "ArraySpeciesProtector",
("old_space", 0x006f9): "TypedArraySpeciesProtector",
("old_space", 0x00721): "PromiseSpeciesProtector",
("old_space", 0x00749): "StringLengthProtector",
("old_space", 0x00759): "ArrayIteratorProtector",
("old_space", 0x00781): "ArrayBufferDetachingProtector",
("old_space", 0x007a9): "PromiseHookProtector",
("old_space", 0x007d1): "PromiseResolveProtector",
("old_space", 0x007e1): "MapIteratorProtector",
("old_space", 0x00809): "PromiseThenProtector",
("old_space", 0x00831): "SetIteratorProtector",
("old_space", 0x00859): "StringIteratorProtector",
("old_space", 0x00881): "SingleCharacterStringCache",
("old_space", 0x01091): "StringSplitCache",
("old_space", 0x018a1): "RegExpMultipleCache",
("old_space", 0x020b1): "BuiltinsConstantsTable",
("old_space", 0x00571): "RegExpResultIndicesAccessor",
("old_space", 0x005e1): "StringLengthAccessor",
("old_space", 0x00651): "InvalidPrototypeValidityCell",
("old_space", 0x00661): "EmptyScript",
("old_space", 0x006e1): "ManyClosuresCell",
("old_space", 0x006f9): "ArrayConstructorProtector",
("old_space", 0x00709): "NoElementsProtector",
("old_space", 0x00731): "IsConcatSpreadableProtector",
("old_space", 0x00741): "ArraySpeciesProtector",
("old_space", 0x00769): "TypedArraySpeciesProtector",
("old_space", 0x00791): "PromiseSpeciesProtector",
("old_space", 0x007b9): "StringLengthProtector",
("old_space", 0x007c9): "ArrayIteratorProtector",
("old_space", 0x007f1): "ArrayBufferDetachingProtector",
("old_space", 0x00819): "PromiseHookProtector",
("old_space", 0x00841): "PromiseResolveProtector",
("old_space", 0x00851): "MapIteratorProtector",
("old_space", 0x00879): "PromiseThenProtector",
("old_space", 0x008a1): "SetIteratorProtector",
("old_space", 0x008c9): "StringIteratorProtector",
("old_space", 0x008f1): "SingleCharacterStringCache",
("old_space", 0x01101): "StringSplitCache",
("old_space", 0x01911): "RegExpMultipleCache",
("old_space", 0x02121): "BuiltinsConstantsTable",
}
# List of known V8 Frame Markers.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment