diff --git a/features.txt b/features.txt index 44ed23b1594..0e623ee1b97 100644 --- a/features.txt +++ b/features.txt @@ -158,6 +158,7 @@ globalThis hashbang import-attributes import.meta +iterator-chunking iterator-helpers iterator-sequencing Int8Array diff --git a/test/built-ins/Iterator/prototype/chunks/argument-effect-order.js b/test/built-ins/Iterator/prototype/chunks/argument-effect-order.js new file mode 100644 index 00000000000..e225374d37c --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/argument-effect-order.js @@ -0,0 +1,60 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.chunks +description: > + Arguments and this value are validated in the correct order +info: | + Iterator.prototype.chunks ( chunkSize ) + + 1. Let O be the this value. + 2. If O is not an Object, throw a TypeError exception. + 3. Let iterated be the Iterator Record { [[Iterator]]: O, [[NextMethod]]: undefined, [[Done]]: false }. + 4. If chunkSize is not a Number, throw a TypeError ... IteratorClose. + 5. If chunkSize is not an integral Number, throw a TypeError ... IteratorClose. + 6. If chunkSize is not in the inclusive interval from 1๐”ฝ to ๐”ฝ(2^32 - 1), then + a. Let error be ThrowCompletion(a newly created RangeError object). + b. Return ? IteratorClose(iterated, error). + 7. Set iterated to ? GetIteratorDirect(O). + +includes: [compareArray.js] +features: [iterator-chunking] +---*/ +let effects = []; + +// TypeError for non-object this before chunkSize is examined +assert.throws(TypeError, function () { + Iterator.prototype.chunks.call(null, 0); +}); + +// RangeError for invalid chunkSize before next is accessed +assert.throws(RangeError, function () { + Iterator.prototype.chunks.call( + { + get next() { + effects.push('get next'); + return function () { + return { done: true, value: undefined }; + }; + }, + }, + 0 + ); +}); + +assert.compareArray(effects, []); + +// With valid args, next getter IS accessed (GetIteratorDirect runs) +Iterator.prototype.chunks.call( + { + get next() { + effects.push('get next'); + return function () { + return { done: true, value: undefined }; + }; + }, + }, + 1 +); + +assert.compareArray(effects, ['get next']); diff --git a/test/built-ins/Iterator/prototype/chunks/argument-validation-failure-closes-underlying.js b/test/built-ins/Iterator/prototype/chunks/argument-validation-failure-closes-underlying.js new file mode 100644 index 00000000000..c9261f6317b --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/argument-validation-failure-closes-underlying.js @@ -0,0 +1,53 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.chunks +description: > + Underlying iterator is closed when chunkSize validation fails +info: | + Iterator.prototype.chunks ( chunkSize ) + + 3. Let iterated be the Iterator Record { [[Iterator]]: O, [[NextMethod]]: undefined, [[Done]]: false }. + 4. If chunkSize is not a Number, throw a TypeError exception ... IteratorClose(iterated, error). + 5. If chunkSize is not an integral Number, throw a TypeError exception ... IteratorClose(iterated, error). + 6. If chunkSize is not in the inclusive interval from 1๐”ฝ to ๐”ฝ(2^32 - 1), then + a. Let error be ThrowCompletion(a newly created RangeError object). + b. Return ? IteratorClose(iterated, error). + +features: [iterator-chunking] +---*/ + +let closed = false; +let closable = { + __proto__: Iterator.prototype, + get next() { + throw new Test262Error('next should not be read'); + }, + return() { + closed = true; + return {}; + }, +}; + +assert.throws(TypeError, function () { + closable.chunks(); +}); +assert.sameValue(closed, true, 'iterator closed when chunkSize is undefined'); + +closed = false; +assert.throws(RangeError, function () { + closable.chunks(0); +}); +assert.sameValue(closed, true, 'iterator closed when chunkSize is 0'); + +closed = false; +assert.throws(TypeError, function () { + closable.chunks(NaN); +}); +assert.sameValue(closed, true, 'iterator closed when chunkSize is NaN'); + +closed = false; +assert.throws(TypeError, function () { + closable.chunks('1'); +}); +assert.sameValue(closed, true, 'iterator closed when chunkSize is a string'); diff --git a/test/built-ins/Iterator/prototype/chunks/callable.js b/test/built-ins/Iterator/prototype/chunks/callable.js new file mode 100644 index 00000000000..8d848133d3c --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/callable.js @@ -0,0 +1,13 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.chunks +description: > + Iterator.prototype.chunks is callable +features: [iterator-chunking] +---*/ +function* g() {} +Iterator.prototype.chunks.call(g(), 1); + +let iter = g(); +iter.chunks(1); diff --git a/test/built-ins/Iterator/prototype/chunks/chunkSize-no-coercion.js b/test/built-ins/Iterator/prototype/chunks/chunkSize-no-coercion.js new file mode 100644 index 00000000000..83b95c1775b --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/chunkSize-no-coercion.js @@ -0,0 +1,38 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.chunks +description: > + Iterator.prototype.chunks does not coerce chunkSize using ToNumber; the + argument must already be a Number. Unlike take/drop, valueOf and toString + are never called. +info: | + Iterator.prototype.chunks ( chunkSize ) + + 4. If chunkSize is not a Number, throw a TypeError exception. + +features: [iterator-chunking] +---*/ +let iterator = (function* () {})(); + +let valueOfCalled = false; +assert.throws(TypeError, () => { + iterator.chunks({ + valueOf() { + valueOfCalled = true; + return 2; + }, + }); +}); +assert.sameValue(valueOfCalled, false, 'valueOf must not be called'); + +let toStringCalled = false; +assert.throws(TypeError, () => { + iterator.chunks({ + toString() { + toStringCalled = true; + return '2'; + }, + }); +}); +assert.sameValue(toStringCalled, false, 'toString must not be called'); diff --git a/test/built-ins/Iterator/prototype/chunks/chunkSize-not-a-number.js b/test/built-ins/Iterator/prototype/chunks/chunkSize-not-a-number.js new file mode 100644 index 00000000000..35d59c0ea6d --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/chunkSize-not-a-number.js @@ -0,0 +1,68 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.chunks +description: > + Iterator.prototype.chunks throws TypeError when chunkSize is not an integral + Number +info: | + Iterator.prototype.chunks ( chunkSize ) + + 4. If chunkSize is not a Number, throw a TypeError exception. + 5. If chunkSize is not an integral Number, throw a TypeError exception. + +features: [iterator-chunking] +---*/ +let iterator = (function* () {})(); + +assert.throws(TypeError, () => { + iterator.chunks(); +}); + +assert.throws(TypeError, () => { + iterator.chunks(undefined); +}); + +assert.throws(TypeError, () => { + iterator.chunks('1'); +}); + +assert.throws(TypeError, () => { + iterator.chunks(true); +}); + +assert.throws(TypeError, () => { + iterator.chunks(null); +}); + +assert.throws(TypeError, () => { + iterator.chunks({}); +}); + +assert.throws(TypeError, () => { + iterator.chunks(Symbol()); +}); + +assert.throws(TypeError, () => { + iterator.chunks([2]); +}); + +assert.throws(TypeError, () => { + iterator.chunks(NaN); +}); + +assert.throws(TypeError, () => { + iterator.chunks(0.5); +}); + +assert.throws(TypeError, () => { + iterator.chunks(1.5); +}); + +assert.throws(TypeError, () => { + iterator.chunks(Infinity); +}); + +assert.throws(TypeError, () => { + iterator.chunks(-Infinity); +}); diff --git a/test/built-ins/Iterator/prototype/chunks/chunkSize-out-of-range.js b/test/built-ins/Iterator/prototype/chunks/chunkSize-out-of-range.js new file mode 100644 index 00000000000..04b5f55da2f --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/chunkSize-out-of-range.js @@ -0,0 +1,41 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.chunks +description: > + Iterator.prototype.chunks throws RangeError when chunkSize is an integral + Number outside the valid range [1, 2^32 - 1] +info: | + Iterator.prototype.chunks ( chunkSize ) + + 6. If chunkSize is not in the inclusive interval from 1๐”ฝ to ๐”ฝ(2^32 - 1), then + a. Let error be ThrowCompletion(a newly created RangeError object). + b. Return ? IteratorClose(iterated, error). + +features: [iterator-chunking] +---*/ +let iterator = (function* () {})(); + +assert.throws(RangeError, () => { + iterator.chunks(0); +}); + +assert.throws(RangeError, () => { + iterator.chunks(-0); +}); + +assert.throws(RangeError, () => { + iterator.chunks(-1); +}); + +assert.throws(RangeError, () => { + iterator.chunks(2 ** 32); +}); + +assert.throws(RangeError, () => { + iterator.chunks(2 ** 53); +}); + +// Boundary: valid values do not throw +iterator.chunks(1); +iterator.chunks(2 ** 32 - 1); diff --git a/test/built-ins/Iterator/prototype/chunks/chunks-evenly-divisible.js b/test/built-ins/Iterator/prototype/chunks/chunks-evenly-divisible.js new file mode 100644 index 00000000000..a8cfc3e3664 --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/chunks-evenly-divisible.js @@ -0,0 +1,33 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.chunks +description: > + All chunks are full-sized when the iterator length is evenly divisible by + chunkSize +info: | + Iterator.prototype.chunks ( chunkSize ) + + 6.a.iv. If the number of elements in buffer is โ„(chunkSize), then + 6.a.iv.a. Let completion be Completion(Yield(CreateArrayFromList(buffer))). + +features: [iterator-chunking] +includes: [compareArray.js] +---*/ +function* g(n) { + for (let i = 0; i < n; ++i) { + yield i; + } +} + +let chunks = Array.from(g(4).chunks(2)); + +assert.sameValue(chunks.length, 2); +assert.compareArray(chunks[0], [0, 1]); +assert.compareArray(chunks[1], [2, 3]); + +chunks = Array.from(g(6).chunks(3)); + +assert.sameValue(chunks.length, 2); +assert.compareArray(chunks[0], [0, 1, 2]); +assert.compareArray(chunks[1], [3, 4, 5]); diff --git a/test/built-ins/Iterator/prototype/chunks/chunks-last-chunk-partial.js b/test/built-ins/Iterator/prototype/chunks/chunks-last-chunk-partial.js new file mode 100644 index 00000000000..aa459d00a60 --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/chunks-last-chunk-partial.js @@ -0,0 +1,32 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.chunks +description: > + Last chunk may be smaller than chunkSize when the iterator is not evenly + divisible +info: | + Iterator.prototype.chunks ( chunkSize ) + + 6.a.i. Let value be ? IteratorStepValue(iterated). + 6.a.ii. If value is ~done~, then + 6.a.ii.a. If buffer is not empty, then + 6.a.ii.a.i. Perform Completion(Yield(CreateArrayFromList(buffer))). + +features: [iterator-chunking] +includes: [compareArray.js] +---*/ +function* g() { + yield 0; + yield 1; + yield 2; + yield 3; + yield 4; +} + +let chunks = Array.from(g().chunks(2)); + +assert.sameValue(chunks.length, 3); +assert.compareArray(chunks[0], [0, 1]); +assert.compareArray(chunks[1], [2, 3]); +assert.compareArray(chunks[2], [4]); diff --git a/test/built-ins/Iterator/prototype/chunks/chunks-size-1.js b/test/built-ins/Iterator/prototype/chunks/chunks-size-1.js new file mode 100644 index 00000000000..86f8d55ae67 --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/chunks-size-1.js @@ -0,0 +1,28 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.chunks +description: > + When chunkSize is 1, each element is yielded as a single-element array +info: | + Iterator.prototype.chunks ( chunkSize ) + +features: [iterator-chunking] +includes: [compareArray.js] +---*/ +function* g() { + yield 0; + yield 1; + yield 2; + yield 3; + yield 4; +} + +let chunks = Array.from(g().chunks(1)); + +assert.sameValue(chunks.length, 5); +assert.compareArray(chunks[0], [0]); +assert.compareArray(chunks[1], [1]); +assert.compareArray(chunks[2], [2]); +assert.compareArray(chunks[3], [3]); +assert.compareArray(chunks[4], [4]); diff --git a/test/built-ins/Iterator/prototype/chunks/chunks-size-larger-than-iterator.js b/test/built-ins/Iterator/prototype/chunks/chunks-size-larger-than-iterator.js new file mode 100644 index 00000000000..aeeacba1f5c --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/chunks-size-larger-than-iterator.js @@ -0,0 +1,31 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.chunks +description: > + When chunkSize is larger than the number of elements, a single partial chunk + is yielded +info: | + Iterator.prototype.chunks ( chunkSize ) + + 6.a.i. Let value be ? IteratorStepValue(iterated). + 6.a.ii. If value is ~done~, then + 6.a.ii.a. If buffer is not empty, then + 6.a.ii.a.i. Perform Completion(Yield(CreateArrayFromList(buffer))). + +features: [iterator-chunking] +includes: [compareArray.js] +---*/ +function* g() { + yield 0; + yield 1; + yield 2; + yield 3; + yield 4; + yield 5; +} + +let chunks = Array.from(g().chunks(100)); + +assert.sameValue(chunks.length, 1); +assert.compareArray(chunks[0], [0, 1, 2, 3, 4, 5]); diff --git a/test/built-ins/Iterator/prototype/chunks/exhaustion-does-not-call-return.js b/test/built-ins/Iterator/prototype/chunks/exhaustion-does-not-call-return.js new file mode 100644 index 00000000000..56d0ab18358 --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/exhaustion-does-not-call-return.js @@ -0,0 +1,32 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.chunks +description: > + Underlying iterator return is not called when result iterator is exhausted +info: | + Iterator.prototype.chunks ( chunkSize ) + +features: [iterator-chunking] +---*/ +function* g() { + yield 0; + yield 1; + yield 2; +} + +class TestIterator extends Iterator { + get next() { + let n = g(); + return function () { + return n.next(); + }; + } + return() { + throw new Test262Error(); + } +} + +let iterator = new TestIterator().chunks(2); +iterator.next(); +iterator.next(); diff --git a/test/built-ins/Iterator/prototype/chunks/get-next-method-only-once.js b/test/built-ins/Iterator/prototype/chunks/get-next-method-only-once.js new file mode 100644 index 00000000000..9b3272933f5 --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/get-next-method-only-once.js @@ -0,0 +1,40 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.chunks +description: > + Gets the next method from the underlying iterator only once +info: | + Iterator.prototype.chunks ( chunkSize ) + + 5. Set iterated to ? GetIteratorDirect(O). + +features: [iterator-chunking] +---*/ +let nextGets = 0; +let nextCalls = 0; + +class CountingIterator extends Iterator { + get next() { + ++nextGets; + let iter = (function* () { + for (let i = 1; i < 5; ++i) { + yield i; + } + })(); + return function () { + ++nextCalls; + return iter.next(); + }; + } +} + +let iterator = new CountingIterator(); + +assert.sameValue(nextGets, 0); +assert.sameValue(nextCalls, 0); + +for (const value of iterator.chunks(2)); + +assert.sameValue(nextGets, 1); +assert.sameValue(nextCalls, 5); diff --git a/test/built-ins/Iterator/prototype/chunks/get-next-method-throws.js b/test/built-ins/Iterator/prototype/chunks/get-next-method-throws.js new file mode 100644 index 00000000000..d26f41cb856 --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/get-next-method-throws.js @@ -0,0 +1,24 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.chunks +description: > + Throws when getting the next method from the underlying iterator throws +info: | + Iterator.prototype.chunks ( chunkSize ) + + 5. Set iterated to ? GetIteratorDirect(O). + +features: [iterator-chunking] +---*/ +class ThrowingIterator extends Iterator { + get next() { + throw new Test262Error(); + } +} + +let chunked = new ThrowingIterator().chunks(1); + +assert.throws(Test262Error, function () { + chunked.next(); +}); diff --git a/test/built-ins/Iterator/prototype/chunks/get-return-method-throws.js b/test/built-ins/Iterator/prototype/chunks/get-return-method-throws.js new file mode 100644 index 00000000000..ecaa675e39d --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/get-return-method-throws.js @@ -0,0 +1,29 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.chunks +description: > + Underlying iterator return is a throwing getter +info: | + Iterator.prototype.chunks ( chunkSize ) + +features: [iterator-chunking] +---*/ +class TestIterator extends Iterator { + next() { + return { + done: false, + value: 1, + }; + } + get return() { + throw new Test262Error(); + } +} + +let iterator = new TestIterator().chunks(1); +iterator.next(); + +assert.throws(Test262Error, function () { + iterator.return(); +}); diff --git a/test/built-ins/Iterator/prototype/chunks/is-function.js b/test/built-ins/Iterator/prototype/chunks/is-function.js new file mode 100644 index 00000000000..9db49361231 --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/is-function.js @@ -0,0 +1,10 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.chunks +description: > + Iterator.prototype.chunks is a built-in function +features: [iterator-chunking] +---*/ + +assert.sameValue(typeof Iterator.prototype.chunks, 'function'); diff --git a/test/built-ins/Iterator/prototype/chunks/iterator-already-exhausted.js b/test/built-ins/Iterator/prototype/chunks/iterator-already-exhausted.js new file mode 100644 index 00000000000..aa499986ec9 --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/iterator-already-exhausted.js @@ -0,0 +1,22 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.chunks +description: > + Iterator.prototype.chunks yields no chunks when the iterator is already + exhausted +info: | + Iterator.prototype.chunks ( chunkSize ) + + 6.a.i. Let value be ? IteratorStepValue(iterated). + 6.a.ii. If value is ~done~, then + 6.a.ii.a. If buffer is not empty, then + ... + 6.a.ii.b. Return ReturnCompletion(undefined). + +features: [iterator-chunking] +---*/ +function* g() {} + +let chunks = Array.from(g().chunks(2)); +assert.sameValue(chunks.length, 0); diff --git a/test/built-ins/Iterator/prototype/chunks/iterator-return-method-throws.js b/test/built-ins/Iterator/prototype/chunks/iterator-return-method-throws.js new file mode 100644 index 00000000000..5303aa4d193 --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/iterator-return-method-throws.js @@ -0,0 +1,28 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.chunks +description: > + Iterator has throwing return +info: | + Iterator.prototype.chunks ( chunkSize ) + +features: [iterator-chunking] +---*/ +class IteratorThrows extends Iterator { + next() { + return { + done: false, + value: 0, + }; + } + return() { + throw new Test262Error(); + } +} + +let iterator = new IteratorThrows().chunks(1); + +assert.throws(Test262Error, function () { + iterator.return(); +}); diff --git a/test/built-ins/Iterator/prototype/chunks/length.js b/test/built-ins/Iterator/prototype/chunks/length.js new file mode 100644 index 00000000000..9421650afe0 --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/length.js @@ -0,0 +1,22 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.chunks +description: > + Iterator.prototype.chunks has a "length" property whose value is 1. +info: | + ECMAScript Standard Built-in Objects + + Unless otherwise specified, the length property of a built-in + Function object has the attributes { [[Writable]]: false, [[Enumerable]]: + false, [[Configurable]]: true }. +features: [iterator-chunking] +includes: [propertyHelper.js] +---*/ + +verifyProperty(Iterator.prototype.chunks, 'length', { + value: 1, + writable: false, + enumerable: false, + configurable: true, +}); diff --git a/test/built-ins/Iterator/prototype/chunks/name.js b/test/built-ins/Iterator/prototype/chunks/name.js new file mode 100644 index 00000000000..e022d94611f --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/name.js @@ -0,0 +1,27 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.chunks +description: > + The "name" property of Iterator.prototype.chunks +info: | + 17 ECMAScript Standard Built-in Objects + + Every built-in Function object, including constructors, that is not + identified as an anonymous function has a name property whose value is a + String. Unless otherwise specified, this value is the name that is given to + the function in this specification. + + Unless otherwise specified, the name property of a built-in Function + object, if it exists, has the attributes { [[Writable]]: false, + [[Enumerable]]: false, [[Configurable]]: true }. +features: [iterator-chunking] +includes: [propertyHelper.js] +---*/ + +verifyProperty(Iterator.prototype.chunks, 'name', { + value: 'chunks', + writable: false, + enumerable: false, + configurable: true, +}); diff --git a/test/built-ins/Iterator/prototype/chunks/next-method-returns-non-object.js b/test/built-ins/Iterator/prototype/chunks/next-method-returns-non-object.js new file mode 100644 index 00000000000..ea13cf65368 --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/next-method-returns-non-object.js @@ -0,0 +1,22 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.chunks +description: > + Underlying iterator next returns non-object +info: | + Iterator.prototype.chunks ( chunkSize ) + +features: [iterator-chunking] +---*/ +class NonObjectIterator extends Iterator { + next() { + return null; + } +} + +let iterator = new NonObjectIterator().chunks(1); + +assert.throws(TypeError, function () { + iterator.next(); +}); diff --git a/test/built-ins/Iterator/prototype/chunks/next-method-returns-throwing-done.js b/test/built-ins/Iterator/prototype/chunks/next-method-returns-throwing-done.js new file mode 100644 index 00000000000..801dbd44569 --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/next-method-returns-throwing-done.js @@ -0,0 +1,30 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.chunks +description: > + Underlying iterator next returns object with throwing done getter +info: | + Iterator.prototype.chunks ( chunkSize ) + +features: [iterator-chunking] +---*/ +class ThrowingIterator extends Iterator { + next() { + return { + get done() { + throw new Test262Error(); + }, + value: 1, + }; + } + return() { + throw new Error(); + } +} + +let iterator = new ThrowingIterator().chunks(1); + +assert.throws(Test262Error, function () { + iterator.next(); +}); diff --git a/test/built-ins/Iterator/prototype/chunks/next-method-returns-throwing-value-done.js b/test/built-ins/Iterator/prototype/chunks/next-method-returns-throwing-value-done.js new file mode 100644 index 00000000000..f76c2bbf018 --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/next-method-returns-throwing-value-done.js @@ -0,0 +1,28 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.chunks +description: > + Underlying iterator next returns object with throwing value getter, but is + already done +info: | + Iterator.prototype.chunks ( chunkSize ) + +features: [iterator-chunking] +---*/ +class ThrowingIterator extends Iterator { + next() { + return { + done: true, + get value() { + throw new Test262Error(); + }, + }; + } + return() { + throw new Error(); + } +} + +let iterator = new ThrowingIterator().chunks(1); +iterator.next(); diff --git a/test/built-ins/Iterator/prototype/chunks/next-method-returns-throwing-value.js b/test/built-ins/Iterator/prototype/chunks/next-method-returns-throwing-value.js new file mode 100644 index 00000000000..2856b3815ae --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/next-method-returns-throwing-value.js @@ -0,0 +1,30 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.chunks +description: > + Underlying iterator next returns object with throwing value getter +info: | + Iterator.prototype.chunks ( chunkSize ) + +features: [iterator-chunking] +---*/ +class ThrowingIterator extends Iterator { + next() { + return { + done: false, + get value() { + throw new Test262Error(); + }, + }; + } + return() { + throw new Error(); + } +} + +let iterator = new ThrowingIterator().chunks(1); + +assert.throws(Test262Error, function () { + iterator.next(); +}); diff --git a/test/built-ins/Iterator/prototype/chunks/next-method-throws.js b/test/built-ins/Iterator/prototype/chunks/next-method-throws.js new file mode 100644 index 00000000000..f48a39c268f --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/next-method-throws.js @@ -0,0 +1,22 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.chunks +description: > + Underlying iterator next throws +info: | + Iterator.prototype.chunks ( chunkSize ) + +features: [iterator-chunking] +---*/ +class ThrowingIterator extends Iterator { + next() { + throw new Test262Error(); + } +} + +let iterator = new ThrowingIterator().chunks(1); + +assert.throws(Test262Error, function () { + iterator.next(); +}); diff --git a/test/built-ins/Iterator/prototype/chunks/non-constructible.js b/test/built-ins/Iterator/prototype/chunks/non-constructible.js new file mode 100644 index 00000000000..515d9ce0ce1 --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/non-constructible.js @@ -0,0 +1,26 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.chunks +description: > + Iterator.prototype.chunks is not constructible. + + Built-in function objects that are not identified as constructors do not + implement the [[Construct]] internal method unless otherwise specified in + the description of a particular function. +features: [iterator-chunking] +---*/ +function* g() {} +let iter = g(); + +assert.throws(TypeError, () => { + new iter.chunks(1); +}); + +assert.throws(TypeError, () => { + new Iterator.prototype.chunks(1); +}); + +assert.throws(TypeError, () => { + new class extends Iterator {}.chunks(1); +}); diff --git a/test/built-ins/Iterator/prototype/chunks/prop-desc.js b/test/built-ins/Iterator/prototype/chunks/prop-desc.js new file mode 100644 index 00000000000..ddc6729d222 --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/prop-desc.js @@ -0,0 +1,23 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.chunks +description: > + Property descriptor of Iterator.prototype.chunks +info: | + Iterator.prototype.chunks + + 17 ECMAScript Standard Built-in Objects + + Every other data property described in clauses 18 through 26 and in Annex B.2 + has the attributes { [[Writable]]: true, [[Enumerable]]: false, + [[Configurable]]: true } unless otherwise specified. +features: [iterator-chunking] +includes: [propertyHelper.js] +---*/ + +verifyProperty(Iterator.prototype, 'chunks', { + writable: true, + enumerable: false, + configurable: true, +}); diff --git a/test/built-ins/Iterator/prototype/chunks/proto.js b/test/built-ins/Iterator/prototype/chunks/proto.js new file mode 100644 index 00000000000..0e131414ada --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/proto.js @@ -0,0 +1,11 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.chunks +description: > + The value of the [[Prototype]] internal slot of Iterator.prototype.chunks is the + intrinsic object %FunctionPrototype%. +features: [iterator-chunking] +---*/ + +assert.sameValue(Object.getPrototypeOf(Iterator.prototype.chunks), Function.prototype); diff --git a/test/built-ins/Iterator/prototype/chunks/result-is-iterator.js b/test/built-ins/Iterator/prototype/chunks/result-is-iterator.js new file mode 100644 index 00000000000..b225a7e465e --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/result-is-iterator.js @@ -0,0 +1,18 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.chunks +description: > + The value returned by Iterator.prototype.chunks is an Iterator instance +info: | + Iterator.prototype.chunks ( chunkSize ) + + 6. Let result be CreateIteratorFromClosure(closure, "Iterator Helper", %IteratorHelperPrototype%, ยซ [[UnderlyingIterators]] ยป). + +features: [iterator-chunking] +---*/ + +assert( + (function* () {})().chunks(1) instanceof Iterator, + 'function*(){}().chunks(1) must return an Iterator' +); diff --git a/test/built-ins/Iterator/prototype/chunks/return-is-forwarded-to-underlying-iterator.js b/test/built-ins/Iterator/prototype/chunks/return-is-forwarded-to-underlying-iterator.js new file mode 100644 index 00000000000..74d571b33d5 --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/return-is-forwarded-to-underlying-iterator.js @@ -0,0 +1,32 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.chunks +description: > + Underlying iterator return is called when result iterator is closed +info: | + Iterator.prototype.chunks ( chunkSize ) + +features: [iterator-chunking] +---*/ +let returnCount = 0; + +class TestIterator extends Iterator { + next() { + return { + done: false, + value: 1, + }; + } + return() { + ++returnCount; + return {}; + } +} + +let iterator = new TestIterator().chunks(2); +assert.sameValue(returnCount, 0); +iterator.return(); +assert.sameValue(returnCount, 1); +iterator.return(); +assert.sameValue(returnCount, 1); diff --git a/test/built-ins/Iterator/prototype/chunks/return-is-not-forwarded-after-exhaustion.js b/test/built-ins/Iterator/prototype/chunks/return-is-not-forwarded-after-exhaustion.js new file mode 100644 index 00000000000..3609fa5690f --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/return-is-not-forwarded-after-exhaustion.js @@ -0,0 +1,35 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.chunks +description: > + Underlying iterator return is not called after result iterator observes + that underlying iterator is exhausted +info: | + Iterator.prototype.chunks ( chunkSize ) + +features: [iterator-chunking] +---*/ + +class TestIterator extends Iterator { + next() { + return { + done: true, + value: undefined, + }; + } + return() { + throw new Test262Error(); + } +} + +let iterator = new TestIterator().chunks(1); +assert.throws(Test262Error, function () { + iterator.return(); +}); +iterator.next(); +iterator.return(); + +iterator = new TestIterator().chunks(1); +iterator.next(); +iterator.return(); diff --git a/test/built-ins/Iterator/prototype/chunks/this-non-callable-next.js b/test/built-ins/Iterator/prototype/chunks/this-non-callable-next.js new file mode 100644 index 00000000000..8e5926b857c --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/this-non-callable-next.js @@ -0,0 +1,19 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.chunks +description: > + Iterator.prototype.chunks throws TypeError when its this value is an object + with a non-callable next +info: | + Iterator.prototype.chunks ( chunkSize ) + + 5. Set iterated to ? GetIteratorDirect(O). + +features: [iterator-chunking] +---*/ +let iter = Iterator.prototype.chunks.call({ next: 0 }, 1); + +assert.throws(TypeError, function () { + iter.next(); +}); diff --git a/test/built-ins/Iterator/prototype/chunks/this-non-object.js b/test/built-ins/Iterator/prototype/chunks/this-non-object.js new file mode 100644 index 00000000000..44ed77cd1df --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/this-non-object.js @@ -0,0 +1,26 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.chunks +description: > + Iterator.prototype.chunks throws TypeError when its this value is a non-object +info: | + Iterator.prototype.chunks ( chunkSize ) + + 1. Let O be the this value. + 2. If O is not an Object, throw a TypeError exception. + +features: [iterator-chunking] +---*/ +assert.throws(TypeError, function () { + Iterator.prototype.chunks.call(null, 1); +}); + +Object.defineProperty(Number.prototype, 'next', { + get: function () { + throw new Test262Error(); + }, +}); +assert.throws(TypeError, function () { + Iterator.prototype.chunks.call(0, 1); +}); diff --git a/test/built-ins/Iterator/prototype/chunks/this-plain-iterator.js b/test/built-ins/Iterator/prototype/chunks/this-plain-iterator.js new file mode 100644 index 00000000000..e3dfcdbae38 --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/this-plain-iterator.js @@ -0,0 +1,36 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.chunks +description: > + Iterator.prototype.chunks supports a this value that does not inherit from + Iterator.prototype but implements the iterator protocol +info: | + Iterator.prototype.chunks ( chunkSize ) + +features: [iterator-chunking] +includes: [compareArray.js] +---*/ +let iter = { + get next() { + let count = 3; + return function () { + --count; + return count >= 0 ? { done: false, value: count } : { done: true, value: undefined }; + }; + }, +}; + +let chunked = Iterator.prototype.chunks.call(iter, 2); + +let result = chunked.next(); +assert.compareArray(result.value, [2, 1]); +assert.sameValue(result.done, false); + +result = chunked.next(); +assert.compareArray(result.value, [0]); +assert.sameValue(result.done, false); + +result = chunked.next(); +assert.sameValue(result.value, undefined); +assert.sameValue(result.done, true); diff --git a/test/built-ins/Iterator/prototype/chunks/throws-typeerror-when-generator-is-running.js b/test/built-ins/Iterator/prototype/chunks/throws-typeerror-when-generator-is-running.js new file mode 100644 index 00000000000..41757d294a5 --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/throws-typeerror-when-generator-is-running.js @@ -0,0 +1,42 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.chunks +description: > + Throws a TypeError when the closure generator is already running. +info: | + %IteratorHelperPrototype%.next ( ) + 1. Return ? GeneratorResume(this value, undefined, "Iterator Helper"). + + 27.5.3.3 GeneratorResume ( generator, value, generatorBrand ) + 1. Let state be ? GeneratorValidate(generator, generatorBrand). + ... + + 27.5.3.2 GeneratorValidate ( generator, generatorBrand ) + ... + 6. If state is executing, throw a TypeError exception. + ... + +features: [iterator-chunking] +---*/ + +var loopCount = 0; + +var iter; +var iterator = { + get next() { + return function () { + loopCount++; + iter.next(); + return { done: false, value: 0 }; + }; + }, +}; + +iter = Iterator.prototype.chunks.call(iterator, 1); + +assert.throws(TypeError, function () { + iter.next(); +}); + +assert.sameValue(loopCount, 1); diff --git a/test/built-ins/Iterator/prototype/chunks/underlying-iterator-advanced-in-parallel.js b/test/built-ins/Iterator/prototype/chunks/underlying-iterator-advanced-in-parallel.js new file mode 100644 index 00000000000..990a2999b8a --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/underlying-iterator-advanced-in-parallel.js @@ -0,0 +1,40 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.chunks +description: > + Underlying iterator is advanced after calling chunks +info: | + Iterator.prototype.chunks ( chunkSize ) + +features: [iterator-chunking] +includes: [compareArray.js] +---*/ +let iterator = (function* () { + for (let i = 0; i < 6; ++i) { + yield i; + } +})(); + +let chunked = iterator.chunks(2); + +let result = chunked.next(); +assert.compareArray(result.value, [0, 1]); +assert.sameValue(result.done, false); + +let { value, done } = iterator.next(); +assert.sameValue(value, 2); +assert.sameValue(done, false); +iterator.next(); + +result = chunked.next(); +assert.compareArray(result.value, [3, 4]); +assert.sameValue(result.done, false); + +result = chunked.next(); +assert.compareArray(result.value, [5]); +assert.sameValue(result.done, false); + +result = chunked.next(); +assert.sameValue(result.value, undefined); +assert.sameValue(result.done, true); diff --git a/test/built-ins/Iterator/prototype/chunks/underlying-iterator-closed-in-parallel.js b/test/built-ins/Iterator/prototype/chunks/underlying-iterator-closed-in-parallel.js new file mode 100644 index 00000000000..fb4ec10a08b --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/underlying-iterator-closed-in-parallel.js @@ -0,0 +1,25 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.chunks +description: > + Underlying iterator is closed after calling chunks +info: | + Iterator.prototype.chunks ( chunkSize ) + +features: [iterator-chunking] +---*/ +let iterator = (function* () { + for (let i = 0; i < 5; ++i) { + yield i; + } +})(); + +let chunked = iterator.chunks(2); + +iterator.return(); + +let { value, done } = chunked.next(); + +assert.sameValue(value, undefined); +assert.sameValue(done, true); diff --git a/test/built-ins/Iterator/prototype/chunks/yields-distinct-arrays.js b/test/built-ins/Iterator/prototype/chunks/yields-distinct-arrays.js new file mode 100644 index 00000000000..9a3f0ed1a60 --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/yields-distinct-arrays.js @@ -0,0 +1,26 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.chunks +description: > + Each yielded chunk is a distinct new Array object +info: | + Iterator.prototype.chunks ( chunkSize ) + + 6.a.iv.a. Let completion be Completion(Yield(CreateArrayFromList(buffer))). + +features: [iterator-chunking] +---*/ +function* g() { + yield 0; + yield 1; + yield 2; + yield 3; +} + +let chunks = Array.from(g().chunks(2)); + +assert.sameValue(chunks.length, 2); +assert(Array.isArray(chunks[0]), 'chunks[0] is an Array'); +assert(Array.isArray(chunks[1]), 'chunks[1] is an Array'); +assert.notSameValue(chunks[0], chunks[1], 'each chunk is a distinct array'); diff --git a/test/built-ins/Iterator/prototype/windows/argument-effect-order.js b/test/built-ins/Iterator/prototype/windows/argument-effect-order.js new file mode 100644 index 00000000000..fe9f85ae1ae --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/argument-effect-order.js @@ -0,0 +1,83 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.windows +description: > + Arguments and this value are validated in the correct order +info: | + Iterator.prototype.windows ( windowSize [ , undersized ] ) + + 1. Let O be the this value. + 2. If O is not an Object, throw a TypeError exception. + 3. Let iterated be the Iterator Record { [[Iterator]]: O, [[NextMethod]]: undefined, [[Done]]: false }. + 4. If windowSize is not a Number, throw a TypeError ... IteratorClose. + 5. If windowSize is not an integral Number, throw a TypeError ... IteratorClose. + 6. If windowSize is not in the inclusive interval from 1๐”ฝ to ๐”ฝ(2^32 - 1), then + a. Let error be ThrowCompletion(a newly created RangeError object). + b. Return ? IteratorClose(iterated, error). + 7. If undersized is undefined, set undersized to "only-full". + 8. If undersized is neither "only-full" nor "allow-partial", then + a. Let error be ThrowCompletion(a newly created TypeError object). + b. Return ? IteratorClose(iterated, error). + 9. Set iterated to ? GetIteratorDirect(O). + +includes: [compareArray.js] +features: [iterator-chunking] +---*/ +let effects = []; + +// TypeError for non-object this before windowSize is examined +assert.throws(TypeError, function () { + Iterator.prototype.windows.call(null, 0, 'bad'); +}); + +// RangeError for invalid windowSize before undersized is examined +assert.throws(RangeError, function () { + Iterator.prototype.windows.call( + { + get next() { + effects.push('get next'); + return function () { + return { done: true, value: undefined }; + }; + }, + }, + 0, + 'bad' + ); +}); + +assert.compareArray(effects, []); + +// TypeError for invalid undersized before next is accessed +assert.throws(TypeError, function () { + Iterator.prototype.windows.call( + { + get next() { + effects.push('get next'); + return function () { + return { done: true, value: undefined }; + }; + }, + }, + 1, + 'bad' + ); +}); + +assert.compareArray(effects, []); + +// With all valid args, next getter IS accessed (GetIteratorDirect runs) +Iterator.prototype.windows.call( + { + get next() { + effects.push('get next'); + return function () { + return { done: true, value: undefined }; + }; + }, + }, + 1 +); + +assert.compareArray(effects, ['get next']); diff --git a/test/built-ins/Iterator/prototype/windows/argument-validation-failure-closes-underlying.js b/test/built-ins/Iterator/prototype/windows/argument-validation-failure-closes-underlying.js new file mode 100644 index 00000000000..0cce429dc33 --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/argument-validation-failure-closes-underlying.js @@ -0,0 +1,71 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.windows +description: > + Underlying iterator is closed when argument validation fails +info: | + Iterator.prototype.windows ( windowSize [ , undersized ] ) + + 3. Let iterated be the Iterator Record { [[Iterator]]: O, [[NextMethod]]: undefined, [[Done]]: false }. + 4. If windowSize is not a Number, throw a TypeError exception ... IteratorClose(iterated, error). + 5. If windowSize is not an integral Number, throw a TypeError exception ... IteratorClose(iterated, error). + 6. If windowSize is not in the inclusive interval from 1๐”ฝ to ๐”ฝ(2^32 - 1), then + a. Let error be ThrowCompletion(a newly created RangeError object). + b. Return ? IteratorClose(iterated, error). + ... + 8. If undersized is neither "only-full" nor "allow-partial", then + a. Let error be ThrowCompletion(a newly created TypeError object). + b. Return ? IteratorClose(iterated, error). + +features: [iterator-chunking] +---*/ + +let closed = false; +let closable = { + __proto__: Iterator.prototype, + get next() { + throw new Test262Error('next should not be read'); + }, + return() { + closed = true; + return {}; + }, +}; + +// windowSize validation failure closes +assert.throws(TypeError, function () { + closable.windows(); +}); +assert.sameValue(closed, true, 'iterator closed when windowSize is undefined'); + +closed = false; +assert.throws(RangeError, function () { + closable.windows(0); +}); +assert.sameValue(closed, true, 'iterator closed when windowSize is 0'); + +closed = false; +assert.throws(TypeError, function () { + closable.windows(NaN); +}); +assert.sameValue(closed, true, 'iterator closed when windowSize is NaN'); + +closed = false; +assert.throws(TypeError, function () { + closable.windows('1'); +}); +assert.sameValue(closed, true, 'iterator closed when windowSize is a string'); + +// undersized validation failure closes +closed = false; +assert.throws(TypeError, function () { + closable.windows(1, null); +}); +assert.sameValue(closed, true, 'iterator closed when undersized is null'); + +closed = false; +assert.throws(TypeError, function () { + closable.windows(1, 'bad'); +}); +assert.sameValue(closed, true, 'iterator closed when undersized is invalid string'); diff --git a/test/built-ins/Iterator/prototype/windows/callable.js b/test/built-ins/Iterator/prototype/windows/callable.js new file mode 100644 index 00000000000..46f70e23202 --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/callable.js @@ -0,0 +1,13 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.windows +description: > + Iterator.prototype.windows is callable +features: [iterator-chunking] +---*/ +function* g() {} +Iterator.prototype.windows.call(g(), 1); + +let iter = g(); +iter.windows(1); diff --git a/test/built-ins/Iterator/prototype/windows/exhaustion-does-not-call-return.js b/test/built-ins/Iterator/prototype/windows/exhaustion-does-not-call-return.js new file mode 100644 index 00000000000..98b2d18782d --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/exhaustion-does-not-call-return.js @@ -0,0 +1,33 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.windows +description: > + Underlying iterator return is not called when result iterator is exhausted +info: | + Iterator.prototype.windows ( windowSize [ , undersized ] ) + +features: [iterator-chunking] +---*/ +function* g() { + yield 0; + yield 1; + yield 2; +} + +class TestIterator extends Iterator { + get next() { + let n = g(); + return function () { + return n.next(); + }; + } + return() { + throw new Test262Error(); + } +} + +let iterator = new TestIterator().windows(2); +iterator.next(); +iterator.next(); +iterator.next(); diff --git a/test/built-ins/Iterator/prototype/windows/get-next-method-only-once.js b/test/built-ins/Iterator/prototype/windows/get-next-method-only-once.js new file mode 100644 index 00000000000..521513aca9b --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/get-next-method-only-once.js @@ -0,0 +1,40 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.windows +description: > + Gets the next method from the underlying iterator only once +info: | + Iterator.prototype.windows ( windowSize [ , undersized ] ) + + 7. Set iterated to ? GetIteratorDirect(O). + +features: [iterator-chunking] +---*/ +let nextGets = 0; +let nextCalls = 0; + +class CountingIterator extends Iterator { + get next() { + ++nextGets; + let iter = (function* () { + for (let i = 1; i < 5; ++i) { + yield i; + } + })(); + return function () { + ++nextCalls; + return iter.next(); + }; + } +} + +let iterator = new CountingIterator(); + +assert.sameValue(nextGets, 0); +assert.sameValue(nextCalls, 0); + +for (const value of iterator.windows(2)); + +assert.sameValue(nextGets, 1); +assert.sameValue(nextCalls, 5); diff --git a/test/built-ins/Iterator/prototype/windows/get-next-method-throws.js b/test/built-ins/Iterator/prototype/windows/get-next-method-throws.js new file mode 100644 index 00000000000..730f0dc1e9f --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/get-next-method-throws.js @@ -0,0 +1,24 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.windows +description: > + Throws when getting the next method from the underlying iterator throws +info: | + Iterator.prototype.windows ( windowSize [ , undersized ] ) + + 7. Set iterated to ? GetIteratorDirect(O). + +features: [iterator-chunking] +---*/ +class ThrowingIterator extends Iterator { + get next() { + throw new Test262Error(); + } +} + +let windowed = new ThrowingIterator().windows(1); + +assert.throws(Test262Error, function () { + windowed.next(); +}); diff --git a/test/built-ins/Iterator/prototype/windows/get-return-method-throws.js b/test/built-ins/Iterator/prototype/windows/get-return-method-throws.js new file mode 100644 index 00000000000..e7dcd365c0a --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/get-return-method-throws.js @@ -0,0 +1,29 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.windows +description: > + Underlying iterator return is a throwing getter +info: | + Iterator.prototype.windows ( windowSize [ , undersized ] ) + +features: [iterator-chunking] +---*/ +class TestIterator extends Iterator { + next() { + return { + done: false, + value: 1, + }; + } + get return() { + throw new Test262Error(); + } +} + +let iterator = new TestIterator().windows(1); +iterator.next(); + +assert.throws(Test262Error, function () { + iterator.return(); +}); diff --git a/test/built-ins/Iterator/prototype/windows/is-function.js b/test/built-ins/Iterator/prototype/windows/is-function.js new file mode 100644 index 00000000000..1297bf16f53 --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/is-function.js @@ -0,0 +1,10 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.windows +description: > + Iterator.prototype.windows is a built-in function +features: [iterator-chunking] +---*/ + +assert.sameValue(typeof Iterator.prototype.windows, 'function'); diff --git a/test/built-ins/Iterator/prototype/windows/iterator-already-exhausted.js b/test/built-ins/Iterator/prototype/windows/iterator-already-exhausted.js new file mode 100644 index 00000000000..fc4e8bc8884 --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/iterator-already-exhausted.js @@ -0,0 +1,27 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.windows +description: > + Iterator.prototype.windows yields no windows when the iterator is already + exhausted, regardless of undersized mode +info: | + Iterator.prototype.windows ( windowSize [ , undersized ] ) + + 8.a.i. Let value be ? IteratorStepValue(iterated). + 8.a.ii. If value is ~done~, then + 8.a.ii.a. If undersized is "allow-partial", buffer is not empty, and ... + 8.a.ii.b. Return ReturnCompletion(undefined). + +features: [iterator-chunking] +---*/ +function* g() {} + +let windows = Array.from(g().windows(2)); +assert.sameValue(windows.length, 0, 'default undersized on empty iterator'); + +windows = Array.from(g().windows(2, 'only-full')); +assert.sameValue(windows.length, 0, '"only-full" on empty iterator'); + +windows = Array.from(g().windows(2, 'allow-partial')); +assert.sameValue(windows.length, 0, '"allow-partial" on empty iterator'); diff --git a/test/built-ins/Iterator/prototype/windows/iterator-return-method-throws.js b/test/built-ins/Iterator/prototype/windows/iterator-return-method-throws.js new file mode 100644 index 00000000000..2a3ac6ce5a3 --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/iterator-return-method-throws.js @@ -0,0 +1,28 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.windows +description: > + Iterator has throwing return +info: | + Iterator.prototype.windows ( windowSize [ , undersized ] ) + +features: [iterator-chunking] +---*/ +class IteratorThrows extends Iterator { + next() { + return { + done: false, + value: 0, + }; + } + return() { + throw new Test262Error(); + } +} + +let iterator = new IteratorThrows().windows(1); + +assert.throws(Test262Error, function () { + iterator.return(); +}); diff --git a/test/built-ins/Iterator/prototype/windows/length.js b/test/built-ins/Iterator/prototype/windows/length.js new file mode 100644 index 00000000000..bda06930409 --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/length.js @@ -0,0 +1,22 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.windows +description: > + Iterator.prototype.windows has a "length" property whose value is 1. +info: | + ECMAScript Standard Built-in Objects + + Unless otherwise specified, the length property of a built-in + Function object has the attributes { [[Writable]]: false, [[Enumerable]]: + false, [[Configurable]]: true }. +features: [iterator-chunking] +includes: [propertyHelper.js] +---*/ + +verifyProperty(Iterator.prototype.windows, 'length', { + value: 1, + writable: false, + enumerable: false, + configurable: true, +}); diff --git a/test/built-ins/Iterator/prototype/windows/name.js b/test/built-ins/Iterator/prototype/windows/name.js new file mode 100644 index 00000000000..1b877069336 --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/name.js @@ -0,0 +1,27 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.windows +description: > + The "name" property of Iterator.prototype.windows +info: | + 17 ECMAScript Standard Built-in Objects + + Every built-in Function object, including constructors, that is not + identified as an anonymous function has a name property whose value is a + String. Unless otherwise specified, this value is the name that is given to + the function in this specification. + + Unless otherwise specified, the name property of a built-in Function + object, if it exists, has the attributes { [[Writable]]: false, + [[Enumerable]]: false, [[Configurable]]: true }. +features: [iterator-chunking] +includes: [propertyHelper.js] +---*/ + +verifyProperty(Iterator.prototype.windows, 'name', { + value: 'windows', + writable: false, + enumerable: false, + configurable: true, +}); diff --git a/test/built-ins/Iterator/prototype/windows/next-method-returns-non-object.js b/test/built-ins/Iterator/prototype/windows/next-method-returns-non-object.js new file mode 100644 index 00000000000..7e994c712ca --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/next-method-returns-non-object.js @@ -0,0 +1,22 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.windows +description: > + Underlying iterator next returns non-object +info: | + Iterator.prototype.windows ( windowSize [ , undersized ] ) + +features: [iterator-chunking] +---*/ +class NonObjectIterator extends Iterator { + next() { + return null; + } +} + +let iterator = new NonObjectIterator().windows(1); + +assert.throws(TypeError, function () { + iterator.next(); +}); diff --git a/test/built-ins/Iterator/prototype/windows/next-method-returns-throwing-done.js b/test/built-ins/Iterator/prototype/windows/next-method-returns-throwing-done.js new file mode 100644 index 00000000000..7552742e318 --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/next-method-returns-throwing-done.js @@ -0,0 +1,30 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.windows +description: > + Underlying iterator next returns object with throwing done getter +info: | + Iterator.prototype.windows ( windowSize [ , undersized ] ) + +features: [iterator-chunking] +---*/ +class ThrowingIterator extends Iterator { + next() { + return { + get done() { + throw new Test262Error(); + }, + value: 1, + }; + } + return() { + throw new Error(); + } +} + +let iterator = new ThrowingIterator().windows(1); + +assert.throws(Test262Error, function () { + iterator.next(); +}); diff --git a/test/built-ins/Iterator/prototype/windows/next-method-returns-throwing-value-done.js b/test/built-ins/Iterator/prototype/windows/next-method-returns-throwing-value-done.js new file mode 100644 index 00000000000..b1f2de77ef4 --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/next-method-returns-throwing-value-done.js @@ -0,0 +1,28 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.windows +description: > + Underlying iterator next returns object with throwing value getter, but is + already done +info: | + Iterator.prototype.windows ( windowSize [ , undersized ] ) + +features: [iterator-chunking] +---*/ +class ThrowingIterator extends Iterator { + next() { + return { + done: true, + get value() { + throw new Test262Error(); + }, + }; + } + return() { + throw new Error(); + } +} + +let iterator = new ThrowingIterator().windows(1); +iterator.next(); diff --git a/test/built-ins/Iterator/prototype/windows/next-method-returns-throwing-value.js b/test/built-ins/Iterator/prototype/windows/next-method-returns-throwing-value.js new file mode 100644 index 00000000000..474f847889b --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/next-method-returns-throwing-value.js @@ -0,0 +1,30 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.windows +description: > + Underlying iterator next returns object with throwing value getter +info: | + Iterator.prototype.windows ( windowSize [ , undersized ] ) + +features: [iterator-chunking] +---*/ +class ThrowingIterator extends Iterator { + next() { + return { + done: false, + get value() { + throw new Test262Error(); + }, + }; + } + return() { + throw new Error(); + } +} + +let iterator = new ThrowingIterator().windows(1); + +assert.throws(Test262Error, function () { + iterator.next(); +}); diff --git a/test/built-ins/Iterator/prototype/windows/next-method-throws.js b/test/built-ins/Iterator/prototype/windows/next-method-throws.js new file mode 100644 index 00000000000..55fd1befed3 --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/next-method-throws.js @@ -0,0 +1,22 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.windows +description: > + Underlying iterator next throws +info: | + Iterator.prototype.windows ( windowSize [ , undersized ] ) + +features: [iterator-chunking] +---*/ +class ThrowingIterator extends Iterator { + next() { + throw new Test262Error(); + } +} + +let iterator = new ThrowingIterator().windows(1); + +assert.throws(Test262Error, function () { + iterator.next(); +}); diff --git a/test/built-ins/Iterator/prototype/windows/non-constructible.js b/test/built-ins/Iterator/prototype/windows/non-constructible.js new file mode 100644 index 00000000000..e38d43d728c --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/non-constructible.js @@ -0,0 +1,26 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.windows +description: > + Iterator.prototype.windows is not constructible. + + Built-in function objects that are not identified as constructors do not + implement the [[Construct]] internal method unless otherwise specified in + the description of a particular function. +features: [iterator-chunking] +---*/ +function* g() {} +let iter = g(); + +assert.throws(TypeError, () => { + new iter.windows(1); +}); + +assert.throws(TypeError, () => { + new Iterator.prototype.windows(1); +}); + +assert.throws(TypeError, () => { + new class extends Iterator {}.windows(1); +}); diff --git a/test/built-ins/Iterator/prototype/windows/prop-desc.js b/test/built-ins/Iterator/prototype/windows/prop-desc.js new file mode 100644 index 00000000000..7a7dd12d061 --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/prop-desc.js @@ -0,0 +1,23 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.windows +description: > + Property descriptor of Iterator.prototype.windows +info: | + Iterator.prototype.windows + + 17 ECMAScript Standard Built-in Objects + + Every other data property described in clauses 18 through 26 and in Annex B.2 + has the attributes { [[Writable]]: true, [[Enumerable]]: false, + [[Configurable]]: true } unless otherwise specified. +features: [iterator-chunking] +includes: [propertyHelper.js] +---*/ + +verifyProperty(Iterator.prototype, 'windows', { + writable: true, + enumerable: false, + configurable: true, +}); diff --git a/test/built-ins/Iterator/prototype/windows/proto.js b/test/built-ins/Iterator/prototype/windows/proto.js new file mode 100644 index 00000000000..93f4cbf44a1 --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/proto.js @@ -0,0 +1,11 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.windows +description: > + The value of the [[Prototype]] internal slot of Iterator.prototype.windows is the + intrinsic object %FunctionPrototype%. +features: [iterator-chunking] +---*/ + +assert.sameValue(Object.getPrototypeOf(Iterator.prototype.windows), Function.prototype); diff --git a/test/built-ins/Iterator/prototype/windows/result-is-iterator.js b/test/built-ins/Iterator/prototype/windows/result-is-iterator.js new file mode 100644 index 00000000000..253b6c86544 --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/result-is-iterator.js @@ -0,0 +1,18 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.windows +description: > + The value returned by Iterator.prototype.windows is an Iterator instance +info: | + Iterator.prototype.windows ( windowSize [ , undersized ] ) + + 8. Let result be CreateIteratorFromClosure(closure, "Iterator Helper", %IteratorHelperPrototype%, ยซ [[UnderlyingIterators]] ยป). + +features: [iterator-chunking] +---*/ + +assert( + (function* () {})().windows(1) instanceof Iterator, + 'function*(){}().windows(1) must return an Iterator' +); diff --git a/test/built-ins/Iterator/prototype/windows/return-is-forwarded-to-underlying-iterator.js b/test/built-ins/Iterator/prototype/windows/return-is-forwarded-to-underlying-iterator.js new file mode 100644 index 00000000000..647b8b6a82b --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/return-is-forwarded-to-underlying-iterator.js @@ -0,0 +1,32 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.windows +description: > + Underlying iterator return is called when result iterator is closed +info: | + Iterator.prototype.windows ( windowSize [ , undersized ] ) + +features: [iterator-chunking] +---*/ +let returnCount = 0; + +class TestIterator extends Iterator { + next() { + return { + done: false, + value: 1, + }; + } + return() { + ++returnCount; + return {}; + } +} + +let iterator = new TestIterator().windows(2); +assert.sameValue(returnCount, 0); +iterator.return(); +assert.sameValue(returnCount, 1); +iterator.return(); +assert.sameValue(returnCount, 1); diff --git a/test/built-ins/Iterator/prototype/windows/return-is-not-forwarded-after-exhaustion.js b/test/built-ins/Iterator/prototype/windows/return-is-not-forwarded-after-exhaustion.js new file mode 100644 index 00000000000..9b202a97cb5 --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/return-is-not-forwarded-after-exhaustion.js @@ -0,0 +1,35 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.windows +description: > + Underlying iterator return is not called after result iterator observes + that underlying iterator is exhausted +info: | + Iterator.prototype.windows ( windowSize [ , undersized ] ) + +features: [iterator-chunking] +---*/ + +class TestIterator extends Iterator { + next() { + return { + done: true, + value: undefined, + }; + } + return() { + throw new Test262Error(); + } +} + +let iterator = new TestIterator().windows(1); +assert.throws(Test262Error, function () { + iterator.return(); +}); +iterator.next(); +iterator.return(); + +iterator = new TestIterator().windows(1); +iterator.next(); +iterator.return(); diff --git a/test/built-ins/Iterator/prototype/windows/this-non-callable-next.js b/test/built-ins/Iterator/prototype/windows/this-non-callable-next.js new file mode 100644 index 00000000000..3db8dbcc7df --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/this-non-callable-next.js @@ -0,0 +1,19 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.windows +description: > + Iterator.prototype.windows throws TypeError when its this value is an object + with a non-callable next +info: | + Iterator.prototype.windows ( windowSize [ , undersized ] ) + + 7. Set iterated to ? GetIteratorDirect(O). + +features: [iterator-chunking] +---*/ +let iter = Iterator.prototype.windows.call({ next: 0 }, 1); + +assert.throws(TypeError, function () { + iter.next(); +}); diff --git a/test/built-ins/Iterator/prototype/windows/this-non-object.js b/test/built-ins/Iterator/prototype/windows/this-non-object.js new file mode 100644 index 00000000000..162d7bd0b74 --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/this-non-object.js @@ -0,0 +1,26 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.windows +description: > + Iterator.prototype.windows throws TypeError when its this value is a non-object +info: | + Iterator.prototype.windows ( windowSize [ , undersized ] ) + + 1. Let O be the this value. + 2. If O is not an Object, throw a TypeError exception. + +features: [iterator-chunking] +---*/ +assert.throws(TypeError, function () { + Iterator.prototype.windows.call(null, 1); +}); + +Object.defineProperty(Number.prototype, 'next', { + get: function () { + throw new Test262Error(); + }, +}); +assert.throws(TypeError, function () { + Iterator.prototype.windows.call(0, 1); +}); diff --git a/test/built-ins/Iterator/prototype/windows/this-plain-iterator.js b/test/built-ins/Iterator/prototype/windows/this-plain-iterator.js new file mode 100644 index 00000000000..cdbf4d4e60c --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/this-plain-iterator.js @@ -0,0 +1,36 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.windows +description: > + Iterator.prototype.windows supports a this value that does not inherit from + Iterator.prototype but implements the iterator protocol +info: | + Iterator.prototype.windows ( windowSize [ , undersized ] ) + +features: [iterator-chunking] +includes: [compareArray.js] +---*/ +let iter = { + get next() { + let count = 3; + return function () { + --count; + return count >= 0 ? { done: false, value: count } : { done: true, value: undefined }; + }; + }, +}; + +let windowed = Iterator.prototype.windows.call(iter, 2); + +let result = windowed.next(); +assert.compareArray(result.value, [2, 1]); +assert.sameValue(result.done, false); + +result = windowed.next(); +assert.compareArray(result.value, [1, 0]); +assert.sameValue(result.done, false); + +result = windowed.next(); +assert.sameValue(result.value, undefined); +assert.sameValue(result.done, true); diff --git a/test/built-ins/Iterator/prototype/windows/throws-typeerror-when-generator-is-running.js b/test/built-ins/Iterator/prototype/windows/throws-typeerror-when-generator-is-running.js new file mode 100644 index 00000000000..a06737878ce --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/throws-typeerror-when-generator-is-running.js @@ -0,0 +1,42 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.windows +description: > + Throws a TypeError when the closure generator is already running. +info: | + %IteratorHelperPrototype%.next ( ) + 1. Return ? GeneratorResume(this value, undefined, "Iterator Helper"). + + 27.5.3.3 GeneratorResume ( generator, value, generatorBrand ) + 1. Let state be ? GeneratorValidate(generator, generatorBrand). + ... + + 27.5.3.2 GeneratorValidate ( generator, generatorBrand ) + ... + 6. If state is executing, throw a TypeError exception. + ... + +features: [iterator-chunking] +---*/ + +var loopCount = 0; + +var iter; +var iterator = { + get next() { + return function () { + loopCount++; + iter.next(); + return { done: false, value: 0 }; + }; + }, +}; + +iter = Iterator.prototype.windows.call(iterator, 1); + +assert.throws(TypeError, function () { + iter.next(); +}); + +assert.sameValue(loopCount, 1); diff --git a/test/built-ins/Iterator/prototype/windows/underlying-iterator-advanced-in-parallel.js b/test/built-ins/Iterator/prototype/windows/underlying-iterator-advanced-in-parallel.js new file mode 100644 index 00000000000..42c54cae687 --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/underlying-iterator-advanced-in-parallel.js @@ -0,0 +1,35 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.windows +description: > + Underlying iterator is advanced after calling windows +info: | + Iterator.prototype.windows ( windowSize [ , undersized ] ) + +features: [iterator-chunking] +includes: [compareArray.js] +---*/ +let iterator = (function* () { + for (let i = 0; i < 6; ++i) { + yield i; + } +})(); + +let windowed = iterator.windows(2); + +let result = windowed.next(); +assert.compareArray(result.value, [0, 1]); +assert.sameValue(result.done, false); + +let { value, done } = iterator.next(); +assert.sameValue(value, 2); +assert.sameValue(done, false); + +result = windowed.next(); +assert.compareArray(result.value, [1, 3]); +assert.sameValue(result.done, false); + +result = windowed.next(); +assert.compareArray(result.value, [3, 4]); +assert.sameValue(result.done, false); diff --git a/test/built-ins/Iterator/prototype/windows/underlying-iterator-closed-in-parallel.js b/test/built-ins/Iterator/prototype/windows/underlying-iterator-closed-in-parallel.js new file mode 100644 index 00000000000..99304aa931b --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/underlying-iterator-closed-in-parallel.js @@ -0,0 +1,25 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.windows +description: > + Underlying iterator is closed after calling windows +info: | + Iterator.prototype.windows ( windowSize [ , undersized ] ) + +features: [iterator-chunking] +---*/ +let iterator = (function* () { + for (let i = 0; i < 5; ++i) { + yield i; + } +})(); + +let windowed = iterator.windows(2); + +iterator.return(); + +let { value, done } = windowed.next(); + +assert.sameValue(value, undefined); +assert.sameValue(done, true); diff --git a/test/built-ins/Iterator/prototype/windows/undersized-default.js b/test/built-ins/Iterator/prototype/windows/undersized-default.js new file mode 100644 index 00000000000..4d8d4f972b8 --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/undersized-default.js @@ -0,0 +1,33 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.windows +description: > + When undersized is undefined or omitted, it defaults to "only-full" behavior +info: | + Iterator.prototype.windows ( windowSize [ , undersized ] ) + + 5. If undersized is undefined, set undersized to "only-full". + +features: [iterator-chunking] +---*/ +function* g() { + yield 0; + yield 1; + yield 2; + yield 3; + yield 4; + yield 5; +} + +// With windowSize larger than iterator, "only-full" yields nothing +let result; + +result = Array.from(g().windows(100)); +assert.sameValue(result.length, 0, 'omitted undersized defaults to "only-full"'); + +result = Array.from(g().windows(100, undefined)); +assert.sameValue(result.length, 0, 'explicit undefined defaults to "only-full"'); + +result = Array.from(g().windows(100, 'only-full')); +assert.sameValue(result.length, 0, 'explicit "only-full" yields nothing'); diff --git a/test/built-ins/Iterator/prototype/windows/undersized-invalid.js b/test/built-ins/Iterator/prototype/windows/undersized-invalid.js new file mode 100644 index 00000000000..ad53b62a8cc --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/undersized-invalid.js @@ -0,0 +1,50 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.windows +description: > + Iterator.prototype.windows throws TypeError when undersized is not a valid + value +info: | + Iterator.prototype.windows ( windowSize [ , undersized ] ) + + 5. If undersized is undefined, set undersized to "only-full". + 6. If undersized is neither "only-full" nor "allow-partial", then + a. Let error be ThrowCompletion(a newly created TypeError object). + b. Return ? IteratorClose(iterated, error). + +features: [iterator-chunking] +---*/ +let iterator = (function* () {})(); + +assert.throws(TypeError, () => { + iterator.windows(1, null); +}); + +assert.throws(TypeError, () => { + iterator.windows(1, ''); +}); + +assert.throws(TypeError, () => { + iterator.windows(1, 'something else'); +}); + +assert.throws(TypeError, () => { + iterator.windows(1, 0); +}); + +assert.throws(TypeError, () => { + iterator.windows(1, true); +}); + +assert.throws(TypeError, () => { + iterator.windows(1, false); +}); + +assert.throws(TypeError, () => { + iterator.windows(1, {}); +}); + +assert.throws(TypeError, () => { + iterator.windows(1, Symbol()); +}); diff --git a/test/built-ins/Iterator/prototype/windows/windowSize-no-coercion.js b/test/built-ins/Iterator/prototype/windows/windowSize-no-coercion.js new file mode 100644 index 00000000000..d4468338708 --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/windowSize-no-coercion.js @@ -0,0 +1,38 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.windows +description: > + Iterator.prototype.windows does not coerce windowSize using ToNumber; the + argument must already be a Number. Unlike take/drop, valueOf and toString + are never called. +info: | + Iterator.prototype.windows ( windowSize [ , undersized ] ) + + 4. If windowSize is not a Number, throw a TypeError exception. + +features: [iterator-chunking] +---*/ +let iterator = (function* () {})(); + +let valueOfCalled = false; +assert.throws(TypeError, () => { + iterator.windows({ + valueOf() { + valueOfCalled = true; + return 2; + }, + }); +}); +assert.sameValue(valueOfCalled, false, 'valueOf must not be called'); + +let toStringCalled = false; +assert.throws(TypeError, () => { + iterator.windows({ + toString() { + toStringCalled = true; + return '2'; + }, + }); +}); +assert.sameValue(toStringCalled, false, 'toString must not be called'); diff --git a/test/built-ins/Iterator/prototype/windows/windowSize-not-a-number.js b/test/built-ins/Iterator/prototype/windows/windowSize-not-a-number.js new file mode 100644 index 00000000000..471bdceda63 --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/windowSize-not-a-number.js @@ -0,0 +1,68 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.windows +description: > + Iterator.prototype.windows throws TypeError when windowSize is not an + integral Number +info: | + Iterator.prototype.windows ( windowSize [ , undersized ] ) + + 4. If windowSize is not a Number, throw a TypeError exception. + 5. If windowSize is not an integral Number, throw a TypeError exception. + +features: [iterator-chunking] +---*/ +let iterator = (function* () {})(); + +assert.throws(TypeError, () => { + iterator.windows(); +}); + +assert.throws(TypeError, () => { + iterator.windows(undefined); +}); + +assert.throws(TypeError, () => { + iterator.windows('1'); +}); + +assert.throws(TypeError, () => { + iterator.windows(true); +}); + +assert.throws(TypeError, () => { + iterator.windows(null); +}); + +assert.throws(TypeError, () => { + iterator.windows({}); +}); + +assert.throws(TypeError, () => { + iterator.windows(Symbol()); +}); + +assert.throws(TypeError, () => { + iterator.windows([2]); +}); + +assert.throws(TypeError, () => { + iterator.windows(NaN); +}); + +assert.throws(TypeError, () => { + iterator.windows(0.5); +}); + +assert.throws(TypeError, () => { + iterator.windows(1.5); +}); + +assert.throws(TypeError, () => { + iterator.windows(Infinity); +}); + +assert.throws(TypeError, () => { + iterator.windows(-Infinity); +}); diff --git a/test/built-ins/Iterator/prototype/windows/windowSize-out-of-range.js b/test/built-ins/Iterator/prototype/windows/windowSize-out-of-range.js new file mode 100644 index 00000000000..ad25c87fba5 --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/windowSize-out-of-range.js @@ -0,0 +1,41 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.windows +description: > + Iterator.prototype.windows throws RangeError when windowSize is an integral + Number outside the valid range [1, 2^32 - 1] +info: | + Iterator.prototype.windows ( windowSize [ , undersized ] ) + + 6. If windowSize is not in the inclusive interval from 1๐”ฝ to ๐”ฝ(2^32 - 1), then + a. Let error be ThrowCompletion(a newly created RangeError object). + b. Return ? IteratorClose(iterated, error). + +features: [iterator-chunking] +---*/ +let iterator = (function* () {})(); + +assert.throws(RangeError, () => { + iterator.windows(0); +}); + +assert.throws(RangeError, () => { + iterator.windows(-0); +}); + +assert.throws(RangeError, () => { + iterator.windows(-1); +}); + +assert.throws(RangeError, () => { + iterator.windows(2 ** 32); +}); + +assert.throws(RangeError, () => { + iterator.windows(2 ** 53); +}); + +// Boundary: valid values do not throw +iterator.windows(1); +iterator.windows(2 ** 32 - 1); diff --git a/test/built-ins/Iterator/prototype/windows/windows-allow-partial.js b/test/built-ins/Iterator/prototype/windows/windows-allow-partial.js new file mode 100644 index 00000000000..84274964685 --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/windows-allow-partial.js @@ -0,0 +1,31 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.windows +description: > + When undersized is "allow-partial", a partial final window is yielded if + the buffer is non-empty and smaller than windowSize at exhaustion +info: | + Iterator.prototype.windows ( windowSize [ , undersized ] ) + + 8.a.ii. If value is ~done~, then + 8.a.ii.a. If undersized is "allow-partial", buffer is not empty, and the number of elements in buffer < โ„(windowSize), then + 8.a.ii.a.i. Perform Completion(Yield(CreateArrayFromList(buffer))). + 8.a.ii.b. Return ReturnCompletion(undefined). + +features: [iterator-chunking] +includes: [compareArray.js] +---*/ +function* g() { + yield 0; + yield 1; + yield 2; + yield 3; + yield 4; + yield 5; +} + +let windows = Array.from(g().windows(100, 'allow-partial')); + +assert.sameValue(windows.length, 1); +assert.compareArray(windows[0], [0, 1, 2, 3, 4, 5]); diff --git a/test/built-ins/Iterator/prototype/windows/windows-basic.js b/test/built-ins/Iterator/prototype/windows/windows-basic.js new file mode 100644 index 00000000000..87a472ff87f --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/windows-basic.js @@ -0,0 +1,33 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.windows +description: > + Iterator.prototype.windows yields sliding windows of the specified size +info: | + Iterator.prototype.windows ( windowSize [ , undersized ] ) + + 8.a.iii. If the number of elements in buffer is โ„(windowSize), then + 8.a.iii.a. Remove the first element from buffer. + 8.a.iv. Append value to buffer. + 8.a.v. If the number of elements in buffer is โ„(windowSize), then + 8.a.v.a. Let completion be Completion(Yield(CreateArrayFromList(buffer))). + +features: [iterator-chunking] +includes: [compareArray.js] +---*/ +function* g() { + yield 0; + yield 1; + yield 2; + yield 3; + yield 4; +} + +let windows = Array.from(g().windows(2)); + +assert.sameValue(windows.length, 4); +assert.compareArray(windows[0], [0, 1]); +assert.compareArray(windows[1], [1, 2]); +assert.compareArray(windows[2], [2, 3]); +assert.compareArray(windows[3], [3, 4]); diff --git a/test/built-ins/Iterator/prototype/windows/windows-size-1.js b/test/built-ins/Iterator/prototype/windows/windows-size-1.js new file mode 100644 index 00000000000..3d19e6d3d1f --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/windows-size-1.js @@ -0,0 +1,28 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.windows +description: > + When windowSize is 1, each element is yielded as a single-element array +info: | + Iterator.prototype.windows ( windowSize [ , undersized ] ) + +features: [iterator-chunking] +includes: [compareArray.js] +---*/ +function* g() { + yield 0; + yield 1; + yield 2; + yield 3; + yield 4; +} + +let windows = Array.from(g().windows(1)); + +assert.sameValue(windows.length, 5); +assert.compareArray(windows[0], [0]); +assert.compareArray(windows[1], [1]); +assert.compareArray(windows[2], [2]); +assert.compareArray(windows[3], [3]); +assert.compareArray(windows[4], [4]); diff --git a/test/built-ins/Iterator/prototype/windows/windows-size-3.js b/test/built-ins/Iterator/prototype/windows/windows-size-3.js new file mode 100644 index 00000000000..282c44e118f --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/windows-size-3.js @@ -0,0 +1,28 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.windows +description: > + Iterator.prototype.windows with windowSize 3 +info: | + Iterator.prototype.windows ( windowSize [ , undersized ] ) + +features: [iterator-chunking] +includes: [compareArray.js] +---*/ +function* g() { + yield 0; + yield 1; + yield 2; + yield 3; + yield 4; + yield 5; +} + +let windows = Array.from(g().windows(3)); + +assert.sameValue(windows.length, 4); +assert.compareArray(windows[0], [0, 1, 2]); +assert.compareArray(windows[1], [1, 2, 3]); +assert.compareArray(windows[2], [2, 3, 4]); +assert.compareArray(windows[3], [3, 4, 5]); diff --git a/test/built-ins/Iterator/prototype/windows/yields-distinct-arrays.js b/test/built-ins/Iterator/prototype/windows/yields-distinct-arrays.js new file mode 100644 index 00000000000..3258c3c6700 --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/yields-distinct-arrays.js @@ -0,0 +1,29 @@ +// Copyright (C) 2026 Michael Ficarra. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iterator.prototype.windows +description: > + Each yielded window is a distinct new Array object +info: | + Iterator.prototype.windows ( windowSize [ , undersized ] ) + + 8.a.v.a. Let completion be Completion(Yield(CreateArrayFromList(buffer))). + +features: [iterator-chunking] +---*/ +function* g() { + yield 0; + yield 1; + yield 2; + yield 3; +} + +let windows = Array.from(g().windows(2)); + +assert.sameValue(windows.length, 3); +assert(Array.isArray(windows[0]), 'windows[0] is an Array'); +assert(Array.isArray(windows[1]), 'windows[1] is an Array'); +assert(Array.isArray(windows[2]), 'windows[2] is an Array'); +assert.notSameValue(windows[0], windows[1], 'windows[0] !== windows[1]'); +assert.notSameValue(windows[1], windows[2], 'windows[1] !== windows[2]'); +assert.notSameValue(windows[0], windows[2], 'windows[0] !== windows[2]');