[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; let map: Map = a.map;
if (!IsPrototypeInitialArrayPrototype(context, map)) goto Bailout; if (!IsPrototypeInitialArrayPrototype(context, map)) goto Bailout;
if (IsNoElementsProtectorCellInvalid()) 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. // Fast path only works on fast elements kind and with writable length.
let elementsKind: int32 = EnsureArrayPushable(map) otherwise Bailout; 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 IsPrototypeInitialArrayPrototype(Context, Map): bit;
extern macro IsNoElementsProtectorCellInvalid(): 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; 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), GotoIfNot(IsPrototypeInitialArrayPrototype(context(), original_map),
&runtime); &runtime);
Node* species_protector = SpeciesProtectorConstant(); Node* species_protector = ArraySpeciesProtectorConstant();
Node* value = Node* value =
LoadObjectField(species_protector, PropertyCell::kValueOffset); LoadObjectField(species_protector, PropertyCell::kValueOffset);
TNode<Smi> const protector_invalid = TNode<Smi> const protector_invalid =
@ -862,7 +862,7 @@ Node* ArrayBuiltinsAssembler::FindProcessor(Node* k_value, Node* k) {
GotoIfNot(IsPrototypeInitialArrayPrototype(context(), original_map), GotoIfNot(IsPrototypeInitialArrayPrototype(context(), original_map),
&runtime); &runtime);
Node* species_protector = SpeciesProtectorConstant(); Node* species_protector = ArraySpeciesProtectorConstant();
Node* value = Node* value =
LoadObjectField(species_protector, PropertyCell::kValueOffset); LoadObjectField(species_protector, PropertyCell::kValueOffset);
Node* const protector_invalid = SmiConstant(Isolate::kProtectorInvalid); Node* const protector_invalid = SmiConstant(Isolate::kProtectorInvalid);
@ -1155,7 +1155,7 @@ class ArrayPrototypeSliceCodeStubAssembler : public CodeStubAssembler {
GotoIf(IsNoElementsProtectorCellInvalid(), slow); GotoIf(IsNoElementsProtectorCellInvalid(), slow);
GotoIf(IsSpeciesProtectorCellInvalid(), slow); GotoIf(IsArraySpeciesProtectorCellInvalid(), slow);
// Bailout if receiver has slow elements. // Bailout if receiver has slow elements.
Node* elements_kind = LoadMapElementsKind(map); 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. // If this is a subclass of Array, then call out to JS.
!Handle<JSArray>::cast(receiver)->HasArrayPrototype(isolate) || !Handle<JSArray>::cast(receiver)->HasArrayPrototype(isolate) ||
// If anything with @@species has been messed with, call out to JS. // If anything with @@species has been messed with, call out to JS.
!isolate->IsSpeciesLookupChainIntact())) { !isolate->IsArraySpeciesLookupChainIntact())) {
return CallJsIntrinsic(isolate, isolate->array_splice(), args); return CallJsIntrinsic(isolate, isolate->array_splice(), args);
} }
Handle<JSArray> array = Handle<JSArray>::cast(receiver); 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 // Avoid a real species read to avoid extra lookups to the array constructor
if (V8_LIKELY(receiver->IsJSArray() && if (V8_LIKELY(receiver->IsJSArray() &&
Handle<JSArray>::cast(receiver)->HasArrayPrototype(isolate) && Handle<JSArray>::cast(receiver)->HasArrayPrototype(isolate) &&
isolate->IsSpeciesLookupChainIntact())) { isolate->IsArraySpeciesLookupChainIntact())) {
if (Fast_ArrayConcat(isolate, &args).ToHandle(&result_array)) { if (Fast_ArrayConcat(isolate, &args).ToHandle(&result_array)) {
return *result_array; return *result_array;
} }

View File

@ -635,7 +635,7 @@ void PromiseBuiltinsAssembler::BranchIfPromiseSpeciesLookupChainIntact(
GotoIfForceSlowPath(if_slow); GotoIfForceSlowPath(if_slow);
GotoIfNot(WordEqual(LoadMapPrototype(promise_map), promise_prototype), GotoIfNot(WordEqual(LoadMapPrototype(promise_map), promise_prototype),
if_slow); if_slow);
Branch(IsSpeciesProtectorCellInvalid(), if_slow, if_fast); Branch(IsPromiseSpeciesProtectorCellInvalid(), if_slow, if_fast);
} }
void PromiseBuiltinsAssembler::BranchIfPromiseThenLookupChainIntact( void PromiseBuiltinsAssembler::BranchIfPromiseThenLookupChainIntact(
@ -1285,7 +1285,7 @@ TF_BUILTIN(PromiseResolve, PromiseBuiltinsAssembler) {
LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_INDEX); LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_INDEX);
GotoIfNot(WordEqual(LoadMapPrototype(value_map), promise_prototype), GotoIfNot(WordEqual(LoadMapPrototype(value_map), promise_prototype),
&if_slow_constructor); &if_slow_constructor);
GotoIf(IsSpeciesProtectorCellInvalid(), &if_slow_constructor); GotoIf(IsPromiseSpeciesProtectorCellInvalid(), &if_slow_constructor);
// If the {constructor} is the Promise function, we just immediately // If the {constructor} is the Promise function, we just immediately
// return the {value} here and don't bother wrapping it into a // 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; var_constructor = default_constructor;
Node* map = LoadMap(exemplar); Node* map = LoadMap(exemplar);
GotoIfNot(IsPrototypeTypedArrayPrototype(context, map), &slow); GotoIfNot(IsPrototypeTypedArrayPrototype(context, map), &slow);
Branch(IsSpeciesProtectorCellInvalid(), &slow, &done); Branch(IsTypedArraySpeciesProtectorCellInvalid(), &slow, &done);
BIND(&slow); BIND(&slow);
var_constructor = SpeciesConstructor(context, exemplar, default_constructor); 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, void CodeStubAssembler::BranchIfFastJSArrayForCopy(Node* object, Node* context,
Label* if_true, Label* if_true,
Label* if_false) { Label* if_false) {
GotoIf(IsSpeciesProtectorCellInvalid(), if_false); GotoIf(IsArraySpeciesProtectorCellInvalid(), if_false);
BranchIfFastJSArray(object, context, if_true, if_false); BranchIfFastJSArray(object, context, if_true, if_false);
} }
@ -4545,9 +4545,23 @@ Node* CodeStubAssembler::IsPromiseThenProtectorCellInvalid() {
return WordEqual(cell_value, invalid); return WordEqual(cell_value, invalid);
} }
Node* CodeStubAssembler::IsSpeciesProtectorCellInvalid() { Node* CodeStubAssembler::IsArraySpeciesProtectorCellInvalid() {
Node* invalid = SmiConstant(Isolate::kProtectorInvalid); 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); Node* cell_value = LoadObjectField(cell, PropertyCell::kValueOffset);
return WordEqual(cell_value, invalid); return WordEqual(cell_value, invalid);
} }

