Skip to content

Commit 364fce3

Browse files
authored
Merge pull request #24474 from Microsoft/fix21115.2
Do not await iterated value in for-await-of
2 parents f411164 + 394862e commit 364fce3

File tree

5 files changed

+263
-130
lines changed

5 files changed

+263
-130
lines changed

src/compiler/transformers/esnext.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ namespace ts {
416416
createLogicalNot(getDone)
417417
),
418418
/*incrementor*/ undefined,
419-
/*statement*/ convertForOfStatementHead(node, createDownlevelAwait(getValue))
419+
/*statement*/ convertForOfStatementHead(node, getValue)
420420
),
421421
/*location*/ node
422422
),
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
/// <reference path="..\harness.ts" />
2+
3+
namespace ts {
4+
declare var Symbol: SymbolConstructor;
5+
6+
describe("forAwaitOfEvaluation", () => {
7+
const sourceFile = vpath.combine(vfs.srcFolder, "source.ts");
8+
9+
function compile(sourceText: string, options?: CompilerOptions) {
10+
const fs = vfs.createFromFileSystem(Harness.IO, /*ignoreCase*/ false);
11+
fs.writeFileSync(sourceFile, sourceText);
12+
const compilerOptions: CompilerOptions = { target: ScriptTarget.ES5, module: ModuleKind.CommonJS, lib: ["lib.esnext.d.ts"], ...options };
13+
const host = new fakes.CompilerHost(fs, compilerOptions);
14+
return compiler.compileFiles(host, [sourceFile], compilerOptions);
15+
}
16+
17+
function noRequire(id: string) {
18+
throw new Error(`Module '${id}' could not be found.`);
19+
}
20+
21+
// Define a custom "Symbol" constructor to attach missing built-in symbols without
22+
// modifying the global "Symbol" constructor
23+
// tslint:disable-next-line:variable-name
24+
const FakeSymbol: SymbolConstructor = ((description?: string) => Symbol(description)) as any;
25+
(<any>FakeSymbol).prototype = Symbol.prototype;
26+
for (const key of Object.getOwnPropertyNames(Symbol)) {
27+
Object.defineProperty(FakeSymbol, key, Object.getOwnPropertyDescriptor(Symbol, key)!);
28+
}
29+
30+
// Add "asyncIterator" if missing
31+
if (!hasProperty(FakeSymbol, "asyncIterator")) Object.defineProperty(FakeSymbol, "asyncIterator", { value: Symbol.for("Symbol.asyncIterator"), configurable: true });
32+
33+
function evaluate(result: compiler.CompilationResult) {
34+
const output = result.getOutput(sourceFile, "js")!;
35+
assert.isDefined(output);
36+
37+
const evaluateText = `(function (module, exports, require, __dirname, __filename, Symbol) { ${output.text} })`;
38+
const evaluateThunk = eval(evaluateText) as (module: any, exports: any, require: (id: string) => any, dirname: string, filename: string, symbolConstructor: SymbolConstructor) => void;
39+
const module: { exports: any; } = { exports: {} };
40+
evaluateThunk(module, module.exports, noRequire, vpath.dirname(output.file), output.file, FakeSymbol);
41+
return module;
42+
}
43+
44+
it("sync (es5)", async () => {
45+
const module = evaluate(compile(`
46+
let i = 0;
47+
const iterator = {
48+
[Symbol.iterator]() { return this; },
49+
next() {
50+
switch (i++) {
51+
case 0: return { value: 1, done: false };
52+
case 1: return { value: Promise.resolve(2), done: false };
53+
case 2: return { value: new Promise<number>(resolve => setTimeout(resolve, 100, 3)), done: false };
54+
default: return { value: undefined: done: true };
55+
}
56+
}
57+
};
58+
export const output: any[] = [];
59+
export async function main() {
60+
for await (const item of iterator) {
61+
output.push(item);
62+
}
63+
}`));
64+
await module.exports.main();
65+
assert.strictEqual(module.exports.output[0], 1);
66+
assert.strictEqual(module.exports.output[1], 2);
67+
assert.strictEqual(module.exports.output[2], 3);
68+
});
69+
70+
it("sync (es2015)", async () => {
71+
const module = evaluate(compile(`
72+
let i = 0;
73+
const iterator = {
74+
[Symbol.iterator]() { return this; },
75+
next() {
76+
switch (i++) {
77+
case 0: return { value: 1, done: false };
78+
case 1: return { value: Promise.resolve(2), done: false };
79+
case 2: return { value: new Promise<number>(resolve => setTimeout(resolve, 100, 3)), done: false };
80+
default: return { value: undefined: done: true };
81+
}
82+
}
83+
};
84+
export const output: any[] = [];
85+
export async function main() {
86+
for await (const item of iterator) {
87+
output.push(item);
88+
}
89+
}`, { target: ScriptTarget.ES2015 }));
90+
await module.exports.main();
91+
assert.strictEqual(module.exports.output[0], 1);
92+
assert.strictEqual(module.exports.output[1], 2);
93+
assert.strictEqual(module.exports.output[2], 3);
94+
});
95+
96+
it("async (es5)", async () => {
97+
const module = evaluate(compile(`
98+
let i = 0;
99+
const iterator = {
100+
[Symbol.asyncIterator]() { return this; },
101+
async next() {
102+
switch (i++) {
103+
case 0: return { value: 1, done: false };
104+
case 1: return { value: Promise.resolve(2), done: false };
105+
case 2: return { value: new Promise<number>(resolve => setTimeout(resolve, 100, 3)), done: false };
106+
default: return { value: undefined: done: true };
107+
}
108+
}
109+
};
110+
export const output: any[] = [];
111+
export async function main() {
112+
for await (const item of iterator) {
113+
output.push(item);
114+
}
115+
}`));
116+
await module.exports.main();
117+
assert.strictEqual(module.exports.output[0], 1);
118+
assert.instanceOf(module.exports.output[1], Promise);
119+
assert.instanceOf(module.exports.output[2], Promise);
120+
});
121+
122+
it("async (es2015)", async () => {
123+
const module = evaluate(compile(`
124+
let i = 0;
125+
const iterator = {
126+
[Symbol.asyncIterator]() { return this; },
127+
async next() {
128+
switch (i++) {
129+
case 0: return { value: 1, done: false };
130+
case 1: return { value: Promise.resolve(2), done: false };
131+
case 2: return { value: new Promise<number>(resolve => setTimeout(resolve, 100, 3)), done: false };
132+
default: return { value: undefined: done: true };
133+
}
134+
}
135+
};
136+
export const output: any[] = [];
137+
export async function main() {
138+
for await (const item of iterator) {
139+
output.push(item);
140+
}
141+
}`, { target: ScriptTarget.ES2015 }));
142+
await module.exports.main();
143+
assert.strictEqual(module.exports.output[0], 1);
144+
assert.instanceOf(module.exports.output[1], Promise);
145+
assert.instanceOf(module.exports.output[2], Promise);
146+
});
147+
});
148+
}

