Commit a27a42f5 authored by Jaroslav Sevcik's avatar Jaroslav Sevcik Committed by Commit Bot

Reland "[regexp] Introduce species constructor protector for regexps."

This is a reland of 3ca32e98

Original change's description:
> [regexp] Introduce species constructor protector for regexps.
> 
> Bug: v8:8445
> Change-Id: Iea69c65d0054b24b3f8c7234c4c556ebee2dd45f
> Reviewed-on: https://chromium-review.googlesource.com/c/1335696
> Reviewed-by: Jakob Gruber <jgruber@chromium.org>
> Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
> Commit-Queue: Jaroslav Sevcik <jarin@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#57564}

Bug: v8:8445
Change-Id: Ib79d716ff857eed95eed800b33ccb53f33d5167f
Reviewed-on: https://chromium-review.googlesource.com/c/1340286Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Commit-Queue: Jaroslav Sevcik <jarin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#57598}
parent 7b7e61c1
......@@ -2532,6 +2532,7 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
// Setup %RegExpPrototype%.
Handle<JSObject> prototype(
JSObject::cast(regexp_fun->instance_prototype()), isolate());
native_context()->set_regexp_prototype(*prototype);
{
Handle<JSFunction> fun = SimpleInstallFunction(
......
......@@ -915,6 +915,10 @@ void RegExpBuiltinsAssembler::BranchIfFastRegExp(
GotoIfForceSlowPath(if_ismodified);
// This should only be needed for String.p.(split||matchAll), but we are
// conservative here.
GotoIf(IsRegExpSpeciesProtectorCellInvalid(), if_ismodified);
Node* const native_context = LoadNativeContext(context);
Node* const regexp_fun =
LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
......
......@@ -5960,6 +5960,13 @@ TNode<BoolT> CodeStubAssembler::IsTypedArraySpeciesProtectorCellInvalid() {
return WordEqual(cell_value, invalid);
}
TNode<BoolT> CodeStubAssembler::IsRegExpSpeciesProtectorCellInvalid() {
Node* invalid = SmiConstant(Isolate::kProtectorInvalid);
Node* cell = LoadRoot(RootIndex::kRegExpSpeciesProtector);
Node* cell_value = LoadObjectField(cell, PropertyCell::kValueOffset);
return WordEqual(cell_value, invalid);
}
TNode<BoolT> CodeStubAssembler::IsPromiseSpeciesProtectorCellInvalid() {
Node* invalid = SmiConstant(Isolate::kProtectorInvalid);
Node* cell = LoadRoot(RootIndex::kPromiseSpeciesProtector);
......
......@@ -33,7 +33,8 @@ enum class PrimitiveType { kBoolean, kNumber, kString, kSymbol };
V(PromiseSpeciesProtector, promise_species_protector, \
PromiseSpeciesProtector) \
V(TypedArraySpeciesProtector, typed_array_species_protector, \
TypedArraySpeciesProtector)
TypedArraySpeciesProtector) \
V(RegExpSpeciesProtector, regexp_species_protector, RegExpSpeciesProtector)
#define HEAP_IMMUTABLE_IMMOVABLE_OBJECT_LIST(V) \
V(AccessorInfoMap, accessor_info_map, AccessorInfoMap) \
......@@ -2136,6 +2137,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
TNode<BoolT> IsPromiseThenProtectorCellInvalid();
TNode<BoolT> IsArraySpeciesProtectorCellInvalid();
TNode<BoolT> IsTypedArraySpeciesProtectorCellInvalid();
TNode<BoolT> IsRegExpSpeciesProtectorCellInvalid();
TNode<BoolT> IsPromiseSpeciesProtectorCellInvalid();
// True iff |object| is a Smi or a HeapNumber.
......
......@@ -275,6 +275,7 @@ enum ContextLookupFlags {
V(INITIAL_REGEXP_STRING_ITERATOR_PROTOTYPE_MAP_INDEX, Map, \
initial_regexp_string_iterator_prototype_map) \
V(REGEXP_RESULT_MAP_INDEX, Map, regexp_result_map) \
V(REGEXP_PROTOTYPE_INDEX, JSObject, regexp_prototype) \
V(SCRIPT_CONTEXT_TABLE_INDEX, ScriptContextTable, script_context_table) \
V(SECURITY_TOKEN_INDEX, Object, security_token) \
V(SERIALIZED_OBJECTS, FixedArray, serialized_objects) \
......
......@@ -872,6 +872,10 @@ void Heap::CreateInitialObjects() {
cell->set_value(Smi::FromInt(Isolate::kProtectorValid));
set_promise_species_protector(*cell);
cell = factory->NewPropertyCell(factory->empty_string());
cell->set_value(Smi::FromInt(Isolate::kProtectorValid));
set_regexp_species_protector(*cell);
cell = factory->NewPropertyCell(factory->empty_string());
cell->set_value(Smi::FromInt(Isolate::kProtectorValid));
set_string_iterator_protector(*cell);
......
......@@ -163,6 +163,12 @@ bool Isolate::IsTypedArraySpeciesLookupChainIntact() {
Smi::ToInt(species_cell->value()) == kProtectorValid;
}
bool Isolate::IsRegExpSpeciesLookupChainIntact() {
PropertyCell* species_cell = heap()->regexp_species_protector();
return species_cell->value()->IsSmi() &&
Smi::ToInt(species_cell->value()) == kProtectorValid;
}
bool Isolate::IsPromiseSpeciesLookupChainIntact() {
PropertyCell* species_cell = heap()->promise_species_protector();
return species_cell->value()->IsSmi() &&
......
......@@ -3793,6 +3793,15 @@ void Isolate::InvalidateTypedArraySpeciesProtector() {
DCHECK(!IsTypedArraySpeciesLookupChainIntact());
}
void Isolate::InvalidateRegExpSpeciesProtector() {
DCHECK(factory()->regexp_species_protector()->value()->IsSmi());
DCHECK(IsRegExpSpeciesLookupChainIntact());
PropertyCell::SetValueWithInvalidation(
this, factory()->regexp_species_protector(),
handle(Smi::FromInt(kProtectorInvalid), this));
DCHECK(!IsRegExpSpeciesLookupChainIntact());
}
void Isolate::InvalidatePromiseSpeciesProtector() {
DCHECK(factory()->promise_species_protector()->value()->IsSmi());
DCHECK(IsPromiseSpeciesLookupChainIntact());
......
......@@ -1225,6 +1225,7 @@ class Isolate final : private HiddenFactory {
inline bool IsArraySpeciesLookupChainIntact();
inline bool IsTypedArraySpeciesLookupChainIntact();
inline bool IsRegExpSpeciesLookupChainIntact();
inline bool IsPromiseSpeciesLookupChainIntact();
bool IsIsConcatSpreadableLookupChainIntact();
bool IsIsConcatSpreadableLookupChainIntact(JSReceiver* receiver);
......@@ -1303,6 +1304,7 @@ class Isolate final : private HiddenFactory {
void InvalidateArrayConstructorProtector();
void InvalidateArraySpeciesProtector();
void InvalidateTypedArraySpeciesProtector();
void InvalidateRegExpSpeciesProtector();
void InvalidatePromiseSpeciesProtector();
void InvalidateIsConcatSpreadableProtector();
void InvalidateStringLengthOverflowProtector();
......
......@@ -279,9 +279,11 @@ void LookupIterator::InternalUpdateProtector() {
ReadOnlyRoots roots(heap());
if (*name_ == roots.constructor_string()) {
if (!isolate_->IsArraySpeciesLookupChainIntact() &&
!isolate_->IsTypedArraySpeciesLookupChainIntact() &&
!isolate_->IsPromiseSpeciesLookupChainIntact())
!isolate_->IsPromiseSpeciesLookupChainIntact() &&
!isolate_->IsRegExpSpeciesLookupChainIntact() &&
!isolate_->IsTypedArraySpeciesLookupChainIntact()) {
return;
}
// Setting the constructor property could change an instance's @@species
if (holder_->IsJSArray()) {
if (!isolate_->IsArraySpeciesLookupChainIntact()) return;
......@@ -293,6 +295,10 @@ void LookupIterator::InternalUpdateProtector() {
if (!isolate_->IsPromiseSpeciesLookupChainIntact()) return;
isolate_->InvalidatePromiseSpeciesProtector();
return;
} else if (holder_->IsJSRegExp()) {
if (!isolate_->IsRegExpSpeciesLookupChainIntact()) return;
isolate_->InvalidateRegExpSpeciesProtector();
return;
} else if (holder_->IsJSTypedArray()) {
if (!isolate_->IsTypedArraySpeciesLookupChainIntact()) return;
isolate_->InvalidateTypedArraySpeciesProtector();
......@@ -300,9 +306,8 @@ void LookupIterator::InternalUpdateProtector() {
}
if (holder_->map()->is_prototype_map()) {
DisallowHeapAllocation no_gc;
// Setting the constructor of Array.prototype, Promise.prototype or
// %TypedArray%.prototype of any realm also needs to invalidate the
// @@species protector.
// Setting the constructor of any prototype with the @@species protector
// (of any realm) also needs to invalidate the protector.
// For typed arrays, we check a prototype of this holder since TypedArrays
// have different prototypes for each type, and their parent prototype is
// pointing the same TYPED_ARRAY_PROTOTYPE.
......@@ -316,6 +321,10 @@ void LookupIterator::InternalUpdateProtector() {
Context::PROMISE_PROTOTYPE_INDEX)) {
if (!isolate_->IsPromiseSpeciesLookupChainIntact()) return;
isolate_->InvalidatePromiseSpeciesProtector();
} else if (isolate_->IsInAnyContext(*holder_,
Context::REGEXP_PROTOTYPE_INDEX)) {
if (!isolate_->IsRegExpSpeciesLookupChainIntact()) return;
isolate_->InvalidateRegExpSpeciesProtector();
} else if (isolate_->IsInAnyContext(
holder_->map()->prototype(),
Context::TYPED_ARRAY_PROTOTYPE_INDEX)) {
......@@ -348,9 +357,11 @@ void LookupIterator::InternalUpdateProtector() {
}
} else if (*name_ == roots.species_symbol()) {
if (!isolate_->IsArraySpeciesLookupChainIntact() &&
!isolate_->IsTypedArraySpeciesLookupChainIntact() &&
!isolate_->IsPromiseSpeciesLookupChainIntact())
!isolate_->IsPromiseSpeciesLookupChainIntact() &&
!isolate_->IsRegExpSpeciesLookupChainIntact() &&
!isolate_->IsTypedArraySpeciesLookupChainIntact()) {
return;
}
// Setting the Symbol.species property of any Array, Promise or TypedArray
// constructor invalidates the @@species protector
if (isolate_->IsInAnyContext(*holder_, Context::ARRAY_FUNCTION_INDEX)) {
......@@ -362,6 +373,10 @@ void LookupIterator::InternalUpdateProtector() {
Context::PROMISE_FUNCTION_INDEX)) {
if (!isolate_->IsPromiseSpeciesLookupChainIntact()) return;
isolate_->InvalidatePromiseSpeciesProtector();
} else if (isolate_->IsInAnyContext(*holder_,
Context::REGEXP_FUNCTION_INDEX)) {
if (!isolate_->IsRegExpSpeciesLookupChainIntact()) return;
isolate_->InvalidateRegExpSpeciesProtector();
} else if (IsTypedArrayFunctionInAnyContext(isolate_, *holder_)) {
if (!isolate_->IsTypedArraySpeciesLookupChainIntact()) return;
isolate_->InvalidateTypedArraySpeciesProtector();
......
......@@ -172,6 +172,8 @@ bool RegExpUtils::IsUnmodifiedRegExp(Isolate* isolate, Handle<Object> obj) {
}
}
if (!isolate->IsRegExpSpeciesLookupChainIntact()) return false;
// The smi check is required to omit ToLength(lastIndex) calls with possible
// user-code execution on the fast path.
Object* last_index = JSRegExp::cast(recv)->last_index();
......
......@@ -238,6 +238,7 @@ class RootVisitor;
V(Cell*, is_concat_spreadable_protector, IsConcatSpreadableProtector) \
V(PropertyCell*, array_species_protector, ArraySpeciesProtector) \
V(PropertyCell*, typed_array_species_protector, TypedArraySpeciesProtector) \
V(PropertyCell*, regexp_species_protector, RegExpSpeciesProtector) \
V(PropertyCell*, promise_species_protector, PromiseSpeciesProtector) \
V(Cell*, string_length_protector, StringLengthProtector) \
V(PropertyCell*, array_iterator_protector, ArrayIteratorProtector) \
......
// Copyright 2018 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.
/a/.constructor = "";
assertEquals("b", "a".replace(/a/, () => "b"));
// Copyright 2018 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: --allow-natives-syntax --harmony-string-matchall
class MyRegExp {
exec() { return null; }
}
var r = /c/;
assertEquals(["ab", ""], "abc".split(r));
assertEquals([["c"]], [..."c".matchAll(r)]);
r.constructor = { [Symbol.species] : MyRegExp };
assertEquals(["abc"], "abc".split(r));
assertEquals([], [..."c".matchAll(r)]);
assertEquals(["ab", ""], "abc".split(/c/));
assertEquals([["c"]], [..."c".matchAll(/c/)]);
RegExp.prototype.constructor = { [Symbol.species] : MyRegExp };
assertEquals(["abc"], "abc".split(/c/));
assertEquals([], [..."c".matchAll(/c/)]);
// Copyright 2018 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: --allow-natives-syntax --harmony-string-matchall
class MyRegExp {
exec() { return null; }
}
assertEquals(["ab", ""], "abc".split(/c/));
assertEquals([["a"]], [..."a".matchAll(/a/)]);
Object.defineProperty(RegExp, Symbol.species, { get() { return MyRegExp; }});
assertEquals(["abc"], "abc".split(/c/));
assertEquals([], [..."a".matchAll(/a/)]);
......@@ -412,21 +412,22 @@ KNOWN_OBJECTS = {
("OLD_SPACE", 0x006e1): "IsConcatSpreadableProtector",
("OLD_SPACE", 0x006f1): "ArraySpeciesProtector",
("OLD_SPACE", 0x00719): "TypedArraySpeciesProtector",
("OLD_SPACE", 0x00741): "PromiseSpeciesProtector",
("OLD_SPACE", 0x00769): "StringLengthProtector",
("OLD_SPACE", 0x00779): "ArrayIteratorProtector",
("OLD_SPACE", 0x007a1): "ArrayBufferNeuteringProtector",
("OLD_SPACE", 0x007c9): "PromiseHookProtector",
("OLD_SPACE", 0x007f1): "PromiseResolveProtector",
("OLD_SPACE", 0x00801): "MapIteratorProtector",
("OLD_SPACE", 0x00829): "PromiseThenProtector",
("OLD_SPACE", 0x00851): "SetIteratorProtector",
("OLD_SPACE", 0x00879): "StringIteratorProtector",
("OLD_SPACE", 0x008a1): "SingleCharacterStringCache",
("OLD_SPACE", 0x010b1): "StringSplitCache",
("OLD_SPACE", 0x018c1): "RegExpMultipleCache",
("OLD_SPACE", 0x020d1): "DefaultMicrotaskQueue",
("OLD_SPACE", 0x020e9): "BuiltinsConstantsTable",
("OLD_SPACE", 0x00741): "RegExpSpeciesProtector",
("OLD_SPACE", 0x00769): "PromiseSpeciesProtector",
("OLD_SPACE", 0x00791): "StringLengthProtector",
("OLD_SPACE", 0x007a1): "ArrayIteratorProtector",
("OLD_SPACE", 0x007c9): "ArrayBufferNeuteringProtector",
("OLD_SPACE", 0x007f1): "PromiseHookProtector",
("OLD_SPACE", 0x00819): "PromiseResolveProtector",
("OLD_SPACE", 0x00829): "MapIteratorProtector",
("OLD_SPACE", 0x00851): "PromiseThenProtector",
("OLD_SPACE", 0x00879): "SetIteratorProtector",
("OLD_SPACE", 0x008a1): "StringIteratorProtector",
("OLD_SPACE", 0x008c9): "SingleCharacterStringCache",
("OLD_SPACE", 0x010d9): "StringSplitCache",
("OLD_SPACE", 0x018e9): "RegExpMultipleCache",
("OLD_SPACE", 0x020f9): "DefaultMicrotaskQueue",
("OLD_SPACE", 0x02111): "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