View File

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

View File

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

View File

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

View File

@ -787,7 +787,15 @@ void Heap::CreateInitialObjects() {
cell = factory->NewPropertyCell(factory->empty_string()); cell = factory->NewPropertyCell(factory->empty_string());
cell->set_value(Smi::FromInt(Isolate::kProtectorValid)); 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<Cell> string_length_overflow_cell = factory->NewCell(
handle(Smi::FromInt(Isolate::kProtectorValid), isolate())); handle(Smi::FromInt(Isolate::kProtectorValid), isolate()));

View File

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

View File

@ -3573,11 +3573,28 @@ void Isolate::InvalidateArrayConstructorProtector() {
DCHECK(!IsArrayConstructorIntact()); DCHECK(!IsArrayConstructorIntact());
} }
void Isolate::InvalidateSpeciesProtector() { void Isolate::InvalidateArraySpeciesProtector() {
DCHECK(factory()->species_protector()->value()->IsSmi()); DCHECK(factory()->array_species_protector()->value()->IsSmi());
DCHECK(IsSpeciesLookupChainIntact()); DCHECK(IsArraySpeciesLookupChainIntact());
factory()->species_protector()->set_value(Smi::FromInt(kProtectorInvalid)); factory()->array_species_protector()->set_value(
DCHECK(!IsSpeciesLookupChainIntact()); 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() { void Isolate::InvalidateStringLengthOverflowProtector() {

View File

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

View File

@ -272,14 +272,27 @@ void LookupIterator::InternalUpdateProtector() {
if (isolate_->bootstrapper()->IsActive()) return; if (isolate_->bootstrapper()->IsActive()) return;
if (*name_ == heap()->constructor_string()) { 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 // Setting the constructor property could change an instance's @@species
if (holder_->IsJSArray() || holder_->IsJSPromise() || if (holder_->IsJSArray()) {
holder_->IsJSTypedArray()) { if (!isolate_->IsArraySpeciesLookupChainIntact()) return;
isolate_->CountUsage( isolate_->CountUsage(
v8::Isolate::UseCounterFeature::kArrayInstanceConstructorModified); v8::Isolate::UseCounterFeature::kArrayInstanceConstructorModified);
isolate_->InvalidateSpeciesProtector(); isolate_->InvalidateArraySpeciesProtector();
} else if (holder_->map()->is_prototype_map()) { 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; DisallowHeapAllocation no_gc;
// Setting the constructor of Array.prototype, Promise.prototype or // Setting the constructor of Array.prototype, Promise.prototype or
// %TypedArray%.prototype of any realm also needs to invalidate the // %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 // have different prototypes for each type, and their parent prototype is
// pointing the same TYPED_ARRAY_PROTOTYPE. // pointing the same TYPED_ARRAY_PROTOTYPE.
if (isolate_->IsInAnyContext(*holder_, if (isolate_->IsInAnyContext(*holder_,
Context::INITIAL_ARRAY_PROTOTYPE_INDEX) || Context::INITIAL_ARRAY_PROTOTYPE_INDEX)) {
isolate_->IsInAnyContext(*holder_, if (!isolate_->IsArraySpeciesLookupChainIntact()) return;
Context::PROMISE_PROTOTYPE_INDEX) || isolate_->CountUsage(
isolate_->IsInAnyContext(holder_->map()->prototype(), v8::Isolate::UseCounterFeature::kArrayPrototypeConstructorModified);
Context::TYPED_ARRAY_PROTOTYPE_INDEX)) { isolate_->InvalidateArraySpeciesProtector();
isolate_->CountUsage(v8::Isolate::UseCounterFeature:: } else if (isolate_->IsInAnyContext(*holder_,
kArrayPrototypeConstructorModified); Context::PROMISE_PROTOTYPE_INDEX)) {
isolate_->InvalidateSpeciesProtector(); 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()) { } else if (*name_ == heap()->next_string()) {
@ -307,15 +326,24 @@ void LookupIterator::InternalUpdateProtector() {
isolate_->InvalidateArrayIteratorProtector(); isolate_->InvalidateArrayIteratorProtector();
} }
} else if (*name_ == heap()->species_symbol()) { } 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 // Setting the Symbol.species property of any Array, Promise or TypedArray
// constructor invalidates the @@species protector // constructor invalidates the @@species protector
if (isolate_->IsInAnyContext(*holder_, Context::ARRAY_FUNCTION_INDEX) || if (isolate_->IsInAnyContext(*holder_, Context::ARRAY_FUNCTION_INDEX)) {
isolate_->IsInAnyContext(*holder_, Context::PROMISE_FUNCTION_INDEX) || if (!isolate_->IsArraySpeciesLookupChainIntact()) return;
IsTypedArrayFunctionInAnyContext(isolate_, *holder_)) {
isolate_->CountUsage( isolate_->CountUsage(
v8::Isolate::UseCounterFeature::kArraySpeciesModified); 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()) { } else if (*name_ == heap()->is_concat_spreadable_symbol()) {
if (!isolate_->IsIsConcatSpreadableLookupChainIntact()) return; if (!isolate_->IsIsConcatSpreadableLookupChainIntact()) return;

View File

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

View File

@ -396,7 +396,7 @@ RUNTIME_FUNCTION(Runtime_TrySliceSimpleNonFastElements) {
// implementation. // implementation.
if (receiver->IsJSArray()) { if (receiver->IsJSArray()) {
// This "fastish" path must make sure the destination array is a JSArray. // This "fastish" path must make sure the destination array is a JSArray.
if (!isolate->IsSpeciesLookupChainIntact() || if (!isolate->IsArraySpeciesLookupChainIntact() ||
!JSArray::cast(*receiver)->HasArrayPrototype(isolate)) { !JSArray::cast(*receiver)->HasArrayPrototype(isolate)) {
return Smi::FromInt(0); 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 #undef FIXED_TYPED_ARRAYS_CHECK_RUNTIME_FUNCTION
RUNTIME_FUNCTION(Runtime_ArraySpeciesProtector) {
RUNTIME_FUNCTION(Runtime_SpeciesProtector) {
SealHandleScope shs(isolate); SealHandleScope shs(isolate);
DCHECK_EQ(0, args.length()); 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 // 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(SetForceSlowPath, 1, 1) \
F(SetWasmCompileControls, 2, 1) \ F(SetWasmCompileControls, 2, 1) \
F(SetWasmInstantiateControls, 0, 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(SystemBreak, 0, 1) \
F(TraceEnter, 0, 1) \ F(TraceEnter, 0, 1) \
F(TraceExit, 1, 1) \ F(TraceExit, 1, 1) \

View File

@ -117,12 +117,12 @@ void TestSpeciesProtector(char* code,
v8::internal::Isolate* i_isolate = v8::internal::Isolate* i_isolate =
reinterpret_cast<v8::internal::Isolate*>(isolate); reinterpret_cast<v8::internal::Isolate*>(isolate);
CHECK(i_isolate->IsSpeciesLookupChainIntact()); CHECK(i_isolate->IsTypedArraySpeciesLookupChainIntact());
CompileRun(code); CompileRun(code);
if (invalidates_species_protector) { if (invalidates_species_protector) {
CHECK(!i_isolate->IsSpeciesLookupChainIntact()); CHECK(!i_isolate->IsTypedArraySpeciesLookupChainIntact());
} else { } else {
CHECK(i_isolate->IsSpeciesLookupChainIntact()); CHECK(i_isolate->IsTypedArraySpeciesLookupChainIntact());
} }
v8::Local<v8::Value> my_typed_array = CompileRun("MyTypedArray"); 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 { } class MyArray extends Array { }
Object.defineProperty(x, 'constructor', {get() { return MyArray; }}); Object.defineProperty(x, 'constructor', {get() { return MyArray; }});
assertFalse(%SpeciesProtector()); assertFalse(%ArraySpeciesProtector());
assertEquals(MyArray, x.map(()=>{}).constructor); assertEquals(MyArray, x.map(()=>{}).constructor);
assertEquals(MyArray, x.filter(()=>{}).constructor); assertEquals(MyArray, x.filter(()=>{}).constructor);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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