Iterator.from(x) returns an iterator that is exhausted before it starts, and
the whole TC39 iterator-helpers surface goes with it: .map, .filter,
.take, .drop, .flatMap all return undefined, so any chain throws
TypeError: Cannot read properties of undefined.
Found while working #7564 (which touches the same file). Not caused by it —
verified byte-identical on the pre-#7564 runtime.
Repro
function makeCounter(n: number): Iterator<number> {
let i = 0;
return {
next(): IteratorResult<number> {
return i < n ? { value: i++, done: false } : { value: undefined, done: true };
},
};
}
const a = Iterator.from(makeCounter(4));
const out: unknown[] = [];
let s = a.next();
let guard = 0;
while (!s.done && guard++ < 20) { out.push(s.value); s = a.next(); }
console.log("A Iterator.from({next}):", JSON.stringify(out));
console.log("B Iterator.from(arrayIter):", JSON.stringify([...(function () {
const b = Iterator.from([7, 8, 9][Symbol.iterator]());
const o: unknown[] = []; let t = b.next(); let g = 0;
while (!t.done && g++ < 20) { o.push(t.value); t = b.next(); }
return o;
})()]));
// the same object driven directly, WITHOUT the helper
const c = makeCounter(3);
const outC: unknown[] = []; let sc = c.next(); guard = 0;
while (!sc.done && guard++ < 20) { outC.push(sc.value); sc = c.next(); }
console.log("C bare {next}:", JSON.stringify(outC));
const d = Iterator.from(makeCounter(2));
console.log("D first step:", JSON.stringify(d.next()));
| line |
node 26.5.1 |
perry |
A Iterator.from({next}) |
[0,1,2,3] |
[] |
B Iterator.from(arrayIter) |
[7,8,9] |
[] |
C bare {next} (no helper) |
[0,1,2] |
[0,1,2] ✅ |
| D first step |
{"value":0,"done":false} |
{"done":true} |
E Iterator.from(mapIter) |
[1,2] |
[] |
Row C is the tell: the underlying iterator works fine when driven directly. It
is the helper wrapper that reports done on its very first step, for every
source kind — bare {next} object, array iterator, Map iterator.
And the chain form:
Iterator.from(gen()).map((x) => x * 2).filter((x) => x % 3 !== 0);
// TypeError: Cannot read properties of undefined (reading 'filter')
.map(...) returns undefined, so .filter on it throws.
Where to look
crates/perry-runtime/src/iterator_helpers.rs. Iterator.from is
js_iterator_from (~line 168) building an OP_IDENTITY helper object;
helper_next (~line 188) then drives it through iterator_step. Method
dispatch is dispatch_iterator_helper_method (~line 367), routed from
object/native_call_method/collection_methods.rs:410 and
.../handle_methods.rs:855.
Since .map/.filter return undefined rather than a helper object, the
method-dispatch arm is the first thing to check — an unrecognized method name
falling through to a next-shaped return would explain both symptoms at once
(the identity case reporting done immediately, and the combinators returning
nothing).
Impact
The entire iterator-helpers proposal surface is non-functional, silently for
Iterator.from (wrong answer, no error) and loudly for the combinators. Silent
is the worse half: a program that iterates Iterator.from(x) gets an empty
sequence and no diagnostic.
Iterator.from(x)returns an iterator that is exhausted before it starts, andthe whole TC39 iterator-helpers surface goes with it:
.map,.filter,.take,.drop,.flatMapall returnundefined, so any chain throwsTypeError: Cannot read properties of undefined.Found while working #7564 (which touches the same file). Not caused by it —
verified byte-identical on the pre-#7564 runtime.
Repro
Iterator.from({next})[0,1,2,3][]Iterator.from(arrayIter)[7,8,9][]{next}(no helper)[0,1,2][0,1,2]✅{"value":0,"done":false}{"done":true}Iterator.from(mapIter)[1,2][]Row C is the tell: the underlying iterator works fine when driven directly. It
is the helper wrapper that reports
doneon its very first step, for everysource kind — bare
{next}object, array iterator, Map iterator.And the chain form:
.map(...)returnsundefined, so.filteron it throws.Where to look
crates/perry-runtime/src/iterator_helpers.rs.Iterator.fromisjs_iterator_from(~line 168) building anOP_IDENTITYhelper object;helper_next(~line 188) then drives it throughiterator_step. Methoddispatch is
dispatch_iterator_helper_method(~line 367), routed fromobject/native_call_method/collection_methods.rs:410and.../handle_methods.rs:855.Since
.map/.filterreturnundefinedrather than a helper object, themethod-dispatch arm is the first thing to check — an unrecognized method name
falling through to a
next-shaped return would explain both symptoms at once(the identity case reporting
doneimmediately, and the combinators returningnothing).
Impact
The entire iterator-helpers proposal surface is non-functional, silently for
Iterator.from(wrong answer, no error) and loudly for the combinators. Silentis the worse half: a program that iterates
Iterator.from(x)gets an emptysequence and no diagnostic.