1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 11:48:10 +00:00

LibJS: Stop making shapes unique

We previously had a concept of unique shapes, which meant that they
couldn't be shared between multiple objects.

Object shapes became unique in three situations:

- They were the shape of the global object.
- They had more than 100 properties added to them.
- They had one or more properties deleted from them.

Unfortunately, unique shapes presented an annoying problem for inline
caches, and we added a "unique shape serial number" for being able to
tell that a unique shape had been mutated.

This patch gets rid of the concept of unique shapes, simplifying all
the caching code, since inline caches can now simply perform a shape
check and then we're good.

To make this possible, we now have the concept of delete transitions,
which occur when a property is deleted from a shape.

Note that this patch by itself introduces a performance regression in
some situtations, since we now create a lot more shapes, and marking
their property keys can be very heavy. This will be addressed in a
subsequent patch.
This commit is contained in:
Andreas Kling 2023-12-11 14:29:40 +01:00
parent ef86cf4646
commit 3d92c26445
8 changed files with 63 additions and 214 deletions

View file

@ -1158,26 +1158,13 @@ void Object::storage_set(PropertyKey const& property_key, ValueAndAttributes con
auto metadata = shape().lookup(property_key_string_or_symbol);
if (!metadata.has_value()) {
if (!m_shape->is_unique() && shape().property_count() > 100) {
// If you add more than 100 properties to an object, let's stop doing
// transitions to avoid filling up the heap with shapes.
ensure_shape_is_unique();
}
if (m_shape->is_unique())
m_shape->add_property_to_unique_shape(property_key_string_or_symbol, attributes);
else
set_shape(*m_shape->create_put_transition(property_key_string_or_symbol, attributes));
set_shape(*m_shape->create_put_transition(property_key_string_or_symbol, attributes));
m_storage.append(value);
return;
}
if (attributes != metadata->attributes) {
if (m_shape->is_unique())
m_shape->reconfigure_property_in_unique_shape(property_key_string_or_symbol, attributes);
else
set_shape(*m_shape->create_configure_transition(property_key_string_or_symbol, attributes));
set_shape(*m_shape->create_configure_transition(property_key_string_or_symbol, attributes));
}
m_storage[metadata->offset] = value;
@ -1199,9 +1186,7 @@ void Object::storage_delete(PropertyKey const& property_key)
auto metadata = shape().lookup(property_key.to_string_or_symbol());
VERIFY(metadata.has_value());
ensure_shape_is_unique();
shape().remove_property_from_unique_shape(property_key.to_string_or_symbol(), metadata->offset);
m_shape = m_shape->create_delete_transition(property_key.to_string_or_symbol());
m_storage.remove(metadata->offset);
}
@ -1209,11 +1194,7 @@ void Object::set_prototype(Object* new_prototype)
{
if (prototype() == new_prototype)
return;
auto& shape = this->shape();
if (shape.is_unique())
shape.set_prototype_without_transition(new_prototype);
else
m_shape = shape.create_prototype_transition(new_prototype);
m_shape = shape().create_prototype_transition(new_prototype);
}
void Object::define_native_accessor(Realm& realm, PropertyKey const& property_key, Function<ThrowCompletionOr<Value>(VM&)> getter, Function<ThrowCompletionOr<Value>(VM&)> setter, PropertyAttributes attribute)
@ -1255,14 +1236,6 @@ void Object::define_intrinsic_accessor(PropertyKey const& property_key, Property
intrinsics.set(property_key.as_string(), move(accessor));
}
void Object::ensure_shape_is_unique()
{
if (shape().is_unique())
return;
m_shape = m_shape->create_unique_clone();
}
// Simple side-effect free property lookup, following the prototype chain. Non-standard.
Value Object::get_without_side_effects(PropertyKey const& property_key) const
{