Commit 6ea4061b authored by ishell's avatar ishell Committed by Commit bot

[ic] Load IC data handlers now support prototype chain checks with global and dictionary objects.

BUG=v8:5561

Review-Url: https://codereview.chromium.org/2449463002
Cr-Commit-Position: refs/heads/master@{#40626}
parent 3a5056a2
...@@ -5086,7 +5086,7 @@ void CodeStubAssembler::TryProbeStubCacheTable( ...@@ -5086,7 +5086,7 @@ void CodeStubAssembler::TryProbeStubCacheTable(
DCHECK_EQ(kPointerSize, stub_cache->value_reference(table).address() - DCHECK_EQ(kPointerSize, stub_cache->value_reference(table).address() -
stub_cache->key_reference(table).address()); stub_cache->key_reference(table).address());
Node* handler = Load(MachineType::Pointer(), key_base, Node* handler = Load(MachineType::TaggedPointer(), key_base,
IntPtrAdd(entry_offset, IntPtrConstant(kPointerSize))); IntPtrAdd(entry_offset, IntPtrConstant(kPointerSize)));
// We found the handler. // We found the handler.
...@@ -5530,29 +5530,106 @@ void CodeStubAssembler::HandleLoadICProtoHandler( ...@@ -5530,29 +5530,106 @@ void CodeStubAssembler::HandleLoadICProtoHandler(
DCHECK_EQ(MachineRepresentation::kTagged, var_holder->rep()); DCHECK_EQ(MachineRepresentation::kTagged, var_holder->rep());
DCHECK_EQ(MachineRepresentation::kTagged, var_smi_handler->rep()); DCHECK_EQ(MachineRepresentation::kTagged, var_smi_handler->rep());
Node* validity_cell = LoadObjectField(handler, Tuple3::kValue1Offset); // IC dispatchers rely on these assumptions to be held.
STATIC_ASSERT(FixedArray::kLengthOffset == LoadHandler::kHolderCellOffset);
DCHECK_EQ(FixedArray::OffsetOfElementAt(LoadHandler::kSmiHandlerIndex),
LoadHandler::kSmiHandlerOffset);
DCHECK_EQ(FixedArray::OffsetOfElementAt(LoadHandler::kValidityCellIndex),
LoadHandler::kValidityCellOffset);
// Both FixedArray and Tuple3 handlers have validity cell at the same offset.
Node* validity_cell =
LoadObjectField(handler, LoadHandler::kValidityCellOffset);
Node* cell_value = LoadObjectField(validity_cell, Cell::kValueOffset); Node* cell_value = LoadObjectField(validity_cell, Cell::kValueOffset);
GotoIf(WordNotEqual(cell_value, GotoIf(WordNotEqual(cell_value,
SmiConstant(Smi::FromInt(Map::kPrototypeChainValid))), SmiConstant(Smi::FromInt(Map::kPrototypeChainValid))),
miss); miss);
Node* holder = Node* smi_handler = LoadObjectField(handler, LoadHandler::kSmiHandlerOffset);
LoadWeakCellValue(LoadObjectField(handler, Tuple3::kValue2Offset));
// The |holder| is guaranteed to be alive at this point since we passed
// both the receiver map check and the validity cell check.
CSA_ASSERT(WordNotEqual(holder, IntPtrConstant(0)));
Node* smi_handler = LoadObjectField(handler, Tuple3::kValue3Offset);
CSA_ASSERT(TaggedIsSmi(smi_handler)); CSA_ASSERT(TaggedIsSmi(smi_handler));
var_holder->Bind(holder);
var_smi_handler->Bind(smi_handler);
Label check_prototypes(this);
GotoUnless(IsSetWord<LoadHandler::DoNegativeLookupOnReceiverBits>( GotoUnless(IsSetWord<LoadHandler::DoNegativeLookupOnReceiverBits>(
SmiUntag(smi_handler)), SmiUntag(smi_handler)),
if_smi_handler); &check_prototypes);
{
// We have a dictionary receiver, do a negative lookup check.
NameDictionaryNegativeLookup(p->receiver, p->name, miss);
Goto(&check_prototypes);
}
Bind(&check_prototypes);
Node* maybe_holder_cell =
LoadObjectField(handler, LoadHandler::kHolderCellOffset);
Label array_handler(this), tuple_handler(this);
Branch(TaggedIsSmi(maybe_holder_cell), &array_handler, &tuple_handler);
Bind(&tuple_handler);
{
Node* holder = LoadWeakCellValue(maybe_holder_cell);
// The |holder| is guaranteed to be alive at this point since we passed
// both the receiver map check and the validity cell check.
CSA_ASSERT(WordNotEqual(holder, IntPtrConstant(0)));
var_holder->Bind(holder);
var_smi_handler->Bind(smi_handler);
Goto(if_smi_handler);
}
Bind(&array_handler);
{
Node* length = SmiUntag(maybe_holder_cell);
BuildFastLoop(MachineType::PointerRepresentation(),
IntPtrConstant(LoadHandler::kFirstPrototypeIndex), length,
[this, p, handler, miss](CodeStubAssembler*, Node* current) {
Node* prototype_cell = LoadFixedArrayElement(
handler, current, 0, INTPTR_PARAMETERS);
CheckPrototype(prototype_cell, p->name, miss);
},
1, IndexAdvanceMode::kPost);
Node* holder_cell = LoadFixedArrayElement(
handler, IntPtrConstant(LoadHandler::kHolderCellIndex), 0,
INTPTR_PARAMETERS);
Node* holder = LoadWeakCellValue(holder_cell);
// The |holder| is guaranteed to be alive at this point since we passed
// the receiver map check, the validity cell check and the prototype chain
// check.
CSA_ASSERT(WordNotEqual(holder, IntPtrConstant(0)));
var_holder->Bind(holder);
var_smi_handler->Bind(smi_handler);
Goto(if_smi_handler);
}
}
void CodeStubAssembler::CheckPrototype(Node* prototype_cell, Node* name,
Label* miss) {
Node* maybe_prototype = LoadWeakCellValue(prototype_cell, miss);
Label done(this);
Label if_property_cell(this), if_dictionary_object(this);
// |maybe_prototype| is either a PropertyCell or a slow-mode prototype.
Branch(WordEqual(LoadMap(maybe_prototype),
LoadRoot(Heap::kGlobalPropertyCellMapRootIndex)),
&if_property_cell, &if_dictionary_object);
Bind(&if_dictionary_object);
{
NameDictionaryNegativeLookup(maybe_prototype, name, miss);
Goto(&done);
}
NameDictionaryNegativeLookup(p->receiver, p->name, miss); Bind(&if_property_cell);
Goto(if_smi_handler); {
// Ensure the property cell still contains the hole.
Node* value = LoadObjectField(maybe_prototype, PropertyCell::kValueOffset);
GotoIf(WordNotEqual(value, LoadRoot(Heap::kTheHoleValueRootIndex)), miss);
Goto(&done);
}
Bind(&done);
} }
void CodeStubAssembler::NameDictionaryNegativeLookup(Node* object, Node* name, void CodeStubAssembler::NameDictionaryNegativeLookup(Node* object, Node* name,
......
...@@ -1107,6 +1107,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler { ...@@ -1107,6 +1107,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
Variable* var_smi_handler, Variable* var_smi_handler,
Label* if_smi_handler, Label* miss); Label* if_smi_handler, Label* miss);
void CheckPrototype(compiler::Node* prototype_cell, compiler::Node* name,
Label* miss);
void NameDictionaryNegativeLookup(compiler::Node* object, void NameDictionaryNegativeLookup(compiler::Node* object,
compiler::Node* name, Label* miss); compiler::Node* name, Label* miss);
......
...@@ -478,10 +478,7 @@ Register PropertyHandlerCompiler::CheckPrototypes( ...@@ -478,10 +478,7 @@ Register PropertyHandlerCompiler::CheckPrototypes(
name, scratch2, miss); name, scratch2, miss);
} else if (current_map->is_dictionary_map()) { } else if (current_map->is_dictionary_map()) {
DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast. DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast.
if (!name->IsUniqueName()) { DCHECK(name->IsUniqueName());
DCHECK(name->IsString());
name = factory()->InternalizeString(Handle<String>::cast(name));
}
DCHECK(current.is_null() || DCHECK(current.is_null() ||
current->property_dictionary()->FindEntry(name) == current->property_dictionary()->FindEntry(name) ==
NameDictionary::kNotFound); NameDictionary::kNotFound);
......
...@@ -508,10 +508,7 @@ Register PropertyHandlerCompiler::CheckPrototypes( ...@@ -508,10 +508,7 @@ Register PropertyHandlerCompiler::CheckPrototypes(
name, scratch2, miss); name, scratch2, miss);
} else if (current_map->is_dictionary_map()) { } else if (current_map->is_dictionary_map()) {
DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast. DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast.
if (!name->IsUniqueName()) { DCHECK(name->IsUniqueName());
DCHECK(name->IsString());
name = factory()->InternalizeString(Handle<String>::cast(name));
}
DCHECK(current.is_null() || (current->property_dictionary()->FindEntry( DCHECK(current.is_null() || (current->property_dictionary()->FindEntry(
name) == NameDictionary::kNotFound)); name) == NameDictionary::kNotFound));
......
...@@ -59,6 +59,21 @@ class LoadHandler { ...@@ -59,6 +59,21 @@ class LoadHandler {
// Make sure we don't overflow the smi. // Make sure we don't overflow the smi.
STATIC_ASSERT(ElementsKindBits::kNext <= kSmiValueSize); STATIC_ASSERT(ElementsKindBits::kNext <= kSmiValueSize);
// The layout of an Tuple3 handler representing a load of a field from
// prototype when prototype chain checks do not include non-existing lookups
// or access checks.
static const int kHolderCellOffset = Tuple3::kValue1Offset;
static const int kSmiHandlerOffset = Tuple3::kValue2Offset;
static const int kValidityCellOffset = Tuple3::kValue3Offset;
// The layout of an array handler representing a load of a field from
// prototype when prototype chain checks include non-existing lookups and
// access checks.
static const int kSmiHandlerIndex = 0;
static const int kValidityCellIndex = 1;
static const int kHolderCellIndex = 2;
static const int kFirstPrototypeIndex = 3;
// Creates a Smi-handler for loading a field from fast object. // Creates a Smi-handler for loading a field from fast object.
static inline Handle<Object> LoadField(Isolate* isolate, static inline Handle<Object> LoadField(Isolate* isolate,
FieldIndex field_index); FieldIndex field_index);
......
...@@ -482,10 +482,7 @@ Register PropertyHandlerCompiler::CheckPrototypes( ...@@ -482,10 +482,7 @@ Register PropertyHandlerCompiler::CheckPrototypes(
name, scratch2, miss); name, scratch2, miss);
} else if (current_map->is_dictionary_map()) { } else if (current_map->is_dictionary_map()) {
DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast. DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast.
if (!name->IsUniqueName()) { DCHECK(name->IsUniqueName());
DCHECK(name->IsString());
name = factory()->InternalizeString(Handle<String>::cast(name));
}
DCHECK(current.is_null() || DCHECK(current.is_null() ||
current->property_dictionary()->FindEntry(name) == current->property_dictionary()->FindEntry(name) ==
NameDictionary::kNotFound); NameDictionary::kNotFound);
......
...@@ -93,7 +93,8 @@ Code* IC::target() const { ...@@ -93,7 +93,8 @@ Code* IC::target() const {
} }
bool IC::IsHandler(Object* object) { bool IC::IsHandler(Object* object) {
return (object->IsSmi() && (object != nullptr)) || (object->IsTuple3()) || return (object->IsSmi() && (object != nullptr)) || object->IsTuple3() ||
object->IsFixedArray() ||
(object->IsCode() && Code::cast(object)->is_handler()); (object->IsCode() && Code::cast(object)->is_handler());
} }
......
...@@ -853,36 +853,77 @@ Handle<Object> LoadIC::SimpleFieldLoad(FieldIndex index) { ...@@ -853,36 +853,77 @@ Handle<Object> LoadIC::SimpleFieldLoad(FieldIndex index) {
return stub.GetCode(); return stub.GetCode();
} }
bool LoadIC::IsPrototypeValidityCellCheckEnough(Handle<Map> receiver_map, namespace {
Handle<JSObject> holder) {
template <bool fill_array>
int InitPrototypeChecks(Isolate* isolate, Handle<Map> receiver_map,
Handle<JSObject> holder, Handle<FixedArray> array,
Handle<Name> name) {
DCHECK(holder->HasFastProperties()); DCHECK(holder->HasFastProperties());
// The following kinds of receiver maps require custom handler compilation. // The following kinds of receiver maps require custom handler compilation.
if (receiver_map->IsPrimitiveMap() || receiver_map->IsJSGlobalProxyMap() || if (receiver_map->IsPrimitiveMap() || receiver_map->IsJSGlobalProxyMap() ||
receiver_map->IsJSGlobalObjectMap()) { receiver_map->IsJSGlobalObjectMap()) {
return false; return -1;
} }
HandleScope scope(isolate);
int checks_count = 0;
// Switch to custom compiled handler if the prototype chain contains global // Switch to custom compiled handler if the prototype chain contains global
// or dictionary objects. // or dictionary objects.
for (PrototypeIterator iter(*receiver_map); !iter.IsAtEnd(); iter.Advance()) { for (PrototypeIterator iter(receiver_map); !iter.IsAtEnd(); iter.Advance()) {
JSObject* current = iter.GetCurrent<JSObject>(); Handle<JSObject> current = PrototypeIterator::GetCurrent<JSObject>(iter);
if (current == *holder) break; if (*current == *holder) break;
Map* current_map = current->map(); Handle<Map> current_map(current->map(), isolate);
// Only global objects and objects that do not require access
// checks are allowed in stubs.
DCHECK(current_map->IsJSGlobalProxyMap() ||
!current_map->is_access_check_needed());
if (current_map->IsJSGlobalObjectMap()) { if (current_map->IsJSGlobalObjectMap()) {
return false; if (fill_array) {
Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(current);
Handle<PropertyCell> cell = JSGlobalObject::EnsureEmptyPropertyCell(
global, name, PropertyCellType::kInvalidated);
DCHECK(cell->value()->IsTheHole(isolate));
Handle<WeakCell> weak_cell = isolate->factory()->NewWeakCell(cell);
array->set(LoadHandler::kFirstPrototypeIndex + checks_count,
*weak_cell);
}
checks_count++;
} else if (current_map->is_dictionary_map()) { } else if (current_map->is_dictionary_map()) {
DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast. DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast.
return false; if (fill_array) {
DCHECK_EQ(NameDictionary::kNotFound,
current->property_dictionary()->FindEntry(name));
Handle<WeakCell> weak_cell =
Map::GetOrCreatePrototypeWeakCell(current, isolate);
array->set(LoadHandler::kFirstPrototypeIndex + checks_count,
*weak_cell);
}
checks_count++;
} }
} }
return true; return checks_count;
}
} // namespace
int LoadIC::GetPrototypeCheckCount(Handle<Map> receiver_map,
Handle<JSObject> holder) {
return InitPrototypeChecks<false>(isolate(), receiver_map, holder,
Handle<FixedArray>(), Handle<Name>());
} }
Handle<Object> LoadIC::SimpleLoadFromPrototype(Handle<Map> receiver_map, Handle<Object> LoadIC::SimpleLoadFromPrototype(Handle<Map> receiver_map,
Handle<JSObject> holder, Handle<JSObject> holder,
Handle<Name> name,
Handle<Object> smi_handler) { Handle<Object> smi_handler) {
DCHECK(IsPrototypeValidityCellCheckEnough(receiver_map, holder)); int checks_count = GetPrototypeCheckCount(receiver_map, holder);
DCHECK_LE(0, checks_count);
if (receiver_map->IsJSGlobalProxyMap() || if (receiver_map->IsJSGlobalProxyMap() ||
receiver_map->IsJSGlobalObjectMap()) { receiver_map->IsJSGlobalObjectMap()) {
...@@ -898,8 +939,19 @@ Handle<Object> LoadIC::SimpleLoadFromPrototype(Handle<Map> receiver_map, ...@@ -898,8 +939,19 @@ Handle<Object> LoadIC::SimpleLoadFromPrototype(Handle<Map> receiver_map,
Handle<WeakCell> holder_cell = Handle<WeakCell> holder_cell =
Map::GetOrCreatePrototypeWeakCell(holder, isolate()); Map::GetOrCreatePrototypeWeakCell(holder, isolate());
return isolate()->factory()->NewTuple3(validity_cell, holder_cell,
smi_handler); if (checks_count == 0) {
return isolate()->factory()->NewTuple3(holder_cell, smi_handler,
validity_cell);
}
Handle<FixedArray> handler_array(isolate()->factory()->NewFixedArray(
LoadHandler::kFirstPrototypeIndex + checks_count, TENURED));
handler_array->set(LoadHandler::kSmiHandlerIndex, *smi_handler);
handler_array->set(LoadHandler::kValidityCellIndex, *validity_cell);
handler_array->set(LoadHandler::kHolderCellIndex, *holder_cell);
InitPrototypeChecks<true>(isolate(), receiver_map, holder, handler_array,
name);
return handler_array;
} }
bool IsCompatibleReceiver(LookupIterator* lookup, Handle<Map> receiver_map) { bool IsCompatibleReceiver(LookupIterator* lookup, Handle<Map> receiver_map) {
...@@ -1237,10 +1289,10 @@ Handle<Object> LoadIC::GetMapIndependentHandler(LookupIterator* lookup) { ...@@ -1237,10 +1289,10 @@ Handle<Object> LoadIC::GetMapIndependentHandler(LookupIterator* lookup) {
if (receiver_is_holder) { if (receiver_is_holder) {
return smi_handler; return smi_handler;
} }
if (FLAG_tf_load_ic_stub && if (FLAG_tf_load_ic_stub && GetPrototypeCheckCount(map, holder) >= 0) {
IsPrototypeValidityCellCheckEnough(map, holder)) {
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadFieldFromPrototypeDH); TRACE_HANDLER_STATS(isolate(), LoadIC_LoadFieldFromPrototypeDH);
return SimpleLoadFromPrototype(map, holder, smi_handler); return SimpleLoadFromPrototype(map, holder, lookup->name(),
smi_handler);
} }
break; // Custom-compiled handler. break; // Custom-compiled handler.
} }
...@@ -1254,9 +1306,10 @@ Handle<Object> LoadIC::GetMapIndependentHandler(LookupIterator* lookup) { ...@@ -1254,9 +1306,10 @@ Handle<Object> LoadIC::GetMapIndependentHandler(LookupIterator* lookup) {
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadConstantDH); TRACE_HANDLER_STATS(isolate(), LoadIC_LoadConstantDH);
return smi_handler; return smi_handler;
} }
if (IsPrototypeValidityCellCheckEnough(map, holder)) { if (GetPrototypeCheckCount(map, holder) >= 0) {
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadConstantFromPrototypeDH); TRACE_HANDLER_STATS(isolate(), LoadIC_LoadConstantFromPrototypeDH);
return SimpleLoadFromPrototype(map, holder, smi_handler); return SimpleLoadFromPrototype(map, holder, lookup->name(),
smi_handler);
} }
} else { } else {
if (receiver_is_holder) { if (receiver_is_holder) {
......
...@@ -311,16 +311,18 @@ class LoadIC : public IC { ...@@ -311,16 +311,18 @@ class LoadIC : public IC {
private: private:
Handle<Object> SimpleFieldLoad(FieldIndex index); Handle<Object> SimpleFieldLoad(FieldIndex index);
// Returns true if the validity cell check is enough to ensure that the // Returns 0 if the validity cell check is enough to ensure that the
// prototype chain from |receiver_map| till |holder| did not change. // prototype chain from |receiver_map| till |holder| did not change.
bool IsPrototypeValidityCellCheckEnough(Handle<Map> receiver_map, // Returns -1 if the handler has to be compiled or the number of prototype
Handle<JSObject> holder); // checks otherwise.
int GetPrototypeCheckCount(Handle<Map> receiver_map, Handle<JSObject> holder);
// Creates a data handler that represents a prototype chain check followed // Creates a data handler that represents a prototype chain check followed
// by given Smi-handler that encoded a load from the holder. // by given Smi-handler that encoded a load from the holder.
// Can be used only if IsPrototypeValidityCellCheckEnough() predicate is true. // Can be used only if IsPrototypeValidityCellCheckEnough() predicate is true.
Handle<Object> SimpleLoadFromPrototype(Handle<Map> receiver_map, Handle<Object> SimpleLoadFromPrototype(Handle<Map> receiver_map,
Handle<JSObject> holder, Handle<JSObject> holder,
Handle<Name> name,
Handle<Object> smi_handler); Handle<Object> smi_handler);
friend class IC; friend class IC;
......
...@@ -464,10 +464,7 @@ Register PropertyHandlerCompiler::CheckPrototypes( ...@@ -464,10 +464,7 @@ Register PropertyHandlerCompiler::CheckPrototypes(
name, scratch2, miss); name, scratch2, miss);
} else if (current_map->is_dictionary_map()) { } else if (current_map->is_dictionary_map()) {
DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast. DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast.
if (!name->IsUniqueName()) { DCHECK(name->IsUniqueName());
DCHECK(name->IsString());
name = factory()->InternalizeString(Handle<String>::cast(name));
}
DCHECK(current.is_null() || DCHECK(current.is_null() ||
current->property_dictionary()->FindEntry(name) == current->property_dictionary()->FindEntry(name) ==
NameDictionary::kNotFound); NameDictionary::kNotFound);
......
...@@ -464,10 +464,7 @@ Register PropertyHandlerCompiler::CheckPrototypes( ...@@ -464,10 +464,7 @@ Register PropertyHandlerCompiler::CheckPrototypes(
name, scratch2, miss); name, scratch2, miss);
} else if (current_map->is_dictionary_map()) { } else if (current_map->is_dictionary_map()) {
DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast. DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast.
if (!name->IsUniqueName()) { DCHECK(name->IsUniqueName());
DCHECK(name->IsString());
name = factory()->InternalizeString(Handle<String>::cast(name));
}
DCHECK(current.is_null() || DCHECK(current.is_null() ||
current->property_dictionary()->FindEntry(name) == current->property_dictionary()->FindEntry(name) ==
NameDictionary::kNotFound); NameDictionary::kNotFound);
......
...@@ -472,10 +472,7 @@ Register PropertyHandlerCompiler::CheckPrototypes( ...@@ -472,10 +472,7 @@ Register PropertyHandlerCompiler::CheckPrototypes(
name, scratch2, miss); name, scratch2, miss);
} else if (current_map->is_dictionary_map()) { } else if (current_map->is_dictionary_map()) {
DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast. DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast.
if (!name->IsUniqueName()) { DCHECK(name->IsUniqueName());
DCHECK(name->IsString());
name = factory()->InternalizeString(Handle<String>::cast(name));
}
DCHECK(current.is_null() || DCHECK(current.is_null() ||
current->property_dictionary()->FindEntry(name) == current->property_dictionary()->FindEntry(name) ==
NameDictionary::kNotFound); NameDictionary::kNotFound);
......
...@@ -452,10 +452,7 @@ Register PropertyHandlerCompiler::CheckPrototypes( ...@@ -452,10 +452,7 @@ Register PropertyHandlerCompiler::CheckPrototypes(
name, scratch2, miss); name, scratch2, miss);
} else if (current_map->is_dictionary_map()) { } else if (current_map->is_dictionary_map()) {
DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast. DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast.
if (!name->IsUniqueName()) { DCHECK(name->IsUniqueName());
DCHECK(name->IsString());
name = factory()->InternalizeString(Handle<String>::cast(name));
}
DCHECK(current.is_null() || DCHECK(current.is_null() ||
current->property_dictionary()->FindEntry(name) == current->property_dictionary()->FindEntry(name) ==
NameDictionary::kNotFound); NameDictionary::kNotFound);
......
...@@ -30,9 +30,10 @@ namespace { ...@@ -30,9 +30,10 @@ namespace {
bool CommonStubCacheChecks(StubCache* stub_cache, Name* name, Map* map, bool CommonStubCacheChecks(StubCache* stub_cache, Name* name, Map* map,
Object* handler) { Object* handler) {
// Validate that the name does not move on scavenge, and that we // Validate that the name and handler do not move on scavenge, and that we
// can use identity checks instead of structural equality checks. // can use identity checks instead of structural equality checks.
DCHECK(!name->GetHeap()->InNewSpace(name)); DCHECK(!name->GetHeap()->InNewSpace(name));
DCHECK(!name->GetHeap()->InNewSpace(handler));
DCHECK(name->IsUniqueName()); DCHECK(name->IsUniqueName());
DCHECK(name->HasHashCode()); DCHECK(name->HasHashCode());
if (handler) { if (handler) {
......
...@@ -475,10 +475,7 @@ Register PropertyHandlerCompiler::CheckPrototypes( ...@@ -475,10 +475,7 @@ Register PropertyHandlerCompiler::CheckPrototypes(
name, scratch2, miss); name, scratch2, miss);
} else if (current_map->is_dictionary_map()) { } else if (current_map->is_dictionary_map()) {
DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast. DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast.
if (!name->IsUniqueName()) { DCHECK(name->IsUniqueName());
DCHECK(name->IsString());
name = factory()->InternalizeString(Handle<String>::cast(name));
}
DCHECK(current.is_null() || DCHECK(current.is_null() ||
current->property_dictionary()->FindEntry(name) == current->property_dictionary()->FindEntry(name) ==
NameDictionary::kNotFound); NameDictionary::kNotFound);
......
...@@ -482,10 +482,7 @@ Register PropertyHandlerCompiler::CheckPrototypes( ...@@ -482,10 +482,7 @@ Register PropertyHandlerCompiler::CheckPrototypes(
name, scratch2, miss); name, scratch2, miss);
} else if (current_map->is_dictionary_map()) { } else if (current_map->is_dictionary_map()) {
DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast. DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast.
if (!name->IsUniqueName()) { DCHECK(name->IsUniqueName());
DCHECK(name->IsString());
name = factory()->InternalizeString(Handle<String>::cast(name));
}
DCHECK(current.is_null() || DCHECK(current.is_null() ||
current->property_dictionary()->FindEntry(name) == current->property_dictionary()->FindEntry(name) ==
NameDictionary::kNotFound); NameDictionary::kNotFound);
......
// 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
// Dictionary object in the prototype chain.
(function() {
function A() {
this.z = "a";
}
var a = new A();
function B() {
this.b = "b";
}
B.prototype = a;
var b = new B();
// Ensure b stays slow.
for (var i = 0; i < 1200; i++) {
b["b"+i] = 0;
}
assertFalse(%HasFastProperties(b));
function C() {
this.c = "c";
}
C.prototype = b;
var c = new C();
function f(expected) {
assertFalse(%HasFastProperties(b));
var result = c.z;
assertEquals(expected, result);
}
f("a");
f("a");
f("a");
%OptimizeFunctionOnNextCall(f);
f("a");
a.z = "z";
f("z");
f("z");
f("z");
b.z = "bz";
f("bz");
f("bz");
f("bz");
})();
// Global object in the prototype chain.
(function() {
var global = this;
function A() {
this.z = "a";
}
A.prototype = global.__proto__;
var a = new A();
global.__proto__ = a;
function C() {
this.c = "c";
}
C.prototype = global;
var c = new C();
function f(expected) {
var result = c.z;
assertEquals(expected, result);
}
f("a");
f("a");
f("a");
%OptimizeFunctionOnNextCall(f);
f("a");
a.z = "z";
f("z");
f("z");
f("z");
global.z = "bz";
f("bz");
f("bz");
f("bz");
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment