iterables-iteration.js (4483B)
1 // Copyright (C) 2025 André Bargull. All rights reserved. 2 // This code is governed by the BSD license found in the LICENSE file. 3 4 /*--- 5 esid: sec-iterator.zip 6 description: > 7 Perform iteration of the "iterables" argument. 8 info: | 9 Iterator.zip ( iterables [ , options ] ) 10 ... 11 10. Let inputIter be ? GetIterator(iterables, sync). 12 11. Let next be not-started. 13 12. Repeat, while next is not done, 14 a. Set next to Completion(IteratorStepValue(inputIter)). 15 b. IfAbruptCloseIterators(next, iters). 16 c. If next is not done, then 17 i. Let iter be Completion(GetIteratorFlattenable(next, reject-strings)). 18 ii. IfAbruptCloseIterators(iter, the list-concatenation of « inputIter » and iters). 19 iii. Append iter to iters. 20 ... 21 22 GetIterator ( obj, kind ) 23 ... 24 2. Else, 25 a. Let method be ? GetMethod(obj, %Symbol.iterator%). 26 3. If method is undefined, throw a TypeError exception. 27 4. Return ? GetIteratorFromMethod(obj, method). 28 29 GetIteratorFromMethod ( obj, method ) 30 1. Let iterator be ? Call(method, obj). 31 2. If iterator is not an Object, throw a TypeError exception. 32 3. Return ? GetIteratorDirect(iterator). 33 34 GetIteratorDirect ( obj ) 35 1. Let nextMethod be ? Get(obj, "next"). 36 2. Let iteratorRecord be the Iterator Record { [[Iterator]]: obj, [[NextMethod]]: nextMethod, [[Done]]: false }. 37 3. Return iteratorRecord. 38 39 GetIteratorFlattenable ( obj, primitiveHandling ) 40 1. If obj is not an Object, then 41 a. If primitiveHandling is reject-primitives, throw a TypeError exception. 42 b. Assert: primitiveHandling is iterate-string-primitives. 43 c. If obj is not a String, throw a TypeError exception. 44 2. Let method be ? GetMethod(obj, %Symbol.iterator%). 45 3. If method is undefined, then 46 a. Let iterator be obj. 47 4. Else, 48 a. Let iterator be ? Call(method, obj). 49 5. If iterator is not an Object, throw a TypeError exception. 50 6. Return ? GetIteratorDirect(iterator). 51 includes: [proxyTrapsHelper.js, compareArray.js] 52 features: [joint-iteration] 53 ---*/ 54 55 // Object implementing Iterator protocol, but throws when calling any Iterator methods. 56 var throwingIterator = { 57 next() { 58 throw new Test262Error(); 59 }, 60 return() { 61 throw new Test262Error(); 62 } 63 }; 64 65 var iterableReturningThrowingIterator = { 66 [Symbol.iterator]() { 67 return throwingIterator; 68 } 69 }; 70 71 // "iterables" argument must be an iterable. 72 assert.throws(TypeError, function() { 73 Iterator.zip(Object.create(null)); 74 }); 75 76 // GetIteratorFlattenable accepts both iterables and iterators. 77 Iterator.zip([ 78 throwingIterator, 79 iterableReturningThrowingIterator, 80 ]); 81 82 // GetIteratorFlattenable rejects non-objects. 83 var badIterators = [ 84 undefined, 85 null, 86 true, 87 "", 88 Symbol(), 89 0, 90 0n, 91 ]; 92 93 for (var iterator of badIterators) { 94 assert.throws(TypeError, function() { 95 Iterator.zip([iterator]); 96 }); 97 } 98 99 // GetIterator and GetIteratorFlattenable read properties in the correct order. 100 var log = []; 101 102 function makeProxyWithGetHandler(name, obj) { 103 return new Proxy(obj, allowProxyTraps({ 104 get(target, propertyKey, receiver) { 105 log.push(`${name}::${String(propertyKey)}`); 106 return Reflect.get(target, propertyKey, receiver); 107 } 108 })); 109 } 110 111 var elements = [ 112 // An iterator. 113 makeProxyWithGetHandler("first", throwingIterator), 114 115 // An iterable. 116 makeProxyWithGetHandler("second", iterableReturningThrowingIterator), 117 118 // An object without any iteration methods. 119 makeProxyWithGetHandler("third", Object.create(null)), 120 ]; 121 122 var elementsIter = elements.values(); 123 124 var iterables = makeProxyWithGetHandler("iterables", { 125 [Symbol.iterator]() { 126 // Called with the correct receiver and no arguments. 127 assert.sameValue(this, iterables); 128 assert.sameValue(arguments.length, 0); 129 130 return this; 131 }, 132 next() { 133 log.push("call next"); 134 135 // Called with the correct receiver and no arguments. 136 assert.sameValue(this, iterables); 137 assert.sameValue(arguments.length, 0); 138 139 return elementsIter.next(); 140 }, 141 return() { 142 throw new Test262Error("unexpected call to return method"); 143 } 144 }); 145 146 Iterator.zip(iterables); 147 148 assert.compareArray(log, [ 149 "iterables::Symbol(Symbol.iterator)", 150 "iterables::next", 151 152 "call next", 153 "first::Symbol(Symbol.iterator)", 154 "first::next", 155 156 "call next", 157 "second::Symbol(Symbol.iterator)", 158 159 "call next", 160 "third::Symbol(Symbol.iterator)", 161 "third::next", 162 163 "call next", 164 ]); 165 166 reportCompare(0, 0);