1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 19:37:34 +00:00

LibJS: Implement basic support for the "delete" operator

It turns out "delete" is actually a unary op :)
This patch implements deletion of object properties, it doesn't yet
work for casually deleting properties from the global object.

When deleting a property from an object, we switch that object to
having a unique shape, no longer sharing shapes with others.
Once an object has a unique shape, it no longer needs to care about
shape transitions.
This commit is contained in:
Andreas Kling 2020-04-26 13:53:40 +02:00
parent 1617be1e6f
commit f897c41092
9 changed files with 190 additions and 8 deletions

View file

@ -71,6 +71,10 @@ void Object::set_prototype(Object* new_prototype)
{
if (prototype() == new_prototype)
return;
if (shape().is_unique()) {
shape().set_prototype_without_transition(new_prototype);
return;
}
m_shape = m_shape->create_prototype_transition(new_prototype);
}
@ -113,8 +117,11 @@ void Object::put_own_property(Object& this_object, const FlyString& property_nam
{
auto metadata = shape().lookup(property_name);
if (!metadata.has_value()) {
auto* new_shape = m_shape->create_put_transition(property_name, attributes);
set_shape(*new_shape);
if (m_shape->is_unique()) {
m_shape->add_property_to_unique_shape(property_name, attributes);
} else {
set_shape(*m_shape->create_put_transition(property_name, attributes));
}
metadata = shape().lookup(property_name);
ASSERT(metadata.has_value());
}
@ -126,8 +133,11 @@ void Object::put_own_property(Object& this_object, const FlyString& property_nam
}
if (mode == PutOwnPropertyMode::DefineProperty && attributes != metadata.value().attributes) {
auto* new_shape = m_shape->create_configure_transition(property_name, attributes);
set_shape(*new_shape);
if (m_shape->is_unique()) {
m_shape->reconfigure_property_in_unique_shape(property_name, attributes);
} else {
set_shape(*m_shape->create_configure_transition(property_name, attributes));
}
metadata = shape().lookup(property_name);
dbg() << "Reconfigured property " << property_name << ", new shape says offset is " << metadata.value().offset << " and my storage capacity is " << m_storage.size();
@ -154,6 +164,39 @@ void Object::put_own_property(Object& this_object, const FlyString& property_nam
}
}
Value Object::delete_property(PropertyName property_name)
{
ASSERT(property_name.is_valid());
if (property_name.is_number()) {
if (property_name.as_number() < static_cast<i32>(elements().size())) {
elements()[property_name.as_number()] = {};
return Value(true);
}
return Value(true);
}
auto metadata = shape().lookup(property_name.as_string());
if (!metadata.has_value())
return Value(true);
if (!(metadata.value().attributes & Attribute::Configurable))
return Value(false);
size_t deleted_offset = metadata.value().offset;
ensure_shape_is_unique();
shape().remove_property_from_unique_shape(property_name.as_string(), deleted_offset);
m_storage.remove(deleted_offset);
return Value(true);
}
void Object::ensure_shape_is_unique()
{
if (shape().is_unique())
return;
m_shape = m_shape->create_unique_clone();
}
Value Object::get_by_index(i32 property_index) const
{
if (property_index < 0)

View file

@ -46,6 +46,8 @@ public:
Shape& shape() { return *m_shape; }
const Shape& shape() const { return *m_shape; }
Value delete_property(PropertyName);
virtual Value get_by_index(i32 property_index) const;
Value get(const FlyString& property_name) const;
Value get(PropertyName) const;
@ -102,6 +104,7 @@ public:
private:
void set_shape(Shape&);
void ensure_shape_is_unique();
Shape* m_shape { nullptr };
Vector<Value> m_storage;

View file

@ -75,8 +75,9 @@ Value ObjectConstructor::get_own_property_names(Interpreter& interpreter)
result->elements().append(js_string(interpreter, String::number(i)));
}
for (auto& it : object->shape().property_table())
for (auto& it : object->shape().property_table()) {
result->elements().append(js_string(interpreter, it.key));
}
return result;
}

View file

@ -29,6 +29,17 @@
namespace JS {
Shape* Shape::create_unique_clone() const
{
auto* new_shape = heap().allocate<Shape>();
new_shape->m_unique = true;
new_shape->m_prototype = m_prototype;
ensure_property_table();
new_shape->ensure_property_table();
(*new_shape->m_property_table) = *m_property_table;
return new_shape;
}
Shape* Shape::create_put_transition(const FlyString& property_name, u8 attributes)
{
TransitionKey key { property_name, attributes };
@ -89,7 +100,10 @@ void Shape::visit_children(Cell::Visitor& visitor)
Optional<PropertyMetadata> Shape::lookup(const FlyString& property_name) const
{
return property_table().get(property_name);
auto property = property_table().get(property_name);
if (!property.has_value())
return {};
return property;
}
const HashMap<FlyString, PropertyMetadata>& Shape::property_table() const
@ -134,4 +148,32 @@ void Shape::ensure_property_table() const
}
}
void Shape::add_property_to_unique_shape(const FlyString& property_name, u8 attributes)
{
ASSERT(is_unique());
ASSERT(m_property_table);
ASSERT(!m_property_table->contains(property_name));
m_property_table->set(property_name, { m_property_table->size(), attributes });
}
void Shape::reconfigure_property_in_unique_shape(const FlyString& property_name, u8 attributes)
{
ASSERT(is_unique());
ASSERT(m_property_table);
ASSERT(m_property_table->contains(property_name));
m_property_table->set(property_name, { m_property_table->size(), attributes });
}
void Shape::remove_property_from_unique_shape(const FlyString& property_name, size_t offset)
{
ASSERT(is_unique());
ASSERT(m_property_table);
m_property_table->remove(property_name);
for (auto& it : *m_property_table) {
ASSERT(it.value.offset != offset);
if (it.value.offset > offset)
--it.value.offset;
}
}
}

View file

@ -78,6 +78,9 @@ public:
Shape* create_configure_transition(const FlyString& name, u8 attributes);
Shape* create_prototype_transition(Object* new_prototype);
bool is_unique() const { return m_unique; }
Shape* create_unique_clone() const;
Object* prototype() { return m_prototype; }
const Object* prototype() const { return m_prototype; }
@ -87,6 +90,10 @@ public:
void set_prototype_without_transition(Object* new_prototype) { m_prototype = new_prototype; }
void remove_property_from_unique_shape(const FlyString&, size_t offset);
void add_property_to_unique_shape(const FlyString&, u8 attributes);
void reconfigure_property_in_unique_shape(const FlyString& property_name, u8 attributes);
private:
virtual const char* class_name() const override { return "Shape"; }
virtual void visit_children(Visitor&) override;
@ -99,6 +106,7 @@ private:
Shape* m_previous { nullptr };
FlyString m_property_name;
u8 m_attributes { 0 };
bool m_unique { false };
Object* m_prototype { nullptr };
TransitionType m_transition_type { TransitionType::Invalid };
};