Source code
Revision control
Copy as Markdown
Other Tools
// |jit-test| --fast-warmup; --no-threads
// yield* with the Throw and Return resume kinds + a hot delegation loop.
let innerClosed = 0;
function* innerGen(n) {
try {
for (let i = 0; i < n; i++) {
yield i;
}
return "innerDone";
} finally {
innerClosed++;
}
}
function* deleg(n) {
return "outer:" + (yield* innerGen(n));
}
// The inner generator catches the thrown value and keeps going, so the
// delegation loop resumes it rather than closing it.
function* innerCatches(n) {
for (let i = 0; i < n; i++) {
try {
yield i;
} catch (e) {
yield "caught:" + e;
}
}
return "innerDone";
}
function* delegCatch(n) {
return "outer:" + (yield* innerCatches(n));
}
// An iterator with no throw method: throwing into the delegation has to close it
// and report a TypeError.
function noThrowIterable(n) {
let i = 0;
return {
[Symbol.iterator]() { return this; },
next: () => (i < n ? { value: i++, done: false } : { value: "d", done: true }),
return: v => { innerClosed++; return { value: v, done: true }; },
};
}
function* delegNoThrow(n) {
return "outer:" + (yield* noThrowIterable(n));
}
function drive(g) {
let out = [], r;
while (!(r = g.next()).done) {
out.push(String(r.value));
}
out.push(String(r.value));
return out.join(",");
}
// Run the delegations to completion first, so the loops are Ion-compiled and the
// Next resumes go through the resume dispatch.
for (let i = 0; i < 200; i++) {
assertEq(drive(deleg(4)), "0,1,2,3,outer:innerDone");
assertEq(drive(delegCatch(4)), "0,1,2,3,outer:innerDone");
assertEq(drive(delegNoThrow(4)), "0,1,2,3,outer:d");
}
// Force a return while suspended inside the delegation: the inner generator is
// closed and the outer one finishes with the forced value.
innerClosed = 0;
for (let i = 0; i < 200; i++) {
const g = deleg(4);
assertEq(g.next().value, 0);
assertEq(g.next().value, 1);
const r = g.return("stop");
assertEq(r.value, "stop");
assertEq(r.done, true);
}
assertEq(innerClosed, 200);
// Throw into the delegation, caught by the inner generator.
for (let i = 0; i < 200; i++) {
const g = delegCatch(4);
assertEq(g.next().value, 0);
assertEq(g.next().value, 1);
const r = g.throw("boom");
assertEq(r.value, "caught:boom");
assertEq(r.done, false);
}
// Throw into the delegation, not caught: the inner generator is closed and the
// exception comes out of the outer one.
innerClosed = 0;
for (let i = 0; i < 200; i++) {
const g = deleg(4);
assertEq(g.next().value, 0);
let caught = null;
try {
g.throw("boom");
} catch (e) {
caught = e;
}
assertEq(caught, "boom");
assertEq(g.next().done, true);
}
assertEq(innerClosed, 200);
// Throw into a delegation whose iterator has no throw method.
innerClosed = 0;
for (let i = 0; i < 200; i++) {
const g = delegNoThrow(4);
assertEq(g.next().value, 0);
let caught = null;
try {
g.throw("boom");
} catch (e) {
caught = e;
}
assertEq(caught instanceof TypeError, true);
}
assertEq(innerClosed, 200);