destructuring-iterator.js (3542B)
1 load(libdir + 'asserts.js'); 2 load(libdir + 'iteration.js'); 3 load(libdir + 'eqArrayHelper.js'); 4 5 // throw on non-iterables 6 assertThrowsInstanceOf(() => { [a, b, c] = {0: 0, 1: 1, 2: 2} }, TypeError); 7 8 var nextcalls = 0, donecalls = 0, valuecalls = 0; 9 var doneafter = 0; 10 var iterable = {}; 11 iterable[Symbol.iterator] = function () { 12 return { 13 next: function () { 14 assertEq(arguments.length, 0, 'iterator.next() should be called with no arguments'); 15 nextcalls++; 16 return { 17 get done() { 18 donecalls++; 19 return --doneafter < 0; 20 }, 21 get value() { 22 valuecalls++; 23 return valuecalls; 24 } 25 }; 26 } 27 } 28 }; 29 30 function assertIterable(expectCalls, fn, expectResult) { 31 [nextcalls, donecalls, valuecalls, doneafter] = [0,0,0, expectCalls[3]]; 32 assertEqArray(fn(iterable), expectResult); 33 assertEq(nextcalls, expectCalls[0], 'calls to iterator.next()'); 34 assertEq(donecalls, expectCalls[1], 'getting iterator.next().done'); 35 assertEq(valuecalls, expectCalls[2], 'getting iterator.next().value'); 36 } 37 38 assertIterable([1,1,1,1], 39 it => { var [a] = it; return [a]; }, 40 [1]); 41 assertIterable([2,2,1,1], 42 it => { var [a,b,c] = it; return [a,b,c]; }, 43 [1,undefined,undefined]); 44 assertIterable([2,2,1,1], 45 it => { var [a,b,...rest] = it; return [a,b,...rest]; }, 46 [1,undefined]); 47 assertIterable([5,5,4,4], 48 it => { var [,,...rest] = it; return rest; }, 49 [3,4]); 50 51 // the iterator should be exhausted before any error is thrown 52 assertIterable([5,5,4,4], 53 it => { 54 assertThrowsInstanceOf(function () { 55 "use strict"; 56 [...{0: "".x}] = it; 57 }, TypeError); 58 return []; 59 }, 60 []); 61 62 var arraycalls = 0; 63 var ArrayIterator = Array.prototype[Symbol.iterator]; 64 Array.prototype[Symbol.iterator] = function () { 65 arraycalls++; 66 return ArrayIterator.apply(this, arguments); 67 }; 68 // [...rest] should not call Array#@@iterator for the LHS 69 var [...rest] = iterable; 70 assertEq(arraycalls, 0, 'calls to Array#@@iterator'); 71 // [...[...rest]] should do so, since it creates an implicit array for the 72 // first rest pattern, then destructures that again using @@iterator() for the 73 // second rest pattern. 74 var [...[...rest]] = iterable; 75 assertEq(arraycalls, 1, 'calls to Array#@@iterator'); 76 77 // loop `fn` a few times, to get it JIT-compiled 78 function loop(fn) { 79 var i = 1e4; 80 while (i--) fn(); 81 } 82 83 loop(() => { doneafter = 4; var [a] = iterable; return a; }); 84 loop(() => { doneafter = 4; var [a,b,...[...rest]] = iterable; return rest; }); 85 86 87 // destructuring assignment should always use iterators and not optimize 88 // to a "group assignment" 89 delete Array.prototype[Symbol.iterator]; 90 assertThrowsInstanceOf(() => { var [a,b] = [1,2]; }, TypeError); 91 Array.prototype[Symbol.iterator] = ArrayIterator; 92 93 // observe the binding order 94 a = undefined, b = undefined, c = undefined; 95 var obj = {}; 96 obj[Symbol.iterator] = function* () { 97 // normal fields should be initialized right after |.next()| 98 yield 1; 99 assertEq(a, 1); 100 yield 2; 101 yield 3; 102 assertEq(b, 3); 103 yield 4; 104 yield 5; 105 // rest should be initialized after the iterator is exhausted 106 assertEq(c, undefined); 107 }; 108 [a, , b, ...c] = obj; 109 assertEqArray(c, [4,5]); 110 111 // a throw inside the destructuring of the "catch" value should not enter 112 // the "catch" block 113 114 assertThrowsValue(function () { 115 try { 116 Array.prototype[Symbol.iterator] = function () { throw 'from iterator'; }; 117 throw [1, 2]; 118 } catch ([x, y]) { 119 throw 'not reached'; 120 } 121 }, 'from iterator'); 122 Array.prototype[Symbol.iterator] = ArrayIterator;