From 5ad5322f6a1f9119e676ea74499ec6d19b085047 Mon Sep 17 00:00:00 2001 From: Matthew Olsson Date: Tue, 2 Jun 2020 17:13:09 -0700 Subject: [PATCH] LibJS: Distinguish between omitted descriptor attributes and false ones When calling Object.defineProperty, there is now a difference between omitting a descriptor attribute and specifying that it is false. For example, "{}" and "{ configurable: false }" will have different attribute values. --- Libraries/LibJS/AST.cpp | 2 +- Libraries/LibJS/Runtime/IndexedProperties.cpp | 14 +-- Libraries/LibJS/Runtime/IndexedProperties.h | 20 ++-- Libraries/LibJS/Runtime/Object.cpp | 81 ++++++++++------- Libraries/LibJS/Runtime/Object.h | 12 +-- Libraries/LibJS/Runtime/PropertyAttributes.h | 91 +++++++++++++++++++ Libraries/LibJS/Runtime/Shape.cpp | 15 ++- Libraries/LibJS/Runtime/Shape.h | 34 +++---- 8 files changed, 186 insertions(+), 83 deletions(-) create mode 100644 Libraries/LibJS/Runtime/PropertyAttributes.h diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp index 1aebc919449..1bb684d7ee5 100644 --- a/Libraries/LibJS/AST.cpp +++ b/Libraries/LibJS/AST.cpp @@ -1304,7 +1304,7 @@ Value ObjectExpression::execute(Interpreter& interpreter) const auto& obj_to_spread = key_result.as_object(); for (auto& it : obj_to_spread.shape().property_table_ordered()) { - if (it.value.attributes & Attribute::Enumerable) + if (it.value.attributes.is_enumerable()) object->define_property(it.key, obj_to_spread.get(it.key)); } } else if (key_result.is_string()) { diff --git a/Libraries/LibJS/Runtime/IndexedProperties.cpp b/Libraries/LibJS/Runtime/IndexedProperties.cpp index 5da0f570b66..3ef6f16166f 100644 --- a/Libraries/LibJS/Runtime/IndexedProperties.cpp +++ b/Libraries/LibJS/Runtime/IndexedProperties.cpp @@ -47,7 +47,7 @@ Optional SimpleIndexedPropertyStorage::get(u32 index) const return ValueAndAttributes { m_packed_elements[index], default_attributes }; } -void SimpleIndexedPropertyStorage::put(u32 index, Value value, u8 attributes) +void SimpleIndexedPropertyStorage::put(u32 index, Value value, PropertyAttributes attributes) { ASSERT(attributes == default_attributes); ASSERT(index < SPARSE_ARRAY_THRESHOLD); @@ -66,7 +66,7 @@ void SimpleIndexedPropertyStorage::remove(u32 index) m_packed_elements[index] = {}; } -void SimpleIndexedPropertyStorage::insert(u32 index, Value value, u8 attributes) +void SimpleIndexedPropertyStorage::insert(u32 index, Value value, PropertyAttributes attributes) { ASSERT(attributes == default_attributes); ASSERT(index < SPARSE_ARRAY_THRESHOLD); @@ -122,7 +122,7 @@ Optional GenericIndexedPropertyStorage::get(u32 index) const return m_sparse_elements.get(index); } -void GenericIndexedPropertyStorage::put(u32 index, Value value, u8 attributes) +void GenericIndexedPropertyStorage::put(u32 index, Value value, PropertyAttributes attributes) { if (index >= m_array_size) m_array_size = index + 1; @@ -151,7 +151,7 @@ void GenericIndexedPropertyStorage::remove(u32 index) } } -void GenericIndexedPropertyStorage::insert(u32 index, Value value, u8 attributes) +void GenericIndexedPropertyStorage::insert(u32 index, Value value, PropertyAttributes attributes) { if (index >= m_array_size) { put(index, value, attributes); @@ -281,7 +281,7 @@ Optional IndexedProperties::get(Object* this_object, u32 ind return result; } -void IndexedProperties::put(Object* this_object, u32 index, Value value, u8 attributes, bool evaluate_accessors) +void IndexedProperties::put(Object* this_object, u32 index, Value value, PropertyAttributes attributes, bool evaluate_accessors) { if (m_storage->is_simple_storage() && (index >= SPARSE_ARRAY_THRESHOLD || attributes != default_attributes)) switch_to_generic_storage(); @@ -304,13 +304,13 @@ bool IndexedProperties::remove(u32 index) auto result = m_storage->get(index); if (!result.has_value()) return true; - if (!(result.value().attributes & Attribute::Configurable)) + if (!result.value().attributes.is_configurable()) return false; m_storage->remove(index); return true; } -void IndexedProperties::insert(u32 index, Value value, u8 attributes) +void IndexedProperties::insert(u32 index, Value value, PropertyAttributes attributes) { if (m_storage->is_simple_storage() && (index >= SPARSE_ARRAY_THRESHOLD || attributes != default_attributes || array_like_size() == SPARSE_ARRAY_THRESHOLD)) switch_to_generic_storage(); diff --git a/Libraries/LibJS/Runtime/IndexedProperties.h b/Libraries/LibJS/Runtime/IndexedProperties.h index 9456a9dafe2..916a7747c2c 100644 --- a/Libraries/LibJS/Runtime/IndexedProperties.h +++ b/Libraries/LibJS/Runtime/IndexedProperties.h @@ -37,7 +37,7 @@ const u32 MIN_PACKED_RESIZE_AMOUNT = 20; struct ValueAndAttributes { Value value; - u8 attributes { default_attributes }; + PropertyAttributes attributes { default_attributes }; }; class IndexedProperties; @@ -50,10 +50,10 @@ public: virtual bool has_index(u32 index) const = 0; virtual Optional get(u32 index) const = 0; - virtual void put(u32 index, Value value, u8 attributes = default_attributes) = 0; + virtual void put(u32 index, Value value, PropertyAttributes attributes = default_attributes) = 0; virtual void remove(u32 index) = 0; - virtual void insert(u32 index, Value value, u8 attributes = default_attributes) = 0; + virtual void insert(u32 index, Value value, PropertyAttributes attributes = default_attributes) = 0; virtual ValueAndAttributes take_first() = 0; virtual ValueAndAttributes take_last() = 0; @@ -71,10 +71,10 @@ public: virtual bool has_index(u32 index) const override; virtual Optional get(u32 index) const override; - virtual void put(u32 index, Value value, u8 attributes = default_attributes) override; + virtual void put(u32 index, Value value, PropertyAttributes attributes = default_attributes) override; virtual void remove(u32 index) override; - virtual void insert(u32 index, Value value, u8 attributes = default_attributes) override; + virtual void insert(u32 index, Value value, PropertyAttributes attributes = default_attributes) override; virtual ValueAndAttributes take_first() override; virtual ValueAndAttributes take_last() override; @@ -98,10 +98,10 @@ public: virtual bool has_index(u32 index) const override; virtual Optional get(u32 index) const override; - virtual void put(u32 index, Value value, u8 attributes = default_attributes) override; + virtual void put(u32 index, Value value, PropertyAttributes attributes = default_attributes) override; virtual void remove(u32 index) override; - virtual void insert(u32 index, Value value, u8 attributes = default_attributes) override; + virtual void insert(u32 index, Value value, PropertyAttributes attributes = default_attributes) override; virtual ValueAndAttributes take_first() override; virtual ValueAndAttributes take_last() override; @@ -146,14 +146,14 @@ public: bool has_index(u32 index) const { return m_storage->has_index(index); } Optional get(Object* this_object, u32 index, bool evaluate_accessors = true) const; - void put(Object* this_object, u32 index, Value value, u8 attributes = default_attributes, bool evaluate_accessors = true); + void put(Object* this_object, u32 index, Value value, PropertyAttributes attributes = default_attributes, bool evaluate_accessors = true); bool remove(u32 index); - void insert(u32 index, Value value, u8 attributes = default_attributes); + void insert(u32 index, Value value, PropertyAttributes attributes = default_attributes); ValueAndAttributes take_first(Object* this_object); ValueAndAttributes take_last(Object* this_object); - void append(Value value, u8 attributes = default_attributes) { put(nullptr, array_like_size(), value, attributes, false); } + void append(Value value, PropertyAttributes attributes = default_attributes) { put(nullptr, array_like_size(), value, attributes, false); } void append_all(Object* this_object, const IndexedProperties& properties, bool evaluate_accessors = true); IndexedPropertyIterator begin(bool skip_empty = true) const { return IndexedPropertyIterator(*this, 0, skip_empty); }; diff --git a/Libraries/LibJS/Runtime/Object.cpp b/Libraries/LibJS/Runtime/Object.cpp index e62c68cfaa4..2b3995f98ad 100644 --- a/Libraries/LibJS/Runtime/Object.cpp +++ b/Libraries/LibJS/Runtime/Object.cpp @@ -123,7 +123,7 @@ Value Object::get_own_property(const Object& this_object, PropertyName property_ return value_here; } -Value Object::get_own_properties(const Object& this_object, GetOwnPropertyMode kind, u8 attributes) const +Value Object::get_own_properties(const Object& this_object, GetOwnPropertyMode kind, PropertyAttributes attributes) const { auto* properties_array = Array::create(interpreter().global_object()); @@ -168,7 +168,7 @@ Value Object::get_own_properties(const Object& this_object, GetOwnPropertyMode k } for (auto& it : this_object.shape().property_table_ordered()) { - if (it.value.attributes & attributes) { + if (it.value.attributes.bits() & attributes.bits()) { size_t offset = it.value.offset + property_index; if (kind == GetOwnPropertyMode::Key) { @@ -194,7 +194,7 @@ Value Object::get_own_properties(const Object& this_object, GetOwnPropertyMode k Value Object::get_own_property_descriptor(PropertyName property_name) const { Value value; - u8 attributes; + PropertyAttributes attributes; if (property_name.is_number()) { auto existing_value = m_indexed_properties.get(nullptr, property_name.as_number(), false); @@ -214,12 +214,12 @@ Value Object::get_own_property_descriptor(PropertyName property_name) const } auto* descriptor = Object::create_empty(interpreter(), interpreter().global_object()); - descriptor->define_property("enumerable", Value((attributes & Attribute::Enumerable) != 0)); - descriptor->define_property("configurable", Value((attributes & Attribute::Configurable) != 0)); + descriptor->define_property("enumerable", Value(attributes.is_enumerable())); + descriptor->define_property("configurable", Value(attributes.is_configurable())); if (value.is_object() && value.as_object().is_native_property()) { auto result = call_native_property_getter(const_cast(this), value); descriptor->define_property("value", result); - descriptor->define_property("writable", Value((attributes & Attribute::Writable) != 0)); + descriptor->define_property("writable", Value(attributes.is_writable())); } else if (value.is_accessor()) { auto& pair = value.as_accessor(); if (pair.getter()) @@ -228,7 +228,7 @@ Value Object::get_own_property_descriptor(PropertyName property_name) const descriptor->define_property("set", pair.setter()); } else { descriptor->define_property("value", value.value_or(js_undefined())); - descriptor->define_property("writable", Value((attributes & Attribute::Writable) != 0)); + descriptor->define_property("writable", Value(attributes.is_writable())); } return descriptor; } @@ -242,13 +242,25 @@ void Object::set_shape(Shape& new_shape) bool Object::define_property(const FlyString& property_name, const Object& descriptor, bool throw_exceptions) { bool is_accessor_property = descriptor.has_property("get") || descriptor.has_property("set"); - u8 configurable = descriptor.get("configurable").value_or(Value(false)).to_boolean() * Attribute::Configurable; - if (interpreter().exception()) - return {}; - u8 enumerable = descriptor.get("enumerable").value_or(Value(false)).to_boolean() * Attribute::Enumerable; - if (interpreter().exception()) - return {}; - u8 attributes = configurable | enumerable; + PropertyAttributes attributes; + if (descriptor.has_property("configurable")) { + if (interpreter().exception()) + return false; + attributes.set_has_configurable(); + if (descriptor.get("configurable").value_or(Value(false)).to_boolean()) + attributes.set_configurable(); + if (interpreter().exception()) + return false; + } + if (descriptor.has_property("enumerable")) { + if (interpreter().exception()) + return false; + attributes.set_has_enumerable(); + if (descriptor.get("enumerable").value_or(Value(false)).to_boolean()) + attributes.set_enumerable(); + if (interpreter().exception()) + return false; + } if (is_accessor_property) { if (descriptor.has_property("value") || descriptor.has_property("writable")) { @@ -291,10 +303,17 @@ bool Object::define_property(const FlyString& property_name, const Object& descr auto value = descriptor.get("value"); if (interpreter().exception()) return {}; - u8 writable = descriptor.get("writable").value_or(Value(false)).to_boolean() * Attribute::Writable; + if (descriptor.has_property("writable")) { + if (interpreter().exception()) + return false; + attributes.set_has_writable(); + if (descriptor.get("writable").value_or(Value(false)).to_boolean()) + attributes.set_writable(); + if (interpreter().exception()) + return false; + } if (interpreter().exception()) return {}; - attributes |= writable; dbg() << "Defining new property " << property_name << " with data descriptor { attributes=" << attributes << ", value=" << (value.is_empty() ? "" : value.to_string_without_side_effects()) << " }"; @@ -302,7 +321,7 @@ bool Object::define_property(const FlyString& property_name, const Object& descr return define_property(property_name, value, attributes, throw_exceptions); } -bool Object::define_property(PropertyName property_name, Value value, u8 attributes, bool throw_exceptions) +bool Object::define_property(PropertyName property_name, Value value, PropertyAttributes attributes, bool throw_exceptions) { if (property_name.is_number()) return put_own_property_by_index(*this, property_name.as_number(), value, attributes, PutOwnPropertyMode::DefineProperty, throw_exceptions); @@ -313,7 +332,7 @@ bool Object::define_property(PropertyName property_name, Value value, u8 attribu return put_own_property(*this, property_name.as_string(), value, attributes, PutOwnPropertyMode::DefineProperty, throw_exceptions); } -bool Object::put_own_property(Object& this_object, const FlyString& property_name, Value value, u8 attributes, PutOwnPropertyMode mode, bool throw_exceptions) +bool Object::put_own_property(Object& this_object, const FlyString& property_name, Value value, PropertyAttributes attributes, PutOwnPropertyMode mode, bool throw_exceptions) { ASSERT(!(mode == PutOwnPropertyMode::Put && value.is_accessor())); @@ -327,9 +346,9 @@ bool Object::put_own_property(Object& this_object, const FlyString& property_nam if (value.is_accessor()) { auto& accessor = value.as_accessor(); if (accessor.getter()) - attributes |= Attribute::HasGet; + attributes.set_has_getter(); if (accessor.setter()) - attributes |= Attribute::HasSet; + attributes.set_has_setter(); } auto metadata = shape().lookup(property_name); @@ -352,7 +371,7 @@ bool Object::put_own_property(Object& this_object, const FlyString& property_nam ASSERT(metadata.has_value()); } - if (!new_property && mode == PutOwnPropertyMode::DefineProperty && !(metadata.value().attributes & Attribute::Configurable) && attributes != metadata.value().attributes) { + if (!new_property && mode == PutOwnPropertyMode::DefineProperty && !metadata.value().attributes.is_configurable() && attributes != metadata.value().attributes) { dbg() << "Disallow reconfig of non-configurable property"; if (throw_exceptions) interpreter().throw_exception(String::format("Cannot change attributes of non-configurable property '%s'", property_name.characters())); @@ -371,7 +390,7 @@ bool Object::put_own_property(Object& this_object, const FlyString& property_nam } auto value_here = m_storage[metadata.value().offset]; - if (!new_property && mode == PutOwnPropertyMode::Put && !value_here.is_accessor() && !(metadata.value().attributes & Attribute::Writable)) { + if (!new_property && mode == PutOwnPropertyMode::Put && !value_here.is_accessor() && !metadata.value().attributes.is_writable()) { dbg() << "Disallow write to non-writable property"; return false; } @@ -387,7 +406,7 @@ bool Object::put_own_property(Object& this_object, const FlyString& property_nam return true; } -bool Object::put_own_property_by_index(Object& this_object, u32 property_index, Value value, u8 attributes, PutOwnPropertyMode mode, bool throw_exceptions) +bool Object::put_own_property_by_index(Object& this_object, u32 property_index, Value value, PropertyAttributes attributes, PutOwnPropertyMode mode, bool throw_exceptions) { ASSERT(!(mode == PutOwnPropertyMode::Put && value.is_accessor())); @@ -401,16 +420,16 @@ bool Object::put_own_property_by_index(Object& this_object, u32 property_index, if (value.is_accessor()) { auto& accessor = value.as_accessor(); if (accessor.getter()) - attributes |= Attribute::HasGet; + attributes.set_has_getter(); if (accessor.setter()) - attributes |= Attribute::HasSet; + attributes.set_has_setter(); } auto existing_property = m_indexed_properties.get(nullptr, property_index, false); auto new_property = !existing_property.has_value(); - auto existing_attributes = new_property ? 0 : existing_property.value().attributes; + PropertyAttributes existing_attributes = new_property ? 0 : existing_property.value().attributes; - if (!new_property && mode == PutOwnPropertyMode::DefineProperty && !(existing_attributes & Attribute::Configurable) && attributes != existing_attributes) { + if (!new_property && mode == PutOwnPropertyMode::DefineProperty && !existing_attributes.is_configurable() && attributes != existing_attributes) { dbg() << "Disallow reconfig of non-configurable property"; if (throw_exceptions) interpreter().throw_exception(String::format("Cannot change attributes of non-configurable property %d", property_index)); @@ -418,7 +437,7 @@ bool Object::put_own_property_by_index(Object& this_object, u32 property_index, } auto value_here = new_property ? Value() : existing_property.value().value; - if (!new_property && mode == PutOwnPropertyMode::Put && !value_here.is_accessor() && !(existing_attributes & Attribute::Writable)) { + if (!new_property && mode == PutOwnPropertyMode::Put && !value_here.is_accessor() && !existing_attributes.is_writable()) { dbg() << "Disallow write to non-writable property"; return false; } @@ -442,7 +461,7 @@ Value Object::delete_property(PropertyName property_name) auto metadata = shape().lookup(property_name.as_string()); if (!metadata.has_value()) return Value(true); - if (!(metadata.value().attributes & Attribute::Configurable)) + if (!metadata.value().attributes.is_configurable()) return Value(false); size_t deleted_offset = metadata.value().offset; @@ -565,7 +584,7 @@ bool Object::put(PropertyName property_name, Value value) return put_own_property(*this, property_string, value, default_attributes, PutOwnPropertyMode::Put); } -bool Object::define_native_function(const FlyString& property_name, AK::Function native_function, i32 length, u8 attribute) +bool Object::define_native_function(const FlyString& property_name, AK::Function native_function, i32 length, PropertyAttributes attribute) { auto* function = NativeFunction::create(interpreter(), interpreter().global_object(), property_name, move(native_function)); function->define_property("length", Value(length), Attribute::Configurable); @@ -573,7 +592,7 @@ bool Object::define_native_function(const FlyString& property_name, AK::Function return define_property(property_name, function, attribute); } -bool Object::define_native_property(const FlyString& property_name, AK::Function getter, AK::Function setter, u8 attribute) +bool Object::define_native_property(const FlyString& property_name, AK::Function getter, AK::Function setter, PropertyAttributes attribute) { return define_property(property_name, heap().allocate(move(getter), move(setter)), attribute); } diff --git a/Libraries/LibJS/Runtime/Object.h b/Libraries/LibJS/Runtime/Object.h index 3263b225ad4..f39d816c08d 100644 --- a/Libraries/LibJS/Runtime/Object.h +++ b/Libraries/LibJS/Runtime/Object.h @@ -68,14 +68,14 @@ public: bool put(PropertyName, Value); Value get_own_property(const Object& this_object, PropertyName) const; - Value get_own_properties(const Object& this_object, GetOwnPropertyMode, u8 attributes = default_attributes) const; + Value get_own_properties(const Object& this_object, GetOwnPropertyMode, PropertyAttributes attributes = default_attributes) const; Value get_own_property_descriptor(PropertyName) const; bool define_property(const FlyString& property_name, const Object& descriptor, bool throw_exceptions = true); - bool define_property(PropertyName, Value value, u8 attributes = default_attributes, bool throw_exceptions = true); + bool define_property(PropertyName, Value value, PropertyAttributes attributes = default_attributes, bool throw_exceptions = true); - bool define_native_function(const FlyString& property_name, AK::Function, i32 length = 0, u8 attribute = default_attributes); - bool define_native_property(const FlyString& property_name, AK::Function getter, AK::Function setter, u8 attribute = default_attributes); + bool define_native_function(const FlyString& property_name, AK::Function, i32 length = 0, PropertyAttributes attributes = default_attributes); + bool define_native_property(const FlyString& property_name, AK::Function getter, AK::Function setter, PropertyAttributes attributes = default_attributes); Value delete_property(PropertyName); @@ -116,8 +116,8 @@ public: private: virtual Value get_by_index(u32 property_index) const; virtual bool put_by_index(u32 property_index, Value); - bool put_own_property(Object& this_object, const FlyString& property_name, Value, u8 attributes, PutOwnPropertyMode = PutOwnPropertyMode::Put, bool throw_exceptions = true); - bool put_own_property_by_index(Object& this_object, u32 property_index, Value, u8 attributes, PutOwnPropertyMode = PutOwnPropertyMode::Put, bool throw_exceptions = true); + bool put_own_property(Object& this_object, const FlyString& property_name, Value, PropertyAttributes attributes, PutOwnPropertyMode = PutOwnPropertyMode::Put, bool throw_exceptions = true); + bool put_own_property_by_index(Object& this_object, u32 property_index, Value, PropertyAttributes attributes, PutOwnPropertyMode = PutOwnPropertyMode::Put, bool throw_exceptions = true); Value call_native_property_getter(Object* this_object, Value property) const; void call_native_property_setter(Object* this_object, Value property, Value) const; diff --git a/Libraries/LibJS/Runtime/PropertyAttributes.h b/Libraries/LibJS/Runtime/PropertyAttributes.h new file mode 100644 index 00000000000..d421182a8c4 --- /dev/null +++ b/Libraries/LibJS/Runtime/PropertyAttributes.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2020, Matthew Olsson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +namespace JS { + +struct Attribute { + enum { + Configurable = 1 << 0, + Enumerable = 1 << 1, + Writable = 1 << 2, + HasGetter = 1 << 3, + HasSetter = 1 << 4, + HasConfigurable = 1 << 5, + HasEnumerable = 1 << 6, + HasWritable = 1 << 7, + }; +}; + +class PropertyAttributes { +public: + PropertyAttributes(u8 bits = 0) + { + m_bits = bits; + if (bits & Attribute::Configurable) + m_bits |= Attribute::HasConfigurable; + if (bits & Attribute::Enumerable) + m_bits |= Attribute::HasEnumerable; + if (bits & Attribute::Writable) + m_bits |= Attribute::HasWritable; + } + + bool is_empty() const { return !m_bits; } + + bool has_configurable() const { return m_bits & Attribute::HasConfigurable; } + bool has_enumerable() const { return m_bits & Attribute::HasEnumerable; } + bool has_writable() const { return m_bits & Attribute::HasWritable; } + bool has_getter() const { return m_bits & Attribute::HasGetter; } + bool has_setter() const { return m_bits & Attribute::HasSetter; } + + bool is_configurable() const { return m_bits & Attribute::Configurable; } + bool is_enumerable() const { return m_bits & Attribute::Enumerable; } + bool is_writable() const { return m_bits & Attribute::Writable; } + + void set_has_configurable() { m_bits |= Attribute::HasConfigurable; } + void set_has_enumerable() { m_bits |= Attribute::HasEnumerable; } + void set_has_writable() { m_bits |= Attribute::HasWritable; } + void set_configurable() { m_bits |= Attribute::Configurable; } + void set_enumerable() { m_bits |= Attribute::Enumerable; } + void set_writable() { m_bits |= Attribute::Writable; } + void set_has_getter() { m_bits |= Attribute::HasGetter; } + void set_has_setter() { m_bits |= Attribute::HasSetter; } + + bool operator==(const PropertyAttributes& other) const { return m_bits == other.m_bits; } + bool operator!=(const PropertyAttributes& other) const { return m_bits != other.m_bits; } + + u8 bits() const { return m_bits; } + +private: + u8 m_bits; +}; + +const LogStream& operator<<(const LogStream& stream, const PropertyAttributes& attributes); + +const PropertyAttributes default_attributes = Attribute::Configurable | Attribute::Writable | Attribute::Enumerable; + +} diff --git a/Libraries/LibJS/Runtime/Shape.cpp b/Libraries/LibJS/Runtime/Shape.cpp index ef10ea8c51b..989894ec5dd 100644 --- a/Libraries/LibJS/Runtime/Shape.cpp +++ b/Libraries/LibJS/Runtime/Shape.cpp @@ -29,6 +29,11 @@ namespace JS { +const LogStream& operator<<(const LogStream& stream, const PropertyAttributes& attributes) +{ + return stream << attributes.bits(); +} + Shape* Shape::create_unique_clone() const { auto* new_shape = heap().allocate(); @@ -40,7 +45,7 @@ Shape* Shape::create_unique_clone() const return new_shape; } -Shape* Shape::create_put_transition(const FlyString& property_name, u8 attributes) +Shape* Shape::create_put_transition(const FlyString& property_name, PropertyAttributes attributes) { TransitionKey key { property_name, attributes }; if (auto* existing_shape = m_forward_transitions.get(key).value_or(nullptr)) @@ -50,7 +55,7 @@ Shape* Shape::create_put_transition(const FlyString& property_name, u8 attribute return new_shape; } -Shape* Shape::create_configure_transition(const FlyString& property_name, u8 attributes) +Shape* Shape::create_configure_transition(const FlyString& property_name, PropertyAttributes attributes) { TransitionKey key { property_name, attributes }; if (auto* existing_shape = m_forward_transitions.get(key).value_or(nullptr)) @@ -69,7 +74,7 @@ Shape::Shape() { } -Shape::Shape(Shape* previous_shape, const FlyString& property_name, u8 attributes, TransitionType transition_type) +Shape::Shape(Shape* previous_shape, const FlyString& property_name, PropertyAttributes attributes, TransitionType transition_type) : m_previous(previous_shape) , m_property_name(property_name) , m_attributes(attributes) @@ -160,7 +165,7 @@ void Shape::ensure_property_table() const } } -void Shape::add_property_to_unique_shape(const FlyString& property_name, u8 attributes) +void Shape::add_property_to_unique_shape(const FlyString& property_name, PropertyAttributes attributes) { ASSERT(is_unique()); ASSERT(m_property_table); @@ -168,7 +173,7 @@ void Shape::add_property_to_unique_shape(const FlyString& property_name, u8 attr m_property_table->set(property_name, { m_property_table->size(), attributes }); } -void Shape::reconfigure_property_in_unique_shape(const FlyString& property_name, u8 attributes) +void Shape::reconfigure_property_in_unique_shape(const FlyString& property_name, PropertyAttributes attributes) { ASSERT(is_unique()); ASSERT(m_property_table); diff --git a/Libraries/LibJS/Runtime/Shape.h b/Libraries/LibJS/Runtime/Shape.h index ac19d775c7e..60f59f09e40 100644 --- a/Libraries/LibJS/Runtime/Shape.h +++ b/Libraries/LibJS/Runtime/Shape.h @@ -31,35 +31,23 @@ #include #include #include +#include #include namespace JS { -struct Attribute { - enum { - Configurable = 1 << 0, - Enumerable = 1 << 1, - Writable = 1 << 2, - HasGet = 1 << 3, - HasSet = 1 << 4, - }; -}; - -const u8 default_attributes = Attribute::Configurable | Attribute::Writable | Attribute::Enumerable; - struct PropertyMetadata { size_t offset { 0 }; - u8 attributes { 0 }; + PropertyAttributes attributes { 0 }; }; struct TransitionKey { FlyString property_name; - u8 attributes { 0 }; + PropertyAttributes attributes { 0 }; bool operator==(const TransitionKey& other) const { - return property_name == other.property_name - && attributes == other.attributes; + return property_name == other.property_name && attributes == other.attributes; } }; @@ -75,11 +63,11 @@ public: }; Shape(); - Shape(Shape* previous_shape, const FlyString& property_name, u8 attributes, TransitionType); + Shape(Shape* previous_shape, const FlyString& property_name, PropertyAttributes attributes, TransitionType); Shape(Shape* previous_shape, Object* new_prototype); - Shape* create_put_transition(const FlyString& name, u8 attributes); - Shape* create_configure_transition(const FlyString& name, u8 attributes); + Shape* create_put_transition(const FlyString& name, PropertyAttributes attributes); + Shape* create_configure_transition(const FlyString& name, PropertyAttributes attributes); Shape* create_prototype_transition(Object* new_prototype); bool is_unique() const { return m_unique; } @@ -102,8 +90,8 @@ 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); + void add_property_to_unique_shape(const FlyString&, PropertyAttributes attributes); + void reconfigure_property_in_unique_shape(const FlyString& property_name, PropertyAttributes attributes); private: virtual const char* class_name() const override { return "Shape"; } @@ -116,7 +104,7 @@ private: HashMap m_forward_transitions; Shape* m_previous { nullptr }; FlyString m_property_name; - u8 m_attributes { 0 }; + PropertyAttributes m_attributes { 0 }; bool m_unique { false }; Object* m_prototype { nullptr }; TransitionType m_transition_type { TransitionType::Invalid }; @@ -128,6 +116,6 @@ template<> struct AK::Traits : public GenericTraits { static unsigned hash(const JS::TransitionKey& key) { - return pair_int_hash(int_hash(key.attributes), key.property_name.hash()); + return pair_int_hash(key.attributes.bits(), key.property_name.hash()); } };