#include "src/objects/lookup.h"
#include <optional>
#include "src/common/globals.h"
#include "src/deoptimizer/deoptimizer.h"
#include "src/execution/isolate-inl.h"
#include "src/execution/isolate.h"
#include "src/execution/protectors-inl.h"
#include "src/init/bootstrapper.h"
#include "src/logging/counters.h"
#include "src/objects/arguments-inl.h"
#include "src/objects/elements.h"
#include "src/objects/field-type.h"
#include "src/objects/hash-table-inl.h"
#include "src/objects/heap-number-inl.h"
#include "src/objects/js-shared-array-inl.h"
#include "src/objects/js-struct-inl.h"
#include "src/objects/map-updater.h"
#include "src/objects/ordered-hash-table.h"
#include "src/objects/property-details.h"
#include "src/objects/struct-inl.h"
namespace v8::internal {
template <bool is_element>
void LookupIterator::Start() {
DisallowGarbageCollection no_gc;
if (V8_LIKELY(IsJSReceiver(*lookup_start_object_, isolate_))) {
holder_ = Cast<JSReceiver>(lookup_start_object_);
} else {
if (IsString(*lookup_start_object_, isolate_) &&
((!is_element && *name_ == ReadOnlyRoots(isolate_).length_string()) ||
(is_element &&
index_ < static_cast<size_t>(
Cast<String>(*lookup_start_object_)->length())))) {
has_property_ = true;
state_ = STRING_LOOKUP_START_OBJECT;
return;
}
if (!check_prototype_chain()) {
DCHECK(!IsJSReceiver(*lookup_start_object_));
has_property_ = false;
state_ = NOT_FOUND;
return;
}
holder_ = direct_handle(GetRootForNonJSReceiver(), isolate_);
}
has_property_ = false;
state_ = NOT_FOUND;
Tagged<JSReceiver> holder = *holder_;
Tagged<Map> map = holder->map(isolate_);
state_ = LookupInHolder<is_element>(map, holder);
if (IsFound()) return;
NextInternal<is_element>(map, holder);
}
template void LookupIterator::Start<true>();
template void LookupIterator::Start<false>();
void LookupIterator::Next() {
DCHECK_NE(JSPROXY, state_);
DCHECK_NE(TRANSITION, state_);
DCHECK_NE(NOT_FOUND, state_);
DisallowGarbageCollection no_gc;
has_property_ = false;
Tagged<JSReceiver> holder = *holder_;
Tagged<Map> map = holder->map(isolate_);
if (IsSpecialReceiverMap(map)) {
state_ = IsElement() ? LookupInSpecialHolder<true>(map, holder)
: LookupInSpecialHolder<false>(map, holder);
if (IsFound()) return;
}
IsElement() ? NextInternal<true>(map, holder)
: NextInternal<false>(map, holder);
}
template <bool is_element>
void LookupIterator::NextInternal(Tagged<Map> map, Tagged<JSReceiver> holder) {
do {
Tagged<JSReceiver> maybe_holder = NextHolder(map);
if (maybe_holder.is_null()) {
if (interceptor_state_ == InterceptorState::kSkipNonMasking) {
RestartLookupForNonMaskingInterceptors<is_element>();
return;
}
state_ = NOT_FOUND;
if (holder != *holder_) holder_ = direct_handle(holder, isolate_);
return;
}
holder = maybe_holder;
map = holder->map(isolate_);
state_ = LookupInHolder<is_element>(map, holder);
} while (!IsFound());
holder_ = direct_handle(holder, isolate_);
}
template <bool is_element>
void LookupIterator::RestartInternal(InterceptorState interceptor_state) {
interceptor_state_ = interceptor_state;
property_details_ = PropertyDetails::Empty();
number_ = InternalIndex::NotFound();
Start<is_element>();
}
template void LookupIterator::RestartInternal<true>(InterceptorState);
template void LookupIterator::RestartInternal<false>(InterceptorState);
void LookupIterator::RecheckTypedArrayBounds() {
DCHECK(IsJSTypedArray(*holder_, isolate_));
DCHECK_EQ(state_, TYPED_ARRAY_INDEX_NOT_FOUND);
if (!IsElement(*holder_)) {
return;
}
Tagged<JSObject> js_object = Cast<JSObject>(*holder_);
ElementsAccessor* accessor = js_object->GetElementsAccessor(isolate_);
Tagged<FixedArrayBase> backing_store = js_object->elements(isolate_);
number_ =
accessor->GetEntryForIndex(isolate_, js_object, backing_store, index_);
if (number_.is_not_found()) {
return;
}
property_details_ = accessor->GetDetails(js_object, number_);
#ifdef DEBUG
Tagged<Map> map = holder_->map(isolate_);
DCHECK(!map->has_frozen_elements());
DCHECK(!map->has_sealed_elements());
#endif
has_property_ = true;
DCHECK_EQ(property_details_.kind(), v8::internal::PropertyKind::kData);
state_ = DATA;
}
Tagged<JSReceiver> LookupIterator::GetRootForNonJSReceiver() const {
DCHECK(check_prototype_chain());
Tagged<HeapObject> root =
Object::GetPrototypeChainRootMap(*lookup_start_object_, isolate_)
->prototype(isolate_);
if (IsNull(root, isolate_)) {
isolate_->PushStackTraceAndDie(
reinterpret_cast<void*>((*lookup_start_object_).ptr()));
}
return Cast<JSReceiver>(root);
}
DirectHandle<Map> LookupIterator::GetReceiverMap() const {
if (IsNumber(*receiver_, isolate_)) return factory()->heap_number_map();
return direct_handle(Cast<HeapObject>(receiver_)->map(isolate_), isolate_);
}
bool LookupIterator::HasAccess() const {
DCHECK(state_ == ACCESS_CHECK || state_ == TRANSITION);
return isolate_->MayAccess(isolate_->native_context(), GetHolder<JSObject>());
}
template <bool is_element>
void LookupIterator::ReloadPropertyInformation() {
state_ = BEFORE_PROPERTY;
interceptor_state_ = InterceptorState::kUninitialized;
state_ = LookupInHolder<is_element>(holder_->map(isolate_), *holder_);
DCHECK(IsFound() || !holder_->HasFastProperties(isolate_));
}
void LookupIterator::InternalUpdateProtector(
Isolate* isolate, DirectHandle<JSAny> receiver_generic,
DirectHandle<Name> name, MaybeDirectHandle<Object> value,
MaybeDirectHandle<Object> old_value) {
if (isolate->bootstrapper()->IsActive()) return;
if (!IsJSObject(*receiver_generic)) return;
auto receiver = Cast<JSObject>(receiver_generic);
ReadOnlyRoots roots(isolate);
if (*name == roots.constructor_string()) {
if (IsJSArray(*receiver, isolate)) {
if (!Protectors::IsArraySpeciesLookupChainIntact(isolate)) return;
isolate->CountUsage(
v8::Isolate::UseCounterFeature::kArrayInstanceConstructorModified);
Protectors::InvalidateArraySpeciesLookupChain(isolate);
return;
} else if (IsJSPromise(*receiver, isolate)) {
if (!Protectors::IsPromiseSpeciesLookupChainIntact(isolate)) return;
Protectors::InvalidatePromiseSpeciesLookupChain(isolate);
return;
} else if (IsJSRegExp(*receiver, isolate)) {
if (!Protectors::IsRegExpSpeciesLookupChainIntact(isolate)) return;
Protectors::InvalidateRegExpSpeciesLookupChain(isolate);
return;
} else if (IsJSTypedArray(*receiver, isolate)) {
if (!Protectors::IsTypedArraySpeciesLookupChainIntact(isolate)) return;
Protectors::InvalidateTypedArraySpeciesLookupChain(isolate);
return;
}
if (receiver->map(isolate)->is_prototype_map()) {
DisallowGarbageCollection no_gc;
if (isolate->IsInCreationContext(
Cast<JSObject>(*receiver),
Context::INITIAL_ARRAY_PROTOTYPE_INDEX)) {
if (!Protectors::IsArraySpeciesLookupChainIntact(isolate)) return;
isolate->CountUsage(
v8::Isolate::UseCounterFeature::kArrayPrototypeConstructorModified);
Protectors::InvalidateArraySpeciesLookupChain(isolate);
} else if (IsJSPromisePrototype(*receiver)) {
if (!Protectors::IsPromiseSpeciesLookupChainIntact(isolate)) return;
Protectors::InvalidatePromiseSpeciesLookupChain(isolate);
} else if (IsJSRegExpPrototype(*receiver)) {
if (!Protectors::IsRegExpSpeciesLookupChainIntact(isolate)) return;
Protectors::InvalidateRegExpSpeciesLookupChain(isolate);
} else if (IsJSTypedArrayPrototype(*receiver)) {
if (!Protectors::IsTypedArraySpeciesLookupChainIntact(isolate)) return;
Protectors::InvalidateTypedArraySpeciesLookupChain(isolate);
}
}
} else if (*name == roots.next_string()) {
if (IsJSArrayIterator(*receiver) || IsJSArrayIteratorPrototype(*receiver)) {
if (!Protectors::IsArrayIteratorLookupChainIntact(isolate)) return;
Protectors::InvalidateArrayIteratorLookupChain(isolate);
} else if (IsJSMapIterator(*receiver) ||
IsJSMapIteratorPrototype(*receiver)) {
if (!Protectors::IsMapIteratorLookupChainIntact(isolate)) return;
Protectors::InvalidateMapIteratorLookupChain(isolate);
} else if (IsJSSetIterator(*receiver) ||
IsJSSetIteratorPrototype(*receiver)) {
if (!Protectors::IsSetIteratorLookupChainIntact(isolate)) return;
Protectors::InvalidateSetIteratorLookupChain(isolate);
} else if (IsJSStringIterator(*receiver) ||
IsJSStringIteratorPrototype(*receiver)) {
if (!Protectors::IsStringIteratorLookupChainIntact(isolate)) return;
Protectors::InvalidateStringIteratorLookupChain(isolate);
}
} else if (*name == roots.species_symbol()) {
if (IsJSArrayConstructor(*receiver)) {
if (!Protectors::IsArraySpeciesLookupChainIntact(isolate)) return;
isolate->CountUsage(
v8::Isolate::UseCounterFeature::kArraySpeciesModified);
Protectors::InvalidateArraySpeciesLookupChain(isolate);
} else if (IsJSPromiseConstructor(*receiver)) {
if (!Protectors::IsPromiseSpeciesLookupChainIntact(isolate)) return;
Protectors::InvalidatePromiseSpeciesLookupChain(isolate);
} else if (IsJSRegExpConstructor(*receiver)) {
if (!Protectors::IsRegExpSpeciesLookupChainIntact(isolate)) return;
Protectors::InvalidateRegExpSpeciesLookupChain(isolate);
} else if (IsTypedArrayConstructor(*receiver)) {
if (!Protectors::IsTypedArraySpeciesLookupChainIntact(isolate)) return;
Protectors::InvalidateTypedArraySpeciesLookupChain(isolate);
}
} else if (*name == roots.is_concat_spreadable_symbol()) {
if (!Protectors::IsIsConcatSpreadableLookupChainIntact(isolate)) return;
Protectors::InvalidateIsConcatSpreadableLookupChain(isolate);
} else if (*name == roots.iterator_symbol()) {
if (IsJSArray(*receiver, isolate)) {
if (!Protectors::IsArrayIteratorLookupChainIntact(isolate)) return;
DirectHandle<Object> the_old_value, the_value;
if (old_value.To(&the_old_value) && value.To(&the_value) &&
the_old_value.is_identical_to(the_value)) {
return;
}
Protectors::InvalidateArrayIteratorLookupChain(isolate);
} else if (IsJSSet(*receiver, isolate) || IsJSSetIterator(*receiver) ||
IsJSSetIteratorPrototype(*receiver) ||
IsJSSetPrototype(*receiver)) {
if (Protectors::IsSetIteratorLookupChainIntact(isolate)) {
Protectors::InvalidateSetIteratorLookupChain(isolate);
}
} else if (IsJSMapIterator(*receiver) ||
IsJSMapIteratorPrototype(*receiver)) {
if (Protectors::IsMapIteratorLookupChainIntact(isolate)) {
Protectors::InvalidateMapIteratorLookupChain(isolate);
}
} else if (IsJSIteratorPrototype(*receiver)) {
if (Protectors::IsMapIteratorLookupChainIntact(isolate)) {
Protectors::InvalidateMapIteratorLookupChain(isolate);
}
if (Protectors::IsSetIteratorLookupChainIntact(isolate)) {
Protectors::InvalidateSetIteratorLookupChain(isolate);
}
} else if (isolate->IsInCreationContext(
*receiver, Context::INITIAL_STRING_PROTOTYPE_INDEX)) {
if (!Protectors::IsStringIteratorLookupChainIntact(isolate)) return;
Protectors::InvalidateStringIteratorLookupChain(isolate);
}
} else if (*name == roots.resolve_string()) {
if (!Protectors::IsPromiseResolveLookupChainIntact(isolate)) return;
if (IsJSPromiseConstructor(*receiver)) {
Protectors::InvalidatePromiseResolveLookupChain(isolate);
}
} else if (*name == roots.then_string()) {
if (!Protectors::IsPromiseThenLookupChainIntact(isolate)) return;
if (IsJSPromise(*receiver, isolate) || IsJSObjectPrototype(*receiver) ||
IsJSPromisePrototype(*receiver)) {
Protectors::InvalidatePromiseThenLookupChain(isolate);
}
} else if (*name == roots.match_all_symbol() ||
*name == roots.replace_symbol() || *name == roots.split_symbol()) {
if (!Protectors::IsNumberStringNotRegexpLikeIntact(isolate)) return;
if (receiver->map()->is_prototype_map() &&
(IsJSPrimitiveWrapper(*receiver) || IsJSObjectPrototype(*receiver))) {
Protectors::InvalidateNumberStringNotRegexpLike(isolate);
}
} else if (*name == roots.to_primitive_symbol()) {
if (!Protectors::IsStringWrapperToPrimitiveIntact(isolate)) return;
if (isolate->IsInCreationContext(*receiver,
Context::INITIAL_STRING_PROTOTYPE_INDEX) ||
isolate->IsInCreationContext(*receiver,
Context::INITIAL_OBJECT_PROTOTYPE_INDEX) ||
IsStringWrapper(*receiver)) {
Protectors::InvalidateStringWrapperToPrimitive(isolate);
}
} else if (*name == roots.valueOf_string()) {
if (!Protectors::IsStringWrapperToPrimitiveIntact(isolate)) return;
if (isolate->IsInCreationContext(*receiver,
Context::INITIAL_STRING_PROTOTYPE_INDEX) ||
IsStringWrapper(*receiver)) {
Protectors::InvalidateStringWrapperToPrimitive(isolate);
}
}
}
void LookupIterator::PrepareForDataProperty(DirectHandle<Object> value) {
DCHECK(state_ == DATA || state_ == ACCESSOR);
DCHECK(HolderIsReceiverOrHiddenPrototype());
DCHECK(!IsWasmObject(*receiver_, isolate_));
DirectHandle<JSReceiver> holder = GetHolder<JSReceiver>();
DCHECK_IMPLIES(IsJSProxy(*holder, isolate_), name()->IsPrivate());
if (IsJSProxy(*holder, isolate_)) return;
if (IsElement(*holder)) {
DirectHandle<JSObject> holder_obj = Cast<JSObject>(holder);
ElementsKind kind = holder_obj->GetElementsKind(isolate_);
ElementsKind to = Object::OptimalElementsKind(*value, isolate_);
if (IsHoleyElementsKind(kind)) to = GetHoleyElementsKind(to);
to = GetMoreGeneralElementsKind(kind, to);
if (kind != to) {
JSObject::TransitionElementsKind(isolate(), holder_obj, to);
}
if (IsSmiOrObjectElementsKind(to) || IsSealedElementsKind(to) ||
IsNonextensibleElementsKind(to)) {
JSObject::EnsureWritableFastElements(isolate(), holder_obj);
}
return;
}
if (IsJSGlobalObject(*holder, isolate_)) {
DirectHandle<GlobalDictionary> dictionary(
Cast<JSGlobalObject>(*holder)->global_dictionary(isolate_,
kAcquireLoad),
isolate());
DirectHandle<PropertyCell> cell(
dictionary->CellAt(isolate_, dictionary_entry()), isolate());
property_details_ = cell->property_details();
PropertyCell::PrepareForAndSetValue(
isolate(), dictionary, dictionary_entry(), value, property_details_);
return;
}
PropertyConstness new_constness = PropertyConstness::kConst;
if (constness() == PropertyConstness::kConst) {
DCHECK_EQ(PropertyKind::kData, property_details_.kind());
if (holder->HasFastProperties(isolate_)) {
if (!CanStayConst(*value)) new_constness = PropertyConstness::kMutable;
} else if (V8_DICT_PROPERTY_CONST_TRACKING_BOOL) {
if (!DictCanStayConst(*value)) {
property_details_ =
property_details_.CopyWithConstness(PropertyConstness::kMutable);
if constexpr (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
Tagged<SwissNameDictionary> dict =
holder->property_dictionary_swiss();
dict->DetailsAtPut(dictionary_entry(), property_details_);
} else {
Tagged<NameDictionary> dict = holder->property_dictionary();
dict->DetailsAtPut(dictionary_entry(), property_details_);
}
Tagged<Map> old_map = holder->map(isolate_);
if (old_map->is_prototype_map()) {
JSObject::InvalidatePrototypeChains(old_map);
}
}
return;
}
}
if (!holder->HasFastProperties(isolate_)) return;
auto holder_obj = Cast<JSObject>(holder);
Handle<Map> old_map(holder->map(isolate_), isolate_);
DirectHandle<Map> new_map = Map::Update(isolate_, old_map);
if (!new_map->is_dictionary_map()) {
new_map = Map::PrepareForDataProperty(
isolate(), new_map, descriptor_number(), new_constness, value);
if (old_map.is_identical_to(new_map)) {
if (constness() != new_constness || representation().IsNone()) {
property_details_ = new_map->instance_descriptors(isolate_)->GetDetails(
descriptor_number());
}
return;
}
}
DCHECK_NE(*old_map, *new_map);
JSObject::MigrateToMap(isolate_, holder_obj, new_map);
ReloadPropertyInformation<false>();
DCHECK(!old_map->is_dictionary_map());
if (V8_DICT_PROPERTY_CONST_TRACKING_BOOL && new_map->is_dictionary_map() &&
new_constness == PropertyConstness::kMutable) {
property_details_ =
property_details_.CopyWithConstness(PropertyConstness::kMutable);
if constexpr (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
Tagged<SwissNameDictionary> dict =
holder_obj->property_dictionary_swiss();
dict->DetailsAtPut(dictionary_entry(), property_details_);
} else {
Tagged<NameDictionary> dict = holder_obj->property_dictionary();
dict->DetailsAtPut(dictionary_entry(), property_details_);
}
DCHECK_IMPLIES(new_map->is_prototype_map(),
!new_map->IsPrototypeValidityCellValid());
}
}
void LookupIterator::ReconfigureDataProperty(DirectHandle<Object> value,
PropertyAttributes attributes) {
DCHECK(state_ == DATA || state_ == ACCESSOR);
DCHECK(HolderIsReceiverOrHiddenPrototype());
DirectHandle<JSReceiver> holder = GetHolder<JSReceiver>();
if (V8_UNLIKELY(IsWasmObject(*holder))) UNREACHABLE();
if (IsJSProxy(*holder, isolate_)) {
DCHECK(name()->IsPrivate());
return;
}
DirectHandle<JSObject> holder_obj = Cast<JSObject>(holder);
if (IsElement(*holder)) {
DCHECK(!holder_obj->HasTypedArrayOrRabGsabTypedArrayElements(isolate_));
DCHECK(attributes != NONE || !holder_obj->HasFastElements(isolate_));
DirectHandle<FixedArrayBase> elements(holder_obj->elements(isolate_),
isolate());
holder_obj->GetElementsAccessor(isolate_)->Reconfigure(
isolate_, holder_obj, elements, number_, value, attributes);
ReloadPropertyInformation<true>();
} else if (holder_obj->HasFastProperties(isolate_)) {
DirectHandle<Map> old_map(holder_obj->map(isolate_), isolate_);
DirectHandle<Map> new_map = MapUpdater::ReconfigureExistingProperty(
isolate_, old_map, descriptor_number(), i::PropertyKind::kData,
attributes, PropertyConstness::kMutable);
if (!new_map->is_dictionary_map()) {
new_map =
Map::PrepareForDataProperty(isolate(), new_map, descriptor_number(),
PropertyConstness::kMutable, value);
}
JSObject::MigrateToMap(isolate_, holder_obj, new_map);
ReloadPropertyInformation<false>();
}
if (!IsElement(*holder) && !holder_obj->HasFastProperties(isolate_)) {
if (holder_obj->map(isolate_)->is_prototype_map() &&
(((property_details_.attributes() & READ_ONLY) == 0 &&
(attributes & READ_ONLY) != 0) ||
(property_details_.attributes() & DONT_ENUM) !=
(attributes & DONT_ENUM))) {
JSObject::InvalidatePrototypeChains(holder->map(isolate_));
}
if (IsJSGlobalObject(*holder_obj, isolate_)) {
PropertyDetails details(PropertyKind::kData, attributes,
PropertyCellType::kMutable);
DirectHandle<GlobalDictionary> dictionary(
Cast<JSGlobalObject>(*holder_obj)
->global_dictionary(isolate_, kAcquireLoad),
isolate());
DirectHandle<PropertyCell> cell = PropertyCell::PrepareForAndSetValue(
isolate(), dictionary, dictionary_entry(), value, details);
property_details_ = cell->property_details();
DCHECK_EQ(cell->value(), *value);
} else {
PropertyDetails details(PropertyKind::kData, attributes,
PropertyConstness::kMutable);
if constexpr (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
DirectHandle<SwissNameDictionary> dictionary(
holder_obj->property_dictionary_swiss(isolate_), isolate());
dictionary->ValueAtPut(dictionary_entry(), *value);
dictionary->DetailsAtPut(dictionary_entry(), details);
DCHECK_EQ(details.AsSmi(),
dictionary->DetailsAt(dictionary_entry()).AsSmi());
property_details_ = details;
} else {
DirectHandle<NameDictionary> dictionary(
holder_obj->property_dictionary(isolate_), isolate());
PropertyDetails original_details =
dictionary->DetailsAt(dictionary_entry());
int enumeration_index = original_details.dictionary_index();
DCHECK_GT(enumeration_index, 0);
details = details.set_index(enumeration_index);
dictionary->SetEntry(dictionary_entry(), *name(), *value, details);
property_details_ = details;
}
}
state_ = DATA;
}
WriteDataValue(value, true);
#if VERIFY_HEAP
if (v8_flags.verify_heap) {
holder->HeapObjectVerify(isolate());
}
#endif
}
void LookupIterator::PrepareTransitionToDataProperty(
DirectHandle<JSReceiver> receiver, DirectHandle<Object> value,
PropertyAttributes attributes, StoreOrigin store_origin) {
DCHECK_IMPLIES(IsJSProxy(*receiver, isolate_), name()->IsPrivate());
DCHECK_IMPLIES(!receiver.is_identical_to(GetStoreTarget<JSReceiver>()),
name()->IsPrivateName());
DCHECK(!IsAlwaysSharedSpaceJSObject(*receiver));
if (state_ == TRANSITION) return;
if (!IsElement() && name()->IsPrivate()) {
attributes = static_cast<PropertyAttributes>(attributes | DONT_ENUM);
}
DCHECK(state_ != LookupIterator::ACCESSOR ||
IsAccessorInfo(*GetAccessors(), isolate_));
DCHECK_NE(TYPED_ARRAY_INDEX_NOT_FOUND, state_);
DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype());
DirectHandle<Map> map(receiver->map(isolate_), isolate_);
map = Map::Update(isolate(), map);
if (map->is_dictionary_map()) {
state_ = TRANSITION;
if (IsJSGlobalObjectMap(*map)) {
DCHECK(!IsTheHole(*value, isolate_));
property_details_ =
PropertyDetails(PropertyKind::kData, attributes,
PropertyCell::InitialType(isolate_, *value));
transition_ = isolate_->factory()->NewPropertyCell(
name(), property_details_, value);
has_property_ = true;
} else {
property_details_ =
PropertyDetails(PropertyKind::kData, attributes,
PropertyDetails::kConstIfDictConstnessTracking);
transition_ = map;
}
return;
}
DirectHandle<Map> transition =
Map::TransitionToDataProperty(isolate_, map, name_, value, attributes,
PropertyConstness::kConst, store_origin);
state_ = TRANSITION;
transition_ = transition;
if (transition->is_dictionary_map()) {
DCHECK(!IsJSGlobalObjectMap(*transition));
property_details_ =
PropertyDetails(PropertyKind::kData, attributes,
PropertyDetails::kConstIfDictConstnessTracking);
} else {
property_details_ = transition->GetLastDescriptorDetails(isolate_);
has_property_ = true;
}
}
Maybe<bool> LookupIterator::ApplyTransitionToDataProperty(
DirectHandle<JSReceiver> receiver, Maybe<ShouldThrow> should_throw) {
DCHECK_EQ(TRANSITION, state_);
DCHECK_IMPLIES(!receiver.is_identical_to(GetStoreTarget<JSReceiver>()),
name()->IsPrivateName());
holder_ = receiver;
if (IsJSGlobalObject(*receiver, isolate_)) {
JSObject::InvalidatePrototypeChains(receiver->map(isolate_));
auto global = Cast<JSGlobalObject>(receiver);
DCHECK(!global->HasFastProperties());
DirectHandle<GlobalDictionary> dictionary(
global->global_dictionary(isolate_, kAcquireLoad), isolate_);
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate_, dictionary,
GlobalDictionary::Add(isolate_, dictionary, name(), transition_cell(),
property_details_, &number_),
Nothing<bool>());
global->set_global_dictionary(*dictionary, kReleaseStore);
property_details_ = transition_cell()->property_details();
has_property_ = true;
state_ = DATA;
return Just(true);
}
DirectHandle<Map> transition = transition_map();
bool simple_transition =
transition->GetBackPointer(isolate_) == receiver->map(isolate_);
if (configuration_ == DEFAULT && !transition->is_dictionary_map() &&
!transition->is_prototype_map() &&
!transition->IsPrototypeValidityCellValid()) {
DirectHandle<UnionOf<Smi, Cell>> validity_cell =
Map::GetOrCreatePrototypeChainValidityCell(transition, isolate());
transition->set_prototype_validity_cell(*validity_cell, kRelaxedStore);
}
if (!IsJSProxy(*receiver, isolate_)) {
JSObject::MigrateToMap(isolate_, Cast<JSObject>(receiver), transition);
}
if (simple_transition) {
number_ = transition->LastAdded();
property_details_ = transition->GetLastDescriptorDetails(isolate_);
state_ = DATA;
} else if (receiver->map(isolate_)->is_dictionary_map()) {
if (receiver->map(isolate_)->is_prototype_map() &&
IsJSObject(*receiver, isolate_)) {
JSObject::InvalidatePrototypeChains(receiver->map(isolate_));
}
if constexpr (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
DirectHandle<SwissNameDictionary> dictionary(
receiver->property_dictionary_swiss(isolate_), isolate_);
dictionary =
SwissNameDictionary::Add(isolate(), dictionary, name(),
isolate_->factory()->uninitialized_value(),
property_details_, &number_);
receiver->SetProperties(*dictionary);
} else {
DirectHandle<NameDictionary> dictionary(
receiver->property_dictionary(isolate_), isolate_);
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate_, dictionary,
NameDictionary::Add(isolate(), dictionary, name(),
isolate_->factory()->uninitialized_value(),
property_details_, &number_),
Nothing<bool>());
receiver->SetProperties(*dictionary);
if (name()->IsInteresting(isolate())) {
dictionary->set_may_have_interesting_properties(true);
}
property_details_ = dictionary->DetailsAt(number_);
}
has_property_ = true;
state_ = DATA;
} else {
ReloadPropertyInformation<false>();
}
return Just(true);
}
void LookupIterator::Delete() {
DirectHandle<JSReceiver> holder = Cast<JSReceiver>(holder_);
if (IsElement(*holder)) {
DirectHandle<JSObject> object = Cast<JSObject>(holder);
ElementsAccessor* accessor = object->GetElementsAccessor(isolate_);
accessor->Delete(isolate_, object, number_);
} else {
DCHECK(!name()->IsPrivateName());
bool is_prototype_map = holder->map(isolate_)->is_prototype_map();
RCS_SCOPE(isolate_,
is_prototype_map
? RuntimeCallCounterId::kPrototypeObject_DeleteProperty
: RuntimeCallCounterId::kObject_DeleteProperty);
PropertyNormalizationMode mode =
is_prototype_map ? KEEP_INOBJECT_PROPERTIES : CLEAR_INOBJECT_PROPERTIES;
if (holder->HasFastProperties(isolate_)) {
JSObject::NormalizeProperties(isolate_, Cast<JSObject>(holder), mode, 0,
"DeletingProperty");
ReloadPropertyInformation<false>();
}
JSReceiver::DeleteNormalizedProperty(holder, dictionary_entry());
if (IsJSObject(*holder, isolate_)) {
JSObject::ReoptimizeIfPrototype(Cast<JSObject>(holder));
}
}
state_ = NOT_FOUND;
}
void LookupIterator::TransitionToAccessorProperty(
DirectHandle<Object> getter, DirectHandle<Object> setter,
PropertyAttributes attributes) {
DCHECK(!IsNull(*getter, isolate_) || !IsNull(*setter, isolate_));
DirectHandle<JSObject> receiver = GetStoreTarget<JSObject>();
if (!IsElement() && name()->IsPrivate()) {
attributes = static_cast<PropertyAttributes>(attributes | DONT_ENUM);
}
if (!IsElement(*receiver) && !receiver->map(isolate_)->is_dictionary_map()) {
DirectHandle<Map> old_map(receiver->map(isolate_), isolate_);
if (!holder_.is_identical_to(receiver)) {
holder_ = receiver;
state_ = NOT_FOUND;
} else if (state_ == INTERCEPTOR) {
LookupInRegularHolder<false>(*old_map, *holder_);
}
DCHECK_IMPLIES(!IsFound(), number_.is_not_found());
DirectHandle<Map> new_map = Map::TransitionToAccessorProperty(
isolate_, old_map, name_, number_, getter, setter, attributes);
bool simple_transition =
new_map->GetBackPointer(isolate_) == receiver->map(isolate_);
JSObject::MigrateToMap(isolate_, receiver, new_map);
if (simple_transition) {
number_ = new_map->LastAdded();
property_details_ = new_map->GetLastDescriptorDetails(isolate_);
state_ = ACCESSOR;
return;
}
ReloadPropertyInformation<false>();
if (!new_map->is_dictionary_map()) return;
}
DirectHandle<AccessorPair> pair;
if (state() == ACCESSOR && IsAccessorPair(*GetAccessors(), isolate_)) {
pair = Cast<AccessorPair>(GetAccessors());
if (pair->Equals(*getter, *setter)) {
if (property_details().attributes() == attributes) {
if (!IsElement(*receiver)) JSObject::ReoptimizeIfPrototype(receiver);
return;
}
} else {
pair = AccessorPair::Copy(isolate(), pair);
pair->SetComponents(*getter, *setter);
}
} else {
pair = factory()->NewAccessorPair();
pair->SetComponents(*getter, *setter);
}
TransitionToAccessorPair(pair, attributes);
#if VERIFY_HEAP
if (v8_flags.verify_heap) {
receiver->JSObjectVerify(isolate());
}
#endif
}
void LookupIterator::TransitionToAccessorPair(DirectHandle<Object> pair,
PropertyAttributes attributes) {
DirectHandle<JSObject> receiver = GetStoreTarget<JSObject>();
holder_ = receiver;
PropertyDetails details(PropertyKind::kAccessor, attributes,
PropertyCellType::kMutable);
if (IsElement(*receiver)) {
isolate_->CountUsage(v8::Isolate::kIndexAccessor);
DirectHandle<NumberDictionary> dictionary =
JSObject::NormalizeElements(isolate_, receiver);
dictionary = NumberDictionary::Set(isolate_, dictionary, array_index(),
pair, receiver, details);
receiver->RequireSlowElements(*dictionary);
if (receiver->HasSlowArgumentsElements(isolate_)) {
Tagged<SloppyArgumentsElements> parameter_map =
Cast<SloppyArgumentsElements>(receiver->elements(isolate_));
uint32_t length = parameter_map->length();
if (number_.is_found() && number_.as_uint32() < length) {
parameter_map->set_mapped_entries(
number_.as_int(), ReadOnlyRoots(isolate_).the_hole_value());
}
parameter_map->set_arguments(*dictionary);
} else {
receiver->set_elements(*dictionary);
}
ReloadPropertyInformation<true>();
} else {
PropertyNormalizationMode mode = CLEAR_INOBJECT_PROPERTIES;
if (receiver->map(isolate_)->is_prototype_map()) {
JSObject::InvalidatePrototypeChains(receiver->map(isolate_));
mode = KEEP_INOBJECT_PROPERTIES;
}
JSObject::NormalizeProperties(isolate_, receiver, mode, 0,
"TransitionToAccessorPair");
JSObject::SetNormalizedProperty(receiver, name_, pair, details);
JSObject::ReoptimizeIfPrototype(receiver);
ReloadPropertyInformation<false>();
}
}
Tagged<JSObject> LookupIterator::GetHolderForApi() const {
DCHECK(state_ == INTERCEPTOR || state_ == ACCESSOR || state_ == ACCESS_CHECK);
return i::GetHolderForApi(Cast<JSObject>(*holder_));
}
bool LookupIterator::HolderIsReceiver() const {
DCHECK_NE(state_, STRING_LOOKUP_START_OBJECT);
DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY ||
state_ == TYPED_ARRAY_INDEX_NOT_FOUND);
if (!check_prototype_chain()) return true;
return *receiver_ == *holder_;
}
bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
DCHECK_NE(state_, STRING_LOOKUP_START_OBJECT);
DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
if (!check_prototype_chain()) return true;
if (*receiver_ == *holder_) return true;
if (!IsJSGlobalProxy(*receiver_, isolate_)) return false;
return Cast<JSGlobalProxy>(receiver_)->map(isolate_)->prototype(isolate_) ==
*holder_;
}
DirectHandle<Object> LookupIterator::FetchValue(
AllocationPolicy allocation_policy) const {
Tagged<Object> result;
DCHECK_NE(state_, STRING_LOOKUP_START_OBJECT);
DCHECK(!IsWasmObject(*holder_));
if (IsElement(*holder_)) {
DirectHandle<JSObject> holder = GetHolder<JSObject>();
ElementsAccessor* accessor = holder->GetElementsAccessor(isolate_);
return accessor->Get(isolate_, holder, number_);
} else if (IsJSGlobalObject(*holder_, isolate_)) {
DirectHandle<JSGlobalObject> holder = GetHolder<JSGlobalObject>();
result = holder->global_dictionary(isolate_, kAcquireLoad)
->ValueAt(isolate_, dictionary_entry());
} else if (!holder_->HasFastProperties(isolate_)) {
if constexpr (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
result = holder_->property_dictionary_swiss(isolate_)->ValueAt(
dictionary_entry());
} else {
result = holder_->property_dictionary(isolate_)->ValueAt(
isolate_, dictionary_entry());
}
} else if (property_details_.location() == PropertyLocation::kField) {
DCHECK_EQ(PropertyKind::kData, property_details_.kind());
DirectHandle<JSObject> holder = GetHolder<JSObject>();
FieldIndex field_index =
FieldIndex::ForDetails(holder->map(isolate_), property_details_);
if (allocation_policy == AllocationPolicy::kAllocationDisallowed &&
field_index.is_inobject() && field_index.is_double()) {
return isolate_->factory()->undefined_value();
}
return JSObject::FastPropertyAt(
isolate_, holder, property_details_.representation(), field_index);
} else {
result =
holder_->map(isolate_)->instance_descriptors(isolate_)->GetStrongValue(
isolate_, descriptor_number());
}
return direct_handle(result, isolate_);
}
bool LookupIterator::CanStayConst(Tagged<Object> value) const {
DCHECK(!holder_.is_null());
DCHECK(!IsElement(*holder_));
DCHECK(holder_->HasFastProperties(isolate_));
DCHECK_EQ(PropertyLocation::kField, property_details_.location());
DCHECK_EQ(PropertyConstness::kConst, property_details_.constness());
if (IsUninitializedHole(value, isolate())) {
return true;
}
DirectHandle<JSObject> holder = GetHolder<JSObject>();
FieldIndex field_index =
FieldIndex::ForDetails(holder->map(isolate_), property_details_);
if (property_details_.representation().IsDouble()) {
if (!IsNumber(value, isolate_)) return false;
uint64_t bits;
Tagged<Object> current_value =
holder->RawFastPropertyAt(isolate_, field_index);
DCHECK(IsHeapNumber(current_value, isolate_));
bits = Cast<HeapNumber>(current_value)->value_as_bits();
return bits == kHoleNanInt64;
}
Tagged<Object> current_value =
holder->RawFastPropertyAt(isolate_, field_index);
return IsUninitializedHole(current_value, isolate());
}
bool LookupIterator::DictCanStayConst(Tagged<Object> value) const {
DCHECK(!holder_.is_null());
DCHECK(!IsElement(*holder_));
DCHECK(!holder_->HasFastProperties(isolate_));
DCHECK(!IsJSGlobalObject(*holder_));
DCHECK(!IsJSProxy(*holder_));
DCHECK_EQ(PropertyConstness::kConst, property_details_.constness());
DisallowHeapAllocation no_gc;
if (IsUninitializedHole(value, isolate())) {
return true;
}
DirectHandle<JSReceiver> holder = GetHolder<JSReceiver>();
Tagged<Object> current_value;
if constexpr (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
Tagged<SwissNameDictionary> dict = holder->property_dictionary_swiss();
current_value = dict->ValueAt(dictionary_entry());
} else {
Tagged<NameDictionary> dict = holder->property_dictionary();
current_value = dict->ValueAt(dictionary_entry());
}
return IsUninitializedHole(current_value, isolate());
}
int LookupIterator::GetFieldDescriptorIndex() const {
DCHECK(has_property_);
DCHECK(holder_->HasFastProperties());
DCHECK_EQ(PropertyLocation::kField, property_details_.location());
DCHECK_EQ(PropertyKind::kData, property_details_.kind());
return descriptor_number().as_int();
}
int LookupIterator::GetAccessorIndex() const {
DCHECK(has_property_);
DCHECK(holder_->HasFastProperties(isolate_));
DCHECK_EQ(PropertyLocation::kDescriptor, property_details_.location());
DCHECK_EQ(PropertyKind::kAccessor, property_details_.kind());
return descriptor_number().as_int();
}
FieldIndex LookupIterator::GetFieldIndex() const {
DCHECK(has_property_);
DCHECK(!holder_.is_null());
DCHECK(holder_->HasFastProperties(isolate_));
DCHECK_EQ(PropertyLocation::kField, property_details_.location());
DCHECK(!IsElement(*holder_));
return FieldIndex::ForDetails(holder_->map(isolate_), property_details_);
}
DirectHandle<PropertyCell> LookupIterator::GetPropertyCell() const {
DCHECK(!holder_.is_null());
DCHECK(!IsElement(*holder_));
DirectHandle<JSGlobalObject> holder = GetHolder<JSGlobalObject>();
return direct_handle(holder->global_dictionary(isolate_, kAcquireLoad)
->CellAt(isolate_, dictionary_entry()),
isolate_);
}
DirectHandle<Object> LookupIterator::GetAccessors() const {
DCHECK_EQ(ACCESSOR, state_);
return FetchValue();
}
Handle<Object> LookupIterator::GetStringPropertyValue(
AllocationPolicy allocation_policy) const {
DCHECK_EQ(state_, STRING_LOOKUP_START_OBJECT);
if (IsElement()) {
DirectHandle<String> string = Cast<String>(lookup_start_object_);
DCHECK_LT(index_, string->length());
return isolate_->factory()->LookupSingleCharacterStringFromCode(
String::Flatten(isolate_, string)->Get(static_cast<uint32_t>(index_)));
}
DCHECK_EQ(*name_, ReadOnlyRoots(isolate_).length_string());
uint32_t length = Cast<String>(*lookup_start_object_)->length();
static_assert(String::kMaxLength < Smi::kMaxValue);
return handle(Smi::FromInt(length), isolate_);
}
Handle<Object> LookupIterator::GetDataValue(
AllocationPolicy allocation_policy) const {
DCHECK_EQ(DATA, state_);
return indirect_handle(FetchValue(allocation_policy), isolate_);
}
DirectHandle<Object> LookupIterator::GetDataValue(SeqCstAccessTag tag) const {
DCHECK_EQ(DATA, state_);
DCHECK(IsJSSharedStruct(*holder_, isolate_) ||
IsJSSharedArray(*holder_, isolate_));
DirectHandle<JSObject> holder = GetHolder<JSObject>();
if (IsElement(*holder)) {
ElementsAccessor* accessor = holder->GetElementsAccessor(isolate_);
return accessor->GetAtomic(isolate_, holder, number_, kSeqCstAccess);
}
DCHECK_EQ(PropertyLocation::kField, property_details_.location());
DCHECK_EQ(PropertyKind::kData, property_details_.kind());
FieldIndex field_index =
FieldIndex::ForDetails(holder->map(isolate_), property_details_);
return JSObject::FastPropertyAt(
isolate_, holder, property_details_.representation(), field_index, tag);
}
void LookupIterator::WriteDataValue(DirectHandle<Object> value,
bool initializing_store) {
DCHECK_EQ(DATA, state_);
DCHECK(!IsWasmObject(*holder_, isolate_));
DCHECK_IMPLIES(IsJSSharedStruct(*holder_), IsShared(*value));
DirectHandle<JSReceiver> holder = GetHolder<JSReceiver>();
if (IsElement(*holder)) {
DirectHandle<JSObject> object = Cast<JSObject>(holder);
ElementsAccessor* accessor = object->GetElementsAccessor(isolate_);
accessor->Set(object, number_, *value);
} else if (holder->HasFastProperties(isolate_)) {
DCHECK(IsJSObject(*holder, isolate_));
if (property_details_.location() == PropertyLocation::kField) {
DCHECK_IMPLIES(!initializing_store && property_details_.constness() ==
PropertyConstness::kConst,
CanStayConst(*value));
Cast<JSObject>(*holder)->WriteToField(descriptor_number(),
property_details_, *value);
} else {
DCHECK_EQ(PropertyLocation::kDescriptor, property_details_.location());
DCHECK_EQ(PropertyConstness::kConst, property_details_.constness());
}
} else if (IsJSGlobalObject(*holder, isolate_)) {
#ifdef DEBUG
Tagged<GlobalDictionary> dictionary =
Cast<JSGlobalObject>(*holder)->global_dictionary(isolate_,
kAcquireLoad);
Tagged<PropertyCell> cell =
dictionary->CellAt(isolate_, dictionary_entry());
DCHECK(cell->value() == *value ||
(IsString(cell->value()) && IsString(*value) &&
Cast<String>(cell->value())->Equals(Cast<String>(*value))));
#endif
} else {
DCHECK_IMPLIES(IsJSProxy(*holder, isolate_), name()->IsPrivate());
DCHECK_IMPLIES(
V8_DICT_PROPERTY_CONST_TRACKING_BOOL && !initializing_store &&
property_details_.constness() == PropertyConstness::kConst,
IsJSProxy(*holder, isolate_) || DictCanStayConst(*value));
if constexpr (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
Tagged<SwissNameDictionary> dictionary =
holder->property_dictionary_swiss(isolate_);
dictionary->ValueAtPut(dictionary_entry(), *value);
} else {
Tagged<NameDictionary> dictionary = holder->property_dictionary(isolate_);
dictionary->ValueAtPut(dictionary_entry(), *value);
}
}
}
void LookupIterator::WriteDataValue(DirectHandle<Object> value,
SeqCstAccessTag tag) {
DCHECK_EQ(DATA, state_);
DCHECK(IsJSSharedStruct(*holder_, isolate_) ||
IsJSSharedArray(*holder_, isolate_));
DirectHandle<JSObject> holder = GetHolder<JSObject>();
if (IsElement(*holder)) {
ElementsAccessor* accessor = holder->GetElementsAccessor(isolate_);
accessor->SetAtomic(holder, number_, *value, kSeqCstAccess);
return;
}
DCHECK_EQ(PropertyLocation::kField, property_details_.location());
DCHECK_EQ(PropertyKind::kData, property_details_.kind());
DisallowGarbageCollection no_gc;
FieldIndex field_index =
FieldIndex::ForDescriptor(holder->map(isolate_), descriptor_number());
holder->FastPropertyAtPut(field_index, *value, tag);
}
DirectHandle<Object> LookupIterator::SwapDataValue(DirectHandle<Object> value,
SeqCstAccessTag tag) {
DCHECK_EQ(DATA, state_);
DCHECK(IsJSSharedStruct(*holder_, isolate_) ||
IsJSSharedArray(*holder_, isolate_));
DirectHandle<JSObject> holder = GetHolder<JSObject>();
if (IsElement(*holder)) {
ElementsAccessor* accessor = holder->GetElementsAccessor(isolate_);
return accessor->SwapAtomic(isolate_, holder, number_, *value,
kSeqCstAccess);
}
DCHECK_EQ(PropertyLocation::kField, property_details_.location());
DCHECK_EQ(PropertyKind::kData, property_details_.kind());
DisallowGarbageCollection no_gc;
FieldIndex field_index =
FieldIndex::ForDescriptor(holder->map(isolate_), descriptor_number());
return direct_handle(holder->RawFastPropertyAtSwap(field_index, *value, tag),
isolate_);
}
DirectHandle<Object> LookupIterator::CompareAndSwapDataValue(
DirectHandle<Object> expected, DirectHandle<Object> value,
SeqCstAccessTag tag) {
DCHECK_EQ(DATA, state_);
DCHECK(IsJSSharedStruct(*holder_, isolate_) ||
IsJSSharedArray(*holder_, isolate_));
DisallowGarbageCollection no_gc;
DirectHandle<JSObject> holder = GetHolder<JSObject>();
if (IsElement(*holder)) {
ElementsAccessor* accessor = holder->GetElementsAccessor(isolate_);
return accessor->CompareAndSwapAtomic(isolate_, holder, number_, *expected,
*value, kSeqCstAccess);
}
DCHECK_EQ(PropertyLocation::kField, property_details_.location());
DCHECK_EQ(PropertyKind::kData, property_details_.kind());
FieldIndex field_index =
FieldIndex::ForDescriptor(holder->map(isolate_), descriptor_number());
return direct_handle(holder->RawFastPropertyAtCompareAndSwap(
field_index, *expected, *value, tag),
isolate_);
}
template <bool is_element>
bool LookupIterator::SkipInterceptor(Tagged<JSObject> holder) {
Tagged<InterceptorInfo> info = GetInterceptor<is_element>(holder);
if (!is_element && IsSymbol(*name_, isolate_) &&
!info->can_intercept_symbols()) {
return true;
}
if (info->non_masking()) {
switch (interceptor_state_) {
case InterceptorState::kUninitialized:
interceptor_state_ = InterceptorState::kSkipNonMasking;
[[fallthrough]];
case InterceptorState::kSkipNonMasking:
return true;
case InterceptorState::kProcessNonMasking:
return false;
}
}
return interceptor_state_ == InterceptorState::kProcessNonMasking;
}
Tagged<JSReceiver> LookupIterator::NextHolder(Tagged<Map> map) {
DisallowGarbageCollection no_gc;
if (map->prototype(isolate_) == ReadOnlyRoots(isolate_).null_value()) {
return JSReceiver();
}
if (!check_prototype_chain() && !IsJSGlobalProxyMap(map)) {
return JSReceiver();
}
return Cast<JSReceiver>(map->prototype(isolate_));
}
LookupIterator::State LookupIterator::NotFound(
Tagged<JSReceiver> const holder) const {
if (!IsJSTypedArray(holder, isolate_)) return NOT_FOUND;
if (IsElement()) return TYPED_ARRAY_INDEX_NOT_FOUND;
if (!IsString(*name_, isolate_)) return NOT_FOUND;
return IsSpecialIndex(Cast<String>(*name_)) ? TYPED_ARRAY_INDEX_NOT_FOUND
: NOT_FOUND;
}
namespace {
template <bool is_element>
bool HasInterceptor(Tagged<Map> map, size_t index) {
if (is_element) {
if (index > JSObject::kMaxElementIndex) {
DCHECK(!map->has_typed_array_or_rab_gsab_typed_array_elements());
return map->has_named_interceptor();
}
return map->has_indexed_interceptor();
} else {
return map->has_named_interceptor();
}
}
}
template <bool is_element>
LookupIterator::State LookupIterator::LookupInSpecialHolder(
Tagged<Map> const map, Tagged<JSReceiver> const holder) {
static_assert(INTERCEPTOR == BEFORE_PROPERTY);
switch (state_) {
case NOT_FOUND:
if (IsJSProxyMap(map)) {
if (is_element || !name_->IsPrivate()) return JSPROXY;
}
#if V8_ENABLE_WEBASSEMBLY
if (IsWasmObjectMap(map)) return WASM_OBJECT;
#endif
if (map->is_access_check_needed()) {
if (is_element || !name_->IsPrivate() || name_->IsPrivateName())
return ACCESS_CHECK;
}
[[fallthrough]];
case ACCESS_CHECK:
if (check_interceptor() && HasInterceptor<is_element>(map, index_) &&
!SkipInterceptor<is_element>(Cast<JSObject>(holder))) {
if (is_element || !name_->IsPrivate()) return INTERCEPTOR;
}
[[fallthrough]];
case INTERCEPTOR:
if (IsJSGlobalObjectMap(map) && !is_js_array_element(is_element)) {
Tagged<GlobalDictionary> dict =
Cast<JSGlobalObject>(holder)->global_dictionary(isolate_,
kAcquireLoad);
number_ = dict->FindEntry(isolate(), name_);
if (number_.is_not_found()) return NOT_FOUND;
Tagged<PropertyCell> cell = dict->CellAt(isolate_, number_);
if (IsPropertyCellHole(cell->value(isolate_), isolate_)) {
return NOT_FOUND;
}
property_details_ = cell->property_details();
has_property_ = true;
switch (property_details_.kind()) {
case v8::internal::PropertyKind::kData:
return DATA;
case v8::internal::PropertyKind::kAccessor:
return ACCESSOR;
}
}
return LookupInRegularHolder<is_element>(map, holder);
case ACCESSOR:
case DATA:
case WASM_OBJECT:
return NOT_FOUND;
case TYPED_ARRAY_INDEX_NOT_FOUND:
case JSPROXY:
case TRANSITION:
case STRING_LOOKUP_START_OBJECT:
UNREACHABLE();
}
UNREACHABLE();
}
template <bool is_element>
LookupIterator::State LookupIterator::LookupInRegularHolder(
Tagged<Map> const map, Tagged<JSReceiver> const holder) {
DisallowGarbageCollection no_gc;
if (interceptor_state_ == InterceptorState::kProcessNonMasking) {
return NOT_FOUND;
}
DCHECK(!IsWasmObject(holder, isolate_));
if (is_element && IsElement(holder)) {
Tagged<JSObject> js_object = Cast<JSObject>(holder);
ElementsAccessor* accessor = js_object->GetElementsAccessor(isolate_);
Tagged<FixedArrayBase> backing_store = js_object->elements(isolate_);
number_ =
accessor->GetEntryForIndex(isolate_, js_object, backing_store, index_);
if (number_.is_not_found()) {
return IsJSTypedArray(holder, isolate_) ? TYPED_ARRAY_INDEX_NOT_FOUND
: NOT_FOUND;
}
property_details_ = accessor->GetDetails(js_object, number_);
if (map->has_frozen_elements()) {
property_details_ = property_details_.CopyAddAttributes(FROZEN);
} else if (map->has_sealed_elements()) {
property_details_ = property_details_.CopyAddAttributes(SEALED);
}
} else if (!map->is_dictionary_map()) {
Tagged<DescriptorArray> descriptors = map->instance_descriptors(isolate_);
number_ = descriptors->SearchWithCache(isolate_, *name_, map);
if (number_.is_not_found()) return NotFound(holder);
property_details_ = descriptors->GetDetails(number_);
} else {
DCHECK_IMPLIES(IsJSProxy(holder, isolate_), name()->IsPrivate());
if constexpr (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
Tagged<SwissNameDictionary> dict =
holder->property_dictionary_swiss(isolate_);
number_ = dict->FindEntry(isolate(), *name_);
if (number_.is_not_found()) return NotFound(holder);
property_details_ = dict->DetailsAt(number_);
} else {
Tagged<NameDictionary> dict = holder->property_dictionary(isolate_);
number_ = dict->FindEntry(isolate(), name_);
if (number_.is_not_found()) return NotFound(holder);
property_details_ = dict->DetailsAt(number_);
}
}
has_property_ = true;
switch (property_details_.kind()) {
case v8::internal::PropertyKind::kData:
return DATA;
case v8::internal::PropertyKind::kAccessor:
return ACCESSOR;
}
UNREACHABLE();
}
bool LookupIterator::HasInternalMarkerProperty(
Isolate* isolate, Tagged<JSReceiver> const holder,
DirectHandle<Symbol> const marker) {
DisallowGarbageCollection no_gc;
Tagged<Map> map = holder->map(isolate);
if (map->is_dictionary_map()) {
if constexpr (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
Tagged<SwissNameDictionary> dict =
holder->property_dictionary_swiss(isolate);
InternalIndex entry = dict->FindEntry(isolate, marker);
return entry.is_found();
} else {
Tagged<NameDictionary> dict = holder->property_dictionary(isolate);
InternalIndex entry = dict->FindEntry(isolate, marker);
return entry.is_found();
}
} else {
Tagged<DescriptorArray> descriptors = map->instance_descriptors(isolate);
InternalIndex entry = descriptors->SearchWithCache(isolate, *marker, map);
return entry.is_found();
}
}
DirectHandle<InterceptorInfo>
LookupIterator::GetInterceptorForFailedAccessCheck() const {
DCHECK_EQ(ACCESS_CHECK, state_);
if (IsPrivateName()) {
return DirectHandle<InterceptorInfo>();
}
DisallowGarbageCollection no_gc;
Tagged<AccessCheckInfo> access_check_info =
AccessCheckInfo::Get(isolate_, Cast<JSObject>(holder_));
if (!access_check_info.is_null()) {
DCHECK(!holder_->map()->has_typed_array_or_rab_gsab_typed_array_elements());
Tagged<Object> interceptor = is_js_array_element(IsElement())
? access_check_info->indexed_interceptor()
: access_check_info->named_interceptor();
if (interceptor != Tagged<Object>()) {
return direct_handle(Cast<InterceptorInfo>(interceptor), isolate_);
}
}
return DirectHandle<InterceptorInfo>();
}
bool LookupIterator::TryLookupCachedProperty(
DirectHandle<AccessorPair> accessor) {
DCHECK_EQ(state(), LookupIterator::ACCESSOR);
return LookupCachedProperty(accessor);
}
bool LookupIterator::TryLookupCachedProperty() {
if (state() != LookupIterator::ACCESSOR) return false;
DirectHandle<Object> accessor_pair = GetAccessors();
return IsAccessorPair(*accessor_pair, isolate_) &&
LookupCachedProperty(Cast<AccessorPair>(accessor_pair));
}
bool LookupIterator::LookupCachedProperty(
DirectHandle<AccessorPair> accessor_pair) {
if (!HolderIsReceiverOrHiddenPrototype()) return false;
if (!lookup_start_object_.is_identical_to(receiver_) &&
!lookup_start_object_.is_identical_to(holder_)) {
return false;
}
DCHECK_EQ(state(), LookupIterator::ACCESSOR);
DCHECK(IsAccessorPair(*GetAccessors(), isolate_));
Tagged<Object> getter = accessor_pair->getter();
std::optional<Tagged<Name>> maybe_name =
FunctionTemplateInfo::TryGetCachedPropertyName(isolate(), getter);
if (!maybe_name.has_value()) return false;
if (IsJSFunction(getter)) {
LookupIterator it(isolate_, holder_,
direct_handle(maybe_name.value(), isolate_));
if (it.state() != DATA) return false;
name_ = it.name();
} else {
name_ = direct_handle(maybe_name.value(), isolate_);
}
Restart();
CHECK_EQ(state(), LookupIterator::DATA);
return true;
}
std::optional<Tagged<Object>> ConcurrentLookupIterator::TryGetOwnCowElement(
Isolate* isolate, Tagged<FixedArray> array_elements,
ElementsKind elements_kind, int array_length, size_t index) {
DisallowGarbageCollection no_gc;
CHECK_EQ(array_elements->map(), ReadOnlyRoots(isolate).fixed_cow_array_map());
DCHECK(IsFastElementsKind(elements_kind) &&
IsSmiOrObjectElementsKind(elements_kind));
USE(elements_kind);
DCHECK_GE(array_length, 0);
// ||----w |
if (index >= static_cast<size_t>(array_length)) return {};
if (index >= static_cast<size_t>(array_elements->length())) return {};
Tagged<Object> result = array_elements->get(static_cast<int>(index));
// ||----w |
if (result == ReadOnlyRoots(isolate).the_hole_value()) return {};
return result;
}
ConcurrentLookupIterator::Result
ConcurrentLookupIterator::TryGetOwnConstantElement(
Tagged<Object>* result_out, Isolate* isolate, LocalIsolate* local_isolate,
Tagged<JSObject> holder, Tagged<FixedArrayBase> elements,
ElementsKind elements_kind, size_t index) {
DisallowGarbageCollection no_gc;
DCHECK_LE(index, JSObject::kMaxElementIndex);
if (IsFrozenElementsKind(elements_kind)) {
if (!IsFixedArray(elements)) return kGaveUp;
Tagged<FixedArray> elements_fixed_array = Cast<FixedArray>(elements);
if (index >= static_cast<uint32_t>(elements_fixed_array->length())) {
return kGaveUp;
}
Tagged<Object> result = elements_fixed_array->get(static_cast<int>(index));
if (IsHoleyElementsKindForRead(elements_kind) &&
result == ReadOnlyRoots(isolate).the_hole_value()) {
return kNotPresent;
}
*result_out = result;
return kPresent;
} else if (IsDictionaryElementsKind(elements_kind)) {
if (!IsNumberDictionary(elements)) return kGaveUp;
return kGaveUp;
} else if (IsStringWrapperElementsKind(elements_kind)) {
Tagged<JSPrimitiveWrapper> js_value = Cast<JSPrimitiveWrapper>(holder);
Tagged<String> wrapped_string = Cast<String>(js_value->value());
return ConcurrentLookupIterator::TryGetOwnChar(
reinterpret_cast<Tagged<String>*>(result_out), isolate, local_isolate,
wrapped_string, index);
} else {
DCHECK(!IsFrozenElementsKind(elements_kind));
DCHECK(!IsDictionaryElementsKind(elements_kind));
DCHECK(!IsStringWrapperElementsKind(elements_kind));
return kGaveUp;
}
UNREACHABLE();
}
ConcurrentLookupIterator::Result ConcurrentLookupIterator::TryGetOwnChar(
Tagged<String>* result_out, Isolate* isolate, LocalIsolate* local_isolate,
Tagged<String> string, size_t index) {
DisallowGarbageCollection no_gc;
Tagged<Map> string_map = string->map(kAcquireLoad);
InstanceType type = string_map->instance_type();
if (!(InstanceTypeChecker::IsInternalizedString(type) ||
InstanceTypeChecker::IsThinString(type))) {
return kGaveUp;
}
const uint32_t length = static_cast<uint32_t>(string->length());
if (index >= length) return kGaveUp;
uint16_t charcode;
{
SharedStringAccessGuardIfNeeded access_guard(local_isolate);
charcode = string->Get(static_cast<int>(index), access_guard);
}
if (charcode > unibrow::Latin1::kMaxChar) return kGaveUp;
ReadOnlyRoots roots(isolate);
Tagged<String> value = roots.single_character_string(charcode);
*result_out = value;
return kPresent;
}
std::optional<Tagged<PropertyCell>>
ConcurrentLookupIterator::TryGetPropertyCell(
Isolate* isolate, LocalIsolate* local_isolate,
DirectHandle<JSGlobalObject> holder, DirectHandle<Name> name) {
DisallowGarbageCollection no_gc;
Tagged<Map> holder_map = holder->map();
if (holder_map->is_access_check_needed()) return {};
if (holder_map->has_named_interceptor()) return {};
Tagged<GlobalDictionary> dict = holder->global_dictionary(kAcquireLoad);
std::optional<Tagged<PropertyCell>> maybe_cell =
dict->TryFindPropertyCellForConcurrentLookupIterator(isolate, name,
kRelaxedLoad);
if (!maybe_cell.has_value()) return {};
Tagged<PropertyCell> cell = maybe_cell.value();
if (cell->property_details(kAcquireLoad).kind() == PropertyKind::kAccessor) {
Tagged<Object> maybe_accessor_pair = cell->value(kAcquireLoad);
if (!IsAccessorPair(maybe_accessor_pair)) return {};
std::optional<Tagged<Name>> maybe_cached_property_name =
FunctionTemplateInfo::TryGetCachedPropertyName(
isolate,
Cast<AccessorPair>(maybe_accessor_pair)->getter(kAcquireLoad));
if (!maybe_cached_property_name.has_value()) return {};
maybe_cell = dict->TryFindPropertyCellForConcurrentLookupIterator(
isolate, direct_handle(*maybe_cached_property_name, local_isolate),
kRelaxedLoad);
if (!maybe_cell.has_value()) return {};
cell = maybe_cell.value();
if (cell->property_details(kAcquireLoad).kind() != PropertyKind::kData)
return {};
}
DCHECK(maybe_cell.has_value());
DCHECK_EQ(cell->property_details(kAcquireLoad).kind(), PropertyKind::kData);
return cell;
}
}