[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:
ishell 2016-12-02 02:03:18 -08:00 committed by Commit bot
parent 13afe42705
commit 39e6f2ca4a
26 changed files with 286 additions and 160 deletions

View File

@ -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;

View File

@ -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();

View File

@ -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);
}

View File

@ -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

View File

@ -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();

View File

@ -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);

View File

@ -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));

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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);
}

View File

@ -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);
};

View File

@ -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());
}

View File

@ -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);

View File

@ -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);

View File

@ -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()) {

View File

@ -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);

View File

@ -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)

View File

@ -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());

View File

@ -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());

View File

@ -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:

View File

@ -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 ||

View File

@ -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;

View File

@ -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(

View 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);
})();

View 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);