[builtins] Separate species protectors for Array, TypedArray, Promise

Previously, there was one species protector for Array, TypedArray and
Promise. This CL splits the protector in three separate ones. This means
that invalidating one of them does not have negative performance
implications for the other ones.

Bug: chromium:835347, v8:7340
Change-Id: Id84aa0071f17096192965264eb60ddadd1e8e73f
Reviewed-on: https://chromium-review.googlesource.com/1023408
Commit-Queue: Sigurd Schneider <sigurds@chromium.org>
Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#52733}
This commit is contained in:
Sigurd Schneider 2018-04-23 15:58:02 +02:00 committed by Commit Bot
parent c823ca959e
commit 5728b3fbc5
30 changed files with 248 additions and 136 deletions

View File

@ -20,7 +20,7 @@ module array {
let map: Map = a.map;
if (!IsPrototypeInitialArrayPrototype(context, map)) goto Bailout;
if (IsNoElementsProtectorCellInvalid()) goto Bailout;
if (IsSpeciesProtectorCellInvalid()) goto Bailout;
if (IsArraySpeciesProtectorCellInvalid()) goto Bailout;
// Fast path only works on fast elements kind and with writable length.
let elementsKind: int32 = EnsureArrayPushable(map) otherwise Bailout;

View File

@ -199,7 +199,9 @@ extern macro BranchIfNotFastJSArray(Object, Context): never labels True, False;
extern macro IsPrototypeInitialArrayPrototype(Context, Map): bit;
extern macro IsNoElementsProtectorCellInvalid(): bit;
extern macro IsSpeciesProtectorCellInvalid(): bit;
extern macro IsArraySpeciesProtectorCellInvalid(): bit;
extern macro IsTypedArraySpeciesProtectorCellInvalid(): bit;
extern macro IsPromiseSpeciesProtectorCellInvalid(): bit;
extern operator '.elements_kind' macro LoadMapElementsKind(Map): int32;

View File

@ -817,7 +817,7 @@ Node* ArrayBuiltinsAssembler::FindProcessor(Node* k_value, Node* k) {
GotoIfNot(IsPrototypeInitialArrayPrototype(context(), original_map),
&runtime);
Node* species_protector = SpeciesProtectorConstant();
Node* species_protector = ArraySpeciesProtectorConstant();
Node* value =
LoadObjectField(species_protector, PropertyCell::kValueOffset);
TNode<Smi> const protector_invalid =
@ -862,7 +862,7 @@ Node* ArrayBuiltinsAssembler::FindProcessor(Node* k_value, Node* k) {
GotoIfNot(IsPrototypeInitialArrayPrototype(context(), original_map),
&runtime);
Node* species_protector = SpeciesProtectorConstant();
Node* species_protector = ArraySpeciesProtectorConstant();
Node* value =
LoadObjectField(species_protector, PropertyCell::kValueOffset);
Node* const protector_invalid = SmiConstant(Isolate::kProtectorInvalid);
@ -1155,7 +1155,7 @@ class ArrayPrototypeSliceCodeStubAssembler : public CodeStubAssembler {
GotoIf(IsNoElementsProtectorCellInvalid(), slow);
GotoIf(IsSpeciesProtectorCellInvalid(), slow);
GotoIf(IsArraySpeciesProtectorCellInvalid(), slow);
// Bailout if receiver has slow elements.
Node* elements_kind = LoadMapElementsKind(map);

View File

@ -247,7 +247,7 @@ BUILTIN(ArraySplice) {
// If this is a subclass of Array, then call out to JS.
!Handle<JSArray>::cast(receiver)->HasArrayPrototype(isolate) ||
// If anything with @@species has been messed with, call out to JS.
!isolate->IsSpeciesLookupChainIntact())) {
!isolate->IsArraySpeciesLookupChainIntact())) {
return CallJsIntrinsic(isolate, isolate->array_splice(), args);
}
Handle<JSArray> array = Handle<JSArray>::cast(receiver);
@ -1123,7 +1123,7 @@ BUILTIN(ArrayConcat) {
// Avoid a real species read to avoid extra lookups to the array constructor
if (V8_LIKELY(receiver->IsJSArray() &&
Handle<JSArray>::cast(receiver)->HasArrayPrototype(isolate) &&
isolate->IsSpeciesLookupChainIntact())) {
isolate->IsArraySpeciesLookupChainIntact())) {
if (Fast_ArrayConcat(isolate, &args).ToHandle(&result_array)) {
return *result_array;
}

View File

@ -635,7 +635,7 @@ void PromiseBuiltinsAssembler::BranchIfPromiseSpeciesLookupChainIntact(
GotoIfForceSlowPath(if_slow);
GotoIfNot(WordEqual(LoadMapPrototype(promise_map), promise_prototype),
if_slow);
Branch(IsSpeciesProtectorCellInvalid(), if_slow, if_fast);
Branch(IsPromiseSpeciesProtectorCellInvalid(), if_slow, if_fast);
}
void PromiseBuiltinsAssembler::BranchIfPromiseThenLookupChainIntact(
@ -1285,7 +1285,7 @@ TF_BUILTIN(PromiseResolve, PromiseBuiltinsAssembler) {
LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_INDEX);
GotoIfNot(WordEqual(LoadMapPrototype(value_map), promise_prototype),
&if_slow_constructor);
GotoIf(IsSpeciesProtectorCellInvalid(), &if_slow_constructor);
GotoIf(IsPromiseSpeciesProtectorCellInvalid(), &if_slow_constructor);
// If the {constructor} is the Promise function, we just immediately
// return the {value} here and don't bother wrapping it into a

View File

@ -883,7 +883,7 @@ TNode<Object> TypedArrayBuiltinsAssembler::TypedArraySpeciesConstructor(
var_constructor = default_constructor;
Node* map = LoadMap(exemplar);
GotoIfNot(IsPrototypeTypedArrayPrototype(context, map), &slow);
Branch(IsSpeciesProtectorCellInvalid(), &slow, &done);
Branch(IsTypedArraySpeciesProtectorCellInvalid(), &slow, &done);
BIND(&slow);
var_constructor = SpeciesConstructor(context, exemplar, default_constructor);

View File

@ -991,7 +991,7 @@ void CodeStubAssembler::BranchIfFastJSArray(Node* object, Node* context,
void CodeStubAssembler::BranchIfFastJSArrayForCopy(Node* object, Node* context,
Label* if_true,
Label* if_false) {
GotoIf(IsSpeciesProtectorCellInvalid(), if_false);
GotoIf(IsArraySpeciesProtectorCellInvalid(), if_false);
BranchIfFastJSArray(object, context, if_true, if_false);
}
@ -4545,9 +4545,23 @@ Node* CodeStubAssembler::IsPromiseThenProtectorCellInvalid() {
return WordEqual(cell_value, invalid);
}
Node* CodeStubAssembler::IsSpeciesProtectorCellInvalid() {
Node* CodeStubAssembler::IsArraySpeciesProtectorCellInvalid() {
Node* invalid = SmiConstant(Isolate::kProtectorInvalid);
Node* cell = LoadRoot(Heap::kSpeciesProtectorRootIndex);
Node* cell = LoadRoot(Heap::kArraySpeciesProtectorRootIndex);
Node* cell_value = LoadObjectField(cell, PropertyCell::kValueOffset);
return WordEqual(cell_value, invalid);
}
Node* CodeStubAssembler::IsTypedArraySpeciesProtectorCellInvalid() {
Node* invalid = SmiConstant(Isolate::kProtectorInvalid);
Node* cell = LoadRoot(Heap::kTypedArraySpeciesProtectorRootIndex);
Node* cell_value = LoadObjectField(cell, PropertyCell::kValueOffset);
return WordEqual(cell_value, invalid);
}
Node* CodeStubAssembler::IsPromiseSpeciesProtectorCellInvalid() {
Node* invalid = SmiConstant(Isolate::kProtectorInvalid);
Node* cell = LoadRoot(Heap::kPromiseSpeciesProtectorRootIndex);
Node* cell_value = LoadObjectField(cell, PropertyCell::kValueOffset);
return WordEqual(cell_value, invalid);
}

View File

@ -22,50 +22,54 @@ class StubCache;
enum class PrimitiveType { kBoolean, kNumber, kString, kSymbol };
#define HEAP_CONSTANT_LIST(V) \
V(AccessorInfoMap, accessor_info_map, AccessorInfoMap) \
V(AccessorPairMap, accessor_pair_map, AccessorPairMap) \
V(AllocationSiteMap, allocation_site_map, AllocationSiteMap) \
V(BooleanMap, boolean_map, BooleanMap) \
V(CodeMap, code_map, CodeMap) \
V(EmptyPropertyDictionary, empty_property_dictionary, \
EmptyPropertyDictionary) \
V(EmptyFixedArray, empty_fixed_array, EmptyFixedArray) \
V(EmptySlowElementDictionary, empty_slow_element_dictionary, \
EmptySlowElementDictionary) \
V(empty_string, empty_string, EmptyString) \
V(EmptyWeakCell, empty_weak_cell, EmptyWeakCell) \
V(FalseValue, false_value, False) \
V(FeedbackVectorMap, feedback_vector_map, FeedbackVectorMap) \
V(FixedArrayMap, fixed_array_map, FixedArrayMap) \
V(FixedCOWArrayMap, fixed_cow_array_map, FixedCOWArrayMap) \
V(FixedDoubleArrayMap, fixed_double_array_map, FixedDoubleArrayMap) \
V(FunctionTemplateInfoMap, function_template_info_map, \
FunctionTemplateInfoMap) \
V(GlobalPropertyCellMap, global_property_cell_map, PropertyCellMap) \
V(has_instance_symbol, has_instance_symbol, HasInstanceSymbol) \
V(HeapNumberMap, heap_number_map, HeapNumberMap) \
V(iterator_symbol, iterator_symbol, IteratorSymbol) \
V(length_string, length_string, LengthString) \
V(ManyClosuresCellMap, many_closures_cell_map, ManyClosuresCellMap) \
V(MetaMap, meta_map, MetaMap) \
V(MinusZeroValue, minus_zero_value, MinusZero) \
V(MutableHeapNumberMap, mutable_heap_number_map, MutableHeapNumberMap) \
V(NanValue, nan_value, Nan) \
V(NoClosuresCellMap, no_closures_cell_map, NoClosuresCellMap) \
V(NullValue, null_value, Null) \
V(OneClosureCellMap, one_closure_cell_map, OneClosureCellMap) \
V(prototype_string, prototype_string, PrototypeString) \
V(SpeciesProtector, species_protector, SpeciesProtector) \
V(StoreHandler0Map, store_handler0_map, StoreHandler0Map) \
V(SymbolMap, symbol_map, SymbolMap) \
V(TheHoleValue, the_hole_value, TheHole) \
V(TransitionArrayMap, transition_array_map, TransitionArrayMap) \
V(TrueValue, true_value, True) \
V(Tuple2Map, tuple2_map, Tuple2Map) \
V(Tuple3Map, tuple3_map, Tuple3Map) \
V(UndefinedValue, undefined_value, Undefined) \
V(WeakCellMap, weak_cell_map, WeakCellMap) \
#define HEAP_CONSTANT_LIST(V) \
V(AccessorInfoMap, accessor_info_map, AccessorInfoMap) \
V(AccessorPairMap, accessor_pair_map, AccessorPairMap) \
V(AllocationSiteMap, allocation_site_map, AllocationSiteMap) \
V(BooleanMap, boolean_map, BooleanMap) \
V(CodeMap, code_map, CodeMap) \
V(EmptyPropertyDictionary, empty_property_dictionary, \
EmptyPropertyDictionary) \
V(EmptyFixedArray, empty_fixed_array, EmptyFixedArray) \
V(EmptySlowElementDictionary, empty_slow_element_dictionary, \
EmptySlowElementDictionary) \
V(empty_string, empty_string, EmptyString) \
V(EmptyWeakCell, empty_weak_cell, EmptyWeakCell) \
V(FalseValue, false_value, False) \
V(FeedbackVectorMap, feedback_vector_map, FeedbackVectorMap) \
V(FixedArrayMap, fixed_array_map, FixedArrayMap) \
V(FixedCOWArrayMap, fixed_cow_array_map, FixedCOWArrayMap) \
V(FixedDoubleArrayMap, fixed_double_array_map, FixedDoubleArrayMap) \
V(FunctionTemplateInfoMap, function_template_info_map, \
FunctionTemplateInfoMap) \
V(GlobalPropertyCellMap, global_property_cell_map, PropertyCellMap) \
V(has_instance_symbol, has_instance_symbol, HasInstanceSymbol) \
V(HeapNumberMap, heap_number_map, HeapNumberMap) \
V(iterator_symbol, iterator_symbol, IteratorSymbol) \
V(length_string, length_string, LengthString) \
V(ManyClosuresCellMap, many_closures_cell_map, ManyClosuresCellMap) \
V(MetaMap, meta_map, MetaMap) \
V(MinusZeroValue, minus_zero_value, MinusZero) \
V(MutableHeapNumberMap, mutable_heap_number_map, MutableHeapNumberMap) \
V(NanValue, nan_value, Nan) \
V(NoClosuresCellMap, no_closures_cell_map, NoClosuresCellMap) \
V(NullValue, null_value, Null) \
V(OneClosureCellMap, one_closure_cell_map, OneClosureCellMap) \
V(prototype_string, prototype_string, PrototypeString) \
V(ArraySpeciesProtector, array_species_protector, ArraySpeciesProtector) \
V(TypedArraySpeciesProtector, typed_array_species_protector, \
TypedArraySpeciesProtector) \
V(PromiseSpeciesProtector, promise_species_protector, \
PromiseSpeciesProtector) \
V(StoreHandler0Map, store_handler0_map, StoreHandler0Map) \
V(SymbolMap, symbol_map, SymbolMap) \
V(TheHoleValue, the_hole_value, TheHole) \
V(TransitionArrayMap, transition_array_map, TransitionArrayMap) \
V(TrueValue, true_value, True) \
V(Tuple2Map, tuple2_map, Tuple2Map) \
V(Tuple3Map, tuple3_map, Tuple3Map) \
V(UndefinedValue, undefined_value, Undefined) \
V(WeakCellMap, weak_cell_map, WeakCellMap) \
V(SharedFunctionInfoMap, shared_function_info_map, SharedFunctionInfoMap)
// Returned from IteratorBuiltinsAssembler::GetIterator(). Struct is declared
@ -1383,7 +1387,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
Node* IsPromiseResolveProtectorCellInvalid();
Node* IsPromiseThenProtectorCellInvalid();
Node* IsSpeciesProtectorCellInvalid();
Node* IsArraySpeciesProtectorCellInvalid();
Node* IsTypedArraySpeciesProtectorCellInvalid();
Node* IsPromiseSpeciesProtectorCellInvalid();
// True iff |object| is a Smi or a HeapNumber.
TNode<BoolT> IsNumber(SloppyTNode<Object> object);

View File

@ -1481,7 +1481,7 @@ Reduction JSCallReducer::ReduceArrayMap(Node* node,
if (result == NodeProperties::kNoReceiverMaps) return NoChange();
// Ensure that any changes to the Array species constructor cause deopt.
if (!isolate()->IsSpeciesLookupChainIntact()) return NoChange();
if (!isolate()->IsArraySpeciesLookupChainIntact()) return NoChange();
const ElementsKind kind = receiver_maps[0]->elements_kind();
@ -1492,7 +1492,7 @@ Reduction JSCallReducer::ReduceArrayMap(Node* node,
if (receiver_map->elements_kind() != kind) return NoChange();
}
dependencies()->AssumePropertyCell(factory()->species_protector());
dependencies()->AssumePropertyCell(factory()->array_species_protector());
Handle<JSFunction> handle_constructor(
JSFunction::cast(
@ -1681,7 +1681,7 @@ Reduction JSCallReducer::ReduceArrayFilter(Node* node,
if (result == NodeProperties::kNoReceiverMaps) return NoChange();
// And ensure that any changes to the Array species constructor cause deopt.
if (!isolate()->IsSpeciesLookupChainIntact()) return NoChange();
if (!isolate()->IsArraySpeciesLookupChainIntact()) return NoChange();
const ElementsKind kind = receiver_maps[0]->elements_kind();
// The output array is packed (filter doesn't visit holes).
@ -1696,7 +1696,7 @@ Reduction JSCallReducer::ReduceArrayFilter(Node* node,
if (receiver_map->elements_kind() != kind) return NoChange();
}
dependencies()->AssumePropertyCell(factory()->species_protector());
dependencies()->AssumePropertyCell(factory()->array_species_protector());
Handle<Map> initial_map(
Map::cast(native_context()->GetInitialJSArrayMap(packed_kind)));
@ -2274,7 +2274,7 @@ Reduction JSCallReducer::ReduceArrayEvery(Node* node,
if (result == NodeProperties::kNoReceiverMaps) return NoChange();
// And ensure that any changes to the Array species constructor cause deopt.
if (!isolate()->IsSpeciesLookupChainIntact()) return NoChange();
if (!isolate()->IsArraySpeciesLookupChainIntact()) return NoChange();
const ElementsKind kind = receiver_maps[0]->elements_kind();
@ -2285,7 +2285,7 @@ Reduction JSCallReducer::ReduceArrayEvery(Node* node,
if (receiver_map->elements_kind() != kind) return NoChange();
}
dependencies()->AssumePropertyCell(factory()->species_protector());
dependencies()->AssumePropertyCell(factory()->array_species_protector());
// If we have unreliable maps, we need a map check.
if (result == NodeProperties::kUnreliableReceiverMaps) {
@ -2597,7 +2597,7 @@ Reduction JSCallReducer::ReduceArraySome(Node* node,
if (result == NodeProperties::kNoReceiverMaps) return NoChange();
// And ensure that any changes to the Array species constructor cause deopt.
if (!isolate()->IsSpeciesLookupChainIntact()) return NoChange();
if (!isolate()->IsArraySpeciesLookupChainIntact()) return NoChange();
if (receiver_maps.size() == 0) return NoChange();
@ -2610,7 +2610,7 @@ Reduction JSCallReducer::ReduceArraySome(Node* node,
if (receiver_map->elements_kind() != kind) return NoChange();
}
dependencies()->AssumePropertyCell(factory()->species_protector());
dependencies()->AssumePropertyCell(factory()->array_species_protector());
Node* k = jsgraph()->ZeroConstant();
@ -5730,7 +5730,7 @@ Reduction JSCallReducer::ReducePromisePrototypeFinally(Node* node) {
// lookup of "constructor" on JSPromise instances, whoch [[Prototype]] is
// the initial %PromisePrototype%, and the Symbol.species lookup on the
// %PromisePrototype%.
if (!isolate()->IsSpeciesLookupChainIntact()) return NoChange();
if (!isolate()->IsPromiseSpeciesLookupChainIntact()) return NoChange();
// Check if we know something about {receiver} already.
ZoneHandleSet<Map> receiver_maps;
@ -5751,7 +5751,7 @@ Reduction JSCallReducer::ReducePromisePrototypeFinally(Node* node) {
// Add a code dependency on the necessary protectors.
dependencies()->AssumePropertyCell(factory()->promise_hook_protector());
dependencies()->AssumePropertyCell(factory()->promise_then_protector());
dependencies()->AssumePropertyCell(factory()->species_protector());
dependencies()->AssumePropertyCell(factory()->promise_species_protector());
// If the {receiver_maps} aren't reliable, we need to repeat the
// map check here, guarded by the CALL_IC.
@ -5878,7 +5878,7 @@ Reduction JSCallReducer::ReducePromisePrototypeThen(Node* node) {
// guards the "constructor" lookup on all JSPromise instances and the
// initial Promise.prototype, as well as the Symbol.species lookup on
// the Promise constructor.
if (!isolate()->IsSpeciesLookupChainIntact()) return NoChange();
if (!isolate()->IsPromiseSpeciesLookupChainIntact()) return NoChange();
// Check if we know something about {receiver} already.
ZoneHandleSet<Map> receiver_maps;
@ -5900,7 +5900,7 @@ Reduction JSCallReducer::ReducePromisePrototypeThen(Node* node) {
// Add a code dependency on the necessary protectors.
dependencies()->AssumePropertyCell(factory()->promise_hook_protector());
dependencies()->AssumePropertyCell(factory()->species_protector());
dependencies()->AssumePropertyCell(factory()->promise_species_protector());
// If the {receiver_maps} aren't reliable, we need to repeat the
// map check here, guarded by the CALL_IC.

View File

@ -228,7 +228,9 @@ using v8::MemoryPressureLevel;
V(Cell, array_constructor_protector, ArrayConstructorProtector) \
V(PropertyCell, no_elements_protector, NoElementsProtector) \
V(Cell, is_concat_spreadable_protector, IsConcatSpreadableProtector) \
V(PropertyCell, species_protector, SpeciesProtector) \
V(PropertyCell, array_species_protector, ArraySpeciesProtector) \
V(PropertyCell, typed_array_species_protector, TypedArraySpeciesProtector) \
V(PropertyCell, promise_species_protector, PromiseSpeciesProtector) \
V(Cell, string_length_protector, StringLengthProtector) \
V(PropertyCell, array_iterator_protector, ArrayIteratorProtector) \
V(PropertyCell, array_buffer_neutering_protector, \
@ -389,7 +391,9 @@ using v8::MemoryPressureLevel;
V(SloppyArgumentsElementsMap) \
V(SmallOrderedHashMapMap) \
V(SmallOrderedHashSetMap) \
V(SpeciesProtector) \
V(ArraySpeciesProtector) \
V(TypedArraySpeciesProtector) \
V(PromiseSpeciesProtector) \
V(StaleRegister) \
V(StringLengthProtector) \
V(StringTableMap) \

View File

@ -787,7 +787,15 @@ void Heap::CreateInitialObjects() {
cell = factory->NewPropertyCell(factory->empty_string());
cell->set_value(Smi::FromInt(Isolate::kProtectorValid));
set_species_protector(*cell);
set_array_species_protector(*cell);
cell = factory->NewPropertyCell(factory->empty_string());
cell->set_value(Smi::FromInt(Isolate::kProtectorValid));
set_typed_array_species_protector(*cell);
cell = factory->NewPropertyCell(factory->empty_string());
cell->set_value(Smi::FromInt(Isolate::kProtectorValid));
set_promise_species_protector(*cell);
Handle<Cell> string_length_overflow_cell = factory->NewCell(
handle(Smi::FromInt(Isolate::kProtectorValid), isolate()));

View File

@ -133,7 +133,7 @@ bool Isolate::IsArrayConstructorIntact() {
return array_constructor_cell->value() == Smi::FromInt(kProtectorValid);
}
bool Isolate::IsSpeciesLookupChainIntact() {
bool Isolate::IsArraySpeciesLookupChainIntact() {
// Note: It would be nice to have debug checks to make sure that the
// species protector is accurate, but this would be hard to do for most of
// what the protector stands for:
@ -146,7 +146,19 @@ bool Isolate::IsSpeciesLookupChainIntact() {
// done here. In place, there are mjsunit tests harmony/array-species* which
// ensure that behavior is correct in various invalid protector cases.
PropertyCell* species_cell = heap()->species_protector();
PropertyCell* species_cell = heap()->array_species_protector();
return species_cell->value()->IsSmi() &&
Smi::ToInt(species_cell->value()) == kProtectorValid;
}
bool Isolate::IsTypedArraySpeciesLookupChainIntact() {
PropertyCell* species_cell = heap()->typed_array_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() &&
Smi::ToInt(species_cell->value()) == kProtectorValid;
}

View File

@ -3573,11 +3573,28 @@ void Isolate::InvalidateArrayConstructorProtector() {
DCHECK(!IsArrayConstructorIntact());
}
void Isolate::InvalidateSpeciesProtector() {
DCHECK(factory()->species_protector()->value()->IsSmi());
DCHECK(IsSpeciesLookupChainIntact());
factory()->species_protector()->set_value(Smi::FromInt(kProtectorInvalid));
DCHECK(!IsSpeciesLookupChainIntact());
void Isolate::InvalidateArraySpeciesProtector() {
DCHECK(factory()->array_species_protector()->value()->IsSmi());
DCHECK(IsArraySpeciesLookupChainIntact());
factory()->array_species_protector()->set_value(
Smi::FromInt(kProtectorInvalid));
DCHECK(!IsArraySpeciesLookupChainIntact());
}
void Isolate::InvalidateTypedArraySpeciesProtector() {
DCHECK(factory()->typed_array_species_protector()->value()->IsSmi());
DCHECK(IsTypedArraySpeciesLookupChainIntact());
factory()->typed_array_species_protector()->set_value(
Smi::FromInt(kProtectorInvalid));
DCHECK(!IsTypedArraySpeciesLookupChainIntact());
}
void Isolate::InvalidatePromiseSpeciesProtector() {
DCHECK(factory()->promise_species_protector()->value()->IsSmi());
DCHECK(IsPromiseSpeciesLookupChainIntact());
factory()->promise_species_protector()->set_value(
Smi::FromInt(kProtectorInvalid));
DCHECK(!IsPromiseSpeciesLookupChainIntact());
}
void Isolate::InvalidateStringLengthOverflowProtector() {

View File

@ -1080,7 +1080,9 @@ class Isolate : private HiddenFactory {
bool IsNoElementsProtectorIntact(Context* context);
bool IsNoElementsProtectorIntact();
inline bool IsSpeciesLookupChainIntact();
inline bool IsArraySpeciesLookupChainIntact();
inline bool IsTypedArraySpeciesLookupChainIntact();
inline bool IsPromiseSpeciesLookupChainIntact();
bool IsIsConcatSpreadableLookupChainIntact();
bool IsIsConcatSpreadableLookupChainIntact(JSReceiver* receiver);
inline bool IsStringLengthOverflowIntact();
@ -1120,7 +1122,9 @@ class Isolate : private HiddenFactory {
UpdateNoElementsProtectorOnSetElement(object);
}
void InvalidateArrayConstructorProtector();
void InvalidateSpeciesProtector();
void InvalidateArraySpeciesProtector();
void InvalidateTypedArraySpeciesProtector();
void InvalidatePromiseSpeciesProtector();
void InvalidateIsConcatSpreadableProtector();
void InvalidateStringLengthOverflowProtector();
void InvalidateArrayIteratorProtector();

View File

@ -272,14 +272,27 @@ void LookupIterator::InternalUpdateProtector() {
if (isolate_->bootstrapper()->IsActive()) return;
if (*name_ == heap()->constructor_string()) {
if (!isolate_->IsSpeciesLookupChainIntact()) return;
if (!isolate_->IsArraySpeciesLookupChainIntact() &&
!isolate_->IsTypedArraySpeciesLookupChainIntact() &&
!isolate_->IsPromiseSpeciesLookupChainIntact())
return;
// Setting the constructor property could change an instance's @@species
if (holder_->IsJSArray() || holder_->IsJSPromise() ||
holder_->IsJSTypedArray()) {
if (holder_->IsJSArray()) {
if (!isolate_->IsArraySpeciesLookupChainIntact()) return;
isolate_->CountUsage(
v8::Isolate::UseCounterFeature::kArrayInstanceConstructorModified);
isolate_->InvalidateSpeciesProtector();
} else if (holder_->map()->is_prototype_map()) {
isolate_->InvalidateArraySpeciesProtector();
return;
} else if (holder_->IsJSPromise()) {
if (!isolate_->IsPromiseSpeciesLookupChainIntact()) return;
isolate_->InvalidatePromiseSpeciesProtector();
return;
} else if (holder_->IsJSTypedArray()) {
if (!isolate_->IsTypedArraySpeciesLookupChainIntact()) return;
isolate_->InvalidateTypedArraySpeciesProtector();
return;
}
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
@ -288,14 +301,20 @@ void LookupIterator::InternalUpdateProtector() {
// have different prototypes for each type, and their parent prototype is
// pointing the same TYPED_ARRAY_PROTOTYPE.
if (isolate_->IsInAnyContext(*holder_,
Context::INITIAL_ARRAY_PROTOTYPE_INDEX) ||
isolate_->IsInAnyContext(*holder_,
Context::PROMISE_PROTOTYPE_INDEX) ||
isolate_->IsInAnyContext(holder_->map()->prototype(),
Context::TYPED_ARRAY_PROTOTYPE_INDEX)) {
isolate_->CountUsage(v8::Isolate::UseCounterFeature::
kArrayPrototypeConstructorModified);
isolate_->InvalidateSpeciesProtector();
Context::INITIAL_ARRAY_PROTOTYPE_INDEX)) {
if (!isolate_->IsArraySpeciesLookupChainIntact()) return;
isolate_->CountUsage(
v8::Isolate::UseCounterFeature::kArrayPrototypeConstructorModified);
isolate_->InvalidateArraySpeciesProtector();
} else if (isolate_->IsInAnyContext(*holder_,
Context::PROMISE_PROTOTYPE_INDEX)) {
if (!isolate_->IsPromiseSpeciesLookupChainIntact()) return;
isolate_->InvalidatePromiseSpeciesProtector();
} else if (isolate_->IsInAnyContext(
holder_->map()->prototype(),
Context::TYPED_ARRAY_PROTOTYPE_INDEX)) {
if (!isolate_->IsTypedArraySpeciesLookupChainIntact()) return;
isolate_->InvalidateTypedArraySpeciesProtector();
}
}
} else if (*name_ == heap()->next_string()) {
@ -307,15 +326,24 @@ void LookupIterator::InternalUpdateProtector() {
isolate_->InvalidateArrayIteratorProtector();
}
} else if (*name_ == heap()->species_symbol()) {
if (!isolate_->IsSpeciesLookupChainIntact()) return;
if (!isolate_->IsArraySpeciesLookupChainIntact() &&
!isolate_->IsTypedArraySpeciesLookupChainIntact() &&
!isolate_->IsPromiseSpeciesLookupChainIntact())
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) ||
isolate_->IsInAnyContext(*holder_, Context::PROMISE_FUNCTION_INDEX) ||
IsTypedArrayFunctionInAnyContext(isolate_, *holder_)) {
if (isolate_->IsInAnyContext(*holder_, Context::ARRAY_FUNCTION_INDEX)) {
if (!isolate_->IsArraySpeciesLookupChainIntact()) return;
isolate_->CountUsage(
v8::Isolate::UseCounterFeature::kArraySpeciesModified);
isolate_->InvalidateSpeciesProtector();
isolate_->InvalidateArraySpeciesProtector();
} else if (isolate_->IsInAnyContext(*holder_,
Context::PROMISE_FUNCTION_INDEX)) {
if (!isolate_->IsPromiseSpeciesLookupChainIntact()) return;
isolate_->InvalidatePromiseSpeciesProtector();
} else if (IsTypedArrayFunctionInAnyContext(isolate_, *holder_)) {
if (!isolate_->IsTypedArraySpeciesLookupChainIntact()) return;
isolate_->InvalidateTypedArraySpeciesProtector();
}
} else if (*name_ == heap()->is_concat_spreadable_symbol()) {
if (!isolate_->IsIsConcatSpreadableLookupChainIntact()) return;

View File

@ -2370,7 +2370,7 @@ MaybeHandle<Object> Object::ArraySpeciesConstructor(
Handle<Object> default_species = isolate->array_function();
if (original_array->IsJSArray() &&
Handle<JSArray>::cast(original_array)->HasArrayPrototype(isolate) &&
isolate->IsSpeciesLookupChainIntact()) {
isolate->IsArraySpeciesLookupChainIntact()) {
return default_species;
}
Handle<Object> constructor = isolate->factory()->undefined_value();

View File

@ -396,7 +396,7 @@ RUNTIME_FUNCTION(Runtime_TrySliceSimpleNonFastElements) {
// implementation.
if (receiver->IsJSArray()) {
// This "fastish" path must make sure the destination array is a JSArray.
if (!isolate->IsSpeciesLookupChainIntact() ||
if (!isolate->IsArraySpeciesLookupChainIntact() ||
!JSArray::cast(*receiver)->HasArrayPrototype(isolate)) {
return Smi::FromInt(0);
}

View File

@ -841,11 +841,24 @@ TYPED_ARRAYS(FIXED_TYPED_ARRAYS_CHECK_RUNTIME_FUNCTION)
#undef FIXED_TYPED_ARRAYS_CHECK_RUNTIME_FUNCTION
RUNTIME_FUNCTION(Runtime_SpeciesProtector) {
RUNTIME_FUNCTION(Runtime_ArraySpeciesProtector) {
SealHandleScope shs(isolate);
DCHECK_EQ(0, args.length());
return isolate->heap()->ToBoolean(isolate->IsSpeciesLookupChainIntact());
return isolate->heap()->ToBoolean(isolate->IsArraySpeciesLookupChainIntact());
}
RUNTIME_FUNCTION(Runtime_TypedArraySpeciesProtector) {
SealHandleScope shs(isolate);
DCHECK_EQ(0, args.length());
return isolate->heap()->ToBoolean(
isolate->IsTypedArraySpeciesLookupChainIntact());
}
RUNTIME_FUNCTION(Runtime_PromiseSpeciesProtector) {
SealHandleScope shs(isolate);
DCHECK_EQ(0, args.length());
return isolate->heap()->ToBoolean(
isolate->IsPromiseSpeciesLookupChainIntact());
}
// Take a compiled wasm module, serialize it and copy the buffer into an array

View File

@ -588,7 +588,9 @@ namespace internal {
F(SetForceSlowPath, 1, 1) \
F(SetWasmCompileControls, 2, 1) \
F(SetWasmInstantiateControls, 0, 1) \
F(SpeciesProtector, 0, 1) \
F(ArraySpeciesProtector, 0, 1) \
F(TypedArraySpeciesProtector, 0, 1) \
F(PromiseSpeciesProtector, 0, 1) \
F(SystemBreak, 0, 1) \
F(TraceEnter, 0, 1) \
F(TraceExit, 1, 1) \

View File

@ -117,12 +117,12 @@ void TestSpeciesProtector(char* code,
v8::internal::Isolate* i_isolate =
reinterpret_cast<v8::internal::Isolate*>(isolate);
CHECK(i_isolate->IsSpeciesLookupChainIntact());
CHECK(i_isolate->IsTypedArraySpeciesLookupChainIntact());
CompileRun(code);
if (invalidates_species_protector) {
CHECK(!i_isolate->IsSpeciesLookupChainIntact());
CHECK(!i_isolate->IsTypedArraySpeciesLookupChainIntact());
} else {
CHECK(i_isolate->IsSpeciesLookupChainIntact());
CHECK(i_isolate->IsTypedArraySpeciesLookupChainIntact());
}
v8::Local<v8::Value> my_typed_array = CompileRun("MyTypedArray");

View File

@ -18,7 +18,7 @@ assertEquals(1, x.concat([1])[0]);
class MyArray extends Array { }
Object.defineProperty(x, 'constructor', {get() { return MyArray; }});
assertFalse(%SpeciesProtector());
assertFalse(%ArraySpeciesProtector());
assertEquals(MyArray, x.map(()=>{}).constructor);
assertEquals(MyArray, x.filter(()=>{}).constructor);

View File

@ -19,7 +19,7 @@ class MyArray extends Array { }
Object.prototype.constructor = MyArray;
delete Array.prototype.constructor;
assertFalse(%SpeciesProtector());
assertFalse(%ArraySpeciesProtector());
assertEquals(MyArray, x.map(()=>{}).constructor);
assertEquals(MyArray, x.filter(()=>{}).constructor);

View File

@ -18,7 +18,7 @@ assertEquals(1, x.concat([1])[0]);
class MyArray extends Array { }
x.constructor = MyArray;
assertFalse(%SpeciesProtector());
assertFalse(%ArraySpeciesProtector());
assertEquals(MyArray, x.map(()=>{}).constructor);
assertEquals(MyArray, x.filter(()=>{}).constructor);

View File

@ -19,7 +19,7 @@ class MyArray extends Array { }
Object.prototype[Symbol.species] = MyArray;
delete Array[Symbol.species];
assertFalse(%SpeciesProtector());
assertFalse(%ArraySpeciesProtector());
assertEquals(MyArray, x.map(()=>{}).constructor);
assertEquals(MyArray, x.filter(()=>{}).constructor);

View File

@ -18,7 +18,7 @@ assertEquals(1, x.concat([1])[0]);
class MyArray extends Array { }
Object.defineProperty(Array, Symbol.species, {value: MyArray});
assertFalse(%SpeciesProtector());
assertFalse(%ArraySpeciesProtector());
assertEquals(MyArray, x.map(()=>{}).constructor);
assertEquals(MyArray, x.filter(()=>{}).constructor);

View File

@ -18,7 +18,7 @@ assertEquals(1, x.concat([1])[0]);
class MyArray extends Array { }
Array.prototype.constructor = MyArray;
assertFalse(%SpeciesProtector());
assertFalse(%ArraySpeciesProtector());
assertEquals(MyArray, x.map(()=>{}).constructor);
assertEquals(MyArray, x.filter(()=>{}).constructor);

View File

@ -18,7 +18,7 @@ assertEquals(1, x.concat([1])[0]);
class MyArray extends Array { }
x.__proto__ = MyArray.prototype;
assertTrue(%SpeciesProtector());
assertTrue(%ArraySpeciesProtector());
assertEquals(MyArray, x.map(()=>{}).constructor);
assertEquals(MyArray, x.filter(()=>{}).constructor);

View File

@ -425,7 +425,7 @@ function toSlowMode(re) {
{
const re = /./;
const result = re.exec("a");
assertTrue(%SpeciesProtector());
assertTrue(%ArraySpeciesProtector());
assertEquals(result.__proto__, Array.prototype);
assertTrue(result.hasOwnProperty('groups'));
assertArrayEquals(["a"], result);
@ -433,7 +433,7 @@ function toSlowMode(re) {
assertEquals(undefined, result.groups);
Array.prototype.groups = { a: "b" };
assertTrue(%SpeciesProtector());
assertTrue(%ArraySpeciesProtector());
assertEquals("$<a>", "a".replace(re, "$<a>"));
Array.prototype.groups = undefined;
}
@ -441,7 +441,7 @@ function toSlowMode(re) {
{
const re = toSlowMode(/./);
const result = re.exec("a");
assertTrue(%SpeciesProtector());
assertTrue(%ArraySpeciesProtector());
assertEquals(result.__proto__, Array.prototype);
assertTrue(result.hasOwnProperty('groups'));
assertArrayEquals(["a"], result);
@ -449,7 +449,7 @@ function toSlowMode(re) {
assertEquals(undefined, result.groups);
Array.prototype.groups = { a: "b" };
assertTrue(%SpeciesProtector());
assertTrue(%ArraySpeciesProtector());
assertEquals("$<a>", "a".replace(re, "$<a>"));
Array.prototype.groups = undefined;
}
@ -457,7 +457,7 @@ function toSlowMode(re) {
{
const re = /(?<a>a).|(?<x>x)/;
const result = re.exec("ab");
assertTrue(%SpeciesProtector());
assertTrue(%ArraySpeciesProtector());
assertEquals(result.__proto__, Array.prototype);
assertTrue(result.hasOwnProperty('groups'));
assertArrayEquals(["ab", "a", undefined], result);
@ -467,7 +467,7 @@ function toSlowMode(re) {
// a is a matched named capture, b is an unmatched named capture, and z
// is not a named capture.
Array.prototype.groups = { a: "b", x: "y", z: "z" };
assertTrue(%SpeciesProtector());
assertTrue(%ArraySpeciesProtector());
assertEquals("a", "ab".replace(re, "$<a>"));
assertEquals("", "ab".replace(re, "$<x>"));
assertEquals("", "ab".replace(re, "$<z>"));
@ -477,7 +477,7 @@ function toSlowMode(re) {
{
const re = toSlowMode(/(?<a>a).|(?<x>x)/);
const result = re.exec("ab");
assertTrue(%SpeciesProtector());
assertTrue(%ArraySpeciesProtector());
assertEquals(result.__proto__, Array.prototype);
assertTrue(result.hasOwnProperty('groups'));
assertArrayEquals(["ab", "a", undefined], result);
@ -487,7 +487,7 @@ function toSlowMode(re) {
// a is a matched named capture, b is an unmatched named capture, and z
// is not a named capture.
Array.prototype.groups = { a: "b", x: "y", z: "z" };
assertTrue(%SpeciesProtector());
assertTrue(%ArraySpeciesProtector());
assertEquals("a", "ab".replace(re, "$<a>"));
assertEquals("", "ab".replace(re, "$<x>"));
assertEquals("", "ab".replace(re, "$<z>"));
@ -506,13 +506,13 @@ function toSlowMode(re) {
const re = new FakeRegExp();
const result = re.exec("ab");
assertTrue(%SpeciesProtector());
assertTrue(%ArraySpeciesProtector());
assertEquals(result.__proto__, Array.prototype);
assertFalse(result.hasOwnProperty('groups'));
Array.prototype.groups = { a: "b" };
Array.prototype.groups.__proto__.b = "c";
assertTrue(%SpeciesProtector());
assertTrue(%ArraySpeciesProtector());
assertEquals("b", "ab".replace(re, "$<a>"));
assertEquals("c", "ab".replace(re, "$<b>"));
Array.prototype.groups = undefined;
@ -531,7 +531,7 @@ function toSlowMode(re) {
const re = new FakeRegExp();
const result = re.exec("ab");
assertTrue(%SpeciesProtector());
assertTrue(%ArraySpeciesProtector());
assertEquals(result.__proto__, Array.prototype);
assertTrue(result.hasOwnProperty('groups'));
assertEquals({ a: "b" }, result.groups);

View File

@ -16,7 +16,7 @@ f("make it generic", 0, 0);
(function TestSpeciesProtector() {
function MyArray() {}
assertTrue(%SpeciesProtector());
assertTrue(%ArraySpeciesProtector());
f(Array.prototype, "constructor", MyArray);
assertFalse(%SpeciesProtector());
assertFalse(%ArraySpeciesProtector());
})();

View File

@ -333,13 +333,15 @@ KNOWN_OBJECTS = {
("OLD_SPACE", 0x02659): "EmptyWeakCell",
("OLD_SPACE", 0x026e1): "NoElementsProtector",
("OLD_SPACE", 0x02709): "IsConcatSpreadableProtector",
("OLD_SPACE", 0x02719): "SpeciesProtector",
("OLD_SPACE", 0x02741): "StringLengthProtector",
("OLD_SPACE", 0x02751): "ArrayIteratorProtector",
("OLD_SPACE", 0x02779): "ArrayBufferNeuteringProtector",
("OLD_SPACE", 0x02801): "InfinityValue",
("OLD_SPACE", 0x02811): "MinusZeroValue",
("OLD_SPACE", 0x02821): "MinusInfinityValue",
("OLD_SPACE", 0x02719): "ArraySpeciesProtector",
("OLD_SPACE", 0x02741): "TypedArraySpeciesProtector",
("OLD_SPACE", 0x02769): "PromiseSpeciesProtector",
("OLD_SPACE", 0x02791): "StringLengthProtector",
("OLD_SPACE", 0x027a1): "ArrayIteratorProtector",
("OLD_SPACE", 0x027c9): "ArrayBufferNeuteringProtector",
("OLD_SPACE", 0x02851): "InfinityValue",
("OLD_SPACE", 0x02861): "MinusZeroValue",
("OLD_SPACE", 0x02871): "MinusInfinityValue",
}
# List of known V8 Frame Markers.