1// Copyright 2013 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6//     * Redistributions of source code must retain the above copyright
7//       notice, this list of conditions and the following disclaimer.
8//     * Redistributions in binary form must reproduce the above
9//       copyright notice, this list of conditions and the following
10//       disclaimer in the documentation and/or other materials provided
11//       with the distribution.
12//     * Neither the name of Google Inc. nor the names of its
13//       contributors may be used to endorse or promote products derived
14//       from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28// Flags: --expose-gc
29
30// Test generator iteration.
31
32var GeneratorFunction = (function*(){yield 1;}).__proto__.constructor;
33
34function assertIteratorResult(value, done, result) {
35  assertEquals({ value: value, done: done}, result);
36}
37
38function assertIteratorIsClosed(iter) {
39  assertIteratorResult(undefined, true, iter.next());
40  assertDoesNotThrow(function() { iter.next(); });
41}
42
43function assertThrownIteratorIsClosed(iter) {
44  assertIteratorIsClosed(iter);
45}
46
47function TestGeneratorResultPrototype() {
48  function* g() { yield 1; }
49  var iter = g();
50  var result = iter.next();
51
52  assertSame(Object.prototype, Object.getPrototypeOf(result));
53  property_names = Object.getOwnPropertyNames(result);
54  property_names.sort();
55  assertEquals(["done", "value"], property_names);
56  assertIteratorResult(1, false, result);
57}
58TestGeneratorResultPrototype()
59
60function TestGenerator(g, expected_values_for_next,
61                       send_val, expected_values_for_send) {
62  function testNext(thunk) {
63    var iter = thunk();
64    for (var i = 0; i < expected_values_for_next.length; i++) {
65      var v1 = expected_values_for_next[i];
66      var v2 = i == expected_values_for_next.length - 1;
67      // var v3 = iter.next();
68      assertIteratorResult(v1, v2, iter.next());
69    }
70    assertIteratorIsClosed(iter);
71  }
72  function testSend(thunk) {
73    var iter = thunk();
74    for (var i = 0; i < expected_values_for_send.length; i++) {
75      assertIteratorResult(expected_values_for_send[i],
76                           i == expected_values_for_send.length - 1,
77                           iter.next(send_val));
78    }
79    assertIteratorIsClosed(iter);
80  }
81  function testThrow(thunk) {
82    for (var i = 0; i < expected_values_for_next.length; i++) {
83      var iter = thunk();
84      for (var j = 0; j < i; j++) {
85        assertIteratorResult(expected_values_for_next[j],
86                             j == expected_values_for_next.length - 1,
87                             iter.next());
88      }
89      function Sentinel() {}
90      assertThrows(function () { iter.throw(new Sentinel); }, Sentinel);
91      assertThrownIteratorIsClosed(iter);
92    }
93  }
94
95  testNext(g);
96  testSend(g);
97  testThrow(g);
98
99  testNext(function*() { return yield* g(); });
100  testSend(function*() { return yield* g(); });
101  testThrow(function*() { return yield* g(); });
102
103  if (g instanceof GeneratorFunction) {
104    testNext(function() { return new g(); });
105    testSend(function() { return new g(); });
106    testThrow(function() { return new g(); });
107  }
108}
109
110TestGenerator(function* g1() { },
111              [undefined],
112              "foo",
113              [undefined]);
114
115TestGenerator(function* g2() { yield 1; },
116              [1, undefined],
117              "foo",
118              [1, undefined]);
119
120TestGenerator(function* g3() { yield 1; yield 2; },
121              [1, 2, undefined],
122              "foo",
123              [1, 2, undefined]);
124
125TestGenerator(function* g4() { yield 1; yield 2; return 3; },
126              [1, 2, 3],
127              "foo",
128              [1, 2, 3]);
129
130TestGenerator(function* g5() { return 1; },
131              [1],
132             "foo",
133              [1]);
134
135TestGenerator(function* g6() { var x = yield 1; return x; },
136              [1, undefined],
137              "foo",
138              [1, "foo"]);
139
140TestGenerator(function* g7() { var x = yield 1; yield 2; return x; },
141              [1, 2, undefined],
142              "foo",
143              [1, 2, "foo"]);
144
145TestGenerator(function* g8() { for (var x = 0; x < 4; x++) { yield x; } },
146              [0, 1, 2, 3, undefined],
147              "foo",
148              [0, 1, 2, 3, undefined]);
149
150// Generator with arguments.
151TestGenerator(
152    function g9() {
153      return (function*(a, b, c, d) {
154        yield a; yield b; yield c; yield d;
155      })("fee", "fi", "fo", "fum");
156    },
157    ["fee", "fi", "fo", "fum", undefined],
158    "foo",
159    ["fee", "fi", "fo", "fum", undefined]);
160
161// Too few arguments.
162TestGenerator(
163    function g10() {
164      return (function*(a, b, c, d) {
165        yield a; yield b; yield c; yield d;
166      })("fee", "fi");
167    },
168    ["fee", "fi", undefined, undefined, undefined],
169    "foo",
170    ["fee", "fi", undefined, undefined, undefined]);
171
172// Too many arguments.
173TestGenerator(
174    function g11() {
175      return (function*(a, b, c, d) {
176        yield a; yield b; yield c; yield d;
177      })("fee", "fi", "fo", "fum", "I smell the blood of an Englishman");
178    },
179    ["fee", "fi", "fo", "fum", undefined],
180    "foo",
181    ["fee", "fi", "fo", "fum", undefined]);
182
183// The arguments object.
184TestGenerator(
185    function g12() {
186      return (function*(a, b, c, d) {
187        for (var i = 0; i < arguments.length; i++) {
188          yield arguments[i];
189        }
190      })("fee", "fi", "fo", "fum", "I smell the blood of an Englishman");
191    },
192    ["fee", "fi", "fo", "fum", "I smell the blood of an Englishman",
193     undefined],
194    "foo",
195    ["fee", "fi", "fo", "fum", "I smell the blood of an Englishman",
196     undefined]);
197
198// Access to captured free variables.
199TestGenerator(
200    function g13() {
201      return (function(a, b, c, d) {
202        return (function*() {
203          yield a; yield b; yield c; yield d;
204        })();
205      })("fee", "fi", "fo", "fum");
206    },
207    ["fee", "fi", "fo", "fum", undefined],
208    "foo",
209    ["fee", "fi", "fo", "fum", undefined]);
210
211// Abusing the arguments object.
212TestGenerator(
213    function g14() {
214      return (function*(a, b, c, d) {
215        arguments[0] = "Be he live";
216        arguments[1] = "or be he dead";
217        arguments[2] = "I'll grind his bones";
218        arguments[3] = "to make my bread";
219        yield a; yield b; yield c; yield d;
220      })("fee", "fi", "fo", "fum");
221    },
222    ["Be he live", "or be he dead", "I'll grind his bones", "to make my bread",
223     undefined],
224    "foo",
225    ["Be he live", "or be he dead", "I'll grind his bones", "to make my bread",
226     undefined]);
227
228// Abusing the arguments object: strict mode.
229TestGenerator(
230    function g15() {
231      return (function*(a, b, c, d) {
232        "use strict";
233        arguments[0] = "Be he live";
234        arguments[1] = "or be he dead";
235        arguments[2] = "I'll grind his bones";
236        arguments[3] = "to make my bread";
237        yield a; yield b; yield c; yield d;
238      })("fee", "fi", "fo", "fum");
239    },
240    ["fee", "fi", "fo", "fum", undefined],
241    "foo",
242    ["fee", "fi", "fo", "fum", undefined]);
243
244// GC.
245TestGenerator(function* g16() { yield "baz"; gc(); yield "qux"; },
246              ["baz", "qux", undefined],
247              "foo",
248              ["baz", "qux", undefined]);
249
250// Receivers.
251TestGenerator(
252    function g17() {
253      function* g() { yield this.x; yield this.y; }
254      var o = { start: g, x: 1, y: 2 };
255      return o.start();
256    },
257    [1, 2, undefined],
258    "foo",
259    [1, 2, undefined]);
260
261TestGenerator(
262    function g18() {
263      function* g() { yield this.x; yield this.y; }
264      var iter = new g;
265      iter.x = 1;
266      iter.y = 2;
267      return iter;
268    },
269    [1, 2, undefined],
270    "foo",
271    [1, 2, undefined]);
272
273TestGenerator(
274    function* g19() {
275      var x = 1;
276      yield x;
277      with({x:2}) { yield x; }
278      yield x;
279    },
280    [1, 2, 1, undefined],
281    "foo",
282    [1, 2, 1, undefined]);
283
284TestGenerator(
285    function* g20() { yield (1 + (yield 2) + 3); },
286    [2, NaN, undefined],
287    "foo",
288    [2, "1foo3", undefined]);
289
290TestGenerator(
291    function* g21() { return (1 + (yield 2) + 3); },
292    [2, NaN],
293    "foo",
294    [2, "1foo3"]);
295
296TestGenerator(
297    function* g22() { yield (1 + (yield 2) + 3); yield (4 + (yield 5) + 6); },
298    [2, NaN, 5, NaN, undefined],
299    "foo",
300    [2, "1foo3", 5, "4foo6", undefined]);
301
302TestGenerator(
303    function* g23() {
304      return (yield (1 + (yield 2) + 3)) + (yield (4 + (yield 5) + 6));
305    },
306    [2, NaN, 5, NaN, NaN],
307    "foo",
308    [2, "1foo3", 5, "4foo6", "foofoo"]);
309
310// Rewind a try context with and without operands on the stack.
311TestGenerator(
312    function* g24() {
313      try {
314        return (yield (1 + (yield 2) + 3)) + (yield (4 + (yield 5) + 6));
315      } catch (e) {
316        throw e;
317      }
318    },
319    [2, NaN, 5, NaN, NaN],
320    "foo",
321    [2, "1foo3", 5, "4foo6", "foofoo"]);
322
323// Yielding in a catch context, with and without operands on the stack.
324TestGenerator(
325    function* g25() {
326      try {
327        throw (yield (1 + (yield 2) + 3))
328      } catch (e) {
329        if (typeof e == 'object') throw e;
330        return e + (yield (4 + (yield 5) + 6));
331      }
332    },
333    [2, NaN, 5, NaN, NaN],
334    "foo",
335    [2, "1foo3", 5, "4foo6", "foofoo"]);
336
337// Yield with no arguments yields undefined.
338TestGenerator(
339    function* g26() { return yield yield },
340    [undefined, undefined, undefined],
341    "foo",
342    [undefined, "foo", "foo"]);
343
344// A newline causes the parser to stop looking for an argument to yield.
345TestGenerator(
346    function* g27() {
347      yield
348      3
349      return
350    },
351    [undefined, undefined],
352    "foo",
353    [undefined, undefined]);
354
355// TODO(wingo): We should use TestGenerator for these, except that
356// currently yield* will unconditionally propagate a throw() to the
357// delegate iterator, which fails for these iterators that don't have
358// throw().  See http://code.google.com/p/v8/issues/detail?id=3484.
359(function() {
360    function* g28() {
361      yield* [1, 2, 3];
362    }
363    var iter = g28();
364    assertIteratorResult(1, false, iter.next());
365    assertIteratorResult(2, false, iter.next());
366    assertIteratorResult(3, false, iter.next());
367    assertIteratorResult(undefined, true, iter.next());
368})();
369
370(function() {
371    function* g29() {
372      yield* "abc";
373    }
374    var iter = g29();
375    assertIteratorResult("a", false, iter.next());
376    assertIteratorResult("b", false, iter.next());
377    assertIteratorResult("c", false, iter.next());
378    assertIteratorResult(undefined, true, iter.next());
379})();
380
381// Generator function instances.
382TestGenerator(GeneratorFunction(),
383              [undefined],
384              "foo",
385              [undefined]);
386
387TestGenerator(new GeneratorFunction(),
388              [undefined],
389              "foo",
390              [undefined]);
391
392TestGenerator(GeneratorFunction('yield 1;'),
393              [1, undefined],
394              "foo",
395              [1, undefined]);
396
397TestGenerator(
398    function() { return GeneratorFunction('x', 'y', 'yield x + y;')(1, 2) },
399    [3, undefined],
400    "foo",
401    [3, undefined]);
402
403// Access to this with formal arguments.
404TestGenerator(
405    function () {
406      return ({ x: 42, g: function* (a) { yield this.x } }).g(0);
407    },
408    [42, undefined],
409    "foo",
410    [42, undefined]);
411
412// Test that yield* re-yields received results without re-boxing.
413function TestDelegatingYield() {
414  function results(results) {
415    var i = 0;
416    function next() {
417      return results[i++];
418    }
419    var iter = { next: next };
420    var ret = {};
421    ret[Symbol.iterator] = function() { return iter; };
422    return ret;
423  }
424  function* yield_results(expected) {
425    return yield* results(expected);
426  }
427  function collect_results(iterable) {
428    var iter = iterable[Symbol.iterator]();
429    var ret = [];
430    var result;
431    do {
432      result = iter.next();
433      ret.push(result);
434    } while (!result.done);
435    return ret;
436  }
437  // We have to put a full result for the end, because the return will re-box.
438  var expected = [{value: 1}, 13, "foo", {value: 34, done: true}];
439
440  // Sanity check.
441  assertEquals(expected, collect_results(results(expected)));
442  assertEquals(expected, collect_results(yield_results(expected)));
443}
444TestDelegatingYield();
445
446function TestTryCatch(instantiate) {
447  function* g() { yield 1; try { yield 2; } catch (e) { yield e; } yield 3; }
448  function Sentinel() {}
449
450  function Test1(iter) {
451    assertIteratorResult(1, false, iter.next());
452    assertIteratorResult(2, false, iter.next());
453    assertIteratorResult(3, false, iter.next());
454    assertIteratorIsClosed(iter);
455  }
456  Test1(instantiate(g));
457
458  function Test2(iter) {
459    assertThrows(function() { iter.throw(new Sentinel); }, Sentinel);
460    assertThrownIteratorIsClosed(iter);
461  }
462  Test2(instantiate(g));
463
464  function Test3(iter) {
465    assertIteratorResult(1, false, iter.next());
466    assertThrows(function() { iter.throw(new Sentinel); }, Sentinel);
467    assertThrownIteratorIsClosed(iter);
468  }
469  Test3(instantiate(g));
470
471  function Test4(iter) {
472    assertIteratorResult(1, false, iter.next());
473    assertIteratorResult(2, false, iter.next());
474    var exn = new Sentinel;
475    assertIteratorResult(exn, false, iter.throw(exn));
476    assertIteratorResult(3, false, iter.next());
477    assertIteratorIsClosed(iter);
478  }
479  Test4(instantiate(g));
480
481  function Test5(iter) {
482    assertIteratorResult(1, false, iter.next());
483    assertIteratorResult(2, false, iter.next());
484    var exn = new Sentinel;
485    assertIteratorResult(exn, false, iter.throw(exn));
486    assertIteratorResult(3, false, iter.next());
487    assertThrows(function() { iter.throw(new Sentinel); }, Sentinel);
488    assertThrownIteratorIsClosed(iter);
489  }
490  Test5(instantiate(g));
491
492  function Test6(iter) {
493    assertIteratorResult(1, false, iter.next());
494    assertIteratorResult(2, false, iter.next());
495    var exn = new Sentinel;
496    assertIteratorResult(exn, false, iter.throw(exn));
497    assertThrows(function() { iter.throw(new Sentinel); }, Sentinel);
498    assertThrownIteratorIsClosed(iter);
499  }
500  Test6(instantiate(g));
501
502  function Test7(iter) {
503    assertIteratorResult(1, false, iter.next());
504    assertIteratorResult(2, false, iter.next());
505    assertIteratorResult(3, false, iter.next());
506    assertIteratorIsClosed(iter);
507  }
508  Test7(instantiate(g));
509}
510TestTryCatch(function (g) { return g(); });
511TestTryCatch(function* (g) { return yield* g(); });
512
513function TestTryFinally(instantiate) {
514  function* g() { yield 1; try { yield 2; } finally { yield 3; } yield 4; }
515  function Sentinel() {}
516  function Sentinel2() {}
517
518  function Test1(iter) {
519    assertIteratorResult(1, false, iter.next());
520    assertIteratorResult(2, false, iter.next());
521    assertIteratorResult(3, false, iter.next());
522    assertIteratorResult(4, false, iter.next());
523    assertIteratorIsClosed(iter);
524  }
525  Test1(instantiate(g));
526
527  function Test2(iter) {
528    assertThrows(function() { iter.throw(new Sentinel); }, Sentinel);
529    assertThrownIteratorIsClosed(iter);
530  }
531  Test2(instantiate(g));
532
533  function Test3(iter) {
534    assertIteratorResult(1, false, iter.next());
535    assertThrows(function() { iter.throw(new Sentinel); }, Sentinel);
536    assertThrownIteratorIsClosed(iter);
537  }
538  Test3(instantiate(g));
539
540  function Test4(iter) {
541    assertIteratorResult(1, false, iter.next());
542    assertIteratorResult(2, false, iter.next());
543    assertIteratorResult(3, false, iter.throw(new Sentinel));
544    assertThrows(function() { iter.next(); }, Sentinel);
545    assertThrownIteratorIsClosed(iter);
546  }
547  Test4(instantiate(g));
548
549  function Test5(iter) {
550    assertIteratorResult(1, false, iter.next());
551    assertIteratorResult(2, false, iter.next());
552    assertIteratorResult(3, false, iter.throw(new Sentinel));
553    assertThrows(function() { iter.throw(new Sentinel2); }, Sentinel2);
554    assertThrownIteratorIsClosed(iter);
555  }
556  Test5(instantiate(g));
557
558  function Test6(iter) {
559    assertIteratorResult(1, false, iter.next());
560    assertIteratorResult(2, false, iter.next());
561    assertIteratorResult(3, false, iter.next());
562    assertThrows(function() { iter.throw(new Sentinel); }, Sentinel);
563    assertThrownIteratorIsClosed(iter);
564  }
565  Test6(instantiate(g));
566
567  function Test7(iter) {
568    assertIteratorResult(1, false, iter.next());
569    assertIteratorResult(2, false, iter.next());
570    assertIteratorResult(3, false, iter.next());
571    assertIteratorResult(4, false, iter.next());
572    assertThrows(function() { iter.throw(new Sentinel); }, Sentinel);
573    assertThrownIteratorIsClosed(iter);
574  }
575  Test7(instantiate(g));
576
577  function Test8(iter) {
578    assertIteratorResult(1, false, iter.next());
579    assertIteratorResult(2, false, iter.next());
580    assertIteratorResult(3, false, iter.next());
581    assertIteratorResult(4, false, iter.next());
582    assertIteratorIsClosed(iter);
583  }
584  Test8(instantiate(g));
585}
586TestTryFinally(function (g) { return g(); });
587TestTryFinally(function* (g) { return yield* g(); });
588
589function TestNestedTry(instantiate) {
590  function* g() {
591    try {
592      yield 1;
593      try { yield 2; } catch (e) { yield e; }
594      yield 3;
595    } finally {
596      yield 4;
597    }
598    yield 5;
599  }
600  function Sentinel() {}
601  function Sentinel2() {}
602
603  function Test1(iter) {
604    assertIteratorResult(1, false, iter.next());
605    assertIteratorResult(2, false, iter.next());
606    assertIteratorResult(3, false, iter.next());
607    assertIteratorResult(4, false, iter.next());
608    assertIteratorResult(5, false, iter.next());
609    assertIteratorIsClosed(iter);
610  }
611  Test1(instantiate(g));
612
613  function Test2(iter) {
614    assertThrows(function() { iter.throw(new Sentinel); }, Sentinel);
615    assertThrownIteratorIsClosed(iter);
616  }
617  Test2(instantiate(g));
618
619  function Test3(iter) {
620    assertIteratorResult(1, false, iter.next());
621    assertIteratorResult(4, false, iter.throw(new Sentinel));
622    assertThrows(function() { iter.next(); }, Sentinel);
623    assertThrownIteratorIsClosed(iter);
624  }
625  Test3(instantiate(g));
626
627  function Test4(iter) {
628    assertIteratorResult(1, false, iter.next());
629    assertIteratorResult(4, false, iter.throw(new Sentinel));
630    assertThrows(function() { iter.throw(new Sentinel2); }, Sentinel2);
631    assertThrownIteratorIsClosed(iter);
632  }
633  Test4(instantiate(g));
634
635  function Test5(iter) {
636    assertIteratorResult(1, false, iter.next());
637    assertIteratorResult(2, false, iter.next());
638    var exn = new Sentinel;
639    assertIteratorResult(exn, false, iter.throw(exn));
640    assertIteratorResult(3, false, iter.next());
641    assertIteratorResult(4, false, iter.next());
642    assertIteratorResult(5, false, iter.next());
643    assertIteratorIsClosed(iter);
644  }
645  Test5(instantiate(g));
646
647  function Test6(iter) {
648    assertIteratorResult(1, false, iter.next());
649    assertIteratorResult(2, false, iter.next());
650    var exn = new Sentinel;
651    assertIteratorResult(exn, false, iter.throw(exn));
652    assertIteratorResult(4, false, iter.throw(new Sentinel2));
653    assertThrows(function() { iter.next(); }, Sentinel2);
654    assertThrownIteratorIsClosed(iter);
655  }
656  Test6(instantiate(g));
657
658  function Test7(iter) {
659    assertIteratorResult(1, false, iter.next());
660    assertIteratorResult(2, false, iter.next());
661    var exn = new Sentinel;
662    assertIteratorResult(exn, false, iter.throw(exn));
663    assertIteratorResult(3, false, iter.next());
664    assertIteratorResult(4, false, iter.throw(new Sentinel2));
665    assertThrows(function() { iter.next(); }, Sentinel2);
666    assertThrownIteratorIsClosed(iter);
667  }
668  Test7(instantiate(g));
669
670  // That's probably enough.
671}
672TestNestedTry(function (g) { return g(); });
673TestNestedTry(function* (g) { return yield* g(); });
674
675function TestRecursion() {
676  function TestNextRecursion() {
677    function* g() { yield iter.next(); }
678    var iter = g();
679    return iter.next();
680  }
681  function TestSendRecursion() {
682    function* g() { yield iter.next(42); }
683    var iter = g();
684    return iter.next();
685  }
686  function TestThrowRecursion() {
687    function* g() { yield iter.throw(1); }
688    var iter = g();
689    return iter.next();
690  }
691  assertThrows(TestNextRecursion, Error);
692  assertThrows(TestSendRecursion, Error);
693  assertThrows(TestThrowRecursion, Error);
694}
695TestRecursion();
696