test262/test/built-ins/Iterator/zip/iterables-iteration.js
2025-11-25 13:06:50 -08:00

165 lines
4.4 KiB
JavaScript

// Copyright (C) 2025 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-iterator.zip
description: >
Perform iteration of the "iterables" argument.
info: |
Iterator.zip ( iterables [ , options ] )
...
10. Let inputIter be ? GetIterator(iterables, sync).
11. Let next be not-started.
12. Repeat, while next is not done,
a. Set next to Completion(IteratorStepValue(inputIter)).
b. IfAbruptCloseIterators(next, iters).
c. If next is not done, then
i. Let iter be Completion(GetIteratorFlattenable(next, reject-strings)).
ii. IfAbruptCloseIterators(iter, the list-concatenation of « inputIter » and iters).
iii. Append iter to iters.
...
GetIterator ( obj, kind )
...
2. Else,
a. Let method be ? GetMethod(obj, %Symbol.iterator%).
3. If method is undefined, throw a TypeError exception.
4. Return ? GetIteratorFromMethod(obj, method).
GetIteratorFromMethod ( obj, method )
1. Let iterator be ? Call(method, obj).
2. If iterator is not an Object, throw a TypeError exception.
3. Return ? GetIteratorDirect(iterator).
GetIteratorDirect ( obj )
1. Let nextMethod be ? Get(obj, "next").
2. Let iteratorRecord be the Iterator Record { [[Iterator]]: obj, [[NextMethod]]: nextMethod, [[Done]]: false }.
3. Return iteratorRecord.
GetIteratorFlattenable ( obj, primitiveHandling )
1. If obj is not an Object, then
a. If primitiveHandling is reject-primitives, throw a TypeError exception.
b. Assert: primitiveHandling is iterate-string-primitives.
c. If obj is not a String, throw a TypeError exception.
2. Let method be ? GetMethod(obj, %Symbol.iterator%).
3. If method is undefined, then
a. Let iterator be obj.
4. Else,
a. Let iterator be ? Call(method, obj).
5. If iterator is not an Object, throw a TypeError exception.
6. Return ? GetIteratorDirect(iterator).
includes: [proxyTrapsHelper.js, compareArray.js]
features: [joint-iteration]
---*/
// Object implementing Iterator protocol, but throws when calling any Iterator methods.
var throwingIterator = {
next() {
throw new Test262Error();
},
return() {
throw new Test262Error();
}
};
var iterableReturningThrowingIterator = {
[Symbol.iterator]() {
return throwingIterator;
}
};
// "iterables" argument must be an iterable.
assert.throws(TypeError, function() {
Iterator.zip(Object.create(null));
});
// GetIteratorFlattenable accepts both iterables and iterators.
Iterator.zip([
throwingIterator,
iterableReturningThrowingIterator,
]);
// GetIteratorFlattenable rejects non-objects.
var badIterators = [
undefined,
null,
true,
"",
Symbol(),
0,
0n,
];
for (var iterator of badIterators) {
assert.throws(TypeError, function() {
Iterator.zip([iterator]);
});
}
// GetIterator and GetIteratorFlattenable read properties in the correct order.
var log = [];
function makeProxyWithGetHandler(name, obj) {
return new Proxy(obj, allowProxyTraps({
get(target, propertyKey, receiver) {
log.push(`${name}::${String(propertyKey)}`);
return Reflect.get(target, propertyKey, receiver);
}
}));
}
var elements = [
// An iterator.
makeProxyWithGetHandler("first", throwingIterator),
// An iterable.
makeProxyWithGetHandler("second", iterableReturningThrowingIterator),
// An object without any iteration methods.
makeProxyWithGetHandler("third", Object.create(null)),
];
var elementsIter = elements.values();
var iterables = makeProxyWithGetHandler("iterables", {
[Symbol.iterator]() {
// Called with the correct receiver and no arguments.
assert.sameValue(this, iterables);
assert.sameValue(arguments.length, 0);
return this;
},
next() {
log.push("call next");
// Called with the correct receiver and no arguments.
assert.sameValue(this, iterables);
assert.sameValue(arguments.length, 0);
return elementsIter.next();
},
return() {
throw new Test262Error("unexpected call to return method");
}
});
Iterator.zip(iterables);
assert.compareArray(log, [
"iterables::Symbol(Symbol.iterator)",
"iterables::next",
"call next",
"first::Symbol(Symbol.iterator)",
"first::next",
"call next",
"second::Symbol(Symbol.iterator)",
"call next",
"third::Symbol(Symbol.iterator)",
"third::next",
"call next",
]);