v8/test/mjsunit/es6/generators-states.js
neis 2a0e4225dd Fix bug where generators got closed prematurely.
In a generator function, the parser rewrites a return statement into a "final"
yield.  A final yield used to close the generator, which was incorrect because
the return may occur inside a try-finally clause and so the generator may not
yet terminate.

BUG=

Review URL: https://codereview.chromium.org/1634553002

Cr-Commit-Position: refs/heads/master@{#33537}
2016-01-27 08:13:24 +00:00

95 lines
2.4 KiB
JavaScript

// Copyright 2014 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.
// Test generator states.
function Foo() {}
function Bar() {}
function assertIteratorResult(value, done, result) {
assertEquals({ value: value, done: done}, result);
}
function assertIteratorIsClosed(iter) {
assertIteratorResult(undefined, true, iter.next());
// Next and throw on a closed iterator.
assertDoesNotThrow(function() { iter.next(); });
assertThrows(function() { iter.throw(new Bar); }, Bar);
}
var iter;
function* nextGenerator() { yield iter.next(); }
function* throwGenerator() { yield iter.throw(new Bar); }
// Throw on a suspendedStart iterator.
iter = nextGenerator();
assertThrows(function() { iter.throw(new Foo) }, Foo)
assertIteratorIsClosed(iter);
assertThrows(function() { iter.throw(new Foo) }, Foo)
assertIteratorIsClosed(iter);
// The same.
iter = throwGenerator();
assertThrows(function() { iter.throw(new Foo) }, Foo)
assertThrows(function() { iter.throw(new Foo) }, Foo)
assertIteratorIsClosed(iter);
// Next on an executing iterator raises a TypeError.
iter = nextGenerator();
assertThrows(function() { iter.next() }, TypeError)
assertIteratorIsClosed(iter);
// Throw on an executing iterator raises a TypeError.
iter = throwGenerator();
assertThrows(function() { iter.next() }, TypeError)
assertIteratorIsClosed(iter);
// Next on an executing iterator doesn't change the state of the
// generator.
iter = (function* () {
try {
iter.next();
yield 1;
} catch (e) {
try {
// This next() should raise the same exception, because the first
// next() left the iter in the executing state.
iter.next();
yield 2;
} catch (e) {
yield 3;
}
}
yield 4;
})();
assertIteratorResult(3, false, iter.next());
assertIteratorResult(4, false, iter.next());
assertIteratorIsClosed(iter);
// A return that doesn't close.
{
let g = function*() { try {return 42} finally {yield 43} };
let x = g();
assertEquals({value: 43, done: false}, x.next());
assertEquals({value: 42, done: true}, x.next());
}
{
let x;
let g = function*() { try {return 42} finally {x.throw(666)} };
x = g();
assertThrows(() => x.next(), TypeError); // Still executing.
}
{
let x;
let g = function*() {
try {return 42} finally {try {x.throw(666)} catch(e) {}}
};
x = g();
assertEquals({value: 42, done: true}, x.next());
}