1
0
Fork 0
mirror of https://github.com/LadybirdBrowser/ladybird.git synced 2025-06-11 18:20:43 +09:00

LibJS: Introduce "dictionary" mode for object shapes

This is similar to "unique" shapes, which were removed in commit
3d92c26445.

The key difference is that dictionary shapes don't have a serial number,
but instead have a "cacheable" flag.

Shapes become dictionaries after 64 transitions have occurred, at which
point no further transitions occur.

As long as properties are only added to a dictionary shape, it remains
cacheable. (Since if we've cached the shape pointer in an IC somewhere,
we know the IC is still valid.)

Deleting a property from a dictionary shape causes it to become an
uncacheable dictionary.

Note that deleting a property from a non-dictionary shape still performs
a delete transition.

This fixes an issue on Discord where Object.freeze() would eventually
OOM us, since they add more than 16000 properties to a single object
before freezing it.

It also yields a 15% speedup on Octane/pdfjs.js :^)
This commit is contained in:
Andreas Kling 2023-12-16 11:34:01 +01:00
parent 8255a1a5ee
commit 1e90379008
Notes: sideshowbarker 2024-07-17 18:46:30 +09:00
4 changed files with 94 additions and 4 deletions

View file

@ -875,7 +875,7 @@ ThrowCompletionOr<Value> Object::internal_get(PropertyKey const& property_key, V
// 3. If IsDataDescriptor(desc) is true, return desc.[[Value]].
if (descriptor->is_data_descriptor()) {
// Non-standard: If the caller has requested cacheable metadata and the property is an own property, fill it in.
if (cacheable_metadata && descriptor->property_offset.has_value()) {
if (cacheable_metadata && descriptor->property_offset.has_value() && shape().is_cacheable()) {
*cacheable_metadata = CacheablePropertyMetadata {
.type = CacheablePropertyMetadata::Type::OwnProperty,
.property_offset = descriptor->property_offset.value(),
@ -969,7 +969,7 @@ ThrowCompletionOr<bool> Object::ordinary_set_with_own_descriptor(PropertyKey con
// iii. Let valueDesc be the PropertyDescriptor { [[Value]]: V }.
auto value_descriptor = PropertyDescriptor { .value = value };
if (cacheable_metadata && own_descriptor.has_value() && own_descriptor->property_offset.has_value()) {
if (cacheable_metadata && own_descriptor.has_value() && own_descriptor->property_offset.has_value() && shape().is_cacheable()) {
*cacheable_metadata = CacheablePropertyMetadata {
.type = CacheablePropertyMetadata::Type::OwnProperty,
.property_offset = own_descriptor->property_offset.value(),
@ -1158,13 +1158,23 @@ void Object::storage_set(PropertyKey const& property_key, ValueAndAttributes con
auto metadata = shape().lookup(property_key_string_or_symbol);
if (!metadata.has_value()) {
set_shape(*m_shape->create_put_transition(property_key_string_or_symbol, attributes));
static constexpr size_t max_transitions_before_converting_to_dictionary = 64;
if (!m_shape->is_dictionary() && m_shape->property_count() >= max_transitions_before_converting_to_dictionary)
set_shape(m_shape->create_cacheable_dictionary_transition());
if (m_shape->is_dictionary())
m_shape->add_property_without_transition(property_key_string_or_symbol, attributes);
else
set_shape(*m_shape->create_put_transition(property_key_string_or_symbol, attributes));
m_storage.append(value);
return;
}
if (attributes != metadata->attributes) {
set_shape(*m_shape->create_configure_transition(property_key_string_or_symbol, attributes));
if (m_shape->is_dictionary())
m_shape->set_property_attributes_without_transition(property_key_string_or_symbol, attributes);
else
set_shape(*m_shape->create_configure_transition(property_key_string_or_symbol, attributes));
}
m_storage[metadata->offset] = value;
@ -1186,6 +1196,14 @@ void Object::storage_delete(PropertyKey const& property_key)
auto metadata = shape().lookup(property_key.to_string_or_symbol());
VERIFY(metadata.has_value());
if (m_shape->is_cacheable_dictionary()) {
m_shape = m_shape->create_uncacheable_dictionary_transition();
}
if (m_shape->is_uncacheable_dictionary()) {
m_shape->remove_property_without_transition(property_key.to_string_or_symbol(), metadata->offset);
m_storage.remove(metadata->offset);
return;
}
m_shape = m_shape->create_delete_transition(property_key.to_string_or_symbol());
m_storage.remove(metadata->offset);
}