Assigning a patched .next() onto a builtin Set iterator and then driving it with for…of segfaults (exit 139).
Reproducer
const s = new Set<number>([10, 20, 30, 40]);
const it: any = s.values();
let calls = 0;
const orig = it.next.bind(it);
it.next = function () { calls++; return orig(); };
const got: number[] = [];
for (const v of it) got.push(v as number);
console.log("E", calls > 0, got.join(","));
Node 26.5.1 (the pinned oracle) prints E true 10,20,30,40. Perry dies with SIGSEGV before printing anything.
Not a regression — pre-existing
Found while auditing #9017, so I A/B'd it before attributing it. Both arms are --profile perry-dev builds of -p perry -p perry-runtime-static -p perry-stdlib-static, compiled with PERRY_NO_AUTO_OPTIMIZE=1:
| build |
result |
| #9017 branch |
exit 139 |
main @ 06151e61f3 (no #9017) |
exit 139 |
So main has it too and #9017 neither causes nor worsens it. Recording that explicitly because the crash surfaces on the exact path #9017 documents ("a patched next wins exactly as it does on the manual path"), which makes it easy to misattribute.
Why it matters beyond the crash
It means the override path on builtin collection iterators has no working end-to-end coverage: any test that would exercise it dies first. #9017 keeps an override probe inside the dispatcher and states that a patched next still wins — that claim cannot currently be validated through for…of, because the baseline crashes.
Scope
s.values() with a patched next, driven by for…of → SIGSEGV.
- The same iterator driven by manual
.next() calls is fine (case B/C of my probe: manual .next() returns fresh, correct results).
- Not yet checked:
Map iterators, array iterators, generators, and whether an own next assigned before first use behaves differently. Worth doing when someone picks this up.
No stderr, no panic message — a bare SIGSEGV, so the first step is a debug/ASAN build or PERRY_GC_PROTECT_FROMSPACE=1 to see whether it is a rooting fault or a bad dispatch target.
Assigning a patched
.next()onto a builtin Set iterator and then driving it withfor…ofsegfaults (exit 139).Reproducer
Node 26.5.1 (the pinned oracle) prints
E true 10,20,30,40. Perry dies with SIGSEGV before printing anything.Not a regression — pre-existing
Found while auditing #9017, so I A/B'd it before attributing it. Both arms are
--profile perry-devbuilds of-p perry -p perry-runtime-static -p perry-stdlib-static, compiled withPERRY_NO_AUTO_OPTIMIZE=1:main@06151e61f3(no #9017)So
mainhas it too and #9017 neither causes nor worsens it. Recording that explicitly because the crash surfaces on the exact path #9017 documents ("a patchednextwins exactly as it does on the manual path"), which makes it easy to misattribute.Why it matters beyond the crash
It means the override path on builtin collection iterators has no working end-to-end coverage: any test that would exercise it dies first. #9017 keeps an override probe inside the dispatcher and states that a patched
nextstill wins — that claim cannot currently be validated throughfor…of, because the baseline crashes.Scope
s.values()with a patchednext, driven byfor…of→ SIGSEGV..next()calls is fine (case B/C of my probe: manual.next()returns fresh, correct results).Mapiterators, array iterators, generators, and whether an ownnextassigned before first use behaves differently. Worth doing when someone picks this up.No stderr, no panic message — a bare SIGSEGV, so the first step is a debug/ASAN build or
PERRY_GC_PROTECT_FROMSPACE=1to see whether it is a rooting fault or a bad dispatch target.