Skip to content

feat(class): class X extends Array — instance methods + iteration (#6232)#6260

Open
proggeramlug wants to merge 1 commit into
PerryTS:mainfrom
proggeramlug:feat/class-extends-array
Open

feat(class): class X extends Array — instance methods + iteration (#6232)#6260
proggeramlug wants to merge 1 commit into
PerryTS:mainfrom
proggeramlug:feat/class-extends-array

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Closes #6232 (the instance-side of it).

Problem

An Array subclass instance is a plain ObjectHeader — perry has no exotic array-object representation — so every inherited Array.prototype method, index-based iteration, Array.isArray, for-of, and spread was unsupported. x.push(1) threw TypeError: value is not a function. This made class X extends Array (a very common way to build a custom collection with extra methods) effectively unusable, and was the root of #6232's new Blob(arraySubclassInstance) misbehavior.

Approach

Wire the object-backed instance into the runtime's existing spec-generic array-like engine, rather than introducing a new exotic-array representation. The change is additive: it only newly-admits Array subclasses; plain arrays, plain-object array-like borrows, Map/Set subclasses, and ordinary same-named-method classes are untouched.

Codegen (lower_call/console_promise.rs)

  • Array-subclass carve-out from the skip_native gate, mirroring the existing Map/Set-subclass carve-out. An inherited Array method on a class X extends Array receiver is not a class method, so it routes to js_native_call_method instead of the closure-call fallthrough (which read the method as a non-callable property and threw). A user-defined override of the same name still resolves earlier via the static class tower — verified.

Runtime

  • array/generic.rsis_array_subclass_class_id / is_array_subclass_instance predicates; relaxed the "plain objects only" guard in the array-like mutator engine + plain_object_value; array_subclass_dense_snapshot materializer.
  • object/native_call_method.rs — read-method arm (map/filter/join/at/indexOf/…) for Array-subclass receivers (the mutator family was already covered by the relaxed guard; the read engines had no arm outside the Proxy block).
  • array/is_array.rsArray.isArray returns true for an Array-subclass instance (ECMA-262 IsArray).
  • symbol/iterator.rs, array/from_concat.rs, array/iterator.rs — for-of / [...x] / fn(...x) / Array.from / [].concat(x) iterate a dense snapshot of the object-backed instance (the array iterator + concat spread read a real ArrayHeader and would otherwise misread the plain object — Array.isArray now reporting true routes them there).

Testing

test-files/test_gap_6232_class_extends_array.ts passes the parity harness byte-for-byte against Node (./run_parity_tests.sh --filter test_gap_6232 → Parity Pass: 1). Covers: push/pop/length, index access, own methods, map/filter/forEach/reduce/join/at/indexOf/includes/slice, Array.isArray, for-of, spread, Array.from, concat, instanceof. Manual regression pass confirmed plain arrays, plain-object borrows, Map/Set subclasses, and user same-named-method overrides are unchanged.

Known follow-ups (distinct mechanisms, intentionally out of scope)

  • Array-format toString / String() / JSON.stringify on a subclass still return object formatting ([object Object] / {"0":..}) — wrong but safe (no garbage/crash).
  • Inherited statics (Sub.from / Sub.of / Sub.isArray) — a separate static-dispatch path; this is what Bun's blob-array-fast-path DerivedArray test additionally needs.
  • Explicit .values() / .keys() / .entries() method calls.
  • Generic-class instanceof and fresh-empty .length on a generic subclass — a pre-existing perry generics limitation, not specific to this feature (class Box<T>{}; new Box() instanceof Box is also false; the instance methods + iteration above all work on generic subclasses regardless).
  • Iteration uses snapshot (not live) semantics for the object-backed instance.

Summary by CodeRabbit

  • Bug Fixes
    • Improved runtime handling of class X extends Array, including more accurate Array.isArray / instanceof behavior.
    • Fixed inherited read-only Array methods (e.g., map, filter, reduce, join, slice, concat, at, includes) so calls resolve correctly.
    • Corrected iteration, spread, Array.from, and concat, including proper handling of Symbol.iterator overrides and Symbol.isConcatSpreadable.
  • Tests
    • Added coverage for array-subclass behavior across common operations and type checks.

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b1a7738b-79a2-491f-b730-0747fe93101f

📥 Commits

Reviewing files that changed from the base of the PR and between 268f4ea and 770607a.

📒 Files selected for processing (10)
  • crates/perry-codegen/src/lower_call/console_promise.rs
  • crates/perry-runtime/src/array/from_concat.rs
  • crates/perry-runtime/src/array/generic.rs
  • crates/perry-runtime/src/array/is_array.rs
  • crates/perry-runtime/src/array/iterator.rs
  • crates/perry-runtime/src/array/mod.rs
  • crates/perry-runtime/src/array/subclass.rs
  • crates/perry-runtime/src/object/native_call_method.rs
  • crates/perry-runtime/src/symbol/iterator.rs
  • test-files/test_gap_6232_class_extends_array.ts
🚧 Files skipped from review as they are similar to previous changes (8)
  • crates/perry-runtime/src/array/mod.rs
  • crates/perry-runtime/src/array/from_concat.rs
  • test-files/test_gap_6232_class_extends_array.ts
  • crates/perry-runtime/src/symbol/iterator.rs
  • crates/perry-runtime/src/array/iterator.rs
  • crates/perry-runtime/src/array/is_array.rs
  • crates/perry-runtime/src/object/native_call_method.rs
  • crates/perry-codegen/src/lower_call/console_promise.rs

📝 Walkthrough

Walkthrough

Array subclass support adds transitive compiler detection, runtime subclass classification, dense snapshots for iteration and spread, inherited method dispatch, concat handling, relaxed mutator guards, and coverage for core Array behavior.

Changes

Array subclass support

Layer / File(s) Summary
Compiler routing for inherited Array methods
crates/perry-codegen/src/lower_call/console_promise.rs
Transitive Array inheritance detection routes selected inherited array-like methods through native dispatch.
Runtime classification and snapshots
crates/perry-runtime/src/array/subclass.rs, crates/perry-runtime/src/array/generic.rs, crates/perry-runtime/src/array/mod.rs
Runtime class-id checks identify Array subclasses, create dense snapshots, expose helpers, and admit subclasses to array-like mutator paths.
Array operations and iteration
crates/perry-runtime/src/array/is_array.rs, crates/perry-runtime/src/object/native_call_method.rs, crates/perry-runtime/src/array/from_concat.rs, crates/perry-runtime/src/array/iterator.rs, crates/perry-runtime/src/symbol/iterator.rs
Inherited reads, Array.isArray, concat, spread, and iteration use subclass-aware dispatch and snapshots.
Behavior validation
test-files/test_gap_6232_class_extends_array.ts
Tests cover inherited methods, transformations, iteration, spread, concat, Array.isArray, and instanceof for Array subclasses.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant TypeScriptCode
  participant try_lower_native_method_str_dispatch
  participant js_native_call_method
  participant dispatch_arraylike_read_method
  participant array_subclass_dense_snapshot

  TypeScriptCode->>try_lower_native_method_str_dispatch: invoke inherited Array method
  try_lower_native_method_str_dispatch->>js_native_call_method: route Array-subclass method
  js_native_call_method->>dispatch_arraylike_read_method: dispatch read operation
  dispatch_arraylike_read_method-->>TypeScriptCode: return array-like result
  TypeScriptCode->>array_subclass_dense_snapshot: materialize subclass for iteration, spread, or concat
  array_subclass_dense_snapshot-->>TypeScriptCode: provide dense indexed elements
Loading

Possibly related issues

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers the problem, approach, testing, and related issue, but it does not follow the required template sections. Rewrite the PR body to use the template headings: Summary, Changes, Related issue, Test plan, Screenshots/output, and Checklist.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: adding instance-method and iteration support for class X extends Array.
Linked Issues check ✅ Passed The changes add Array-subclass iteration and array-like dispatch, which addresses #6232 by making new Blob(x) treat subclass instances as iterable parts.
Out of Scope Changes check ✅ Passed All changes stay within the Array-subclass support objective and directly support the linked Blob/iteration behavior; no unrelated work stands out.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/perry-runtime/src/array/from_concat.rs`:
- Around line 263-275: Preserve sparse holes when creating subclass snapshots:
update array_subclass_dense_snapshot in generic.rs to detect absent indices and
store TAG_HOLE instead of writing al_get’s undefined result. Ensure
append_spread_array can replay HasProperty/Get behavior for those holes so
sparse Array subclass concat results retain their original holes.

In `@crates/perry-runtime/src/array/generic.rs`:
- Around line 1939-1952: Guard the length in array_subclass_dense_snapshot
before allocation: after obtaining len from al_length, invoke the existing
array_length_range_error() when len exceeds the supported u32 range, matching
the validation used by js_arraylike_map. Only cast to u32, allocate, and iterate
after this check so the loop cannot write beyond the allocated buffer.

In `@crates/perry-runtime/src/array/iterator.rs`:
- Around line 738-746: Update the array-subclass fast path in the iterator
resolution logic to check for an own or inherited @@iterator override before
calling array_subclass_dense_snapshot. Only use the dense snapshot when the
subclass retains the default array iterator; otherwise fall through to generic
iterator lookup so custom iterators are respected.

In `@crates/perry-runtime/src/object/native_call_method.rs`:
- Around line 889-920: Array-subclass read methods are dispatched before
user-defined overrides, causing subclass methods or own properties to be
skipped. Update the dispatch block in the native method lookup to first check
the relevant own slot/vtable override, or move dispatch_arraylike_read_method
behind generic dispatch, while preserving fast-path handling only when no
override exists.

In `@crates/perry-runtime/src/symbol/iterator.rs`:
- Around line 171-183: Ensure the Array subclass fast path in the iterator
dispatch checks for an own or inherited custom @@iterator before snapshotting.
Update the branch using is_array_subclass_instance and
array_subclass_dense_snapshot to detect the default Array.prototype iterator,
and fall through to the generic symbol lookup when the iterator is overridden or
patched, matching the Map/Set subclass handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0a4d7289-bcc7-4812-9348-e0fc073c5bc5

📥 Commits

Reviewing files that changed from the base of the PR and between 83f4fcb and 925ac01.

📒 Files selected for processing (9)
  • crates/perry-codegen/src/lower_call/console_promise.rs
  • crates/perry-runtime/src/array/from_concat.rs
  • crates/perry-runtime/src/array/generic.rs
  • crates/perry-runtime/src/array/is_array.rs
  • crates/perry-runtime/src/array/iterator.rs
  • crates/perry-runtime/src/array/mod.rs
  • crates/perry-runtime/src/object/native_call_method.rs
  • crates/perry-runtime/src/symbol/iterator.rs
  • test-files/test_gap_6232_class_extends_array.ts

Comment on lines +263 to +275
// `class X extends Array` — the instance is object-backed, so the dense
// `ArrayHeader` spread below would misread it. `js_array_is_array` reports
// true for it, so intercept before that branch and spread a dense snapshot
// of its elements (honoring an explicit `@@isConcatSpreadable === false`).
if crate::array::is_array_subclass_instance(value) {
if spreadable == Some(false) {
return js_array_push_f64(result, value);
}
let snap = crate::array::array_subclass_dense_snapshot(value);
let snap_ptr = (snap.to_bits() & 0x0000_FFFF_FFFF_FFFF) as *const ArrayHeader;
return append_spread_array(result, snap_ptr);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check what al_get returns for absent indices on object-backed array subclass instances.
rg -n -A 20 'pub.*fn al_get\b' crates/perry-runtime/src/array/generic.rs

Repository: PerryTS/perry

Length of output: 151


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate relevant symbols and files.
rg -n "array_subclass_dense_snapshot|append_spread_array|al_get\\b|TAG_HOLE|is_array_subclass_instance|subclass_backing_for_default_iteration" crates/perry-runtime/src

Repository: PerryTS/perry

Length of output: 14478


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the array module files and sizes before reading targeted sections.
git ls-files 'crates/perry-runtime/src/array/**' | sort

Repository: PerryTS/perry

Length of output: 1114


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find the source files that define the relevant helpers.
fd -a -t f 'generic.rs|from_concat.rs|*.rs' crates/perry-runtime/src/array

Repository: PerryTS/perry

Length of output: 487


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '412,470p;1939,1968p' crates/perry-runtime/src/array/generic.rs
printf '\n----\n'
sed -n '724,770p' crates/perry-runtime/src/array/from_concat.rs

Repository: PerryTS/perry

Length of output: 5844


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the array-subclass snapshot path and the concat spreader.
sed -n '412,470p;1939,1968p' crates/perry-runtime/src/array/generic.rs
printf '\n----\n'
sed -n '724,770p' crates/perry-runtime/src/array/from_concat.rs

Repository: PerryTS/perry

Length of output: 5844


Preserve holes in array-subclass concat snapshots. crates/perry-runtime/src/array/generic.rs::array_subclass_dense_snapshot writes al_get results directly, so absent indices become undefined instead of TAG_HOLE. append_spread_array only replays HasProperty/Get for TAG_HOLE, so sparse class X extends Array instances will spread with explicit undefined elements.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/perry-runtime/src/array/from_concat.rs` around lines 263 - 275,
Preserve sparse holes when creating subclass snapshots: update
array_subclass_dense_snapshot in generic.rs to detect absent indices and store
TAG_HOLE instead of writing al_get’s undefined result. Ensure
append_spread_array can replay HasProperty/Get behavior for those holes so
sparse Array subclass concat results retain their original holes.

Comment on lines +1939 to +1952
pub fn array_subclass_dense_snapshot(recv: f64) -> f64 {
let len = al_length(recv).max(0);
let result = js_array_alloc_with_length(len as u32);
let elems = unsafe { (result as *mut u8).add(std::mem::size_of::<ArrayHeader>()) as *mut f64 };
for k in 0..len {
let v = al_get(recv, k);
unsafe {
// GC_STORE_AUDIT(BARRIERED): note_array_slot re-stores with the barrier.
ptr::write(elems.add(k as usize), v);
note_array_slot(result, k as usize, v.to_bits());
}
}
nanbox_arr(result)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm js_arraylike_map's guard pattern and array_length_range_error signature.
rg -nP -C3 'array_length_range_error' crates/perry-runtime/src/array/

Repository: PerryTS/perry

Length of output: 2682


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the snapshot implementation and the length helper it relies on.
sed -n '1928,1960p' crates/perry-runtime/src/array/generic.rs
printf '\n---- al_length / related helpers ----\n'
rg -n -C3 '\bal_length\b|ToLength|length property|array_length_range_error' crates/perry-runtime/src/array/generic.rs crates/perry-runtime/src/array/alloc.rs

Repository: PerryTS/perry

Length of output: 24117


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect al_length and the array allocator to check for any internal range checks or truncation behavior.
sed -n '260,330p' crates/perry-runtime/src/array/generic.rs
printf '\n---- allocator ----\n'
sed -n '1,120p' crates/perry-runtime/src/array/alloc.rs
printf '\n---- snapshot neighbors for comparison ----\n'
sed -n '640,680p' crates/perry-runtime/src/array/generic.rs
sed -n '1038,1052p' crates/perry-runtime/src/array/generic.rs

Repository: PerryTS/perry

Length of output: 10942


Guard len before allocating the snapshot array
al_length can exceed u32::MAX; casting it to u32 truncates the allocation while the loop still writes len slots, which can overrun the buffer. Mirror the array_length_range_error() check used in js_arraylike_map here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/perry-runtime/src/array/generic.rs` around lines 1939 - 1952, Guard
the length in array_subclass_dense_snapshot before allocation: after obtaining
len from al_length, invoke the existing array_length_range_error() when len
exceeds the supported u32 range, matching the validation used by
js_arraylike_map. Only cast to u32, allocate, and iterate after this check so
the loop cannot write beyond the allocated buffer.

Comment thread crates/perry-runtime/src/array/iterator.rs
Comment thread crates/perry-runtime/src/object/native_call_method.rs
Comment thread crates/perry-runtime/src/symbol/iterator.rs Outdated
@proggeramlug proggeramlug force-pushed the feat/class-extends-array branch from 925ac01 to 268f4ea Compare July 10, 2026 21:14
…erryTS#6232)

An `Array` subclass instance is a plain `ObjectHeader` (perry has no exotic
array-object representation), so the inherited `Array.prototype` methods, index
access, `Array.isArray`, and iteration were all unsupported: `x.push(1)` threw
"value is not a function". This wires the object-backed instance into the
runtime's existing spec-generic array-like engine so real code using an Array
subclass as a custom collection works.

Codegen (console_promise.rs):
  - Array-subclass carve-out from the `skip_native` gate, mirroring the existing
    Map/Set-subclass carve-out: an inherited Array method (`push`/`map`/`join`/…)
    on a `class X extends Array` receiver is NOT a class method, so route it to
    `js_native_call_method` instead of the closure-call fallthrough (which read
    the method as a non-callable property and threw). A user-defined override of
    the same name still resolves earlier via the static class tower.

Runtime:
  - array/generic.rs: `is_array_subclass_class_id` / `is_array_subclass_instance`
    predicates; relaxed the "plain objects only" guard in the array-like mutator
    engine and `plain_object_value` to admit Array-subclass instances;
    `array_subclass_dense_snapshot` materializer.
  - object/native_call_method.rs: read-method arm (`map`/`filter`/`join`/`at`/…)
    for Array-subclass receivers (the mutator arm was already covered by the
    relaxed guard; the read engines had no arm).
  - array/is_array.rs: `Array.isArray` returns true for an Array-subclass
    instance (ECMA-262 IsArray).
  - symbol/iterator.rs + array/from_concat.rs + array/iterator.rs: iterate/spread
    a dense snapshot of the object-backed instance (for-of, `[...x]`, `fn(...x)`,
    `Array.from`, `[].concat(x)`), since the array iterator / concat spread read
    a real `ArrayHeader` and would otherwise misread the plain object.

Verified byte-for-byte against Node (test-files/test_gap_6232_class_extends_array.ts):
push/pop/length, index access, own methods, map/filter/forEach/reduce/join/at/
indexOf/includes/slice, Array.isArray, for-of, spread, Array.from, concat,
instanceof. Additive: plain arrays, plain-object array-like borrows, Map/Set
subclasses, and ordinary same-named-method classes are unchanged.

Known follow-ups (distinct mechanisms, out of scope here): array-format
`toString`/`String()`/`JSON.stringify` (still object-formatted, not garbage);
inherited statics (`Sub.from`/`.of`); explicit `.values()`/`.keys()`/`.entries()`;
and generic-class `instanceof` / fresh-empty `.length` (a pre-existing perry
generics limitation — `class Box<T>{}; new Box() instanceof Box` is also false).
@proggeramlug proggeramlug force-pushed the feat/class-extends-array branch from 268f4ea to 770607a Compare July 10, 2026 22:20
@proggeramlug

Copy link
Copy Markdown
Contributor Author

Thanks @coderabbitai — addressed the review in the latest push (770607a):

  1. array_subclass_dense_snapshot length guard (Major) — added the len > u32::MAXarray_length_range_error() check before allocation, matching js_arraylike_map. This also prevents the as u32 truncation from under-allocating while the 0..len loop iterated the full i64 count (the real hazard). ✅
  2. Read-arm own-method override (native_call_method.rs, Major) — the Array-subclass read arm now defers via a new object_owns_user_method gate (mirrors the own-slot check try_object_arraylike_mutator already applies to the mutating family), so an own this.map = fn override wins. ✅
  3. [Symbol.iterator] override in js_get_iterator (Major) — added array_subclass_has_iterator_override (mirrors map_set_subclass::subclass_has_iterator_override: own symbol prop + @@iterator class method). The snapshot interception is skipped when an override exists, and the is_array branch is now guarded so an override-subclass falls through to the generic symbol lookup that resolves the user iterator. ✅
  4. [Symbol.iterator] override in spread (array_from_spread_value, Minor) — same array_subclass_has_iterator_override guard, so [...sub] / fn(...sub) honor a custom iterator. ✅
  5. Sparse holes in concat snapshot (Minor) — documented as an accepted limitation: the snapshot materializes absent indices as undefined, which is correct for iteration/spread (the array iterator yields undefined for holes anyway) and only differs for the rare concat of a sparse Array subclass. Preserving true holes would pull in the numeric-layout/hole machinery for a rare case; left for a follow-up.

Verified byte-for-byte against Node — the fixture now also covers a *[Symbol.iterator]() override winning over default iteration (spread/Array.from/for-of100,200).

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
gh pr view 6260 --repo PerryTS/perry --json commits -q '.commits[-3:]' || true
gh api repos/PerryTS/perry/commits/770607a --jq '.files[].filename' || true

Length of output: 3794


🏁 Script executed:

#!/bin/bash
set -euo pipefail
git fetch origin pull/6260/head:pr6260 2>&1 | tail -5 || true
git checkout pr6260 2>&1 | tail -5 || true

echo "--- subclass.rs length guard ---"
rg -n "u32::MAX|array_length_range_error" crates/perry-runtime/src/array/subclass.rs

echo "--- native_call_method.rs own method gate ---"
rg -n "object_owns_user_method|is_array_subclass" crates/perry-runtime/src/object/native_call_method.rs

echo "--- iterator override in symbol/iterator.rs ---"
rg -n "array_subclass_has_iterator_override|is_array" crates/perry-runtime/src/symbol/iterator.rs

echo "--- iterator override in array/iterator.rs ---"
rg -n "array_subclass_has_iterator_override" crates/perry-runtime/src/array/iterator.rs

echo "--- subclass.rs iterator override fn ---"
rg -n "fn array_subclass_has_iterator_override" -A 30 crates/perry-runtime/src/array/subclass.rs

Length of output: 2806


All five fixes check out in commit 770607a: the u32::MAX length guard with array_length_range_error() before allocation in subclass.rs, the object_owns_user_method gate on the read arm in native_call_method.rs, and the array_subclass_has_iterator_override guard (own Symbol.iterator property check plus @@iterator class-method lookup) applied consistently in both js_get_iterator (symbol/iterator.rs) and array_from_spread_value (array/iterator.rs). The sparse-holes-as-undefined limitation is a reasonable, well-documented tradeoff for now.

Thanks for the thorough turnaround and the byte-for-byte Node parity verification, including the new [Symbol.iterator]() override fixture — nicely done.

ヽ(・∀・)ノ

@proggeramlug

Copy link
Copy Markdown
Contributor Author

CI status — conformance-smoke (1) red is a pre-existing flake, not this PR.

13/14 required checks pass (lint, cargo-test, compiler-output-regression, api-docs-drift, CodeRabbit, and conformance-smoke shards 2–8). Only shard 1 is red, on three tests entirely unrelated to class extends Array:

  • test_gap_node_v8_3137plus — already triaged in test-parity/known_failures.json.
  • test_gap_string_locale_2781_2845_2897 and test_gap_zlib_4917_levelboth pass locally byte-for-byte against Node with this exact branch's binary; the CI mismatch is Node-version reference drift (locale/zlib output is Node-version-sensitive), not a Perry regression.

This diff only touches the Array subclass path (array/*, symbol/iterator.rs, object/native_call_method.rs, and the codegen carve-out); none of it can affect zlib/locale/v8 serialization. Same shard-1 flakiness that required an admin-merge for #6238 and #6244. Re-ran shard 1 once already — same three env-drift tests.

Happy to rebase again if a later main pins the Node version, but this branch is otherwise green.

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.

runtime: new Blob(x) where x is an Array subclass stringifies the constructor instead of iterating elements

1 participant