// Copyright 2016 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. function* g() { yield 42; return 88 }; // Return method is "undefined". { g.prototype.return = null; assertEquals(undefined, (() => { for (var x of g()) { break; } })()); assertEquals(undefined, (() => { for (let x of g()) { break; } })()); assertEquals(undefined, (() => { for (const x of g()) { break; } })()); assertEquals(undefined, (() => { for (x of g()) { break; } })()); assertThrowsEquals(() => { for (var x of g()) { throw 42; } }, 42); assertThrowsEquals(() => { for (let x of g()) { throw 42; } }, 42); assertThrowsEquals(() => { for (const x of g()) { throw 42; } }, 42); assertThrowsEquals(() => { for (x of g()) { throw 42; } }, 42); assertEquals(42, (() => { for (var x of g()) { return 42; } })()); assertEquals(42, (() => { for (let x of g()) { return 42; } })()); assertEquals(42, (() => { for (const x of g()) { return 42; } })()); assertEquals(42, (() => { for (x of g()) { return 42; } })()); assertEquals(42, eval('for (var x of g()) { x; }')); assertEquals(42, eval('for (let x of g()) { x; }')); assertEquals(42, eval('for (const x of g()) { x; }')); assertEquals(42, eval('for (x of g()) { x; }')); assertEquals(42, (() => { var [x] = g(); return x; })()); assertEquals(42, (() => { let [x] = g(); return x; })()); assertEquals(42, (() => { const [x] = g(); return x; })()); assertEquals(42, (() => { [x] = g(); return x; })()); assertEquals(42, (([x]) => x)(g()) ); } // Return method is not callable. { g.prototype.return = 666; assertThrows(() => { for (var x of g()) { break; } }, TypeError); assertThrows(() => { for (let x of g()) { break; } }, TypeError); assertThrows(() => { for (const x of g()) { break; } }, TypeError); assertThrows(() => { for (x of g()) { break; } }, TypeError); assertThrows(() => { for (var x of g()) { throw 666; } }, TypeError); assertThrows(() => { for (let x of g()) { throw 666; } }, TypeError); assertThrows(() => { for (const x of g()) { throw 666; } }, TypeError); assertThrows(() => { for (x of g()) { throw 666; } }, TypeError); assertThrows(() => { for (var x of g()) { return 666; } }, TypeError); assertThrows(() => { for (let x of g()) { return 666; } }, TypeError); assertThrows(() => { for (const x of g()) { return 666; } }, TypeError); assertThrows(() => { for (x of g()) { return 666; } }, TypeError); assertEquals(42, eval('for (var x of g()) { x; }')); assertEquals(42, eval('for (let x of g()) { x; }')); assertEquals(42, eval('for (const x of g()) { x; }')); assertEquals(42, eval('for (x of g()) { x; }')); assertThrows(() => { var [x] = g(); return x; }, TypeError); assertThrows(() => { let [x] = g(); return x; }, TypeError); assertThrows(() => { const [x] = g(); return x; }, TypeError); assertThrows(() => { [x] = g(); return x; }, TypeError); assertThrows(() => { (([x]) => x)(g()); }, TypeError); } // Return method does not return an object. { g.prototype.return = () => 666; assertThrows(() => { for (var x of g()) { break; } }, TypeError); assertThrows(() => { for (let x of g()) { break; } }, TypeError); assertThrows(() => { for (const x of g()) { break; } }, TypeError); assertThrows(() => { for (x of g()) { break; } }, TypeError); // Throw from the body of a for loop 'wins' vs throw // originating from a bad 'return' value. assertThrowsEquals(() => { for (var x of g()) { throw 666; } }, 666); assertThrowsEquals(() => { for (let x of g()) { throw 666; } }, 666); assertThrowsEquals(() => { for (const x of g()) { throw 666; } }, 666); assertThrowsEquals(() => { for (x of g()) { throw 666; } }, 666); assertThrows(() => { for (var x of g()) { return 666; } }, TypeError); assertThrows(() => { for (let x of g()) { return 666; } }, TypeError); assertThrows(() => { for (const x of g()) { return 666; } }, TypeError); assertThrows(() => { for (x of g()) { return 666; } }, TypeError); assertEquals(42, eval('for (var x of g()) { x; }')); assertEquals(42, eval('for (let x of g()) { x; }')); assertEquals(42, eval('for (const x of g()) { x; }')); assertEquals(42, eval('for (x of g()) { x; }')); assertThrows(() => { var [x] = g(); return x; }, TypeError); assertThrows(() => { let [x] = g(); return x; }, TypeError); assertThrows(() => { const [x] = g(); return x; }, TypeError); assertThrows(() => { [x] = g(); return x; }, TypeError); assertThrows(() => { (([x]) => x)(g()); }, TypeError); } // Return method returns an object. { let log = []; g.prototype.return = (...args) => { log.push(args); return {} }; log = []; for (var x of g()) { break; } assertEquals([[]], log); log = []; for (let x of g()) { break; } assertEquals([[]], log); log = []; for (const x of g()) { break; } assertEquals([[]], log); log = []; for (x of g()) { break; } assertEquals([[]], log); log = []; assertThrowsEquals(() => { for (var x of g()) { throw 42; } }, 42); assertEquals([[]], log); log = []; assertThrowsEquals(() => { for (let x of g()) { throw 42; } }, 42); assertEquals([[]], log); log = []; assertThrowsEquals(() => { for (const x of g()) { throw 42; } }, 42); assertEquals([[]], log); log = []; assertThrowsEquals(() => { for (x of g()) { throw 42; } }, 42); assertEquals([[]], log); log = []; assertEquals(42, (() => { for (var x of g()) { return 42; } })()); assertEquals([[]], log); log = []; assertEquals(42, (() => { for (let x of g()) { return 42; } })()); assertEquals([[]], log); log = []; assertEquals(42, (() => { for (const x of g()) { return 42; } })()); assertEquals([[]], log); log = []; assertEquals(42, (() => { for (x of g()) { return 42; } })()); assertEquals([[]], log); log = []; assertEquals(42, eval('for (var x of g()) { x; }')); assertEquals([], log); log = []; assertEquals(42, eval('for (let x of g()) { x; }')); assertEquals([], log); log = []; assertEquals(42, eval('for (const x of g()) { x; }')); assertEquals([], log); log = []; assertEquals(42, eval('for (x of g()) { x; }')); assertEquals([], log); // Even if doing the assignment throws, still call return log = []; x = { set attr(_) { throw 1234; } }; assertThrowsEquals(() => { for (x.attr of g()) { throw 456; } }, 1234); assertEquals([[]], log); log = []; assertEquals(42, (() => { var [x] = g(); return x; })()); assertEquals([[]], log); log = []; assertEquals(42, (() => { let [x] = g(); return x; })()); assertEquals([[]], log); log = []; assertEquals(42, (() => { const [x] = g(); return x; })()); assertEquals([[]], log); log = []; assertEquals(42, (() => { [x] = g(); return x; })()); assertEquals([[]], log); log = [] assertEquals(42, (([x]) => x)(g()) ); assertEquals([[]], log); log = []; assertEquals(42, (() => { var [x,] = g(); return x; })()); assertEquals([[]], log); log = []; assertEquals(42, (() => { let [x,] = g(); return x; })()); assertEquals([[]], log); log = []; assertEquals(42, (() => { const [x,] = g(); return x; })()); assertEquals([[]], log); log = []; assertEquals(42, (() => { [x,] = g(); return x; })()); assertEquals([[]], log); log = [] assertEquals(42, (([x,]) => x)(g()) ); assertEquals([[]], log); log = []; assertEquals(42, (() => { var [x,,] = g(); return x; })()); assertEquals([], log); log = []; assertEquals(42, (() => { let [x,,] = g(); return x; })()); assertEquals([], log); log = []; assertEquals(42, (() => { const [x,,] = g(); return x; })()); assertEquals([], log); log = []; assertEquals(42, (() => { [x,,] = g(); return x; })()); assertEquals([], log); log = [] assertEquals(42, (([x,,]) => x)(g()) ); assertEquals([], log); log = []; assertEquals([42, undefined], (() => { var [x, y] = g(); return [x, y]; })()); assertEquals([], log); log = []; assertEquals([42, undefined], (() => { let [x, y] = g(); return [x, y]; })()); assertEquals([], log); log = []; assertEquals([42, undefined], (() => { const [x, y] = g(); return [x, y]; })()); assertEquals([], log); log = []; assertEquals([42, undefined], (() => { [x, y] = g(); return [x, y]; })()); assertEquals([], log); log = [] assertEquals([42, undefined], (([x, y]) => [x, y])(g()) ); assertEquals([], log); log = []; assertEquals([42], (() => { var [...x] = g(); return x; })()); assertEquals([], log); log = []; assertEquals([42], (() => { let [...x] = g(); return x; })()); assertEquals([], log); log = []; assertEquals([42], (() => { const [...x] = g(); return x; })()); assertEquals([], log); log = []; assertEquals([42], (() => { [...x] = g(); return x; })()); assertEquals([], log); log = [] assertEquals([42], (([...x]) => x)(g()) ); assertEquals([], log); log = []; assertEquals([42, []], (() => { var [x, ...y] = g(); return [x, y]; })()); assertEquals([], log); log = []; assertEquals([42, []], (() => { let [x, ...y] = g(); return [x, y]; })()); assertEquals([], log); log = []; assertEquals([42, []], (() => { const [x, ...y] = g(); return [x, y]; })()); assertEquals([], log); log = []; assertEquals([42, []], (() => { [x, ...y] = g(); return [x, y]; })()); assertEquals([], log); log = [] assertEquals([42, []], (([x, ...y]) => [x, y])(g()) ); assertEquals([], log); log = []; assertEquals([], (() => { var [] = g(); return []; })()); assertEquals([[]], log); log = []; assertEquals([], (() => { let [] = g(); return []; })()); assertEquals([[]], log); log = []; assertEquals([], (() => { const [] = g(); return []; })()); assertEquals([[]], log); log = []; assertEquals([], (() => { [] = g(); return []; })()); assertEquals([[]], log); log = [] assertEquals([], (([]) => [])(g()) ); assertEquals([[]], log); log = []; assertEquals([], (() => { var [...[]] = g(); return []; })()); assertEquals([], log); log = []; assertEquals([], (() => { let [...[]] = g(); return []; })()); assertEquals([], log); log = []; assertEquals([], (() => { const [...[]] = g(); return []; })()); assertEquals([], log); log = []; assertEquals([], (() => { [...[]] = g(); return []; })()); assertEquals([], log); log = [] assertEquals([], (([...[]]) => [])(g()) ); assertEquals([], log); log = []; assertEquals([42], (() => { var [...[x]] = g(); return [x]; })()); assertEquals([], log); log = []; assertEquals([42], (() => { let [...[x]] = g(); return [x]; })()); assertEquals([], log); log = []; assertEquals([42], (() => { const [...[x]] = g(); return [x]; })()); assertEquals([], log); log = []; assertEquals([42], (() => { [...[x]] = g(); return [x]; })()); assertEquals([], log); log = [] assertEquals([42], (([...[x]]) => [x])(g()) ); assertEquals([], log); log = []; assertEquals([42, undefined], (() => { var [...[x, y]] = g(); return [x, y]; })()); assertEquals([], log); log = []; assertEquals([42, undefined], (() => { let [...[x, y]] = g(); return [x, y]; })()); assertEquals([], log); log = []; assertEquals([42, undefined], (() => { const [...[x, y]] = g(); return [x, y]; })()); assertEquals([], log); log = []; assertEquals([42, undefined], (() => { [...[x, y]] = g(); return [x, y]; })()); assertEquals([], log); log = [] assertEquals([42, undefined], (([...[x, y]]) => [x, y])(g()) ); assertEquals([], log); log = [] assertThrowsEquals(() => { let x = { set foo(_) { throw 666; } }; [x.foo] = g(); }, 666); assertEquals([[]], log); log = [] assertThrows(() => { var [[]] = g(); }, TypeError); assertEquals([[]], log); log = [] assertThrows(() => { let [[]] = g(); }, TypeError); assertEquals([[]], log); log = [] assertThrows(() => { const [[]] = g(); }, TypeError); assertEquals([[]], log); log = [] assertThrows(() => { [[]] = g(); }, TypeError); assertEquals([[]], log); log = [] assertThrows(() => { (([[]]) => 0)(g()); }, TypeError); assertEquals([[]], log); log = [] assertThrows(() => { var [...[[]]] = g(); }, TypeError); assertEquals([], log); log = [] assertThrows(() => { let [...[[]]] = g(); }, TypeError); assertEquals([], log); log = [] assertThrows(() => { const [...[[]]] = g(); }, TypeError); assertEquals([], log); log = [] assertThrows(() => { [...[[]]] = g(); }, TypeError); assertEquals([], log); log = [] assertThrows(() => { (([...[[]]]) => 0)(g()); }, TypeError); assertEquals([], log); { let backup = Array.prototype[Symbol.iterator]; Array.prototype[Symbol.iterator] = () => g(); log = []; assertDoesNotThrow(() => { var [x, ...[y]] = [1, 2, 3] }); assertEquals(log, [[]]); log = []; assertDoesNotThrow(() => { let [x, ...[y]] = [1, 2, 3]; }); assertEquals(log, [[]]); log = []; assertDoesNotThrow(() => { const [x, ...[y]] = [1, 2, 3]; }); assertEquals(log, [[]]); log = []; assertDoesNotThrow(() => { (([x, ...[y]]) => {})([1, 2, 3]); }); assertEquals(log, [[]]); log = []; assertThrows(() => { var [x, ...[[]]] = [1, 2, 3]; }, TypeError); assertEquals(log, [[]]); log = []; assertThrows(() => { let [x, ...[[]]] = [1, 2, 3]; }, TypeError); assertEquals(log, [[]]); log = []; assertThrows(() => { const [x, ...[[]]] = [1, 2, 3]; }, TypeError); assertEquals(log, [[]]); log = []; assertThrows(() => { (([x, ...[[]]]) => {})([1, 2, 3]); }, TypeError); assertEquals(log, [[]]); log = []; assertDoesNotThrow(() => { var [x, ...[...y]] = [1, 2, 3]; }); assertEquals(log, []); log = []; assertDoesNotThrow(() => { let [x, ...[...y]] = [1, 2, 3]; }); assertEquals(log, []); log = []; assertDoesNotThrow(() => { const [x, ...[...y]] = [1, 2, 3]; }); assertEquals(log, []); log = []; assertDoesNotThrow(() => { (([x, ...[...y]]) => {})([1, 2, 3]); }); assertEquals(log, []); Array.prototype[Symbol.iterator] = backup; } } // Return method throws. { let log = []; g.prototype.return = (...args) => { log.push(args); throw 23 }; log = []; assertThrowsEquals(() => { for (var x of g()) { break; } }, 23); assertEquals([[]], log); log = []; assertThrowsEquals(() => { for (let x of g()) { break; } }, 23); assertEquals([[]], log); log = []; assertThrowsEquals(() => { for (const x of g()) { break; } }, 23); assertEquals([[]], log); log = []; assertThrowsEquals(() => { for (x of g()) { break; } }, 23); assertEquals([[]], log); log = []; assertThrowsEquals(() => { for (var x of g()) { throw 42; } }, 42); assertEquals([[]], log); log = []; assertThrowsEquals(() => { for (let x of g()) { throw 42; } }, 42); assertEquals([[]], log); log = []; assertThrowsEquals(() => { for (const x of g()) { throw 42; } }, 42); assertEquals([[]], log); log = []; assertThrowsEquals(() => { for (x of g()) { throw 42; } }, 42); assertEquals([[]], log); log = []; assertThrowsEquals(() => { for (var x of g()) { return 42; } }, 23); assertEquals([[]], log); log = []; assertThrowsEquals(() => { for (let x of g()) { return 42; } }, 23); assertEquals([[]], log); log = []; assertThrowsEquals(() => { for (const x of g()) { return 42; } }, 23); assertEquals([[]], log); log = []; assertThrowsEquals(() => { for (x of g()) { return 42; } }, 23); assertEquals([[]], log); log = []; assertEquals(42, eval('for (var x of g()) { x; }')); assertEquals([], log); log = []; assertEquals(42, eval('for (let x of g()) { x; }')); assertEquals([], log); log = []; assertEquals(42, eval('for (const x of g()) { x; }')); assertEquals([], log); log = []; assertEquals(42, eval('for (x of g()) { x; }')); assertEquals([], log); log = []; assertThrowsEquals(() => { var [x] = g(); return x; }, 23); assertEquals([[]], log); log = []; assertThrowsEquals(() => { let [x] = g(); return x; }, 23); assertEquals([[]], log); log = []; assertThrowsEquals(() => { const [x] = g(); return x; }, 23); assertEquals([[]], log); log = []; assertThrowsEquals(() => { [x] = g(); return x; }, 23); assertEquals([[]], log); log = []; assertThrowsEquals(() => { (([x]) => x)(g()) }, 23); assertEquals([[]], log); } // Next method throws. { let closed = false; g.prototype.next = () => { throw 666; }; g.prototype.return = () => { closed = true; }; assertThrowsEquals(() => { for (var x of g()) {} }, 666); assertThrowsEquals(() => { for (let x of g()) {} }, 666); assertThrowsEquals(() => { for (const x of g()) {} }, 666); assertThrowsEquals(() => { for (x of g()) {} }, 666); assertThrowsEquals(() => { var [x] = g(); }, 666); assertThrowsEquals(() => { let [x] = g(); }, 666); assertThrowsEquals(() => { const [x] = g(); }, 666); assertThrowsEquals(() => { [x] = g(); }, 666); assertThrowsEquals(() => { (([x]) => x)(g()); }, 666); assertThrowsEquals(() => { var [...x] = g(); }, 666); assertThrowsEquals(() => { let [...x] = g(); }, 666); assertThrowsEquals(() => { const [...x] = g(); }, 666); assertThrowsEquals(() => { [...x] = g(); }, 666); assertThrowsEquals(() => { (([...x]) => x)(g()); }, 666); assertFalse(closed); } // Value throws. { let closed = false; g.prototype.next = () => ({get value() {throw 666}}); g.prototype.return = () => { closed = true; }; assertThrowsEquals(() => { for (var x of g()) {} }, 666); assertThrowsEquals(() => { for (let x of g()) {} }, 666); assertThrowsEquals(() => { for (const x of g()) {} }, 666); assertThrowsEquals(() => { for (x of g()) {} }, 666); assertThrowsEquals(() => { var [x] = g(); }, 666); assertThrowsEquals(() => { let [x] = g(); }, 666); assertThrowsEquals(() => { const [x] = g(); }, 666); assertThrowsEquals(() => { [x] = g(); }, 666); assertThrowsEquals(() => { (([x]) => x)(g()); }, 666); assertThrowsEquals(() => { var [...x] = g(); }, 666); assertThrowsEquals(() => { let [...x] = g(); }, 666); assertThrowsEquals(() => { const [...x] = g(); }, 666); assertThrowsEquals(() => { [...x] = g(); }, 666); assertThrowsEquals(() => { (([...x]) => x)(g()); }, 666); assertFalse(closed); } // Done throws. { let closed = false; g.prototype.next = () => ({get done() {throw 666}}); g.prototype.return = () => { closed = true; }; assertThrowsEquals(() => { for (var x of g()) {} }, 666); assertThrowsEquals(() => { for (let x of g()) {} }, 666); assertThrowsEquals(() => { for (const x of g()) {} }, 666); assertThrowsEquals(() => { for (x of g()) {} }, 666); assertThrowsEquals(() => { var [x] = g(); }, 666); assertThrowsEquals(() => { let [x] = g(); }, 666); assertThrowsEquals(() => { const [x] = g(); }, 666); assertThrowsEquals(() => { [x] = g(); }, 666); assertThrowsEquals(() => { (([x]) => x)(g()); }, 666); assertThrowsEquals(() => { var [...x] = g(); }, 666); assertThrowsEquals(() => { let [...x] = g(); }, 666); assertThrowsEquals(() => { const [...x] = g(); }, 666); assertThrowsEquals(() => { [...x] = g(); }, 666); assertThrowsEquals(() => { (([...x]) => x)(g()); }, 666); assertFalse(closed); } // Nested loops. { function* g1() { yield 1; yield 2; throw 3; } function* g2() { yield -1; yield -2; throw -3; } assertDoesNotThrow(() => { for (let x of g1()) { for (let y of g2()) { if (y == -2) break; } if (x == 2) break; } }, -3); assertThrowsEquals(() => { for (let x of g1()) { for (let y of g2()) { } } }, -3); assertThrowsEquals(() => { for (let x of g1()) { for (let y of g2()) { if (y == -2) break; } } }, 3); assertDoesNotThrow(() => { l: for (let x of g1()) { for (let y of g2()) { if (y == -2) break l; } } }); assertThrowsEquals(() => { for (let x of g1()) { for (let y of g2()) { throw 4; } } }, 4); assertThrowsEquals(() => { for (let x of g1()) { for (let y of g2()) { if (y == -2) throw 4; } } }, 4); let log = []; g1.prototype.return = () => { log.push(1); throw 5 }; g2.prototype.return = () => { log.push(2); throw -5 }; log = []; assertThrowsEquals(() => { for (let x of g1()) { for (let y of g2()) { if (y == -2) break; } if (x == 2) break; } }, -5); assertEquals([2, 1], log); log = []; assertThrowsEquals(() => { for (let x of g1()) { for (let y of g2()) { } } }, -3); assertEquals([1], log); log = []; assertThrowsEquals(() => { for (let x of g1()) { for (let y of g2()) { if (y == -2) break; } } }, -5); assertEquals([2, 1], log); log = []; assertThrowsEquals(() => { l: for (let x of g1()) { for (let y of g2()) { if (y == -2) break l; } } }, -5); assertEquals([2, 1], log); log = []; assertThrowsEquals(() => { for (let x of g1()) { for (let y of g2()) { throw 4; } } }, 4); assertEquals([2, 1], log); log = []; assertThrowsEquals(() => { for (let x of g1()) { for (let y of g2()) { if (y == -2) throw 4; } } }, 4); assertEquals([2, 1], log); log = []; assertThrowsEquals(() => { for (let x of g1()) { try { for (let y of g2()) { } } catch (_) {} } }, 3); assertEquals([], log); log = []; assertThrowsEquals(() => { for (let x of g1()) { try { for (let y of g2()) { } } catch (_) {} if (x == 2) break; } }, 5); assertEquals([1], log); } // yield*, argument's return method is "undefined". function TestYieldStarWithoutReturn(get_iterable) { assertTrue(get_iterable().return == undefined); function* g() { yield* get_iterable() } { let gen = g(); assertEquals({value: 1, done: false}, gen.next()); assertEquals({value: undefined, done: true}, gen.return()); } assertEquals(42, (() => { for (let x of g()) break; return 42; })()); assertEquals(42, (() => { for (let x of g()) return 42; })()); assertThrowsEquals(() => { for (let x of g()) throw 42; }, 42); } { let get_iterable1 = () => [1, 2]; let get_iterable2 = function*() { yield 1; yield 2 }; get_iterable2.prototype.return = null; TestYieldStarWithoutReturn(get_iterable1); TestYieldStarWithoutReturn(get_iterable2); } // yield*, argument's return method is defined. { let get_iterable = function*() { yield 1; yield 2 }; const obj = {}; get_iterable.prototype.return = (...args) => obj; function* g() { yield* get_iterable() } { let gen = g(); assertEquals({value: 1, done: false}, gen.next()); assertSame(obj, gen.return()); assertSame(obj, gen.return()); assertSame(obj, gen.return()); assertEquals({value: 2, done: false}, gen.next()); assertSame(obj, gen.return()); assertSame(obj, gen.return()); assertSame(obj, gen.return()); assertEquals({value: undefined, done: true}, gen.next()); assertEquals({value: undefined, done: true}, gen.return()); assertEquals({value: undefined, done: true}, gen.return()); } assertEquals(42, (() => { for (let x of g()) break; return 42; })()); assertEquals(42, (() => { for (let x of g()) return 42; })()); assertThrowsEquals(() => { for (let x of g()) throw 42; }, 42); }