diff --git a/Libraries/LibJS/Runtime/ProxyObject.cpp b/Libraries/LibJS/Runtime/ProxyObject.cpp index 02cd11634f..4096558bdc 100644 --- a/Libraries/LibJS/Runtime/ProxyObject.cpp +++ b/Libraries/LibJS/Runtime/ProxyObject.cpp @@ -359,24 +359,26 @@ bool ProxyObject::has_property(const PropertyName& name) const 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(); if (m_is_revoked) { vm.throw_exception(global_object(), ErrorType::ProxyRevoked); return {}; } + if (receiver.is_empty()) + receiver = Value(const_cast(this)); auto trap = m_handler.get(vm.names.get); if (vm.exception()) return {}; if (trap.is_empty() || trap.is_nullish()) - return m_target.get(name); + return m_target.get(name, receiver); if (!trap.is_function()) { vm.throw_exception(global_object(), ErrorType::ProxyInvalidTrap, "get"); return {}; } - auto trap_result = vm.call(trap.as_function(), Value(&m_handler), Value(&m_target), name.to_value(vm), Value(const_cast(this))); + auto trap_result = vm.call(trap.as_function(), Value(&m_handler), Value(&m_target), name.to_value(vm), receiver); if (vm.exception()) return {}; 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; } -bool ProxyObject::put(const PropertyName& name, Value value, Value) +bool ProxyObject::put(const PropertyName& name, Value value, Value receiver) { auto& vm = this->vm(); if (m_is_revoked) { vm.throw_exception(global_object(), ErrorType::ProxyRevoked); return false; } + if (receiver.is_empty()) + receiver = Value(const_cast(this)); auto trap = m_handler.get(vm.names.set); if (vm.exception()) return false; if (trap.is_empty() || trap.is_nullish()) - return m_target.put(name, value); + return m_target.put(name, value, receiver); if (!trap.is_function()) { vm.throw_exception(global_object(), ErrorType::ProxyInvalidTrap, "set"); return false; } - auto trap_result = vm.call(trap.as_function(), Value(&m_handler), Value(&m_target), name.to_value(vm), value, Value(const_cast(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()) return false; auto target_desc = m_target.get_own_property_descriptor(name); diff --git a/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-get.js b/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-get.js index a1fa8499c2..9d9ab486c9 100644 --- a/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-get.js +++ b/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-get.js @@ -40,6 +40,16 @@ describe("[[Get]] trap normal behavior", () => { expect(p.test).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", () => { diff --git a/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-set.js b/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-set.js index c78235884e..ef1a75c48a 100644 --- a/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-set.js +++ b/Libraries/LibJS/Tests/builtins/Proxy/Proxy.handler-set.js @@ -43,6 +43,21 @@ describe("[[Set]] trap normal behavior", () => { p[Symbol.hasInstance] = "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", () => {