/* * Copyright (c) 2026-present, the Ladybird developers. * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include namespace JS { GC_DEFINE_ALLOCATOR(DescriptorArray); static u32 property_key_hash(PropertyKey const& property_key) { return AK::Traits::hash(property_key); } DescriptorArray::DescriptorArray(DescriptorArray const& other, u32 descriptor_count) { VERIFY(descriptor_count <= max_descriptor_count); m_entries.ensure_capacity(descriptor_count); m_entry_indices_by_enum_index.ensure_capacity(descriptor_count); other.for_each_in_insertion_order([&](auto const& property_key, auto const& metadata) { set(property_key, metadata, m_entries.size()); }, descriptor_count); } Optional DescriptorArray::lookup(PropertyKey const& property_key, u32 descriptor_count) const { auto index = find(property_key, descriptor_count); if (!index.has_value()) return {}; return m_entries[*index].metadata(); } void DescriptorArray::set(PropertyKey const& property_key, PropertyMetadata metadata, u32 enum_index) { VERIFY(enum_index < max_descriptor_count); if (auto existing_index = find(property_key, enum_index + 1); existing_index.has_value()) { auto& entry = m_entries[*existing_index]; entry.offset = metadata.offset; entry.attributes = metadata.attributes.bits(); return; } auto insertion_index = find_insertion_index(property_key); VERIFY(insertion_index <= NumericLimits::max()); VERIFY(enum_index == m_entry_indices_by_enum_index.size()); for (auto& entry_index : m_entry_indices_by_enum_index) { if (entry_index >= insertion_index) ++entry_index; } m_entries.insert(insertion_index, { property_key, metadata.offset, metadata.attributes.bits(), static_cast(enum_index) }); m_entry_indices_by_enum_index.append(static_cast(insertion_index)); } void DescriptorArray::set_attributes(PropertyKey const& property_key, PropertyAttributes attributes, u32 descriptor_count) { auto index = find(property_key, descriptor_count); VERIFY(index.has_value()); m_entries[*index].attributes = attributes.bits(); } void DescriptorArray::remove(PropertyKey const& property_key, u32 descriptor_count) { auto index = find(property_key, descriptor_count); VERIFY(index.has_value()); auto removed_offset = m_entries[*index].offset; auto removed_enum_index = m_entries[*index].enum_index; m_entries.remove(*index); m_entry_indices_by_enum_index.remove(removed_enum_index); for (auto& entry_index : m_entry_indices_by_enum_index) { if (entry_index > *index) --entry_index; } for (auto& entry : m_entries) { if (entry.enum_index >= descriptor_count) continue; if (entry.offset > removed_offset) --entry.offset; if (entry.enum_index > removed_enum_index) --entry.enum_index; } } void DescriptorArray::for_each_in_insertion_order(Function const& callback, u32 descriptor_count) const { VERIFY(descriptor_count <= m_entry_indices_by_enum_index.size()); for (u32 enum_index = 0; enum_index < descriptor_count; ++enum_index) { auto const& entry = m_entries[m_entry_indices_by_enum_index[enum_index]]; VERIFY(entry.enum_index == enum_index); auto metadata = entry.metadata(); callback(entry.property_key, metadata); } } void DescriptorArray::visit_edges(Visitor& visitor) { Base::visit_edges(visitor); for (auto const& entry : m_entries) entry.property_key.visit_edges(visitor); } size_t DescriptorArray::external_memory_size() const { return vector_external_memory_size(m_entries) + vector_external_memory_size(m_entry_indices_by_enum_index); } Optional DescriptorArray::find(PropertyKey const& property_key, u32 descriptor_count) const { if (m_entries.size() <= 16) { for (size_t i = 0; i < m_entries.size(); ++i) { auto const& entry = m_entries[i]; if (entry.enum_index < descriptor_count && entry.property_key == property_key) return i; } return {}; } auto hash = property_key_hash(property_key); size_t low = 0; size_t high = m_entries.size(); while (low < high) { auto middle = low + (high - low) / 2; if (property_key_hash(m_entries[middle].property_key) < hash) low = middle + 1; else high = middle; } for (auto i = low; i < m_entries.size() && property_key_hash(m_entries[i].property_key) == hash; ++i) { auto const& entry = m_entries[i]; if (entry.enum_index < descriptor_count && entry.property_key == property_key) return i; } return {}; } size_t DescriptorArray::find_insertion_index(PropertyKey const& property_key) const { auto hash = property_key_hash(property_key); size_t low = 0; size_t high = m_entries.size(); while (low < high) { auto middle = low + (high - low) / 2; if (property_key_hash(m_entries[middle].property_key) <= hash) low = middle + 1; else high = middle; } return low; } }