padding-iteration.js (3946B)
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 "padding" option. 8 info: | 9 Iterator.zip ( iterables [ , options ] ) 10 ... 11 14. If mode is "longest", then 12 ... 13 b. Else, 14 i. Let paddingIter be Completion(GetIterator(paddingOption, sync)). 15 ... 16 iii. Let usingIterator be true. 17 iv. Perform the following steps iterCount times: 18 1. If usingIterator is true, then 19 a. Set next to Completion(IteratorStepValue(paddingIter)). 20 ... 21 c. If next is done, then 22 i. Set usingIterator to false. 23 d. Else, 24 i. Append next to padding. 25 2. If usingIterator is false, append undefined to padding. 26 v. If usingIterator is true, then 27 1. Let completion be Completion(IteratorClose(paddingIter, NormalCompletion(unused))). 28 ... 29 ... 30 31 GetIterator ( obj, kind ) 32 ... 33 2. Else, 34 a. Let method be ? GetMethod(obj, %Symbol.iterator%). 35 3. If method is undefined, throw a TypeError exception. 36 4. Return ? GetIteratorFromMethod(obj, method). 37 38 GetIteratorFromMethod ( obj, method ) 39 1. Let iterator be ? Call(method, obj). 40 2. If iterator is not an Object, throw a TypeError exception. 41 3. Return ? GetIteratorDirect(iterator). 42 43 GetIteratorDirect ( obj ) 44 1. Let nextMethod be ? Get(obj, "next"). 45 2. Let iteratorRecord be the Iterator Record { [[Iterator]]: obj, [[NextMethod]]: nextMethod, [[Done]]: false }. 46 3. Return iteratorRecord. 47 includes: [proxyTrapsHelper.js, compareArray.js] 48 features: [joint-iteration] 49 ---*/ 50 51 function makeProxyWithGetHandler(log, name, obj) { 52 return new Proxy(obj, allowProxyTraps({ 53 get(target, propertyKey, receiver) { 54 log.push(`${name}::${String(propertyKey)}`); 55 return Reflect.get(target, propertyKey, receiver); 56 } 57 })); 58 } 59 60 // "padding" option must be an iterable. 61 assert.throws(TypeError, function() { 62 Iterator.zip([], { 63 mode: "longest", 64 padding: {}, 65 }); 66 }); 67 68 for (var n = 0; n <= 5; ++n) { 69 var iterables = Array(n).fill([]); 70 71 for (var k = 0; k <= n + 2; ++k) { 72 var elements = Array(k).fill(0); 73 var elementsIter = elements.values(); 74 75 var log = []; 76 77 var padding = makeProxyWithGetHandler(log, "padding", { 78 [Symbol.iterator]() { 79 log.push("call iterator"); 80 81 // Called with the correct receiver and no arguments. 82 assert.sameValue(this, padding); 83 assert.sameValue(arguments.length, 0); 84 85 return this; 86 }, 87 next() { 88 log.push("call next"); 89 90 // Called with the correct receiver and no arguments. 91 assert.sameValue(this, padding); 92 assert.sameValue(arguments.length, 0); 93 94 return elementsIter.next(); 95 }, 96 return() { 97 log.push("call return"); 98 99 // Called with the correct receiver and no arguments. 100 assert.sameValue(this, padding); 101 assert.sameValue(arguments.length, 0); 102 103 return {}; 104 } 105 }); 106 107 Iterator.zip(iterables, {mode: "longest", padding}); 108 109 // Property reads and calls from GetIterator. 110 var expected = [ 111 "padding::Symbol(Symbol.iterator)", 112 "call iterator", 113 "padding::next", 114 ]; 115 116 // Call the "next" method |n| times until the padding iterator is exhausted. 117 for (var i = 0; i < Math.min(n, k); ++i) { 118 expected.push("call next"); 119 } 120 121 // If |n| is larger than |k|, then there was one final call to the "next" 122 // method. Otherwise the "return" method was called to close the padding 123 // iterator. 124 if (n > k) { 125 expected.push("call next"); 126 } else { 127 expected.push( 128 "padding::return", 129 "call return" 130 ); 131 } 132 133 assert.compareArray(log, expected); 134 } 135 } 136 137 reportCompare(0, 0);