Repository navigation
runtime: add dynamic Array.prototype.copyWithin dispatch #2802
Description
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-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 copyWithin branch:
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.- The static codegen comment at
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 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
targetargument. - Default omitted
startto0. - Preserve omitted
endwithhas_end = 0; otherwise pass the suppliedend. - Call
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.
Activity
fix(array): honor fromIndex/start/end/depth in indexOf/fill/flat/copy…