1
Fork 0
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:
Matthew Olsson 2020-06-03 09:40:17 -07:00 committed by Andreas Kling
parent 5ad5322f6a
commit 79958f4520
4 changed files with 89 additions and 15 deletions

View file

@ -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());

View file

@ -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);

View file

@ -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)

View file

@ -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)