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; 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; Value value_here;
@ -170,7 +170,7 @@ Value Object::get_own_property(const Object& this_object, PropertyName property_
ASSERT(!value_here.is_empty()); ASSERT(!value_here.is_empty());
if (value_here.is_accessor()) { 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()) if (value_here.is_native_property())
return call_native_property_getter(const_cast<Object*>(&this_object), value_here); 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 {}; return {};
} }
Value Object::get(PropertyName property_name) const Value Object::get(PropertyName property_name, Value receiver) const
{ {
if (property_name.is_number()) if (property_name.is_number())
return get_by_index(property_name.as_number()); return get_by_index(property_name.as_number());
@ -617,7 +617,9 @@ Value Object::get(PropertyName property_name) const
const Object* object = this; const Object* object = this;
while (object) { 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()) if (interpreter().exception())
return {}; return {};
if (!value.is_empty()) 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); 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()) if (property_name.is_number())
return put_by_index(property_name.as_number(), value); 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()) { if (metadata.has_value()) {
auto value_here = object->m_storage[metadata.value().offset]; auto value_here = object->m_storage[metadata.value().offset];
if (value_here.is_accessor()) { 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; return true;
} }
if (value_here.is_native_property()) { if (value_here.is_native_property()) {

View file

@ -84,14 +84,14 @@ public:
GlobalObject& global_object() const { return shape().global_object(); } 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; virtual bool has_property(PropertyName) const;
bool has_own_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; Value get_own_properties(const Object& this_object, GetOwnPropertyMode, bool only_enumerable_properties = false) const;
virtual Optional<PropertyDescriptor> get_own_property_descriptor(PropertyName) const; virtual Optional<PropertyDescriptor> get_own_property_descriptor(PropertyName) const;
Value get_own_property_descriptor_object(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; return trap_result;
} }
Value ProxyObject::get(PropertyName name) const Value ProxyObject::get(PropertyName name, Value) const
{ {
if (m_is_revoked) { if (m_is_revoked) {
interpreter().throw_exception<TypeError>(ErrorType::ProxyRevoked); interpreter().throw_exception<TypeError>(ErrorType::ProxyRevoked);
@ -395,7 +395,7 @@ Value ProxyObject::get(PropertyName name) const
return trap_result; return trap_result;
} }
bool ProxyObject::put(PropertyName name, Value value) bool ProxyObject::put(PropertyName name, Value value, Value)
{ {
if (m_is_revoked) { if (m_is_revoked) {
interpreter().throw_exception<TypeError>(ErrorType::ProxyRevoked); interpreter().throw_exception<TypeError>(ErrorType::ProxyRevoked);

View file

@ -50,8 +50,8 @@ public:
virtual Optional<PropertyDescriptor> get_own_property_descriptor(PropertyName) const override; 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 define_property(const FlyString& property_name, const Object& descriptor, bool throw_exceptions = true) override;
virtual bool has_property(PropertyName name) const override; virtual bool has_property(PropertyName name) const override;
virtual Value get(PropertyName name) const override; virtual Value get(PropertyName name, Value receiver) const override;
virtual bool put(PropertyName name, Value value) override; virtual bool put(PropertyName name, Value value, Value receiver) override;
virtual Value delete_property(PropertyName name) override; virtual Value delete_property(PropertyName name) override;
void revoke() { m_is_revoked = true; } 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) 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"); auto* target = get_target_object_from(interpreter, "get");
if (!target) if (!target)
return {}; return {};
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(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) 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) 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"); auto* target = get_target_object_from(interpreter, "set");
if (!target) if (!target)
return {}; return {};
@ -252,7 +253,10 @@ JS_DEFINE_NATIVE_FUNCTION(ReflectObject::set)
if (interpreter.exception()) if (interpreter.exception())
return {}; return {};
auto value = interpreter.argument(2); 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) 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"), 2) === "o");
assert(Reflect.get(new String("foo"), 3) === undefined); 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"); console.log("PASS");
} catch (e) { } catch (e) {
console.log("FAIL: " + e); console.log("FAIL: " + e);

View file

@ -48,6 +48,24 @@ try {
assert(a[3] === undefined); assert(a[3] === undefined);
assert(a[4] === "bar"); 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"); console.log("PASS");
} catch (e) { } catch (e) {
console.log("FAIL: " + e); console.log("FAIL: " + e);