Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions features.txt
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ globalThis
hashbang
import-attributes
import.meta
iterator-chunking
iterator-helpers
iterator-sequencing
Int8Array
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright (C) 2026 Michael Ficarra. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-iterator.prototype.chunks
description: >
Arguments and this value are validated in the correct order
info: |
Iterator.prototype.chunks ( chunkSize )

1. Let O be the this value.
2. If O is not an Object, throw a TypeError exception.
3. Let iterated be the Iterator Record { [[Iterator]]: O, [[NextMethod]]: undefined, [[Done]]: false }.
4. If chunkSize is not a Number, throw a TypeError ... IteratorClose.
5. If chunkSize is not an integral Number, throw a TypeError ... IteratorClose.
6. If chunkSize is not in the inclusive interval from 1𝔽 to 𝔽(2^32 - 1), then
a. Let error be ThrowCompletion(a newly created RangeError object).
b. Return ? IteratorClose(iterated, error).
7. Set iterated to ? GetIteratorDirect(O).

includes: [compareArray.js]
features: [iterator-chunking]
---*/
let effects = [];

// TypeError for non-object this before chunkSize is examined
assert.throws(TypeError, function () {
Iterator.prototype.chunks.call(null, 0);
});

// RangeError for invalid chunkSize before next is accessed
assert.throws(RangeError, function () {
Iterator.prototype.chunks.call(
{
get next() {
effects.push('get next');
return function () {
return { done: true, value: undefined };
};
},
},
0
);
});

assert.compareArray(effects, []);

// With valid args, next getter IS accessed (GetIteratorDirect runs)
Iterator.prototype.chunks.call(
{
get next() {
effects.push('get next');
return function () {
return { done: true, value: undefined };
};
},
},
1
);

assert.compareArray(effects, ['get next']);
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright (C) 2026 Michael Ficarra. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-iterator.prototype.chunks
description: >
Underlying iterator is closed when chunkSize validation fails
info: |
Iterator.prototype.chunks ( chunkSize )

3. Let iterated be the Iterator Record { [[Iterator]]: O, [[NextMethod]]: undefined, [[Done]]: false }.
4. If chunkSize is not a Number, throw a TypeError exception ... IteratorClose(iterated, error).
5. If chunkSize is not an integral Number, throw a TypeError exception ... IteratorClose(iterated, error).
6. If chunkSize is not in the inclusive interval from 1𝔽 to 𝔽(2^32 - 1), then
a. Let error be ThrowCompletion(a newly created RangeError object).
b. Return ? IteratorClose(iterated, error).

features: [iterator-chunking]
---*/

let closed = false;
let closable = {
__proto__: Iterator.prototype,
get next() {
throw new Test262Error('next should not be read');
},
return() {
closed = true;
return {};
},
};

assert.throws(TypeError, function () {
closable.chunks();
});
assert.sameValue(closed, true, 'iterator closed when chunkSize is undefined');

closed = false;
assert.throws(RangeError, function () {
closable.chunks(0);
});
assert.sameValue(closed, true, 'iterator closed when chunkSize is 0');

closed = false;
assert.throws(TypeError, function () {
closable.chunks(NaN);
});
assert.sameValue(closed, true, 'iterator closed when chunkSize is NaN');

closed = false;
assert.throws(TypeError, function () {
closable.chunks('1');
});
assert.sameValue(closed, true, 'iterator closed when chunkSize is a string');
13 changes: 13 additions & 0 deletions test/built-ins/Iterator/prototype/chunks/callable.js
Original file line number Diff line number Diff line change
@@ -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);
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (C) 2026 Michael Ficarra. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-iterator.prototype.chunks
description: >
Iterator.prototype.chunks does not coerce chunkSize using ToNumber; the
argument must already be a Number. Unlike take/drop, valueOf and toString
are never called.
info: |
Iterator.prototype.chunks ( chunkSize )

4. If chunkSize is not a Number, throw a TypeError exception.

features: [iterator-chunking]
---*/
let iterator = (function* () {})();

let valueOfCalled = false;
assert.throws(TypeError, () => {
iterator.chunks({
valueOf() {
valueOfCalled = true;
return 2;
},
});
});
assert.sameValue(valueOfCalled, false, 'valueOf must not be called');

let toStringCalled = false;
assert.throws(TypeError, () => {
iterator.chunks({
toString() {
toStringCalled = true;
return '2';
},
});
});
assert.sameValue(toStringCalled, false, 'toString must not be called');
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright (C) 2026 Michael Ficarra. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-iterator.prototype.chunks
description: >
Iterator.prototype.chunks throws TypeError when chunkSize is not an integral
Number
info: |
Iterator.prototype.chunks ( chunkSize )

4. If chunkSize is not a Number, throw a TypeError exception.
5. If chunkSize is not an integral Number, throw a TypeError exception.

features: [iterator-chunking]
---*/
let iterator = (function* () {})();

assert.throws(TypeError, () => {
iterator.chunks();
});

assert.throws(TypeError, () => {
iterator.chunks(undefined);
});

assert.throws(TypeError, () => {
iterator.chunks('1');
});

assert.throws(TypeError, () => {
iterator.chunks(true);
});

assert.throws(TypeError, () => {
iterator.chunks(null);
});

assert.throws(TypeError, () => {
iterator.chunks({});
});

assert.throws(TypeError, () => {
iterator.chunks(Symbol());
});

assert.throws(TypeError, () => {
iterator.chunks([2]);
});

assert.throws(TypeError, () => {
iterator.chunks(NaN);
});

assert.throws(TypeError, () => {
iterator.chunks(0.5);
});

assert.throws(TypeError, () => {
iterator.chunks(1.5);
});

assert.throws(TypeError, () => {
iterator.chunks(Infinity);
});

assert.throws(TypeError, () => {
iterator.chunks(-Infinity);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (C) 2026 Michael Ficarra. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-iterator.prototype.chunks
description: >
Iterator.prototype.chunks throws RangeError when chunkSize is an integral
Number outside the valid range [1, 2^32 - 1]
info: |
Iterator.prototype.chunks ( chunkSize )

6. If chunkSize is not in the inclusive interval from 1𝔽 to 𝔽(2^32 - 1), then
a. Let error be ThrowCompletion(a newly created RangeError object).
b. Return ? IteratorClose(iterated, error).

features: [iterator-chunking]
---*/
let iterator = (function* () {})();

assert.throws(RangeError, () => {
iterator.chunks(0);
});

assert.throws(RangeError, () => {
iterator.chunks(-0);
});

assert.throws(RangeError, () => {
iterator.chunks(-1);
});

assert.throws(RangeError, () => {
iterator.chunks(2 ** 32);
});

assert.throws(RangeError, () => {
iterator.chunks(2 ** 53);
});

// Boundary: valid values do not throw
iterator.chunks(1);
iterator.chunks(2 ** 32 - 1);
Original file line number Diff line number Diff line change
@@ -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]);
Original file line number Diff line number Diff line change
@@ -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]);
28 changes: 28 additions & 0 deletions test/built-ins/Iterator/prototype/chunks/chunks-size-1.js
Original file line number Diff line number Diff line change
@@ -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]);
Loading
Loading