Repository navigation
runtime: honor fromIndex for Array and TypedArray indexOf/includes #2804
Description
Summary
Array.prototype.indexOf, Array.prototype.includes, and the matching %TypedArray% methods should honor the optional fromIndex argument. Perry's static indexOf / includes codegen currently accepts exactly one argument, and the runtime helpers / dynamic dispatch paths only search from index 0.
Node parity probe
Run with Node v25.9.0:
const arr = [1, 2, 1, NaN];
const ta = new Float64Array([1, 2, 1, NaN]);
for (const [label, recv] of [["Array", arr], ["TypedArray", ta]]) {
for (const [caseLabel, fn] of [
["indexOf from 1", () => recv.indexOf(1, 1)],
["indexOf from -2", () => recv.indexOf(1, -2)],
["indexOf from Infinity", () => recv.indexOf(1, Infinity)],
["includes NaN", () => recv.includes(NaN)],
["includes from 4", () => recv.includes(1, 4)],
["includes from -2", () => recv.includes(1, -2)],
["includes from Infinity", () => recv.includes(1, Infinity)],
]) {
try {
console.log(`${label}.${caseLabel}: ${JSON.stringify(fn())}`);
} catch (e) {
console.log(`${label}.${caseLabel}: throws ${e.name}: ${e.message}`);
}
}
}Output:
Array.indexOf from 1: 2
Array.indexOf from -2: 2
Array.indexOf from Infinity: -1
Array.includes NaN: true
Array.includes from 4: false
Array.includes from -2: true
Array.includes from Infinity: false
TypedArray.indexOf from 1: 2
TypedArray.indexOf from -2: 2
TypedArray.indexOf from Infinity: -1
TypedArray.includes NaN: true
TypedArray.includes from 4: false
TypedArray.includes from -2: true
TypedArray.includes from Infinity: false
Current Perry behavior in source
lastIndexOf already has an optional-from path, but forward search does not:
crates/perry-codegen/src/lower_array_method.rs:420-450requiresArray.includesto have exactly one argument and callsjs_array_includes_jsvalue(arr, value).crates/perry-codegen/src/lower_array_method.rs:451-472requiresArray.indexOfto have exactly one argument and callsjs_array_indexOf_jsvalue(arr, value).- By contrast,
crates/perry-codegen/src/lower_array_method.rs:474-492accepts one or two args forlastIndexOfand passesfromIndexplus ahas_fromflag.
The runtime helpers also search from the beginning only:
crates/perry-runtime/src/array/search.rs:41-65implementsjs_array_indexOf_jsvalue(arr, value)and loops from0for Arrays and TypedArrays.crates/perry-runtime/src/array/search.rs:175-206implementsjs_array_includes_jsvalue(arr, value)and loops from0for Arrays and TypedArrays while using SameValueZero.crates/perry-runtime/src/object/native_call_method.rs:1262-1271dynamic dispatch forindexOf/includesreads only the first argument and calls those two-argument helpers.
So indexOf(value, fromIndex) and includes(value, fromIndex) either fail the static codegen arity check or silently ignore fromIndex in dynamic dispatch.
Expected fix shape
Add from-index-aware helpers or extend the existing helpers so both static and dynamic paths can pass the optional second argument:
- Omitted
fromIndex: start at0. - Positive finite
fromIndex: start at that index, returning not-found when it is>= length. - Negative
fromIndex: start atmax(length + fromIndex, 0). NaN: treat as0.Infinity: return not-found for forward search.- Preserve
indexOfstrict-equality behavior andincludesSameValueZero behavior, includingincludes(NaN) === true.
Please cover Array and TypedArray receivers, static and dynamic dispatch, and fromIndex values undefined, positive, negative, NaN, and Infinity.
Related but separate: #2457 / PR #2501 covered TypedArray lastIndexOf; this issue is for forward indexOf / includes optional fromIndex handling.
Activity
fix(array): honor fromIndex/start/end/depth in indexOf/fill/flat/copy…