iterables-iteration-iterator-step-value-abrupt-completion.js (3244B)
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 a. Set next to Completion(IteratorStepValue(inputIter)). 13 b. IfAbruptCloseIterators(next, iters). 14 ... 15 16 IfAbruptCloseIterators ( value, iteratorRecords ) 17 1. Assert: value is a Completion Record. 18 2. If value is an abrupt completion, return ? IteratorCloseAll(iteratorRecords, value). 19 3. Else, set value to value.[[Value]]. 20 21 IteratorCloseAll ( iters, completion ) 22 1. For each element iter of iters, in reverse List order, do 23 a. Set completion to Completion(IteratorClose(iter, completion)). 24 2. Return ? completion. 25 26 IteratorClose ( iteratorRecord, completion ) 27 1. Assert: iteratorRecord.[[Iterator]] is an Object. 28 2. Let iterator be iteratorRecord.[[Iterator]]. 29 3. Let innerResult be Completion(GetMethod(iterator, "return")). 30 4. If innerResult is a normal completion, then 31 a. Let return be innerResult.[[Value]]. 32 b. If return is undefined, return ? completion. 33 c. Set innerResult to Completion(Call(return, iterator)). 34 5. If completion is a throw completion, return ? completion. 35 ... 36 includes: [compareArray.js] 37 features: [joint-iteration] 38 ---*/ 39 40 class ExpectedError extends Error {} 41 42 var log = []; 43 44 var first = { 45 next() { 46 log.push("unexpected call to next method"); 47 }, 48 return() { 49 // Called with the correct receiver and no arguments. 50 assert.sameValue(this, first); 51 assert.sameValue(arguments.length, 0); 52 53 // NB: Log after above asserts, because failures aren't propagated. 54 log.push("close first iterator"); 55 56 // IteratorClose ignores new exceptions when called with a Throw completion. 57 throw new Test262Error(); 58 }, 59 }; 60 61 var second = { 62 next() { 63 log.push("unexpected call to next method"); 64 }, 65 return() { 66 // Called with the correct receiver and no arguments. 67 assert.sameValue(this, second); 68 assert.sameValue(arguments.length, 0); 69 70 // NB: Log after above asserts, because failures aren't propagated. 71 log.push("close second iterator"); 72 73 // IteratorClose ignores new exceptions when called with a Throw completion. 74 throw new Test262Error(); 75 }, 76 }; 77 78 var elements = [first, second]; 79 var elementsIter = elements.values(); 80 81 var iterables = { 82 [Symbol.iterator]() { 83 return this; 84 }, 85 next() { 86 log.push("call next"); 87 var result = elementsIter.next(); 88 if (result.done) { 89 throw new ExpectedError(); 90 } 91 return result; 92 }, 93 return() { 94 // This method shouldn't be called. 95 log.push("UNEXPECTED - close iterables iterator"); 96 97 // IteratorClose ignores new exceptions when called with a Throw completion. 98 throw new Test262Error(); 99 }, 100 }; 101 102 assert.throws(ExpectedError, function() { 103 Iterator.zip(iterables); 104 }); 105 106 // Ensure iterators are closed in the correct order. 107 assert.compareArray(log, [ 108 "call next", 109 "call next", 110 "call next", 111 "close second iterator", 112 "close first iterator", 113 ]); 114 115 reportCompare(0, 0);