Commit 10540937 authored by Joshua Litt's avatar Joshua Litt Committed by Commit Bot

Reland "[regexp] Re-execute regexp when '.indices' is accessed."

This is a reland of f2a74165

Original change's description:
> [regexp] Re-execute regexp when '.indices' is accessed.
>
> Instead of storing a pointer to the last_match_info, which may
> change, this cl modifies JSRegExpResult to store a pointer to
> the original JSRegExp which generated it, as well as additional
> data needed to re-execute the match.
>
> Basically a straight copy and tidy off jgruber@'s prototype:
> https://chromium-review.googlesource.com/c/v8/v8/+/1876810
>
> Bug: v8:9548
> Change-Id: I11b7deae681b8287e41e8d0e342291ff484751fb
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1910129
> Commit-Queue: Joshua Litt <joshualitt@chromium.org>
> Reviewed-by: Jakob Gruber <jgruber@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#65053}

Bug: v8:9548
Change-Id: Ieeba4b1ae59ef0c7946d654dc314adfae09d24b5
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1925554Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Commit-Queue: Joshua Litt <joshualitt@chromium.org>
Cr-Commit-Position: refs/heads/master@{#65096}
parent 002d5be8
......@@ -851,9 +851,16 @@ void Accessors::RegExpResultIndicesGetter(
HandleScope scope(isolate);
Handle<JSRegExpResult> regexp_result(
Handle<JSRegExpResult>::cast(Utils::OpenHandle(*info.Holder())));
Handle<Object> indices(
MaybeHandle<JSArray> maybe_indices(
JSRegExpResult::GetAndCacheIndices(isolate, regexp_result));
info.GetReturnValue().Set(Utils::ToLocal(indices));
Handle<JSArray> indices;
if (!maybe_indices.ToHandle(&indices)) {
isolate->OptionalRescheduleException(false);
Handle<Object> result = isolate->factory()->undefined_value();
info.GetReturnValue().Set(Utils::ToLocal(result));
} else {
info.GetReturnValue().Set(Utils::ToLocal(indices));
}
}
Handle<AccessorInfo> Accessors::MakeRegExpResultIndicesInfo(Isolate* isolate) {
......
......@@ -78,7 +78,7 @@ TNode<RawPtrT> RegExpBuiltinsAssembler::LoadCodeObjectEntry(TNode<Code> code) {
TNode<JSRegExpResult> RegExpBuiltinsAssembler::AllocateRegExpResult(
TNode<Context> context, TNode<Smi> length, TNode<Smi> index,
TNode<String> input, TNode<RegExpMatchInfo> match_info,
TNode<String> input, TNode<JSRegExp> regexp, TNode<Number> last_index,
TNode<FixedArray>* elements_out) {
CSA_ASSERT(this, SmiLessThanOrEqual(
length, SmiConstant(JSArray::kMaxFastArrayLength)));
......@@ -117,10 +117,20 @@ TNode<JSRegExpResult> RegExpBuiltinsAssembler::AllocateRegExpResult(
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);
// Stash regexp in order to re-execute and build JSRegExpResultIndices lazily
// when the 'indices' property is accessed.
StoreObjectField(result, JSRegExpResult::kCachedIndicesOrRegexpOffset,
regexp);
StoreObjectField(result, JSRegExpResult::kRegexpInputOffset, input);
// If non-smi last_index then store an SmiZero instead.
{
TNode<Smi> last_index_smi = Select<Smi>(
TaggedIsSmi(last_index), [=] { return CAST(last_index); },
[=] { return SmiZero(); });
StoreObjectField(result, JSRegExpResult::kRegexpLastIndexOffset,
last_index_smi);
}
// Finish elements initialization.
......@@ -162,8 +172,9 @@ void RegExpBuiltinsAssembler::SlowStoreLastIndex(SloppyTNode<Context> context,
}
TNode<JSRegExpResult> RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo(
TNode<Context> context, TNode<JSReceiver> maybe_regexp,
TNode<RegExpMatchInfo> match_info, TNode<String> string) {
TNode<Context> context, TNode<JSRegExp> regexp,
TNode<RegExpMatchInfo> match_info, TNode<String> string,
TNode<Number> last_index) {
Label named_captures(this), out(this);
TNode<IntPtrT> num_indices = SmiUntag(CAST(UnsafeLoadFixedArrayElement(
......@@ -181,8 +192,9 @@ TNode<JSRegExpResult> RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo(
CAST(CallBuiltin(Builtins::kSubString, context, string, start, end));
TNode<FixedArray> result_elements;
TNode<JSRegExpResult> result = AllocateRegExpResult(
context, num_results, start, string, match_info, &result_elements);
TNode<JSRegExpResult> result =
AllocateRegExpResult(context, num_results, start, string, regexp,
last_index, &result_elements);
UnsafeStoreFixedArrayElement(result_elements, 0, first);
......@@ -231,16 +243,14 @@ TNode<JSRegExpResult> RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo(
{
CSA_ASSERT(this, SmiGreaterThan(num_results, SmiConstant(1)));
// We reach this point only if captures exist, implying that this is an
// IRREGEXP JSRegExp.
TNode<JSRegExp> regexp = CAST(maybe_regexp);
// Preparations for named capture properties. Exit early if the result does
// not have any named captures to minimize performance impact.
TNode<FixedArray> data =
CAST(LoadObjectField(regexp, JSRegExp::kDataOffset));
// We reach this point only if captures exist, implying that this is an
// IRREGEXP JSRegExp.
CSA_ASSERT(this,
SmiEqual(CAST(LoadFixedArrayElement(data, JSRegExp::kTagIndex)),
SmiConstant(JSRegExp::IRREGEXP)));
......
......@@ -27,7 +27,7 @@ class RegExpBuiltinsAssembler : public CodeStubAssembler {
// and input string.
TNode<JSRegExpResult> AllocateRegExpResult(
TNode<Context> context, TNode<Smi> length, TNode<Smi> index,
TNode<String> input, TNode<RegExpMatchInfo> match_info,
TNode<String> input, TNode<JSRegExp> regexp, TNode<Number> last_index,
TNode<FixedArray>* elements_out = nullptr);
TNode<Object> FastLoadLastIndexBeforeSmiCheck(TNode<JSRegExp> regexp);
......@@ -58,8 +58,9 @@ class RegExpBuiltinsAssembler : public CodeStubAssembler {
TNode<RegExpMatchInfo> match_info);
TNode<JSRegExpResult> ConstructNewResultFromMatchInfo(
TNode<Context> context, TNode<JSReceiver> maybe_regexp,
TNode<RegExpMatchInfo> match_info, TNode<String> string);
TNode<Context> context, TNode<JSRegExp> regexp,
TNode<RegExpMatchInfo> match_info, TNode<String> string,
TNode<Number> last_index);
// Fast path check logic.
//
......
......@@ -161,12 +161,14 @@ namespace regexp {
let isFastRegExp: bool = false;
try {
if (IsFastRegExpPermissive(iteratingRegExp)) {
const regexp = UnsafeCast<JSRegExp>(iteratingRegExp);
const lastIndex = LoadLastIndexAsLength(regexp, true);
const matchIndices: RegExpMatchInfo =
RegExpPrototypeExecBodyWithoutResultFast(
UnsafeCast<JSRegExp>(iteratingRegExp), iteratingString)
regexp, iteratingString, lastIndex)
otherwise IfNoMatch;
match = ConstructNewResultFromMatchInfo(
iteratingRegExp, matchIndices, iteratingString);
regexp, matchIndices, iteratingString, lastIndex);
isFastRegExp = true;
} else {
match = RegExpExec(iteratingRegExp, iteratingString);
......
......@@ -20,7 +20,8 @@ namespace regexp {
// Call exec.
try {
const matchIndices: RegExpMatchInfo =
RegExpPrototypeExecBodyWithoutResultFast(regexp, string)
RegExpPrototypeExecBodyWithoutResultFast(
UnsafeCast<JSRegExp>(regexp), string)
otherwise DidNotMatch;
// Successful match.
......
......@@ -52,7 +52,7 @@ namespace regexp {
}
extern macro RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo(
implicit context: Context)(JSReceiver, RegExpMatchInfo, String):
implicit context: Context)(JSRegExp, RegExpMatchInfo, String, Number):
JSRegExpResult;
const kGlobalOrSticky: constexpr int31
......@@ -70,31 +70,15 @@ namespace regexp {
// IfDidNotMatch otherwise.
transitioning macro RegExpPrototypeExecBodyWithoutResult(implicit context:
Context)(
maybeRegexp: JSReceiver, string: String,
regexp: JSRegExp, string: String, regexpLastIndex: Number,
isFastPath: constexpr bool): RegExpMatchInfo labels IfDidNotMatch {
if (isFastPath) {
assert(HasInitialRegExpMap(maybeRegexp));
assert(HasInitialRegExpMap(regexp));
} else {
IncrementUseCounter(context, SmiConstant(kRegExpExecCalledOnSlowRegExp));
if (!Is<JSRegExp>(maybeRegexp)) {
ThrowTypeError(
kIncompatibleMethodReceiver, 'RegExp.prototype.exec', maybeRegexp);
}
}
const regexp = UnsafeCast<JSRegExp>(maybeRegexp);
let lastIndex: Number;
const regexpLastIndex = LoadLastIndex(regexp, isFastPath);
if (isFastPath) {
// ToLength on a positive smi is a nop and can be skipped.
assert(TaggedIsPositiveSmi(regexpLastIndex));
lastIndex = UnsafeCast<Number>(regexpLastIndex);
} else {
// Omit ToLength if lastindex is a non-negative smi.
lastIndex = TaggedIsPositiveSmi(regexpLastIndex) ?
UnsafeCast<Number>(regexpLastIndex) :
ToLength_Inline(regexpLastIndex);
}
let lastIndex = regexpLastIndex;
// Check whether the regexp is global or sticky, which determines whether we
// update last index later on.
......@@ -141,18 +125,35 @@ namespace regexp {
transitioning macro RegExpPrototypeExecBodyWithoutResultFast(
implicit context: Context)(regexp: JSRegExp, string: String):
RegExpMatchInfo labels IfDidNotMatch {
return RegExpPrototypeExecBodyWithoutResult(regexp, string, true)
const lastIndex = LoadLastIndexAsLength(regexp, true);
return RegExpPrototypeExecBodyWithoutResult(regexp, string, lastIndex, true)
otherwise IfDidNotMatch;
}
transitioning macro RegExpPrototypeExecBodyWithoutResultFast(
implicit context:
Context)(regexp: JSRegExp, string: String, lastIndex: Number):
RegExpMatchInfo labels IfDidNotMatch {
return RegExpPrototypeExecBodyWithoutResult(regexp, string, lastIndex, true)
otherwise IfDidNotMatch;
}
// ES#sec-regexp.prototype.exec
// RegExp.prototype.exec ( string )
transitioning macro RegExpPrototypeExecBody(implicit context: Context)(
maybeRegexp: JSReceiver, string: String,
isFastPath: constexpr bool): JSAny {
receiver: JSReceiver, string: String, isFastPath: constexpr bool): JSAny {
let regexp: JSRegExp;
if constexpr (isFastPath) {
regexp = UnsafeCast<JSRegExp>(receiver);
} else {
regexp = Cast<JSRegExp>(receiver) otherwise ThrowTypeError(
kIncompatibleMethodReceiver, 'RegExp.prototype.exec', receiver);
}
const lastIndex = LoadLastIndexAsLength(regexp, isFastPath);
const matchIndices: RegExpMatchInfo = RegExpPrototypeExecBodyWithoutResult(
maybeRegexp, string, isFastPath) otherwise return Null;
return ConstructNewResultFromMatchInfo(maybeRegexp, matchIndices, string);
regexp, string, lastIndex, isFastPath) otherwise return Null;
return ConstructNewResultFromMatchInfo(
regexp, matchIndices, string, lastIndex);
}
macro LoadRegExpFunction(implicit context: Context)(
......@@ -322,6 +323,26 @@ namespace regexp {
SlowLoadLastIndex(regexp);
}
@export
transitioning macro LoadLastIndexAsLength(implicit context: Context)(
regexp: JSRegExp, isFastPath: constexpr bool): Number {
const lastIndex = LoadLastIndex(regexp, isFastPath);
if (isFastPath) {
// ToLength on a positive smi is a nop and can be skipped.
return UnsafeCast<PositiveSmi>(lastIndex);
} else {
// Omit ToLength if last_index is a non-negative smi.
typeswitch (lastIndex) {
case (i: PositiveSmi): {
return i;
}
case (o: JSAny): {
return ToLength_Inline(o);
}
}
}
}
@export
transitioning macro StoreLastIndex(implicit context: Context)(
regexp: JSAny, value: Number, isFastPath: constexpr bool): void {
......
......@@ -4842,14 +4842,15 @@ bool Genesis::InstallABunchOfRandomThings() {
// Private internal only fields. All of the remaining fields have special
// symbols to prevent their use in Javascript.
// cached_indices_or_match_info descriptor.
{
PropertyAttributes attribs = DONT_ENUM;
// cached_indices_or_regexp descriptor.
{
Descriptor d = Descriptor::DataField(
isolate(),
factory()->regexp_result_cached_indices_or_match_info_symbol(),
JSRegExpResult::kCachedIndicesOrMatchInfoIndex, attribs,
factory()->regexp_result_cached_indices_or_regexp_symbol(),
JSRegExpResult::kCachedIndicesOrRegExpIndex, attribs,
Representation::Tagged());
initial_map->AppendDescriptor(isolate(), &d);
}
......@@ -4861,6 +4862,24 @@ bool Genesis::InstallABunchOfRandomThings() {
JSRegExpResult::kNamesIndex, attribs, Representation::Tagged());
initial_map->AppendDescriptor(isolate(), &d);
}
// regexp_input_index descriptor.
{
Descriptor d = Descriptor::DataField(
isolate(), factory()->regexp_result_regexp_input_symbol(),
JSRegExpResult::kRegExpInputIndex, attribs,
Representation::Tagged());
initial_map->AppendDescriptor(isolate(), &d);
}
// regexp_last_index descriptor.
{
Descriptor d = Descriptor::DataField(
isolate(), factory()->regexp_result_regexp_last_index_symbol(),
JSRegExpResult::kRegExpLastIndex, attribs,
Representation::Tagged());
initial_map->AppendDescriptor(isolate(), &d);
}
}
native_context()->set_regexp_result_map(*initial_map);
......
......@@ -326,36 +326,38 @@
V(_, writable_string, "writable") \
V(_, zero_string, "0")
#define PRIVATE_SYMBOL_LIST_GENERATOR(V, _) \
V(_, call_site_frame_array_symbol) \
V(_, call_site_frame_index_symbol) \
V(_, console_context_id_symbol) \
V(_, console_context_name_symbol) \
V(_, class_fields_symbol) \
V(_, class_positions_symbol) \
V(_, detailed_stack_trace_symbol) \
V(_, elements_transition_symbol) \
V(_, error_end_pos_symbol) \
V(_, error_script_symbol) \
V(_, error_start_pos_symbol) \
V(_, frozen_symbol) \
V(_, generic_symbol) \
V(_, home_object_symbol) \
V(_, interpreter_trampoline_symbol) \
V(_, megamorphic_symbol) \
V(_, native_context_index_symbol) \
V(_, nonextensible_symbol) \
V(_, not_mapped_symbol) \
V(_, promise_debug_marker_symbol) \
V(_, promise_forwarding_handler_symbol) \
V(_, promise_handled_by_symbol) \
V(_, regexp_result_cached_indices_or_match_info_symbol) \
V(_, regexp_result_names_symbol) \
V(_, sealed_symbol) \
V(_, stack_trace_symbol) \
V(_, strict_function_transition_symbol) \
V(_, wasm_exception_tag_symbol) \
V(_, wasm_exception_values_symbol) \
#define PRIVATE_SYMBOL_LIST_GENERATOR(V, _) \
V(_, call_site_frame_array_symbol) \
V(_, call_site_frame_index_symbol) \
V(_, console_context_id_symbol) \
V(_, console_context_name_symbol) \
V(_, class_fields_symbol) \
V(_, class_positions_symbol) \
V(_, detailed_stack_trace_symbol) \
V(_, elements_transition_symbol) \
V(_, error_end_pos_symbol) \
V(_, error_script_symbol) \
V(_, error_start_pos_symbol) \
V(_, frozen_symbol) \
V(_, generic_symbol) \
V(_, home_object_symbol) \
V(_, interpreter_trampoline_symbol) \
V(_, megamorphic_symbol) \
V(_, native_context_index_symbol) \
V(_, nonextensible_symbol) \
V(_, not_mapped_symbol) \
V(_, promise_debug_marker_symbol) \
V(_, promise_forwarding_handler_symbol) \
V(_, promise_handled_by_symbol) \
V(_, regexp_result_cached_indices_or_regexp_symbol) \
V(_, regexp_result_names_symbol) \
V(_, regexp_result_regexp_input_symbol) \
V(_, regexp_result_regexp_last_index_symbol) \
V(_, sealed_symbol) \
V(_, stack_trace_symbol) \
V(_, strict_function_transition_symbol) \
V(_, wasm_exception_tag_symbol) \
V(_, wasm_exception_values_symbol) \
V(_, uninitialized_symbol)
#define PUBLIC_SYMBOL_LIST_GENERATOR(V, _) \
......
......@@ -6,46 +6,80 @@
#include "src/objects/js-array-inl.h"
#include "src/objects/js-regexp-inl.h"
#include "src/regexp/regexp.h"
namespace v8 {
namespace internal {
Handle<JSArray> JSRegExpResult::GetAndCacheIndices(
MaybeHandle<JSArray> JSRegExpResult::GetAndCacheIndices(
Isolate* isolate, Handle<JSRegExpResult> regexp_result) {
// Check for cached indices. We do a slow lookup and set of
// the cached_indices_or_match_info and names fields just in
// case they have been migrated to dictionaries.
Handle<Object> indices_or_match_info(
GetProperty(isolate, regexp_result,
isolate->factory()
->regexp_result_cached_indices_or_match_info_symbol())
Handle<Object> indices_or_regexp(
GetProperty(
isolate, regexp_result,
isolate->factory()->regexp_result_cached_indices_or_regexp_symbol())
.ToHandleChecked());
if (indices_or_match_info->IsRegExpMatchInfo()) {
if (indices_or_regexp->IsJSRegExp()) {
// 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(*indices_or_match_info), isolate);
// Grab regexp, its last_index, and the original subject string from the
// result and the re-execute the regexp to generate a new MatchInfo.
Handle<JSRegExp> regexp(JSRegExp::cast(*indices_or_regexp), isolate);
Handle<Object> input_object(
GetProperty(isolate, regexp_result,
isolate->factory()->regexp_result_regexp_input_symbol())
.ToHandleChecked());
Handle<String> subject(String::cast(*input_object), isolate);
Handle<Object> last_index_object(
GetProperty(
isolate, regexp_result,
isolate->factory()->regexp_result_regexp_last_index_symbol())
.ToHandleChecked());
int capture_count = regexp->CaptureCount();
Handle<RegExpMatchInfo> match_info =
RegExpMatchInfo::New(isolate, capture_count);
int last_index = Smi::ToInt(*last_index_object);
Handle<Object> result;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, result,
RegExp::Exec(isolate, regexp, subject, last_index, match_info),
JSArray);
DCHECK_EQ(*result, *match_info);
Handle<Object> maybe_names(
GetProperty(isolate, regexp_result,
isolate->factory()->regexp_result_names_symbol())
.ToHandleChecked());
indices_or_match_info =
indices_or_regexp =
JSRegExpResultIndices::BuildIndices(isolate, match_info, maybe_names);
// Cache the result and clear the names array.
// Cache the result and clear the names array, last_index and subject.
SetProperty(
isolate, regexp_result,
isolate->factory()->regexp_result_cached_indices_or_match_info_symbol(),
indices_or_match_info)
isolate->factory()->regexp_result_cached_indices_or_regexp_symbol(),
indices_or_regexp)
.ToHandleChecked();
SetProperty(isolate, regexp_result,
isolate->factory()->regexp_result_names_symbol(),
isolate->factory()->undefined_value())
.ToHandleChecked();
SetProperty(isolate, regexp_result,
isolate->factory()->regexp_result_regexp_last_index_symbol(),
isolate->factory()->undefined_value())
.ToHandleChecked();
SetProperty(isolate, regexp_result,
isolate->factory()->regexp_result_regexp_input_symbol(),
isolate->factory()->undefined_value())
.ToHandleChecked();
}
return Handle<JSArray>::cast(indices_or_match_info);
return Handle<JSArray>::cast(indices_or_regexp);
}
Handle<JSRegExpResultIndices> JSRegExpResultIndices::BuildIndices(
......
......@@ -239,7 +239,7 @@ class JSRegExpResult : public JSArray {
DEFINE_FIELD_OFFSET_CONSTANTS(JSArray::kHeaderSize,
TORQUE_GENERATED_JS_REG_EXP_RESULT_FIELDS)
static Handle<JSArray> GetAndCacheIndices(
static MaybeHandle<JSArray> GetAndCacheIndices(
Isolate* isolate, Handle<JSRegExpResult> regexp_result);
// Indices of in-object properties.
......@@ -248,9 +248,11 @@ class JSRegExpResult : public JSArray {
static const int kGroupsIndex = 2;
// Private internal only fields.
static const int kCachedIndicesOrMatchInfoIndex = 3;
static const int kCachedIndicesOrRegExpIndex = 3;
static const int kNamesIndex = 4;
static const int kInObjectPropertyCount = 5;
static const int kRegExpInputIndex = 5;
static const int kRegExpLastIndex = 6;
static const int kInObjectPropertyCount = 7;
OBJECT_CONSTRUCTORS(JSRegExpResult, JSArray);
};
......
......@@ -33,8 +33,10 @@ extern class JSRegExpResult extends JSArray {
groups: JSAny;
// The below fields are for internal use only.
cached_indices_or_match_info: JSRegExpResultIndices|RegExpMatchInfo;
cached_indices_or_regexp: JSRegExpResultIndices|JSRegExp;
names: FixedArray|Undefined;
regexp_input: String;
regexp_last_index: Smi;
}
@hasSameInstanceTypeAsParent
......
......@@ -4097,12 +4097,22 @@ WeakArrayList PrototypeUsers::Compact(Handle<WeakArrayList> array, Heap* heap,
return *new_array;
}
Handle<RegExpMatchInfo> RegExpMatchInfo::New(Isolate* isolate,
int capture_count) {
Handle<RegExpMatchInfo> match_info = isolate->factory()->NewRegExpMatchInfo();
return ReserveCaptures(isolate, match_info, capture_count);
}
Handle<RegExpMatchInfo> RegExpMatchInfo::ReserveCaptures(
Isolate* isolate, Handle<RegExpMatchInfo> match_info, int capture_count) {
DCHECK_GE(match_info->length(), kLastMatchOverhead);
const int required_length = kFirstCaptureIndex + capture_count;
return Handle<RegExpMatchInfo>::cast(
int capture_register_count = (capture_count + 1) * 2;
const int required_length = kFirstCaptureIndex + capture_register_count;
Handle<RegExpMatchInfo> result = Handle<RegExpMatchInfo>::cast(
EnsureSpaceInFixedArray(isolate, match_info, required_length));
result->SetNumberOfCaptureRegisters(capture_register_count);
return result;
}
// static
......
......@@ -45,6 +45,9 @@ class V8_EXPORT_PRIVATE RegExpMatchInfo : NON_EXPORTED_BASE(public FixedArray) {
inline int Capture(int i);
inline void SetCapture(int i, int value);
// Creates a new RegExpMatchInfo with space for capture_count captures.
static Handle<RegExpMatchInfo> New(Isolate* isolate, int capture_count);
// Reserves space for captures.
static Handle<RegExpMatchInfo> ReserveCaptures(
Isolate* isolate, Handle<RegExpMatchInfo> match_info, int capture_count);
......
......@@ -673,12 +673,8 @@ Handle<RegExpMatchInfo> RegExp::SetLastMatchInfo(
// regexp, RegExpExecStub finds that the match info is too small, it restarts
// execution in RegExpImpl::Exec, which finally grows the match info right
// here.
int capture_register_count = (capture_count + 1) * 2;
Handle<RegExpMatchInfo> result = RegExpMatchInfo::ReserveCaptures(
isolate, last_match_info, capture_register_count);
result->SetNumberOfCaptureRegisters(capture_register_count);
Handle<RegExpMatchInfo> result =
RegExpMatchInfo::ReserveCaptures(isolate, last_match_info, capture_count);
if (*result != *last_match_info) {
if (*last_match_info == *isolate->regexp_last_match_info()) {
// This inner condition is only needed for special situations like the
......@@ -689,6 +685,7 @@ Handle<RegExpMatchInfo> RegExp::SetLastMatchInfo(
}
}
int capture_register_count = (capture_count + 1) * 2;
DisallowHeapAllocation no_allocation;
if (match != nullptr) {
for (int i = 0; i < capture_register_count; i += 2) {
......
......@@ -2,7 +2,7 @@
// 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 --expose-gc
// Flags: --harmony-regexp-match-indices --expose-gc --stack-size=100
// Sanity test.
{
......@@ -104,6 +104,13 @@
assertEquals(m.indices.groups, {'Z': [4, 5]})
}
// Test atomic regexp.
{
const m = /undefined/.exec();
assertEquals(m.indices, [[0, 9]]);
}
// Test deleting unrelated fields does not break.
{
const m = /undefined/.exec();
......@@ -111,3 +118,20 @@
gc();
assertEquals(m.indices, [[0, 9]]);
}
// Stack overflow.
{
const re = /a+(?<Z>z)?/;
const m = re.exec("xaaaz");
function rec() {
try {
return rec();
} catch (e) {
assertEquals(m.indices, [[1, 5], [4, 5]]);
assertEquals(m.indices.groups, {'Z': [4, 5]})
return true;
}
}
assertTrue(rec());
}
......@@ -281,54 +281,54 @@ KNOWN_MAPS = {
("read_only_space", 0x01d79): (92, "EnumCacheMap"),
("read_only_space", 0x01e11): (86, "ArrayBoilerplateDescriptionMap"),
("read_only_space", 0x01ff9): (95, "InterceptorInfoMap"),
("read_only_space", 0x049a1): (71, "PromiseFulfillReactionJobTaskMap"),
("read_only_space", 0x049e9): (72, "PromiseRejectReactionJobTaskMap"),
("read_only_space", 0x04a31): (73, "CallableTaskMap"),
("read_only_space", 0x04a79): (74, "CallbackTaskMap"),
("read_only_space", 0x04ac1): (75, "PromiseResolveThenableJobTaskMap"),
("read_only_space", 0x04b09): (78, "FunctionTemplateInfoMap"),
("read_only_space", 0x04b51): (79, "ObjectTemplateInfoMap"),
("read_only_space", 0x04b99): (80, "AccessCheckInfoMap"),
("read_only_space", 0x04be1): (81, "AccessorInfoMap"),
("read_only_space", 0x04c29): (82, "AccessorPairMap"),
("read_only_space", 0x04c71): (83, "AliasedArgumentsEntryMap"),
("read_only_space", 0x04cb9): (84, "AllocationMementoMap"),
("read_only_space", 0x04d01): (87, "AsmWasmDataMap"),
("read_only_space", 0x04d49): (88, "AsyncGeneratorRequestMap"),
("read_only_space", 0x04d91): (90, "ClassPositionsMap"),
("read_only_space", 0x04dd9): (91, "DebugInfoMap"),
("read_only_space", 0x04e21): (94, "FunctionTemplateRareDataMap"),
("read_only_space", 0x04e69): (97, "InterpreterDataMap"),
("read_only_space", 0x04eb1): (98, "PromiseCapabilityMap"),
("read_only_space", 0x04ef9): (99, "PromiseReactionMap"),
("read_only_space", 0x04f41): (100, "PrototypeInfoMap"),
("read_only_space", 0x04f89): (101, "ScriptMap"),
("read_only_space", 0x04fd1): (105, "SourcePositionTableWithFrameCacheMap"),
("read_only_space", 0x05019): (106, "SourceTextModuleInfoEntryMap"),
("read_only_space", 0x05061): (107, "StackFrameInfoMap"),
("read_only_space", 0x050a9): (108, "StackTraceFrameMap"),
("read_only_space", 0x050f1): (109, "TemplateObjectDescriptionMap"),
("read_only_space", 0x05139): (110, "Tuple2Map"),
("read_only_space", 0x05181): (111, "Tuple3Map"),
("read_only_space", 0x051c9): (112, "WasmCapiFunctionDataMap"),
("read_only_space", 0x05211): (113, "WasmDebugInfoMap"),
("read_only_space", 0x05259): (114, "WasmExceptionTagMap"),
("read_only_space", 0x052a1): (115, "WasmExportedFunctionDataMap"),
("read_only_space", 0x052e9): (116, "WasmIndirectFunctionTableMap"),
("read_only_space", 0x05331): (117, "WasmJSFunctionDataMap"),
("read_only_space", 0x05379): (96, "InternalClassMap"),
("read_only_space", 0x053c1): (103, "SmiPairMap"),
("read_only_space", 0x05409): (102, "SmiBoxMap"),
("read_only_space", 0x05451): (104, "SortStateMap"),
("read_only_space", 0x05499): (85, "AllocationSiteWithWeakNextMap"),
("read_only_space", 0x054e1): (85, "AllocationSiteWithoutWeakNextMap"),
("read_only_space", 0x05529): (76, "LoadHandler1Map"),
("read_only_space", 0x05571): (76, "LoadHandler2Map"),
("read_only_space", 0x055b9): (76, "LoadHandler3Map"),
("read_only_space", 0x05601): (77, "StoreHandler0Map"),
("read_only_space", 0x05649): (77, "StoreHandler1Map"),
("read_only_space", 0x05691): (77, "StoreHandler2Map"),
("read_only_space", 0x056d9): (77, "StoreHandler3Map"),
("read_only_space", 0x049d1): (71, "PromiseFulfillReactionJobTaskMap"),
("read_only_space", 0x04a19): (72, "PromiseRejectReactionJobTaskMap"),
("read_only_space", 0x04a61): (73, "CallableTaskMap"),
("read_only_space", 0x04aa9): (74, "CallbackTaskMap"),
("read_only_space", 0x04af1): (75, "PromiseResolveThenableJobTaskMap"),
("read_only_space", 0x04b39): (78, "FunctionTemplateInfoMap"),
("read_only_space", 0x04b81): (79, "ObjectTemplateInfoMap"),
("read_only_space", 0x04bc9): (80, "AccessCheckInfoMap"),
("read_only_space", 0x04c11): (81, "AccessorInfoMap"),
("read_only_space", 0x04c59): (82, "AccessorPairMap"),
("read_only_space", 0x04ca1): (83, "AliasedArgumentsEntryMap"),
("read_only_space", 0x04ce9): (84, "AllocationMementoMap"),
("read_only_space", 0x04d31): (87, "AsmWasmDataMap"),
("read_only_space", 0x04d79): (88, "AsyncGeneratorRequestMap"),
("read_only_space", 0x04dc1): (90, "ClassPositionsMap"),
("read_only_space", 0x04e09): (91, "DebugInfoMap"),
("read_only_space", 0x04e51): (94, "FunctionTemplateRareDataMap"),
("read_only_space", 0x04e99): (97, "InterpreterDataMap"),
("read_only_space", 0x04ee1): (98, "PromiseCapabilityMap"),
("read_only_space", 0x04f29): (99, "PromiseReactionMap"),
("read_only_space", 0x04f71): (100, "PrototypeInfoMap"),
("read_only_space", 0x04fb9): (101, "ScriptMap"),
("read_only_space", 0x05001): (105, "SourcePositionTableWithFrameCacheMap"),
("read_only_space", 0x05049): (106, "SourceTextModuleInfoEntryMap"),
("read_only_space", 0x05091): (107, "StackFrameInfoMap"),
("read_only_space", 0x050d9): (108, "StackTraceFrameMap"),
("read_only_space", 0x05121): (109, "TemplateObjectDescriptionMap"),
("read_only_space", 0x05169): (110, "Tuple2Map"),
("read_only_space", 0x051b1): (111, "Tuple3Map"),
("read_only_space", 0x051f9): (112, "WasmCapiFunctionDataMap"),
("read_only_space", 0x05241): (113, "WasmDebugInfoMap"),
("read_only_space", 0x05289): (114, "WasmExceptionTagMap"),
("read_only_space", 0x052d1): (115, "WasmExportedFunctionDataMap"),
("read_only_space", 0x05319): (116, "WasmIndirectFunctionTableMap"),
("read_only_space", 0x05361): (117, "WasmJSFunctionDataMap"),
("read_only_space", 0x053a9): (96, "InternalClassMap"),
("read_only_space", 0x053f1): (103, "SmiPairMap"),
("read_only_space", 0x05439): (102, "SmiBoxMap"),
("read_only_space", 0x05481): (104, "SortStateMap"),
("read_only_space", 0x054c9): (85, "AllocationSiteWithWeakNextMap"),
("read_only_space", 0x05511): (85, "AllocationSiteWithoutWeakNextMap"),
("read_only_space", 0x05559): (76, "LoadHandler1Map"),
("read_only_space", 0x055a1): (76, "LoadHandler2Map"),
("read_only_space", 0x055e9): (76, "LoadHandler3Map"),
("read_only_space", 0x05631): (77, "StoreHandler0Map"),
("read_only_space", 0x05679): (77, "StoreHandler1Map"),
("read_only_space", 0x056c1): (77, "StoreHandler2Map"),
("read_only_space", 0x05709): (77, "StoreHandler3Map"),
("map_space", 0x00119): (1057, "ExternalMap"),
("map_space", 0x00161): (1073, "JSMessageObjectMap"),
}
......
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