tests/baselines/reference/emitter.forAwait.es2015.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ function f1() {
6363
let y;
6464
try {
6565
for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = yield y_1.next(), !y_1_1.done;) {
66-
const x = yield y_1_1.value;
66+
const x = y_1_1.value;
6767
}
6868
}
6969
catch (e_1_1) { e_1 = { error: e_1_1 }; }
@@ -97,7 +97,7 @@ function f2() {
9797
let x, y;
9898
try {
9999
for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = yield y_1.next(), !y_1_1.done;) {
100-
x = yield y_1_1.value;
100+
x = y_1_1.value;
101101
}
102102
}
103103
catch (e_1_1) { e_1 = { error: e_1_1 }; }
@@ -135,7 +135,7 @@ function f3() {
135135
let y;
136136
try {
137137
for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = yield __await(y_1.next()), !y_1_1.done;) {
138-
const x = yield yield __await(__await(y_1_1.value));
138+
const x = y_1_1.value;
139139
}
140140
}
141141
catch (e_1_1) { e_1 = { error: e_1_1 }; }
@@ -173,7 +173,7 @@ function f4() {
173173
let x, y;
174174
try {
175175
for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = yield __await(y_1.next()), !y_1_1.done;) {
176-
x = yield yield __await(__await(y_1_1.value));
176+
x = y_1_1.value;
177177
}
178178
}
179179
catch (e_1_1) { e_1 = { error: e_1_1 }; }
@@ -208,7 +208,7 @@ function f5() {
208208
let y;
209209
try {
210210
outer: for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = yield y_1.next(), !y_1_1.done;) {
211-
const x = yield y_1_1.value;
211+
const x = y_1_1.value;
212212
continue outer;
213213
}
214214
}
@@ -248,7 +248,7 @@ function f6() {
248248
let y;
249249
try {
250250
outer: for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = yield __await(y_1.next()), !y_1_1.done;) {
251-
const x = yield yield __await(__await(y_1_1.value));
251+
const x = y_1_1.value;
252252
continue outer;
253253
}
254254
}

tests/baselines/reference/emitter.forAwait.es2017.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ async function f1() {
5454
let y;
5555
try {
5656
for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = await y_1.next(), !y_1_1.done;) {
57-
const x = await y_1_1.value;
57+
const x = y_1_1.value;
5858
}
5959
}
6060
catch (e_1_1) { e_1 = { error: e_1_1 }; }
@@ -78,7 +78,7 @@ async function f2() {
7878
let x, y;
7979
try {
8080
for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = await y_1.next(), !y_1_1.done;) {
81-
x = await y_1_1.value;
81+
x = y_1_1.value;
8282
}
8383
}
8484
catch (e_1_1) { e_1 = { error: e_1_1 }; }
@@ -115,7 +115,7 @@ function f3() {
115115
let y;
116116
try {
117117
for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = yield __await(y_1.next()), !y_1_1.done;) {
118-
const x = yield yield __await(__await(y_1_1.value));
118+
const x = y_1_1.value;
119119
}
120120
}
121121
catch (e_1_1) { e_1 = { error: e_1_1 }; }
@@ -153,7 +153,7 @@ function f4() {
153153
let x, y;
154154
try {
155155
for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = yield __await(y_1.next()), !y_1_1.done;) {
156-
x = yield yield __await(__await(y_1_1.value));
156+
x = y_1_1.value;
157157
}
158158
}
159159
catch (e_1_1) { e_1 = { error: e_1_1 }; }
@@ -179,7 +179,7 @@ async function f5() {
179179
let y;
180180
try {
181181
outer: for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = await y_1.next(), !y_1_1.done;) {
182-
const x = await y_1_1.value;
182+
const x = y_1_1.value;
183183
continue outer;
184184
}
185185
}
@@ -218,7 +218,7 @@ function f6() {
218218
let y;
219219
try {
220220
outer: for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = yield __await(y_1.next()), !y_1_1.done;) {
221-
const x = yield yield __await(__await(y_1_1.value));
221+
const x = y_1_1.value;
222222
continue outer;
223223
}
224224
}

0 commit comments

Comments
 (0)