Summary
Array.prototype.copyWithin(target, start?, end?) mutates and returns the receiver for all valid argument counts with a supplied target. Perry has a static/codegen path for this method, but dynamic runtime method dispatch still has no copyWithin branch, so calls that fall through to dynamic dispatch do not reach js_array_copy_within.
Node parity probe
Run with Node v25.9.0:
for (const [label, fn] of [
["copyWithin target only", () => [1, 2, 3, 4].copyWithin(1)],
["copyWithin target start", () => [1, 2, 3, 4].copyWithin(1, 2)],
["copyWithin target start end", () => [1, 2, 3, 4].copyWithin(1, 0, 2)],
["copyWithin neg", () => [1, 2, 3, 4].copyWithin(-2, 0, -1)],
]) {
try {
console.log(label + ": " + JSON.stringify(fn()));
} catch (e) {
console.log(label + ": throws " + e.name + ": " + e.message);
}
}
Output:
copyWithin target only: [1,1,2,3]
copyWithin target start: [1,3,4,4]
copyWithin target start end: [1,1,2,4]
copyWithin neg: [1,2,1,2]
Current Perry behavior in source
The static/codegen path exists:
crates/perry-codegen/src/lower_array_method.rs:160-195 handles copyWithin, including the one-argument form by defaulting start to 0 and the omitted end form by passing has_end = 0.
crates/perry-runtime/src/array/immutable.rs:198-257 implements js_array_copy_within(target, start, has_end, end) with negative-index normalization, end clamping, and memmove semantics.
crates/perry-hir/src/lower/expr_call/local_array_methods.rs:508-520 has an HIR fast path for local array receivers with at least two arguments.
But dynamic runtime method dispatch does not include a copyWithin branch:
crates/perry-runtime/src/object/native_call_method.rs has dynamic branches for nearby mutating array methods such as sort, reverse, reduce, flat, flatMap, fill, and others, but source search finds no "copyWithin" branch.
- The static codegen comment at
crates/perry-codegen/src/lower_array_method.rs:161-167 explicitly notes that the general method-dispatch fallback does not know about copyWithin and silently no-ops.
So any copyWithin call that reaches dynamic dispatch, including calls on any/unproven receivers, misses the runtime helper even though the helper already exists.
Expected fix shape
Add a dynamic dispatch branch that mirrors the static path:
- Require at least the
target argument.
- Default omitted
start to 0.
- Preserve omitted
end with has_end = 0; otherwise pass the supplied end.
- Call
js_array_copy_within and return the original receiver pointer.
Please add regression coverage for dynamically dispatched copyWithin(target), copyWithin(target, start), copyWithin(target, start, end), negative indexes, and overlapping copy regions.
Summary
Array.prototype.copyWithin(target, start?, end?)mutates and returns the receiver for all valid argument counts with a suppliedtarget. Perry has a static/codegen path for this method, but dynamic runtime method dispatch still has nocopyWithinbranch, so calls that fall through to dynamic dispatch do not reachjs_array_copy_within.Node parity probe
Run with Node v25.9.0:
Output:
Current Perry behavior in source
The static/codegen path exists:
crates/perry-codegen/src/lower_array_method.rs:160-195handlescopyWithin, including the one-argument form by defaultingstartto0and the omittedendform by passinghas_end = 0.crates/perry-runtime/src/array/immutable.rs:198-257implementsjs_array_copy_within(target, start, has_end, end)with negative-index normalization, end clamping, and memmove semantics.crates/perry-hir/src/lower/expr_call/local_array_methods.rs:508-520has an HIR fast path for local array receivers with at least two arguments.But dynamic runtime method dispatch does not include a
copyWithinbranch:crates/perry-runtime/src/object/native_call_method.rshas dynamic branches for nearby mutating array methods such assort,reverse,reduce,flat,flatMap,fill, and others, but source search finds no"copyWithin"branch.crates/perry-codegen/src/lower_array_method.rs:161-167explicitly notes that the general method-dispatch fallback does not know aboutcopyWithinand silently no-ops.So any
copyWithincall that reaches dynamic dispatch, including calls onany/unproven receivers, misses the runtime helper even though the helper already exists.Expected fix shape
Add a dynamic dispatch branch that mirrors the static path:
targetargument.startto0.endwithhas_end = 0; otherwise pass the suppliedend.js_array_copy_withinand return the original receiver pointer.Please add regression coverage for dynamically dispatched
copyWithin(target),copyWithin(target, start),copyWithin(target, start, end), negative indexes, and overlapping copy regions.