Repository navigation
Array methods absent from %TypedArray%.prototype answer instead of throwing (flat, flatMap, push, pop, shift, unshift, splice, toSpliced) #8138
Description
Summary
Nine Array.prototype methods that do not exist on %TypedArray%.prototype
answer with a value on a typed-array receiver instead of throwing. Node throws
TypeError: … is not a function; perry returns [], undefined, or garbage.
function opaque(x: any): any { return x; }
opaque(new Int32Array([1, 2, 3])).flatMap((x: any) => [x])
// node: TypeError: opaque(...).flatMap is not a function
// perry: [5.08e-321, 2.1219957924e-314, 1e-323]Measured against node v26.5.1 on main @ 0a1e78e5f.
Full list
The methods absent from %TypedArray%.prototype are exactly:
concat, flat, flatMap, pop, push, shift, splice, toSpliced,
unshift.
| expression | perry | node |
|---|---|---|
i32.concat([9]) |
[9] |
TypeError |
i32.flat() |
[] |
TypeError |
i32.flatMap(f) |
[5.08e-321,2.12e-314,1e-323] — garbage |
TypeError |
i32.push(9) |
[] |
TypeError |
i32.pop() |
undefined |
TypeError |
i32.shift() |
undefined |
TypeError |
i32.unshift(9) |
[] |
TypeError |
i32.splice(1,1) |
{obj}, receiver untouched |
TypeError |
i32.toSpliced(1,1) |
[] |
TypeError |
u8.flat() |
[] |
TypeError |
u8.toSpliced(1,1) |
[] |
TypeError |
u8.push(9) |
[] |
TypeError |
u8.splice, u8.concat and u8.flatMap DO throw — but with
TypeError: (Buffer).splice is not a function, i.e. right answer, wrong
message and wrong reason (dispatch_buffer_method's name list simply has no
entry, rather than the property genuinely being absent).
Why this is NOT the #8041 receiver-reroute family
These helpers are dead below clean_arr_ptr in the same textual sense as the
ones #8090/#8109/#8119/#8120/#8130 fixed, and it is tempting to file them
together. They are a different bug, and "fixing the reroute" would make them
worse:
js_array_concat is the worked example, already noted in #8130's caveats. Node
gives [{}] for a typed-array argument; perry gives [] today; and the
pre-#8041 live reroute gave [1,2,3]. It was wrong before #8041 and wrong
after, in different ways. Waking the reroute back up would restore an answer
node never produces.
The actual defect is one level up: the property should not resolve at all.
Perry's method dispatch answers flatMap on a typed array because the fused
Expr::ArrayFlatMap fold and is_array_expr both treat Int32Array as an
array, and nothing consults %TypedArray%.prototype's membership. So the fix
is a receiver-kind check at method resolution, producing the spec
TypeError, not a reroute inside the helper.
js_array_concat specifically is being handled by #8124
(concat-spreadable); this issue covers the other eight.
Suggested shape
A single table of the nine names, consulted where the typed-array/Buffer
dispatch decision is already made — dispatch_typed_array_method
(object/native_call_method/typed_array.rs) and dispatch_buffer_method
(object/buffer_dispatch.rs) already return None / "is not a function" for
unrecognised names, so the missing piece is preventing the static HIR folds
from claiming these nine for a receiver whose type is a typed array. That is
the same gate is_array_expr already applies to exclude Uint8Array from
copyWithin's fold (local_array_methods.rs:800-839), generalised.
Low urgency: every row here is a program that is already broken and would
throw under node, so no working code depends on the current answers. The
flatMap row is the one worth prioritising, because it returns garbage
values rather than an empty result, which a caller can propagate silently.
Context
Found by the exhaustive Array.prototype receiver sweep that produced #8135.
The full 33-row table, with the funnel verdict and measured node diff for every
Array.prototype-reachable runtime entry point, is in the #8135 PR description.
Activity
proggeramlug commentedon Aug 15, 2026
The "Suggested shape" premise is false — measured
This issue proposes:
The first half is only true for
dispatch_buffer_method. Measured onmain@
a3118cfea, oracle node26.5.1,--profile perry-dev,PERRY_NO_AUTO_OPTIMIZE=1:dispatch_typed_array_methoddoes returnNonefor an unrecognised name, butthe dispatch tower's tail then answers the empty string — and it does so
for a plain array receiver too, not just a typed array. So "decline the
fold" produces
"", not aTypeError: the nine names would move from a wrongvalue to a different wrong value.
Making them throw therefore means introducing an unknown-method throw for
array-ish receivers in general — a much broader behavioural change than the
nine names, with its own blast radius (every
is_array_expr-classifiedreceiver reaching the tower's tail). And the static-receiver half
(
i32.flat(), 11 of the 29 measured rows) has to decline inis_array_expr/the HIR folds, which puts
perry-codegenin the diff and brings theperry-codegenintegration suite (1476 passed / 9 failed baseline, not runper-PR) into scope.
Full measured table on
a3118cfeau8.*rows already throw with the right shape but the wrong reason(
dispatch_buffer_method's name list has no entry).i32.*rows do not throwat all:
opaque(i32).flat()[]opaque(i32).flatMap(f)[5.08e-321,4.24e-314,1.5e-323]— garbageopaque(i32).push(9)0opaque(i32).pop()undefinedopaque(i32).shift()undefinedopaque(i32).unshift(9)0opaque(i32).splice(1,1)[]opaque(i32).toSpliced(1,1)[]opaque(i32).concat([9])[9]opaque(u8).flat()[]opaque(u8).toSpliced(1,1)[]i32.flat()(static)[](i32 as any).push(9)(static)1, and the binding is corrupted(i32 as any).pop()(static)9(i32 as any).toSpliced(1,1)(static)[9](u8 as any).flat()(static)[](u8 as any).toSpliced(1,1)(static)[]The static rows are worse than the issue records. After
(i32 as any).push(9); (i32 as any).pop()the receiver itself is destroyed:Array.from(i32)reads[9]where node has[1,2,3]— theArrayHeadermutators rewrote the
TypedArrayHeader's{length, capacity}prefix. Any fixneeds a control asserting the receiver's contents are UNCHANGED after each
attempted call, not only that the call threw.
Suggested re-scoping
typed-array receiver throw? That is the actual defect and it subsumes all
nine names.
opaque(arr).nosuch()returning""is its own bug.already cover them.
TypeError: <name> is not a function(the object path) or
TypeError: (Buffer).<name> is not a function(thebuffer path); node's
opaque(...).<name> is not a functionneeds thecall-site expression text, which the runtime does not have. A test should
assert the shape, not byte-equality with node.
Measured while fixing #8149 / #8139 (PR #8186), which were filed from the same
sweep. Those two do not share a cause with this one: #8149 is the
receiver-routing family, #8139 is a missing arm in a value-kind dispatcher, and
this issue is about method existence.