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