// Tests that the "return" method on iterators is called in yield* // expressions. function test() { var returnCalled = 0; var returnCalledExpected = 0; var nextCalled = 0; var nextCalledExpected = 0; var throwCalled = 0; var throwCalledExpected = 0; var iterable = {}; iterable[Symbol.iterator] = makeIterator({ next: function() { nextCalled++; return { done: false }; }, ret: function() { returnCalled++; return { done: true, value: "iter.return" }; } }); function* y() { yield* iterable; } // G.p.throw on an iterator without "throw" calls IteratorClose. var g1 = y(); g1.next(); assertThrowsInstanceOf(function() { g1.throw("foo"); }, TypeError); assertEq(returnCalled, ++returnCalledExpected); assertEq(nextCalled, ++nextCalledExpected); g1.next(); assertEq(nextCalled, nextCalledExpected); // G.p.return calls "return", and if the result.done is true, return the // result. var g2 = y(); g2.next(); var v2 = g2.return("test return"); assertEq(v2.done, true); assertEq(v2.value, "iter.return"); assertEq(returnCalled, ++returnCalledExpected); assertEq(nextCalled, ++nextCalledExpected); g2.next(); assertEq(nextCalled, nextCalledExpected); // G.p.return calls "return", and if the result.done is false, continue // yielding. iterable[Symbol.iterator] = makeIterator({ next: function() { nextCalled++; return { done: false }; }, ret: function() { returnCalled++; return { done: false, value: "iter.return" }; } }); var g3 = y(); g3.next(); var v3 = g3.return("test return"); assertEq(v3.done, false); assertEq(v3.value, "iter.return"); assertEq(returnCalled, ++returnCalledExpected); assertEq(nextCalled, ++nextCalledExpected); g3.next(); assertEq(nextCalled, ++nextCalledExpected); // G.p.return throwing does not re-call iter.return. iterable[Symbol.iterator] = makeIterator({ ret: function() { returnCalled++; throw "in iter.return"; } }); var g4 = y(); g4.next(); assertThrowsValue(function() { g4.return("in test"); }, "in iter.return"); assertEq(returnCalled, ++returnCalledExpected); // G.p.return expects iter.return to return an Object. iterable[Symbol.iterator] = makeIterator({ ret: function() { returnCalled++; return 42; } }); var g5 = y(); g5.next(); assertThrowsInstanceOf(function() { g5.return("foo"); }, TypeError); assertEq(returnCalled, ++returnCalledExpected); // IteratorClose expects iter.return to return an Object. var g6 = y(); g6.next(); var exc; try { g6.throw("foo"); } catch (e) { exc = e; } finally { assertEq(exc instanceof TypeError, true); // The message test is here because instanceof TypeError doesn't // distinguish the non-Object return TypeError and the // throw-method-is-not-defined iterator protocol error. assertEq(exc.toString().indexOf("non-object") > 0, true); } assertEq(returnCalled, ++returnCalledExpected); // G.p.return passes its argument to "return". iterable[Symbol.iterator] = makeIterator({ ret: function(x) { assertEq(x, "in test"); returnCalled++; return { done: true }; } }); var g7 = y(); g7.next(); g7.return("in test"); assertEq(returnCalled, ++returnCalledExpected); // If a throw method is present, do not call "return". iterable[Symbol.iterator] = makeIterator({ throw: function(e) { throwCalled++; throw e; }, ret: function(x) { returnCalled++; return { done: true }; } }); var g8 = y(); g8.next(); assertThrowsValue(function() { g8.throw("foo"); }, "foo"); assertEq(throwCalled, ++throwCalledExpected); assertEq(returnCalled, returnCalledExpected); } test(); if (typeof reportCompare === "function") reportCompare(0, 0);