From 1a167080d842a9526e86f5272a09dd984055be6a Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Wed, 1 Apr 2026 14:32:24 -0600 Subject: [PATCH 1/2] iterator chunking tests --- features.txt | 1 + .../prototype/chunks/argument-effect-order.js | 58 +++++++++++++ ...nt-validation-failure-closes-underlying.js | 51 ++++++++++++ .../Iterator/prototype/chunks/callable.js | 13 +++ .../prototype/chunks/chunkSize-no-coercion.js | 40 +++++++++ .../chunks/chunkSize-not-a-number.js | 48 +++++++++++ .../chunks/chunkSize-out-of-range.js | 61 ++++++++++++++ .../chunks/chunks-evenly-divisible.js | 33 ++++++++ .../chunks/chunks-last-chunk-partial.js | 32 ++++++++ .../prototype/chunks/chunks-size-1.js | 28 +++++++ .../chunks-size-larger-than-iterator.js | 31 +++++++ .../chunks/exhaustion-does-not-call-return.js | 32 ++++++++ .../chunks/get-next-method-only-once.js | 40 +++++++++ .../chunks/get-next-method-throws.js | 24 ++++++ .../chunks/get-return-method-throws.js | 29 +++++++ .../Iterator/prototype/chunks/is-function.js | 10 +++ .../chunks/iterator-already-exhausted.js | 22 +++++ .../chunks/iterator-return-method-throws.js | 28 +++++++ .../Iterator/prototype/chunks/length.js | 22 +++++ .../Iterator/prototype/chunks/name.js | 27 +++++++ .../chunks/next-method-returns-non-object.js | 22 +++++ .../next-method-returns-throwing-done.js | 30 +++++++ ...next-method-returns-throwing-value-done.js | 28 +++++++ .../next-method-returns-throwing-value.js | 30 +++++++ .../prototype/chunks/next-method-throws.js | 22 +++++ .../prototype/chunks/non-constructible.js | 26 ++++++ .../Iterator/prototype/chunks/prop-desc.js | 23 ++++++ .../Iterator/prototype/chunks/proto.js | 11 +++ .../prototype/chunks/result-is-iterator.js | 18 +++++ ...urn-is-forwarded-to-underlying-iterator.js | 32 ++++++++ ...eturn-is-not-forwarded-after-exhaustion.js | 35 ++++++++ .../chunks/this-non-callable-next.js | 19 +++++ .../prototype/chunks/this-non-object.js | 26 ++++++ .../prototype/chunks/this-plain-iterator.js | 36 +++++++++ ...ows-typeerror-when-generator-is-running.js | 42 ++++++++++ ...nderlying-iterator-advanced-in-parallel.js | 40 +++++++++ .../underlying-iterator-closed-in-parallel.js | 25 ++++++ .../chunks/yields-distinct-arrays.js | 26 ++++++ .../windows/argument-effect-order.js | 81 +++++++++++++++++++ ...nt-validation-failure-closes-underlying.js | 69 ++++++++++++++++ .../Iterator/prototype/windows/callable.js | 13 +++ .../exhaustion-does-not-call-return.js | 33 ++++++++ .../windows/get-next-method-only-once.js | 40 +++++++++ .../windows/get-next-method-throws.js | 24 ++++++ .../windows/get-return-method-throws.js | 29 +++++++ .../Iterator/prototype/windows/is-function.js | 10 +++ .../windows/iterator-already-exhausted.js | 27 +++++++ .../windows/iterator-return-method-throws.js | 28 +++++++ .../Iterator/prototype/windows/length.js | 22 +++++ .../Iterator/prototype/windows/name.js | 27 +++++++ .../windows/next-method-returns-non-object.js | 22 +++++ .../next-method-returns-throwing-done.js | 30 +++++++ ...next-method-returns-throwing-value-done.js | 28 +++++++ .../next-method-returns-throwing-value.js | 30 +++++++ .../prototype/windows/next-method-throws.js | 22 +++++ .../prototype/windows/non-constructible.js | 26 ++++++ .../Iterator/prototype/windows/prop-desc.js | 23 ++++++ .../Iterator/prototype/windows/proto.js | 11 +++ .../prototype/windows/result-is-iterator.js | 18 +++++ ...urn-is-forwarded-to-underlying-iterator.js | 32 ++++++++ ...eturn-is-not-forwarded-after-exhaustion.js | 35 ++++++++ .../windows/this-non-callable-next.js | 19 +++++ .../prototype/windows/this-non-object.js | 26 ++++++ .../prototype/windows/this-plain-iterator.js | 36 +++++++++ ...ows-typeerror-when-generator-is-running.js | 42 ++++++++++ ...nderlying-iterator-advanced-in-parallel.js | 35 ++++++++ .../underlying-iterator-closed-in-parallel.js | 25 ++++++ .../prototype/windows/undersized-default.js | 33 ++++++++ .../prototype/windows/undersized-invalid.js | 50 ++++++++++++ .../windows/windowSize-no-coercion.js | 40 +++++++++ .../windows/windowSize-not-a-number.js | 48 +++++++++++ .../windows/windowSize-out-of-range.js | 61 ++++++++++++++ .../windows/windows-allow-partial.js | 31 +++++++ .../prototype/windows/windows-basic.js | 33 ++++++++ .../prototype/windows/windows-size-1.js | 28 +++++++ .../prototype/windows/windows-size-3.js | 28 +++++++ .../windows/yields-distinct-arrays.js | 29 +++++++ 77 files changed, 2365 insertions(+) create mode 100644 test/built-ins/Iterator/prototype/chunks/argument-effect-order.js create mode 100644 test/built-ins/Iterator/prototype/chunks/argument-validation-failure-closes-underlying.js create mode 100644 test/built-ins/Iterator/prototype/chunks/callable.js create mode 100644 test/built-ins/Iterator/prototype/chunks/chunkSize-no-coercion.js create mode 100644 test/built-ins/Iterator/prototype/chunks/chunkSize-not-a-number.js create mode 100644 test/built-ins/Iterator/prototype/chunks/chunkSize-out-of-range.js create mode 100644 test/built-ins/Iterator/prototype/chunks/chunks-evenly-divisible.js create mode 100644 test/built-ins/Iterator/prototype/chunks/chunks-last-chunk-partial.js create mode 100644 test/built-ins/Iterator/prototype/chunks/chunks-size-1.js create mode 100644 test/built-ins/Iterator/prototype/chunks/chunks-size-larger-than-iterator.js create mode 100644 test/built-ins/Iterator/prototype/chunks/exhaustion-does-not-call-return.js create mode 100644 test/built-ins/Iterator/prototype/chunks/get-next-method-only-once.js create mode 100644 test/built-ins/Iterator/prototype/chunks/get-next-method-throws.js create mode 100644 test/built-ins/Iterator/prototype/chunks/get-return-method-throws.js create mode 100644 test/built-ins/Iterator/prototype/chunks/is-function.js create mode 100644 test/built-ins/Iterator/prototype/chunks/iterator-already-exhausted.js create mode 100644 test/built-ins/Iterator/prototype/chunks/iterator-return-method-throws.js create mode 100644 test/built-ins/Iterator/prototype/chunks/length.js create mode 100644 test/built-ins/Iterator/prototype/chunks/name.js create mode 100644 test/built-ins/Iterator/prototype/chunks/next-method-returns-non-object.js create mode 100644 test/built-ins/Iterator/prototype/chunks/next-method-returns-throwing-done.js create mode 100644 test/built-ins/Iterator/prototype/chunks/next-method-returns-throwing-value-done.js create mode 100644 test/built-ins/Iterator/prototype/chunks/next-method-returns-throwing-value.js create mode 100644 test/built-ins/Iterator/prototype/chunks/next-method-throws.js create mode 100644 test/built-ins/Iterator/prototype/chunks/non-constructible.js create mode 100644 test/built-ins/Iterator/prototype/chunks/prop-desc.js create mode 100644 test/built-ins/Iterator/prototype/chunks/proto.js create mode 100644 test/built-ins/Iterator/prototype/chunks/result-is-iterator.js create mode 100644 test/built-ins/Iterator/prototype/chunks/return-is-forwarded-to-underlying-iterator.js create mode 100644 test/built-ins/Iterator/prototype/chunks/return-is-not-forwarded-after-exhaustion.js create mode 100644 test/built-ins/Iterator/prototype/chunks/this-non-callable-next.js create mode 100644 test/built-ins/Iterator/prototype/chunks/this-non-object.js create mode 100644 test/built-ins/Iterator/prototype/chunks/this-plain-iterator.js create mode 100644 test/built-ins/Iterator/prototype/chunks/throws-typeerror-when-generator-is-running.js create mode 100644 test/built-ins/Iterator/prototype/chunks/underlying-iterator-advanced-in-parallel.js create mode 100644 test/built-ins/Iterator/prototype/chunks/underlying-iterator-closed-in-parallel.js create mode 100644 test/built-ins/Iterator/prototype/chunks/yields-distinct-arrays.js create mode 100644 test/built-ins/Iterator/prototype/windows/argument-effect-order.js create mode 100644 test/built-ins/Iterator/prototype/windows/argument-validation-failure-closes-underlying.js create mode 100644 test/built-ins/Iterator/prototype/windows/callable.js create mode 100644 test/built-ins/Iterator/prototype/windows/exhaustion-does-not-call-return.js create mode 100644 test/built-ins/Iterator/prototype/windows/get-next-method-only-once.js create mode 100644 test/built-ins/Iterator/prototype/windows/get-next-method-throws.js create mode 100644 test/built-ins/Iterator/prototype/windows/get-return-method-throws.js create mode 100644 test/built-ins/Iterator/prototype/windows/is-function.js create mode 100644 test/built-ins/Iterator/prototype/windows/iterator-already-exhausted.js create mode 100644 test/built-ins/Iterator/prototype/windows/iterator-return-method-throws.js create mode 100644 test/built-ins/Iterator/prototype/windows/length.js create mode 100644 test/built-ins/Iterator/prototype/windows/name.js create mode 100644 test/built-ins/Iterator/prototype/windows/next-method-returns-non-object.js create mode 100644 test/built-ins/Iterator/prototype/windows/next-method-returns-throwing-done.js create mode 100644 test/built-ins/Iterator/prototype/windows/next-method-returns-throwing-value-done.js create mode 100644 test/built-ins/Iterator/prototype/windows/next-method-returns-throwing-value.js create mode 100644 test/built-ins/Iterator/prototype/windows/next-method-throws.js create mode 100644 test/built-ins/Iterator/prototype/windows/non-constructible.js create mode 100644 test/built-ins/Iterator/prototype/windows/prop-desc.js create mode 100644 test/built-ins/Iterator/prototype/windows/proto.js create mode 100644 test/built-ins/Iterator/prototype/windows/result-is-iterator.js create mode 100644 test/built-ins/Iterator/prototype/windows/return-is-forwarded-to-underlying-iterator.js create mode 100644 test/built-ins/Iterator/prototype/windows/return-is-not-forwarded-after-exhaustion.js create mode 100644 test/built-ins/Iterator/prototype/windows/this-non-callable-next.js create mode 100644 test/built-ins/Iterator/prototype/windows/this-non-object.js create mode 100644 test/built-ins/Iterator/prototype/windows/this-plain-iterator.js create mode 100644 test/built-ins/Iterator/prototype/windows/throws-typeerror-when-generator-is-running.js create mode 100644 test/built-ins/Iterator/prototype/windows/underlying-iterator-advanced-in-parallel.js create mode 100644 test/built-ins/Iterator/prototype/windows/underlying-iterator-closed-in-parallel.js create mode 100644 test/built-ins/Iterator/prototype/windows/undersized-default.js create mode 100644 test/built-ins/Iterator/prototype/windows/undersized-invalid.js create mode 100644 test/built-ins/Iterator/prototype/windows/windowSize-no-coercion.js create mode 100644 test/built-ins/Iterator/prototype/windows/windowSize-not-a-number.js create mode 100644 test/built-ins/Iterator/prototype/windows/windowSize-out-of-range.js create mode 100644 test/built-ins/Iterator/prototype/windows/windows-allow-partial.js create mode 100644 test/built-ins/Iterator/prototype/windows/windows-basic.js create mode 100644 test/built-ins/Iterator/prototype/windows/windows-size-1.js create mode 100644 test/built-ins/Iterator/prototype/windows/windows-size-3.js create mode 100644 test/built-ins/Iterator/prototype/windows/yields-distinct-arrays.js 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..89f36d35640 --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/argument-effect-order.js @@ -0,0 +1,58 @@ +// 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 an integral Number 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). + 5. 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..edbb58ebc3e --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/argument-validation-failure-closes-underlying.js @@ -0,0 +1,51 @@ +// 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 an integral Number 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(RangeError, 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(RangeError, function () { + closable.chunks(NaN); +}); +assert.sameValue(closed, true, 'iterator closed when chunkSize is NaN'); + +closed = false; +assert.throws(RangeError, 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..f7b0b26b28f --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/chunkSize-no-coercion.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: > + 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 an integral Number 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* () {})(); + +let valueOfCalled = false; +assert.throws(RangeError, () => { + iterator.chunks({ + valueOf() { + valueOfCalled = true; + return 2; + }, + }); +}); +assert.sameValue(valueOfCalled, false, 'valueOf must not be called'); + +let toStringCalled = false; +assert.throws(RangeError, () => { + 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..37c1d98c2a1 --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/chunkSize-not-a-number.js @@ -0,0 +1,48 @@ +// 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 not a Number +info: | + Iterator.prototype.chunks ( chunkSize ) + + 4. If chunkSize is not an integral Number 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(); +}); + +assert.throws(RangeError, () => { + iterator.chunks(undefined); +}); + +assert.throws(RangeError, () => { + iterator.chunks('1'); +}); + +assert.throws(RangeError, () => { + iterator.chunks(true); +}); + +assert.throws(RangeError, () => { + iterator.chunks(null); +}); + +assert.throws(RangeError, () => { + iterator.chunks({}); +}); + +assert.throws(RangeError, () => { + iterator.chunks(Symbol()); +}); + +assert.throws(RangeError, () => { + iterator.chunks([2]); +}); 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..bbf68291fa6 --- /dev/null +++ b/test/built-ins/Iterator/prototype/chunks/chunkSize-out-of-range.js @@ -0,0 +1,61 @@ +// 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 out of the + valid range [1, 2^32 - 1] +info: | + Iterator.prototype.chunks ( chunkSize ) + + 4. If chunkSize is not an integral Number 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(-Infinity); +}); + +assert.throws(RangeError, () => { + iterator.chunks(0.5); +}); + +assert.throws(RangeError, () => { + iterator.chunks(1.5); +}); + +assert.throws(RangeError, () => { + iterator.chunks(NaN); +}); + +assert.throws(RangeError, () => { + iterator.chunks(Infinity); +}); + +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..23afc3386c6 --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/argument-effect-order.js @@ -0,0 +1,81 @@ +// 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 an integral Number 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). + 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). + 7. 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..14fd6f8f5bc --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/argument-validation-failure-closes-underlying.js @@ -0,0 +1,69 @@ +// 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 an integral Number 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). + ... + 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 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(RangeError, 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(RangeError, function () { + closable.windows(NaN); +}); +assert.sameValue(closed, true, 'iterator closed when windowSize is NaN'); + +closed = false; +assert.throws(RangeError, 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..6e2fda0ba31 --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/windowSize-no-coercion.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: > + 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 an integral Number 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* () {})(); + +let valueOfCalled = false; +assert.throws(RangeError, () => { + iterator.windows({ + valueOf() { + valueOfCalled = true; + return 2; + }, + }); +}); +assert.sameValue(valueOfCalled, false, 'valueOf must not be called'); + +let toStringCalled = false; +assert.throws(RangeError, () => { + 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..da05e0dcc84 --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/windowSize-not-a-number.js @@ -0,0 +1,48 @@ +// 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 not a Number +info: | + Iterator.prototype.windows ( windowSize [ , undersized ] ) + + 4. If windowSize is not an integral Number 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(); +}); + +assert.throws(RangeError, () => { + iterator.windows(undefined); +}); + +assert.throws(RangeError, () => { + iterator.windows('1'); +}); + +assert.throws(RangeError, () => { + iterator.windows(true); +}); + +assert.throws(RangeError, () => { + iterator.windows(null); +}); + +assert.throws(RangeError, () => { + iterator.windows({}); +}); + +assert.throws(RangeError, () => { + iterator.windows(Symbol()); +}); + +assert.throws(RangeError, () => { + iterator.windows([2]); +}); 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..a5321a64016 --- /dev/null +++ b/test/built-ins/Iterator/prototype/windows/windowSize-out-of-range.js @@ -0,0 +1,61 @@ +// 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 out of the + valid range [1, 2^32 - 1] +info: | + Iterator.prototype.windows ( windowSize [ , undersized ] ) + + 4. If windowSize is not an integral Number 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(-Infinity); +}); + +assert.throws(RangeError, () => { + iterator.windows(0.5); +}); + +assert.throws(RangeError, () => { + iterator.windows(1.5); +}); + +assert.throws(RangeError, () => { + iterator.windows(NaN); +}); + +assert.throws(RangeError, () => { + iterator.windows(Infinity); +}); + +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]'); From eaaf57e79427a0b3c476670c188b1af9715dc615 Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Wed, 1 Apr 2026 17:36:54 -0600 Subject: [PATCH 2/2] change to TypeError --- .../prototype/chunks/argument-effect-order.js | 6 ++- ...nt-validation-failure-closes-underlying.js | 10 +++-- .../prototype/chunks/chunkSize-no-coercion.js | 8 ++-- .../chunks/chunkSize-not-a-number.js | 44 ++++++++++++++----- .../chunks/chunkSize-out-of-range.js | 26 ++--------- .../windows/argument-effect-order.js | 10 +++-- ...nt-validation-failure-closes-underlying.js | 12 ++--- .../windows/windowSize-no-coercion.js | 8 ++-- .../windows/windowSize-not-a-number.js | 44 ++++++++++++++----- .../windows/windowSize-out-of-range.js | 26 ++--------- 10 files changed, 99 insertions(+), 95 deletions(-) diff --git a/test/built-ins/Iterator/prototype/chunks/argument-effect-order.js b/test/built-ins/Iterator/prototype/chunks/argument-effect-order.js index 89f36d35640..e225374d37c 100644 --- a/test/built-ins/Iterator/prototype/chunks/argument-effect-order.js +++ b/test/built-ins/Iterator/prototype/chunks/argument-effect-order.js @@ -10,10 +10,12 @@ info: | 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 an integral Number in the inclusive interval from 1๐”ฝ to ๐”ฝ(2^32 - 1), then + 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). - 5. Set iterated to ? GetIteratorDirect(O). + 7. Set iterated to ? GetIteratorDirect(O). includes: [compareArray.js] features: [iterator-chunking] 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 index edbb58ebc3e..c9261f6317b 100644 --- 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 @@ -8,7 +8,9 @@ info: | Iterator.prototype.chunks ( chunkSize ) 3. Let iterated be the Iterator Record { [[Iterator]]: O, [[NextMethod]]: undefined, [[Done]]: false }. - 4. If chunkSize is not an integral Number in the inclusive interval from 1๐”ฝ to ๐”ฝ(2^32 - 1), then + 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). @@ -27,7 +29,7 @@ let closable = { }, }; -assert.throws(RangeError, function () { +assert.throws(TypeError, function () { closable.chunks(); }); assert.sameValue(closed, true, 'iterator closed when chunkSize is undefined'); @@ -39,13 +41,13 @@ assert.throws(RangeError, function () { assert.sameValue(closed, true, 'iterator closed when chunkSize is 0'); closed = false; -assert.throws(RangeError, function () { +assert.throws(TypeError, function () { closable.chunks(NaN); }); assert.sameValue(closed, true, 'iterator closed when chunkSize is NaN'); closed = false; -assert.throws(RangeError, function () { +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/chunkSize-no-coercion.js b/test/built-ins/Iterator/prototype/chunks/chunkSize-no-coercion.js index f7b0b26b28f..83b95c1775b 100644 --- a/test/built-ins/Iterator/prototype/chunks/chunkSize-no-coercion.js +++ b/test/built-ins/Iterator/prototype/chunks/chunkSize-no-coercion.js @@ -9,16 +9,14 @@ description: > info: | Iterator.prototype.chunks ( chunkSize ) - 4. If chunkSize is not an integral Number 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). + 4. If chunkSize is not a Number, throw a TypeError exception. features: [iterator-chunking] ---*/ let iterator = (function* () {})(); let valueOfCalled = false; -assert.throws(RangeError, () => { +assert.throws(TypeError, () => { iterator.chunks({ valueOf() { valueOfCalled = true; @@ -29,7 +27,7 @@ assert.throws(RangeError, () => { assert.sameValue(valueOfCalled, false, 'valueOf must not be called'); let toStringCalled = false; -assert.throws(RangeError, () => { +assert.throws(TypeError, () => { iterator.chunks({ toString() { toStringCalled = true; 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 index 37c1d98c2a1..35d59c0ea6d 100644 --- a/test/built-ins/Iterator/prototype/chunks/chunkSize-not-a-number.js +++ b/test/built-ins/Iterator/prototype/chunks/chunkSize-not-a-number.js @@ -3,46 +3,66 @@ /*--- esid: sec-iterator.prototype.chunks description: > - Iterator.prototype.chunks throws RangeError when chunkSize is not a Number + Iterator.prototype.chunks throws TypeError when chunkSize is not an integral + Number info: | Iterator.prototype.chunks ( chunkSize ) - 4. If chunkSize is not an integral Number 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). + 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(RangeError, () => { +assert.throws(TypeError, () => { iterator.chunks(); }); -assert.throws(RangeError, () => { +assert.throws(TypeError, () => { iterator.chunks(undefined); }); -assert.throws(RangeError, () => { +assert.throws(TypeError, () => { iterator.chunks('1'); }); -assert.throws(RangeError, () => { +assert.throws(TypeError, () => { iterator.chunks(true); }); -assert.throws(RangeError, () => { +assert.throws(TypeError, () => { iterator.chunks(null); }); -assert.throws(RangeError, () => { +assert.throws(TypeError, () => { iterator.chunks({}); }); -assert.throws(RangeError, () => { +assert.throws(TypeError, () => { iterator.chunks(Symbol()); }); -assert.throws(RangeError, () => { +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 index bbf68291fa6..04b5f55da2f 100644 --- a/test/built-ins/Iterator/prototype/chunks/chunkSize-out-of-range.js +++ b/test/built-ins/Iterator/prototype/chunks/chunkSize-out-of-range.js @@ -3,12 +3,12 @@ /*--- esid: sec-iterator.prototype.chunks description: > - Iterator.prototype.chunks throws RangeError when chunkSize is out of the - valid range [1, 2^32 - 1] + Iterator.prototype.chunks throws RangeError when chunkSize is an integral + Number outside the valid range [1, 2^32 - 1] info: | Iterator.prototype.chunks ( chunkSize ) - 4. If chunkSize is not an integral Number in the inclusive interval from 1๐”ฝ to ๐”ฝ(2^32 - 1), then + 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). @@ -28,26 +28,6 @@ assert.throws(RangeError, () => { iterator.chunks(-1); }); -assert.throws(RangeError, () => { - iterator.chunks(-Infinity); -}); - -assert.throws(RangeError, () => { - iterator.chunks(0.5); -}); - -assert.throws(RangeError, () => { - iterator.chunks(1.5); -}); - -assert.throws(RangeError, () => { - iterator.chunks(NaN); -}); - -assert.throws(RangeError, () => { - iterator.chunks(Infinity); -}); - assert.throws(RangeError, () => { iterator.chunks(2 ** 32); }); diff --git a/test/built-ins/Iterator/prototype/windows/argument-effect-order.js b/test/built-ins/Iterator/prototype/windows/argument-effect-order.js index 23afc3386c6..fe9f85ae1ae 100644 --- a/test/built-ins/Iterator/prototype/windows/argument-effect-order.js +++ b/test/built-ins/Iterator/prototype/windows/argument-effect-order.js @@ -10,14 +10,16 @@ info: | 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 an integral Number in the inclusive interval from 1๐”ฝ to ๐”ฝ(2^32 - 1), then + 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). - 5. If undersized is undefined, set undersized to "only-full". - 6. If undersized is neither "only-full" nor "allow-partial", then + 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). - 7. Set iterated to ? GetIteratorDirect(O). + 9. Set iterated to ? GetIteratorDirect(O). includes: [compareArray.js] features: [iterator-chunking] 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 index 14fd6f8f5bc..0cce429dc33 100644 --- 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 @@ -8,11 +8,13 @@ info: | Iterator.prototype.windows ( windowSize [ , undersized ] ) 3. Let iterated be the Iterator Record { [[Iterator]]: O, [[NextMethod]]: undefined, [[Done]]: false }. - 4. If windowSize is not an integral Number in the inclusive interval from 1๐”ฝ to ๐”ฝ(2^32 - 1), then + 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). ... - 6. If undersized is neither "only-full" nor "allow-partial", then + 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). @@ -32,7 +34,7 @@ let closable = { }; // windowSize validation failure closes -assert.throws(RangeError, function () { +assert.throws(TypeError, function () { closable.windows(); }); assert.sameValue(closed, true, 'iterator closed when windowSize is undefined'); @@ -44,13 +46,13 @@ assert.throws(RangeError, function () { assert.sameValue(closed, true, 'iterator closed when windowSize is 0'); closed = false; -assert.throws(RangeError, function () { +assert.throws(TypeError, function () { closable.windows(NaN); }); assert.sameValue(closed, true, 'iterator closed when windowSize is NaN'); closed = false; -assert.throws(RangeError, function () { +assert.throws(TypeError, function () { closable.windows('1'); }); assert.sameValue(closed, true, 'iterator closed when windowSize is a string'); diff --git a/test/built-ins/Iterator/prototype/windows/windowSize-no-coercion.js b/test/built-ins/Iterator/prototype/windows/windowSize-no-coercion.js index 6e2fda0ba31..d4468338708 100644 --- a/test/built-ins/Iterator/prototype/windows/windowSize-no-coercion.js +++ b/test/built-ins/Iterator/prototype/windows/windowSize-no-coercion.js @@ -9,16 +9,14 @@ description: > info: | Iterator.prototype.windows ( windowSize [ , undersized ] ) - 4. If windowSize is not an integral Number 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). + 4. If windowSize is not a Number, throw a TypeError exception. features: [iterator-chunking] ---*/ let iterator = (function* () {})(); let valueOfCalled = false; -assert.throws(RangeError, () => { +assert.throws(TypeError, () => { iterator.windows({ valueOf() { valueOfCalled = true; @@ -29,7 +27,7 @@ assert.throws(RangeError, () => { assert.sameValue(valueOfCalled, false, 'valueOf must not be called'); let toStringCalled = false; -assert.throws(RangeError, () => { +assert.throws(TypeError, () => { iterator.windows({ toString() { toStringCalled = true; 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 index da05e0dcc84..471bdceda63 100644 --- a/test/built-ins/Iterator/prototype/windows/windowSize-not-a-number.js +++ b/test/built-ins/Iterator/prototype/windows/windowSize-not-a-number.js @@ -3,46 +3,66 @@ /*--- esid: sec-iterator.prototype.windows description: > - Iterator.prototype.windows throws RangeError when windowSize is not a Number + Iterator.prototype.windows throws TypeError when windowSize is not an + integral Number info: | Iterator.prototype.windows ( windowSize [ , undersized ] ) - 4. If windowSize is not an integral Number 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). + 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(RangeError, () => { +assert.throws(TypeError, () => { iterator.windows(); }); -assert.throws(RangeError, () => { +assert.throws(TypeError, () => { iterator.windows(undefined); }); -assert.throws(RangeError, () => { +assert.throws(TypeError, () => { iterator.windows('1'); }); -assert.throws(RangeError, () => { +assert.throws(TypeError, () => { iterator.windows(true); }); -assert.throws(RangeError, () => { +assert.throws(TypeError, () => { iterator.windows(null); }); -assert.throws(RangeError, () => { +assert.throws(TypeError, () => { iterator.windows({}); }); -assert.throws(RangeError, () => { +assert.throws(TypeError, () => { iterator.windows(Symbol()); }); -assert.throws(RangeError, () => { +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 index a5321a64016..ad25c87fba5 100644 --- a/test/built-ins/Iterator/prototype/windows/windowSize-out-of-range.js +++ b/test/built-ins/Iterator/prototype/windows/windowSize-out-of-range.js @@ -3,12 +3,12 @@ /*--- esid: sec-iterator.prototype.windows description: > - Iterator.prototype.windows throws RangeError when windowSize is out of the - valid range [1, 2^32 - 1] + 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 ] ) - 4. If windowSize is not an integral Number in the inclusive interval from 1๐”ฝ to ๐”ฝ(2^32 - 1), then + 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). @@ -28,26 +28,6 @@ assert.throws(RangeError, () => { iterator.windows(-1); }); -assert.throws(RangeError, () => { - iterator.windows(-Infinity); -}); - -assert.throws(RangeError, () => { - iterator.windows(0.5); -}); - -assert.throws(RangeError, () => { - iterator.windows(1.5); -}); - -assert.throws(RangeError, () => { - iterator.windows(NaN); -}); - -assert.throws(RangeError, () => { - iterator.windows(Infinity); -}); - assert.throws(RangeError, () => { iterator.windows(2 ** 32); });