Source code

Revision control

Copy as Markdown

Other Tools

// Tests for for-of loops + `await using`, in particular cases where the for-of
// head has no lexical declarations.
let log = [];
function test(f, expected) {
log = [];
f();
drainJobQueue();
assertEq(log.join(","), expected);
}
function makeIter() {
let i = 0;
return {
[Symbol.iterator]() {
return this;
},
next() {
return {value: i, done: i++ >= 3};
},
return() {
log.push("it.return");
return {done: true};
},
};
}
function makeAsyncIter() {
let i = 0;
return {
[Symbol.asyncIterator]() {
return this;
},
async next() {
return {value: i, done: i++ >= 3};
},
async return() {
log.push("async-it.return");
return {done: true};
},
};
}
function makeAsyncRes() {
return {disposed: false,
[Symbol.asyncDispose]() {
log.push("asyncDispose");
this.disposed = true;
return Promise.resolve();
}};
}
// Like makeIter, but yields async resources and logs a distinct `return` label.
function makeAsyncResIter() {
let i = 0;
return {
[Symbol.iterator]() {
return this;
},
next() {
return {value: makeAsyncRes(), done: i++ >= 3};
},
return() {
log.push("res-it.return");
return {done: true};
},
};
}
// break, and the resource is still live after the loop.
test(function() {
(async function() {
var x;
{
await using a = makeAsyncRes();
for (x of makeIter()) {
break;
}
assertEq(a.disposed, false);
log.push("after loop");
}
log.push("left block");
})();
}, "it.return,after loop,asyncDispose,left block");
// return a value out of a for-await-of, no lexical declaration in the head.
test(function() {
(async function() {
var x;
{
await using a = makeAsyncRes();
for await (x of makeAsyncIter()) {
return "rval";
}
}
})().then(v => log.push(`resolved: ${v}`));
}, "async-it.return,asyncDispose,resolved: rval");
// return out of an inner for-of nested in a for-of head with `await using`.
test(function() {
(async function() {
var b;
for (await using a of makeAsyncResIter()) {
for (b of makeIter()) {
return "rval";
}
}
})().then(v => log.push(`resolved: ${v}`));
}, "it.return,asyncDispose,res-it.return,resolved: rval");
// An async generator left via a forced return at a yield inside the loop.
test(function() {
const gen = (async function*() {
var x;
{
await using a = makeAsyncRes();
for (x of makeIter()) {
yield 1;
}
}
})();
gen.next()
.then(() => gen.return("rval"))
.then(r => log.push(`returned: ${r.value}`));
}, "it.return,asyncDispose,returned: rval");
// Test throw behavior.
test(function() {
(async function() {
var x;
try {
{
await using a = makeAsyncRes();
for (x of makeIter()) {
throw new Error("e");
}
}
} catch (e) {
log.push("caught");
}
})();
}, "it.return,asyncDispose,caught");