From 24fbcf8847a7dc50b85ed14cebdb54cc152d9f5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marja=20H=C3=B6ltt=C3=A4?= Date: Thu, 24 Sep 2020 12:54:23 +0200 Subject: [PATCH] Try 2: [super ic] Fix more receiver vs lookup start object vs holder confusion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. Reland: not relying on the prototype validity cell after all Previous version: https://chromium-review.googlesource.com/c/v8/v8/+/2414039 Bug: v8:9237, chromium:1127653 Change-Id: I1949442f8ddcecb776f0c5d2cf737cb75f80e313 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2428588 Reviewed-by: Igor Sheludko Commit-Queue: Marja Hölttä Cr-Commit-Position: refs/heads/master@{#70112} --- src/ic/accessor-assembler.cc | 70 +++++++++++-------- src/ic/accessor-assembler.h | 10 +-- src/ic/handler-configuration.cc | 94 ++++++++++++++----------- src/ic/handler-configuration.h | 36 +++++----- src/ic/ic-inl.h | 9 +-- src/ic/ic.cc | 95 +++++++++++++------------ src/ic/ic.h | 11 +-- src/objects/lookup-inl.h | 73 +++++++++---------- src/objects/lookup.cc | 41 ++++++----- src/objects/lookup.h | 24 +++---- src/runtime/runtime-object.cc | 3 +- test/mjsunit/es6/super-ic.js | 120 ++++++++++++++++++++++++++++++-- 12 files changed, 363 insertions(+), 223 deletions(-) diff --git a/src/ic/accessor-assembler.cc b/src/ic/accessor-assembler.cc index bb59916d40..40728edf90 100644 --- a/src/ic/accessor-assembler.cc +++ b/src/ic/accessor-assembler.cc @@ -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 maybe_context = Select( - IsSetWord(handler_word), + IsSetWord( + 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 TNode AccessorAssembler::HandleProtoHandler( const ICParameters* p, TNode 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 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(handler_flags), - &if_do_access_check, &if_lookup_on_receiver); + Branch( + IsSetWord( + handler_flags), + &if_do_access_check, &if_lookup_on_lookup_start_object); BIND(&if_do_access_check); { @@ -857,29 +863,31 @@ TNode AccessorAssembler::HandleProtoHandler( CSA_ASSERT(this, IsWeakOrCleared(data2)); TNode 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))); + // 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 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(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 +909,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 properties, TNode name_index) { if (access_mode == LoadAccessMode::kHas) { exit_point->Return(TrueConstant()); @@ -1542,7 +1550,7 @@ void AccessorAssembler::HandleStoreICProtoHandler( TNode smi_handler = HandleProtoHandler( p, handler, on_code_handler, - // on_found_on_receiver + // on_found_on_lookup_start_object [=](TNode properties, TNode name_index) { TNode details = LoadDetailsByKeyIndex(properties, name_index); // Check that the property is a writable data property (no accessor). @@ -1618,7 +1626,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 receiver_map = LoadMap(CAST(p->receiver())); InvalidateValidityCellIfPrototype(receiver_map); @@ -1648,7 +1657,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 maybe_context = Select( - IsSetWord32(handler_word), + IsSetWord32( + handler_word), [=] { return LoadHandlerDataField(handler, 3); }, [=] { return LoadHandlerDataField(handler, 2); }); diff --git a/src/ic/accessor-assembler.h b/src/ic/accessor-assembler.h index f4c10a7868..bf506da478 100644 --- a/src/ic/accessor-assembler.h +++ b/src/ic/accessor-assembler.h @@ -199,6 +199,8 @@ class V8_EXPORT_PRIVATE AccessorAssembler : public CodeStubAssembler { TNode slot() const { return slot_; } TNode vector() const { return vector_; } + TNode 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 code_handler)>; - using OnFoundOnReceiver = std::function properties, - TNode name_index)>; + using OnFoundOnLookupStartObject = std::function properties, TNode name_index)>; template TNode HandleProtoHandler( const ICParameters* p, TNode 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 handler_word, TNode holder, diff --git a/src/ic/handler-configuration.cc b/src/ic/handler-configuration.cc index 1aa0046892..7138989252 100644 --- a/src/ic/handler-configuration.cc +++ b/src/ic/handler-configuration.cc @@ -27,16 +27,20 @@ Handle SetBitFieldValue(Isolate* isolate, Handle smi_handler, // Load/StoreHandler to the base class. template int InitPrototypeChecksImpl(Isolate* isolate, Handle handler, - Handle* smi_handler, Handle receiver_map, + Handle* smi_handler, + Handle lookup_start_object_map, Handle 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,19 @@ int InitPrototypeChecksImpl(Isolate* isolate, Handle handler, Handle 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(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->IsJSGlobalObjectMap()) { if (!fill_handler) { - // Enable lookup on receiver. - using Bit = typename ICHandler::LookupOnReceiverBits; - *smi_handler = SetBitFieldValue(isolate, *smi_handler, true); + // Enable lookup on lookup start object. + *smi_handler = + SetBitFieldValue( + isolate, *smi_handler, true); } } if (fill_handler) { @@ -80,38 +86,37 @@ int InitPrototypeChecksImpl(Isolate* isolate, Handle 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. template int GetHandlerDataSize(Isolate* isolate, Handle* smi_handler, - Handle receiver_map, Handle holder, - MaybeObjectHandle data1, + Handle lookup_start_object_map, + Handle holder, MaybeObjectHandle data1, MaybeObjectHandle maybe_data2 = MaybeObjectHandle()) { DCHECK_NOT_NULL(smi_handler); - return InitPrototypeChecksImpl(isolate, Handle(), - smi_handler, receiver_map, - holder, data1, maybe_data2); + return InitPrototypeChecksImpl( + isolate, Handle(), smi_handler, lookup_start_object_map, + holder, data1, maybe_data2); } template void InitPrototypeChecks(Isolate* isolate, Handle handler, - Handle receiver_map, Handle holder, - MaybeObjectHandle data1, + Handle lookup_start_object_map, + Handle holder, MaybeObjectHandle data1, MaybeObjectHandle maybe_data2 = MaybeObjectHandle()) { - InitPrototypeChecksImpl( - isolate, handler, nullptr, receiver_map, holder, data1, maybe_data2); + InitPrototypeChecksImpl(isolate, handler, nullptr, + lookup_start_object_map, holder, + data1, maybe_data2); } } // namespace // static -Handle LoadHandler::LoadFromPrototype(Isolate* isolate, - Handle receiver_map, - Handle holder, - Handle smi_handler, - MaybeObjectHandle maybe_data1, - MaybeObjectHandle maybe_data2) { +Handle LoadHandler::LoadFromPrototype( + Isolate* isolate, Handle lookup_start_object_map, + Handle holder, Handle smi_handler, + MaybeObjectHandle maybe_data1, MaybeObjectHandle maybe_data2) { MaybeObjectHandle data1; if (maybe_data1.is_null()) { data1 = MaybeObjectHandle::Weak(holder); @@ -119,44 +124,48 @@ Handle LoadHandler::LoadFromPrototype(Isolate* isolate, data1 = maybe_data1; } - int data_size = GetHandlerDataSize( - isolate, &smi_handler, receiver_map, holder, data1, maybe_data2); + int data_size = GetHandlerDataSize(isolate, &smi_handler, + lookup_start_object_map, + holder, data1, maybe_data2); - Handle validity_cell = - Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate); + Handle validity_cell = Map::GetOrCreatePrototypeChainValidityCell( + lookup_start_object_map, isolate); Handle 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 LoadHandler::LoadFullChain(Isolate* isolate, - Handle receiver_map, + Handle lookup_start_object_map, const MaybeObjectHandle& holder, Handle smi_handler) { Handle end; // null handle, means full prototype chain lookup. MaybeObjectHandle data1 = holder; - int data_size = GetHandlerDataSize(isolate, &smi_handler, - receiver_map, end, data1); + int data_size = GetHandlerDataSize( + isolate, &smi_handler, lookup_start_object_map, end, data1); - Handle validity_cell = - Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate); + Handle 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 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; } @@ -245,7 +254,8 @@ MaybeObjectHandle StoreHandler::StoreTransition(Isolate* isolate, DCHECK(!transition_map->IsJSGlobalObjectMap()); Handle 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); diff --git a/src/ic/handler-configuration.h b/src/ic/handler-configuration.h index 56d1ed9728..d0afe54697 100644 --- a/src/ic/handler-configuration.h +++ b/src/ic/handler-configuration.h @@ -50,15 +50,17 @@ class LoadHandler final : public DataHandler { }; using KindBits = base::BitField; - // 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; + // chain. Ignored when loading from lookup start object. + using DoAccessCheckOnLookupStartObjectBits = KindBits::Next; - // 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; + // when loading value from prototype chain. Ignored when loading from lookup + // start object. + using LookupOnLookupStartObjectBits = + DoAccessCheckOnLookupStartObjectBits::Next; // // 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; + LookupOnLookupStartObjectBits::Next; // Make sure we don't overflow the smi. STATIC_ASSERT(DescriptorBits::kLastUsedBit < kSmiValueSize); // // Encoding when KindBits contains kField. // - using IsInobjectBits = LookupOnReceiverBits::Next; + using IsInobjectBits = LookupOnLookupStartObjectBits::Next; using IsDoubleBits = IsInobjectBits::Next; // +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; + using AllowOutOfBoundsBits = LookupOnLookupStartObjectBits::Next; // // 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); @@ -212,20 +215,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; + // Defines whether access rights check should be done on lookup start object. + using DoAccessCheckOnLookupStartObjectBits = KindBits::Next; - // 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; + using LookupOnLookupStartObjectBits = + DoAccessCheckOnLookupStartObjectBits::Next; // Applicable to kField, kTransitionToField and kTransitionToConstant // kinds. // Index of a value entry in the descriptor array. using DescriptorBits = - LookupOnReceiverBits::Next; + LookupOnLookupStartObjectBits::Next; // // Encodes the bits when StoreSlow contains KeyedAccessStoreMode. diff --git a/src/ic/ic-inl.h b/src/ic/ic-inl.h index 29373d85d8..35218f7df5 100644 --- a/src/ic/ic-inl.h +++ b/src/ic/ic-inl.h @@ -16,11 +16,12 @@ namespace v8 { namespace internal { -void IC::update_receiver_map(Handle receiver) { - if (receiver->IsSmi()) { - receiver_map_ = isolate_->factory()->heap_number_map(); +void IC::update_lookup_start_object_map(Handle 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_); } } diff --git a/src/ic/ic.cc b/src/ic/ic.cc index a6d311a0eb..d8e25f3c74 100644 --- a/src/ic/ic.cc +++ b/src/ic/ic.cc @@ -97,7 +97,7 @@ void IC::TraceIC(const char* type, Handle name, State old_state, State new_state) { if (V8_LIKELY(!TracingFlags::is_ic_stats_enabled())) return; - Handle map = receiver_map(); // Might be empty. + Handle map = lookup_start_object_map(); // Might be empty. const char* modifier = ""; if (state() == NO_FEEDBACK) { @@ -217,7 +217,8 @@ bool IC::ShouldRecomputeHandler(Handle 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 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 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 name) { return true; } -void IC::UpdateState(Handle receiver, Handle name) { +void IC::UpdateState(Handle lookup_start_object, Handle 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 LoadIC::Load(Handle object, Handle 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 LoadIC::Load(Handle object, Handle 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, if (is_keyed() && state() != RECOMPUTE_HANDLER) { if (nexus()->GetName() != *name) return false; } - Handle map = receiver_map(); + Handle map = lookup_start_object_map(); std::vector maps_and_handlers; maps_and_handlers.reserve(FLAG_max_valid_polymorphic_map_count); @@ -642,7 +642,7 @@ bool IC::UpdatePolymorphicIC(Handle 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, void IC::UpdateMonomorphicIC(const MaybeObjectHandle& handler, Handle name) { DCHECK(IsHandler(*handler)); - ConfigureVectorState(name, receiver_map(), handler); + ConfigureVectorState(name, lookup_start_object_map(), handler); } void IC::CopyICToMegamorphicCache(Handle name) { @@ -721,7 +721,7 @@ void IC::SetCache(Handle 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_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 slow_handler = LoadHandler::LoadSlow(isolate()); Handle holder = lookup->GetHolder(); - 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 LoadIC::ComputeHandler(LookupIterator* lookup) { } } - Handle map = receiver_map(); + Handle map = lookup_start_object_map(); Handle holder; - bool receiver_is_holder; + bool holder_is_lookup_start_object; if (lookup->state() != LookupIterator::JSPROXY) { holder = lookup->GetHolder(); - 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 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 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 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 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 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 LoadIC::ComputeHandler(LookupIterator* lookup) { Handle 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 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 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 value = lookup->GetDataValue(); @@ -1032,9 +1033,10 @@ Handle LoadIC::ComputeHandler(LookupIterator* lookup) { return LoadHandler::LoadNonExistent(isolate()); case LookupIterator::JSPROXY: { Handle holder_proxy = lookup->GetHolder(); - 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_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 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 StoreIC::Store(Handle object, Handle 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 holder = lookup->GetHolder(); @@ -1647,13 +1649,13 @@ MaybeObjectHandle StoreIC::ComputeHandler(LookupIterator* lookup) { Handle smi_handler = StoreHandler::StoreGlobalProxy(isolate()); Handle 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 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 setter(Handle::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_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::cast(lookup->GetReceiver()); Handle holder = lookup->GetHolder(); return MaybeObjectHandle(StoreHandler::StoreProxy( - isolate(), receiver_map(), holder, receiver)); + isolate(), lookup_start_object_map(), holder, receiver)); } case LookupIterator::INTEGER_INDEXED_EXOTIC: diff --git a/src/ic/ic.h b/src/ic/ic.h index 6bc6c65f8c..170ee609cb 100644 --- a/src/ic/ic.h +++ b/src/ic/ic.h @@ -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 receiver, Handle name); + // Compute the current IC state based on the target stub, lookup_start_object + // and name. + void UpdateState(Handle lookup_start_object, Handle name); bool RecomputeHandlerForName(Handle name); void MarkRecomputeHandler(Handle name) { @@ -121,8 +122,8 @@ class IC { } bool ShouldRecomputeHandler(Handle name); - Handle receiver_map() { return receiver_map_; } - inline void update_receiver_map(Handle receiver); + Handle lookup_start_object_map() { return lookup_start_object_map_; } + inline void update_lookup_start_object_map(Handle 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 receiver_map_; + Handle lookup_start_object_map_; MapHandles target_maps_; bool target_maps_set_; diff --git a/src/objects/lookup-inl.h b/src/objects/lookup-inl.h index fde14c594b..2339606a45 100644 --- a/src/objects/lookup-inl.h +++ b/src/objects/lookup-inl.h @@ -20,67 +20,60 @@ namespace internal { LookupIterator::LookupIterator(Isolate* isolate, Handle receiver, Handle name, Configuration configuration) - : LookupIterator(isolate, receiver, name, kInvalidIndex, - GetRoot(isolate, receiver, kInvalidIndex), configuration) { -} - -LookupIterator::LookupIterator(Isolate* isolate, Handle receiver, - Handle name, Handle holder, - Configuration configuration) - : LookupIterator(isolate, receiver, name, kInvalidIndex, holder, + : LookupIterator(isolate, receiver, name, kInvalidIndex, receiver, configuration) {} LookupIterator::LookupIterator(Isolate* isolate, Handle receiver, - size_t index, Configuration configuration) - : LookupIterator(isolate, receiver, Handle(), index, - GetRoot(isolate, receiver, index), configuration) { - DCHECK_NE(index, kInvalidIndex); -} + Handle name, + Handle lookup_start_object, + Configuration configuration) + : LookupIterator(isolate, receiver, name, kInvalidIndex, + lookup_start_object, configuration) {} LookupIterator::LookupIterator(Isolate* isolate, Handle receiver, - size_t index, Handle holder, - Configuration configuration) - : LookupIterator(isolate, receiver, Handle(), index, holder, + size_t index, Configuration configuration) + : LookupIterator(isolate, receiver, Handle(), index, receiver, configuration) { DCHECK_NE(index, kInvalidIndex); } LookupIterator::LookupIterator(Isolate* isolate, Handle receiver, - const Key& key, Configuration configuration) - : LookupIterator(isolate, receiver, key.name(), key.index(), - GetRoot(isolate, receiver, key.index()), configuration) {} + size_t index, Handle lookup_start_object, + Configuration configuration) + : LookupIterator(isolate, receiver, Handle(), index, + lookup_start_object, configuration) { + DCHECK_NE(index, kInvalidIndex); +} LookupIterator::LookupIterator(Isolate* isolate, Handle receiver, - const Key& key, Handle holder, - Configuration configuration) - : LookupIterator(isolate, receiver, key.name(), key.index(), holder, + const Key& key, Configuration configuration) + : LookupIterator(isolate, receiver, key.name(), key.index(), receiver, configuration) {} -LookupIterator LookupIterator::LookupWithReceiver(Isolate* isolate, - Handle receiver, - const Key& key, - Handle holder, - Configuration configuration) { - return LookupIterator(isolate, receiver, key, - GetRoot(isolate, holder, key.index()), configuration); -} +LookupIterator::LookupIterator(Isolate* isolate, Handle receiver, + const Key& key, + Handle 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 receiver, Handle name, size_t index, - Handle holder, + Handle 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 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 LookupIterator::GetRoot(Isolate* isolate, - Handle receiver, + Handle lookup_start_object, size_t index) { - if (receiver->IsJSReceiver(isolate)) { - return Handle::cast(receiver); + if (lookup_start_object->IsJSReceiver(isolate)) { + return Handle::cast(lookup_start_object); } - return GetRootForNonJSReceiver(isolate, receiver, index); + return GetRootForNonJSReceiver(isolate, lookup_start_object, index); } template diff --git a/src/objects/lookup.cc b/src/objects/lookup.cc index b7086fc06f..c32cedd81f 100644 --- a/src/objects/lookup.cc +++ b/src/objects/lookup.cc @@ -49,26 +49,30 @@ LookupIterator::LookupIterator(Isolate* isolate, Handle 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 void LookupIterator::Start() { - DisallowHeapAllocation no_gc; + // GetRoot might allocate if lookup_start_object_ is a string. + holder_ = GetRoot(isolate_, lookup_start_object_, index_); - has_property_ = false; - state_ = NOT_FOUND; - holder_ = initial_holder_; + { + DisallowHeapAllocation no_gc; - JSReceiver holder = *holder_; - Map map = holder.map(isolate_); + has_property_ = false; + state_ = NOT_FOUND; - state_ = LookupInHolder(map, holder); - if (IsFound()) return; + JSReceiver holder = *holder_; + Map map = holder.map(isolate_); - NextInternal(map, holder); + state_ = LookupInHolder(map, holder); + if (IsFound()) return; + + NextInternal(map, holder); + } } template void LookupIterator::Start(); @@ -127,22 +131,25 @@ template void LookupIterator::RestartInternal(InterceptorState); // static Handle LookupIterator::GetRootForNonJSReceiver( - Isolate* isolate, Handle receiver, size_t index) { + Isolate* isolate, Handle 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(String::cast(*receiver).length())) { + if (lookup_start_object->IsString(isolate) && + index < + static_cast(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 constructor = isolate->string_function(); Handle result = isolate->factory()->NewJSObject(constructor); - Handle::cast(result)->set_value(*receiver); + Handle::cast(result)->set_value(*lookup_start_object); return result; } Handle root( - receiver->GetPrototypeChainRootMap(isolate).prototype(isolate), isolate); + lookup_start_object->GetPrototypeChainRootMap(isolate).prototype(isolate), + isolate); if (root->IsNull(isolate)) { - isolate->PushStackTraceAndDie(reinterpret_cast(receiver->ptr())); + isolate->PushStackTraceAndDie( + reinterpret_cast(lookup_start_object->ptr())); } return Handle::cast(root); } diff --git a/src/objects/lookup.h b/src/objects/lookup.h index d9c76dc1ef..28a6bd8336 100644 --- a/src/objects/lookup.h +++ b/src/objects/lookup.h @@ -70,27 +70,21 @@ class V8_EXPORT_PRIVATE LookupIterator final { Handle name, Configuration configuration = DEFAULT); inline LookupIterator(Isolate* isolate, Handle receiver, - Handle name, Handle holder, + Handle name, Handle lookup_start_object, Configuration configuration = DEFAULT); inline LookupIterator(Isolate* isolate, Handle receiver, size_t index, Configuration configuration = DEFAULT); inline LookupIterator(Isolate* isolate, Handle receiver, size_t index, - Handle holder, + Handle lookup_start_object, Configuration configuration = DEFAULT); inline LookupIterator(Isolate* isolate, Handle receiver, const Key& key, Configuration configuration = DEFAULT); inline LookupIterator(Isolate* isolate, Handle receiver, - const Key& key, Handle holder, + const Key& key, Handle 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 receiver, const Key& key, - Handle holder, Configuration configuration = DEFAULT); - void Restart() { InterceptorState state = InterceptorState::kUninitialized; IsElement() ? RestartInternal(state) : RestartInternal(state); @@ -134,6 +128,8 @@ class V8_EXPORT_PRIVATE LookupIterator final { template inline Handle GetHolder() const; + Handle 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 receiver, Handle name, size_t index, - Handle holder, Configuration configuration); + Handle lookup_start_object, + Configuration configuration); // For |ForTransitionHandler|. LookupIterator(Isolate* isolate, Handle receiver, Handle name, @@ -267,9 +264,10 @@ class V8_EXPORT_PRIVATE LookupIterator final { Handle name); static Handle GetRootForNonJSReceiver( - Isolate* isolate, Handle receiver, size_t index = kInvalidIndex); + Isolate* isolate, Handle lookup_start_object, + size_t index = kInvalidIndex); static inline Handle GetRoot(Isolate* isolate, - Handle receiver, + Handle lookup_start_object, size_t index = kInvalidIndex); State NotFound(JSReceiver const holder) const; @@ -286,7 +284,7 @@ class V8_EXPORT_PRIVATE LookupIterator final { Handle transition_; const Handle receiver_; Handle holder_; - const Handle initial_holder_; + const Handle lookup_start_object_; const size_t index_; InternalIndex number_ = InternalIndex::NotFound(); }; diff --git a/src/runtime/runtime-object.cc b/src/runtime/runtime-object.cc index c2b5926596..bd0b16a404 100644 --- a/src/runtime/runtime-object.cc +++ b/src/runtime/runtime-object.cc @@ -39,8 +39,7 @@ MaybeHandle Runtime::GetObjectProperty(Isolate* isolate, bool success = false; LookupIterator::Key lookup_key(isolate, key, &success); if (!success) return MaybeHandle(); - LookupIterator it = - LookupIterator::LookupWithReceiver(isolate, receiver, lookup_key, holder); + LookupIterator it = LookupIterator(isolate, receiver, lookup_key, holder); MaybeHandle result = Object::GetProperty(&it); if (is_found) *is_found = it.IsFound(); diff --git a/test/mjsunit/es6/super-ic.js b/test/mjsunit/es6/super-ic.js index ec1d081dce..60f6a2394a 100644 --- a/test/mjsunit/es6/super-ic.js +++ b/test/mjsunit/es6/super-ic.js @@ -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,112 @@ 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()); + } +})(); + +// Regression test for (mis)using the prototype validity cell mechanism. +(function TestLoadFromDictionaryModePrototype() { + const obj1 = {}; + const obj2 = {__proto__: obj1}; + forceDictionaryMode(obj1); + + for (let i = 0; i < 1000; ++i) { + assertEquals(undefined, obj1.x); + } + + obj1.x = "added"; + assertEquals("added", obj1.x); +})();