mirror of
https://github.com/RGBCube/serenity
synced 2025-07-23 13:57:35 +00:00
LibJS: Support receiver in ProxyObject::get/put()
If a receiver is given, e.g. via Reflect.get/set(), forward it to the target object's get()/put() or use it as last argument of the trap function. The default value is the Proxy object itself.
This commit is contained in:
parent
76308c2e1f
commit
f6f0d3cbae
3 changed files with 35 additions and 6 deletions
|
@ -359,24 +359,26 @@ bool ProxyObject::has_property(const PropertyName& name) const
|
||||||
return trap_result.to_boolean();
|
return trap_result.to_boolean();
|
||||||
}
|
}
|
||||||
|
|
||||||
Value ProxyObject::get(const PropertyName& name, Value) const
|
Value ProxyObject::get(const PropertyName& name, Value receiver) const
|
||||||
{
|
{
|
||||||
auto& vm = this->vm();
|
auto& vm = this->vm();
|
||||||
if (m_is_revoked) {
|
if (m_is_revoked) {
|
||||||
vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
|
vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
if (receiver.is_empty())
|
||||||
|
receiver = Value(const_cast<ProxyObject*>(this));
|
||||||
auto trap = m_handler.get(vm.names.get);
|
auto trap = m_handler.get(vm.names.get);
|
||||||
if (vm.exception())
|
if (vm.exception())
|
||||||
return {};
|
return {};
|
||||||
if (trap.is_empty() || trap.is_nullish())
|
if (trap.is_empty() || trap.is_nullish())
|
||||||
return m_target.get(name);
|
return m_target.get(name, receiver);
|
||||||
if (!trap.is_function()) {
|
if (!trap.is_function()) {
|
||||||
vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyInvalidTrap, "get");
|
vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyInvalidTrap, "get");
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto trap_result = vm.call(trap.as_function(), Value(&m_handler), Value(&m_target), name.to_value(vm), Value(const_cast<ProxyObject*>(this)));
|
auto trap_result = vm.call(trap.as_function(), Value(&m_handler), Value(&m_target), name.to_value(vm), receiver);
|
||||||
if (vm.exception())
|
if (vm.exception())
|
||||||
return {};
|
return {};
|
||||||
auto target_desc = m_target.get_own_property_descriptor(name);
|
auto target_desc = m_target.get_own_property_descriptor(name);
|
||||||
|
@ -395,23 +397,25 @@ Value ProxyObject::get(const PropertyName& name, Value) const
|
||||||
return trap_result;
|
return trap_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProxyObject::put(const PropertyName& name, Value value, Value)
|
bool ProxyObject::put(const PropertyName& name, Value value, Value receiver)
|
||||||
{
|
{
|
||||||
auto& vm = this->vm();
|
auto& vm = this->vm();
|
||||||
if (m_is_revoked) {
|
if (m_is_revoked) {
|
||||||
vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
|
vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (receiver.is_empty())
|
||||||
|
receiver = Value(const_cast<ProxyObject*>(this));
|
||||||
auto trap = m_handler.get(vm.names.set);
|
auto trap = m_handler.get(vm.names.set);
|
||||||
if (vm.exception())
|
if (vm.exception())
|
||||||
return false;
|
return false;
|
||||||
if (trap.is_empty() || trap.is_nullish())
|
if (trap.is_empty() || trap.is_nullish())
|
||||||
return m_target.put(name, value);
|
return m_target.put(name, value, receiver);
|
||||||
if (!trap.is_function()) {
|
if (!trap.is_function()) {
|
||||||
vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyInvalidTrap, "set");
|
vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyInvalidTrap, "set");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
auto trap_result = vm.call(trap.as_function(), Value(&m_handler), Value(&m_target), name.to_value(vm), value, Value(const_cast<ProxyObject*>(this)));
|
auto trap_result = vm.call(trap.as_function(), Value(&m_handler), Value(&m_target), name.to_value(vm), value, receiver);
|
||||||
if (vm.exception() || !trap_result.to_boolean())
|
if (vm.exception() || !trap_result.to_boolean())
|
||||||
return false;
|
return false;
|
||||||
auto target_desc = m_target.get_own_property_descriptor(name);
|
auto target_desc = m_target.get_own_property_descriptor(name);
|
||||||
|
|
|
@ -40,6 +40,16 @@ describe("[[Get]] trap normal behavior", () => {
|
||||||
expect(p.test).toBeUndefined();
|
expect(p.test).toBeUndefined();
|
||||||
expect(p[Symbol.hasInstance]).toBeUndefined();
|
expect(p[Symbol.hasInstance]).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("custom receiver value", () => {
|
||||||
|
let p = new Proxy({}, {
|
||||||
|
get(target, property, receiver) {
|
||||||
|
return receiver;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(Reflect.get(p, "foo", 42)).toBe(42);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("[[Get]] invariants", () => {
|
describe("[[Get]] invariants", () => {
|
||||||
|
|
|
@ -43,6 +43,21 @@ describe("[[Set]] trap normal behavior", () => {
|
||||||
p[Symbol.hasInstance] = "foo"
|
p[Symbol.hasInstance] = "foo"
|
||||||
expect(p[Symbol.hasInstance]).toBe("foo");
|
expect(p[Symbol.hasInstance]).toBe("foo");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("custom receiver value", () => {
|
||||||
|
const o = {};
|
||||||
|
const r = {};
|
||||||
|
let p = new Proxy(o, {
|
||||||
|
set(target, property, value, receiver) {
|
||||||
|
receiver[property] = value;
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(Reflect.set(p, "foo", 42, r)).toBe(true);
|
||||||
|
expect(o.foo).toBeUndefined();
|
||||||
|
expect(r.foo).toBe(42);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("[[Set]] invariants", () => {
|
describe("[[Set]] invariants", () => {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue