[ic] Use validity cells to protect keyed element stores against object's prototype chain modifications.
... instead of clearing of all the KeyedStoreICs which didn't always work. BUG=chromium:662907, chromium:669411, v8:5561 TBR=verwaest@chromium.org, bmeurer@chromium.org Committed: https://crrev.com/a39522f44f7e0be4686831688917e9675255dcaf Review-Url: https://codereview.chromium.org/2534613002 Cr-Original-Commit-Position: refs/heads/master@{#41332} Cr-Commit-Position: refs/heads/master@{#41449}
This commit is contained in:
parent
13afe42705
commit
39e6f2ca4a
@ -8298,8 +8298,8 @@ class Internals {
|
||||
static const int kNodeIsIndependentShift = 3;
|
||||
static const int kNodeIsActiveShift = 4;
|
||||
|
||||
static const int kJSObjectType = 0xbc;
|
||||
static const int kJSApiObjectType = 0xbb;
|
||||
static const int kJSObjectType = 0xbd;
|
||||
static const int kJSApiObjectType = 0xbc;
|
||||
static const int kFirstNonstringType = 0x80;
|
||||
static const int kOddballType = 0x83;
|
||||
static const int kForeignType = 0x87;
|
||||
|
@ -315,6 +315,7 @@ AstType::bitset AstBitsetType::Lub(i::Map* map) {
|
||||
case CELL_TYPE:
|
||||
case WEAK_CELL_TYPE:
|
||||
case PROTOTYPE_INFO_TYPE:
|
||||
case TUPLE2_TYPE:
|
||||
case TUPLE3_TYPE:
|
||||
case CONTEXT_EXTENSION_TYPE:
|
||||
UNREACHABLE();
|
||||
|
@ -601,8 +601,9 @@ class ArrayConcatVisitor {
|
||||
SeededNumberDictionary::cast(*storage_));
|
||||
// The object holding this backing store has just been allocated, so
|
||||
// it cannot yet be used as a prototype.
|
||||
Handle<SeededNumberDictionary> result =
|
||||
SeededNumberDictionary::AtNumberPut(dict, index, elm, false);
|
||||
Handle<JSObject> not_a_prototype_holder;
|
||||
Handle<SeededNumberDictionary> result = SeededNumberDictionary::AtNumberPut(
|
||||
dict, index, elm, not_a_prototype_holder);
|
||||
if (!result.is_identical_to(dict)) {
|
||||
// Dictionary needed to grow.
|
||||
clear_storage();
|
||||
@ -669,9 +670,10 @@ class ArrayConcatVisitor {
|
||||
if (!element->IsTheHole(isolate_)) {
|
||||
// The object holding this backing store has just been allocated, so
|
||||
// it cannot yet be used as a prototype.
|
||||
Handle<JSObject> not_a_prototype_holder;
|
||||
Handle<SeededNumberDictionary> new_storage =
|
||||
SeededNumberDictionary::AtNumberPut(slow_storage, i, element,
|
||||
false);
|
||||
not_a_prototype_holder);
|
||||
if (!new_storage.is_identical_to(slow_storage)) {
|
||||
slow_storage = loop_scope.CloseAndEscape(new_storage);
|
||||
}
|
||||
|
@ -39,6 +39,8 @@ enum class PrimitiveType { kBoolean, kNumber, kString, kSymbol };
|
||||
V(SymbolMap, SymbolMap) \
|
||||
V(TheHoleValue, TheHole) \
|
||||
V(TrueValue, True) \
|
||||
V(Tuple2Map, Tuple2Map) \
|
||||
V(Tuple3Map, Tuple3Map) \
|
||||
V(UndefinedValue, Undefined)
|
||||
|
||||
// Provides JavaScript-specific "macro-assembler" functionality on top of the
|
||||
|
@ -324,6 +324,7 @@ Type::bitset BitsetType::Lub(i::Map* map) {
|
||||
case CELL_TYPE:
|
||||
case WEAK_CELL_TYPE:
|
||||
case PROTOTYPE_INFO_TYPE:
|
||||
case TUPLE2_TYPE:
|
||||
case TUPLE3_TYPE:
|
||||
case CONTEXT_EXTENSION_TYPE:
|
||||
UNREACHABLE();
|
||||
|
@ -1431,9 +1431,8 @@ class DictionaryElementsAccessor
|
||||
? JSObject::NormalizeElements(object)
|
||||
: handle(SeededNumberDictionary::cast(object->elements()));
|
||||
Handle<SeededNumberDictionary> new_dictionary =
|
||||
SeededNumberDictionary::AddNumberEntry(
|
||||
dictionary, index, value, details,
|
||||
object->map()->is_prototype_map());
|
||||
SeededNumberDictionary::AddNumberEntry(dictionary, index, value,
|
||||
details, object);
|
||||
if (attributes != NONE) object->RequireSlowElements(*new_dictionary);
|
||||
if (dictionary.is_identical_to(new_dictionary)) return;
|
||||
object->set_elements(*new_dictionary);
|
||||
@ -1773,15 +1772,14 @@ class FastElementsAccessor : public ElementsAccessorBase<Subclass, KindTraits> {
|
||||
SeededNumberDictionary::New(isolate, capacity);
|
||||
|
||||
PropertyDetails details = PropertyDetails::Empty();
|
||||
bool used_as_prototype = object->map()->is_prototype_map();
|
||||
int j = 0;
|
||||
for (int i = 0; j < capacity; i++) {
|
||||
if (IsHoleyElementsKind(kind)) {
|
||||
if (BackingStore::cast(*store)->is_the_hole(isolate, i)) continue;
|
||||
}
|
||||
Handle<Object> value = Subclass::GetImpl(isolate, *store, i);
|
||||
dictionary = SeededNumberDictionary::AddNumberEntry(
|
||||
dictionary, i, value, details, used_as_prototype);
|
||||
dictionary = SeededNumberDictionary::AddNumberEntry(dictionary, i, value,
|
||||
details, object);
|
||||
j++;
|
||||
}
|
||||
return dictionary;
|
||||
@ -3276,9 +3274,8 @@ class SlowSloppyArgumentsElementsAccessor
|
||||
: JSObject::NormalizeElements(object);
|
||||
PropertyDetails details(attributes, DATA, 0, PropertyCellType::kNoCell);
|
||||
Handle<SeededNumberDictionary> new_dictionary =
|
||||
SeededNumberDictionary::AddNumberEntry(
|
||||
dictionary, index, value, details,
|
||||
object->map()->is_prototype_map());
|
||||
SeededNumberDictionary::AddNumberEntry(dictionary, index, value,
|
||||
details, object);
|
||||
if (attributes != NONE) object->RequireSlowElements(*new_dictionary);
|
||||
if (*dictionary != *new_dictionary) {
|
||||
FixedArray::cast(object->elements())->set(1, *new_dictionary);
|
||||
@ -3311,7 +3308,7 @@ class SlowSloppyArgumentsElementsAccessor
|
||||
Handle<SeededNumberDictionary> arguments(
|
||||
SeededNumberDictionary::cast(parameter_map->get(1)), isolate);
|
||||
arguments = SeededNumberDictionary::AddNumberEntry(
|
||||
arguments, entry, value, details, object->map()->is_prototype_map());
|
||||
arguments, entry, value, details, object);
|
||||
// If the attributes were NONE, we would have called set rather than
|
||||
// reconfigure.
|
||||
DCHECK_NE(NONE, attributes);
|
||||
|
@ -103,6 +103,14 @@ Handle<PrototypeInfo> Factory::NewPrototypeInfo() {
|
||||
return result;
|
||||
}
|
||||
|
||||
Handle<Tuple2> Factory::NewTuple2(Handle<Object> value1,
|
||||
Handle<Object> value2) {
|
||||
Handle<Tuple2> result = Handle<Tuple2>::cast(NewStruct(TUPLE2_TYPE));
|
||||
result->set_value1(*value1);
|
||||
result->set_value2(*value2);
|
||||
return result;
|
||||
}
|
||||
|
||||
Handle<Tuple3> Factory::NewTuple3(Handle<Object> value1, Handle<Object> value2,
|
||||
Handle<Object> value3) {
|
||||
Handle<Tuple3> result = Handle<Tuple3>::cast(NewStruct(TUPLE3_TYPE));
|
||||
|
@ -85,6 +85,9 @@ class V8_EXPORT_PRIVATE Factory final {
|
||||
// Create a new PrototypeInfo struct.
|
||||
Handle<PrototypeInfo> NewPrototypeInfo();
|
||||
|
||||
// Create a new Tuple2 struct.
|
||||
Handle<Tuple2> NewTuple2(Handle<Object> value1, Handle<Object> value2);
|
||||
|
||||
// Create a new Tuple3 struct.
|
||||
Handle<Tuple3> NewTuple3(Handle<Object> value1, Handle<Object> value2,
|
||||
Handle<Object> value3);
|
||||
|
@ -81,12 +81,12 @@ class AccessorAssemblerImpl : public CodeStubAssembler {
|
||||
Node* value;
|
||||
};
|
||||
|
||||
void HandleStoreICHandlerCase(const StoreICParameters* p, Node* handler,
|
||||
Label* miss);
|
||||
enum ElementSupport { kOnlyProperties, kSupportElements };
|
||||
void HandleStoreICHandlerCase(
|
||||
const StoreICParameters* p, Node* handler, Label* miss,
|
||||
ElementSupport support_elements = kOnlyProperties);
|
||||
|
||||
private:
|
||||
enum ElementSupport { kOnlyProperties, kSupportElements };
|
||||
|
||||
// Stub generation entry points.
|
||||
|
||||
void LoadIC(const LoadICParameters* p);
|
||||
@ -142,6 +142,9 @@ class AccessorAssemblerImpl : public CodeStubAssembler {
|
||||
|
||||
// StoreIC implementation.
|
||||
|
||||
void HandleStoreICElementHandlerCase(const StoreICParameters* p,
|
||||
Node* handler, Label* miss);
|
||||
|
||||
void HandleStoreICProtoHandler(const StoreICParameters* p, Node* handler,
|
||||
Label* miss);
|
||||
// If |transition| is nullptr then the normal field store is generated or
|
||||
|
@ -470,13 +470,13 @@ void AccessorAssemblerImpl::HandleLoadGlobalICHandlerCase(
|
||||
miss, kOnlyProperties);
|
||||
}
|
||||
|
||||
void AccessorAssemblerImpl::HandleStoreICHandlerCase(const StoreICParameters* p,
|
||||
Node* handler,
|
||||
Label* miss) {
|
||||
Label if_smi_handler(this);
|
||||
Label try_proto_handler(this), call_handler(this);
|
||||
void AccessorAssemblerImpl::HandleStoreICHandlerCase(
|
||||
const StoreICParameters* p, Node* handler, Label* miss,
|
||||
ElementSupport support_elements) {
|
||||
Label if_smi_handler(this), if_nonsmi_handler(this);
|
||||
Label if_proto_handler(this), if_element_handler(this), call_handler(this);
|
||||
|
||||
Branch(TaggedIsSmi(handler), &if_smi_handler, &try_proto_handler);
|
||||
Branch(TaggedIsSmi(handler), &if_smi_handler, &if_nonsmi_handler);
|
||||
|
||||
// |handler| is a Smi, encoding what to do. See SmiHandler methods
|
||||
// for the encoding format.
|
||||
@ -489,9 +489,22 @@ void AccessorAssemblerImpl::HandleStoreICHandlerCase(const StoreICParameters* p,
|
||||
HandleStoreICSmiHandlerCase(handler_word, holder, p->value, nullptr, miss);
|
||||
}
|
||||
|
||||
Bind(&try_proto_handler);
|
||||
Bind(&if_nonsmi_handler);
|
||||
{
|
||||
Node* handler_map = LoadMap(handler);
|
||||
if (support_elements == kSupportElements) {
|
||||
GotoIf(IsTuple2Map(handler_map), &if_element_handler);
|
||||
}
|
||||
Branch(IsCodeMap(handler_map), &call_handler, &if_proto_handler);
|
||||
}
|
||||
|
||||
if (support_elements == kSupportElements) {
|
||||
Bind(&if_element_handler);
|
||||
{ HandleStoreICElementHandlerCase(p, handler, miss); }
|
||||
}
|
||||
|
||||
Bind(&if_proto_handler);
|
||||
{
|
||||
GotoIf(IsCodeMap(LoadMap(handler)), &call_handler);
|
||||
HandleStoreICProtoHandler(p, handler, miss);
|
||||
}
|
||||
|
||||
@ -504,6 +517,23 @@ void AccessorAssemblerImpl::HandleStoreICHandlerCase(const StoreICParameters* p,
|
||||
}
|
||||
}
|
||||
|
||||
void AccessorAssemblerImpl::HandleStoreICElementHandlerCase(
|
||||
const StoreICParameters* p, Node* handler, Label* miss) {
|
||||
Comment("HandleStoreICElementHandlerCase");
|
||||
Node* validity_cell = LoadObjectField(handler, Tuple2::kValue1Offset);
|
||||
Node* cell_value = LoadObjectField(validity_cell, Cell::kValueOffset);
|
||||
GotoIf(WordNotEqual(cell_value,
|
||||
SmiConstant(Smi::FromInt(Map::kPrototypeChainValid))),
|
||||
miss);
|
||||
|
||||
Node* code_handler = LoadObjectField(handler, Tuple2::kValue2Offset);
|
||||
CSA_ASSERT(this, IsCodeMap(LoadMap(code_handler)));
|
||||
|
||||
StoreWithVectorDescriptor descriptor(isolate());
|
||||
TailCallStub(descriptor, code_handler, p->context, p->receiver, p->name,
|
||||
p->value, p->slot, p->vector);
|
||||
}
|
||||
|
||||
void AccessorAssemblerImpl::HandleStoreICProtoHandler(
|
||||
const StoreICParameters* p, Node* handler, Label* miss) {
|
||||
// IC dispatchers rely on these assumptions to be held.
|
||||
@ -1503,7 +1533,7 @@ void AccessorAssemblerImpl::KeyedStoreIC(const StoreICParameters* p,
|
||||
Bind(&if_handler);
|
||||
{
|
||||
Comment("KeyedStoreIC_if_handler");
|
||||
HandleStoreICHandlerCase(p, var_handler.value(), &miss);
|
||||
HandleStoreICHandlerCase(p, var_handler.value(), &miss, kSupportElements);
|
||||
}
|
||||
|
||||
Bind(&try_polymorphic);
|
||||
@ -1520,11 +1550,38 @@ void AccessorAssemblerImpl::KeyedStoreIC(const StoreICParameters* p,
|
||||
&var_transition_map_cell, &miss);
|
||||
Bind(&if_transition_handler);
|
||||
Comment("KeyedStoreIC_polymorphic_transition");
|
||||
Node* transition_map =
|
||||
LoadWeakCellValue(var_transition_map_cell.value(), &miss);
|
||||
StoreTransitionDescriptor descriptor(isolate());
|
||||
TailCallStub(descriptor, var_handler.value(), p->context, p->receiver,
|
||||
p->name, transition_map, p->value, p->slot, p->vector);
|
||||
{
|
||||
Node* handler = var_handler.value();
|
||||
|
||||
Label call_handler(this);
|
||||
Variable var_code_handler(this, MachineRepresentation::kTagged);
|
||||
var_code_handler.Bind(handler);
|
||||
GotoUnless(IsTuple2Map(LoadMap(handler)), &call_handler);
|
||||
{
|
||||
CSA_ASSERT(this, IsTuple2Map(LoadMap(handler)));
|
||||
|
||||
// Check validity cell.
|
||||
Node* validity_cell = LoadObjectField(handler, Tuple2::kValue1Offset);
|
||||
Node* cell_value = LoadObjectField(validity_cell, Cell::kValueOffset);
|
||||
GotoIf(WordNotEqual(cell_value, SmiConstant(Map::kPrototypeChainValid)),
|
||||
&miss);
|
||||
|
||||
var_code_handler.Bind(LoadObjectField(handler, Tuple2::kValue2Offset));
|
||||
Goto(&call_handler);
|
||||
}
|
||||
|
||||
Bind(&call_handler);
|
||||
{
|
||||
Node* code_handler = var_code_handler.value();
|
||||
CSA_ASSERT(this, IsCodeMap(LoadMap(code_handler)));
|
||||
|
||||
Node* transition_map =
|
||||
LoadWeakCellValue(var_transition_map_cell.value(), &miss);
|
||||
StoreTransitionDescriptor descriptor(isolate());
|
||||
TailCallStub(descriptor, code_handler, p->context, p->receiver, p->name,
|
||||
transition_map, p->value, p->slot, p->vector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Bind(&try_megamorphic);
|
||||
|
@ -10,7 +10,7 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
Handle<Code> PropertyICCompiler::ComputeKeyedStoreMonomorphicHandler(
|
||||
Handle<Object> PropertyICCompiler::ComputeKeyedStoreMonomorphicHandler(
|
||||
Handle<Map> receiver_map, KeyedAccessStoreMode store_mode) {
|
||||
Isolate* isolate = receiver_map->GetIsolate();
|
||||
|
||||
@ -20,14 +20,14 @@ Handle<Code> PropertyICCompiler::ComputeKeyedStoreMonomorphicHandler(
|
||||
store_mode == STORE_NO_TRANSITION_HANDLE_COW);
|
||||
|
||||
PropertyICCompiler compiler(isolate);
|
||||
Handle<Code> code =
|
||||
Handle<Object> handler =
|
||||
compiler.CompileKeyedStoreMonomorphicHandler(receiver_map, store_mode);
|
||||
return code;
|
||||
return handler;
|
||||
}
|
||||
|
||||
void PropertyICCompiler::ComputeKeyedStorePolymorphicHandlers(
|
||||
MapHandleList* receiver_maps, MapHandleList* transitioned_maps,
|
||||
CodeHandleList* handlers, KeyedAccessStoreMode store_mode) {
|
||||
List<Handle<Object>>* handlers, KeyedAccessStoreMode store_mode) {
|
||||
Isolate* isolate = receiver_maps->at(0)->GetIsolate();
|
||||
DCHECK(store_mode == STANDARD_STORE ||
|
||||
store_mode == STORE_AND_GROW_NO_TRANSITION ||
|
||||
@ -38,13 +38,12 @@ void PropertyICCompiler::ComputeKeyedStorePolymorphicHandlers(
|
||||
receiver_maps, transitioned_maps, handlers, store_mode);
|
||||
}
|
||||
|
||||
|
||||
void PropertyICCompiler::CompileKeyedStorePolymorphicHandlers(
|
||||
MapHandleList* receiver_maps, MapHandleList* transitioned_maps,
|
||||
CodeHandleList* handlers, KeyedAccessStoreMode store_mode) {
|
||||
List<Handle<Object>>* handlers, KeyedAccessStoreMode store_mode) {
|
||||
for (int i = 0; i < receiver_maps->length(); ++i) {
|
||||
Handle<Map> receiver_map(receiver_maps->at(i));
|
||||
Handle<Code> cached_stub;
|
||||
Handle<Object> handler;
|
||||
Handle<Map> transitioned_map;
|
||||
{
|
||||
Map* tmap = receiver_map->FindElementsKindTransitionedMap(receiver_maps);
|
||||
@ -61,21 +60,29 @@ void PropertyICCompiler::CompileKeyedStorePolymorphicHandlers(
|
||||
ElementsKind elements_kind = receiver_map->elements_kind();
|
||||
TRACE_HANDLER_STATS(isolate(),
|
||||
KeyedStoreIC_ElementsTransitionAndStoreStub);
|
||||
cached_stub =
|
||||
Handle<Code> stub =
|
||||
ElementsTransitionAndStoreStub(isolate(), elements_kind,
|
||||
transitioned_map->elements_kind(),
|
||||
is_js_array, store_mode).GetCode();
|
||||
is_js_array, store_mode)
|
||||
.GetCode();
|
||||
Handle<Object> validity_cell =
|
||||
Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate());
|
||||
if (validity_cell.is_null()) {
|
||||
handler = stub;
|
||||
} else {
|
||||
handler = isolate()->factory()->NewTuple2(validity_cell, stub);
|
||||
}
|
||||
|
||||
} else if (receiver_map->instance_type() < FIRST_JS_RECEIVER_TYPE) {
|
||||
// TODO(mvstanton): Consider embedding store_mode in the state of the slow
|
||||
// keyed store ic for uniformity.
|
||||
TRACE_HANDLER_STATS(isolate(), KeyedStoreIC_SlowStub);
|
||||
cached_stub = isolate()->builtins()->KeyedStoreIC_Slow();
|
||||
handler = isolate()->builtins()->KeyedStoreIC_Slow();
|
||||
} else {
|
||||
cached_stub =
|
||||
CompileKeyedStoreMonomorphicHandler(receiver_map, store_mode);
|
||||
handler = CompileKeyedStoreMonomorphicHandler(receiver_map, store_mode);
|
||||
}
|
||||
DCHECK(!cached_stub.is_null());
|
||||
handlers->Add(cached_stub);
|
||||
DCHECK(!handler.is_null());
|
||||
handlers->Add(handler);
|
||||
transitioned_maps->Add(transitioned_map);
|
||||
}
|
||||
}
|
||||
@ -83,8 +90,7 @@ void PropertyICCompiler::CompileKeyedStorePolymorphicHandlers(
|
||||
|
||||
#define __ ACCESS_MASM(masm())
|
||||
|
||||
|
||||
Handle<Code> PropertyICCompiler::CompileKeyedStoreMonomorphicHandler(
|
||||
Handle<Object> PropertyICCompiler::CompileKeyedStoreMonomorphicHandler(
|
||||
Handle<Map> receiver_map, KeyedAccessStoreMode store_mode) {
|
||||
ElementsKind elements_kind = receiver_map->elements_kind();
|
||||
bool is_jsarray = receiver_map->instance_type() == JS_ARRAY_TYPE;
|
||||
@ -101,7 +107,12 @@ Handle<Code> PropertyICCompiler::CompileKeyedStoreMonomorphicHandler(
|
||||
TRACE_HANDLER_STATS(isolate(), KeyedStoreIC_StoreElementStub);
|
||||
stub = StoreElementStub(isolate(), elements_kind, store_mode).GetCode();
|
||||
}
|
||||
return stub;
|
||||
Handle<Object> validity_cell =
|
||||
Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate());
|
||||
if (validity_cell.is_null()) {
|
||||
return stub;
|
||||
}
|
||||
return isolate()->factory()->NewTuple2(validity_cell, stub);
|
||||
}
|
||||
|
||||
|
||||
|
@ -14,22 +14,22 @@ namespace internal {
|
||||
class PropertyICCompiler : public PropertyAccessCompiler {
|
||||
public:
|
||||
// Keyed
|
||||
static Handle<Code> ComputeKeyedStoreMonomorphicHandler(
|
||||
static Handle<Object> ComputeKeyedStoreMonomorphicHandler(
|
||||
Handle<Map> receiver_map, KeyedAccessStoreMode store_mode);
|
||||
static void ComputeKeyedStorePolymorphicHandlers(
|
||||
MapHandleList* receiver_maps, MapHandleList* transitioned_maps,
|
||||
CodeHandleList* handlers, KeyedAccessStoreMode store_mode);
|
||||
List<Handle<Object>>* handlers, KeyedAccessStoreMode store_mode);
|
||||
|
||||
private:
|
||||
explicit PropertyICCompiler(Isolate* isolate)
|
||||
: PropertyAccessCompiler(isolate, Code::KEYED_STORE_IC,
|
||||
kCacheOnReceiver) {}
|
||||
|
||||
Handle<Code> CompileKeyedStoreMonomorphicHandler(
|
||||
Handle<Object> CompileKeyedStoreMonomorphicHandler(
|
||||
Handle<Map> receiver_map, KeyedAccessStoreMode store_mode);
|
||||
void CompileKeyedStorePolymorphicHandlers(MapHandleList* receiver_maps,
|
||||
MapHandleList* transitioned_maps,
|
||||
CodeHandleList* handlers,
|
||||
List<Handle<Object>>* handlers,
|
||||
KeyedAccessStoreMode store_mode);
|
||||
};
|
||||
|
||||
|
@ -93,8 +93,8 @@ Code* IC::target() const {
|
||||
}
|
||||
|
||||
bool IC::IsHandler(Object* object) {
|
||||
return (object->IsSmi() && (object != nullptr)) || object->IsTuple3() ||
|
||||
object->IsFixedArray() ||
|
||||
return (object->IsSmi() && (object != nullptr)) || object->IsTuple2() ||
|
||||
object->IsTuple3() || object->IsFixedArray() ||
|
||||
(object->IsCode() && Code::cast(object)->is_handler());
|
||||
}
|
||||
|
||||
|
11
src/ic/ic.cc
11
src/ic/ic.cc
@ -603,10 +603,9 @@ void IC::ConfigureVectorState(Handle<Name> name, MapHandleList* maps,
|
||||
OnTypeFeedbackChanged(isolate(), get_host());
|
||||
}
|
||||
|
||||
|
||||
void IC::ConfigureVectorState(MapHandleList* maps,
|
||||
MapHandleList* transitioned_maps,
|
||||
CodeHandleList* handlers) {
|
||||
List<Handle<Object>>* handlers) {
|
||||
DCHECK(UseVector());
|
||||
DCHECK(kind() == Code::KEYED_STORE_IC);
|
||||
KeyedStoreICNexus* nexus = casted_nexus<KeyedStoreICNexus>();
|
||||
@ -2208,7 +2207,7 @@ void KeyedStoreIC::UpdateStoreElement(Handle<Map> receiver_map,
|
||||
Handle<Map> monomorphic_map =
|
||||
ComputeTransitionedMap(receiver_map, store_mode);
|
||||
store_mode = GetNonTransitioningStoreMode(store_mode);
|
||||
Handle<Code> handler =
|
||||
Handle<Object> handler =
|
||||
PropertyICCompiler::ComputeKeyedStoreMonomorphicHandler(monomorphic_map,
|
||||
store_mode);
|
||||
return ConfigureVectorState(Handle<Name>(), monomorphic_map, handler);
|
||||
@ -2242,7 +2241,7 @@ void KeyedStoreIC::UpdateStoreElement(Handle<Map> receiver_map,
|
||||
// if they at least come from the same origin for a transitioning store,
|
||||
// stay MONOMORPHIC and use the map for the most generic ElementsKind.
|
||||
store_mode = GetNonTransitioningStoreMode(store_mode);
|
||||
Handle<Code> handler =
|
||||
Handle<Object> handler =
|
||||
PropertyICCompiler::ComputeKeyedStoreMonomorphicHandler(
|
||||
transitioned_receiver_map, store_mode);
|
||||
ConfigureVectorState(Handle<Name>(), transitioned_receiver_map, handler);
|
||||
@ -2256,7 +2255,7 @@ void KeyedStoreIC::UpdateStoreElement(Handle<Map> receiver_map,
|
||||
// A "normal" IC that handles stores can switch to a version that can
|
||||
// grow at the end of the array, handle OOB accesses or copy COW arrays
|
||||
// and still stay MONOMORPHIC.
|
||||
Handle<Code> handler =
|
||||
Handle<Object> handler =
|
||||
PropertyICCompiler::ComputeKeyedStoreMonomorphicHandler(receiver_map,
|
||||
store_mode);
|
||||
return ConfigureVectorState(Handle<Name>(), receiver_map, handler);
|
||||
@ -2317,7 +2316,7 @@ void KeyedStoreIC::UpdateStoreElement(Handle<Map> receiver_map,
|
||||
}
|
||||
|
||||
MapHandleList transitioned_maps(target_receiver_maps.length());
|
||||
CodeHandleList handlers(target_receiver_maps.length());
|
||||
List<Handle<Object>> handlers(target_receiver_maps.length());
|
||||
PropertyICCompiler::ComputeKeyedStorePolymorphicHandlers(
|
||||
&target_receiver_maps, &transitioned_maps, &handlers, store_mode);
|
||||
ConfigureVectorState(&target_receiver_maps, &transitioned_maps, &handlers);
|
||||
|
@ -120,7 +120,7 @@ class IC {
|
||||
// keyed stores).
|
||||
void ConfigureVectorState(MapHandleList* maps,
|
||||
MapHandleList* transitioned_maps,
|
||||
CodeHandleList* handlers);
|
||||
List<Handle<Object>>* handlers);
|
||||
|
||||
char TransitionMarkFromState(IC::State state);
|
||||
void TraceIC(const char* type, Handle<Object> name);
|
||||
|
@ -526,11 +526,8 @@ void LookupIterator::TransitionToAccessorPair(Handle<Object> pair,
|
||||
Handle<SeededNumberDictionary> dictionary =
|
||||
JSObject::NormalizeElements(receiver);
|
||||
|
||||
// We unconditionally pass used_as_prototype=false here because the call
|
||||
// to RequireSlowElements takes care of the required IC clearing and
|
||||
// we don't want to walk the heap twice.
|
||||
dictionary =
|
||||
SeededNumberDictionary::Set(dictionary, index_, pair, details, false);
|
||||
dictionary = SeededNumberDictionary::Set(dictionary, index_, pair, details,
|
||||
receiver);
|
||||
receiver->RequireSlowElements(*dictionary);
|
||||
|
||||
if (receiver->HasSlowArgumentsElements()) {
|
||||
|
@ -1086,6 +1086,12 @@ void PrototypeInfo::PrototypeInfoVerify() {
|
||||
CHECK(validity_cell()->IsCell() || validity_cell()->IsSmi());
|
||||
}
|
||||
|
||||
void Tuple2::Tuple2Verify() {
|
||||
CHECK(IsTuple2());
|
||||
VerifyObjectField(kValue1Offset);
|
||||
VerifyObjectField(kValue2Offset);
|
||||
}
|
||||
|
||||
void Tuple3::Tuple3Verify() {
|
||||
CHECK(IsTuple3());
|
||||
VerifyObjectField(kValue1Offset);
|
||||
|
@ -5787,8 +5787,8 @@ ACCESSORS(PrototypeInfo, validity_cell, Object, kValidityCellOffset)
|
||||
SMI_ACCESSORS(PrototypeInfo, bit_field, kBitFieldOffset)
|
||||
BOOL_ACCESSORS(PrototypeInfo, bit_field, should_be_fast_map, kShouldBeFastBit)
|
||||
|
||||
ACCESSORS(Tuple3, value1, Object, kValue1Offset)
|
||||
ACCESSORS(Tuple3, value2, Object, kValue2Offset)
|
||||
ACCESSORS(Tuple2, value1, Object, kValue1Offset)
|
||||
ACCESSORS(Tuple2, value2, Object, kValue2Offset)
|
||||
ACCESSORS(Tuple3, value3, Object, kValue3Offset)
|
||||
|
||||
ACCESSORS(ContextExtension, scope_info, ScopeInfo, kScopeInfoOffset)
|
||||
|
@ -1272,6 +1272,13 @@ void PrototypeInfo::PrototypeInfoPrint(std::ostream& os) { // NOLINT
|
||||
os << "\n";
|
||||
}
|
||||
|
||||
void Tuple2::Tuple2Print(std::ostream& os) { // NOLINT
|
||||
HeapObject::PrintHeader(os, "Tuple2");
|
||||
os << "\n - value1: " << Brief(value1());
|
||||
os << "\n - value2: " << Brief(value2());
|
||||
os << "\n";
|
||||
}
|
||||
|
||||
void Tuple3::Tuple3Print(std::ostream& os) { // NOLINT
|
||||
HeapObject::PrintHeader(os, "Tuple3");
|
||||
os << "\n - value1: " << Brief(value1());
|
||||
|
@ -6163,9 +6163,10 @@ void JSObject::ResetElements(Handle<JSObject> object) {
|
||||
void JSObject::RequireSlowElements(SeededNumberDictionary* dictionary) {
|
||||
if (dictionary->requires_slow_elements()) return;
|
||||
dictionary->set_requires_slow_elements();
|
||||
// TODO(verwaest): Remove this hack.
|
||||
if (map()->is_prototype_map()) {
|
||||
TypeFeedbackVector::ClearAllKeyedStoreICs(GetIsolate());
|
||||
// If this object is a prototype (the callee will check), invalidate any
|
||||
// prototype chains involving it.
|
||||
InvalidatePrototypeChains(map());
|
||||
}
|
||||
}
|
||||
|
||||
@ -15592,9 +15593,6 @@ Maybe<bool> JSObject::SetPrototype(Handle<JSObject> object,
|
||||
// SpiderMonkey behaves this way.
|
||||
if (!value->IsJSReceiver() && !value->IsNull(isolate)) return Just(true);
|
||||
|
||||
bool dictionary_elements_in_chain =
|
||||
object->map()->DictionaryElementsInPrototypeChainOnly();
|
||||
|
||||
bool all_extensible = object->map()->is_extensible();
|
||||
Handle<JSObject> real_receiver = object;
|
||||
if (from_javascript) {
|
||||
@ -15660,14 +15658,6 @@ Maybe<bool> JSObject::SetPrototype(Handle<JSObject> object,
|
||||
DCHECK(new_map->prototype() == *value);
|
||||
JSObject::MigrateToMap(real_receiver, new_map);
|
||||
|
||||
if (from_javascript && !dictionary_elements_in_chain &&
|
||||
new_map->DictionaryElementsInPrototypeChainOnly()) {
|
||||
// If the prototype chain didn't previously have element callbacks, then
|
||||
// KeyedStoreICs need to be cleared to ensure any that involve this
|
||||
// map go generic.
|
||||
TypeFeedbackVector::ClearAllKeyedStoreICs(isolate);
|
||||
}
|
||||
|
||||
heap->ClearInstanceofCache();
|
||||
DCHECK(size == object->Size());
|
||||
return Just(true);
|
||||
@ -17364,7 +17354,7 @@ Handle<Object> JSObject::PrepareSlowElementsForSort(
|
||||
return bailout;
|
||||
} else {
|
||||
Handle<Object> result = SeededNumberDictionary::AddNumberEntry(
|
||||
new_dict, pos, value, details, object->map()->is_prototype_map());
|
||||
new_dict, pos, value, details, object);
|
||||
DCHECK(result.is_identical_to(new_dict));
|
||||
USE(result);
|
||||
pos++;
|
||||
@ -17375,7 +17365,7 @@ Handle<Object> JSObject::PrepareSlowElementsForSort(
|
||||
return bailout;
|
||||
} else {
|
||||
Handle<Object> result = SeededNumberDictionary::AddNumberEntry(
|
||||
new_dict, key, value, details, object->map()->is_prototype_map());
|
||||
new_dict, key, value, details, object);
|
||||
DCHECK(result.is_identical_to(new_dict));
|
||||
USE(result);
|
||||
}
|
||||
@ -17392,7 +17382,7 @@ Handle<Object> JSObject::PrepareSlowElementsForSort(
|
||||
HandleScope scope(isolate);
|
||||
Handle<Object> result = SeededNumberDictionary::AddNumberEntry(
|
||||
new_dict, pos, isolate->factory()->undefined_value(), no_details,
|
||||
object->map()->is_prototype_map());
|
||||
object);
|
||||
DCHECK(result.is_identical_to(new_dict));
|
||||
USE(result);
|
||||
pos++;
|
||||
@ -18260,8 +18250,8 @@ bool SeededNumberDictionary::HasComplexElements() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void SeededNumberDictionary::UpdateMaxNumberKey(uint32_t key,
|
||||
bool used_as_prototype) {
|
||||
void SeededNumberDictionary::UpdateMaxNumberKey(
|
||||
uint32_t key, Handle<JSObject> dictionary_holder) {
|
||||
DisallowHeapAllocation no_allocation;
|
||||
// If the dictionary requires slow elements an element has already
|
||||
// been added at a high index.
|
||||
@ -18269,9 +18259,8 @@ void SeededNumberDictionary::UpdateMaxNumberKey(uint32_t key,
|
||||
// Check if this index is high enough that we should require slow
|
||||
// elements.
|
||||
if (key > kRequiresSlowElementsLimit) {
|
||||
if (used_as_prototype) {
|
||||
// TODO(verwaest): Remove this hack.
|
||||
TypeFeedbackVector::ClearAllKeyedStoreICs(GetIsolate());
|
||||
if (!dictionary_holder.is_null()) {
|
||||
dictionary_holder->RequireSlowElements(this);
|
||||
}
|
||||
set_requires_slow_elements();
|
||||
return;
|
||||
@ -18284,11 +18273,11 @@ void SeededNumberDictionary::UpdateMaxNumberKey(uint32_t key,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Handle<SeededNumberDictionary> SeededNumberDictionary::AddNumberEntry(
|
||||
Handle<SeededNumberDictionary> dictionary, uint32_t key,
|
||||
Handle<Object> value, PropertyDetails details, bool used_as_prototype) {
|
||||
dictionary->UpdateMaxNumberKey(key, used_as_prototype);
|
||||
Handle<Object> value, PropertyDetails details,
|
||||
Handle<JSObject> dictionary_holder) {
|
||||
dictionary->UpdateMaxNumberKey(key, dictionary_holder);
|
||||
SLOW_DCHECK(dictionary->FindEntry(key) == kNotFound);
|
||||
return Add(dictionary, key, value, details);
|
||||
}
|
||||
@ -18316,8 +18305,8 @@ Handle<UnseededNumberDictionary> UnseededNumberDictionary::DeleteKey(
|
||||
|
||||
Handle<SeededNumberDictionary> SeededNumberDictionary::AtNumberPut(
|
||||
Handle<SeededNumberDictionary> dictionary, uint32_t key,
|
||||
Handle<Object> value, bool used_as_prototype) {
|
||||
dictionary->UpdateMaxNumberKey(key, used_as_prototype);
|
||||
Handle<Object> value, Handle<JSObject> dictionary_holder) {
|
||||
dictionary->UpdateMaxNumberKey(key, dictionary_holder);
|
||||
return AtPut(dictionary, key, value);
|
||||
}
|
||||
|
||||
@ -18329,13 +18318,13 @@ Handle<UnseededNumberDictionary> UnseededNumberDictionary::AtNumberPut(
|
||||
return AtPut(dictionary, key, value);
|
||||
}
|
||||
|
||||
|
||||
Handle<SeededNumberDictionary> SeededNumberDictionary::Set(
|
||||
Handle<SeededNumberDictionary> dictionary, uint32_t key,
|
||||
Handle<Object> value, PropertyDetails details, bool used_as_prototype) {
|
||||
Handle<Object> value, PropertyDetails details,
|
||||
Handle<JSObject> dictionary_holder) {
|
||||
int entry = dictionary->FindEntry(key);
|
||||
if (entry == kNotFound) {
|
||||
return AddNumberEntry(dictionary, key, value, details, used_as_prototype);
|
||||
return AddNumberEntry(dictionary, key, value, details, dictionary_holder);
|
||||
}
|
||||
// Preserve enumeration index.
|
||||
details = details.set_index(dictionary->DetailsAt(entry).dictionary_index());
|
||||
|
@ -389,6 +389,7 @@ const int kStubMinorKeyBits = kSmiValueSize - kStubMajorKeyBits - 1;
|
||||
V(PROMISE_RESOLVE_THENABLE_JOB_INFO_TYPE) \
|
||||
V(PROMISE_REACTION_JOB_INFO_TYPE) \
|
||||
V(PROTOTYPE_INFO_TYPE) \
|
||||
V(TUPLE2_TYPE) \
|
||||
V(TUPLE3_TYPE) \
|
||||
V(CONTEXT_EXTENSION_TYPE) \
|
||||
V(MODULE_TYPE) \
|
||||
@ -556,6 +557,7 @@ const int kStubMinorKeyBits = kSmiValueSize - kStubMajorKeyBits - 1;
|
||||
V(DEBUG_INFO, DebugInfo, debug_info) \
|
||||
V(BREAK_POINT_INFO, BreakPointInfo, break_point_info) \
|
||||
V(PROTOTYPE_INFO, PrototypeInfo, prototype_info) \
|
||||
V(TUPLE2, Tuple2, tuple2) \
|
||||
V(TUPLE3, Tuple3, tuple3) \
|
||||
V(MODULE, Module, module) \
|
||||
V(MODULE_INFO_ENTRY, ModuleInfoEntry, module_info_entry) \
|
||||
@ -735,6 +737,7 @@ enum InstanceType {
|
||||
TRANSITION_ARRAY_TYPE,
|
||||
PROPERTY_CELL_TYPE,
|
||||
PROTOTYPE_INFO_TYPE,
|
||||
TUPLE2_TYPE,
|
||||
TUPLE3_TYPE,
|
||||
CONTEXT_EXTENSION_TYPE,
|
||||
MODULE_TYPE,
|
||||
@ -4012,18 +4015,20 @@ class SeededNumberDictionary
|
||||
// Type specific at put (default NONE attributes is used when adding).
|
||||
MUST_USE_RESULT static Handle<SeededNumberDictionary> AtNumberPut(
|
||||
Handle<SeededNumberDictionary> dictionary, uint32_t key,
|
||||
Handle<Object> value, bool used_as_prototype);
|
||||
Handle<Object> value, Handle<JSObject> dictionary_holder);
|
||||
MUST_USE_RESULT static Handle<SeededNumberDictionary> AddNumberEntry(
|
||||
Handle<SeededNumberDictionary> dictionary, uint32_t key,
|
||||
Handle<Object> value, PropertyDetails details, bool used_as_prototype);
|
||||
Handle<Object> value, PropertyDetails details,
|
||||
Handle<JSObject> dictionary_holder);
|
||||
|
||||
// Set an existing entry or add a new one if needed.
|
||||
// Return the updated dictionary.
|
||||
MUST_USE_RESULT static Handle<SeededNumberDictionary> Set(
|
||||
Handle<SeededNumberDictionary> dictionary, uint32_t key,
|
||||
Handle<Object> value, PropertyDetails details, bool used_as_prototype);
|
||||
Handle<Object> value, PropertyDetails details,
|
||||
Handle<JSObject> dictionary_holder);
|
||||
|
||||
void UpdateMaxNumberKey(uint32_t key, bool used_as_prototype);
|
||||
void UpdateMaxNumberKey(uint32_t key, Handle<JSObject> dictionary_holder);
|
||||
|
||||
// Returns true if the dictionary contains any elements that are non-writable,
|
||||
// non-configurable, non-enumerable, or have getters/setters.
|
||||
@ -6989,10 +6994,27 @@ class PrototypeInfo : public Struct {
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(PrototypeInfo);
|
||||
};
|
||||
|
||||
class Tuple3 : public Struct {
|
||||
class Tuple2 : public Struct {
|
||||
public:
|
||||
DECL_ACCESSORS(value1, Object)
|
||||
DECL_ACCESSORS(value2, Object)
|
||||
|
||||
DECLARE_CAST(Tuple2)
|
||||
|
||||
// Dispatched behavior.
|
||||
DECLARE_PRINTER(Tuple2)
|
||||
DECLARE_VERIFIER(Tuple2)
|
||||
|
||||
static const int kValue1Offset = HeapObject::kHeaderSize;
|
||||
static const int kValue2Offset = kValue1Offset + kPointerSize;
|
||||
static const int kSize = kValue2Offset + kPointerSize;
|
||||
|
||||
private:
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(Tuple2);
|
||||
};
|
||||
|
||||
class Tuple3 : public Tuple2 {
|
||||
public:
|
||||
DECL_ACCESSORS(value3, Object)
|
||||
|
||||
DECLARE_CAST(Tuple3)
|
||||
@ -7001,9 +7023,7 @@ class Tuple3 : public Struct {
|
||||
DECLARE_PRINTER(Tuple3)
|
||||
DECLARE_VERIFIER(Tuple3)
|
||||
|
||||
static const int kValue1Offset = HeapObject::kHeaderSize;
|
||||
static const int kValue2Offset = kValue1Offset + kPointerSize;
|
||||
static const int kValue3Offset = kValue2Offset + kPointerSize;
|
||||
static const int kValue3Offset = Tuple2::kSize;
|
||||
static const int kSize = kValue3Offset + kPointerSize;
|
||||
|
||||
private:
|
||||
|
@ -326,50 +326,6 @@ void TypeFeedbackVector::ClearSlotsImpl(SharedFunctionInfo* shared,
|
||||
}
|
||||
|
||||
|
||||
// static
|
||||
void TypeFeedbackVector::ClearAllKeyedStoreICs(Isolate* isolate) {
|
||||
SharedFunctionInfo::Iterator iterator(isolate);
|
||||
SharedFunctionInfo* shared;
|
||||
while ((shared = iterator.Next())) {
|
||||
if (!shared->OptimizedCodeMapIsCleared()) {
|
||||
FixedArray* optimized_code_map = shared->optimized_code_map();
|
||||
int length = optimized_code_map->length();
|
||||
for (int i = SharedFunctionInfo::kEntriesStart; i < length;
|
||||
i += SharedFunctionInfo::kEntryLength) {
|
||||
WeakCell* cell = WeakCell::cast(
|
||||
optimized_code_map->get(i + SharedFunctionInfo::kLiteralsOffset));
|
||||
if (cell->value()->IsLiteralsArray()) {
|
||||
TypeFeedbackVector* vector =
|
||||
LiteralsArray::cast(cell->value())->feedback_vector();
|
||||
vector->ClearKeyedStoreICs(shared);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TypeFeedbackVector::ClearKeyedStoreICs(SharedFunctionInfo* shared) {
|
||||
Isolate* isolate = GetIsolate();
|
||||
|
||||
Code* host = shared->code();
|
||||
Object* uninitialized_sentinel =
|
||||
TypeFeedbackVector::RawUninitializedSentinel(isolate);
|
||||
|
||||
TypeFeedbackMetadataIterator iter(metadata());
|
||||
while (iter.HasNext()) {
|
||||
FeedbackVectorSlot slot = iter.Next();
|
||||
FeedbackVectorSlotKind kind = iter.kind();
|
||||
if (kind != FeedbackVectorSlotKind::KEYED_STORE_IC) continue;
|
||||
Object* obj = Get(slot);
|
||||
if (obj != uninitialized_sentinel) {
|
||||
KeyedStoreICNexus nexus(this, slot);
|
||||
nexus.Clear(host);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// static
|
||||
Handle<TypeFeedbackVector> TypeFeedbackVector::DummyVector(Isolate* isolate) {
|
||||
return isolate->factory()->dummy_vector();
|
||||
@ -760,10 +716,9 @@ void KeyedStoreICNexus::ConfigurePolymorphic(Handle<Name> name,
|
||||
InstallHandlers(array, maps, handlers);
|
||||
}
|
||||
|
||||
|
||||
void KeyedStoreICNexus::ConfigurePolymorphic(MapHandleList* maps,
|
||||
MapHandleList* transitioned_maps,
|
||||
CodeHandleList* handlers) {
|
||||
List<Handle<Object>>* handlers) {
|
||||
int receiver_count = maps->length();
|
||||
DCHECK(receiver_count > 1);
|
||||
Handle<FixedArray> array = EnsureArrayOfSize(receiver_count * 3);
|
||||
@ -960,7 +915,14 @@ KeyedAccessStoreMode KeyedStoreICNexus::GetKeyedAccessStoreMode() const {
|
||||
FindHandlers(&handlers, maps.length());
|
||||
for (int i = 0; i < handlers.length(); i++) {
|
||||
// The first handler that isn't the slow handler will have the bits we need.
|
||||
Handle<Code> handler = Handle<Code>::cast(handlers.at(i));
|
||||
Handle<Object> maybe_code_handler = handlers.at(i);
|
||||
Handle<Code> handler;
|
||||
if (maybe_code_handler->IsTuple2()) {
|
||||
Handle<Tuple2> data_handler = Handle<Tuple2>::cast(maybe_code_handler);
|
||||
handler = handle(Code::cast(data_handler->value2()));
|
||||
} else {
|
||||
handler = Handle<Code>::cast(maybe_code_handler);
|
||||
}
|
||||
CodeStub::Major major_key = CodeStub::MajorKeyFromKey(handler->stub_key());
|
||||
uint32_t minor_key = CodeStub::MinorKeyFromKey(handler->stub_key());
|
||||
CHECK(major_key == CodeStub::KeyedStoreSloppyArguments ||
|
||||
|
@ -264,9 +264,6 @@ class TypeFeedbackVector : public FixedArray {
|
||||
ClearSlotsImpl(shared, false);
|
||||
}
|
||||
|
||||
static void ClearAllKeyedStoreICs(Isolate* isolate);
|
||||
void ClearKeyedStoreICs(SharedFunctionInfo* shared);
|
||||
|
||||
// The object that indicates an uninitialized cache.
|
||||
static inline Handle<Symbol> UninitializedSentinel(Isolate* isolate);
|
||||
|
||||
@ -606,7 +603,7 @@ class KeyedStoreICNexus : public FeedbackNexus {
|
||||
List<Handle<Object>>* handlers);
|
||||
void ConfigurePolymorphic(MapHandleList* maps,
|
||||
MapHandleList* transitioned_maps,
|
||||
CodeHandleList* handlers);
|
||||
List<Handle<Object>>* handlers);
|
||||
void ConfigureMegamorphicKeyed(IcCheckType property_type);
|
||||
|
||||
KeyedAccessStoreMode GetKeyedAccessStoreMode() const;
|
||||
|
@ -1006,10 +1006,10 @@ void ValueDeserializer::TransferArrayBuffer(
|
||||
}
|
||||
Handle<SeededNumberDictionary> dictionary =
|
||||
array_buffer_transfer_map_.ToHandleChecked();
|
||||
const bool used_as_prototype = false;
|
||||
Handle<JSObject> not_a_prototype_holder;
|
||||
Handle<SeededNumberDictionary> new_dictionary =
|
||||
SeededNumberDictionary::AtNumberPut(dictionary, transfer_id, array_buffer,
|
||||
used_as_prototype);
|
||||
not_a_prototype_holder);
|
||||
if (!new_dictionary.is_identical_to(dictionary)) {
|
||||
GlobalHandles::Destroy(Handle<Object>::cast(dictionary).location());
|
||||
array_buffer_transfer_map_ = Handle<SeededNumberDictionary>::cast(
|
||||
|
53
test/mjsunit/regress/regress-crbug-662907.js
Normal file
53
test/mjsunit/regress/regress-crbug-662907.js
Normal file
@ -0,0 +1,53 @@
|
||||
// Copyright 2016 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --allow-natives-syntax --expose-gc
|
||||
|
||||
(function() {
|
||||
function foo() {
|
||||
var a = new Array();
|
||||
a[0] = 10;
|
||||
return a;
|
||||
}
|
||||
|
||||
assertEquals(1, foo().length);
|
||||
|
||||
gc();
|
||||
gc();
|
||||
gc();
|
||||
gc();
|
||||
|
||||
// Change prototype elements from fast smi to slow elements dictionary.
|
||||
// The validity cell is invalidated by the change of Array.prototype's
|
||||
// map.
|
||||
Array.prototype.__defineSetter__("0", function() {});
|
||||
|
||||
assertEquals(0, foo().length);
|
||||
})();
|
||||
|
||||
|
||||
(function() {
|
||||
function foo() {
|
||||
var a = new Array();
|
||||
a[0] = 10;
|
||||
return a;
|
||||
}
|
||||
|
||||
// Change prototype elements from fast smi to dictionary.
|
||||
Array.prototype[123456789] = 42;
|
||||
Array.prototype.length = 0;
|
||||
|
||||
assertEquals(1, foo().length);
|
||||
|
||||
gc();
|
||||
gc();
|
||||
gc();
|
||||
gc();
|
||||
|
||||
// Change prototype elements from dictionary to slow elements dictionary.
|
||||
// The validity cell is invalidated by making the elements dictionary slow.
|
||||
Array.prototype.__defineSetter__("0", function() {});
|
||||
|
||||
assertEquals(0, foo().length);
|
||||
})();
|
11
test/mjsunit/regress/regress-crbug-669411.js
Normal file
11
test/mjsunit/regress/regress-crbug-669411.js
Normal file
@ -0,0 +1,11 @@
|
||||
// Copyright 2016 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
function f(o) {
|
||||
o[5000000] = 0;
|
||||
}
|
||||
var o = Object.create(null);
|
||||
f(o);
|
||||
f(o);
|
||||
f(o);
|
Loading…
Reference in New Issue
Block a user