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

This is a reland of 5728b3fbc5

Original change's description:
> [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}

Bug: chromium:835347, v8:7340
Change-Id: I0c0188a0723e206ddb362834bcf872b23cd7666d
Reviewed-on: https://chromium-review.googlesource.com/1023811
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#52742}
This commit is contained in:
Sigurd Schneider 2018-04-23 15:58:02 +02:00 committed by Commit Bot
parent f1e3051ef6
commit 30be479711
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

@ -985,7 +985,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);
}
@ -4525,9 +4525,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
@ -1374,7 +1378,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.