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