iterables-iteration-get-iterator-flattenable-abrupt-completion.js (4643B)
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 Handle abrupt completions during iterables iteration. 8 info: | 9 Iterator.zip ( iterables [ , options ] ) 10 ... 11 12. Repeat, while next is not done, 12 ... 13 c. If next is not done, then 14 i. Let iter be Completion(GetIteratorFlattenable(next, reject-strings)). 15 ii. IfAbruptCloseIterators(iter, the list-concatenation of « inputIter » and iters). 16 ... 17 18 GetIteratorFlattenable ( obj, primitiveHandling ) 19 1. If obj is not an Object, then 20 a. If primitiveHandling is reject-primitives, throw a TypeError exception. 21 b. Assert: primitiveHandling is iterate-string-primitives. 22 c. If obj is not a String, throw a TypeError exception. 23 2. Let method be ? GetMethod(obj, %Symbol.iterator%). 24 3. If method is undefined, then 25 a. Let iterator be obj. 26 4. Else, 27 a. Let iterator be ? Call(method, obj). 28 5. If iterator is not an Object, throw a TypeError exception. 29 6. Return ? GetIteratorDirect(iterator). 30 31 IteratorCloseAll ( iters, completion ) 32 1. For each element iter of iters, in reverse List order, do 33 a. Set completion to Completion(IteratorClose(iter, completion)). 34 2. Return ? completion. 35 36 IteratorClose ( iteratorRecord, completion ) 37 1. Assert: iteratorRecord.[[Iterator]] is an Object. 38 2. Let iterator be iteratorRecord.[[Iterator]]. 39 3. Let innerResult be Completion(GetMethod(iterator, "return")). 40 4. If innerResult is a normal completion, then 41 a. Let return be innerResult.[[Value]]. 42 b. If return is undefined, return ? completion. 43 c. Set innerResult to Completion(Call(return, iterator)). 44 5. If completion is a throw completion, return ? completion. 45 ... 46 includes: [compareArray.js] 47 features: [joint-iteration] 48 ---*/ 49 50 class ExpectedError extends Error {} 51 52 var badIterators = [ 53 // Throw TypeError in GetIteratorFlattenable because strings are rejected. 54 { 55 iterator: "bad iterator", 56 error: TypeError 57 }, 58 59 // Throw an error when GetIteratorFlattenable performs GetMethod. 60 { 61 iterator: { 62 get [Symbol.iterator]() { 63 throw new ExpectedError(); 64 } 65 }, 66 error: ExpectedError, 67 }, 68 69 // Throw an error when GetIteratorFlattenable performs Call. 70 { 71 iterator: { 72 [Symbol.iterator]() { 73 throw new ExpectedError(); 74 } 75 }, 76 error: ExpectedError, 77 }, 78 79 // Throw an error when GetIteratorFlattenable performs GetIteratorDirect. 80 { 81 iterator: { 82 get next() { 83 throw new ExpectedError(); 84 } 85 }, 86 error: ExpectedError, 87 }, 88 ]; 89 90 function makeIterables(badIterator) { 91 var log = []; 92 93 var first = { 94 next() { 95 log.push("unexpected call to next method"); 96 }, 97 return() { 98 // Called with the correct receiver and no arguments. 99 assert.sameValue(this, first); 100 assert.sameValue(arguments.length, 0); 101 102 // NB: Log after above asserts, because failures aren't propagated. 103 log.push("close first iterator"); 104 105 // IteratorClose ignores new exceptions when called with a Throw completion. 106 throw new Test262Error(); 107 }, 108 }; 109 110 var second = { 111 next() { 112 log.push("unexpected call to next method"); 113 }, 114 return() { 115 // Called with the correct receiver and no arguments. 116 assert.sameValue(this, second); 117 assert.sameValue(arguments.length, 0); 118 119 // NB: Log after above asserts, because failures aren't propagated. 120 log.push("close second iterator"); 121 122 // IteratorClose ignores new exceptions when called with a Throw completion. 123 throw new Test262Error(); 124 }, 125 }; 126 127 var elements = [first, second, badIterator]; 128 var elementsIter = elements.values(); 129 130 var iterables = { 131 [Symbol.iterator]() { 132 return this; 133 }, 134 next() { 135 log.push("call next"); 136 return elementsIter.next(); 137 }, 138 return() { 139 log.push("close iterables iterator"); 140 141 // IteratorClose ignores new exceptions when called with a Throw completion. 142 throw new Test262Error(); 143 }, 144 }; 145 146 return {log, iterables}; 147 } 148 149 for (var {iterator, error} of badIterators) { 150 var {log, iterables} = makeIterables(iterator); 151 152 assert.throws(error, function() { 153 Iterator.zip(iterables); 154 }); 155 156 // Ensure iterators are closed in the correct order. 157 assert.compareArray(log, [ 158 "call next", 159 "call next", 160 "call next", 161 "close second iterator", 162 "close first iterator", 163 "close iterables iterator", 164 ]); 165 } 166 167 reportCompare(0, 0);