Commit ab7e6df0 authored by Marja Hölttä's avatar Marja Hölttä Committed by Commit Bot

[super ic] Fix more receiver vs lookup start object vs holder confusion

The actual fix is in LoadIC::ComputeHandler (checking
lookup_start_object == holder instead of receiver == holder) + the
LookupIterator changes for preserving lookup_start_object.

The rest is renaming / refactoring.

Bug: v8:9237, chromium:1127653
Change-Id: Ieef46fb46ababa79623951c48639429c5b552d2d
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2414039
Commit-Queue: Marja Hölttä <marja@chromium.org>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#70045}
parent 5b7cb7f4
......@@ -200,7 +200,8 @@ void AccessorAssembler::HandleLoadAccessor(
// Context is stored either in data2 or data3 field depending on whether
// the access check is enabled for this handler or not.
TNode<MaybeObject> maybe_context = Select<MaybeObject>(
IsSetWord<LoadHandler::DoAccessCheckOnReceiverBits>(handler_word),
IsSetWord<LoadHandler::DoAccessCheckOnLookupStartObjectBits>(
handler_word),
[=] { return LoadHandlerDataField(handler, 3); },
[=] { return LoadHandlerDataField(handler, 2); });
......@@ -790,12 +791,14 @@ void AccessorAssembler::HandleLoadICSmiHandlerHasNamedCase(
// generate a code that handles Code handlers.
// If |on_code_handler| is not provided, then only smi sub handler are
// expected.
// 3. Does access check on receiver if ICHandler::DoAccessCheckOnReceiverBits
// bit is set in the smi handler.
// 4. Does dictionary lookup on receiver if ICHandler::LookupOnReceiverBits bit
// is set in the smi handler. If |on_found_on_receiver| is provided then
// it calls it to generate a code that handles the "found on receiver case"
// or just misses if the |on_found_on_receiver| is not provided.
// 3. Does access check on lookup start object if
// ICHandler::DoAccessCheckOnLookupStartObjectBits bit is set in the smi
// handler.
// 4. Does dictionary lookup on receiver if
// ICHandler::LookupOnLookupStartObjectBits bit is set in the smi handler. If
// |on_found_on_lookup_start_object| is provided then it calls it to
// generate a code that handles the "found on receiver case" or just misses
// if the |on_found_on_lookup_start_object| is not provided.
// 5. Falls through in a case of a smi handler which is returned from this
// function (tagged!).
// TODO(ishell): Remove templatezation once we move common bits from
......@@ -804,8 +807,8 @@ template <typename ICHandler, typename ICParameters>
TNode<Object> AccessorAssembler::HandleProtoHandler(
const ICParameters* p, TNode<DataHandler> handler,
const OnCodeHandler& on_code_handler,
const OnFoundOnReceiver& on_found_on_receiver, Label* miss,
ICMode ic_mode) {
const OnFoundOnLookupStartObject& on_found_on_lookup_start_object,
Label* miss, ICMode ic_mode) {
//
// Check prototype validity cell.
//
......@@ -835,21 +838,24 @@ TNode<Object> AccessorAssembler::HandleProtoHandler(
// because in the former case the validity cell check guards modifications
// of the global object and the latter is not applicable to the global
// object.
int mask = ICHandler::LookupOnReceiverBits::kMask |
ICHandler::DoAccessCheckOnReceiverBits::kMask;
int mask = ICHandler::LookupOnLookupStartObjectBits::kMask |
ICHandler::DoAccessCheckOnLookupStartObjectBits::kMask;
if (ic_mode == ICMode::kGlobalIC) {
CSA_ASSERT(this, IsClearWord(handler_flags, mask));
} else {
DCHECK_EQ(ICMode::kNonGlobalIC, ic_mode);
Label done(this), if_do_access_check(this), if_lookup_on_receiver(this);
Label done(this), if_do_access_check(this),
if_lookup_on_lookup_start_object(this);
GotoIf(IsClearWord(handler_flags, mask), &done);
// Only one of the bits can be set at a time.
CSA_ASSERT(this,
WordNotEqual(WordAnd(handler_flags, IntPtrConstant(mask)),
IntPtrConstant(mask)));
Branch(IsSetWord<LoadHandler::DoAccessCheckOnReceiverBits>(handler_flags),
&if_do_access_check, &if_lookup_on_receiver);
Branch(
IsSetWord<typename ICHandler::DoAccessCheckOnLookupStartObjectBits>(
handler_flags),
&if_do_access_check, &if_lookup_on_lookup_start_object);
BIND(&if_do_access_check);
{
......@@ -857,29 +863,39 @@ TNode<Object> AccessorAssembler::HandleProtoHandler(
CSA_ASSERT(this, IsWeakOrCleared(data2));
TNode<Context> expected_native_context =
CAST(GetHeapObjectAssumeWeak(data2, miss));
EmitAccessCheck(expected_native_context, p->context(), p->receiver(),
&done, miss);
EmitAccessCheck(expected_native_context, p->context(),
p->lookup_start_object(), &done, miss);
}
// Dictionary lookup on receiver is not necessary for Load/StoreGlobalIC
// because prototype validity cell check already guards modifications of
// the global object.
BIND(&if_lookup_on_receiver);
BIND(&if_lookup_on_lookup_start_object);
{
DCHECK_EQ(ICMode::kNonGlobalIC, ic_mode);
CSA_ASSERT(this, Word32BinaryNot(HasInstanceType(
CAST(p->receiver()), JS_GLOBAL_OBJECT_TYPE)));
// Assert that the LookupOnLookupStartObjectBits is never set for
// Load/StoreSuperIC case (which is when receiver !=
// lookup_start_object). For the super IC case the lookup start object
// is guaranteed to be a prototype of a home object and thus the
// prototype validity cell check already guards modifications of the
// lookup start object.
CSA_ASSERT(this, TaggedEqual(p->receiver(), p->lookup_start_object()));
// Dictionary lookup on lookup start object is not necessary for
// Load/StoreGlobalIC (which is the only case when the
// lookup_start_object can be a JSGlobalObject) because prototype
// validity cell check already guards modifications of the global
// object.
CSA_ASSERT(this,
Word32BinaryNot(HasInstanceType(
CAST(p->lookup_start_object()), JS_GLOBAL_OBJECT_TYPE)));
TNode<NameDictionary> properties =
CAST(LoadSlowProperties(CAST(p->receiver())));
CAST(LoadSlowProperties(CAST(p->lookup_start_object())));
TVARIABLE(IntPtrT, var_name_index);
Label found(this, &var_name_index);
NameDictionaryLookup<NameDictionary>(properties, CAST(p->name()),
&found, &var_name_index, &done);
BIND(&found);
{
if (on_found_on_receiver) {
on_found_on_receiver(properties, var_name_index.value());
if (on_found_on_lookup_start_object) {
on_found_on_lookup_start_object(properties, var_name_index.value());
} else {
Goto(miss);
}
......@@ -901,7 +917,7 @@ void AccessorAssembler::HandleLoadICProtoHandler(
p, handler,
// Code sub-handlers are not expected in LoadICs, so no |on_code_handler|.
nullptr,
// on_found_on_receiver
// on_found_on_lookup_start_object
[=](TNode<NameDictionary> properties, TNode<IntPtrT> name_index) {
if (access_mode == LoadAccessMode::kHas) {
exit_point->Return(TrueConstant());
......@@ -1542,7 +1558,7 @@ void AccessorAssembler::HandleStoreICProtoHandler(
TNode<Object> smi_handler = HandleProtoHandler<StoreHandler>(
p, handler, on_code_handler,
// on_found_on_receiver
// on_found_on_lookup_start_object
[=](TNode<NameDictionary> properties, TNode<IntPtrT> name_index) {
TNode<Uint32T> details = LoadDetailsByKeyIndex(properties, name_index);
// Check that the property is a writable data property (no accessor).
......@@ -1618,7 +1634,8 @@ void AccessorAssembler::HandleStoreICProtoHandler(
{
// This is a case of "transitioning store" to a dictionary mode object
// when the property does not exist. The "existing property" case is
// covered above by LookupOnReceiver bit handling of the smi handler.
// covered above by LookupOnLookupStartObject bit handling of the smi
// handler.
Label slow(this);
TNode<Map> receiver_map = LoadMap(CAST(p->receiver()));
InvalidateValidityCellIfPrototype(receiver_map);
......@@ -1648,7 +1665,8 @@ void AccessorAssembler::HandleStoreICProtoHandler(
// Context is stored either in data2 or data3 field depending on whether
// the access check is enabled for this handler or not.
TNode<MaybeObject> maybe_context = Select<MaybeObject>(
IsSetWord32<LoadHandler::DoAccessCheckOnReceiverBits>(handler_word),
IsSetWord32<StoreHandler::DoAccessCheckOnLookupStartObjectBits>(
handler_word),
[=] { return LoadHandlerDataField(handler, 3); },
[=] { return LoadHandlerDataField(handler, 2); });
......
......@@ -199,6 +199,8 @@ class V8_EXPORT_PRIVATE AccessorAssembler : public CodeStubAssembler {
TNode<TaggedIndex> slot() const { return slot_; }
TNode<HeapObject> vector() const { return vector_; }
TNode<Object> lookup_start_object() const { return receiver(); }
bool receiver_is_null() const { return !receiver_.has_value(); }
private:
......@@ -415,15 +417,15 @@ class V8_EXPORT_PRIVATE AccessorAssembler : public CodeStubAssembler {
// Low-level helpers.
using OnCodeHandler = std::function<void(TNode<Code> code_handler)>;
using OnFoundOnReceiver = std::function<void(TNode<NameDictionary> properties,
TNode<IntPtrT> name_index)>;
using OnFoundOnLookupStartObject = std::function<void(
TNode<NameDictionary> properties, TNode<IntPtrT> name_index)>;
template <typename ICHandler, typename ICParameters>
TNode<Object> HandleProtoHandler(
const ICParameters* p, TNode<DataHandler> handler,
const OnCodeHandler& on_code_handler,
const OnFoundOnReceiver& on_found_on_receiver, Label* miss,
ICMode ic_mode);
const OnFoundOnLookupStartObject& on_found_on_lookup_start_object,
Label* miss, ICMode ic_mode);
void CheckHeapObjectTypeMatchesDescriptor(TNode<Word32T> handler_word,
TNode<JSObject> holder,
......
......@@ -27,16 +27,20 @@ Handle<Smi> SetBitFieldValue(Isolate* isolate, Handle<Smi> smi_handler,
// Load/StoreHandler to the base class.
template <typename ICHandler, bool fill_handler = true>
int InitPrototypeChecksImpl(Isolate* isolate, Handle<ICHandler> handler,
Handle<Smi>* smi_handler, Handle<Map> receiver_map,
Handle<Smi>* smi_handler,
Handle<Map> lookup_start_object_map,
Handle<JSReceiver> holder, MaybeObjectHandle data1,
MaybeObjectHandle maybe_data2) {
int data_size = 1;
// Holder-is-receiver case itself does not add entries unless there is an
// optional data2 value provided.
if (receiver_map->IsPrimitiveMap() ||
receiver_map->is_access_check_needed()) {
DCHECK(!receiver_map->IsJSGlobalObjectMap());
DCHECK_IMPLIES(lookup_start_object_map->IsJSGlobalObjectMap(),
lookup_start_object_map->is_prototype_map());
if (lookup_start_object_map->IsPrimitiveMap() ||
lookup_start_object_map->is_access_check_needed()) {
DCHECK(!lookup_start_object_map->IsJSGlobalObjectMap());
// The validity cell check for primitive and global proxy receivers does
// not guarantee that certain native context ever had access to other
// native context. However, a handler created for one native context could
......@@ -47,17 +51,23 @@ int InitPrototypeChecksImpl(Isolate* isolate, Handle<ICHandler> handler,
Handle<Context> native_context = isolate->native_context();
handler->set_data2(HeapObjectReference::Weak(*native_context));
} else {
// Enable access checks on receiver.
using Bit = typename ICHandler::DoAccessCheckOnReceiverBits;
*smi_handler = SetBitFieldValue<Bit>(isolate, *smi_handler, true);
// Enable access checks on the lookup start object.
*smi_handler = SetBitFieldValue<
typename ICHandler::DoAccessCheckOnLookupStartObjectBits>(
isolate, *smi_handler, true);
}
data_size++;
} else if (receiver_map->is_dictionary_map() &&
!receiver_map->IsJSGlobalObjectMap()) {
} else if (lookup_start_object_map->is_dictionary_map() &&
!lookup_start_object_map->is_prototype_map()) {
if (!fill_handler) {
// Enable lookup on receiver.
using Bit = typename ICHandler::LookupOnReceiverBits;
*smi_handler = SetBitFieldValue<Bit>(isolate, *smi_handler, true);
// The lookup on lookup start object is only required for dictionary mode
// objects which are not prototypes. When we start lookup from a prototype
// object, the prototype validity cell guards modifications of the lookup
// start object. This includes both Load/StoreGlobalIC and
// Load/StoreSuperIC cases.
*smi_handler =
SetBitFieldValue<typename ICHandler::LookupOnLookupStartObjectBits>(
isolate, *smi_handler, true);
}
}
if (fill_handler) {
......@@ -80,40 +90,39 @@ int InitPrototypeChecksImpl(Isolate* isolate, Handle<ICHandler> handler,
}
// 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 |lookup_start_object_map| till |holder| did not change.
// If the |holder| is an empty handle then the full prototype chain is
// checked.
// Returns -1 if the handler has to be compiled or the number of prototype
// checks otherwise.
template <typename ICHandler>
int GetHandlerDataSize(Isolate* isolate, Handle<Smi>* smi_handler,
Handle<Map> receiver_map, Handle<JSReceiver> holder,
MaybeObjectHandle data1,
Handle<Map> lookup_start_object_map,
Handle<JSReceiver> holder, MaybeObjectHandle data1,
MaybeObjectHandle maybe_data2 = MaybeObjectHandle()) {
DCHECK_NOT_NULL(smi_handler);
return InitPrototypeChecksImpl<ICHandler, false>(isolate, Handle<ICHandler>(),
smi_handler, receiver_map,
return InitPrototypeChecksImpl<ICHandler, false>(
isolate, Handle<ICHandler>(), smi_handler, lookup_start_object_map,
holder, data1, maybe_data2);
}
template <typename ICHandler>
void InitPrototypeChecks(Isolate* isolate, Handle<ICHandler> handler,
Handle<Map> receiver_map, Handle<JSReceiver> holder,
MaybeObjectHandle data1,
Handle<Map> lookup_start_object_map,
Handle<JSReceiver> holder, MaybeObjectHandle data1,
MaybeObjectHandle maybe_data2 = MaybeObjectHandle()) {
InitPrototypeChecksImpl<ICHandler, true>(
isolate, handler, nullptr, receiver_map, holder, data1, maybe_data2);
InitPrototypeChecksImpl<ICHandler, true>(isolate, handler, nullptr,
lookup_start_object_map, holder,
data1, maybe_data2);
}
} // namespace
// static
Handle<Object> LoadHandler::LoadFromPrototype(Isolate* isolate,
Handle<Map> receiver_map,
Handle<JSReceiver> holder,
Handle<Smi> smi_handler,
MaybeObjectHandle maybe_data1,
MaybeObjectHandle maybe_data2) {
Handle<Object> LoadHandler::LoadFromPrototype(
Isolate* isolate, Handle<Map> lookup_start_object_map,
Handle<JSReceiver> holder, Handle<Smi> smi_handler,
MaybeObjectHandle maybe_data1, MaybeObjectHandle maybe_data2) {
MaybeObjectHandle data1;
if (maybe_data1.is_null()) {
data1 = MaybeObjectHandle::Weak(holder);
......@@ -121,44 +130,48 @@ Handle<Object> LoadHandler::LoadFromPrototype(Isolate* isolate,
data1 = maybe_data1;
}
int data_size = GetHandlerDataSize<LoadHandler>(
isolate, &smi_handler, receiver_map, holder, data1, maybe_data2);
int data_size = GetHandlerDataSize<LoadHandler>(isolate, &smi_handler,
lookup_start_object_map,
holder, data1, maybe_data2);
Handle<Object> validity_cell =
Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate);
Handle<Object> validity_cell = Map::GetOrCreatePrototypeChainValidityCell(
lookup_start_object_map, isolate);
Handle<LoadHandler> handler = isolate->factory()->NewLoadHandler(data_size);
handler->set_smi_handler(*smi_handler);
handler->set_validity_cell(*validity_cell);
InitPrototypeChecks(isolate, handler, receiver_map, holder, data1,
InitPrototypeChecks(isolate, handler, lookup_start_object_map, holder, data1,
maybe_data2);
return handler;
}
// static
Handle<Object> LoadHandler::LoadFullChain(Isolate* isolate,
Handle<Map> receiver_map,
Handle<Map> lookup_start_object_map,
const MaybeObjectHandle& holder,
Handle<Smi> smi_handler) {
Handle<JSReceiver> end; // null handle, means full prototype chain lookup.
MaybeObjectHandle data1 = holder;
int data_size = GetHandlerDataSize<LoadHandler>(isolate, &smi_handler,
receiver_map, end, data1);
int data_size = GetHandlerDataSize<LoadHandler>(
isolate, &smi_handler, lookup_start_object_map, end, data1);
Handle<Object> validity_cell =
Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate);
Handle<Object> validity_cell = Map::GetOrCreatePrototypeChainValidityCell(
lookup_start_object_map, isolate);
if (validity_cell->IsSmi()) {
DCHECK_EQ(1, data_size);
// Lookup on receiver isn't supported in case of a simple smi handler.
if (!LookupOnReceiverBits::decode(smi_handler->value())) return smi_handler;
// Lookup on lookup start object isn't supported in case of a simple smi
// handler.
if (!LookupOnLookupStartObjectBits::decode(smi_handler->value())) {
return smi_handler;
}
}
Handle<LoadHandler> handler = isolate->factory()->NewLoadHandler(data_size);
handler->set_smi_handler(*smi_handler);
handler->set_validity_cell(*validity_cell);
InitPrototypeChecks(isolate, handler, receiver_map, end, data1);
InitPrototypeChecks(isolate, handler, lookup_start_object_map, end, data1);
return handler;
}
......@@ -247,7 +260,8 @@ MaybeObjectHandle StoreHandler::StoreTransition(Isolate* isolate,
DCHECK(!transition_map->IsJSGlobalObjectMap());
Handle<StoreHandler> handler = isolate->factory()->NewStoreHandler(0);
// Store normal with enabled lookup on receiver.
int config = KindBits::encode(kNormal) | LookupOnReceiverBits::encode(true);
int config =
KindBits::encode(kNormal) | LookupOnLookupStartObjectBits::encode(true);
handler->set_smi_handler(Smi::FromInt(config));
handler->set_validity_cell(*validity_cell);
return MaybeObjectHandle(handler);
......
......@@ -50,15 +50,17 @@ class LoadHandler final : public DataHandler {
};
using KindBits = base::BitField<Kind, 0, 4>;
// Defines whether access rights check should be done on receiver object.
// Defines whether access rights check should be done on lookup start object.
// Applicable to named property kinds only when loading value from prototype
// chain. Ignored when loading from holder.
using DoAccessCheckOnReceiverBits = KindBits::Next<bool, 1>;
// chain. Ignored when loading from lookup start object.
using DoAccessCheckOnLookupStartObjectBits = KindBits::Next<bool, 1>;
// Defines whether a lookup should be done on receiver object before
// Defines whether a lookup should be done on lookup start object before
// proceeding to the prototype chain. Applicable to named property kinds only
// when loading value from prototype chain. Ignored when loading from holder.
using LookupOnReceiverBits = DoAccessCheckOnReceiverBits::Next<bool, 1>;
// when loading value from prototype chain. Ignored when loading from lookup
// start object.
using LookupOnLookupStartObjectBits =
DoAccessCheckOnLookupStartObjectBits::Next<bool, 1>;
//
// Encoding when KindBits contains kForConstants.
......@@ -66,14 +68,14 @@ class LoadHandler final : public DataHandler {
// Index of a value entry in the descriptor array.
using DescriptorBits =
LookupOnReceiverBits::Next<unsigned, kDescriptorIndexBitCount>;
LookupOnLookupStartObjectBits::Next<unsigned, kDescriptorIndexBitCount>;
// Make sure we don't overflow the smi.
STATIC_ASSERT(DescriptorBits::kLastUsedBit < kSmiValueSize);
//
// Encoding when KindBits contains kField.
//
using IsInobjectBits = LookupOnReceiverBits::Next<bool, 1>;
using IsInobjectBits = LookupOnLookupStartObjectBits::Next<bool, 1>;
using IsDoubleBits = IsInobjectBits::Next<bool, 1>;
// +1 here is to cover all possible JSObject header sizes.
using FieldIndexBits =
......@@ -85,7 +87,7 @@ class LoadHandler final : public DataHandler {
//
// Encoding when KindBits contains kElement or kIndexedString.
//
using AllowOutOfBoundsBits = LookupOnReceiverBits::Next<bool, 1>;
using AllowOutOfBoundsBits = LookupOnLookupStartObjectBits::Next<bool, 1>;
//
// Encoding when KindBits contains kElement.
......@@ -99,8 +101,9 @@ class LoadHandler final : public DataHandler {
//
// Encoding when KindBits contains kModuleExport.
//
using ExportsIndexBits = LookupOnReceiverBits::Next<
unsigned, kSmiValueSize - LookupOnReceiverBits::kLastUsedBit - 1>;
using ExportsIndexBits = LookupOnLookupStartObjectBits::Next<
unsigned,
kSmiValueSize - LookupOnLookupStartObjectBits::kLastUsedBit - 1>;
// Decodes kind from Smi-handler.
static inline Kind GetHandlerKind(Smi smi_handler);
......@@ -213,20 +216,21 @@ class StoreHandler final : public DataHandler {
// Applicable to kGlobalProxy, kProxy kinds.
// Defines whether access rights check should be done on receiver object.
using DoAccessCheckOnReceiverBits = KindBits::Next<bool, 1>;
// Defines whether access rights check should be done on lookup start object.
using DoAccessCheckOnLookupStartObjectBits = KindBits::Next<bool, 1>;
// Defines whether a lookup should be done on receiver object before
// Defines whether a lookup should be done on lookup start object before
// proceeding to the prototype chain. Applicable to named property kinds only
// when storing through prototype chain. Ignored when storing to holder.
using LookupOnReceiverBits = DoAccessCheckOnReceiverBits::Next<bool, 1>;
using LookupOnLookupStartObjectBits =
DoAccessCheckOnLookupStartObjectBits::Next<bool, 1>;
// Applicable to kField, kTransitionToField and kTransitionToConstant
// kinds.
// Index of a value entry in the descriptor array.
using DescriptorBits =
LookupOnReceiverBits::Next<unsigned, kDescriptorIndexBitCount>;
LookupOnLookupStartObjectBits::Next<unsigned, kDescriptorIndexBitCount>;
//
// Encodes the bits when StoreSlow contains KeyedAccessStoreMode.
......
......@@ -16,11 +16,12 @@
namespace v8 {
namespace internal {
void IC::update_receiver_map(Handle<Object> receiver) {
if (receiver->IsSmi()) {
receiver_map_ = isolate_->factory()->heap_number_map();
void IC::update_lookup_start_object_map(Handle<Object> object) {
if (object->IsSmi()) {
lookup_start_object_map_ = isolate_->factory()->heap_number_map();
} else {
receiver_map_ = handle(HeapObject::cast(*receiver).map(), isolate_);
lookup_start_object_map_ =
handle(HeapObject::cast(*object).map(), isolate_);
}
}
......
......@@ -97,7 +97,7 @@ void IC::TraceIC(const char* type, Handle<Object> name, State old_state,
State new_state) {
if (V8_LIKELY(!TracingFlags::is_ic_stats_enabled())) return;
Handle<Map> map = receiver_map(); // Might be empty.
Handle<Map> map = lookup_start_object_map(); // Might be empty.
const char* modifier = "";
if (state() == NO_FEEDBACK) {
......@@ -217,7 +217,8 @@ bool IC::ShouldRecomputeHandler(Handle<String> name) {
// monomorphic.
if (IsGlobalIC()) return true;
MaybeObjectHandle maybe_handler = nexus()->FindHandlerForMap(receiver_map());
MaybeObjectHandle maybe_handler =
nexus()->FindHandlerForMap(lookup_start_object_map());
// The current map wasn't handled yet. There's no reason to stay monomorphic,
// *unless* we're moving from a deprecated map to its replacement, or
......@@ -225,13 +226,13 @@ bool IC::ShouldRecomputeHandler(Handle<String> name) {
// TODO(verwaest): Check if the current map is actually what the old map
// would transition to.
if (maybe_handler.is_null()) {
if (!receiver_map()->IsJSObjectMap()) return false;
if (!lookup_start_object_map()->IsJSObjectMap()) return false;
Map first_map = FirstTargetMap();
if (first_map.is_null()) return false;
Handle<Map> old_map(first_map, isolate());
if (old_map->is_deprecated()) return true;
return IsMoreGeneralElementsKindTransition(old_map->elements_kind(),
receiver_map()->elements_kind());
return IsMoreGeneralElementsKindTransition(
old_map->elements_kind(), lookup_start_object_map()->elements_kind());
}
return true;
......@@ -248,12 +249,12 @@ bool IC::RecomputeHandlerForName(Handle<Object> name) {
return true;
}
void IC::UpdateState(Handle<Object> receiver, Handle<Object> name) {
void IC::UpdateState(Handle<Object> lookup_start_object, Handle<Object> name) {
if (state() == NO_FEEDBACK) return;
update_receiver_map(receiver);
update_lookup_start_object_map(lookup_start_object);
if (!name->IsString()) return;
if (state() != MONOMORPHIC && state() != POLYMORPHIC) return;
if (receiver->IsNullOrUndefined(isolate())) return;
if (lookup_start_object->IsNullOrUndefined(isolate())) return;
// Remove the target from the code cache if it became invalid
// because of changes in the prototype chain to avoid hitting it
......@@ -399,7 +400,7 @@ MaybeHandle<Object> LoadIC::Load(Handle<Object> object, Handle<Name> name,
if (use_ic) {
// Ensure the IC state progresses.
TRACE_HANDLER_STATS(isolate(), LoadIC_NonReceiver);
update_receiver_map(object);
update_lookup_start_object_map(object);
SetCache(name, LoadHandler::LoadSlow(isolate()));
TraceIC("LoadIC", name);
}
......@@ -421,11 +422,10 @@ MaybeHandle<Object> LoadIC::Load(Handle<Object> object, Handle<Name> name,
if (MigrateDeprecated(isolate(), object)) use_ic = false;
JSObject::MakePrototypesFast(object, kStartAtReceiver, isolate());
update_receiver_map(object);
update_lookup_start_object_map(object);
LookupIterator::Key key(isolate(), name);
LookupIterator it =
LookupIterator::LookupWithReceiver(isolate(), receiver, key, object);
LookupIterator it = LookupIterator(isolate(), receiver, key, object);
// Named lookup in the object.
LookupForRead(&it, IsAnyHas());
......@@ -570,7 +570,7 @@ bool IC::UpdatePolymorphicIC(Handle<Name> name,
if (is_keyed() && state() != RECOMPUTE_HANDLER) {
if (nexus()->GetName() != *name) return false;
}
Handle<Map> map = receiver_map();
Handle<Map> map = lookup_start_object_map();
std::vector<MapAndHandler> maps_and_handlers;
maps_and_handlers.reserve(FLAG_max_valid_polymorphic_map_count);
......@@ -642,7 +642,7 @@ bool IC::UpdatePolymorphicIC(Handle<Name> name,
number_of_valid_maps++;
if (number_of_valid_maps == 1) {
ConfigureVectorState(name, receiver_map(), handler);
ConfigureVectorState(name, lookup_start_object_map(), handler);
} else {
if (is_keyed() && nexus()->GetName() != *name) return false;
if (handler_to_overwrite >= 0) {
......@@ -664,7 +664,7 @@ bool IC::UpdatePolymorphicIC(Handle<Name> name,
void IC::UpdateMonomorphicIC(const MaybeObjectHandle& handler,
Handle<Name> name) {
DCHECK(IsHandler(*handler));
ConfigureVectorState(name, receiver_map(), handler);
ConfigureVectorState(name, lookup_start_object_map(), handler);
}
void IC::CopyICToMegamorphicCache(Handle<Name> name) {
......@@ -721,7 +721,7 @@ void IC::SetCache(Handle<Name> name, const MaybeObjectHandle& handler) {
ConfigureVectorState(MEGAMORPHIC, name);
V8_FALLTHROUGH;
case MEGAMORPHIC:
UpdateMegamorphicCache(receiver_map(), name, handler);
UpdateMegamorphicCache(lookup_start_object_map(), name, handler);
// Indicate that we've handled this case.
vector_set_ = true;
break;
......@@ -738,7 +738,7 @@ void LoadIC::UpdateCaches(LookupIterator* lookup) {
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadNonexistentDH);
Handle<Smi> smi_handler = LoadHandler::LoadNonExistent(isolate());
code = LoadHandler::LoadFullChain(
isolate(), receiver_map(),
isolate(), lookup_start_object_map(),
MaybeObjectHandle(isolate()->factory()->null_value()), smi_handler);
} else if (IsLoadGlobalIC() && lookup->state() == LookupIterator::JSPROXY) {
// If there is proxy just install the slow stub since we need to call the
......@@ -746,8 +746,8 @@ void LoadIC::UpdateCaches(LookupIterator* lookup) {
// handle this case.
Handle<Smi> slow_handler = LoadHandler::LoadSlow(isolate());
Handle<JSProxy> holder = lookup->GetHolder<JSProxy>();
code = LoadHandler::LoadFromPrototype(isolate(), receiver_map(), holder,
slow_handler);
code = LoadHandler::LoadFromPrototype(isolate(), lookup_start_object_map(),
holder, slow_handler);
} else {
if (IsLoadGlobalIC()) {
if (lookup->TryLookupCachedProperty()) {
......@@ -816,12 +816,13 @@ Handle<Object> LoadIC::ComputeHandler(LookupIterator* lookup) {
}
}
Handle<Map> map = receiver_map();
Handle<Map> map = lookup_start_object_map();
Handle<JSObject> holder;
bool receiver_is_holder;
bool holder_is_lookup_start_object;
if (lookup->state() != LookupIterator::JSPROXY) {
holder = lookup->GetHolder<JSObject>();
receiver_is_holder = receiver.is_identical_to(holder);
holder_is_lookup_start_object =
lookup->lookup_start_object().is_identical_to(holder);
}
switch (lookup->state()) {
......@@ -830,7 +831,7 @@ Handle<Object> LoadIC::ComputeHandler(LookupIterator* lookup) {
if (holder->GetNamedInterceptor().non_masking()) {
MaybeObjectHandle holder_ref(isolate()->factory()->null_value());
if (!receiver_is_holder || IsLoadGlobalIC()) {
if (!holder_is_lookup_start_object || IsLoadGlobalIC()) {
holder_ref = MaybeObjectHandle::Weak(holder);
}
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadNonMaskingInterceptorDH);
......@@ -838,7 +839,7 @@ Handle<Object> LoadIC::ComputeHandler(LookupIterator* lookup) {
smi_handler);
}
if (receiver_is_holder) {
if (holder_is_lookup_start_object) {
DCHECK(map->has_named_interceptor());
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadInterceptorDH);
return smi_handler;
......@@ -852,7 +853,7 @@ Handle<Object> LoadIC::ComputeHandler(LookupIterator* lookup) {
case LookupIterator::ACCESSOR: {
// Use simple field loads for some well-known callback properties.
// The method will only return true for absolute truths based on the
// receiver maps.
// lookup start object maps.
FieldIndex index;
if (Accessors::IsJSObjectFieldAccessor(isolate(), map, lookup->name(),
&index)) {
......@@ -926,7 +927,7 @@ Handle<Object> LoadIC::ComputeHandler(LookupIterator* lookup) {
LoadHandler::LoadAccessor(isolate(), lookup->GetAccessorIndex());
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadAccessorDH);
if (receiver_is_holder) return smi_handler;
if (holder_is_lookup_start_object) return smi_handler;
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadAccessorFromPrototypeDH);
} else if (holder->IsJSGlobalObject()) {
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadGlobalFromPrototypeDH);
......@@ -938,7 +939,7 @@ Handle<Object> LoadIC::ComputeHandler(LookupIterator* lookup) {
smi_handler = LoadHandler::LoadNormal(isolate());
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadNormalDH);
if (receiver_is_holder) return smi_handler;
if (holder_is_lookup_start_object) return smi_handler;
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadNormalFromPrototypeDH);
}
......@@ -959,7 +960,7 @@ Handle<Object> LoadIC::ComputeHandler(LookupIterator* lookup) {
Handle<Smi> smi_handler = LoadHandler::LoadNativeDataProperty(
isolate(), lookup->GetAccessorIndex());
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadNativeDataPropertyDH);
if (receiver_is_holder) return smi_handler;
if (holder_is_lookup_start_object) return smi_handler;
TRACE_HANDLER_STATS(isolate(),
LoadIC_LoadNativeDataPropertyFromPrototypeDH);
return LoadHandler::LoadFromPrototype(isolate(), map, holder,
......@@ -982,7 +983,7 @@ Handle<Object> LoadIC::ComputeHandler(LookupIterator* lookup) {
smi_handler = LoadHandler::LoadNormal(isolate());
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadNormalDH);
if (receiver_is_holder) return smi_handler;
if (holder_is_lookup_start_object) return smi_handler;
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadNormalFromPrototypeDH);
} else if (lookup->IsElement(*holder)) {
TRACE_HANDLER_STATS(isolate(), LoadIC_SlowStub);
......@@ -993,11 +994,11 @@ Handle<Object> LoadIC::ComputeHandler(LookupIterator* lookup) {
smi_handler =
LoadHandler::LoadField(isolate(), field, map->elements_kind());
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadFieldDH);
if (receiver_is_holder) return smi_handler;
if (holder_is_lookup_start_object) return smi_handler;
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadFieldFromPrototypeDH);
}
if (lookup->constness() == PropertyConstness::kConst &&
!receiver_is_holder) {
!holder_is_lookup_start_object) {
DCHECK(!lookup->is_dictionary_holder());
Handle<Object> value = lookup->GetDataValue();
......@@ -1032,9 +1033,10 @@ Handle<Object> LoadIC::ComputeHandler(LookupIterator* lookup) {
return LoadHandler::LoadNonExistent(isolate());
case LookupIterator::JSPROXY: {
Handle<JSProxy> holder_proxy = lookup->GetHolder<JSProxy>();
bool receiver_is_holder_proxy = receiver.is_identical_to(holder_proxy);
bool holder_proxy_is_lookup_start_object =
lookup->lookup_start_object().is_identical_to(holder_proxy);
Handle<Smi> smi_handler = LoadHandler::LoadProxy(isolate());
if (receiver_is_holder_proxy) {
if (holder_proxy_is_lookup_start_object) {
return smi_handler;
}
return LoadHandler::LoadFromPrototype(isolate(), map, holder_proxy,
......@@ -1442,7 +1444,7 @@ bool StoreIC::LookupForWrite(LookupIterator* it, Handle<Object> value,
it->PrepareForDataProperty(value);
// The previous receiver map might just have been deprecated,
// so reload it.
update_receiver_map(receiver);
update_lookup_start_object_map(receiver);
return true;
}
......@@ -1565,7 +1567,7 @@ MaybeHandle<Object> StoreIC::Store(Handle<Object> object, Handle<Name> name,
if (use_ic) {
// Ensure the IC state progresses.
TRACE_HANDLER_STATS(isolate(), StoreIC_NonReceiver);
update_receiver_map(object);
update_lookup_start_object_map(object);
SetCache(name, StoreHandler::StoreSlow(isolate()));
TraceIC("StoreIC", name);
}
......@@ -1635,7 +1637,7 @@ MaybeObjectHandle StoreIC::ComputeHandler(LookupIterator* lookup) {
if (store_target->IsJSGlobalObject()) {
TRACE_HANDLER_STATS(isolate(), StoreIC_StoreGlobalTransitionDH);
if (receiver_map()->IsJSGlobalObject()) {
if (lookup_start_object_map()->IsJSGlobalObject()) {
DCHECK(IsStoreGlobalIC());
#ifdef DEBUG
Handle<JSObject> holder = lookup->GetHolder<JSObject>();
......@@ -1647,13 +1649,13 @@ MaybeObjectHandle StoreIC::ComputeHandler(LookupIterator* lookup) {
Handle<Smi> smi_handler = StoreHandler::StoreGlobalProxy(isolate());
Handle<Object> handler = StoreHandler::StoreThroughPrototype(
isolate(), receiver_map(), store_target, smi_handler,
isolate(), lookup_start_object_map(), store_target, smi_handler,
MaybeObjectHandle::Weak(lookup->transition_cell()));
return MaybeObjectHandle(handler);
}
// Dictionary-to-fast transitions are not expected and not supported.
DCHECK_IMPLIES(!lookup->transition_map()->is_dictionary_map(),
!receiver_map()->is_dictionary_map());
!lookup_start_object_map()->is_dictionary_map());
DCHECK(lookup->IsCacheableTransition());
......@@ -1680,7 +1682,7 @@ MaybeObjectHandle StoreIC::ComputeHandler(LookupIterator* lookup) {
DCHECK(!info.getter().IsUndefined(isolate()) ||
!info.query().IsUndefined(isolate()));
Handle<Object> handler = StoreHandler::StoreThroughPrototype(
isolate(), receiver_map(), holder,
isolate(), lookup_start_object_map(), holder,
StoreHandler::StoreSlow(isolate()));
return MaybeObjectHandle(handler);
}
......@@ -1712,7 +1714,8 @@ MaybeObjectHandle StoreIC::ComputeHandler(LookupIterator* lookup) {
TRACE_HANDLER_STATS(isolate(), StoreIC_SlowStub);
return MaybeObjectHandle(StoreHandler::StoreSlow(isolate()));
}
if (!AccessorInfo::IsCompatibleReceiverMap(info, receiver_map())) {
if (!AccessorInfo::IsCompatibleReceiverMap(info,
lookup_start_object_map())) {
set_slow_stub_reason("incompatible receiver type");
TRACE_HANDLER_STATS(isolate(), StoreIC_SlowStub);
return MaybeObjectHandle(StoreHandler::StoreSlow(isolate()));
......@@ -1727,7 +1730,7 @@ MaybeObjectHandle StoreIC::ComputeHandler(LookupIterator* lookup) {
TRACE_HANDLER_STATS(isolate(),
StoreIC_StoreNativeDataPropertyOnPrototypeDH);
return MaybeObjectHandle(StoreHandler::StoreThroughPrototype(
isolate(), receiver_map(), holder, smi_handler));
isolate(), lookup_start_object_map(), holder, smi_handler));
} else if (accessors->IsAccessorPair()) {
Handle<Object> setter(Handle<AccessorPair>::cast(accessors)->setter(),
......@@ -1751,8 +1754,8 @@ MaybeObjectHandle StoreIC::ComputeHandler(LookupIterator* lookup) {
if (call_optimization.is_simple_api_call()) {
if (call_optimization.IsCompatibleReceiver(receiver, holder)) {
CallOptimization::HolderLookup holder_lookup;
call_optimization.LookupHolderOfExpectedType(receiver_map(),
&holder_lookup);
call_optimization.LookupHolderOfExpectedType(
lookup_start_object_map(), &holder_lookup);
Handle<Smi> smi_handler = StoreHandler::StoreApiSetter(
isolate(),
......@@ -1762,7 +1765,7 @@ MaybeObjectHandle StoreIC::ComputeHandler(LookupIterator* lookup) {
call_optimization.GetAccessorContext(holder->map()), isolate());
TRACE_HANDLER_STATS(isolate(), StoreIC_StoreApiSetterOnPrototypeDH);
return MaybeObjectHandle(StoreHandler::StoreThroughPrototype(
isolate(), receiver_map(), holder, smi_handler,
isolate(), lookup_start_object_map(), holder, smi_handler,
MaybeObjectHandle::Weak(call_optimization.api_call_info()),
MaybeObjectHandle::Weak(context)));
}
......@@ -1785,7 +1788,7 @@ MaybeObjectHandle StoreIC::ComputeHandler(LookupIterator* lookup) {
TRACE_HANDLER_STATS(isolate(), StoreIC_StoreAccessorOnPrototypeDH);
return MaybeObjectHandle(StoreHandler::StoreThroughPrototype(
isolate(), receiver_map(), holder, smi_handler));
isolate(), lookup_start_object_map(), holder, smi_handler));
}
TRACE_HANDLER_STATS(isolate(), StoreIC_SlowStub);
return MaybeObjectHandle(StoreHandler::StoreSlow(isolate()));
......@@ -1844,7 +1847,7 @@ MaybeObjectHandle StoreIC::ComputeHandler(LookupIterator* lookup) {
Handle<JSReceiver>::cast(lookup->GetReceiver());
Handle<JSProxy> holder = lookup->GetHolder<JSProxy>();
return MaybeObjectHandle(StoreHandler::StoreProxy(
isolate(), receiver_map(), holder, receiver));
isolate(), lookup_start_object_map(), holder, receiver));
}
case LookupIterator::INTEGER_INDEXED_EXOTIC:
......
......@@ -37,8 +37,9 @@ class IC {
State state() const { return state_; }
// Compute the current IC state based on the target stub, receiver and name.
void UpdateState(Handle<Object> receiver, Handle<Object> name);
// Compute the current IC state based on the target stub, lookup_start_object
// and name.
void UpdateState(Handle<Object> lookup_start_object, Handle<Object> name);
bool RecomputeHandlerForName(Handle<Object> name);
void MarkRecomputeHandler(Handle<Object> name) {
......@@ -121,8 +122,8 @@ class IC {
}
bool ShouldRecomputeHandler(Handle<String> name);
Handle<Map> receiver_map() { return receiver_map_; }
inline void update_receiver_map(Handle<Object> receiver);
Handle<Map> lookup_start_object_map() { return lookup_start_object_map_; }
inline void update_lookup_start_object_map(Handle<Object> object);
void TargetMaps(MapHandles* list) {
FindTargetMaps();
......@@ -152,7 +153,7 @@ class IC {
State old_state_; // For saving if we marked as prototype failure.
State state_;
FeedbackSlotKind kind_;
Handle<Map> receiver_map_;
Handle<Map> lookup_start_object_map_;
MapHandles target_maps_;
bool target_maps_set_;
......
......@@ -20,67 +20,60 @@ namespace internal {
LookupIterator::LookupIterator(Isolate* isolate, Handle<Object> receiver,
Handle<Name> name, Configuration configuration)
: LookupIterator(isolate, receiver, name, kInvalidIndex,
GetRoot(isolate, receiver, kInvalidIndex), configuration) {
}
: LookupIterator(isolate, receiver, name, kInvalidIndex, receiver,
configuration) {}
LookupIterator::LookupIterator(Isolate* isolate, Handle<Object> receiver,
Handle<Name> name, Handle<JSReceiver> holder,
Handle<Name> name,
Handle<Object> lookup_start_object,
Configuration configuration)
: LookupIterator(isolate, receiver, name, kInvalidIndex, holder,
configuration) {}
: LookupIterator(isolate, receiver, name, kInvalidIndex,
lookup_start_object, configuration) {}
LookupIterator::LookupIterator(Isolate* isolate, Handle<Object> receiver,
size_t index, Configuration configuration)
: LookupIterator(isolate, receiver, Handle<Name>(), index,
GetRoot(isolate, receiver, index), configuration) {
: LookupIterator(isolate, receiver, Handle<Name>(), index, receiver,
configuration) {
DCHECK_NE(index, kInvalidIndex);
}
LookupIterator::LookupIterator(Isolate* isolate, Handle<Object> receiver,
size_t index, Handle<JSReceiver> holder,
size_t index, Handle<Object> lookup_start_object,
Configuration configuration)
: LookupIterator(isolate, receiver, Handle<Name>(), index, holder,
configuration) {
: LookupIterator(isolate, receiver, Handle<Name>(), index,
lookup_start_object, configuration) {
DCHECK_NE(index, kInvalidIndex);
}
LookupIterator::LookupIterator(Isolate* isolate, Handle<Object> receiver,
const Key& key, Configuration configuration)
: LookupIterator(isolate, receiver, key.name(), key.index(),
GetRoot(isolate, receiver, key.index()), configuration) {}
LookupIterator::LookupIterator(Isolate* isolate, Handle<Object> receiver,
const Key& key, Handle<JSReceiver> holder,
Configuration configuration)
: LookupIterator(isolate, receiver, key.name(), key.index(), holder,
: LookupIterator(isolate, receiver, key.name(), key.index(), receiver,
configuration) {}
LookupIterator LookupIterator::LookupWithReceiver(Isolate* isolate,
Handle<Object> receiver,
LookupIterator::LookupIterator(Isolate* isolate, Handle<Object> receiver,
const Key& key,
Handle<Object> holder,
Configuration configuration) {
return LookupIterator(isolate, receiver, key,
GetRoot(isolate, holder, key.index()), configuration);
}
Handle<Object> lookup_start_object,
Configuration configuration)
: LookupIterator(isolate, receiver, key.name(), key.index(),
lookup_start_object, configuration) {}
// This private constructor is the central bottleneck that all the other
// constructors use.
LookupIterator::LookupIterator(Isolate* isolate, Handle<Object> receiver,
Handle<Name> name, size_t index,
Handle<JSReceiver> holder,
Handle<Object> lookup_start_object,
Configuration configuration)
: configuration_(ComputeConfiguration(isolate, configuration, name)),
isolate_(isolate),
name_(name),
receiver_(receiver),
initial_holder_(holder),
lookup_start_object_(lookup_start_object),
index_(index) {
if (IsElement()) {
// If we're not looking at a TypedArray, we will need the key represented
// as an internalized string.
if (index_ > JSArray::kMaxArrayIndex && !holder->IsJSTypedArray()) {
if (index_ > JSArray::kMaxArrayIndex &&
!lookup_start_object->IsJSTypedArray()) {
if (name_.is_null()) {
name_ = isolate->factory()->SizeToString(index_);
}
......@@ -94,10 +87,10 @@ LookupIterator::LookupIterator(Isolate* isolate, Handle<Object> receiver,
name_ = isolate->factory()->InternalizeName(name_);
#ifdef DEBUG
// Assert that the name is not an index.
// If we're not looking at the prototype chain and the initial holder is
// not a typed array, then this means "array index", otherwise we need to
// If we're not looking at the prototype chain and the lookup start object
// is not a typed array, then this means "array index", otherwise we need to
// ensure the full generality so that typed arrays are handled correctly.
if (!check_prototype_chain() && !holder->IsJSTypedArray()) {
if (!check_prototype_chain() && !lookup_start_object->IsJSTypedArray()) {
uint32_t index;
DCHECK(!name_->AsArrayIndex(&index));
} else {
......@@ -251,12 +244,12 @@ LookupIterator::Configuration LookupIterator::ComputeConfiguration(
// static
Handle<JSReceiver> LookupIterator::GetRoot(Isolate* isolate,
Handle<Object> receiver,
Handle<Object> lookup_start_object,
size_t index) {
if (receiver->IsJSReceiver(isolate)) {
return Handle<JSReceiver>::cast(receiver);
if (lookup_start_object->IsJSReceiver(isolate)) {
return Handle<JSReceiver>::cast(lookup_start_object);
}
return GetRootForNonJSReceiver(isolate, receiver, index);
return GetRootForNonJSReceiver(isolate, lookup_start_object, index);
}
template <class T>
......
......@@ -49,18 +49,21 @@ LookupIterator::LookupIterator(Isolate* isolate, Handle<Object> receiver,
name_(name),
transition_(transition_map),
receiver_(receiver),
initial_holder_(GetRoot(isolate, receiver)),
lookup_start_object_(receiver),
index_(kInvalidIndex) {
holder_ = initial_holder_;
holder_ = GetRoot(isolate, lookup_start_object_);
}
template <bool is_element>
void LookupIterator::Start() {
// GetRoot might allocate if lookup_start_object_ is a string.
holder_ = GetRoot(isolate_, lookup_start_object_, index_);
{
DisallowHeapAllocation no_gc;
has_property_ = false;
state_ = NOT_FOUND;
holder_ = initial_holder_;
JSReceiver holder = *holder_;
Map map = holder.map(isolate_);
......@@ -69,6 +72,7 @@ void LookupIterator::Start() {
if (IsFound()) return;
NextInternal<is_element>(map, holder);
}
}
template void LookupIterator::Start<true>();
......@@ -127,22 +131,25 @@ template void LookupIterator::RestartInternal<false>(InterceptorState);
// static
Handle<JSReceiver> LookupIterator::GetRootForNonJSReceiver(
Isolate* isolate, Handle<Object> receiver, size_t index) {
Isolate* isolate, Handle<Object> lookup_start_object, size_t index) {
// Strings are the only objects with properties (only elements) directly on
// the wrapper. Hence we can skip generating the wrapper for all other cases.
if (receiver->IsString(isolate) &&
index < static_cast<size_t>(String::cast(*receiver).length())) {
if (lookup_start_object->IsString(isolate) &&
index <
static_cast<size_t>(String::cast(*lookup_start_object).length())) {
// TODO(verwaest): Speed this up. Perhaps use a cached wrapper on the native
// context, ensuring that we don't leak it into JS?
Handle<JSFunction> constructor = isolate->string_function();
Handle<JSObject> result = isolate->factory()->NewJSObject(constructor);
Handle<JSPrimitiveWrapper>::cast(result)->set_value(*receiver);
Handle<JSPrimitiveWrapper>::cast(result)->set_value(*lookup_start_object);
return result;
}
Handle<HeapObject> root(
receiver->GetPrototypeChainRootMap(isolate).prototype(isolate), isolate);
lookup_start_object->GetPrototypeChainRootMap(isolate).prototype(isolate),
isolate);
if (root->IsNull(isolate)) {
isolate->PushStackTraceAndDie(reinterpret_cast<void*>(receiver->ptr()));
isolate->PushStackTraceAndDie(
reinterpret_cast<void*>(lookup_start_object->ptr()));
}
return Handle<JSReceiver>::cast(root);
}
......
......@@ -70,27 +70,21 @@ class V8_EXPORT_PRIVATE LookupIterator final {
Handle<Name> name,
Configuration configuration = DEFAULT);
inline LookupIterator(Isolate* isolate, Handle<Object> receiver,
Handle<Name> name, Handle<JSReceiver> holder,
Handle<Name> name, Handle<Object> lookup_start_object,
Configuration configuration = DEFAULT);
inline LookupIterator(Isolate* isolate, Handle<Object> receiver, size_t index,
Configuration configuration = DEFAULT);
inline LookupIterator(Isolate* isolate, Handle<Object> receiver, size_t index,
Handle<JSReceiver> holder,
Handle<Object> lookup_start_object,
Configuration configuration = DEFAULT);
inline LookupIterator(Isolate* isolate, Handle<Object> receiver,
const Key& key, Configuration configuration = DEFAULT);
inline LookupIterator(Isolate* isolate, Handle<Object> receiver,
const Key& key, Handle<JSReceiver> holder,
const Key& key, Handle<Object> lookup_start_object,
Configuration configuration = DEFAULT);
// Usable for cases where "holder" is not necessarily a JSReceiver (a separate
// overloaded constructor is not possible).
static inline LookupIterator LookupWithReceiver(
Isolate* isolate, Handle<Object> receiver, const Key& key,
Handle<Object> holder, Configuration configuration = DEFAULT);
void Restart() {
InterceptorState state = InterceptorState::kUninitialized;
IsElement() ? RestartInternal<true>(state) : RestartInternal<false>(state);
......@@ -134,6 +128,8 @@ class V8_EXPORT_PRIVATE LookupIterator final {
template <class T>
inline Handle<T> GetHolder() const;
Handle<Object> lookup_start_object() const { return lookup_start_object_; }
bool HolderIsReceiver() const;
bool HolderIsReceiverOrHiddenPrototype() const;
......@@ -202,7 +198,8 @@ class V8_EXPORT_PRIVATE LookupIterator final {
inline LookupIterator(Isolate* isolate, Handle<Object> receiver,
Handle<Name> name, size_t index,
Handle<JSReceiver> holder, Configuration configuration);
Handle<Object> lookup_start_object,
Configuration configuration);
// For |ForTransitionHandler|.
LookupIterator(Isolate* isolate, Handle<Object> receiver, Handle<Name> name,
......@@ -267,9 +264,10 @@ class V8_EXPORT_PRIVATE LookupIterator final {
Handle<Name> name);
static Handle<JSReceiver> GetRootForNonJSReceiver(
Isolate* isolate, Handle<Object> receiver, size_t index = kInvalidIndex);
Isolate* isolate, Handle<Object> lookup_start_object,
size_t index = kInvalidIndex);
static inline Handle<JSReceiver> GetRoot(Isolate* isolate,
Handle<Object> receiver,
Handle<Object> lookup_start_object,
size_t index = kInvalidIndex);
State NotFound(JSReceiver const holder) const;
......@@ -286,7 +284,7 @@ class V8_EXPORT_PRIVATE LookupIterator final {
Handle<Object> transition_;
const Handle<Object> receiver_;
Handle<JSReceiver> holder_;
const Handle<JSReceiver> initial_holder_;
const Handle<Object> lookup_start_object_;
const size_t index_;
InternalIndex number_ = InternalIndex::NotFound();
};
......
......@@ -39,8 +39,7 @@ MaybeHandle<Object> Runtime::GetObjectProperty(Isolate* isolate,
bool success = false;
LookupIterator::Key lookup_key(isolate, key, &success);
if (!success) return MaybeHandle<Object>();
LookupIterator it =
LookupIterator::LookupWithReceiver(isolate, receiver, lookup_key, holder);
LookupIterator it = LookupIterator(isolate, receiver, lookup_key, holder);
MaybeHandle<Object> result = Object::GetProperty(&it);
if (is_found) *is_found = it.IsFound();
......
......@@ -4,6 +4,12 @@
// Flags: --allow-natives-syntax --super-ic
function forceDictionaryMode(obj) {
for (let i = 0; i < 2000; ++i) {
obj["prop" + i] = "prop_value";
}
}
(function TestHomeObjectPrototypeNull() {
class A {}
......@@ -303,10 +309,7 @@
// Create a "home object proto" object which is a bound function.
let home_object_proto = (function() {}).bind({});
// Force home_object_proto to dictionary mode.
for (let i = 0; i < 2000; ++i) {
home_object_proto["prop" + i] = "prop_value";
}
forceDictionaryMode(home_object_proto);
B.prototype.__proto__ = home_object_proto;
assertEquals(0, home_object_proto.length);
......@@ -342,3 +345,98 @@
assertEquals(A.prototype.foo, (new B).m());
assertEquals(A.prototype.foo, (new B).n()());
})();
// Regression test for a receiver vs lookup start object confusion.
(function TestProxyAsLookupStartObject1() {
class A {}
class B extends A {
bar() {
return super.foo;
}
}
const o = new B();
B.prototype.__proto__ = new Proxy({}, {});
for (let i = 0; i < 1000; ++i) {
assertEquals(undefined, o.bar());
}
})();
(function TestProxyAsLookupStartObject2() {
class A {}
class B extends A {
bar() {
return super.foo;
}
}
const o = new B();
forceDictionaryMode(o);
o.foo = "wrong value";
B.prototype.__proto__ = new Proxy({}, {});
for (let i = 0; i < 1000; ++i) {
assertEquals(undefined, o.bar());
}
})();
(function TestProxyAsLookupStartObject3() {
class A {}
class B extends A {
bar() {
return super.foo;
}
}
const o = new B();
B.prototype.__proto__ = new Proxy({}, {});
B.prototype.__proto__.foo = "correct value";
for (let i = 0; i < 1000; ++i) {
assertEquals(B.prototype.__proto__.foo, o.bar());
}
})();
(function TestDictionaryModeHomeObjectProto1() {
class A {}
forceDictionaryMode(A.prototype);
A.prototype.foo = "correct value";
class B extends A {
bar() {
return super.foo;
}
}
const o = new B();
for (let i = 0; i < 1000; ++i) {
assertEquals(A.prototype.foo, o.bar());
}
})();
(function TestDictionaryModeHomeObjectProto2() {
class A {}
A.prototype.foo = "correct value";
class B extends A {};
forceDictionaryMode(B.prototype);
class C extends B {
bar() {
return super.foo;
}
}
const o = new C();
for (let i = 0; i < 1000; ++i) {
assertEquals(A.prototype.foo, o.bar());
}
})();
(function TestHomeObjectProtoIsGlobalThis() {
class A {};
class B extends A {
bar() { return super.foo; }
}
B.prototype.__proto__ = globalThis;
const o = new B();
for (let i = 0; i < 1000; ++i) {
assertEquals(undefined, o.bar());
}
})();
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