mirror of
https://github.com/RGBCube/serenity
synced 2025-05-28 07:25:07 +00:00
LibJS: Add PropertyDescriptor object
This new struct is now returned from get_own_property_descriptor. To preserve the old functionality of returning an object, there is now a get_own_property_descriptor_object method, for use in {Object,Reflect}.getOwnPropertyDescriptor(). This change will be useful for the implementation of Proxies, which do a lot of descriptor checks. We want to avoid as many object gets and puts as possible.
This commit is contained in:
parent
5ad5322f6a
commit
79958f4520
4 changed files with 89 additions and 15 deletions
|
@ -40,6 +40,46 @@
|
||||||
|
|
||||||
namespace JS {
|
namespace JS {
|
||||||
|
|
||||||
|
PropertyDescriptor PropertyDescriptor::from_object(Interpreter& interpreter, const Object& object)
|
||||||
|
{
|
||||||
|
PropertyAttributes attributes;
|
||||||
|
if (object.has_property("configurable")) {
|
||||||
|
if (interpreter.exception())
|
||||||
|
return {};
|
||||||
|
attributes.set_has_configurable();
|
||||||
|
if (object.get("configurable").value_or(Value(false)).to_boolean())
|
||||||
|
attributes.set_configurable();
|
||||||
|
if (interpreter.exception())
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (object.has_property("enumerable")) {
|
||||||
|
if (interpreter.exception())
|
||||||
|
return {};
|
||||||
|
attributes.set_has_enumerable();
|
||||||
|
if (object.get("enumerable").value_or(Value(false)).to_boolean())
|
||||||
|
attributes.set_enumerable();
|
||||||
|
if (interpreter.exception())
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (object.has_property("writable")) {
|
||||||
|
if (interpreter.exception())
|
||||||
|
return {};
|
||||||
|
attributes.set_has_writable();
|
||||||
|
if (object.get("writable").value_or(Value(false)).to_boolean())
|
||||||
|
attributes.set_writable();
|
||||||
|
if (interpreter.exception())
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
PropertyDescriptor descriptor { attributes, object.get("value"), nullptr, nullptr };
|
||||||
|
auto getter = object.get("get");
|
||||||
|
if (getter.is_function())
|
||||||
|
descriptor.getter = &getter.as_function();
|
||||||
|
auto setter = object.get("set");
|
||||||
|
if (setter.is_function())
|
||||||
|
descriptor.setter = &setter.as_function();
|
||||||
|
return descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
Object* Object::create_empty(Interpreter&, GlobalObject& global_object)
|
Object* Object::create_empty(Interpreter&, GlobalObject& global_object)
|
||||||
{
|
{
|
||||||
return global_object.heap().allocate<Object>(global_object.object_prototype());
|
return global_object.heap().allocate<Object>(global_object.object_prototype());
|
||||||
|
@ -191,7 +231,7 @@ Value Object::get_own_properties(const Object& this_object, GetOwnPropertyMode k
|
||||||
return properties_array;
|
return properties_array;
|
||||||
}
|
}
|
||||||
|
|
||||||
Value Object::get_own_property_descriptor(PropertyName property_name) const
|
Optional<PropertyDescriptor> Object::get_own_property_descriptor(PropertyName property_name) const
|
||||||
{
|
{
|
||||||
Value value;
|
Value value;
|
||||||
PropertyAttributes attributes;
|
PropertyAttributes attributes;
|
||||||
|
@ -199,40 +239,60 @@ Value Object::get_own_property_descriptor(PropertyName property_name) const
|
||||||
if (property_name.is_number()) {
|
if (property_name.is_number()) {
|
||||||
auto existing_value = m_indexed_properties.get(nullptr, property_name.as_number(), false);
|
auto existing_value = m_indexed_properties.get(nullptr, property_name.as_number(), false);
|
||||||
if (!existing_value.has_value())
|
if (!existing_value.has_value())
|
||||||
return js_undefined();
|
return {};
|
||||||
value = existing_value.value().value;
|
value = existing_value.value().value;
|
||||||
attributes = existing_value.value().attributes;
|
attributes = existing_value.value().attributes;
|
||||||
attributes = default_attributes;
|
attributes = default_attributes;
|
||||||
} else {
|
} else {
|
||||||
auto metadata = shape().lookup(property_name.as_string());
|
auto metadata = shape().lookup(property_name.as_string());
|
||||||
if (!metadata.has_value())
|
if (!metadata.has_value())
|
||||||
return js_undefined();
|
return {};
|
||||||
value = m_storage[metadata.value().offset];
|
value = m_storage[metadata.value().offset];
|
||||||
if (interpreter().exception())
|
if (interpreter().exception())
|
||||||
return {};
|
return {};
|
||||||
attributes = metadata.value().attributes;
|
attributes = metadata.value().attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* descriptor = Object::create_empty(interpreter(), interpreter().global_object());
|
PropertyDescriptor descriptor { attributes, {}, nullptr, nullptr };
|
||||||
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()) {
|
if (value.is_object() && value.as_object().is_native_property()) {
|
||||||
auto result = call_native_property_getter(const_cast<Object*>(this), value);
|
auto result = call_native_property_getter(const_cast<Object*>(this), value);
|
||||||
descriptor->define_property("value", result);
|
descriptor.value = result.value_or(js_undefined());
|
||||||
descriptor->define_property("writable", Value(attributes.is_writable()));
|
|
||||||
} else if (value.is_accessor()) {
|
} else if (value.is_accessor()) {
|
||||||
auto& pair = value.as_accessor();
|
auto& pair = value.as_accessor();
|
||||||
if (pair.getter())
|
if (pair.getter())
|
||||||
descriptor->define_property("get", pair.getter());
|
descriptor.getter = pair.getter();
|
||||||
if (pair.setter())
|
if (pair.setter())
|
||||||
descriptor->define_property("set", pair.setter());
|
descriptor.setter = pair.setter();
|
||||||
} else {
|
} else {
|
||||||
descriptor->define_property("value", value.value_or(js_undefined()));
|
descriptor.value = value.value_or(js_undefined());
|
||||||
descriptor->define_property("writable", Value(attributes.is_writable()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return descriptor;
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Value Object::get_own_property_descriptor_object(PropertyName property_name) const
|
||||||
|
{
|
||||||
|
auto descriptor_opt = get_own_property_descriptor(property_name);
|
||||||
|
if (!descriptor_opt.has_value())
|
||||||
|
return js_undefined();
|
||||||
|
auto descriptor = descriptor_opt.value();
|
||||||
|
|
||||||
|
auto* descriptor_object = Object::create_empty(interpreter(), interpreter().global_object());
|
||||||
|
descriptor_object->define_property("enumerable", Value(descriptor.attributes.is_enumerable()));
|
||||||
|
descriptor_object->define_property("configurable", Value(descriptor.attributes.is_configurable()));
|
||||||
|
if (descriptor.is_data_descriptor()) {
|
||||||
|
descriptor_object->define_property("value", descriptor.value.value_or(js_undefined()));
|
||||||
|
descriptor_object->define_property("writable", Value(descriptor.attributes.is_writable()));
|
||||||
|
} else if (descriptor.is_accessor_descriptor()) {
|
||||||
|
if (descriptor.getter) {
|
||||||
|
descriptor_object->define_property("get", Value(descriptor.getter));
|
||||||
|
}
|
||||||
|
if (descriptor.setter)
|
||||||
|
descriptor_object->define_property("set", Value(descriptor.setter));
|
||||||
|
}
|
||||||
|
return descriptor_object;
|
||||||
|
}
|
||||||
|
|
||||||
void Object::set_shape(Shape& new_shape)
|
void Object::set_shape(Shape& new_shape)
|
||||||
{
|
{
|
||||||
m_storage.resize(new_shape.property_count());
|
m_storage.resize(new_shape.property_count());
|
||||||
|
|
|
@ -39,6 +39,19 @@
|
||||||
|
|
||||||
namespace JS {
|
namespace JS {
|
||||||
|
|
||||||
|
struct PropertyDescriptor {
|
||||||
|
PropertyAttributes attributes;
|
||||||
|
Value value;
|
||||||
|
Function* getter;
|
||||||
|
Function* setter;
|
||||||
|
|
||||||
|
static PropertyDescriptor from_object(Interpreter&, const Object&);
|
||||||
|
|
||||||
|
bool is_accessor_descriptor() const { return getter || setter; }
|
||||||
|
bool is_data_descriptor() const { return !(value.is_empty() && !attributes.has_writable()); }
|
||||||
|
bool is_generic_descriptor() const { return !is_accessor_descriptor() && !is_data_descriptor(); }
|
||||||
|
};
|
||||||
|
|
||||||
class Object : public Cell {
|
class Object : public Cell {
|
||||||
public:
|
public:
|
||||||
static Object* create_empty(Interpreter&, GlobalObject&);
|
static Object* create_empty(Interpreter&, GlobalObject&);
|
||||||
|
@ -69,7 +82,8 @@ public:
|
||||||
|
|
||||||
Value get_own_property(const Object& this_object, PropertyName) const;
|
Value get_own_property(const Object& this_object, PropertyName) const;
|
||||||
Value get_own_properties(const Object& this_object, GetOwnPropertyMode, PropertyAttributes 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;
|
Optional<PropertyDescriptor> get_own_property_descriptor(PropertyName) const;
|
||||||
|
Value get_own_property_descriptor_object(PropertyName) const;
|
||||||
|
|
||||||
bool define_property(const FlyString& property_name, const Object& descriptor, bool throw_exceptions = true);
|
bool define_property(const FlyString& property_name, const Object& descriptor, bool throw_exceptions = true);
|
||||||
bool define_property(PropertyName, Value value, PropertyAttributes attributes = default_attributes, bool throw_exceptions = true);
|
bool define_property(PropertyName, Value value, PropertyAttributes attributes = default_attributes, bool throw_exceptions = true);
|
||||||
|
|
|
@ -147,7 +147,7 @@ Value ObjectConstructor::get_own_property_descriptor(Interpreter& interpreter)
|
||||||
auto property_key = interpreter.argument(1).to_string(interpreter);
|
auto property_key = interpreter.argument(1).to_string(interpreter);
|
||||||
if (interpreter.exception())
|
if (interpreter.exception())
|
||||||
return {};
|
return {};
|
||||||
return object->get_own_property_descriptor(property_key);
|
return object->get_own_property_descriptor_object(property_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
Value ObjectConstructor::define_property_(Interpreter& interpreter)
|
Value ObjectConstructor::define_property_(Interpreter& interpreter)
|
||||||
|
|
|
@ -189,7 +189,7 @@ Value ReflectObject::get_own_property_descriptor(Interpreter& interpreter)
|
||||||
auto property_key = interpreter.argument(1).to_string(interpreter);
|
auto property_key = interpreter.argument(1).to_string(interpreter);
|
||||||
if (interpreter.exception())
|
if (interpreter.exception())
|
||||||
return {};
|
return {};
|
||||||
return target->get_own_property_descriptor(property_key);
|
return target->get_own_property_descriptor_object(property_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
Value ReflectObject::get_prototype_of(Interpreter& interpreter)
|
Value ReflectObject::get_prototype_of(Interpreter& interpreter)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue