/
githubmirror
/
node
Обзор
Документация
Войти
/
githubmirror
/
node
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
deps/v8/src/objects/descriptor-array-inl.h
494 строки
19 KB
Michaël Zasso
deps: update V8 to 14.6.202.33
24 апр 2026, 19:01
Не верифицирован
24 апр 2026, 19:01
f1e0b83
Код
Авторство
О чём код?
// Copyright 2018 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef V8_OBJECTS_DESCRIPTOR_ARRAY_INL_H_ #define V8_OBJECTS_DESCRIPTOR_ARRAY_INL_H_ #include "src/objects/descriptor-array.h" // Include the non-inl header before the rest of the headers. #include "src/execution/isolate.h" #include "src/handles/maybe-handles-inl.h" #include "src/heap/heap-write-barrier.h" #include "src/heap/heap.h" #include "src/objects/api-callbacks.h" #include "src/objects/dictionary.h" #include "src/objects/field-type.h" #include "src/objects/heap-object-inl.h" #include "src/objects/lookup-cache-inl.h" #include "src/objects/maybe-object-inl.h" #include "src/objects/property.h" #include "src/objects/struct-inl.h" #include "src/objects/tagged-field-inl.h" #include "src/torque/runtime-macro-shims.h" #include "src/torque/runtime-support.h" // Has to be the last include (doesn't have include guards): #include "src/objects/object-macros.h" namespace v8 { namespace internal { #include "torque-generated/src/objects/descriptor-array-tq-inl.inc" TQ_OBJECT_CONSTRUCTORS_IMPL(DescriptorArray) Tagged<FixedArray> EnumCache::keys() const { return keys_.load(); } void EnumCache::set_keys(Tagged<FixedArray> value, WriteBarrierMode mode) { keys_.store(this, value, mode); } Tagged<FixedArray> EnumCache::indices() const { return indices_.load(); } void EnumCache::set_indices(Tagged<FixedArray> value, WriteBarrierMode mode) { indices_.store(this, value, mode); } RELAXED_INT16_ACCESSORS(DescriptorArray, number_of_all_descriptors, kNumberOfAllDescriptorsOffset) RELAXED_INT16_ACCESSORS(DescriptorArray, number_of_descriptors, kNumberOfDescriptorsOffset) RELAXED_UINT32_ACCESSORS(DescriptorArray, raw_gc_state, kRawGcStateOffset) RELAXED_UINT32_ACCESSORS(DescriptorArray, flags, kFlagsOffset) inline int16_t DescriptorArray::number_of_slack_descriptors() const { return number_of_all_descriptors() - number_of_descriptors(); } inline int DescriptorArray::number_of_entries() const { return number_of_descriptors(); } DescriptorArray::FastIterableState DescriptorArray::fast_iterable() const { return FastIterableBits::decode(flags(kRelaxedLoad)); } void DescriptorArray::set_fast_iterable(FastIterableState value) { uint32_t f = flags(kRelaxedLoad); f = FastIterableBits::update(f, value); set_flags(f, kRelaxedStore); } void DescriptorArray::set_fast_iterable_if(FastIterableState new_value, FastIterableState if_value) { uint32_t f = flags(kRelaxedLoad); if (FastIterableBits::decode(f) == if_value) { f = FastIterableBits::update(f, new_value); set_flags(f, kRelaxedStore); } } void DescriptorArray::CopyEnumCacheFrom(Tagged<DescriptorArray> array) { set_enum_cache(array->enum_cache()); } InternalIndex DescriptorArray::Search(Tagged<Name> name, int valid_descriptors, bool concurrent_search) { DCHECK(IsUniqueName(name)); SLOW_DCHECK_IMPLIES(!concurrent_search, IsSortedNoDuplicates()); if (valid_descriptors == 0) { return InternalIndex::NotFound(); } if (valid_descriptors <= kMaxElementsForLinearSearch || concurrent_search) { return LinearSearch(name, valid_descriptors); } return BinarySearch(name, valid_descriptors); } InternalIndex DescriptorArray::BinarySearch(Tagged<Name> name, int valid_descriptors) { // We have to binary search all descriptors, not just valid ones, since the // binary search ordering is across all descriptors. int end = number_of_descriptors(); // Binary search must not be used for small number of descriptors since // the descriptor array is not sorted yet. DCHECK_LT(kMaxElementsForLinearSearch, end); uint32_t hash = name->hash(); // Find the first descriptor whose key's hash is greater-than-or-equal-to the // search hash. int number = *std::ranges::lower_bound(std::views::iota(0, end), hash, std::less<>(), [&](int i) { Tagged<Name> entry = GetSortedKey(i); return entry->hash(); }); // There may have been hash collisions, so search for the name from the first // index until the first non-matching hash. for (; number < end; ++number) { InternalIndex index(GetSortedKeyIndex(number)); Tagged<Name> entry = GetKey(index); if (entry == name) { // If we found the entry, but it's outside the owned descriptors of the // caller, return not found. if (index.as_int() >= valid_descriptors) { return InternalIndex::NotFound(); } return index; } if (entry->hash() != hash) { return InternalIndex::NotFound(); } } return InternalIndex::NotFound(); } InternalIndex DescriptorArray::LinearSearch(Tagged<Name> name, int valid_descriptors) { DCHECK_LE(valid_descriptors, number_of_descriptors()); for (int i = 0; i < valid_descriptors; ++i) { InternalIndex index(i); if (GetKey(index) == name) return index; } return InternalIndex::NotFound(); } InternalIndex DescriptorArray::Search(Tagged<Name> name, Tagged<Map> map, bool concurrent_search) { DCHECK(IsUniqueName(name)); int number_of_own_descriptors = map->NumberOfOwnDescriptors(); if (number_of_own_descriptors == 0) return InternalIndex::NotFound(); return Search(name, number_of_own_descriptors, concurrent_search); } InternalIndex DescriptorArray::Search(int field_index, int valid_descriptors) { for (int desc_index = field_index; desc_index < valid_descriptors; ++desc_index) { PropertyDetails details = GetDetails(InternalIndex(desc_index)); if (details.location() != PropertyLocation::kField) continue; if (field_index == details.field_index()) { return InternalIndex(desc_index); } DCHECK_LT(details.field_index(), field_index); } return InternalIndex::NotFound(); } InternalIndex DescriptorArray::Search(int field_index, Tagged<Map> map) { int number_of_own_descriptors = map->NumberOfOwnDescriptors(); if (number_of_own_descriptors == 0) return InternalIndex::NotFound(); return Search(field_index, number_of_own_descriptors); } InternalIndex DescriptorArray::SearchWithCache(Isolate* isolate, Tagged<Name> name, Tagged<Map> map) { DCHECK(IsUniqueName(name)); int number_of_own_descriptors = map->NumberOfOwnDescriptors(); if (number_of_own_descriptors == 0) return InternalIndex::NotFound(); DescriptorLookupCache* cache = isolate->descriptor_lookup_cache(); int number = cache->Lookup(map, name); if (number == DescriptorLookupCache::kAbsent) { InternalIndex result = Search(name, number_of_own_descriptors); number = result.is_found() ? result.as_int() : DescriptorArray::kNotFound; cache->Update(map, name, number); } if (number == DescriptorArray::kNotFound) return InternalIndex::NotFound(); return InternalIndex(number); } ObjectSlot DescriptorArray::GetFirstPointerSlot() { static_assert(kEndOfStrongFieldsOffset == kStartOfWeakFieldsOffset, "Weak and strong fields are continuous."); static_assert(kEndOfWeakFieldsOffset == kHeaderSize, "Weak fields extend up to the end of the header."); return RawField(DescriptorArray::kStartOfStrongFieldsOffset); } ObjectSlot DescriptorArray::GetDescriptorSlot(int descriptor) { // Allow descriptor == number_of_all_descriptors() for computing the slot // address that comes after the last descriptor (for iterating). DCHECK_LE(descriptor, number_of_all_descriptors()); return RawField(OffsetOfDescriptorAt(descriptor)); } bool DescriptorArray::IsInitializedDescriptor( InternalIndex descriptor_number) const { DCHECK_LT(descriptor_number.as_int(), number_of_descriptors()); int entry_offset = OffsetOfDescriptorAt(descriptor_number.as_int()); PtrComprCageBase cage_base = GetPtrComprCageBase(*this); Tagged<Object> maybe_name = EntryKeyField::Relaxed_Load(cage_base, *this, entry_offset); bool is_initialized = !IsUndefined(maybe_name); DCHECK_IMPLIES(is_initialized, IsSmi(EntryDetailsField::Relaxed_Load(*this, entry_offset))); return is_initialized; } Tagged<Name> DescriptorArray::GetKey(InternalIndex descriptor_number) const { PtrComprCageBase cage_base = GetPtrComprCageBase(*this); return GetKey(cage_base, descriptor_number); } Tagged<Name> DescriptorArray::GetKey(PtrComprCageBase cage_base, InternalIndex descriptor_number) const { DCHECK_LT(descriptor_number.as_int(), number_of_descriptors()); int entry_offset = OffsetOfDescriptorAt(descriptor_number.as_int()); return Cast<Name>( EntryKeyField::Relaxed_Load(cage_base, *this, entry_offset)); } void DescriptorArray::SetKey(InternalIndex descriptor_number, Tagged<Name> key) { DCHECK_LT(descriptor_number.as_int(), number_of_descriptors()); int entry_offset = OffsetOfDescriptorAt(descriptor_number.as_int()); EntryKeyField::Relaxed_Store(*this, entry_offset, key); WRITE_BARRIER(*this, entry_offset + kEntryKeyOffset, key); // Conservatively assume that the new key might break fast iteration. // If the key was already known to be slow, it will stay slow. set_fast_iterable_if(FastIterableState::kUnknown, FastIterableState::kJsonFast); } int DescriptorArray::GetSortedKeyIndex(int descriptor_number) { return GetDetails(InternalIndex(descriptor_number)).pointer(); } Tagged<Name> DescriptorArray::GetSortedKey(int descriptor_number) { PtrComprCageBase cage_base = GetPtrComprCageBase(*this); return GetSortedKey(cage_base, descriptor_number); } Tagged<Name> DescriptorArray::GetSortedKey(PtrComprCageBase cage_base, int descriptor_number) { return GetKey(cage_base, InternalIndex(GetSortedKeyIndex(descriptor_number))); } void DescriptorArray::SetSortedKey(int descriptor_number, int pointer) { PropertyDetails details = GetDetails(InternalIndex(descriptor_number)); SetDetails(InternalIndex(descriptor_number), details.set_pointer(pointer)); } Tagged<Object> DescriptorArray::GetStrongValue( InternalIndex descriptor_number) { PtrComprCageBase cage_base = GetPtrComprCageBase(*this); return Cast<Object>(GetStrongValue(cage_base, descriptor_number)); } Tagged<Object> DescriptorArray::GetStrongValue( PtrComprCageBase cage_base, InternalIndex descriptor_number) { return Cast<Object>(GetValue(cage_base, descriptor_number)); } void DescriptorArray::SetValue(InternalIndex descriptor_number, Tagged<MaybeObject> value) { DCHECK_LT(descriptor_number.as_int(), number_of_descriptors()); int entry_offset = OffsetOfDescriptorAt(descriptor_number.as_int()); EntryValueField::Relaxed_Store(*this, entry_offset, value); WRITE_BARRIER(*this, entry_offset + kEntryValueOffset, value); } Tagged<MaybeObject> DescriptorArray::GetValue(InternalIndex descriptor_number) { PtrComprCageBase cage_base = GetPtrComprCageBase(*this); return GetValue(cage_base, descriptor_number); } Tagged<MaybeObject> DescriptorArray::GetValue(PtrComprCageBase cage_base, InternalIndex descriptor_number) { DCHECK_LT(descriptor_number.as_int(), number_of_descriptors()); int entry_offset = OffsetOfDescriptorAt(descriptor_number.as_int()); return EntryValueField::Relaxed_Load(cage_base, *this, entry_offset); } PropertyDetails DescriptorArray::GetDetails(InternalIndex descriptor_number) { DCHECK_LT(descriptor_number.as_int(), number_of_descriptors()); int entry_offset = OffsetOfDescriptorAt(descriptor_number.as_int()); Tagged<Smi> details = EntryDetailsField::Relaxed_Load(*this, entry_offset); return PropertyDetails(details); } void DescriptorArray::SetDetails(InternalIndex descriptor_number, PropertyDetails details) { DCHECK_LT(descriptor_number.as_int(), number_of_descriptors()); int entry_offset = OffsetOfDescriptorAt(descriptor_number.as_int()); EntryDetailsField::Relaxed_Store(*this, entry_offset, details.AsSmi()); // Note: fast_iteration depends on PropertyDetails::location(). // However we don't reset it here as all path either go through SetKey(), // which invalidates fast_iteration, or don't change the location // (GeneralizeAllFields()). } int DescriptorArray::GetFieldIndex(InternalIndex descriptor_number) { DCHECK_EQ(GetDetails(descriptor_number).location(), PropertyLocation::kField); return GetDetails(descriptor_number).field_index(); } Tagged<FieldType> DescriptorArray::GetFieldType( InternalIndex descriptor_number) { PtrComprCageBase cage_base = GetPtrComprCageBase(*this); return GetFieldType(cage_base, descriptor_number); } Tagged<FieldType> DescriptorArray::GetFieldType( PtrComprCageBase cage_base, InternalIndex descriptor_number) { DCHECK_EQ(GetDetails(descriptor_number).location(), PropertyLocation::kField); Tagged<MaybeObject> wrapped_type = GetValue(cage_base, descriptor_number); return Map::UnwrapFieldType(wrapped_type); } void DescriptorArray::Set(InternalIndex descriptor_number, Tagged<Name> key, Tagged<MaybeObject> value, PropertyDetails details) { CHECK_LT(descriptor_number.as_int(), number_of_descriptors()); SetKey(descriptor_number, key); SetDetails(descriptor_number, details); SetValue(descriptor_number, value); // Resetting the fast iterable state is bottlenecked in SetKey(). DCHECK_NE(fast_iterable(), FastIterableState::kJsonFast); } void DescriptorArray::Set(InternalIndex descriptor_number, Descriptor* desc) { Tagged<Name> key = *desc->GetKey(); Tagged<MaybeObject> value = *desc->GetValue(); Set(descriptor_number, key, value, desc->GetDetails()); // Resetting the fast iterable state is bottlenecked in SetKey(). DCHECK_NE(fast_iterable(), FastIterableState::kJsonFast); } void DescriptorArray::Append(Descriptor* desc) { DisallowGarbageCollection no_gc; int descriptor_number = number_of_descriptors(); int new_number_of_descriptors = descriptor_number + 1; DCHECK_LE(new_number_of_descriptors, number_of_all_descriptors()); set_number_of_descriptors(new_number_of_descriptors); Set(InternalIndex(descriptor_number), desc); // Resetting the fast iterable state is bottlenecked in SetKey(). DCHECK_NE(fast_iterable(), FastIterableState::kJsonFast); if (new_number_of_descriptors <= kMaxElementsForLinearSearch) { // Ensure there are no name collisions. CHECK_EQ(LinearSearch(*desc->GetKey(), descriptor_number), InternalIndex::NotFound()); return; } else if (new_number_of_descriptors == kMaxElementsForLinearSearch + 1) { // Sort descriptors as we've just crossed the unsorted-sorted boundary. SortImpl(new_number_of_descriptors); uint32_t desc_hash = desc->GetKey()->hash(); CheckNameCollisionDuringInsertion(desc, desc_hash, desc->GetSortedKeyIndex()); return; } uint32_t desc_hash = desc->GetKey()->hash(); // Hash value can't be zero, see String::ComputeAndSetHash() uint32_t collision_hash = 0; int insertion; for (insertion = descriptor_number; insertion > 0; --insertion) { Tagged<Name> key = GetSortedKey(insertion - 1); collision_hash = key->hash(); if (collision_hash <= desc_hash) break; SetSortedKey(insertion, GetSortedKeyIndex(insertion - 1)); } SetSortedKey(insertion, descriptor_number); if (V8_LIKELY(collision_hash != desc_hash)) return; CheckNameCollisionDuringInsertion(desc, desc_hash, insertion); } void DescriptorArray::Sort() { const int len = number_of_descriptors(); // Sorting matters only for binary search. if (len <= kMaxElementsForLinearSearch) return; SortImpl(len); } void DescriptorArray::SwapSortedKeys(int first, int second) { int first_key = GetSortedKeyIndex(first); SetSortedKey(first, GetSortedKeyIndex(second)); SetSortedKey(second, first_key); } // static bool DescriptorArrayMarkingState::TryUpdateIndicesToMark( unsigned gc_epoch, Tagged<DescriptorArray> array, DescriptorIndex index_to_mark) { const auto current_epoch = gc_epoch & Epoch::kMask; while (true) { const RawGCStateType raw_gc_state = array->raw_gc_state(kRelaxedLoad); const auto epoch_from_state = Epoch::decode(raw_gc_state); RawGCStateType new_raw_gc_state = 0; if (current_epoch != epoch_from_state) { // If the epochs do not match, then either the raw_gc_state is zero // (freshly allocated descriptor array) or the epoch from value lags // by 1. DCHECK_IMPLIES(raw_gc_state != 0, Epoch::decode(epoch_from_state + 1) == current_epoch); new_raw_gc_state = NewState(current_epoch, 0, index_to_mark); } else { const DescriptorIndex already_marked = Marked::decode(raw_gc_state); const DescriptorIndex delta = Delta::decode(raw_gc_state); if ((already_marked + delta) >= index_to_mark) { return false; } new_raw_gc_state = NewState(current_epoch, already_marked, index_to_mark - already_marked); } if (SwapState(array, raw_gc_state, new_raw_gc_state)) { return true; } } } // static std::pair<DescriptorArrayMarkingState::DescriptorIndex, DescriptorArrayMarkingState::DescriptorIndex> DescriptorArrayMarkingState::AcquireDescriptorRangeToMark( unsigned gc_epoch, Tagged<DescriptorArray> array) { const auto current_epoch = gc_epoch & Epoch::kMask; while (true) { const RawGCStateType raw_gc_state = array->raw_gc_state(kRelaxedLoad); const DescriptorIndex marked = Marked::decode(raw_gc_state); const DescriptorIndex delta = Delta::decode(raw_gc_state); // We may encounter an array here that was merely pushed to the marker. In // such a case, we process all descriptors (if we succeed). The cases to // check are: // 1. Epoch mismatch: Happens when descriptors survive a GC cycle. // 2. Epoch matches but marked/delta is 0: Can happen when descriptors are // newly allocated in the current cycle. if (current_epoch != Epoch::decode(raw_gc_state) || (marked + delta) == 0) { // In case number of descriptors is 0 and we reach the array through roots // marking, mark also slack to get a proper transition from 0 marked to X // marked. Otherwise, we would need to treat the state [0,0[ for marked // and delta as valid state which leads to double-accounting through the // marking barrier (when nof>1 in the barrier). const int16_t number_of_descriptors = array->number_of_descriptors() ? array->number_of_descriptors() : array->number_of_all_descriptors(); DCHECK_GT(number_of_descriptors, 0); if (SwapState(array, raw_gc_state, NewState(current_epoch, number_of_descriptors, 0))) { return {0, number_of_descriptors}; } continue; } // The delta is 0, so everything has been processed. Return the marked // indices. if (delta == 0) { return {marked, marked}; } if (SwapState(array, raw_gc_state, NewState(current_epoch, marked + delta, 0))) { return {marked, marked + delta}; } } } } // namespace internal } // namespace v8 #include "src/objects/object-macros-undef.h" #endif // V8_OBJECTS_DESCRIPTOR_ARRAY_INL_H_