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

Try 2: [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.

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/+/2428588Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Commit-Queue: Marja Hölttä <marja@chromium.org>
Cr-Commit-Position: refs/heads/master@{#70112}
parent d17b83e5
...@@ -200,7 +200,8 @@ void AccessorAssembler::HandleLoadAccessor( ...@@ -200,7 +200,8 @@ void AccessorAssembler::HandleLoadAccessor(
// Context is stored either in data2 or data3 field depending on whether // Context is stored either in data2 or data3 field depending on whether
// the access check is enabled for this handler or not. // the access check is enabled for this handler or not.
TNode<MaybeObject> maybe_context = Select<MaybeObject>( TNode<MaybeObject> maybe_context = Select<MaybeObject>(
IsSetWord<LoadHandler::DoAccessCheckOnReceiverBits>(handler_word), IsSetWord<LoadHandler::DoAccessCheckOnLookupStartObjectBits>(
handler_word),
[=] { return LoadHandlerDataField(handler, 3); }, [=] { return LoadHandlerDataField(handler, 3); },
[=] { return LoadHandlerDataField(handler, 2); }); [=] { return LoadHandlerDataField(handler, 2); });
...@@ -790,12 +791,14 @@ void AccessorAssembler::HandleLoadICSmiHandlerHasNamedCase( ...@@ -790,12 +791,14 @@ void AccessorAssembler::HandleLoadICSmiHandlerHasNamedCase(
// generate a code that handles Code handlers. // generate a code that handles Code handlers.
// If |on_code_handler| is not provided, then only smi sub handler are // If |on_code_handler| is not provided, then only smi sub handler are
// expected. // expected.
// 3. Does access check on receiver if ICHandler::DoAccessCheckOnReceiverBits // 3. Does access check on lookup start object if
// bit is set in the smi handler. // ICHandler::DoAccessCheckOnLookupStartObjectBits bit is set in the smi
// 4. Does dictionary lookup on receiver if ICHandler::LookupOnReceiverBits bit // handler.
// is set in the smi handler. If |on_found_on_receiver| is provided then // 4. Does dictionary lookup on receiver if
// it calls it to generate a code that handles the "found on receiver case" // ICHandler::LookupOnLookupStartObjectBits bit is set in the smi handler. If
// or just misses if the |on_found_on_receiver| is not provided. // |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 // 5. Falls through in a case of a smi handler which is returned from this
// function (tagged!). // function (tagged!).
// TODO(ishell): Remove templatezation once we move common bits from // TODO(ishell): Remove templatezation once we move common bits from
...@@ -804,8 +807,8 @@ template <typename ICHandler, typename ICParameters> ...@@ -804,8 +807,8 @@ template <typename ICHandler, typename ICParameters>
TNode<Object> AccessorAssembler::HandleProtoHandler( TNode<Object> AccessorAssembler::HandleProtoHandler(
const ICParameters* p, TNode<DataHandler> handler, const ICParameters* p, TNode<DataHandler> handler,
const OnCodeHandler& on_code_handler, const OnCodeHandler& on_code_handler,
const OnFoundOnReceiver& on_found_on_receiver, Label* miss, const OnFoundOnLookupStartObject& on_found_on_lookup_start_object,
ICMode ic_mode) { Label* miss, ICMode ic_mode) {
// //
// Check prototype validity cell. // Check prototype validity cell.
// //
...@@ -835,21 +838,24 @@ TNode<Object> AccessorAssembler::HandleProtoHandler( ...@@ -835,21 +838,24 @@ TNode<Object> AccessorAssembler::HandleProtoHandler(
// because in the former case the validity cell check guards modifications // because in the former case the validity cell check guards modifications
// of the global object and the latter is not applicable to the global // of the global object and the latter is not applicable to the global
// object. // object.
int mask = ICHandler::LookupOnReceiverBits::kMask | int mask = ICHandler::LookupOnLookupStartObjectBits::kMask |
ICHandler::DoAccessCheckOnReceiverBits::kMask; ICHandler::DoAccessCheckOnLookupStartObjectBits::kMask;
if (ic_mode == ICMode::kGlobalIC) { if (ic_mode == ICMode::kGlobalIC) {
CSA_ASSERT(this, IsClearWord(handler_flags, mask)); CSA_ASSERT(this, IsClearWord(handler_flags, mask));
} else { } else {
DCHECK_EQ(ICMode::kNonGlobalIC, ic_mode); 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); GotoIf(IsClearWord(handler_flags, mask), &done);
// Only one of the bits can be set at a time. // Only one of the bits can be set at a time.
CSA_ASSERT(this, CSA_ASSERT(this,
WordNotEqual(WordAnd(handler_flags, IntPtrConstant(mask)), WordNotEqual(WordAnd(handler_flags, IntPtrConstant(mask)),
IntPtrConstant(mask))); IntPtrConstant(mask)));
Branch(IsSetWord<LoadHandler::DoAccessCheckOnReceiverBits>(handler_flags), Branch(
&if_do_access_check, &if_lookup_on_receiver); IsSetWord<typename ICHandler::DoAccessCheckOnLookupStartObjectBits>(
handler_flags),
&if_do_access_check, &if_lookup_on_lookup_start_object);
BIND(&if_do_access_check); BIND(&if_do_access_check);
{ {
...@@ -857,29 +863,31 @@ TNode<Object> AccessorAssembler::HandleProtoHandler( ...@@ -857,29 +863,31 @@ TNode<Object> AccessorAssembler::HandleProtoHandler(
CSA_ASSERT(this, IsWeakOrCleared(data2)); CSA_ASSERT(this, IsWeakOrCleared(data2));
TNode<Context> expected_native_context = TNode<Context> expected_native_context =
CAST(GetHeapObjectAssumeWeak(data2, miss)); CAST(GetHeapObjectAssumeWeak(data2, miss));
EmitAccessCheck(expected_native_context, p->context(), p->receiver(), EmitAccessCheck(expected_native_context, p->context(),
&done, miss); p->lookup_start_object(), &done, miss);
} }
// Dictionary lookup on receiver is not necessary for Load/StoreGlobalIC BIND(&if_lookup_on_lookup_start_object);
// because prototype validity cell check already guards modifications of
// the global object.
BIND(&if_lookup_on_receiver);
{ {
DCHECK_EQ(ICMode::kNonGlobalIC, ic_mode); // Dictionary lookup on lookup start object is not necessary for
CSA_ASSERT(this, Word32BinaryNot(HasInstanceType( // Load/StoreGlobalIC (which is the only case when the
CAST(p->receiver()), JS_GLOBAL_OBJECT_TYPE))); // 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 = TNode<NameDictionary> properties =
CAST(LoadSlowProperties(CAST(p->receiver()))); CAST(LoadSlowProperties(CAST(p->lookup_start_object())));
TVARIABLE(IntPtrT, var_name_index); TVARIABLE(IntPtrT, var_name_index);
Label found(this, &var_name_index); Label found(this, &var_name_index);
NameDictionaryLookup<NameDictionary>(properties, CAST(p->name()), NameDictionaryLookup<NameDictionary>(properties, CAST(p->name()),
&found, &var_name_index, &done); &found, &var_name_index, &done);
BIND(&found); BIND(&found);
{ {
if (on_found_on_receiver) { if (on_found_on_lookup_start_object) {
on_found_on_receiver(properties, var_name_index.value()); on_found_on_lookup_start_object(properties, var_name_index.value());
} else { } else {
Goto(miss); Goto(miss);
} }
...@@ -901,7 +909,7 @@ void AccessorAssembler::HandleLoadICProtoHandler( ...@@ -901,7 +909,7 @@ void AccessorAssembler::HandleLoadICProtoHandler(
p, handler, p, handler,
// Code sub-handlers are not expected in LoadICs, so no |on_code_handler|. // Code sub-handlers are not expected in LoadICs, so no |on_code_handler|.
nullptr, nullptr,
// on_found_on_receiver // on_found_on_lookup_start_object
[=](TNode<NameDictionary> properties, TNode<IntPtrT> name_index) { [=](TNode<NameDictionary> properties, TNode<IntPtrT> name_index) {
if (access_mode == LoadAccessMode::kHas) { if (access_mode == LoadAccessMode::kHas) {
exit_point->Return(TrueConstant()); exit_point->Return(TrueConstant());
...@@ -1542,7 +1550,7 @@ void AccessorAssembler::HandleStoreICProtoHandler( ...@@ -1542,7 +1550,7 @@ void AccessorAssembler::HandleStoreICProtoHandler(
TNode<Object> smi_handler = HandleProtoHandler<StoreHandler>( TNode<Object> smi_handler = HandleProtoHandler<StoreHandler>(
p, handler, on_code_handler, p, handler, on_code_handler,
// on_found_on_receiver // on_found_on_lookup_start_object
[=](TNode<NameDictionary> properties, TNode<IntPtrT> name_index) { [=](TNode<NameDictionary> properties, TNode<IntPtrT> name_index) {
TNode<Uint32T> details = LoadDetailsByKeyIndex(properties, name_index); TNode<Uint32T> details = LoadDetailsByKeyIndex(properties, name_index);
// Check that the property is a writable data property (no accessor). // Check that the property is a writable data property (no accessor).
...@@ -1618,7 +1626,8 @@ void AccessorAssembler::HandleStoreICProtoHandler( ...@@ -1618,7 +1626,8 @@ void AccessorAssembler::HandleStoreICProtoHandler(
{ {
// This is a case of "transitioning store" to a dictionary mode object // This is a case of "transitioning store" to a dictionary mode object
// when the property does not exist. The "existing property" case is // 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); Label slow(this);
TNode<Map> receiver_map = LoadMap(CAST(p->receiver())); TNode<Map> receiver_map = LoadMap(CAST(p->receiver()));
InvalidateValidityCellIfPrototype(receiver_map); InvalidateValidityCellIfPrototype(receiver_map);
...@@ -1648,7 +1657,8 @@ void AccessorAssembler::HandleStoreICProtoHandler( ...@@ -1648,7 +1657,8 @@ void AccessorAssembler::HandleStoreICProtoHandler(
// Context is stored either in data2 or data3 field depending on whether // Context is stored either in data2 or data3 field depending on whether
// the access check is enabled for this handler or not. // the access check is enabled for this handler or not.
TNode<MaybeObject> maybe_context = Select<MaybeObject>( TNode<MaybeObject> maybe_context = Select<MaybeObject>(
IsSetWord32<LoadHandler::DoAccessCheckOnReceiverBits>(handler_word), IsSetWord32<StoreHandler::DoAccessCheckOnLookupStartObjectBits>(
handler_word),
[=] { return LoadHandlerDataField(handler, 3); }, [=] { return LoadHandlerDataField(handler, 3); },
[=] { return LoadHandlerDataField(handler, 2); }); [=] { return LoadHandlerDataField(handler, 2); });
......
...@@ -199,6 +199,8 @@ class V8_EXPORT_PRIVATE AccessorAssembler : public CodeStubAssembler { ...@@ -199,6 +199,8 @@ class V8_EXPORT_PRIVATE AccessorAssembler : public CodeStubAssembler {
TNode<TaggedIndex> slot() const { return slot_; } TNode<TaggedIndex> slot() const { return slot_; }
TNode<HeapObject> vector() const { return vector_; } TNode<HeapObject> vector() const { return vector_; }
TNode<Object> lookup_start_object() const { return receiver(); }
bool receiver_is_null() const { return !receiver_.has_value(); } bool receiver_is_null() const { return !receiver_.has_value(); }
private: private:
...@@ -415,15 +417,15 @@ class V8_EXPORT_PRIVATE AccessorAssembler : public CodeStubAssembler { ...@@ -415,15 +417,15 @@ class V8_EXPORT_PRIVATE AccessorAssembler : public CodeStubAssembler {
// Low-level helpers. // Low-level helpers.
using OnCodeHandler = std::function<void(TNode<Code> code_handler)>; using OnCodeHandler = std::function<void(TNode<Code> code_handler)>;
using OnFoundOnReceiver = std::function<void(TNode<NameDictionary> properties, using OnFoundOnLookupStartObject = std::function<void(
TNode<IntPtrT> name_index)>; TNode<NameDictionary> properties, TNode<IntPtrT> name_index)>;
template <typename ICHandler, typename ICParameters> template <typename ICHandler, typename ICParameters>
TNode<Object> HandleProtoHandler( TNode<Object> HandleProtoHandler(
const ICParameters* p, TNode<DataHandler> handler, const ICParameters* p, TNode<DataHandler> handler,
const OnCodeHandler& on_code_handler, const OnCodeHandler& on_code_handler,
const OnFoundOnReceiver& on_found_on_receiver, Label* miss, const OnFoundOnLookupStartObject& on_found_on_lookup_start_object,
ICMode ic_mode); Label* miss, ICMode ic_mode);
void CheckHeapObjectTypeMatchesDescriptor(TNode<Word32T> handler_word, void CheckHeapObjectTypeMatchesDescriptor(TNode<Word32T> handler_word,
TNode<JSObject> holder, TNode<JSObject> holder,
......
...@@ -27,16 +27,20 @@ Handle<Smi> SetBitFieldValue(Isolate* isolate, Handle<Smi> smi_handler, ...@@ -27,16 +27,20 @@ Handle<Smi> SetBitFieldValue(Isolate* isolate, Handle<Smi> smi_handler,
// Load/StoreHandler to the base class. // Load/StoreHandler to the base class.
template <typename ICHandler, bool fill_handler = true> template <typename ICHandler, bool fill_handler = true>
int InitPrototypeChecksImpl(Isolate* isolate, Handle<ICHandler> handler, 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, Handle<JSReceiver> holder, MaybeObjectHandle data1,
MaybeObjectHandle maybe_data2) { MaybeObjectHandle maybe_data2) {
int data_size = 1; int data_size = 1;
// Holder-is-receiver case itself does not add entries unless there is an // Holder-is-receiver case itself does not add entries unless there is an
// optional data2 value provided. // optional data2 value provided.
if (receiver_map->IsPrimitiveMap() || DCHECK_IMPLIES(lookup_start_object_map->IsJSGlobalObjectMap(),
receiver_map->is_access_check_needed()) { lookup_start_object_map->is_prototype_map());
DCHECK(!receiver_map->IsJSGlobalObjectMap());
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 // The validity cell check for primitive and global proxy receivers does
// not guarantee that certain native context ever had access to other // not guarantee that certain native context ever had access to other
// native context. However, a handler created for one native context could // native context. However, a handler created for one native context could
...@@ -47,17 +51,19 @@ int InitPrototypeChecksImpl(Isolate* isolate, Handle<ICHandler> handler, ...@@ -47,17 +51,19 @@ int InitPrototypeChecksImpl(Isolate* isolate, Handle<ICHandler> handler,
Handle<Context> native_context = isolate->native_context(); Handle<Context> native_context = isolate->native_context();
handler->set_data2(HeapObjectReference::Weak(*native_context)); handler->set_data2(HeapObjectReference::Weak(*native_context));
} else { } else {
// Enable access checks on receiver. // Enable access checks on the lookup start object.
using Bit = typename ICHandler::DoAccessCheckOnReceiverBits; *smi_handler = SetBitFieldValue<
*smi_handler = SetBitFieldValue<Bit>(isolate, *smi_handler, true); typename ICHandler::DoAccessCheckOnLookupStartObjectBits>(
isolate, *smi_handler, true);
} }
data_size++; data_size++;
} else if (receiver_map->is_dictionary_map() && } else if (lookup_start_object_map->is_dictionary_map() &&
!receiver_map->IsJSGlobalObjectMap()) { !lookup_start_object_map->IsJSGlobalObjectMap()) {
if (!fill_handler) { if (!fill_handler) {
// Enable lookup on receiver. // Enable lookup on lookup start object.
using Bit = typename ICHandler::LookupOnReceiverBits; *smi_handler =
*smi_handler = SetBitFieldValue<Bit>(isolate, *smi_handler, true); SetBitFieldValue<typename ICHandler::LookupOnLookupStartObjectBits>(
isolate, *smi_handler, true);
} }
} }
if (fill_handler) { if (fill_handler) {
...@@ -80,38 +86,37 @@ int InitPrototypeChecksImpl(Isolate* isolate, Handle<ICHandler> handler, ...@@ -80,38 +86,37 @@ int InitPrototypeChecksImpl(Isolate* isolate, Handle<ICHandler> handler,
} }
// Returns 0 if the validity cell check is enough to ensure that the // Returns 0 if the validity cell check is enough to ensure that the
// prototype chain from |receiver_map| till |holder| did not change. // prototype chain from |lookup_start_object_map| till |holder| did not change.
// If the |holder| is an empty handle then the full prototype chain is // If the |holder| is an empty handle then the full prototype chain is
// checked. // checked.
template <typename ICHandler> template <typename ICHandler>
int GetHandlerDataSize(Isolate* isolate, Handle<Smi>* smi_handler, int GetHandlerDataSize(Isolate* isolate, Handle<Smi>* smi_handler,
Handle<Map> receiver_map, Handle<JSReceiver> holder, Handle<Map> lookup_start_object_map,
MaybeObjectHandle data1, Handle<JSReceiver> holder, MaybeObjectHandle data1,
MaybeObjectHandle maybe_data2 = MaybeObjectHandle()) { MaybeObjectHandle maybe_data2 = MaybeObjectHandle()) {
DCHECK_NOT_NULL(smi_handler); DCHECK_NOT_NULL(smi_handler);
return InitPrototypeChecksImpl<ICHandler, false>(isolate, Handle<ICHandler>(), return InitPrototypeChecksImpl<ICHandler, false>(
smi_handler, receiver_map, isolate, Handle<ICHandler>(), smi_handler, lookup_start_object_map,
holder, data1, maybe_data2); holder, data1, maybe_data2);
} }
template <typename ICHandler> template <typename ICHandler>
void InitPrototypeChecks(Isolate* isolate, Handle<ICHandler> handler, void InitPrototypeChecks(Isolate* isolate, Handle<ICHandler> handler,
Handle<Map> receiver_map, Handle<JSReceiver> holder, Handle<Map> lookup_start_object_map,
MaybeObjectHandle data1, Handle<JSReceiver> holder, MaybeObjectHandle data1,
MaybeObjectHandle maybe_data2 = MaybeObjectHandle()) { MaybeObjectHandle maybe_data2 = MaybeObjectHandle()) {
InitPrototypeChecksImpl<ICHandler, true>( InitPrototypeChecksImpl<ICHandler, true>(isolate, handler, nullptr,
isolate, handler, nullptr, receiver_map, holder, data1, maybe_data2); lookup_start_object_map, holder,
data1, maybe_data2);
} }
} // namespace } // namespace
// static // static
Handle<Object> LoadHandler::LoadFromPrototype(Isolate* isolate, Handle<Object> LoadHandler::LoadFromPrototype(
Handle<Map> receiver_map, Isolate* isolate, Handle<Map> lookup_start_object_map,
Handle<JSReceiver> holder, Handle<JSReceiver> holder, Handle<Smi> smi_handler,
Handle<Smi> smi_handler, MaybeObjectHandle maybe_data1, MaybeObjectHandle maybe_data2) {
MaybeObjectHandle maybe_data1,
MaybeObjectHandle maybe_data2) {
MaybeObjectHandle data1; MaybeObjectHandle data1;
if (maybe_data1.is_null()) { if (maybe_data1.is_null()) {
data1 = MaybeObjectHandle::Weak(holder); data1 = MaybeObjectHandle::Weak(holder);
...@@ -119,44 +124,48 @@ Handle<Object> LoadHandler::LoadFromPrototype(Isolate* isolate, ...@@ -119,44 +124,48 @@ Handle<Object> LoadHandler::LoadFromPrototype(Isolate* isolate,
data1 = maybe_data1; data1 = maybe_data1;
} }
int data_size = GetHandlerDataSize<LoadHandler>( int data_size = GetHandlerDataSize<LoadHandler>(isolate, &smi_handler,
isolate, &smi_handler, receiver_map, holder, data1, maybe_data2); lookup_start_object_map,
holder, data1, maybe_data2);
Handle<Object> validity_cell = Handle<Object> validity_cell = Map::GetOrCreatePrototypeChainValidityCell(
Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate); lookup_start_object_map, isolate);
Handle<LoadHandler> handler = isolate->factory()->NewLoadHandler(data_size); Handle<LoadHandler> handler = isolate->factory()->NewLoadHandler(data_size);
handler->set_smi_handler(*smi_handler); handler->set_smi_handler(*smi_handler);
handler->set_validity_cell(*validity_cell); handler->set_validity_cell(*validity_cell);
InitPrototypeChecks(isolate, handler, receiver_map, holder, data1, InitPrototypeChecks(isolate, handler, lookup_start_object_map, holder, data1,
maybe_data2); maybe_data2);
return handler; return handler;
} }
// static // static
Handle<Object> LoadHandler::LoadFullChain(Isolate* isolate, Handle<Object> LoadHandler::LoadFullChain(Isolate* isolate,
Handle<Map> receiver_map, Handle<Map> lookup_start_object_map,
const MaybeObjectHandle& holder, const MaybeObjectHandle& holder,
Handle<Smi> smi_handler) { Handle<Smi> smi_handler) {
Handle<JSReceiver> end; // null handle, means full prototype chain lookup. Handle<JSReceiver> end; // null handle, means full prototype chain lookup.
MaybeObjectHandle data1 = holder; MaybeObjectHandle data1 = holder;
int data_size = GetHandlerDataSize<LoadHandler>(isolate, &smi_handler, int data_size = GetHandlerDataSize<LoadHandler>(
receiver_map, end, data1); isolate, &smi_handler, lookup_start_object_map, end, data1);
Handle<Object> validity_cell = Handle<Object> validity_cell = Map::GetOrCreatePrototypeChainValidityCell(
Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate); lookup_start_object_map, isolate);
if (validity_cell->IsSmi()) { if (validity_cell->IsSmi()) {
DCHECK_EQ(1, data_size); DCHECK_EQ(1, data_size);
// Lookup on receiver isn't supported in case of a simple smi handler. // Lookup on lookup start object isn't supported in case of a simple smi
if (!LookupOnReceiverBits::decode(smi_handler->value())) return smi_handler; // handler.
if (!LookupOnLookupStartObjectBits::decode(smi_handler->value())) {
return smi_handler;
}
} }
Handle<LoadHandler> handler = isolate->factory()->NewLoadHandler(data_size); Handle<LoadHandler> handler = isolate->factory()->NewLoadHandler(data_size);
handler->set_smi_handler(*smi_handler); handler->set_smi_handler(*smi_handler);
handler->set_validity_cell(*validity_cell); handler->set_validity_cell(*validity_cell);
InitPrototypeChecks(isolate, handler, receiver_map, end, data1); InitPrototypeChecks(isolate, handler, lookup_start_object_map, end, data1);
return handler; return handler;
} }
...@@ -245,7 +254,8 @@ MaybeObjectHandle StoreHandler::StoreTransition(Isolate* isolate, ...@@ -245,7 +254,8 @@ MaybeObjectHandle StoreHandler::StoreTransition(Isolate* isolate,
DCHECK(!transition_map->IsJSGlobalObjectMap()); DCHECK(!transition_map->IsJSGlobalObjectMap());
Handle<StoreHandler> handler = isolate->factory()->NewStoreHandler(0); Handle<StoreHandler> handler = isolate->factory()->NewStoreHandler(0);
// Store normal with enabled lookup on receiver. // 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_smi_handler(Smi::FromInt(config));
handler->set_validity_cell(*validity_cell); handler->set_validity_cell(*validity_cell);
return MaybeObjectHandle(handler); return MaybeObjectHandle(handler);
......
...@@ -50,15 +50,17 @@ class LoadHandler final : public DataHandler { ...@@ -50,15 +50,17 @@ class LoadHandler final : public DataHandler {
}; };
using KindBits = base::BitField<Kind, 0, 4>; 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 // Applicable to named property kinds only when loading value from prototype
// chain. Ignored when loading from holder. // chain. Ignored when loading from lookup start object.
using DoAccessCheckOnReceiverBits = KindBits::Next<bool, 1>; 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 // proceeding to the prototype chain. Applicable to named property kinds only
// when loading value from prototype chain. Ignored when loading from holder. // when loading value from prototype chain. Ignored when loading from lookup
using LookupOnReceiverBits = DoAccessCheckOnReceiverBits::Next<bool, 1>; // start object.
using LookupOnLookupStartObjectBits =
DoAccessCheckOnLookupStartObjectBits::Next<bool, 1>;
// //
// Encoding when KindBits contains kForConstants. // Encoding when KindBits contains kForConstants.
...@@ -66,14 +68,14 @@ class LoadHandler final : public DataHandler { ...@@ -66,14 +68,14 @@ class LoadHandler final : public DataHandler {
// Index of a value entry in the descriptor array. // Index of a value entry in the descriptor array.
using DescriptorBits = using DescriptorBits =
LookupOnReceiverBits::Next<unsigned, kDescriptorIndexBitCount>; LookupOnLookupStartObjectBits::Next<unsigned, kDescriptorIndexBitCount>;
// Make sure we don't overflow the smi. // Make sure we don't overflow the smi.
STATIC_ASSERT(DescriptorBits::kLastUsedBit < kSmiValueSize); STATIC_ASSERT(DescriptorBits::kLastUsedBit < kSmiValueSize);
// //
// Encoding when KindBits contains kField. // Encoding when KindBits contains kField.
// //
using IsInobjectBits = LookupOnReceiverBits::Next<bool, 1>; using IsInobjectBits = LookupOnLookupStartObjectBits::Next<bool, 1>;
using IsDoubleBits = IsInobjectBits::Next<bool, 1>; using IsDoubleBits = IsInobjectBits::Next<bool, 1>;
// +1 here is to cover all possible JSObject header sizes. // +1 here is to cover all possible JSObject header sizes.
using FieldIndexBits = using FieldIndexBits =
...@@ -85,7 +87,7 @@ class LoadHandler final : public DataHandler { ...@@ -85,7 +87,7 @@ class LoadHandler final : public DataHandler {
// //
// Encoding when KindBits contains kElement or kIndexedString. // Encoding when KindBits contains kElement or kIndexedString.
// //
using AllowOutOfBoundsBits = LookupOnReceiverBits::Next<bool, 1>; using AllowOutOfBoundsBits = LookupOnLookupStartObjectBits::Next<bool, 1>;
// //
// Encoding when KindBits contains kElement. // Encoding when KindBits contains kElement.
...@@ -99,8 +101,9 @@ class LoadHandler final : public DataHandler { ...@@ -99,8 +101,9 @@ class LoadHandler final : public DataHandler {
// //
// Encoding when KindBits contains kModuleExport. // Encoding when KindBits contains kModuleExport.
// //
using ExportsIndexBits = LookupOnReceiverBits::Next< using ExportsIndexBits = LookupOnLookupStartObjectBits::Next<
unsigned, kSmiValueSize - LookupOnReceiverBits::kLastUsedBit - 1>; unsigned,
kSmiValueSize - LookupOnLookupStartObjectBits::kLastUsedBit - 1>;
// Decodes kind from Smi-handler. // Decodes kind from Smi-handler.
static inline Kind GetHandlerKind(Smi smi_handler); static inline Kind GetHandlerKind(Smi smi_handler);
...@@ -212,20 +215,21 @@ class StoreHandler final : public DataHandler { ...@@ -212,20 +215,21 @@ class StoreHandler final : public DataHandler {
// Applicable to kGlobalProxy, kProxy kinds. // Applicable to kGlobalProxy, kProxy kinds.
// Defines whether access rights check should be done on receiver object. // Defines whether access rights check should be done on lookup start object.
using DoAccessCheckOnReceiverBits = KindBits::Next<bool, 1>; 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 // proceeding to the prototype chain. Applicable to named property kinds only
// when storing through prototype chain. Ignored when storing to holder. // 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 // Applicable to kField, kTransitionToField and kTransitionToConstant
// kinds. // kinds.
// Index of a value entry in the descriptor array. // Index of a value entry in the descriptor array.
using DescriptorBits = using DescriptorBits =
LookupOnReceiverBits::Next<unsigned, kDescriptorIndexBitCount>; LookupOnLookupStartObjectBits::Next<unsigned, kDescriptorIndexBitCount>;
// //
// Encodes the bits when StoreSlow contains KeyedAccessStoreMode. // Encodes the bits when StoreSlow contains KeyedAccessStoreMode.
......
...@@ -16,11 +16,12 @@ ...@@ -16,11 +16,12 @@
namespace v8 { namespace v8 {
namespace internal { namespace internal {
void IC::update_receiver_map(Handle<Object> receiver) { void IC::update_lookup_start_object_map(Handle<Object> object) {
if (receiver->IsSmi()) { if (object->IsSmi()) {
receiver_map_ = isolate_->factory()->heap_number_map(); lookup_start_object_map_ = isolate_->factory()->heap_number_map();
} else { } else {
receiver_map_ = handle(HeapObject::cast(*receiver).map(), isolate_); lookup_start_object_map_ =
handle(HeapObject::cast(*object).map(), isolate_);
} }
} }
......
This diff is collapsed.
...@@ -37,8 +37,9 @@ class IC { ...@@ -37,8 +37,9 @@ class IC {
State state() const { return state_; } State state() const { return state_; }
// Compute the current IC state based on the target stub, receiver and name. // Compute the current IC state based on the target stub, lookup_start_object
void UpdateState(Handle<Object> receiver, Handle<Object> name); // and name.
void UpdateState(Handle<Object> lookup_start_object, Handle<Object> name);
bool RecomputeHandlerForName(Handle<Object> name); bool RecomputeHandlerForName(Handle<Object> name);
void MarkRecomputeHandler(Handle<Object> name) { void MarkRecomputeHandler(Handle<Object> name) {
...@@ -121,8 +122,8 @@ class IC { ...@@ -121,8 +122,8 @@ class IC {
} }
bool ShouldRecomputeHandler(Handle<String> name); bool ShouldRecomputeHandler(Handle<String> name);
Handle<Map> receiver_map() { return receiver_map_; } Handle<Map> lookup_start_object_map() { return lookup_start_object_map_; }
inline void update_receiver_map(Handle<Object> receiver); inline void update_lookup_start_object_map(Handle<Object> object);
void TargetMaps(MapHandles* list) { void TargetMaps(MapHandles* list) {
FindTargetMaps(); FindTargetMaps();
...@@ -152,7 +153,7 @@ class IC { ...@@ -152,7 +153,7 @@ class IC {
State old_state_; // For saving if we marked as prototype failure. State old_state_; // For saving if we marked as prototype failure.
State state_; State state_;
FeedbackSlotKind kind_; FeedbackSlotKind kind_;
Handle<Map> receiver_map_; Handle<Map> lookup_start_object_map_;
MapHandles target_maps_; MapHandles target_maps_;
bool target_maps_set_; bool target_maps_set_;
......
...@@ -20,67 +20,60 @@ namespace internal { ...@@ -20,67 +20,60 @@ namespace internal {
LookupIterator::LookupIterator(Isolate* isolate, Handle<Object> receiver, LookupIterator::LookupIterator(Isolate* isolate, Handle<Object> receiver,
Handle<Name> name, Configuration configuration) Handle<Name> name, Configuration configuration)
: LookupIterator(isolate, receiver, name, kInvalidIndex, : LookupIterator(isolate, receiver, name, kInvalidIndex, receiver,
GetRoot(isolate, receiver, kInvalidIndex), configuration) { configuration) {}
}
LookupIterator::LookupIterator(Isolate* isolate, Handle<Object> receiver, LookupIterator::LookupIterator(Isolate* isolate, Handle<Object> receiver,
Handle<Name> name, Handle<JSReceiver> holder, Handle<Name> name,
Handle<Object> lookup_start_object,
Configuration configuration) Configuration configuration)
: LookupIterator(isolate, receiver, name, kInvalidIndex, holder, : LookupIterator(isolate, receiver, name, kInvalidIndex,
configuration) {} lookup_start_object, configuration) {}
LookupIterator::LookupIterator(Isolate* isolate, Handle<Object> receiver, LookupIterator::LookupIterator(Isolate* isolate, Handle<Object> receiver,
size_t index, Configuration configuration) size_t index, Configuration configuration)
: LookupIterator(isolate, receiver, Handle<Name>(), index, : LookupIterator(isolate, receiver, Handle<Name>(), index, receiver,
GetRoot(isolate, receiver, index), configuration) { configuration) {
DCHECK_NE(index, kInvalidIndex); DCHECK_NE(index, kInvalidIndex);
} }
LookupIterator::LookupIterator(Isolate* isolate, Handle<Object> receiver, LookupIterator::LookupIterator(Isolate* isolate, Handle<Object> receiver,
size_t index, Handle<JSReceiver> holder, size_t index, Handle<Object> lookup_start_object,
Configuration configuration) Configuration configuration)
: LookupIterator(isolate, receiver, Handle<Name>(), index, holder, : LookupIterator(isolate, receiver, Handle<Name>(), index,
configuration) { lookup_start_object, configuration) {
DCHECK_NE(index, kInvalidIndex); DCHECK_NE(index, kInvalidIndex);
} }
LookupIterator::LookupIterator(Isolate* isolate, Handle<Object> receiver, LookupIterator::LookupIterator(Isolate* isolate, Handle<Object> receiver,
const Key& key, Configuration configuration) const Key& key, Configuration configuration)
: LookupIterator(isolate, receiver, key.name(), key.index(), : LookupIterator(isolate, receiver, key.name(), key.index(), receiver,
GetRoot(isolate, receiver, key.index()), configuration) {} configuration) {}
LookupIterator::LookupIterator(Isolate* isolate, Handle<Object> receiver, LookupIterator::LookupIterator(Isolate* isolate, Handle<Object> receiver,
const Key& key, Handle<JSReceiver> holder, const Key& key,
Handle<Object> lookup_start_object,
Configuration configuration) Configuration configuration)
: LookupIterator(isolate, receiver, key.name(), key.index(), holder, : LookupIterator(isolate, receiver, key.name(), key.index(),
configuration) {} lookup_start_object, configuration) {}
LookupIterator LookupIterator::LookupWithReceiver(Isolate* isolate,
Handle<Object> receiver,
const Key& key,
Handle<Object> holder,
Configuration configuration) {
return LookupIterator(isolate, receiver, key,
GetRoot(isolate, holder, key.index()), configuration);
}
// This private constructor is the central bottleneck that all the other // This private constructor is the central bottleneck that all the other
// constructors use. // constructors use.
LookupIterator::LookupIterator(Isolate* isolate, Handle<Object> receiver, LookupIterator::LookupIterator(Isolate* isolate, Handle<Object> receiver,
Handle<Name> name, size_t index, Handle<Name> name, size_t index,
Handle<JSReceiver> holder, Handle<Object> lookup_start_object,
Configuration configuration) Configuration configuration)
: configuration_(ComputeConfiguration(isolate, configuration, name)), : configuration_(ComputeConfiguration(isolate, configuration, name)),
isolate_(isolate), isolate_(isolate),
name_(name), name_(name),
receiver_(receiver), receiver_(receiver),
initial_holder_(holder), lookup_start_object_(lookup_start_object),
index_(index) { index_(index) {
if (IsElement()) { if (IsElement()) {
// If we're not looking at a TypedArray, we will need the key represented // If we're not looking at a TypedArray, we will need the key represented
// as an internalized string. // as an internalized string.
if (index_ > JSArray::kMaxArrayIndex && !holder->IsJSTypedArray()) { if (index_ > JSArray::kMaxArrayIndex &&
!lookup_start_object->IsJSTypedArray()) {
if (name_.is_null()) { if (name_.is_null()) {
name_ = isolate->factory()->SizeToString(index_); name_ = isolate->factory()->SizeToString(index_);
} }
...@@ -94,10 +87,10 @@ LookupIterator::LookupIterator(Isolate* isolate, Handle<Object> receiver, ...@@ -94,10 +87,10 @@ LookupIterator::LookupIterator(Isolate* isolate, Handle<Object> receiver,
name_ = isolate->factory()->InternalizeName(name_); name_ = isolate->factory()->InternalizeName(name_);
#ifdef DEBUG #ifdef DEBUG
// Assert that the name is not an index. // Assert that the name is not an index.
// If we're not looking at the prototype chain and the initial holder is // If we're not looking at the prototype chain and the lookup start object
// not a typed array, then this means "array index", otherwise we need to // 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. // 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; uint32_t index;
DCHECK(!name_->AsArrayIndex(&index)); DCHECK(!name_->AsArrayIndex(&index));
} else { } else {
...@@ -251,12 +244,12 @@ LookupIterator::Configuration LookupIterator::ComputeConfiguration( ...@@ -251,12 +244,12 @@ LookupIterator::Configuration LookupIterator::ComputeConfiguration(
// static // static
Handle<JSReceiver> LookupIterator::GetRoot(Isolate* isolate, Handle<JSReceiver> LookupIterator::GetRoot(Isolate* isolate,
Handle<Object> receiver, Handle<Object> lookup_start_object,
size_t index) { size_t index) {
if (receiver->IsJSReceiver(isolate)) { if (lookup_start_object->IsJSReceiver(isolate)) {
return Handle<JSReceiver>::cast(receiver); return Handle<JSReceiver>::cast(lookup_start_object);
} }
return GetRootForNonJSReceiver(isolate, receiver, index); return GetRootForNonJSReceiver(isolate, lookup_start_object, index);
} }
template <class T> template <class T>
......
...@@ -49,26 +49,30 @@ LookupIterator::LookupIterator(Isolate* isolate, Handle<Object> receiver, ...@@ -49,26 +49,30 @@ LookupIterator::LookupIterator(Isolate* isolate, Handle<Object> receiver,
name_(name), name_(name),
transition_(transition_map), transition_(transition_map),
receiver_(receiver), receiver_(receiver),
initial_holder_(GetRoot(isolate, receiver)), lookup_start_object_(receiver),
index_(kInvalidIndex) { index_(kInvalidIndex) {
holder_ = initial_holder_; holder_ = GetRoot(isolate, lookup_start_object_);
} }
template <bool is_element> template <bool is_element>
void LookupIterator::Start() { 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; DisallowHeapAllocation no_gc;
holder_ = initial_holder_;
JSReceiver holder = *holder_; has_property_ = false;
Map map = holder.map(isolate_); state_ = NOT_FOUND;
state_ = LookupInHolder<is_element>(map, holder); JSReceiver holder = *holder_;
if (IsFound()) return; Map map = holder.map(isolate_);
NextInternal<is_element>(map, holder); state_ = LookupInHolder<is_element>(map, holder);
if (IsFound()) return;
NextInternal<is_element>(map, holder);
}
} }
template void LookupIterator::Start<true>(); template void LookupIterator::Start<true>();
...@@ -127,22 +131,25 @@ template void LookupIterator::RestartInternal<false>(InterceptorState); ...@@ -127,22 +131,25 @@ template void LookupIterator::RestartInternal<false>(InterceptorState);
// static // static
Handle<JSReceiver> LookupIterator::GetRootForNonJSReceiver( 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 // Strings are the only objects with properties (only elements) directly on
// the wrapper. Hence we can skip generating the wrapper for all other cases. // the wrapper. Hence we can skip generating the wrapper for all other cases.
if (receiver->IsString(isolate) && if (lookup_start_object->IsString(isolate) &&
index < static_cast<size_t>(String::cast(*receiver).length())) { index <
static_cast<size_t>(String::cast(*lookup_start_object).length())) {
// TODO(verwaest): Speed this up. Perhaps use a cached wrapper on the native // TODO(verwaest): Speed this up. Perhaps use a cached wrapper on the native
// context, ensuring that we don't leak it into JS? // context, ensuring that we don't leak it into JS?
Handle<JSFunction> constructor = isolate->string_function(); Handle<JSFunction> constructor = isolate->string_function();
Handle<JSObject> result = isolate->factory()->NewJSObject(constructor); 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; return result;
} }
Handle<HeapObject> root( Handle<HeapObject> root(
receiver->GetPrototypeChainRootMap(isolate).prototype(isolate), isolate); lookup_start_object->GetPrototypeChainRootMap(isolate).prototype(isolate),
isolate);
if (root->IsNull(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); return Handle<JSReceiver>::cast(root);
} }
......
...@@ -70,27 +70,21 @@ class V8_EXPORT_PRIVATE LookupIterator final { ...@@ -70,27 +70,21 @@ class V8_EXPORT_PRIVATE LookupIterator final {
Handle<Name> name, Handle<Name> name,
Configuration configuration = DEFAULT); Configuration configuration = DEFAULT);
inline LookupIterator(Isolate* isolate, Handle<Object> receiver, inline LookupIterator(Isolate* isolate, Handle<Object> receiver,
Handle<Name> name, Handle<JSReceiver> holder, Handle<Name> name, Handle<Object> lookup_start_object,
Configuration configuration = DEFAULT); Configuration configuration = DEFAULT);
inline LookupIterator(Isolate* isolate, Handle<Object> receiver, size_t index, inline LookupIterator(Isolate* isolate, Handle<Object> receiver, size_t index,
Configuration configuration = DEFAULT); Configuration configuration = DEFAULT);
inline LookupIterator(Isolate* isolate, Handle<Object> receiver, size_t index, inline LookupIterator(Isolate* isolate, Handle<Object> receiver, size_t index,
Handle<JSReceiver> holder, Handle<Object> lookup_start_object,
Configuration configuration = DEFAULT); Configuration configuration = DEFAULT);
inline LookupIterator(Isolate* isolate, Handle<Object> receiver, inline LookupIterator(Isolate* isolate, Handle<Object> receiver,
const Key& key, Configuration configuration = DEFAULT); const Key& key, Configuration configuration = DEFAULT);
inline LookupIterator(Isolate* isolate, Handle<Object> receiver, inline LookupIterator(Isolate* isolate, Handle<Object> receiver,
const Key& key, Handle<JSReceiver> holder, const Key& key, Handle<Object> lookup_start_object,
Configuration configuration = DEFAULT); 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() { void Restart() {
InterceptorState state = InterceptorState::kUninitialized; InterceptorState state = InterceptorState::kUninitialized;
IsElement() ? RestartInternal<true>(state) : RestartInternal<false>(state); IsElement() ? RestartInternal<true>(state) : RestartInternal<false>(state);
...@@ -134,6 +128,8 @@ class V8_EXPORT_PRIVATE LookupIterator final { ...@@ -134,6 +128,8 @@ class V8_EXPORT_PRIVATE LookupIterator final {
template <class T> template <class T>
inline Handle<T> GetHolder() const; inline Handle<T> GetHolder() const;
Handle<Object> lookup_start_object() const { return lookup_start_object_; }
bool HolderIsReceiver() const; bool HolderIsReceiver() const;
bool HolderIsReceiverOrHiddenPrototype() const; bool HolderIsReceiverOrHiddenPrototype() const;
...@@ -202,7 +198,8 @@ class V8_EXPORT_PRIVATE LookupIterator final { ...@@ -202,7 +198,8 @@ class V8_EXPORT_PRIVATE LookupIterator final {
inline LookupIterator(Isolate* isolate, Handle<Object> receiver, inline LookupIterator(Isolate* isolate, Handle<Object> receiver,
Handle<Name> name, size_t index, Handle<Name> name, size_t index,
Handle<JSReceiver> holder, Configuration configuration); Handle<Object> lookup_start_object,
Configuration configuration);
// For |ForTransitionHandler|. // For |ForTransitionHandler|.
LookupIterator(Isolate* isolate, Handle<Object> receiver, Handle<Name> name, LookupIterator(Isolate* isolate, Handle<Object> receiver, Handle<Name> name,
...@@ -267,9 +264,10 @@ class V8_EXPORT_PRIVATE LookupIterator final { ...@@ -267,9 +264,10 @@ class V8_EXPORT_PRIVATE LookupIterator final {
Handle<Name> name); Handle<Name> name);
static Handle<JSReceiver> GetRootForNonJSReceiver( 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, static inline Handle<JSReceiver> GetRoot(Isolate* isolate,
Handle<Object> receiver, Handle<Object> lookup_start_object,
size_t index = kInvalidIndex); size_t index = kInvalidIndex);
State NotFound(JSReceiver const holder) const; State NotFound(JSReceiver const holder) const;
...@@ -286,7 +284,7 @@ class V8_EXPORT_PRIVATE LookupIterator final { ...@@ -286,7 +284,7 @@ class V8_EXPORT_PRIVATE LookupIterator final {
Handle<Object> transition_; Handle<Object> transition_;
const Handle<Object> receiver_; const Handle<Object> receiver_;
Handle<JSReceiver> holder_; Handle<JSReceiver> holder_;
const Handle<JSReceiver> initial_holder_; const Handle<Object> lookup_start_object_;
const size_t index_; const size_t index_;
InternalIndex number_ = InternalIndex::NotFound(); InternalIndex number_ = InternalIndex::NotFound();
}; };
......
...@@ -39,8 +39,7 @@ MaybeHandle<Object> Runtime::GetObjectProperty(Isolate* isolate, ...@@ -39,8 +39,7 @@ MaybeHandle<Object> Runtime::GetObjectProperty(Isolate* isolate,
bool success = false; bool success = false;
LookupIterator::Key lookup_key(isolate, key, &success); LookupIterator::Key lookup_key(isolate, key, &success);
if (!success) return MaybeHandle<Object>(); if (!success) return MaybeHandle<Object>();
LookupIterator it = LookupIterator it = LookupIterator(isolate, receiver, lookup_key, holder);
LookupIterator::LookupWithReceiver(isolate, receiver, lookup_key, holder);
MaybeHandle<Object> result = Object::GetProperty(&it); MaybeHandle<Object> result = Object::GetProperty(&it);
if (is_found) *is_found = it.IsFound(); if (is_found) *is_found = it.IsFound();
......
...@@ -4,6 +4,12 @@ ...@@ -4,6 +4,12 @@
// Flags: --allow-natives-syntax --super-ic // Flags: --allow-natives-syntax --super-ic
function forceDictionaryMode(obj) {
for (let i = 0; i < 2000; ++i) {
obj["prop" + i] = "prop_value";
}
}
(function TestHomeObjectPrototypeNull() { (function TestHomeObjectPrototypeNull() {
class A {} class A {}
...@@ -303,10 +309,7 @@ ...@@ -303,10 +309,7 @@
// Create a "home object proto" object which is a bound function. // Create a "home object proto" object which is a bound function.
let home_object_proto = (function() {}).bind({}); let home_object_proto = (function() {}).bind({});
// Force home_object_proto to dictionary mode. forceDictionaryMode(home_object_proto);
for (let i = 0; i < 2000; ++i) {
home_object_proto["prop" + i] = "prop_value";
}
B.prototype.__proto__ = home_object_proto; B.prototype.__proto__ = home_object_proto;
assertEquals(0, home_object_proto.length); assertEquals(0, home_object_proto.length);
...@@ -342,3 +345,112 @@ ...@@ -342,3 +345,112 @@
assertEquals(A.prototype.foo, (new B).m()); assertEquals(A.prototype.foo, (new B).m());
assertEquals(A.prototype.foo, (new B).n()()); 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);
})();
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