Skip to content

fix(runtime): TypedArray lastIndexOf routes to array path, not string (#2457) - #2501

Merged
proggeramlug merged 1 commit into
mainfrom
worktree-fix-2457-typedarray-lastindexof
May 29, 2026
Merged

fix(runtime): TypedArray lastIndexOf routes to array path, not string (#2457)#2501
proggeramlug merged 1 commit into
mainfrom
worktree-fix-2457-typedarray-lastindexof

Conversation

@proggeramlug

Copy link
Copy Markdown
Contributor

Fixes #2457.

Problem

new Int32Array([1,2,3,2,1]).lastIndexOf(2) threw TypeError: (number).lastIndexOf is not a function, while .indexOf / .includes worked (fixed in #2456).

A --trace llvm showed the divergence: ta.indexOfjs_array_indexOf_jsvalue (array path), but ta.lastIndexOfjs_string_last_index_of (string path). The HIR lowers lastIndexOf on a known-not-string / typed-array local to the string method; at runtime the typed-array pointer reads as a number, so the string dispatch throws. indexOf/includes avoided this via dedicated Expr::ArrayIndexOf/ArrayIncludes lowering — lastIndexOf had no equivalent.

Fix

Add a parallel Expr::ArrayLastIndexOf { array, value, from_index }:

  • HIR (local_array_methods): emit it for a known-not-string / typed-array local; add lastIndexOf to the ambiguous-method set so an unknown-typed local (possibly a string) still falls through to the string path.
  • Codegen (misc_methods): lower to js_array_last_index_of_jsvalue (value + optional fromIndex with spec clamping), mirroring the lower_array_method::lastIndexOf arm.
  • Runtime (array/search): re-add the typed-array branch to js_array_last_index_of_jsvalue (now reached) — backward strict-equality scan over the typed store via js_typed_array_get.
  • Wire the new variant through the walkers, collectors, monomorph substitute, and stable-hash (exhaustive matches).

Uint8Array/Buffer keep their existing byte-level runtime dispatch (they skip the array block).

Testing

  • cargo test -p perry-runtime --lib795 passed, 0 failed (adds typed_array_last_index_of).
  • Byte-for-byte vs node, all match:
    • Int32Array/Float64Array lastIndexOf (incl. fromIndex and NaN),
    • regular-array, string, and any-typed-param lastIndexOf (unchanged — no mis-routing),
    • Uint8Array lastIndexOf (byte dispatch unchanged).

Follow-up to #2456 (TypedArray indexOf/includes); part of #2447.

…ath (#2457)

`new Int32Array([1,2,3,2,1]).lastIndexOf(2)` threw
`TypeError: (number).lastIndexOf is not a function`, while `.indexOf` /
`.includes` worked. The HIR lowered `lastIndexOf` on a known-not-string /
typed-array local to the *string* `lastIndexOf` (`js_string_last_index_of`);
at runtime the typed-array pointer reads as a number, so the string
dispatch failed. `indexOf`/`includes` avoided this via dedicated
`Expr::ArrayIndexOf`/`ArrayIncludes` lowering — `lastIndexOf` had no
equivalent.

Add a parallel `Expr::ArrayLastIndexOf { array, value, from_index }`:
  * HIR (`local_array_methods`): emit it for a known-not-string / typed-array
    local, and add `lastIndexOf` to the ambiguous-method set so an
    *unknown*-typed local (which may be a string) still falls through to the
    string path.
  * Codegen (`misc_methods`): lower to `js_array_last_index_of_jsvalue`
    (value + optional fromIndex with the spec clamping), mirroring the
    `lower_array_method::lastIndexOf` arm.
  * Runtime (`array/search`): re-add the typed-array branch to
    `js_array_last_index_of_jsvalue` (now reached) — backward strict-equality
    scan over the typed store via `js_typed_array_get`.
  * Wire the new variant through the walkers, collectors, monomorph
    substitute, and stable-hash (exhaustive matches).

Uint8Array/Buffer keep their existing byte-level runtime dispatch (they skip
the array block). Verified byte-for-byte vs node: Int32Array/Float64Array
`lastIndexOf` (+ fromIndex, +NaN), and regular-array / string / any-typed
`lastIndexOf` all unchanged. Adds a `typed_array_last_index_of` unit test.
@proggeramlug
proggeramlug merged commit 215d831 into main May 29, 2026
11 checks passed
@proggeramlug
proggeramlug deleted the worktree-fix-2457-typedarray-lastindexof branch May 29, 2026 16:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TypedArray lastIndexOf mis-lowered to string path (returns wrong result / crashes 2-arg)

1 participant