mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-24 06:14:01 +08:00
- Lower number-array and typed-array value iterators statically. - Preserve stored iterator cursor and exhaustion state across loops. - Add Node differential coverage for arrays and Uint8Array.
88 lines
2.9 KiB
TypeScript
88 lines
2.9 KiB
TypeScript
// Stored ArrayIterator<number> values consumed by for-of. The iterator
|
|
// keeps its cursor across loops, advances before the body runs, observes
|
|
// live array growth until it is exhausted, and stays exhausted after that
|
|
// even if the source grows again. Node is the byte-for-byte oracle.
|
|
const numbers = [10, 20, 30];
|
|
const values = numbers.values();
|
|
|
|
for (const n of values) {
|
|
console.log("first", n);
|
|
if (n === 10) break;
|
|
}
|
|
|
|
numbers.push(40);
|
|
for (const n of values) {
|
|
if (n === 30) continue;
|
|
console.log("resume", n);
|
|
}
|
|
|
|
numbers.push(50);
|
|
for (const n of values) console.log("exhausted", n);
|
|
|
|
// The default array iterator is the same values protocol. A pre-declared
|
|
// head is assigned per step, and a break leaves the cursor ready to resume.
|
|
const viaSymbol = [1, 2, 3][Symbol.iterator]();
|
|
let current = 0;
|
|
for (current of viaSymbol) {
|
|
console.log("symbol", current);
|
|
break;
|
|
}
|
|
for (current of viaSymbol) console.log("symbol-rest", current);
|
|
console.log("last", current);
|
|
|
|
// Growth while a loop is active is visible because ArrayIterator.next()
|
|
// re-reads the source length for each step.
|
|
const liveNumbers = [7];
|
|
const liveValues = liveNumbers.values();
|
|
for (const n of liveValues) {
|
|
console.log("live", n);
|
|
if (n < 9) liveNumbers.push(n + 1);
|
|
}
|
|
|
|
// Function-local iterator state is independent per call, and the source
|
|
// expression is evaluated exactly once when the iterator is created.
|
|
function makeNumbers(limit: number): number[] {
|
|
console.log("make", limit);
|
|
return [limit, limit + 1];
|
|
}
|
|
function walkLocal(limit: number): number {
|
|
const localValues = makeNumbers(limit).values();
|
|
let sum = 0;
|
|
for (const n of localValues) sum += n;
|
|
return sum;
|
|
}
|
|
console.log("local", walkLocal(4), walkLocal(8));
|
|
|
|
// Uint8Array uses the same numeric value-iterator protocol. Direct
|
|
// iteration evaluates the source once and observes element writes that
|
|
// happen before a later index is visited.
|
|
function makeBytes(): Uint8Array {
|
|
console.log("make-bytes");
|
|
return new Uint8Array([250, 2, 3]);
|
|
}
|
|
const directBytes = makeBytes();
|
|
for (const byte of directBytes) {
|
|
console.log("byte", byte);
|
|
if (byte === 250) directBytes[1] = 8;
|
|
}
|
|
|
|
// Stored typed-array iterators retain their cursor across loops just like
|
|
// stored number[] iterators.
|
|
const byteValues = new Uint8Array([11, 12, 13]).values();
|
|
for (const byte of byteValues) {
|
|
console.log("byte-first", byte);
|
|
break;
|
|
}
|
|
for (const byte of byteValues) console.log("byte-rest", byte);
|
|
for (const byte of byteValues) console.log("byte-exhausted", byte);
|
|
|
|
// Both direct and stored default-iterator spellings yield numbers.
|
|
for (const byte of new Uint8Array([21, 22]).values()) {
|
|
console.log("byte-direct-values", byte);
|
|
}
|
|
for (const byte of new Uint8Array([31, 32])[Symbol.iterator]()) {
|
|
console.log("byte-direct-symbol", byte);
|
|
}
|
|
const byteSymbol = new Uint8Array([41, 42])[Symbol.iterator]();
|
|
for (const byte of byteSymbol) console.log("byte-symbol", byte);
|