diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index a0ab03a973112..db6069fd5ac9f 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -416,7 +416,7 @@ namespace ts { createLogicalNot(getDone) ), /*incrementor*/ undefined, - /*statement*/ convertForOfStatementHead(node, createDownlevelAwait(getValue)) + /*statement*/ convertForOfStatementHead(node, getValue) ), /*location*/ node ), diff --git a/src/harness/unittests/forAwaitOfEvaluation.ts b/src/harness/unittests/forAwaitOfEvaluation.ts new file mode 100644 index 0000000000000..339e0ca664b9b --- /dev/null +++ b/src/harness/unittests/forAwaitOfEvaluation.ts @@ -0,0 +1,148 @@ +/// + +namespace ts { + declare var Symbol: SymbolConstructor; + + describe("forAwaitOfEvaluation", () => { + const sourceFile = vpath.combine(vfs.srcFolder, "source.ts"); + + function compile(sourceText: string, options?: CompilerOptions) { + const fs = vfs.createFromFileSystem(Harness.IO, /*ignoreCase*/ false); + fs.writeFileSync(sourceFile, sourceText); + const compilerOptions: CompilerOptions = { target: ScriptTarget.ES5, module: ModuleKind.CommonJS, lib: ["lib.esnext.d.ts"], ...options }; + const host = new fakes.CompilerHost(fs, compilerOptions); + return compiler.compileFiles(host, [sourceFile], compilerOptions); + } + + function noRequire(id: string) { + throw new Error(`Module '${id}' could not be found.`); + } + + // Define a custom "Symbol" constructor to attach missing built-in symbols without + // modifying the global "Symbol" constructor + // tslint:disable-next-line:variable-name + const FakeSymbol: SymbolConstructor = ((description?: string) => Symbol(description)) as any; + (FakeSymbol).prototype = Symbol.prototype; + for (const key of Object.getOwnPropertyNames(Symbol)) { + Object.defineProperty(FakeSymbol, key, Object.getOwnPropertyDescriptor(Symbol, key)!); + } + + // Add "asyncIterator" if missing + if (!hasProperty(FakeSymbol, "asyncIterator")) Object.defineProperty(FakeSymbol, "asyncIterator", { value: Symbol.for("Symbol.asyncIterator"), configurable: true }); + + function evaluate(result: compiler.CompilationResult) { + const output = result.getOutput(sourceFile, "js")!; + assert.isDefined(output); + + const evaluateText = `(function (module, exports, require, __dirname, __filename, Symbol) { ${output.text} })`; + const evaluateThunk = eval(evaluateText) as (module: any, exports: any, require: (id: string) => any, dirname: string, filename: string, symbolConstructor: SymbolConstructor) => void; + const module: { exports: any; } = { exports: {} }; + evaluateThunk(module, module.exports, noRequire, vpath.dirname(output.file), output.file, FakeSymbol); + return module; + } + + it("sync (es5)", async () => { + const module = evaluate(compile(` + let i = 0; + const iterator = { + [Symbol.iterator]() { return this; }, + next() { + switch (i++) { + case 0: return { value: 1, done: false }; + case 1: return { value: Promise.resolve(2), done: false }; + case 2: return { value: new Promise(resolve => setTimeout(resolve, 100, 3)), done: false }; + default: return { value: undefined: done: true }; + } + } + }; + export const output: any[] = []; + export async function main() { + for await (const item of iterator) { + output.push(item); + } + }`)); + await module.exports.main(); + assert.strictEqual(module.exports.output[0], 1); + assert.strictEqual(module.exports.output[1], 2); + assert.strictEqual(module.exports.output[2], 3); + }); + + it("sync (es2015)", async () => { + const module = evaluate(compile(` + let i = 0; + const iterator = { + [Symbol.iterator]() { return this; }, + next() { + switch (i++) { + case 0: return { value: 1, done: false }; + case 1: return { value: Promise.resolve(2), done: false }; + case 2: return { value: new Promise(resolve => setTimeout(resolve, 100, 3)), done: false }; + default: return { value: undefined: done: true }; + } + } + }; + export const output: any[] = []; + export async function main() { + for await (const item of iterator) { + output.push(item); + } + }`, { target: ScriptTarget.ES2015 })); + await module.exports.main(); + assert.strictEqual(module.exports.output[0], 1); + assert.strictEqual(module.exports.output[1], 2); + assert.strictEqual(module.exports.output[2], 3); + }); + + it("async (es5)", async () => { + const module = evaluate(compile(` + let i = 0; + const iterator = { + [Symbol.asyncIterator]() { return this; }, + async next() { + switch (i++) { + case 0: return { value: 1, done: false }; + case 1: return { value: Promise.resolve(2), done: false }; + case 2: return { value: new Promise(resolve => setTimeout(resolve, 100, 3)), done: false }; + default: return { value: undefined: done: true }; + } + } + }; + export const output: any[] = []; + export async function main() { + for await (const item of iterator) { + output.push(item); + } + }`)); + await module.exports.main(); + assert.strictEqual(module.exports.output[0], 1); + assert.instanceOf(module.exports.output[1], Promise); + assert.instanceOf(module.exports.output[2], Promise); + }); + + it("async (es2015)", async () => { + const module = evaluate(compile(` + let i = 0; + const iterator = { + [Symbol.asyncIterator]() { return this; }, + async next() { + switch (i++) { + case 0: return { value: 1, done: false }; + case 1: return { value: Promise.resolve(2), done: false }; + case 2: return { value: new Promise(resolve => setTimeout(resolve, 100, 3)), done: false }; + default: return { value: undefined: done: true }; + } + } + }; + export const output: any[] = []; + export async function main() { + for await (const item of iterator) { + output.push(item); + } + }`, { target: ScriptTarget.ES2015 })); + await module.exports.main(); + assert.strictEqual(module.exports.output[0], 1); + assert.instanceOf(module.exports.output[1], Promise); + assert.instanceOf(module.exports.output[2], Promise); + }); + }); +} \ No newline at end of file diff --git a/tests/baselines/reference/emitter.forAwait.es2015.js b/tests/baselines/reference/emitter.forAwait.es2015.js index 6a29e7fad0a50..6260870471b13 100644 --- a/tests/baselines/reference/emitter.forAwait.es2015.js +++ b/tests/baselines/reference/emitter.forAwait.es2015.js @@ -63,7 +63,7 @@ function f1() { let y; try { for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = yield y_1.next(), !y_1_1.done;) { - const x = yield y_1_1.value; + const x = y_1_1.value; } } catch (e_1_1) { e_1 = { error: e_1_1 }; } @@ -97,7 +97,7 @@ function f2() { let x, y; try { for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = yield y_1.next(), !y_1_1.done;) { - x = yield y_1_1.value; + x = y_1_1.value; } } catch (e_1_1) { e_1 = { error: e_1_1 }; } @@ -135,7 +135,7 @@ function f3() { let y; try { for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = yield __await(y_1.next()), !y_1_1.done;) { - const x = yield yield __await(__await(y_1_1.value)); + const x = y_1_1.value; } } catch (e_1_1) { e_1 = { error: e_1_1 }; } @@ -173,7 +173,7 @@ function f4() { let x, y; try { for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = yield __await(y_1.next()), !y_1_1.done;) { - x = yield yield __await(__await(y_1_1.value)); + x = y_1_1.value; } } catch (e_1_1) { e_1 = { error: e_1_1 }; } @@ -208,7 +208,7 @@ function f5() { let y; try { outer: for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = yield y_1.next(), !y_1_1.done;) { - const x = yield y_1_1.value; + const x = y_1_1.value; continue outer; } } @@ -248,7 +248,7 @@ function f6() { let y; try { outer: for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = yield __await(y_1.next()), !y_1_1.done;) { - const x = yield yield __await(__await(y_1_1.value)); + const x = y_1_1.value; continue outer; } } diff --git a/tests/baselines/reference/emitter.forAwait.es2017.js b/tests/baselines/reference/emitter.forAwait.es2017.js index 63e833e81d22d..dd0e8aede2bb8 100644 --- a/tests/baselines/reference/emitter.forAwait.es2017.js +++ b/tests/baselines/reference/emitter.forAwait.es2017.js @@ -54,7 +54,7 @@ async function f1() { let y; try { for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = await y_1.next(), !y_1_1.done;) { - const x = await y_1_1.value; + const x = y_1_1.value; } } catch (e_1_1) { e_1 = { error: e_1_1 }; } @@ -78,7 +78,7 @@ async function f2() { let x, y; try { for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = await y_1.next(), !y_1_1.done;) { - x = await y_1_1.value; + x = y_1_1.value; } } catch (e_1_1) { e_1 = { error: e_1_1 }; } @@ -115,7 +115,7 @@ function f3() { let y; try { for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = yield __await(y_1.next()), !y_1_1.done;) { - const x = yield yield __await(__await(y_1_1.value)); + const x = y_1_1.value; } } catch (e_1_1) { e_1 = { error: e_1_1 }; } @@ -153,7 +153,7 @@ function f4() { let x, y; try { for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = yield __await(y_1.next()), !y_1_1.done;) { - x = yield yield __await(__await(y_1_1.value)); + x = y_1_1.value; } } catch (e_1_1) { e_1 = { error: e_1_1 }; } @@ -179,7 +179,7 @@ async function f5() { let y; try { outer: for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = await y_1.next(), !y_1_1.done;) { - const x = await y_1_1.value; + const x = y_1_1.value; continue outer; } } @@ -218,7 +218,7 @@ function f6() { let y; try { outer: for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = yield __await(y_1.next()), !y_1_1.done;) { - const x = yield yield __await(__await(y_1_1.value)); + const x = y_1_1.value; continue outer; } } diff --git a/tests/baselines/reference/emitter.forAwait.es5.js b/tests/baselines/reference/emitter.forAwait.es5.js index a303b659cb0f7..f264820d3b1f2 100644 --- a/tests/baselines/reference/emitter.forAwait.es5.js +++ b/tests/baselines/reference/emitter.forAwait.es5.js @@ -90,35 +90,33 @@ function f1() { return __generator(this, function (_b) { switch (_b.label) { case 0: - _b.trys.push([0, 6, 7, 12]); + _b.trys.push([0, 5, 6, 11]); y_1 = __asyncValues(y); _b.label = 1; case 1: return [4 /*yield*/, y_1.next()]; case 2: - if (!(y_1_1 = _b.sent(), !y_1_1.done)) return [3 /*break*/, 5]; - return [4 /*yield*/, y_1_1.value]; - case 3: - x = _b.sent(); - _b.label = 4; - case 4: return [3 /*break*/, 1]; - case 5: return [3 /*break*/, 12]; - case 6: + if (!(y_1_1 = _b.sent(), !y_1_1.done)) return [3 /*break*/, 4]; + x = y_1_1.value; + _b.label = 3; + case 3: return [3 /*break*/, 1]; + case 4: return [3 /*break*/, 11]; + case 5: e_1_1 = _b.sent(); e_1 = { error: e_1_1 }; - return [3 /*break*/, 12]; - case 7: - _b.trys.push([7, , 10, 11]); - if (!(y_1_1 && !y_1_1.done && (_a = y_1.return))) return [3 /*break*/, 9]; + return [3 /*break*/, 11]; + case 6: + _b.trys.push([6, , 9, 10]); + if (!(y_1_1 && !y_1_1.done && (_a = y_1.return))) return [3 /*break*/, 8]; return [4 /*yield*/, _a.call(y_1)]; - case 8: + case 7: _b.sent(); - _b.label = 9; - case 9: return [3 /*break*/, 11]; - case 10: + _b.label = 8; + case 8: return [3 /*break*/, 10]; + case 9: if (e_1) throw e_1.error; return [7 /*endfinally*/]; - case 11: return [7 /*endfinally*/]; - case 12: return [2 /*return*/]; + case 10: return [7 /*endfinally*/]; + case 11: return [2 /*return*/]; } }); }); @@ -172,35 +170,33 @@ function f2() { return __generator(this, function (_b) { switch (_b.label) { case 0: - _b.trys.push([0, 6, 7, 12]); + _b.trys.push([0, 5, 6, 11]); y_1 = __asyncValues(y); _b.label = 1; case 1: return [4 /*yield*/, y_1.next()]; case 2: - if (!(y_1_1 = _b.sent(), !y_1_1.done)) return [3 /*break*/, 5]; - return [4 /*yield*/, y_1_1.value]; - case 3: - x = _b.sent(); - _b.label = 4; - case 4: return [3 /*break*/, 1]; - case 5: return [3 /*break*/, 12]; - case 6: + if (!(y_1_1 = _b.sent(), !y_1_1.done)) return [3 /*break*/, 4]; + x = y_1_1.value; + _b.label = 3; + case 3: return [3 /*break*/, 1]; + case 4: return [3 /*break*/, 11]; + case 5: e_1_1 = _b.sent(); e_1 = { error: e_1_1 }; - return [3 /*break*/, 12]; - case 7: - _b.trys.push([7, , 10, 11]); - if (!(y_1_1 && !y_1_1.done && (_a = y_1.return))) return [3 /*break*/, 9]; + return [3 /*break*/, 11]; + case 6: + _b.trys.push([6, , 9, 10]); + if (!(y_1_1 && !y_1_1.done && (_a = y_1.return))) return [3 /*break*/, 8]; return [4 /*yield*/, _a.call(y_1)]; - case 8: + case 7: _b.sent(); - _b.label = 9; - case 9: return [3 /*break*/, 11]; - case 10: + _b.label = 8; + case 8: return [3 /*break*/, 10]; + case 9: if (e_1) throw e_1.error; return [7 /*endfinally*/]; - case 11: return [7 /*endfinally*/]; - case 12: return [2 /*return*/]; + case 10: return [7 /*endfinally*/]; + case 11: return [2 /*return*/]; } }); }); @@ -258,36 +254,33 @@ function f3() { return __generator(this, function (_b) { switch (_b.label) { case 0: - _b.trys.push([0, 7, 8, 13]); + _b.trys.push([0, 5, 6, 11]); y_1 = __asyncValues(y); _b.label = 1; case 1: return [4 /*yield*/, __await(y_1.next())]; case 2: - if (!(y_1_1 = _b.sent(), !y_1_1.done)) return [3 /*break*/, 6]; - return [4 /*yield*/, __await(__await(y_1_1.value))]; - case 3: return [4 /*yield*/, _b.sent()]; - case 4: - x = _b.sent(); - _b.label = 5; - case 5: return [3 /*break*/, 1]; - case 6: return [3 /*break*/, 13]; - case 7: + if (!(y_1_1 = _b.sent(), !y_1_1.done)) return [3 /*break*/, 4]; + x = y_1_1.value; + _b.label = 3; + case 3: return [3 /*break*/, 1]; + case 4: return [3 /*break*/, 11]; + case 5: e_1_1 = _b.sent(); e_1 = { error: e_1_1 }; - return [3 /*break*/, 13]; - case 8: - _b.trys.push([8, , 11, 12]); - if (!(y_1_1 && !y_1_1.done && (_a = y_1.return))) return [3 /*break*/, 10]; + return [3 /*break*/, 11]; + case 6: + _b.trys.push([6, , 9, 10]); + if (!(y_1_1 && !y_1_1.done && (_a = y_1.return))) return [3 /*break*/, 8]; return [4 /*yield*/, __await(_a.call(y_1))]; - case 9: + case 7: _b.sent(); - _b.label = 10; - case 10: return [3 /*break*/, 12]; - case 11: + _b.label = 8; + case 8: return [3 /*break*/, 10]; + case 9: if (e_1) throw e_1.error; return [7 /*endfinally*/]; - case 12: return [7 /*endfinally*/]; - case 13: return [2 /*return*/]; + case 10: return [7 /*endfinally*/]; + case 11: return [2 /*return*/]; } }); }); @@ -345,36 +338,33 @@ function f4() { return __generator(this, function (_b) { switch (_b.label) { case 0: - _b.trys.push([0, 7, 8, 13]); + _b.trys.push([0, 5, 6, 11]); y_1 = __asyncValues(y); _b.label = 1; case 1: return [4 /*yield*/, __await(y_1.next())]; case 2: - if (!(y_1_1 = _b.sent(), !y_1_1.done)) return [3 /*break*/, 6]; - return [4 /*yield*/, __await(__await(y_1_1.value))]; - case 3: return [4 /*yield*/, _b.sent()]; - case 4: - x = _b.sent(); - _b.label = 5; - case 5: return [3 /*break*/, 1]; - case 6: return [3 /*break*/, 13]; - case 7: + if (!(y_1_1 = _b.sent(), !y_1_1.done)) return [3 /*break*/, 4]; + x = y_1_1.value; + _b.label = 3; + case 3: return [3 /*break*/, 1]; + case 4: return [3 /*break*/, 11]; + case 5: e_1_1 = _b.sent(); e_1 = { error: e_1_1 }; - return [3 /*break*/, 13]; - case 8: - _b.trys.push([8, , 11, 12]); - if (!(y_1_1 && !y_1_1.done && (_a = y_1.return))) return [3 /*break*/, 10]; + return [3 /*break*/, 11]; + case 6: + _b.trys.push([6, , 9, 10]); + if (!(y_1_1 && !y_1_1.done && (_a = y_1.return))) return [3 /*break*/, 8]; return [4 /*yield*/, __await(_a.call(y_1))]; - case 9: + case 7: _b.sent(); - _b.label = 10; - case 10: return [3 /*break*/, 12]; - case 11: + _b.label = 8; + case 8: return [3 /*break*/, 10]; + case 9: if (e_1) throw e_1.error; return [7 /*endfinally*/]; - case 12: return [7 /*endfinally*/]; - case 13: return [2 /*return*/]; + case 10: return [7 /*endfinally*/]; + case 11: return [2 /*return*/]; } }); }); @@ -429,35 +419,33 @@ function f5() { return __generator(this, function (_b) { switch (_b.label) { case 0: - _b.trys.push([0, 6, 7, 12]); + _b.trys.push([0, 5, 6, 11]); y_1 = __asyncValues(y); _b.label = 1; case 1: return [4 /*yield*/, y_1.next()]; case 2: - if (!(y_1_1 = _b.sent(), !y_1_1.done)) return [3 /*break*/, 5]; - return [4 /*yield*/, y_1_1.value]; - case 3: - x = _b.sent(); - return [3 /*break*/, 4]; - case 4: return [3 /*break*/, 1]; - case 5: return [3 /*break*/, 12]; - case 6: + if (!(y_1_1 = _b.sent(), !y_1_1.done)) return [3 /*break*/, 4]; + x = y_1_1.value; + return [3 /*break*/, 3]; + case 3: return [3 /*break*/, 1]; + case 4: return [3 /*break*/, 11]; + case 5: e_1_1 = _b.sent(); e_1 = { error: e_1_1 }; - return [3 /*break*/, 12]; - case 7: - _b.trys.push([7, , 10, 11]); - if (!(y_1_1 && !y_1_1.done && (_a = y_1.return))) return [3 /*break*/, 9]; + return [3 /*break*/, 11]; + case 6: + _b.trys.push([6, , 9, 10]); + if (!(y_1_1 && !y_1_1.done && (_a = y_1.return))) return [3 /*break*/, 8]; return [4 /*yield*/, _a.call(y_1)]; - case 8: + case 7: _b.sent(); - _b.label = 9; - case 9: return [3 /*break*/, 11]; - case 10: + _b.label = 8; + case 8: return [3 /*break*/, 10]; + case 9: if (e_1) throw e_1.error; return [7 /*endfinally*/]; - case 11: return [7 /*endfinally*/]; - case 12: return [2 /*return*/]; + case 10: return [7 /*endfinally*/]; + case 11: return [2 /*return*/]; } }); }); @@ -516,36 +504,33 @@ function f6() { return __generator(this, function (_b) { switch (_b.label) { case 0: - _b.trys.push([0, 7, 8, 13]); + _b.trys.push([0, 5, 6, 11]); y_1 = __asyncValues(y); _b.label = 1; case 1: return [4 /*yield*/, __await(y_1.next())]; case 2: - if (!(y_1_1 = _b.sent(), !y_1_1.done)) return [3 /*break*/, 6]; - return [4 /*yield*/, __await(__await(y_1_1.value))]; - case 3: return [4 /*yield*/, _b.sent()]; - case 4: - x = _b.sent(); - return [3 /*break*/, 5]; - case 5: return [3 /*break*/, 1]; - case 6: return [3 /*break*/, 13]; - case 7: + if (!(y_1_1 = _b.sent(), !y_1_1.done)) return [3 /*break*/, 4]; + x = y_1_1.value; + return [3 /*break*/, 3]; + case 3: return [3 /*break*/, 1]; + case 4: return [3 /*break*/, 11]; + case 5: e_1_1 = _b.sent(); e_1 = { error: e_1_1 }; - return [3 /*break*/, 13]; - case 8: - _b.trys.push([8, , 11, 12]); - if (!(y_1_1 && !y_1_1.done && (_a = y_1.return))) return [3 /*break*/, 10]; + return [3 /*break*/, 11]; + case 6: + _b.trys.push([6, , 9, 10]); + if (!(y_1_1 && !y_1_1.done && (_a = y_1.return))) return [3 /*break*/, 8]; return [4 /*yield*/, __await(_a.call(y_1))]; - case 9: + case 7: _b.sent(); - _b.label = 10; - case 10: return [3 /*break*/, 12]; - case 11: + _b.label = 8; + case 8: return [3 /*break*/, 10]; + case 9: if (e_1) throw e_1.error; return [7 /*endfinally*/]; - case 12: return [7 /*endfinally*/]; - case 13: return [2 /*return*/]; + case 10: return [7 /*endfinally*/]; + case 11: return [2 /*return*/]; } }); });