Commit fa2bed3f authored by Jakob Gruber's avatar Jakob Gruber Committed by Commit Bot

[regexp] Move the RegExpSpeciesProtector to the native context

Prior to this CL, it was possible to pollute another context's
fast/slow-path state for RegExp builtins due to the species protector
being per-isolate rather than per-context. Among other things, this
means that iframes can slow down the main site, and slowdowns persist
across page reloads and navigation within the same tab.

This CL thus moves the RegExpSpeciesProtector to the native context.

The same should be done for all other protectors in the future.

Bug: chromium:977382, v8:5577, v8:9463
Change-Id: I577f470229cb9dfcd4a88c20b1b9111c65a9b85f
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1695465
Auto-Submit: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#62631}
parent a61ce8a0
......@@ -928,9 +928,9 @@ void RegExpBuiltinsAssembler::BranchIfFastRegExp(
// This should only be needed for String.p.(split||matchAll), but we are
// conservative here.
GotoIf(IsRegExpSpeciesProtectorCellInvalid(), if_ismodified);
TNode<Context> native_context = LoadNativeContext(context);
GotoIf(IsRegExpSpeciesProtectorCellInvalid(native_context), if_ismodified);
Node* const native_context = LoadNativeContext(context);
Node* const regexp_fun =
LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
Node* const initial_map =
......
......@@ -6098,10 +6098,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);
TNode<BoolT> CodeStubAssembler::IsRegExpSpeciesProtectorCellInvalid(
TNode<Context> native_context) {
CSA_ASSERT(this, IsNativeContext(native_context));
TNode<PropertyCell> cell = CAST(LoadContextElement(
native_context, Context::REGEXP_SPECIES_PROTECTOR_INDEX));
TNode<Object> cell_value = LoadObjectField(cell, PropertyCell::kValueOffset);
TNode<Smi> invalid = SmiConstant(Isolate::kProtectorInvalid);
return WordEqual(cell_value, invalid);
}
......
......@@ -39,7 +39,6 @@ enum class PrimitiveType { kBoolean, kNumber, kString, kSymbol };
PromiseSpeciesProtector) \
V(TypedArraySpeciesProtector, typed_array_species_protector, \
TypedArraySpeciesProtector) \
V(RegExpSpeciesProtector, regexp_species_protector, RegExpSpeciesProtector)
#define HEAP_IMMUTABLE_IMMOVABLE_OBJECT_LIST(V) \
V(AccessorInfoMap, accessor_info_map, AccessorInfoMap) \
......@@ -2321,7 +2320,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TNode<BoolT> IsPromiseThenProtectorCellInvalid();
TNode<BoolT> IsArraySpeciesProtectorCellInvalid();
TNode<BoolT> IsTypedArraySpeciesProtectorCellInvalid();
TNode<BoolT> IsRegExpSpeciesProtectorCellInvalid();
TNode<BoolT> IsRegExpSpeciesProtectorCellInvalid(
TNode<Context> native_context);
TNode<BoolT> IsPromiseSpeciesProtectorCellInvalid();
TNode<BoolT> IsMockArrayBufferAllocatorFlag() {
......
......@@ -145,9 +145,10 @@ bool Isolate::IsTypedArraySpeciesLookupChainIntact() {
Smi::ToInt(species_cell.value()) == kProtectorValid;
}
bool Isolate::IsRegExpSpeciesLookupChainIntact() {
PropertyCell species_cell =
PropertyCell::cast(root(RootIndex::kRegExpSpeciesProtector));
bool Isolate::IsRegExpSpeciesLookupChainIntact(
Handle<NativeContext> native_context) {
DCHECK_EQ(*native_context, this->raw_native_context());
PropertyCell species_cell = native_context->regexp_species_protector();
return species_cell.value().IsSmi() &&
Smi::ToInt(species_cell.value()) == kProtectorValid;
}
......
......@@ -3979,13 +3979,16 @@ void Isolate::InvalidateTypedArraySpeciesProtector() {
DCHECK(!IsTypedArraySpeciesLookupChainIntact());
}
void Isolate::InvalidateRegExpSpeciesProtector() {
DCHECK(factory()->regexp_species_protector()->value().IsSmi());
DCHECK(IsRegExpSpeciesLookupChainIntact());
void Isolate::InvalidateRegExpSpeciesProtector(
Handle<NativeContext> native_context) {
DCHECK_EQ(*native_context, this->raw_native_context());
DCHECK(native_context->regexp_species_protector().value().IsSmi());
DCHECK(IsRegExpSpeciesLookupChainIntact(native_context));
Handle<PropertyCell> species_cell(native_context->regexp_species_protector(),
this);
PropertyCell::SetValueWithInvalidation(
this, factory()->regexp_species_protector(),
handle(Smi::FromInt(kProtectorInvalid), this));
DCHECK(!IsRegExpSpeciesLookupChainIntact());
this, species_cell, handle(Smi::FromInt(kProtectorInvalid), this));
DCHECK(!IsRegExpSpeciesLookupChainIntact(native_context));
}
void Isolate::InvalidatePromiseSpeciesProtector() {
......
......@@ -1176,7 +1176,8 @@ class Isolate final : private HiddenFactory {
inline bool IsArraySpeciesLookupChainIntact();
inline bool IsTypedArraySpeciesLookupChainIntact();
inline bool IsRegExpSpeciesLookupChainIntact();
inline bool IsRegExpSpeciesLookupChainIntact(
Handle<NativeContext> native_context);
// Check that the @@species protector is intact, which guards the lookup of
// "constructor" on JSPromise instances, whose [[Prototype]] is the initial
......@@ -1261,7 +1262,7 @@ class Isolate final : private HiddenFactory {
void InvalidateArrayConstructorProtector();
void InvalidateArraySpeciesProtector();
void InvalidateTypedArraySpeciesProtector();
void InvalidateRegExpSpeciesProtector();
void InvalidateRegExpSpeciesProtector(Handle<NativeContext> native_context);
void InvalidatePromiseSpeciesProtector();
void InvalidateIsConcatSpreadableProtector();
void InvalidateStringLengthOverflowProtector();
......
......@@ -874,10 +874,6 @@ 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);
......
......@@ -2597,6 +2597,14 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
Handle<RegExpMatchInfo> last_match_info = factory->NewRegExpMatchInfo();
native_context()->set_regexp_last_match_info(*last_match_info);
// Install the species protector cell.
{
Handle<PropertyCell> cell =
factory->NewPropertyCell(factory->empty_string());
cell->set_value(Smi::FromInt(Isolate::kProtectorValid));
native_context()->set_regexp_species_protector(*cell);
}
// Force the RegExp constructor to fast properties, so that we can use the
// fast paths for various things like
//
......
......@@ -238,11 +238,12 @@ enum ContextLookupFlags {
V(REGEXP_EXEC_FUNCTION_INDEX, JSFunction, regexp_exec_function) \
V(REGEXP_FUNCTION_INDEX, JSFunction, regexp_function) \
V(REGEXP_LAST_MATCH_INFO_INDEX, RegExpMatchInfo, regexp_last_match_info) \
V(REGEXP_PROTOTYPE_INDEX, JSObject, regexp_prototype) \
V(REGEXP_PROTOTYPE_MAP_INDEX, Map, regexp_prototype_map) \
V(REGEXP_RESULT_MAP_INDEX, Map, regexp_result_map) \
V(REGEXP_SPECIES_PROTECTOR_INDEX, PropertyCell, regexp_species_protector) \
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(SCRIPT_EXECUTION_CALLBACK_INDEX, Object, script_execution_callback) \
V(SECURITY_TOKEN_INDEX, Object, security_token) \
......
......@@ -233,11 +233,13 @@ bool IsTypedArrayFunctionInAnyContext(Isolate* isolate, JSReceiver holder) {
void LookupIterator::InternalUpdateProtector() {
if (isolate_->bootstrapper()->IsActive()) return;
Handle<NativeContext> native_context = isolate_->native_context();
ReadOnlyRoots roots(isolate_);
if (*name_ == roots.constructor_string()) {
if (!isolate_->IsArraySpeciesLookupChainIntact() &&
!isolate_->IsPromiseSpeciesLookupChainIntact() &&
!isolate_->IsRegExpSpeciesLookupChainIntact() &&
!isolate_->IsRegExpSpeciesLookupChainIntact(native_context) &&
!isolate_->IsTypedArraySpeciesLookupChainIntact()) {
return;
}
......@@ -253,8 +255,8 @@ void LookupIterator::InternalUpdateProtector() {
isolate_->InvalidatePromiseSpeciesProtector();
return;
} else if (holder_->IsJSRegExp(isolate_)) {
if (!isolate_->IsRegExpSpeciesLookupChainIntact()) return;
isolate_->InvalidateRegExpSpeciesProtector();
if (!isolate_->IsRegExpSpeciesLookupChainIntact(native_context)) return;
isolate_->InvalidateRegExpSpeciesProtector(native_context);
return;
} else if (holder_->IsJSTypedArray(isolate_)) {
if (!isolate_->IsTypedArraySpeciesLookupChainIntact()) return;
......@@ -280,8 +282,8 @@ void LookupIterator::InternalUpdateProtector() {
isolate_->InvalidatePromiseSpeciesProtector();
} else if (isolate_->IsInAnyContext(*holder_,
Context::REGEXP_PROTOTYPE_INDEX)) {
if (!isolate_->IsRegExpSpeciesLookupChainIntact()) return;
isolate_->InvalidateRegExpSpeciesProtector();
if (!isolate_->IsRegExpSpeciesLookupChainIntact(native_context)) return;
isolate_->InvalidateRegExpSpeciesProtector(native_context);
} else if (isolate_->IsInAnyContext(
holder_->map(isolate_).prototype(isolate_),
Context::TYPED_ARRAY_PROTOTYPE_INDEX)) {
......@@ -315,7 +317,7 @@ void LookupIterator::InternalUpdateProtector() {
} else if (*name_ == roots.species_symbol()) {
if (!isolate_->IsArraySpeciesLookupChainIntact() &&
!isolate_->IsPromiseSpeciesLookupChainIntact() &&
!isolate_->IsRegExpSpeciesLookupChainIntact() &&
!isolate_->IsRegExpSpeciesLookupChainIntact(native_context) &&
!isolate_->IsTypedArraySpeciesLookupChainIntact()) {
return;
}
......@@ -332,8 +334,8 @@ void LookupIterator::InternalUpdateProtector() {
isolate_->InvalidatePromiseSpeciesProtector();
} else if (isolate_->IsInAnyContext(*holder_,
Context::REGEXP_FUNCTION_INDEX)) {
if (!isolate_->IsRegExpSpeciesLookupChainIntact()) return;
isolate_->InvalidateRegExpSpeciesProtector();
if (!isolate_->IsRegExpSpeciesLookupChainIntact(native_context)) return;
isolate_->InvalidateRegExpSpeciesProtector(native_context);
} else if (IsTypedArrayFunctionInAnyContext(isolate_, *holder_)) {
if (!isolate_->IsTypedArraySpeciesLookupChainIntact()) return;
isolate_->InvalidateTypedArraySpeciesProtector();
......
......@@ -179,7 +179,9 @@ bool RegExpUtils::IsUnmodifiedRegExp(Isolate* isolate, Handle<Object> obj) {
return false;
}
if (!isolate->IsRegExpSpeciesLookupChainIntact()) return false;
if (!isolate->IsRegExpSpeciesLookupChainIntact(isolate->native_context())) {
return false;
}
// The smi check is required to omit ToLength(lastIndex) calls with possible
// user-code execution on the fast path.
......
......@@ -219,7 +219,6 @@ class Symbol;
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) \
......
......@@ -409,21 +409,20 @@ KNOWN_OBJECTS = {
("old_space", 0x006e9): "IsConcatSpreadableProtector",
("old_space", 0x006f9): "ArraySpeciesProtector",
("old_space", 0x00721): "TypedArraySpeciesProtector",
("old_space", 0x00749): "RegExpSpeciesProtector",
("old_space", 0x00771): "PromiseSpeciesProtector",
("old_space", 0x00799): "StringLengthProtector",
("old_space", 0x007a9): "ArrayIteratorProtector",
("old_space", 0x007d1): "ArrayBufferDetachingProtector",
("old_space", 0x007f9): "PromiseHookProtector",
("old_space", 0x00821): "PromiseResolveProtector",
("old_space", 0x00831): "MapIteratorProtector",
("old_space", 0x00859): "PromiseThenProtector",
("old_space", 0x00881): "SetIteratorProtector",
("old_space", 0x008a9): "StringIteratorProtector",
("old_space", 0x008d1): "SingleCharacterStringCache",
("old_space", 0x010e1): "StringSplitCache",
("old_space", 0x018f1): "RegExpMultipleCache",
("old_space", 0x02101): "BuiltinsConstantsTable",
("old_space", 0x00749): "PromiseSpeciesProtector",
("old_space", 0x00771): "StringLengthProtector",
("old_space", 0x00781): "ArrayIteratorProtector",
("old_space", 0x007a9): "ArrayBufferDetachingProtector",
("old_space", 0x007d1): "PromiseHookProtector",
("old_space", 0x007f9): "PromiseResolveProtector",
("old_space", 0x00809): "MapIteratorProtector",
("old_space", 0x00831): "PromiseThenProtector",
("old_space", 0x00859): "SetIteratorProtector",
("old_space", 0x00881): "StringIteratorProtector",
("old_space", 0x008a9): "SingleCharacterStringCache",
("old_space", 0x010b9): "StringSplitCache",
("old_space", 0x018c9): "RegExpMultipleCache",
("old_space", 0x020d9): "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