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:
parent
f08aa6324e
commit
afcfea2001
7 changed files with 60 additions and 17 deletions
|
@ -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()) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue