[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}
This commit is contained in:
parent
3a5056a26c
commit
6ea4061b8d
@ -5086,7 +5086,7 @@ void CodeStubAssembler::TryProbeStubCacheTable(
|
||||
|
||||
DCHECK_EQ(kPointerSize, stub_cache->value_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)));
|
||||
|
||||
// We found the handler.
|
||||
@ -5530,29 +5530,106 @@ void CodeStubAssembler::HandleLoadICProtoHandler(
|
||||
DCHECK_EQ(MachineRepresentation::kTagged, var_holder->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);
|
||||
GotoIf(WordNotEqual(cell_value,
|
||||
SmiConstant(Smi::FromInt(Map::kPrototypeChainValid))),
|
||||
miss);
|
||||
|
||||
Node* holder =
|
||||
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);
|
||||
Node* smi_handler = LoadObjectField(handler, LoadHandler::kSmiHandlerOffset);
|
||||
CSA_ASSERT(TaggedIsSmi(smi_handler));
|
||||
var_holder->Bind(holder);
|
||||
var_smi_handler->Bind(smi_handler);
|
||||
|
||||
Label check_prototypes(this);
|
||||
GotoUnless(IsSetWord<LoadHandler::DoNegativeLookupOnReceiverBits>(
|
||||
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);
|
||||
}
|
||||
|
||||
NameDictionaryNegativeLookup(p->receiver, p->name, miss);
|
||||
Goto(if_smi_handler);
|
||||
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);
|
||||
}
|
||||
|
||||
Bind(&if_property_cell);
|
||||
{
|
||||
// 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,
|
||||
|
@ -1107,6 +1107,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
||||
Variable* var_smi_handler,
|
||||
Label* if_smi_handler, Label* miss);
|
||||
|
||||
void CheckPrototype(compiler::Node* prototype_cell, compiler::Node* name,
|
||||
Label* miss);
|
||||
|
||||
void NameDictionaryNegativeLookup(compiler::Node* object,
|
||||
compiler::Node* name, Label* miss);
|
||||
|
||||
|
@ -478,10 +478,7 @@ Register PropertyHandlerCompiler::CheckPrototypes(
|
||||
name, scratch2, miss);
|
||||
} else if (current_map->is_dictionary_map()) {
|
||||
DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast.
|
||||
if (!name->IsUniqueName()) {
|
||||
DCHECK(name->IsString());
|
||||
name = factory()->InternalizeString(Handle<String>::cast(name));
|
||||
}
|
||||
DCHECK(name->IsUniqueName());
|
||||
DCHECK(current.is_null() ||
|
||||
current->property_dictionary()->FindEntry(name) ==
|
||||
NameDictionary::kNotFound);
|
||||
|
@ -508,10 +508,7 @@ Register PropertyHandlerCompiler::CheckPrototypes(
|
||||
name, scratch2, miss);
|
||||
} else if (current_map->is_dictionary_map()) {
|
||||
DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast.
|
||||
if (!name->IsUniqueName()) {
|
||||
DCHECK(name->IsString());
|
||||
name = factory()->InternalizeString(Handle<String>::cast(name));
|
||||
}
|
||||
DCHECK(name->IsUniqueName());
|
||||
DCHECK(current.is_null() || (current->property_dictionary()->FindEntry(
|
||||
name) == NameDictionary::kNotFound));
|
||||
|
||||
|
@ -59,6 +59,21 @@ class LoadHandler {
|
||||
// Make sure we don't overflow the smi.
|
||||
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.
|
||||
static inline Handle<Object> LoadField(Isolate* isolate,
|
||||
FieldIndex field_index);
|
||||
|
@ -482,10 +482,7 @@ Register PropertyHandlerCompiler::CheckPrototypes(
|
||||
name, scratch2, miss);
|
||||
} else if (current_map->is_dictionary_map()) {
|
||||
DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast.
|
||||
if (!name->IsUniqueName()) {
|
||||
DCHECK(name->IsString());
|
||||
name = factory()->InternalizeString(Handle<String>::cast(name));
|
||||
}
|
||||
DCHECK(name->IsUniqueName());
|
||||
DCHECK(current.is_null() ||
|
||||
current->property_dictionary()->FindEntry(name) ==
|
||||
NameDictionary::kNotFound);
|
||||
|
@ -93,7 +93,8 @@ Code* IC::target() const {
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
|
89
src/ic/ic.cc
89
src/ic/ic.cc
@ -853,36 +853,77 @@ Handle<Object> LoadIC::SimpleFieldLoad(FieldIndex index) {
|
||||
return stub.GetCode();
|
||||
}
|
||||
|
||||
bool LoadIC::IsPrototypeValidityCellCheckEnough(Handle<Map> receiver_map,
|
||||
Handle<JSObject> holder) {
|
||||
namespace {
|
||||
|
||||
template <bool fill_array>
|
||||
int InitPrototypeChecks(Isolate* isolate, Handle<Map> receiver_map,
|
||||
Handle<JSObject> holder, Handle<FixedArray> array,
|
||||
Handle<Name> name) {
|
||||
DCHECK(holder->HasFastProperties());
|
||||
|
||||
// The following kinds of receiver maps require custom handler compilation.
|
||||
if (receiver_map->IsPrimitiveMap() || receiver_map->IsJSGlobalProxyMap() ||
|
||||
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
|
||||
// or dictionary objects.
|
||||
for (PrototypeIterator iter(*receiver_map); !iter.IsAtEnd(); iter.Advance()) {
|
||||
JSObject* current = iter.GetCurrent<JSObject>();
|
||||
if (current == *holder) break;
|
||||
Map* current_map = current->map();
|
||||
for (PrototypeIterator iter(receiver_map); !iter.IsAtEnd(); iter.Advance()) {
|
||||
Handle<JSObject> current = PrototypeIterator::GetCurrent<JSObject>(iter);
|
||||
if (*current == *holder) break;
|
||||
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()) {
|
||||
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()) {
|
||||
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<JSObject> holder,
|
||||
Handle<Name> name,
|
||||
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() ||
|
||||
receiver_map->IsJSGlobalObjectMap()) {
|
||||
@ -898,8 +939,19 @@ Handle<Object> LoadIC::SimpleLoadFromPrototype(Handle<Map> receiver_map,
|
||||
|
||||
Handle<WeakCell> holder_cell =
|
||||
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) {
|
||||
@ -1237,10 +1289,10 @@ Handle<Object> LoadIC::GetMapIndependentHandler(LookupIterator* lookup) {
|
||||
if (receiver_is_holder) {
|
||||
return smi_handler;
|
||||
}
|
||||
if (FLAG_tf_load_ic_stub &&
|
||||
IsPrototypeValidityCellCheckEnough(map, holder)) {
|
||||
if (FLAG_tf_load_ic_stub && GetPrototypeCheckCount(map, holder) >= 0) {
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadFieldFromPrototypeDH);
|
||||
return SimpleLoadFromPrototype(map, holder, smi_handler);
|
||||
return SimpleLoadFromPrototype(map, holder, lookup->name(),
|
||||
smi_handler);
|
||||
}
|
||||
break; // Custom-compiled handler.
|
||||
}
|
||||
@ -1254,9 +1306,10 @@ Handle<Object> LoadIC::GetMapIndependentHandler(LookupIterator* lookup) {
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadConstantDH);
|
||||
return smi_handler;
|
||||
}
|
||||
if (IsPrototypeValidityCellCheckEnough(map, holder)) {
|
||||
if (GetPrototypeCheckCount(map, holder) >= 0) {
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadConstantFromPrototypeDH);
|
||||
return SimpleLoadFromPrototype(map, holder, smi_handler);
|
||||
return SimpleLoadFromPrototype(map, holder, lookup->name(),
|
||||
smi_handler);
|
||||
}
|
||||
} else {
|
||||
if (receiver_is_holder) {
|
||||
|
@ -311,16 +311,18 @@ class LoadIC : public IC {
|
||||
private:
|
||||
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.
|
||||
bool IsPrototypeValidityCellCheckEnough(Handle<Map> receiver_map,
|
||||
Handle<JSObject> holder);
|
||||
// Returns -1 if the handler has to be compiled or the number of prototype
|
||||
// checks otherwise.
|
||||
int GetPrototypeCheckCount(Handle<Map> receiver_map, Handle<JSObject> holder);
|
||||
|
||||
// Creates a data handler that represents a prototype chain check followed
|
||||
// by given Smi-handler that encoded a load from the holder.
|
||||
// Can be used only if IsPrototypeValidityCellCheckEnough() predicate is true.
|
||||
Handle<Object> SimpleLoadFromPrototype(Handle<Map> receiver_map,
|
||||
Handle<JSObject> holder,
|
||||
Handle<Name> name,
|
||||
Handle<Object> smi_handler);
|
||||
|
||||
friend class IC;
|
||||
|
@ -464,10 +464,7 @@ Register PropertyHandlerCompiler::CheckPrototypes(
|
||||
name, scratch2, miss);
|
||||
} else if (current_map->is_dictionary_map()) {
|
||||
DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast.
|
||||
if (!name->IsUniqueName()) {
|
||||
DCHECK(name->IsString());
|
||||
name = factory()->InternalizeString(Handle<String>::cast(name));
|
||||
}
|
||||
DCHECK(name->IsUniqueName());
|
||||
DCHECK(current.is_null() ||
|
||||
current->property_dictionary()->FindEntry(name) ==
|
||||
NameDictionary::kNotFound);
|
||||
|
@ -464,10 +464,7 @@ Register PropertyHandlerCompiler::CheckPrototypes(
|
||||
name, scratch2, miss);
|
||||
} else if (current_map->is_dictionary_map()) {
|
||||
DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast.
|
||||
if (!name->IsUniqueName()) {
|
||||
DCHECK(name->IsString());
|
||||
name = factory()->InternalizeString(Handle<String>::cast(name));
|
||||
}
|
||||
DCHECK(name->IsUniqueName());
|
||||
DCHECK(current.is_null() ||
|
||||
current->property_dictionary()->FindEntry(name) ==
|
||||
NameDictionary::kNotFound);
|
||||
|
@ -472,10 +472,7 @@ Register PropertyHandlerCompiler::CheckPrototypes(
|
||||
name, scratch2, miss);
|
||||
} else if (current_map->is_dictionary_map()) {
|
||||
DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast.
|
||||
if (!name->IsUniqueName()) {
|
||||
DCHECK(name->IsString());
|
||||
name = factory()->InternalizeString(Handle<String>::cast(name));
|
||||
}
|
||||
DCHECK(name->IsUniqueName());
|
||||
DCHECK(current.is_null() ||
|
||||
current->property_dictionary()->FindEntry(name) ==
|
||||
NameDictionary::kNotFound);
|
||||
|
@ -452,10 +452,7 @@ Register PropertyHandlerCompiler::CheckPrototypes(
|
||||
name, scratch2, miss);
|
||||
} else if (current_map->is_dictionary_map()) {
|
||||
DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast.
|
||||
if (!name->IsUniqueName()) {
|
||||
DCHECK(name->IsString());
|
||||
name = factory()->InternalizeString(Handle<String>::cast(name));
|
||||
}
|
||||
DCHECK(name->IsUniqueName());
|
||||
DCHECK(current.is_null() ||
|
||||
current->property_dictionary()->FindEntry(name) ==
|
||||
NameDictionary::kNotFound);
|
||||
|
@ -30,9 +30,10 @@ namespace {
|
||||
|
||||
bool CommonStubCacheChecks(StubCache* stub_cache, Name* name, Map* map,
|
||||
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.
|
||||
DCHECK(!name->GetHeap()->InNewSpace(name));
|
||||
DCHECK(!name->GetHeap()->InNewSpace(handler));
|
||||
DCHECK(name->IsUniqueName());
|
||||
DCHECK(name->HasHashCode());
|
||||
if (handler) {
|
||||
|
@ -475,10 +475,7 @@ Register PropertyHandlerCompiler::CheckPrototypes(
|
||||
name, scratch2, miss);
|
||||
} else if (current_map->is_dictionary_map()) {
|
||||
DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast.
|
||||
if (!name->IsUniqueName()) {
|
||||
DCHECK(name->IsString());
|
||||
name = factory()->InternalizeString(Handle<String>::cast(name));
|
||||
}
|
||||
DCHECK(name->IsUniqueName());
|
||||
DCHECK(current.is_null() ||
|
||||
current->property_dictionary()->FindEntry(name) ==
|
||||
NameDictionary::kNotFound);
|
||||
|
@ -482,10 +482,7 @@ Register PropertyHandlerCompiler::CheckPrototypes(
|
||||
name, scratch2, miss);
|
||||
} else if (current_map->is_dictionary_map()) {
|
||||
DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast.
|
||||
if (!name->IsUniqueName()) {
|
||||
DCHECK(name->IsString());
|
||||
name = factory()->InternalizeString(Handle<String>::cast(name));
|
||||
}
|
||||
DCHECK(name->IsUniqueName());
|
||||
DCHECK(current.is_null() ||
|
||||
current->property_dictionary()->FindEntry(name) ==
|
||||
NameDictionary::kNotFound);
|
||||
|
92
test/mjsunit/prototype-non-existing.js
vendored
Normal file
92
test/mjsunit/prototype-non-existing.js
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
// 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");
|
||||
})();
|
Loading…
Reference in New Issue
Block a user