diff options
Diffstat (limited to 'js/src/jit-test/tests/basic/destructuring-iterator.js')
-rw-r--r-- | js/src/jit-test/tests/basic/destructuring-iterator.js | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/basic/destructuring-iterator.js b/js/src/jit-test/tests/basic/destructuring-iterator.js new file mode 100644 index 000000000..ca6593024 --- /dev/null +++ b/js/src/jit-test/tests/basic/destructuring-iterator.js @@ -0,0 +1,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; + |