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

LibJS: Handle "receiver" argument in Reflect.{get,set}()

This commit is contained in:
Linus Groh 2020-06-25 14:00:13 +01:00 committed by Andreas Kling
parent f08aa6324e
commit afcfea2001
7 changed files with 60 additions and 17 deletions

View file

@ -152,7 +152,7 @@ bool Object::prevent_extensions()
return true;
}
Value Object::get_own_property(const Object& this_object, PropertyName property_name) const
Value Object::get_own_property(const Object& this_object, PropertyName property_name, Value receiver) const
{
Value value_here;
@ -170,7 +170,7 @@ Value Object::get_own_property(const Object& this_object, PropertyName property_
ASSERT(!value_here.is_empty());
if (value_here.is_accessor()) {
return value_here.as_accessor().call_getter(Value(const_cast<Object*>(this)));
return value_here.as_accessor().call_getter(receiver);
}
if (value_here.is_native_property())
return call_native_property_getter(const_cast<Object*>(&this_object), value_here);
@ -605,7 +605,7 @@ Value Object::get_by_index(u32 property_index) const
return {};
}
Value Object::get(PropertyName property_name) const
Value Object::get(PropertyName property_name, Value receiver) const
{
if (property_name.is_number())
return get_by_index(property_name.as_number());
@ -617,7 +617,9 @@ Value Object::get(PropertyName property_name) const
const Object* object = this;
while (object) {
auto value = object->get_own_property(*this, property_name);
if (receiver.is_empty())
receiver = Value(const_cast<Object*>(this));
auto value = object->get_own_property(*this, property_name, receiver);
if (interpreter().exception())
return {};
if (!value.is_empty())
@ -656,7 +658,7 @@ bool Object::put_by_index(u32 property_index, Value value)
return put_own_property_by_index(*this, property_index, value, default_attributes, PutOwnPropertyMode::Put);
}
bool Object::put(PropertyName property_name, Value value)
bool Object::put(PropertyName property_name, Value value, Value receiver)
{
if (property_name.is_number())
return put_by_index(property_name.as_number(), value);
@ -676,7 +678,9 @@ bool Object::put(PropertyName property_name, Value value)
if (metadata.has_value()) {
auto value_here = object->m_storage[metadata.value().offset];
if (value_here.is_accessor()) {
value_here.as_accessor().call_setter(Value(this), value);
if (receiver.is_empty())
receiver = Value(this);
value_here.as_accessor().call_setter(receiver, value);
return true;
}
if (value_here.is_native_property()) {

View file

@ -84,14 +84,14 @@ public:
GlobalObject& global_object() const { return shape().global_object(); }
virtual Value get(PropertyName) const;
virtual Value get(PropertyName, Value receiver = {}) const;
virtual bool has_property(PropertyName) const;
bool has_own_property(PropertyName) const;
virtual bool put(PropertyName, Value);
virtual bool put(PropertyName, Value, Value receiver = {});
Value get_own_property(const Object& this_object, PropertyName) const;
Value get_own_property(const Object& this_object, PropertyName, Value receiver) const;
Value get_own_properties(const Object& this_object, GetOwnPropertyMode, bool only_enumerable_properties = false) const;
virtual Optional<PropertyDescriptor> get_own_property_descriptor(PropertyName) const;
Value get_own_property_descriptor_object(PropertyName) const;

View file

@ -363,7 +363,7 @@ bool ProxyObject::has_property(PropertyName name) const
return trap_result;
}
Value ProxyObject::get(PropertyName name) const
Value ProxyObject::get(PropertyName name, Value) const
{
if (m_is_revoked) {
interpreter().throw_exception<TypeError>(ErrorType::ProxyRevoked);
@ -395,7 +395,7 @@ Value ProxyObject::get(PropertyName name) const
return trap_result;
}
bool ProxyObject::put(PropertyName name, Value value)
bool ProxyObject::put(PropertyName name, Value value, Value)
{
if (m_is_revoked) {
interpreter().throw_exception<TypeError>(ErrorType::ProxyRevoked);

View file

@ -50,8 +50,8 @@ public:
virtual Optional<PropertyDescriptor> get_own_property_descriptor(PropertyName) const override;
virtual bool define_property(const FlyString& property_name, const Object& descriptor, bool throw_exceptions = true) override;
virtual bool has_property(PropertyName name) const override;
virtual Value get(PropertyName name) const override;
virtual bool put(PropertyName name, Value value) override;
virtual Value get(PropertyName name, Value receiver) const override;
virtual bool put(PropertyName name, Value value, Value receiver) override;
virtual Value delete_property(PropertyName name) override;
void revoke() { m_is_revoked = true; }

View file

@ -178,14 +178,16 @@ JS_DEFINE_NATIVE_FUNCTION(ReflectObject::delete_property)
JS_DEFINE_NATIVE_FUNCTION(ReflectObject::get)
{
// FIXME: There's a third argument, receiver, for getters - use it once we have those.
auto* target = get_target_object_from(interpreter, "get");
if (!target)
return {};
auto property_key = interpreter.argument(1).to_string(interpreter);
if (interpreter.exception())
return {};
return target->get(property_key).value_or(js_undefined());
Value receiver = {};
if (interpreter.argument_count() > 2)
receiver = interpreter.argument(2);
return target->get(property_key, receiver).value_or(js_undefined());
}
JS_DEFINE_NATIVE_FUNCTION(ReflectObject::get_own_property_descriptor)
@ -244,7 +246,6 @@ JS_DEFINE_NATIVE_FUNCTION(ReflectObject::prevent_extensions)
JS_DEFINE_NATIVE_FUNCTION(ReflectObject::set)
{
// FIXME: There's a fourth argument, receiver, for setters - use it once we have those.
auto* target = get_target_object_from(interpreter, "set");
if (!target)
return {};
@ -252,7 +253,10 @@ JS_DEFINE_NATIVE_FUNCTION(ReflectObject::set)
if (interpreter.exception())
return {};
auto value = interpreter.argument(2);
return Value(target->put(property_key, value));
Value receiver = {};
if (interpreter.argument_count() > 3)
receiver = interpreter.argument(3);
return Value(target->put(property_key, value, receiver));
}
JS_DEFINE_NATIVE_FUNCTION(ReflectObject::set_prototype_of)

View file

@ -33,6 +33,23 @@ try {
assert(Reflect.get(new String("foo"), 2) === "o");
assert(Reflect.get(new String("foo"), 3) === undefined);
const foo = {
get prop() {
this.getPropCalled = true;
}
};
const bar = {};
Object.setPrototypeOf(bar, foo);
assert(foo.getPropCalled === undefined);
assert(bar.getPropCalled === undefined);
Reflect.get(bar, "prop");
assert(foo.getPropCalled === undefined);
assert(bar.getPropCalled === true);
Reflect.get(bar, "prop", foo);
assert(foo.getPropCalled === true);
assert(bar.getPropCalled === true);
console.log("PASS");
} catch (e) {
console.log("FAIL: " + e);

View file

@ -48,6 +48,24 @@ try {
assert(a[3] === undefined);
assert(a[4] === "bar");
const foo = {
set prop(value) {
this.setPropCalled = true;
}
};
const bar = {};
Object.setPrototypeOf(bar, foo);
assert(foo.setPropCalled === undefined);
assert(bar.setPropCalled === undefined);
Reflect.set(bar, "prop", 42);
assert(foo.setPropCalled === undefined);
assert(bar.setPropCalled === true);
Reflect.set(bar, "prop", 42, foo);
assert(foo.setPropCalled === true);
assert(bar.setPropCalled === true);
console.log("PASS");
} catch (e) {
console.log("FAIL: " + e);