Follow-ups from #6907 (Array.prototype mutators on Proxy receivers). That PR trap-routes push/pop/shift/unshift in array_proto_mutator; two adjacent gaps remain:
1. Remaining mutators fall through silently on Proxy receivers
reverse / sort / splice / fill / copyWithin on a Proxy receiver reaching the generic dispatch still hit the pre-#6907 fall-through: as_real_array rejects the handle-band id (correctly — deref is the #6279 segfault class), run_object_mutator rejects non-plain-objects, and the call returns undefined without firing a trap. Extend proxy_array_mutator (crates/perry-runtime/src/array/push_pop.rs) with the spec loops for the rest of the family — all primitives (js_proxy_get/js_proxy_set/js_proxy_delete + proxy_array_length) are already in place.
Repro:
const t: any = [3, 1, 2];
const p: any = new Proxy(t, {});
p.reverse();
console.log(t.join(",")); // node: 2,1,3 — perry: 3,1,2 (silent no-op)
2. Prototype thunks lose their receiver when called as plain values
Array.prototype method thunks resolve their receiver from IMPLICIT_THIS at call time, so a thunk called as a plain value silently mutates nothing:
const t: any = [1, 2];
const f: any = t["push"]; // typeof f === "function" ✓
f(3); // node: TypeError (this === undefined) — perry: silent no-op
console.log(t.join(",")); // node: unreachable — perry: "1,2"
The decomposed proxy form hits the same class (const g = proxy.push; g(3) — node binds this through the call, perry no-ops). The #4661 comment in native_call_method.rs claims the decomposed form works via js_proxy_get; the method VALUE resolves fine, but the receiver binding is lost at the call. Correct semantics: bind the receiver at property-read time (bound-method materialization) or throw the spec TypeError when a receiver-less builtin thunk is invoked — silent no-op is the worst of both.
Discovered while root-causing the release-gate failure proxy_array_push_via_member_routes_through_traps (see #6907 for the full mechanism chain: #6397 exposed it, #6759 ended the x86_64 classification luck, arm64 was broken from #6397 on).
Follow-ups from #6907 (Array.prototype mutators on Proxy receivers). That PR trap-routes
push/pop/shift/unshiftinarray_proto_mutator; two adjacent gaps remain:1. Remaining mutators fall through silently on Proxy receivers
reverse/sort/splice/fill/copyWithinon a Proxy receiver reaching the generic dispatch still hit the pre-#6907 fall-through:as_real_arrayrejects the handle-band id (correctly — deref is the #6279 segfault class),run_object_mutatorrejects non-plain-objects, and the call returnsundefinedwithout firing a trap. Extendproxy_array_mutator(crates/perry-runtime/src/array/push_pop.rs) with the spec loops for the rest of the family — all primitives (js_proxy_get/js_proxy_set/js_proxy_delete+proxy_array_length) are already in place.Repro:
2. Prototype thunks lose their receiver when called as plain values
Array.prototypemethod thunks resolve their receiver fromIMPLICIT_THISat call time, so a thunk called as a plain value silently mutates nothing:The decomposed proxy form hits the same class (
const g = proxy.push; g(3)— node bindsthisthrough the call, perry no-ops). The #4661 comment innative_call_method.rsclaims the decomposed form works viajs_proxy_get; the method VALUE resolves fine, but the receiver binding is lost at the call. Correct semantics: bind the receiver at property-read time (bound-method materialization) or throw the spec TypeError when a receiver-less builtin thunk is invoked — silent no-op is the worst of both.Discovered while root-causing the release-gate failure
proxy_array_push_via_member_routes_through_traps(see #6907 for the full mechanism chain: #6397 exposed it, #6759 ended the x86_64 classification luck, arm64 was broken from #6397 on).