blob: faeb68380fea4aa67a97ace057291d075e23a6c8 [file] [log] [blame]
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001// 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) {
Emily Bernierd0a1eb72015-03-24 16:35:39 -040044 assertIteratorIsClosed(iter);
Ben Murdochb8a8cc12014-11-26 15:28:44 +000045}
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();